pi-crew 0.11.1 → 0.11.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (154) hide show
  1. package/CHANGELOG.md +82 -9
  2. package/README.md +161 -1036
  3. package/agents/verifier.md +18 -7
  4. package/dist/index.mjs +742 -91462
  5. package/docs/README.md +57 -46
  6. package/docs/architecture.md +87 -33
  7. package/docs/commands-reference.md +9 -5
  8. package/docs/troubleshooting.md +3 -2
  9. package/package.json +5 -4
  10. package/schema.json +29 -0
  11. package/skills/real-test-pi-crew/SKILL.md +206 -41
  12. package/src/agents/agent-config.ts +1 -1
  13. package/src/agents/discover-agents.ts +1 -1
  14. package/src/config/config-validation.ts +15 -0
  15. package/src/config/config.ts +47 -13
  16. package/src/config/env-vars.ts +35 -0
  17. package/src/config/types.ts +19 -0
  18. package/src/errors.ts +2 -2
  19. package/src/extension/async-notifier.ts +55 -8
  20. package/src/extension/crew-input-router.ts +9 -2
  21. package/src/extension/help.ts +21 -10
  22. package/src/extension/knowledge-injection.ts +2 -1
  23. package/src/extension/management.ts +8 -3
  24. package/src/extension/notification-sink.ts +17 -0
  25. package/src/extension/plan-orchestrate.ts +19 -3
  26. package/src/extension/registration/command-utils.ts +28 -2
  27. package/src/extension/registration/commands/dashboard.ts +11 -1
  28. package/src/extension/registration/commands/manage.ts +31 -15
  29. package/src/extension/registration/commands/run.ts +24 -2
  30. package/src/extension/registration/commands/shared.ts +34 -10
  31. package/src/extension/registration/commands/status.ts +25 -2
  32. package/src/extension/registration/context-builder.ts +8 -2
  33. package/src/extension/registration/foreground-run-controller.ts +2 -2
  34. package/src/extension/registration/health-notify-policy.ts +100 -0
  35. package/src/extension/registration/lazy-configurers.ts +38 -0
  36. package/src/extension/registration/lifecycle-handlers.ts +107 -40
  37. package/src/extension/registration/lifecycle.ts +77 -10
  38. package/src/extension/registration/observability.ts +125 -53
  39. package/src/extension/registration/registration-types.ts +7 -5
  40. package/src/extension/registration/runtime-cleanup.ts +9 -3
  41. package/src/extension/registration/subagent-helpers.ts +38 -0
  42. package/src/extension/registration/team-tool.ts +9 -2
  43. package/src/extension/registration/wire-cross-extension.ts +30 -0
  44. package/src/extension/run-compare.ts +220 -0
  45. package/src/extension/run-export.ts +37 -5
  46. package/src/extension/run-maintenance.ts +155 -5
  47. package/src/extension/team-manager-command.ts +3 -2
  48. package/src/extension/team-tool/dispatch/index.ts +3 -2
  49. package/src/extension/team-tool/dispatch/manage.ts +5 -2
  50. package/src/extension/team-tool/goal-wrap.ts +7 -0
  51. package/src/extension/team-tool/goal.ts +16 -1
  52. package/src/extension/team-tool/handle-settings.ts +19 -2
  53. package/src/extension/team-tool/health-monitor.ts +21 -7
  54. package/src/extension/team-tool/lifecycle-actions.ts +49 -1
  55. package/src/extension/team-tool/parallel-dispatch.ts +16 -1
  56. package/src/extension/team-tool/plan.ts +10 -0
  57. package/src/extension/team-tool/routing-hint.ts +63 -0
  58. package/src/extension/team-tool/run-intent.ts +1 -1
  59. package/src/extension/team-tool/run.ts +1 -1
  60. package/src/extension/team-tool/status.ts +4 -0
  61. package/src/extension/team-tool.ts +52 -6
  62. package/src/extension/webhook-notify.ts +382 -0
  63. package/src/observability/metric-sink.ts +12 -2
  64. package/src/prompt/prompt-runtime.ts +82 -31
  65. package/src/prompt/worker-events-channel.ts +12 -0
  66. package/src/runtime/README.md +1 -1
  67. package/src/runtime/async-runner.ts +123 -1
  68. package/src/runtime/background-runner.ts +313 -234
  69. package/src/runtime/broker/crew-broker.ts +76 -71
  70. package/src/runtime/broker/delegate/delegate-event.ts +12 -8
  71. package/src/runtime/broker/delegate/shadow-lifecycle.ts +92 -0
  72. package/src/runtime/broker/protocol/connection-state.ts +10 -0
  73. package/src/runtime/broker/protocol/lock-busy.ts +76 -0
  74. package/src/runtime/broker/wait-status-cache.ts +1 -1
  75. package/src/runtime/child-pi/child-pi-timers.ts +1 -1
  76. package/src/runtime/child-pi/mock-fixtures.ts +48 -0
  77. package/src/runtime/crew-agent-records.ts +337 -45
  78. package/src/runtime/deadletter.ts +43 -1
  79. package/src/runtime/delegate-spawn.ts +21 -3
  80. package/src/runtime/deterministic-ast.ts +14 -2
  81. package/src/runtime/dispatch-batch.ts +72 -5
  82. package/src/runtime/goal-workflow/dynamic-workflow-context.ts +78 -58
  83. package/src/runtime/goal-workflow/dynamic-workflow-runner.ts +14 -2
  84. package/src/runtime/goal-workflow/goal-loop-runner.ts +105 -4
  85. package/src/runtime/group-join.ts +11 -3
  86. package/src/runtime/heartbeat/heartbeat-watcher.ts +7 -0
  87. package/src/runtime/live-session/live-session-runtime.ts +1 -1
  88. package/src/runtime/model/model-fallback.ts +21 -1
  89. package/src/runtime/model/pi-args.ts +8 -10
  90. package/src/runtime/model/runtime-warmup.ts +2 -0
  91. package/src/runtime/output/output-validator.ts +10 -5
  92. package/src/runtime/plan-approval.ts +14 -1
  93. package/src/runtime/recovery/crash-recovery.ts +96 -49
  94. package/src/runtime/run-worker.ts +12 -1
  95. package/src/runtime/scheduling/global-worker-cap.ts +13 -6
  96. package/src/runtime/scheduling/run-coalesced-task-group.ts +27 -1
  97. package/src/runtime/scheduling/scheduler.ts +49 -13
  98. package/src/runtime/scheduling/semaphore.ts +148 -20
  99. package/src/runtime/scratchpad/README.md +1 -1
  100. package/src/runtime/scratchpad/protocol.ts +1 -1
  101. package/src/runtime/settings-store.ts +1 -1
  102. package/src/runtime/skill-instructions.ts +22 -0
  103. package/src/runtime/stale-reconciler.ts +85 -13
  104. package/src/runtime/subagent-manager.ts +43 -13
  105. package/src/runtime/task-runner/child-executor.ts +17 -6
  106. package/src/runtime/task-runner/post-execution.ts +44 -14
  107. package/src/runtime/task-runner/pre-execution.ts +26 -2
  108. package/src/runtime/task-runner/prompt-builder.ts +150 -45
  109. package/src/runtime/task-runner/retrieval-orchestrator.ts +36 -14
  110. package/src/runtime/task-runner.ts +28 -1
  111. package/src/runtime/team-runner.ts +42 -2
  112. package/src/runtime/verification/verification-worktree.ts +3 -0
  113. package/src/runtime/workspace-lock.ts +4 -1
  114. package/src/schema/config-schema.ts +14 -0
  115. package/src/schema/team-tool-schema.ts +17 -0
  116. package/src/state/atomic-write.ts +53 -0
  117. package/src/state/contracts.ts +109 -0
  118. package/src/state/coordination/locks.ts +191 -33
  119. package/src/state/coordination/mailbox.ts +147 -17
  120. package/src/state/crew-init.ts +87 -12
  121. package/src/state/event-log/cursor.ts +37 -1
  122. package/src/state/event-log/event-log-rotation.ts +72 -7
  123. package/src/state/stores/active-run-registry.ts +13 -1
  124. package/src/state/stores/state-store.ts +120 -23
  125. package/src/state/types.ts +4 -0
  126. package/src/ui/dashboard-panes/agents-pane.ts +22 -10
  127. package/src/ui/heartbeat-aggregator.ts +34 -0
  128. package/src/ui/inline-panel/index.ts +1 -1
  129. package/src/ui/keybinding-map.ts +22 -4
  130. package/src/ui/live-conversation-overlay.ts +6 -3
  131. package/src/ui/render-diff.ts +19 -6
  132. package/src/ui/run-dashboard.ts +109 -6
  133. package/src/ui/run-snapshot-cache.ts +27 -4
  134. package/src/ui/spinner.ts +26 -2
  135. package/src/ui/tool-progress-formatter.ts +2 -1
  136. package/src/ui/tool-renderers/brief-mode.ts +2 -1
  137. package/src/ui/tool-renderers/index.ts +3 -3
  138. package/src/ui/widget/task-list.ts +8 -5
  139. package/src/ui/widget/widget-model.ts +3 -1
  140. package/src/utils/incremental-reader.ts +11 -3
  141. package/src/utils/paths.ts +94 -12
  142. package/src/utils/project-markers.ts +40 -0
  143. package/src/worktree/worktree-manager.ts +206 -26
  144. package/tsconfig.json +2 -0
  145. package/workflows/distill.workflow.md +3 -3
  146. package/workflows/fast-fix.workflow.md +1 -1
  147. package/workflows/plan-execute.workflow.md +1 -1
  148. package/workflows/review.workflow.md +1 -1
  149. package/workflows/strict-fast-fix.workflow.md +1 -1
  150. package/docs/migration-v0.4-v0.5.md +0 -208
  151. package/docs/runtime-flow.md +0 -148
  152. package/src/ui/card-colors.ts +0 -168
  153. package/src/utils/completion-dedupe.ts +0 -63
  154. package/src/utils/resolve-shell.ts +0 -36
