pi-crew 0.9.49 → 0.9.50
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 +209 -0
- package/dist/build-meta.json +50 -32
- package/dist/index.mjs +75 -42
- package/dist/index.mjs.map +4 -4
- package/package.json +1 -2
- package/skills/distill-persona/SKILL.md +83 -145
- package/skills/distill-persona/references/cross-skill-differentiation.md +12 -0
- package/skills/distill-persona/references/description-discipline.md +6 -0
- package/skills/distill-persona/references/diagnostic-path.md +25 -0
- package/skills/distill-persona/references/fidelity-rubric.md +19 -0
- package/skills/distill-persona/references/field-models.md +20 -0
- package/skills/distill-persona/references/optional-body-sections.md +9 -0
- package/skills/distill-persona/references/registry-routing.md +11 -0
- package/skills/distill-persona/references/self-upgrade-directive.md +20 -0
- package/skills/distill-persona/references/taste-principles.md +8 -0
- package/skills/distill-persona/references/topic-variant.md +13 -0
- package/skills/distill-persona/references/update-mode.md +7 -0
- package/skills/distill-persona/scripts/validate-run.mjs +297 -0
- package/skills/distill-software/SKILL.md +151 -90
- package/skills/research/SKILL.md +1 -1
- package/src/extension/crew-cleanup.ts +18 -1
- package/src/extension/crew-vibes/index.ts +11 -2
- package/src/extension/register.ts +1 -1
- package/src/extension/registration/command-registration.ts +1 -0
- package/src/extension/registration/commands.ts +7 -3
- package/src/extension/registration/lifecycle-handlers.ts +1 -3
- package/src/extension/registration/ui.ts +4 -0
- package/src/extension/registration/viewers.ts +3 -0
- package/src/extension/team-tool/run.ts +7 -6
- package/src/runtime/chain-runner.ts +3 -2
- package/src/runtime/pipeline-runner.ts +8 -7
- package/src/ui/live-run-sidebar.ts +2 -0
- package/src/ui/mascot.ts +11 -9
- package/src/ui/render-coalescer.ts +9 -0
- package/src/ui/run-snapshot-cache.ts +10 -11
- package/src/ui/terminal-status.ts +5 -0
- package/src/ui/widget/index.ts +3 -5
- package/src/ui/widget/widget-types.ts +0 -1
- package/assets/runner-spritesheet.png +0 -0
|
@@ -15,6 +15,7 @@ import { assertCleanLeaderAsync, findGitRootAsync } from "../../worktree/worktre
|
|
|
15
15
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars -- type-only import for TS inference
|
|
16
16
|
const _typeCheck: typeof ExecuteTeamRunFn = null as never as typeof ExecuteTeamRunFn;
|
|
17
17
|
|
|
18
|
+
import { errorMessage } from "../../utils/guards.ts";
|
|
18
19
|
import { logInternalError } from "../../utils/internal-error.ts";
|
|
19
20
|
import { resolveContainedPath, resolveRealContainedPath } from "../../utils/safe-paths.ts";
|
|
20
21
|
|
|
@@ -432,7 +433,7 @@ export async function handleRun(params: TeamToolParamsValue, ctx: TeamContext):
|
|
|
432
433
|
try {
|
|
433
434
|
await assertCleanLeaderAsync(gitRoot);
|
|
434
435
|
} catch (err) {
|
|
435
|
-
const msg =
|
|
436
|
+
const msg = errorMessage(err);
|
|
436
437
|
return result(
|
|
437
438
|
`${msg}\nCommit or stash changes before using worktree mode, or use workspaceMode: 'single'.`,
|
|
438
439
|
{ action: "run", status: "error" },
|
|
@@ -655,7 +656,7 @@ export async function handleRun(params: TeamToolParamsValue, ctx: TeamContext):
|
|
|
655
656
|
// Round-11 runtime fix: persist manifest with status=failed when runner throws
|
|
656
657
|
// (e.g., script timeout, script syntax error, async failure). Previously the
|
|
657
658
|
// manifest stayed at 'queued' indefinitely, leaving an orphan state file.
|
|
658
|
-
const failureReason =
|
|
659
|
+
const failureReason = errorMessage(runnerError);
|
|
659
660
|
const failedManifest = {
|
|
660
661
|
...dwfManifest,
|
|
661
662
|
status: "failed" as const,
|
|
@@ -856,13 +857,13 @@ export async function handleRun(params: TeamToolParamsValue, ctx: TeamContext):
|
|
|
856
857
|
workspaceId: ctx.sessionId ?? ctx.cwd,
|
|
857
858
|
});
|
|
858
859
|
} catch (waitError: unknown) {
|
|
859
|
-
const
|
|
860
|
+
const waitErrMsg = errorMessage(waitError);
|
|
860
861
|
return result(
|
|
861
862
|
[
|
|
862
863
|
`pi-crew run timed out or failed: ${updatedManifest.runId}`,
|
|
863
864
|
`Team: ${team.name}`,
|
|
864
865
|
`Workflow: ${workflow.name}`,
|
|
865
|
-
`Error: ${
|
|
866
|
+
`Error: ${waitErrMsg}`,
|
|
866
867
|
"",
|
|
867
868
|
`Check status with: team status runId=${updatedManifest.runId}`,
|
|
868
869
|
`State: ${updatedManifest.stateRoot}`,
|
|
@@ -970,13 +971,13 @@ export async function handleRun(params: TeamToolParamsValue, ctx: TeamContext):
|
|
|
970
971
|
workspaceId: ctx.sessionId ?? ctx.cwd,
|
|
971
972
|
});
|
|
972
973
|
} catch (waitError: unknown) {
|
|
973
|
-
const
|
|
974
|
+
const waitErrMsg = errorMessage(waitError);
|
|
974
975
|
return result(
|
|
975
976
|
[
|
|
976
977
|
`pi-crew run timed out or failed: ${updatedManifest.runId}`,
|
|
977
978
|
`Team: ${team.name}`,
|
|
978
979
|
`Workflow: ${workflow.name}`,
|
|
979
|
-
`Error: ${
|
|
980
|
+
`Error: ${waitErrMsg}`,
|
|
980
981
|
"",
|
|
981
982
|
`Check status with: team status runId=${updatedManifest.runId}`,
|
|
982
983
|
`State: ${updatedManifest.stateRoot}`,
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
* @see docs/pi-boomerang-integration-plan.md
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
+
import { errorMessage } from "../utils/guards.ts";
|
|
13
14
|
import type { ChainStep as DSLChainStep } from "./chain-parser.ts";
|
|
14
15
|
import { parseChainDSL } from "./chain-parser.ts";
|
|
15
16
|
import type { HandoffManager, HandoffSummary, TaskPacket, TaskResult } from "./handoff-manager.ts";
|
|
@@ -250,14 +251,14 @@ export class ChainRunner {
|
|
|
250
251
|
});
|
|
251
252
|
}
|
|
252
253
|
} catch (error) {
|
|
253
|
-
const
|
|
254
|
+
const errMsg = errorMessage(error);
|
|
254
255
|
|
|
255
256
|
stepResults.push({
|
|
256
257
|
step: i + 1,
|
|
257
258
|
name: step.name,
|
|
258
259
|
outcome: "failure",
|
|
259
260
|
duration: Date.now() - stepStart,
|
|
260
|
-
error:
|
|
261
|
+
error: errMsg,
|
|
261
262
|
});
|
|
262
263
|
|
|
263
264
|
// Stop chain on failure unless configured to continue
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { errors } from "../errors.ts";
|
|
2
2
|
import { appendEventAsync, flushEventLogBuffer } from "../state/event-log.ts";
|
|
3
|
+
import { errorMessage } from "../utils/guards.ts";
|
|
3
4
|
import type { WorkflowConfig } from "../workflows/workflow-config.ts";
|
|
4
5
|
import { mapConcurrent } from "./parallel-utils.ts";
|
|
5
6
|
|
|
@@ -166,26 +167,26 @@ export class PipelineRunner {
|
|
|
166
167
|
});
|
|
167
168
|
} catch (error) {
|
|
168
169
|
const duration = Date.now() - stageStartTime;
|
|
169
|
-
const
|
|
170
|
+
const errMsg = errorMessage(error);
|
|
170
171
|
|
|
171
172
|
if (effectiveStopOnError) {
|
|
172
173
|
stages.push({
|
|
173
174
|
name: stage.name,
|
|
174
175
|
status: "failed",
|
|
175
176
|
results: [],
|
|
176
|
-
error:
|
|
177
|
+
error: errMsg,
|
|
177
178
|
duration,
|
|
178
179
|
});
|
|
179
180
|
|
|
180
181
|
await appendEventAsync(eventsPath, {
|
|
181
182
|
type: "pipeline:stage_failed",
|
|
182
183
|
runId,
|
|
183
|
-
message: `Stage '${stage.name}' failed: ${
|
|
184
|
+
message: `Stage '${stage.name}' failed: ${errMsg}`,
|
|
184
185
|
data: {
|
|
185
186
|
stageIndex: i,
|
|
186
187
|
stageName: stage.name,
|
|
187
188
|
duration,
|
|
188
|
-
error:
|
|
189
|
+
error: errMsg,
|
|
189
190
|
},
|
|
190
191
|
});
|
|
191
192
|
|
|
@@ -193,7 +194,7 @@ export class PipelineRunner {
|
|
|
193
194
|
type: "pipeline:failed",
|
|
194
195
|
runId,
|
|
195
196
|
message: `Pipeline '${workflow.name}' failed at stage '${stage.name}'`,
|
|
196
|
-
data: { failedStage: stage.name, error:
|
|
197
|
+
data: { failedStage: stage.name, error: errMsg },
|
|
197
198
|
});
|
|
198
199
|
|
|
199
200
|
return {
|
|
@@ -207,7 +208,7 @@ export class PipelineRunner {
|
|
|
207
208
|
name: stage.name,
|
|
208
209
|
status: "failed",
|
|
209
210
|
results: [],
|
|
210
|
-
error:
|
|
211
|
+
error: errMsg,
|
|
211
212
|
duration,
|
|
212
213
|
});
|
|
213
214
|
|
|
@@ -219,7 +220,7 @@ export class PipelineRunner {
|
|
|
219
220
|
stageIndex: i,
|
|
220
221
|
stageName: stage.name,
|
|
221
222
|
duration,
|
|
222
|
-
error:
|
|
223
|
+
error: errMsg,
|
|
223
224
|
},
|
|
224
225
|
});
|
|
225
226
|
}
|
|
@@ -270,10 +270,12 @@ export class LiveRunSidebar {
|
|
|
270
270
|
if (isTerminal && !hasActiveAgents && !this.hasAutoClosed) {
|
|
271
271
|
const autoCloseMs = this.config?.autoCloseDashboardMs ?? 3000;
|
|
272
272
|
if (autoCloseMs > 0) {
|
|
273
|
+
if (this.autoCloseTimeout) clearTimeout(this.autoCloseTimeout);
|
|
273
274
|
this.autoCloseTimeout = setTimeout(() => {
|
|
274
275
|
this.hasAutoClosed = true;
|
|
275
276
|
this.done(undefined);
|
|
276
277
|
}, autoCloseMs);
|
|
278
|
+
this.autoCloseTimeout?.unref();
|
|
277
279
|
lines.push(line(`auto-close in ${Math.round(autoCloseMs / 1000)}s…`, w));
|
|
278
280
|
}
|
|
279
281
|
}
|
package/src/ui/mascot.ts
CHANGED
|
@@ -226,7 +226,7 @@ export class AnimatedMascot {
|
|
|
226
226
|
private tickTypewriter(): boolean {
|
|
227
227
|
const state = this.effectState;
|
|
228
228
|
if (state.pos === undefined) return true;
|
|
229
|
-
for (let i = 0; i <
|
|
229
|
+
for (let i = 0; i < 18; i++) {
|
|
230
230
|
const row = Math.floor(state.pos / ARMIN_WIDTH);
|
|
231
231
|
const x = state.pos % ARMIN_WIDTH;
|
|
232
232
|
if (row >= ARMIN_DISPLAY_HEIGHT) return true;
|
|
@@ -239,9 +239,11 @@ export class AnimatedMascot {
|
|
|
239
239
|
private tickScanline(): boolean {
|
|
240
240
|
const state = this.effectState;
|
|
241
241
|
if (state.row === undefined) return true;
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
242
|
+
for (let step = 0; step < 3; step++) {
|
|
243
|
+
if (state.row >= ARMIN_DISPLAY_HEIGHT) return true;
|
|
244
|
+
for (let x = 0; x < ARMIN_WIDTH; x++) this.currentArminGrid[state.row][x] = this.finalArminGrid[state.row][x];
|
|
245
|
+
state.row++;
|
|
246
|
+
}
|
|
245
247
|
return false;
|
|
246
248
|
}
|
|
247
249
|
|
|
@@ -264,7 +266,7 @@ export class AnimatedMascot {
|
|
|
264
266
|
break;
|
|
265
267
|
}
|
|
266
268
|
}
|
|
267
|
-
drop.y
|
|
269
|
+
drop.y += 3;
|
|
268
270
|
if (drop.y >= 0 && drop.y < ARMIN_DISPLAY_HEIGHT) {
|
|
269
271
|
if (targetRow >= 0 && drop.y >= targetRow) {
|
|
270
272
|
drop.settled = ARMIN_DISPLAY_HEIGHT - targetRow;
|
|
@@ -280,7 +282,7 @@ export class AnimatedMascot {
|
|
|
280
282
|
private tickFade(): boolean {
|
|
281
283
|
const state = this.effectState;
|
|
282
284
|
if (!state.positions || state.idx === undefined) return true;
|
|
283
|
-
for (let i = 0; i <
|
|
285
|
+
for (let i = 0; i < 54; i++) {
|
|
284
286
|
if (state.idx >= state.positions.length) return true;
|
|
285
287
|
const [row, x] = state.positions[state.idx];
|
|
286
288
|
this.currentArminGrid[row][x] = this.finalArminGrid[row][x];
|
|
@@ -299,7 +301,7 @@ export class AnimatedMascot {
|
|
|
299
301
|
for (let row = Math.max(0, top); row <= Math.min(ARMIN_DISPLAY_HEIGHT - 1, bottom); row++) {
|
|
300
302
|
for (let x = 0; x < ARMIN_WIDTH; x++) this.currentArminGrid[row][x] = this.finalArminGrid[row][x];
|
|
301
303
|
}
|
|
302
|
-
state.expansion
|
|
304
|
+
state.expansion += 3;
|
|
303
305
|
return state.expansion > ARMIN_DISPLAY_HEIGHT;
|
|
304
306
|
}
|
|
305
307
|
|
|
@@ -321,7 +323,7 @@ export class AnimatedMascot {
|
|
|
321
323
|
}
|
|
322
324
|
}
|
|
323
325
|
}
|
|
324
|
-
state.phase
|
|
326
|
+
state.phase += 3;
|
|
325
327
|
return false;
|
|
326
328
|
}
|
|
327
329
|
// Restore final grid in-place
|
|
@@ -336,7 +338,7 @@ export class AnimatedMascot {
|
|
|
336
338
|
private tickDissolve(): boolean {
|
|
337
339
|
const state = this.effectState;
|
|
338
340
|
if (!state.positions || state.idx === undefined) return true;
|
|
339
|
-
for (let i = 0; i <
|
|
341
|
+
for (let i = 0; i < 66; i++) {
|
|
340
342
|
if (state.idx >= state.positions.length) return true;
|
|
341
343
|
const [row, x] = state.positions[state.idx];
|
|
342
344
|
this.currentArminGrid[row][x] = this.finalArminGrid[row][x];
|
|
@@ -73,6 +73,15 @@ export class RenderCoalescer {
|
|
|
73
73
|
this.#timerId = null;
|
|
74
74
|
}
|
|
75
75
|
this.#pending = false;
|
|
76
|
+
const dropped = this.#dropped;
|
|
77
|
+
this.#dropped = 0;
|
|
78
|
+
if (dropped > 0) {
|
|
79
|
+
try {
|
|
80
|
+
this.#onDrop(dropped);
|
|
81
|
+
} catch {
|
|
82
|
+
/* drop callback errors are non-fatal */
|
|
83
|
+
}
|
|
84
|
+
}
|
|
76
85
|
this.#callback();
|
|
77
86
|
}
|
|
78
87
|
|
|
@@ -1005,17 +1005,16 @@ export function createRunSnapshotCache(cwd: string, options: RunSnapshotCacheOpt
|
|
|
1005
1005
|
const scheduleRefresh = (runId: string): void => {
|
|
1006
1006
|
const existing = pendingRefreshes.get(runId);
|
|
1007
1007
|
if (existing) clearTimeout(existing);
|
|
1008
|
-
|
|
1009
|
-
runId
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
);
|
|
1008
|
+
const timer = setTimeout(() => {
|
|
1009
|
+
pendingRefreshes.delete(runId);
|
|
1010
|
+
try {
|
|
1011
|
+
localRefreshIfStale(runId);
|
|
1012
|
+
} catch {
|
|
1013
|
+
/* best-effort; widget falls back gracefully */
|
|
1014
|
+
}
|
|
1015
|
+
}, INVAL_COALESCE_MS);
|
|
1016
|
+
timer.unref();
|
|
1017
|
+
pendingRefreshes.set(runId, timer);
|
|
1019
1018
|
};
|
|
1020
1019
|
const unsubState = runEventBus.onChannel("run:state", (event) => {
|
|
1021
1020
|
if (entries.has(event.runId)) scheduleRefresh(event.runId);
|
|
@@ -213,6 +213,9 @@ export function createTerminalStatusController(ctx: TerminalStatusUi): TerminalS
|
|
|
213
213
|
setTerminalTitle(ctx, buildIdleTitle());
|
|
214
214
|
scheduleIdleReassert(Math.min(delay * 2, IDLE_REASSERT_MAX_MS));
|
|
215
215
|
}, delay);
|
|
216
|
+
// Unref so this recursive backoff timer never keeps the event loop alive
|
|
217
|
+
// (critical on SIGTERM where dispose() is not reached → process hang).
|
|
218
|
+
state.idleTimer.unref?.();
|
|
216
219
|
};
|
|
217
220
|
|
|
218
221
|
return {
|
|
@@ -240,6 +243,8 @@ export function createTerminalStatusController(ctx: TerminalStatusUi): TerminalS
|
|
|
240
243
|
ghosttyClear();
|
|
241
244
|
}
|
|
242
245
|
}, COMPLETE_FLASH_MS);
|
|
246
|
+
// Unref so the one-shot flash clear never blocks process exit.
|
|
247
|
+
state.flashTimer.unref?.();
|
|
243
248
|
},
|
|
244
249
|
onIdle(): void {
|
|
245
250
|
if (state.destroyed) return;
|
package/src/ui/widget/index.ts
CHANGED
|
@@ -154,7 +154,7 @@ class CrewWidgetComponent implements WidgetComponent {
|
|
|
154
154
|
[...listLiveAgents()].some((h) => h.status === "running");
|
|
155
155
|
const animation = hasRunning ? `:spin=${spinnerBucket()}` : "";
|
|
156
156
|
|
|
157
|
-
|
|
157
|
+
const sig =
|
|
158
158
|
runs
|
|
159
159
|
.map(
|
|
160
160
|
(entry) =>
|
|
@@ -175,8 +175,8 @@ class CrewWidgetComponent implements WidgetComponent {
|
|
|
175
175
|
})
|
|
176
176
|
.join(","),
|
|
177
177
|
)
|
|
178
|
-
.join("|") + `|live:${liveSig}${animation}
|
|
179
|
-
|
|
178
|
+
.join("|") + `|live:${liveSig}${animation}`;
|
|
179
|
+
return sig;
|
|
180
180
|
}
|
|
181
181
|
|
|
182
182
|
private colorize(lines: string[], width: number): string[] {
|
|
@@ -342,8 +342,6 @@ export function stopCrewWidget(
|
|
|
342
342
|
state: CrewWidgetState,
|
|
343
343
|
config?: CrewUiConfig,
|
|
344
344
|
): void {
|
|
345
|
-
if (state.interval) clearInterval(state.interval);
|
|
346
|
-
state.interval = undefined;
|
|
347
345
|
if (ctx?.hasUI) {
|
|
348
346
|
const placement = config?.widgetPlacement ?? DEFAULT_UI.widgetPlacement;
|
|
349
347
|
ctx.ui.setStatus(STATUS_KEY, undefined);
|
|
Binary file
|