Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,6 @@ Current directory, git branch, model (with its reasoning effort), context-window
ccbit keeps a small, **numbers-only** memory per project (no prompt text is ever stored). It folds each completed turn into a couple of moving averages and uses them to move past one-size-fits-all rules:

- **Learned stall threshold.** "Stopped" is no longer a fixed timer — it adapts to how long *this* project's turns normally pause. A repo with slow builds stops falsely reading as stalled; a snappy one flags a hang sooner. (Still overridable with `CCBIT_STALL`.)
- **"longer than usual."** While working, Bit adds a quiet note when a turn runs well past the project's norm.
- **Subtle personality.** A red→green recovery reads `Build green again.` rather than a flat status.

Everything stays silent until there's enough history to be trustworthy — a wrong insight costs more than a missing one. Memory is disposable: delete `~/.claude/ccbit/memory/` and ccbit falls back to its fixed defaults.
Expand Down
1 change: 0 additions & 1 deletion cmd/ccbit/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,6 @@ func statusLine() {
Now: now,
Trend: trend,
Siblings: sessions.Active(in.SessionID, now),
TypicalTurn: stats.TypicalTurn(),
TurnLinesAdded: turnAdded,
TurnLinesRemoved: turnRemoved,
Tasks: tasks,
Expand Down
1 change: 1 addition & 0 deletions internal/render/demo_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
)

func TestDemoCoversEveryState(t *testing.T) {
clearFeatureEnv(t)
out := strings.Join(Demo(false), "\n")
for _, label := range []string{"working", "agents", "waiting", "failed", "done", "redeemed", "stopped", "idle"} {
if !strings.Contains(out, label) {
Expand Down
22 changes: 1 addition & 21 deletions internal/render/line2_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -200,27 +200,6 @@ func TestSiblingClauseStalledAgesOut(t *testing.T) {
}
}

func TestLongerThanUsual(t *testing.T) {
c := ctx()
c.TypicalTurn = time.Minute
// A turn well past 2x the norm gets the subtle note.
long := Render(state.View{State: state.Working, HasElapsed: true, Elapsed: 3 * time.Minute}, c)[0]
if !strings.Contains(long, "(longer than usual)") {
t.Fatalf("long turn line1 = %q, want the note", long)
}
// A normal-length turn stays quiet.
short := Render(state.View{State: state.Working, HasElapsed: true, Elapsed: 30 * time.Second}, c)[0]
if strings.Contains(short, "longer than usual") {
t.Fatalf("normal turn line1 = %q, should be quiet", short)
}
// No learned baseline yet -> never fires.
c.TypicalTurn = 0
cold := Render(state.View{State: state.Working, HasElapsed: true, Elapsed: 9 * time.Minute}, c)[0]
if strings.Contains(cold, "longer than usual") {
t.Fatalf("without a baseline line1 = %q, should be quiet", cold)
}
}

func TestRecoveryGreenAgain(t *testing.T) {
c := ctx()
v := state.View{
Expand Down Expand Up @@ -254,6 +233,7 @@ func TestTrendArrow(t *testing.T) {
}

func TestCtxSegmentTrend(t *testing.T) {
clearFeatureEnv(t)
c := ctx()
pct := 38.0
c.In.CtxPct = &pct
Expand Down
19 changes: 1 addition & 18 deletions internal/render/render.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,6 @@ type Ctx struct {
Trend sessions.Trend // context-window velocity for the ctx% segment
Siblings []sessions.Beat // other live sessions, actionable-first

// TypicalTurn is the project's learned mean turn duration (0 if not yet
// learned), used to flag a turn running unusually long.
TypicalTurn time.Duration

// TurnLinesAdded/Removed are this turn's lines-changed delta. The stdin cost
// counters are session-cumulative, so main re-bases them at each turn start
// (via the heartbeat) — keeping the recap's line numbers in the same scope as
Expand Down Expand Up @@ -308,7 +304,7 @@ func line1(v state.View, c Ctx) string {
if v.Thinking && v.HasLastAge {
base += " · thinking (" + fmtDur(v.LastAge) + ")"
}
return base + elapsedSuffix(v) + longerThanUsual(v, c) + loopNote(v.Turn)
return base + elapsedSuffix(v) + loopNote(v.Turn)

case state.Agents:
s := pluralCount(v.AgentsRunning, "agent") + " running"
Expand Down Expand Up @@ -607,19 +603,6 @@ func doneSentence(v state.View, c Ctx) string {
return strings.Join(sentences, ". ") + "."
}

// longerThanUsual is Bit's subtle note that this turn has run well past the
// project's learned norm. Silent until enough history exists and the turn is
// clearly over the line (2x typical), so it never cries wolf on normal variance.
func longerThanUsual(v state.View, c Ctx) string {
if c.TypicalTurn <= 0 || !v.HasElapsed {
return ""
}
if v.Elapsed > 2*c.TypicalTurn {
return " (longer than usual)"
}
return ""
}

// linesDelta is this turn's diff size as bare "+added/-removed" (per-turn, not
// the session total — see Ctx.TurnLinesAdded); doneSentence supplies the
// "line changes:" lead-in.
Expand Down
1 change: 1 addition & 0 deletions internal/render/render_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ func TestLine1Failed(t *testing.T) {
}

func TestLine2Ambient(t *testing.T) {
clearFeatureEnv(t)
c := ctx()
pct := 38.0
c.In.CtxPct = &pct
Expand Down
Loading