@@ -1,148 +0,0 @@
1
- # pi-crew Runtime Flow
2
-
3
- This document is a compact map of the runtime paths used by `pi-crew`.
4
-
5
- ## Main sequence
6
-
7
- ```text
8
- User / model
9
- │ calls team({ action: "run", ... }) or /team-run
10
- ▼
11
- handleTeamTool()
12
- │ validates schema and routes action
13
- ▼
14
- handleRun()
15
- ├─ discoverTeams/discoverWorkflows/discoverAgents
16
- ├─ validateWorkflowForTeam
17
- ├─ expandParallelResearchWorkflow when applicable
18
- ├─ createRunManifest + tasks.json + goal artifact
19
- ├─ if async=true ─────────────────────────────────────────────┐
20
- │ spawnBackgroundTeamRun() │
21
- │ ├─ resolve jiti-register.mjs │
22
- │ ├─ fail-fast if jiti missing │
23
- │ ├─ node --import jiti-register.mjs background-runner.ts │
24
- │ └─ parent schedules early-exit guard │
25
- │ ▼
26
- │ background-runner.ts
27
- │ ├─ append async.started
28
- │ ├─ write async.pid startup marker
29
- │ ├─ rediscover team/workflow/agents
30
- │ └─ executeTeamRun()
31
- │
32
- └─ if foreground/default
33
- ├─ startForegroundRun schedules session-bound run, or
34
- └─ executeTeamRun inline for scaffold/non-scheduled paths
35
-
36
- executeTeamRun()
37
- ├─ write run.running
38
- ├─ materialize queued/running agent records lazily
39
- ├─ build task graph index
40
- ├─ while queued tasks exist
41
- │ ├─ taskGraphSnapshot
42
- │ ├─ resolveBatchConcurrency
43
- │ ├─ getReadyTasks
44
- │ ├─ append task.progress batch event
45
- │ ├─ mapConcurrent ready batch
46
- │ │ └─ runTeamTask()
47
- │ │ ├─ prepare workspace/worktree
48
- │ │ ├─ build task packet
49
- │ │ ├─ render prompt + dependency context
50
- │ │ ├─ choose model candidates from Pi config
51
- │ │ ├─ spawn child Pi process
52
- │ │ ├─ ChildPiLineObserver parses stdout/stderr
53
- │ │ ├─ append per-agent events/output
54
- │ │ ├─ update agent progress/task state
55
- │ │ ├─ parse final JSONL/session usage
56
- │ │ └─ write result/log/transcript/metadata artifacts
57
- │ ├─ merge task updates monotonically
58
- │ ├─ optional adaptive plan injection
59
- │ ├─ save tasks/agents/progress
60
- │ └─ write batch artifact
61
- ├─ policy closeout
62
- └─ run.completed / run.failed / run.blocked / run.cancelled
63
- ```
64
-
65
- ## Action router
66
-
67
- | Action | Handler | Purpose |
68
- |---|---|---|
69
- | `run` | `team-tool/run.ts` | Create and execute a run, foreground or async. |
70
- | `status` | `team-tool.ts` | Show manifest/tasks/agents/events and mark stale async runs failed. |
71
- | `summary` | `session-summary.ts`/summary handler | Write/read run summary artifact. |
72
- | `events` | `team-tool.ts` | Tail durable run events. |
73
- | `artifacts` | `team-tool.ts` | List run artifacts. |
74
- | `resume` | `team-tool.ts` | Requeue failed/cancelled/skipped/running tasks. |
75
- | `cancel` | `team-tool.ts` | Mark queued/running tasks cancelled and request foreground interrupt. |
76
- | `forget` | `run-maintenance.ts` | Delete run state/artifacts with confirmation. |
77
- | `prune` | `run-maintenance.ts` | Remove old finished runs with confirmation. |
78
- | `export` | `run-export.ts` | Create portable run bundle. |
79
- | `import` / `imports` | `run-import.ts` / `import-index.ts` | Store/list imported bundles. |
80
- | `config` | `config.ts` + config action | Show/update user/project config. |
81
- | `doctor` | `team-tool/doctor.ts` | Platform/resource/runtime diagnostics. |
82
- | `validate` | `validate-resources.ts` | Validate agents/teams/workflows. |
83
- | `recommend` | `team-recommendation.ts` | Suggest team/workflow/action for a goal. |
84
- | management | `management.ts` | Create/update/delete/rename teams, agents, workflows. |
85
- | API | `team-tool/api.ts` | File-backed observability/control/mailbox API. |
86
-
87
- ## Worker modes
88
-
89
- | Mode | Behavior |
90
- |---|---|
91
- | `child-process` | Default. Launches real child `pi` processes per task. |
92
- | `scaffold` | Explicit dry-run. No child Pi worker execution. |
93
- | `live-session` | Experimental/gated in-process/live agent path. |
94
- | `auto` | Resolves to child-process unless config/env requests otherwise. |
95
-
96
- ## Important files
97
-
98
- ```text
99
- src/extension/register.ts Pi extension entry/wiring
100
- src/extension/team-tool/run.ts run creation and foreground/async split
101
- src/runtime/background-runner.ts detached async entrypoint
102
- src/runtime/async-runner.ts background spawn command/options
103
- src/runtime/team-runner.ts workflow/task graph scheduler
104
- src/runtime/task-runner.ts single task execution
105
- src/runtime/child-pi.ts child Pi process and output observer
106
- src/runtime/model-fallback.ts configured model candidates/routing
107
- src/runtime/concurrency.ts batch concurrency decisions
108
- src/runtime/process-status.ts pid/liveness/stale detection
109
- src/state/state-store.ts manifest/tasks persistence
110
- src/state/event-log.ts JSONL run events
111
- src/runtime/crew-agent-records.ts aggregate + per-agent status files
112
- ```
113
-
114
- ## Environment variables
115
-
116
- | Env | Effect |
117
- |---|---|
118
- | `PI_CREW_EXECUTE_WORKERS=0` | Disable real workers, use scaffold behavior. |
119
- | `PI_TEAMS_EXECUTE_WORKERS=0` | Legacy alias for worker disable. |
120
- | `PI_CREW_ENABLE_EXPERIMENTAL_LIVE_SESSION=1` | Allow experimental live-session runtime. |
121
- | `PI_CREW_MOCK_LIVE_SESSION=success` | Test hook for live-session mock. |
122
- | `PI_TEAMS_MOCK_CHILD_PI` | Test hook for mocked child Pi execution. |
123
- | `PI_CREW_DEPTH`, `PI_CREW_MAX_DEPTH` | Canonical subagent recursion guard. |
124
- | `PI_TEAMS_DEPTH`, `PI_TEAMS_MAX_DEPTH` | Legacy recursion guard aliases. |
125
- | `PI_TEAMS_HOME` | Override user config/state home in tests. |
126
- | `PI_TEAMS_PI_BIN` | Override child `pi` executable. |
127
- | `PI_CODING_AGENT_DIR` | Override Pi settings/models directory for model discovery. |
128
- | `PI_CREW_ASYNC_EARLY_EXIT_GUARD=0` | Disable 3s background early-exit guard. |
129
-
130
- ## State transition summary
131
-
132
- ```text
133
- queued/planning/running ── completed
134
- ├─ failed
135
- ├─ blocked
136
- └─ cancelled
137
- ```
138
-
139
- Task states follow the same durable contract plus `skipped`. Terminal states are monotonic during parallel merge.
140
-
141
- ## Observability tips
142
-
143
- - Use `/team-dashboard` for a UI overview.
144
- - Use `team status runId=...` for canonical state and stale async detection.
145
- - Read `background.log` for early import/spawn errors.
146
- - Read `events.jsonl` for event chronology.
147
- - Read `agents/{taskId}/status.json` for per-agent model/progress/tool status.
148
- - Read `artifacts/{runId}/transcripts/{taskId}.jsonl` for raw child Pi transcript.
@@ -1,168 +0,0 @@
1
- /**
2
- * Color utilities for pi-crew UI — background tinting and ANSI parsing.
3
- *
4
- * Ported from pi-diff's mixBg() / autoDeriveBgFromTheme() technique:
5
- * derive subtle status-tinted backgrounds from the theme's foreground colors
6
- * so crew cards stay readable across light/dark themes without hardcoding RGB.
7
- *
8
- * mixBg(base, accent, intensity) does a LINEAR interpolation:
9
- * result = base + (accent - base) * intensity
10
- *
11
- * Intensity tiers (per pi-diff research):
12
- * 10-12% — subtle gutter/hint
13
- * 15-18% — line background (visible but not loud)
14
- * 30-35% — word-level emphasis (prominent)
15
- */
16
-
17
- import { visibleWidth as visualWidth } from "../utils/visual.ts";
18
- import type { CrewTheme } from "./theme-adapter.ts";
19
-
20
- // ── ANSI parsing ────────────────────────────────────────────────────────
21
-
22
- interface Rgb {
23
- r: number;
24
- g: number;
25
- b: number;
26
- }
27
-
28
- const ANSI_RGB_FG_RE = /^\x1b?\[?38;2;(\d+);(\d+);(\d+)m?$/;
29
- const ANSI_RGB_BG_RE = /^\x1b?\[?48;2;(\d+);(\d+);(\d+)m?$/;
30
-
31
- /** Parse a truecolor ANSI SGR sequence (e.g. "\x1b[38;2;255;100;50m") to RGB. */
32
- export function parseAnsiRgb(ansi: string | undefined | null): Rgb | null {
33
- if (!ansi) return null;
34
- const stripped = ansi.replace(/^\x1b?\[?/, "").replace(/m?$/, "");
35
- const fgMatch = stripped.match(/^38;2;(\d+);(\d+);(\d+)$/);
36
- const bgMatch = stripped.match(/^48;2;(\d+);(\d+);(\d+)$/);
37
- const m = fgMatch ?? bgMatch;
38
- if (!m) return null;
39
- const r = Number(m[1]);
40
- const g = Number(m[2]);
41
- const b = Number(m[3]);
42
- if (![r, g, b].every(Number.isFinite)) return null;
43
- return { r, g, b };
44
- }
45
-
46
- // ── Color mixing ────────────────────────────────────────────────────────
47
-
48
- /** Linear-interpolate two RGB colors and emit a 48;2 (truecolor bg) SGR code. */
49
- export function mixBg(base: Rgb, accent: Rgb, intensity: number): string {
50
- const r = Math.round(base.r + (accent.r - base.r) * intensity);
51
- const g = Math.round(base.g + (accent.g - base.g) * intensity);
52
- const b = Math.round(base.b + (accent.b - base.b) * intensity);
53
- return `\x1b[48;2;${r};${g};${b}m`;
54
- }
55
-
56
- const BLACK: Rgb = { r: 0, g: 0, b: 0 };
57
-
58
- /**
59
- * The background slots Pi's Theme actually exposes (`ThemeBg` in
60
- * node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/theme/theme.d.ts).
61
- *
62
- * P1-6 (2026-09-15): this probe used to pass "background", which is NOT a
63
- * ThemeBg member. Pi's `getBgAnsi` throws `Unknown theme background color:
64
- * background` for it, the surrounding catch swallowed the throw, and `base`
65
- * stayed BLACK on every theme — so crew card tints were derived from the wrong
66
- * base (visibly wrong on light themes). Never pass a slot outside this union.
67
- * `selectedBg` is the slot whose value is the surrounding card/selection
68
- * background; it is the one both Pi's `ThemeBg` and pi-crew's `CrewThemeBg`
69
- * (theme-adapter.ts) define. Enforced by
70
- * test/unit/round23-ui-unicode.test.ts (a fake theme records every slot).
71
- */
72
- type ThemeBgSlot =
73
- | "selectedBg"
74
- | "scrollbarThumb"
75
- | "userMessageBg"
76
- | "customMessageBg"
77
- | "toolPendingBg"
78
- | "toolSuccessBg"
79
- | "toolErrorBg";
80
-
81
- /** Duck-typed view of Pi's Theme methods (CrewTheme does not declare them). */
82
- type ThemeProbe = {
83
- getBgAnsi?: (slot: ThemeBgSlot) => string | undefined;
84
- getFgAnsi?: (slot: string) => string | undefined;
85
- };
86
-
87
- // ── Theme → background derivation ───────────────────────────────────────
88
-
89
- /**
90
- * Resolve a subtle status-tinted background for a crew card interior.
91
- *
92
- * Reads the theme's fg color for the given status slot (success/error/border),
93
- * mixes it into a dark base at low intensity (default 8%), and returns a bg SGR
94
- * sequence. Returns "" if the theme exposes no fg ANSI (graceful — caller
95
- * renders without background).
96
- *
97
- * @param theme pi-crew theme adapter
98
- * @param statusSlot which status color to tint with ("success" | "error" | "borderAccent" | "border")
99
- * @param intensity 0-1, how strong the tint is (default 0.08 = very subtle)
100
- */
101
- export function deriveCardBackground(
102
- theme: CrewTheme,
103
- statusSlot: "success" | "error" | "borderAccent" | "border",
104
- intensity = 0.08,
105
- ): string {
106
- // Prefer the theme's bg color as base (matches the card's surroundings).
107
- let base = BLACK;
108
- const themeAny = theme as unknown as ThemeProbe;
109
- if (themeAny.getBgAnsi) {
110
- try {
111
- // "selectedBg" is a valid ThemeBg slot (P1-6). The catch only guards
112
- // third-party theme shims that throw on the probe; it can no longer
113
- // hide a permanently-invalid slot, because the slot type and the
114
- // regression test both pin it to the ThemeBg union.
115
- const parsed = parseAnsiRgb(themeAny.getBgAnsi("selectedBg"));
116
- if (parsed) base = parsed;
117
- } catch {
118
- /* shim threw — fall back to black */
119
- }
120
- }
121
-
122
- // Get the accent from the theme's fg for the requested slot.
123
- let accentRgb: Rgb | null = null;
124
- if (themeAny.getFgAnsi) {
125
- try {
126
- accentRgb = parseAnsiRgb(themeAny.getFgAnsi(statusSlot));
127
- } catch {
128
- /* accent stays null */
129
- }
130
- }
131
- if (!accentRgb) return ""; // can't derive — render without bg tint
132
-
133
- return mixBg(base, accentRgb, intensity);
134
- }
135
-
136
- // ── Helpers for padding lines with a background ─────────────────────────
137
-
138
- const RESET = "\x1b[0m";
139
-
140
- /** Strip ANSI SGR codes then compute the VISUAL width (Unicode-aware).
141
- * Round 23 (BUG 2): previously this used `.length` (UTF-16 code units), which
142
- * under-counts CJK/emoji → wrong padding → broken frame borders in crew cards.
143
- * Delegate to the canonical Unicode-aware visualWidth from utils/visual.ts
144
- * used by every other renderer. */
145
- export function visibleWidth(text: string): number {
146
- return visualWidth(text);
147
- }
148
-
149
- /**
150
- * Pad a line to `targetWidth` and apply `bgAnsi` as the background for the
151
- * full visible width (including any trailing padding spaces).
152
- *
153
- * `bgAnsi` should be a 48;2 (truecolor bg) SGR sequence from mixBg().
154
- * The function inserts the bg code after any existing leading SGR, fills the
155
- * content, pads to width, then resets. This ensures the entire row's background
156
- * is tinted — not just the characters.
157
- */
158
- export function padWithBackground(line: string, targetWidth: number, bgAnsi: string): string {
159
- if (!bgAnsi) {
160
- // No background — just space-pad (preserving any existing styling).
161
- const width = visibleWidth(line);
162
- return width >= targetWidth ? line : line + " ".repeat(targetWidth - width);
163
- }
164
- const width = visibleWidth(line);
165
- const pad = width >= targetWidth ? "" : " ".repeat(targetWidth - width);
166
- // Re-apply bg after a leading reset if present, otherwise prepend.
167
- return `${bgAnsi}${line}${pad}${RESET}`;
168
- }
@@ -1,63 +0,0 @@
1
- interface CompletionDataLike {
2
- id?: unknown;
3
- agent?: unknown;
4
- timestamp?: unknown;
5
- sessionId?: unknown;
6
- taskIndex?: unknown;
7
- totalTasks?: unknown;
8
- success?: unknown;
9
- }
10
-
11
- function asNonEmptyString(value: unknown): string | undefined {
12
- if (typeof value !== "string") return undefined;
13
- const trimmed = value.trim();
14
- return trimmed.length > 0 ? trimmed : undefined;
15
- }
16
-
17
- function asFiniteNumber(value: unknown): number | undefined {
18
- if (typeof value !== "number") return undefined;
19
- return Number.isFinite(value) ? value : undefined;
20
- }
21
-
22
- export function buildCompletionKey(data: CompletionDataLike, fallback: string): string {
23
- const id = asNonEmptyString(data.id);
24
- if (id) return `id:${id}`;
25
- const sessionId = asNonEmptyString(data.sessionId) ?? "no-session";
26
- const agent = asNonEmptyString(data.agent) ?? "unknown";
27
- const timestamp = asFiniteNumber(data.timestamp);
28
- const taskIndex = asFiniteNumber(data.taskIndex);
29
- const totalTasks = asFiniteNumber(data.totalTasks);
30
- const success = typeof data.success === "boolean" ? (data.success ? "1" : "0") : "?";
31
- return [
32
- "meta",
33
- sessionId,
34
- agent,
35
- timestamp !== undefined ? String(timestamp) : "no-ts",
36
- taskIndex !== undefined ? String(taskIndex) : "-",
37
- totalTasks !== undefined ? String(totalTasks) : "-",
38
- success,
39
- fallback,
40
- ].join(":");
41
- }
42
-
43
- export function pruneSeenMap(seen: Map<string, number>, now: number, ttlMs: number): void {
44
- for (const [key, ts] of seen.entries()) {
45
- if (now - ts > ttlMs) seen.delete(key);
46
- }
47
- }
48
-
49
- export function markSeenWithTtl(seen: Map<string, number>, key: string, now: number, ttlMs: number): boolean {
50
- pruneSeenMap(seen, now, ttlMs);
51
- if (seen.has(key)) return true;
52
- seen.set(key, now);
53
- return false;
54
- }
55
-
56
- export function getGlobalSeenMap(storeKey: string): Map<string, number> {
57
- const globalStore = globalThis as Record<string, unknown>;
58
- const existing = globalStore[storeKey];
59
- if (existing instanceof Map) return existing as Map<string, number>;
60
- const map = new Map<string, number>();
61
- globalStore[storeKey] = map;
62
- return map;
63
- }
@@ -1,36 +0,0 @@
1
- import * as fs from "node:fs";
2
-
3
- /**
4
- * Resolve the bash executable path, with Windows fallbacks for Git Bash.
5
- */
6
- export function resolveBashCmd(): string {
7
- if (process.platform !== "win32") return "bash";
8
- const candidates = [process.env.SHELL, "C:\\Program Files\\Git\\bin\\bash.exe", "C:\\Program Files (x86)\\Git\\bin\\bash.exe"];
9
- for (const cand of candidates) {
10
- if (cand && fs.existsSync(cand)) return cand;
11
- }
12
- return "bash";
13
- }
14
-
15
- /**
16
- * Choose the right shell command and arguments for a script path.
17
- * On Windows, powershell (.ps1) and batch (.cmd/.bat) run natively.
18
- */
19
- export function resolveShellForScript(scriptPath: string): {
20
- command: string;
21
- args: string[];
22
- } {
23
- if (process.platform === "win32") {
24
- if (scriptPath.endsWith(".ps1")) {
25
- return { command: "powershell", args: ["-File", scriptPath] };
26
- }
27
- if (scriptPath.endsWith(".cmd") || scriptPath.endsWith(".bat")) {
28
- // Node >= 20 blocks direct spawn of .bat/.cmd without shell (CVE-2024-27980)
29
- return {
30
- command: process.env.ComSpec ?? "cmd.exe",
31
- args: ["/d", "/s", "/c", scriptPath],
32
- };
33
- }
34
- }
35
- return { command: resolveBashCmd(), args: [scriptPath] };
36
- }