pi-crew 0.11.2 → 0.11.5

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 (69) hide show
  1. package/CHANGELOG.md +83 -1
  2. package/README.md +1 -1
  3. package/dist/index.mjs +492 -493
  4. package/docs/actions-reference.md +22 -0
  5. package/index.ts +9 -5
  6. package/package.json +12 -9
  7. package/scripts/analyze-run.mjs +6 -3
  8. package/skills/real-test-pi-crew/SKILL.md +20 -12
  9. package/src/extension/async-notifier.ts +32 -8
  10. package/src/extension/crew-input-router.ts +9 -2
  11. package/src/extension/plan-orchestrate.ts +19 -3
  12. package/src/extension/registration/commands/shared.ts +11 -10
  13. package/src/extension/registration/foreground-run-controller.ts +2 -2
  14. package/src/extension/registration/lazy-configurers.ts +4 -1
  15. package/src/extension/registration/lifecycle-handlers.ts +16 -10
  16. package/src/extension/registration/lifecycle.ts +2 -0
  17. package/src/extension/registration/observability.ts +27 -18
  18. package/src/extension/registration/team-tool.ts +9 -2
  19. package/src/extension/registration/wire-cross-extension.ts +2 -0
  20. package/src/extension/team-manager-command.ts +3 -2
  21. package/src/extension/team-tool/dispatch/index.ts +4 -3
  22. package/src/extension/team-tool/dispatch/status.ts +7 -3
  23. package/src/extension/team-tool/goal-wrap.ts +7 -0
  24. package/src/extension/team-tool/goal.ts +12 -0
  25. package/src/extension/team-tool/inspect.ts +90 -0
  26. package/src/extension/team-tool/parallel-dispatch.ts +16 -1
  27. package/src/extension/team-tool/run-intent.ts +1 -1
  28. package/src/extension/team-tool/run.ts +1 -1
  29. package/src/runtime/async-runner.ts +36 -0
  30. package/src/runtime/broker/crew-broker.ts +61 -62
  31. package/src/runtime/broker/delegate/delegate-event.ts +12 -8
  32. package/src/runtime/broker/protocol/connection-state.ts +10 -0
  33. package/src/runtime/broker/protocol/lock-busy.ts +76 -0
  34. package/src/runtime/delegate-spawn.ts +16 -2
  35. package/src/runtime/deterministic-ast.ts +14 -2
  36. package/src/runtime/goal-workflow/dynamic-workflow-context.ts +78 -58
  37. package/src/runtime/goal-workflow/dynamic-workflow-runner.ts +14 -2
  38. package/src/runtime/goal-workflow/goal-loop-runner.ts +32 -0
  39. package/src/runtime/group-join.ts +11 -3
  40. package/src/runtime/live-session/live-session-runtime.ts +1 -1
  41. package/src/runtime/model/runtime-warmup.ts +2 -0
  42. package/src/runtime/output/output-validator.ts +10 -5
  43. package/src/runtime/plan-approval.ts +14 -1
  44. package/src/runtime/recovery/crash-recovery.ts +80 -52
  45. package/src/runtime/stale-reconciler.ts +8 -0
  46. package/src/runtime/subagent-manager.ts +43 -13
  47. package/src/runtime/task-output-context.ts +11 -1
  48. package/src/runtime/task-runner/child-executor.ts +17 -6
  49. package/src/runtime/task-runner/post-execution.ts +46 -14
  50. package/src/runtime/task-runner/pre-execution.ts +29 -21
  51. package/src/runtime/task-runner/prompt-builder.ts +8 -0
  52. package/src/runtime/task-runner/retrieval-orchestrator.ts +36 -14
  53. package/src/runtime/task-runner.ts +7 -0
  54. package/src/runtime/team-runner.ts +4 -1
  55. package/src/runtime/verification/verification-worktree.ts +3 -0
  56. package/src/schema/team-tool-schema.ts +2 -1
  57. package/src/state/coordination/mailbox.ts +7 -2
  58. package/src/state/stores/state-store.ts +8 -1
  59. package/src/ui/dashboard-panes/agents-pane.ts +11 -8
  60. package/src/ui/inline-panel/index.ts +1 -1
  61. package/src/ui/render-diff.ts +19 -6
  62. package/src/ui/run-dashboard.ts +11 -1
  63. package/src/ui/run-snapshot-cache.ts +9 -3
  64. package/src/ui/widget/task-list.ts +8 -5
  65. package/src/ui/widget/widget-model.ts +3 -1
  66. package/tsconfig.json +2 -0
  67. package/src/ui/card-colors.ts +0 -168
  68. package/src/utils/completion-dedupe.ts +0 -63
  69. package/src/utils/resolve-shell.ts +0 -36
@@ -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
- }