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.
- package/CHANGELOG.md +82 -9
- package/README.md +161 -1036
- package/agents/verifier.md +18 -7
- package/dist/index.mjs +742 -91462
- package/docs/README.md +57 -46
- package/docs/architecture.md +87 -33
- package/docs/commands-reference.md +9 -5
- package/docs/troubleshooting.md +3 -2
- package/package.json +5 -4
- package/schema.json +29 -0
- package/skills/real-test-pi-crew/SKILL.md +206 -41
- package/src/agents/agent-config.ts +1 -1
- package/src/agents/discover-agents.ts +1 -1
- package/src/config/config-validation.ts +15 -0
- package/src/config/config.ts +47 -13
- package/src/config/env-vars.ts +35 -0
- package/src/config/types.ts +19 -0
- package/src/errors.ts +2 -2
- package/src/extension/async-notifier.ts +55 -8
- package/src/extension/crew-input-router.ts +9 -2
- package/src/extension/help.ts +21 -10
- package/src/extension/knowledge-injection.ts +2 -1
- package/src/extension/management.ts +8 -3
- package/src/extension/notification-sink.ts +17 -0
- package/src/extension/plan-orchestrate.ts +19 -3
- package/src/extension/registration/command-utils.ts +28 -2
- package/src/extension/registration/commands/dashboard.ts +11 -1
- package/src/extension/registration/commands/manage.ts +31 -15
- package/src/extension/registration/commands/run.ts +24 -2
- package/src/extension/registration/commands/shared.ts +34 -10
- package/src/extension/registration/commands/status.ts +25 -2
- package/src/extension/registration/context-builder.ts +8 -2
- package/src/extension/registration/foreground-run-controller.ts +2 -2
- package/src/extension/registration/health-notify-policy.ts +100 -0
- package/src/extension/registration/lazy-configurers.ts +38 -0
- package/src/extension/registration/lifecycle-handlers.ts +107 -40
- package/src/extension/registration/lifecycle.ts +77 -10
- package/src/extension/registration/observability.ts +125 -53
- package/src/extension/registration/registration-types.ts +7 -5
- package/src/extension/registration/runtime-cleanup.ts +9 -3
- package/src/extension/registration/subagent-helpers.ts +38 -0
- package/src/extension/registration/team-tool.ts +9 -2
- package/src/extension/registration/wire-cross-extension.ts +30 -0
- package/src/extension/run-compare.ts +220 -0
- package/src/extension/run-export.ts +37 -5
- package/src/extension/run-maintenance.ts +155 -5
- package/src/extension/team-manager-command.ts +3 -2
- package/src/extension/team-tool/dispatch/index.ts +3 -2
- package/src/extension/team-tool/dispatch/manage.ts +5 -2
- package/src/extension/team-tool/goal-wrap.ts +7 -0
- package/src/extension/team-tool/goal.ts +16 -1
- package/src/extension/team-tool/handle-settings.ts +19 -2
- package/src/extension/team-tool/health-monitor.ts +21 -7
- package/src/extension/team-tool/lifecycle-actions.ts +49 -1
- package/src/extension/team-tool/parallel-dispatch.ts +16 -1
- package/src/extension/team-tool/plan.ts +10 -0
- package/src/extension/team-tool/routing-hint.ts +63 -0
- package/src/extension/team-tool/run-intent.ts +1 -1
- package/src/extension/team-tool/run.ts +1 -1
- package/src/extension/team-tool/status.ts +4 -0
- package/src/extension/team-tool.ts +52 -6
- package/src/extension/webhook-notify.ts +382 -0
- package/src/observability/metric-sink.ts +12 -2
- package/src/prompt/prompt-runtime.ts +82 -31
- package/src/prompt/worker-events-channel.ts +12 -0
- package/src/runtime/README.md +1 -1
- package/src/runtime/async-runner.ts +123 -1
- package/src/runtime/background-runner.ts +313 -234
- package/src/runtime/broker/crew-broker.ts +76 -71
- package/src/runtime/broker/delegate/delegate-event.ts +12 -8
- package/src/runtime/broker/delegate/shadow-lifecycle.ts +92 -0
- package/src/runtime/broker/protocol/connection-state.ts +10 -0
- package/src/runtime/broker/protocol/lock-busy.ts +76 -0
- package/src/runtime/broker/wait-status-cache.ts +1 -1
- package/src/runtime/child-pi/child-pi-timers.ts +1 -1
- package/src/runtime/child-pi/mock-fixtures.ts +48 -0
- package/src/runtime/crew-agent-records.ts +337 -45
- package/src/runtime/deadletter.ts +43 -1
- package/src/runtime/delegate-spawn.ts +21 -3
- package/src/runtime/deterministic-ast.ts +14 -2
- package/src/runtime/dispatch-batch.ts +72 -5
- package/src/runtime/goal-workflow/dynamic-workflow-context.ts +78 -58
- package/src/runtime/goal-workflow/dynamic-workflow-runner.ts +14 -2
- package/src/runtime/goal-workflow/goal-loop-runner.ts +105 -4
- package/src/runtime/group-join.ts +11 -3
- package/src/runtime/heartbeat/heartbeat-watcher.ts +7 -0
- package/src/runtime/live-session/live-session-runtime.ts +1 -1
- package/src/runtime/model/model-fallback.ts +21 -1
- package/src/runtime/model/pi-args.ts +8 -10
- package/src/runtime/model/runtime-warmup.ts +2 -0
- package/src/runtime/output/output-validator.ts +10 -5
- package/src/runtime/plan-approval.ts +14 -1
- package/src/runtime/recovery/crash-recovery.ts +96 -49
- package/src/runtime/run-worker.ts +12 -1
- package/src/runtime/scheduling/global-worker-cap.ts +13 -6
- package/src/runtime/scheduling/run-coalesced-task-group.ts +27 -1
- package/src/runtime/scheduling/scheduler.ts +49 -13
- package/src/runtime/scheduling/semaphore.ts +148 -20
- package/src/runtime/scratchpad/README.md +1 -1
- package/src/runtime/scratchpad/protocol.ts +1 -1
- package/src/runtime/settings-store.ts +1 -1
- package/src/runtime/skill-instructions.ts +22 -0
- package/src/runtime/stale-reconciler.ts +85 -13
- package/src/runtime/subagent-manager.ts +43 -13
- package/src/runtime/task-runner/child-executor.ts +17 -6
- package/src/runtime/task-runner/post-execution.ts +44 -14
- package/src/runtime/task-runner/pre-execution.ts +26 -2
- package/src/runtime/task-runner/prompt-builder.ts +150 -45
- package/src/runtime/task-runner/retrieval-orchestrator.ts +36 -14
- package/src/runtime/task-runner.ts +28 -1
- package/src/runtime/team-runner.ts +42 -2
- package/src/runtime/verification/verification-worktree.ts +3 -0
- package/src/runtime/workspace-lock.ts +4 -1
- package/src/schema/config-schema.ts +14 -0
- package/src/schema/team-tool-schema.ts +17 -0
- package/src/state/atomic-write.ts +53 -0
- package/src/state/contracts.ts +109 -0
- package/src/state/coordination/locks.ts +191 -33
- package/src/state/coordination/mailbox.ts +147 -17
- package/src/state/crew-init.ts +87 -12
- package/src/state/event-log/cursor.ts +37 -1
- package/src/state/event-log/event-log-rotation.ts +72 -7
- package/src/state/stores/active-run-registry.ts +13 -1
- package/src/state/stores/state-store.ts +120 -23
- package/src/state/types.ts +4 -0
- package/src/ui/dashboard-panes/agents-pane.ts +22 -10
- package/src/ui/heartbeat-aggregator.ts +34 -0
- package/src/ui/inline-panel/index.ts +1 -1
- package/src/ui/keybinding-map.ts +22 -4
- package/src/ui/live-conversation-overlay.ts +6 -3
- package/src/ui/render-diff.ts +19 -6
- package/src/ui/run-dashboard.ts +109 -6
- package/src/ui/run-snapshot-cache.ts +27 -4
- package/src/ui/spinner.ts +26 -2
- package/src/ui/tool-progress-formatter.ts +2 -1
- package/src/ui/tool-renderers/brief-mode.ts +2 -1
- package/src/ui/tool-renderers/index.ts +3 -3
- package/src/ui/widget/task-list.ts +8 -5
- package/src/ui/widget/widget-model.ts +3 -1
- package/src/utils/incremental-reader.ts +11 -3
- package/src/utils/paths.ts +94 -12
- package/src/utils/project-markers.ts +40 -0
- package/src/worktree/worktree-manager.ts +206 -26
- package/tsconfig.json +2 -0
- package/workflows/distill.workflow.md +3 -3
- package/workflows/fast-fix.workflow.md +1 -1
- package/workflows/plan-execute.workflow.md +1 -1
- package/workflows/review.workflow.md +1 -1
- package/workflows/strict-fast-fix.workflow.md +1 -1
- package/docs/migration-v0.4-v0.5.md +0 -208
- package/docs/runtime-flow.md +0 -148
- package/src/ui/card-colors.ts +0 -168
- package/src/utils/completion-dedupe.ts +0 -63
- package/src/utils/resolve-shell.ts +0 -36
package/docs/runtime-flow.md
DELETED
|
@@ -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.
|
package/src/ui/card-colors.ts
DELETED
|
@@ -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
|
-
}
|