@sayknow-cli/coding-agent 0.3.5 → 0.3.7
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/README.md +76 -0
- package/dist/types/capability/mcp.d.ts +2 -0
- package/dist/types/cli/local-provider-smoke.d.ts +59 -0
- package/dist/types/cli/notify-cli.d.ts +1 -1
- package/dist/types/cli/telegram-cli.d.ts +13 -0
- package/dist/types/cli/update-cli.d.ts +24 -1
- package/dist/types/commands/local-provider.d.ts +34 -0
- package/dist/types/commands/telegram.d.ts +22 -0
- package/dist/types/config/keybindings.d.ts +12 -6
- package/dist/types/config/model-registry.d.ts +5 -0
- package/dist/types/config/models-config-schema.d.ts +5 -0
- package/dist/types/config/settings-schema.d.ts +284 -4
- package/dist/types/config/settings.d.ts +7 -0
- package/dist/types/config/telegram-autostart.d.ts +14 -0
- package/dist/types/config/telegram-env-bridge.d.ts +18 -0
- package/dist/types/coordinator-mcp/server.d.ts +8 -0
- package/dist/types/defaults/skc-defaults.d.ts +7 -0
- package/dist/types/discovery/cursor.d.ts +4 -1
- package/dist/types/discovery/index.d.ts +0 -1
- package/dist/types/discovery/windsurf.d.ts +3 -1
- package/dist/types/extensibility/extensions/types.d.ts +11 -0
- package/dist/types/i18n/messages/en.d.ts +1 -0
- package/dist/types/modes/components/custom-editor.d.ts +6 -1
- package/dist/types/modes/components/custom-model-preset-wizard.d.ts +1 -1
- package/dist/types/modes/components/hook-selector.d.ts +5 -0
- package/dist/types/modes/components/model-selector.d.ts +2 -0
- package/dist/types/modes/components/settings-selector.d.ts +5 -1
- package/dist/types/modes/components/skill-message.d.ts +1 -0
- package/dist/types/modes/components/status-line/types.d.ts +5 -7
- package/dist/types/modes/components/status-line.d.ts +15 -3
- package/dist/types/modes/components/two-column-body.d.ts +9 -0
- package/dist/types/modes/components/welcome.d.ts +9 -1
- package/dist/types/modes/controllers/command-controller.d.ts +1 -0
- package/dist/types/modes/controllers/input-controller.d.ts +6 -1
- package/dist/types/modes/controllers/selector-controller.d.ts +10 -1
- package/dist/types/modes/interactive-mode.d.ts +2 -0
- package/dist/types/modes/prompt-action-autocomplete.d.ts +3 -0
- package/dist/types/modes/rpc/rpc-types.d.ts +1 -0
- package/dist/types/modes/shared/agent-wire/deep-interview-gate.d.ts +41 -7
- package/dist/types/modes/slash-command-visibility.d.ts +1 -0
- package/dist/types/modes/theme/theme.d.ts +1 -1
- package/dist/types/modes/tmux-scroll.d.ts +18 -0
- package/dist/types/modes/types.d.ts +5 -1
- package/dist/types/modes/utils/terminal-bell.d.ts +3 -0
- package/dist/types/notifications/daemon-paths.d.ts +9 -0
- package/dist/types/notifications/html-format.d.ts +2 -0
- package/dist/types/notifications/recent-activity.d.ts +5 -1
- package/dist/types/notifications/telegram-daemon-cli.d.ts +21 -4
- package/dist/types/notifications/telegram-daemon-control.d.ts +8 -0
- package/dist/types/notifications/telegram-daemon.d.ts +4 -10
- package/dist/types/notifications/telegram-reference.d.ts +4 -0
- package/dist/types/notifications/threaded-render.d.ts +1 -0
- package/dist/types/runtime-mcp/config-writer.d.ts +9 -0
- package/dist/types/runtime-mcp/config.d.ts +2 -0
- package/dist/types/runtime-mcp/loader.d.ts +2 -0
- package/dist/types/runtime-mcp/manager.d.ts +2 -0
- package/dist/types/runtime-mcp/types.d.ts +6 -0
- package/dist/types/sdk.d.ts +1 -1
- package/dist/types/skc-runtime/launch-tmux.d.ts +2 -2
- package/dist/types/skc-runtime/ralplan-runtime.d.ts +3 -2
- package/dist/types/skc-runtime/restricted-role-agent-bash.d.ts +1 -0
- package/dist/types/skc-runtime/session-state-sidecar.d.ts +5 -0
- package/dist/types/skc-runtime/state-writer.d.ts +1 -0
- package/dist/types/skc-runtime/team-runtime.d.ts +1 -1
- package/dist/types/skc-runtime/tmux-common.d.ts +12 -0
- package/dist/types/skc-runtime/ultragoal-guard.d.ts +7 -1
- package/dist/types/skc-runtime/ultragoal-runtime.d.ts +0 -4
- package/dist/types/slash-commands/types.d.ts +2 -0
- package/dist/types/system-prompt.d.ts +7 -0
- package/dist/types/tools/skill.d.ts +1 -1
- package/dist/types/utils/cmux-workspace.d.ts +52 -0
- package/dist/types/web/search/index.d.ts +4 -2
- package/dist/types/web/search/provider.d.ts +14 -0
- package/dist/types/web/search/providers/utils.d.ts +42 -13
- package/dist/types/web/search/render.d.ts +1 -0
- package/dist/types/workflow/workflow-intent-diff.d.ts +19 -0
- package/package.json +8 -8
- package/scripts/build-binary.ts +3 -0
- package/scripts/verify-insane-vendor.ts +0 -5
- package/src/capability/mcp.ts +2 -0
- package/src/cli/local-provider-smoke.ts +632 -0
- package/src/cli/notify-cli.ts +4 -2
- package/src/cli/telegram-cli.ts +156 -0
- package/src/cli/update-cli.ts +179 -13
- package/src/cli/web-search-cli.ts +5 -7
- package/src/cli.ts +20 -1
- package/src/commands/launch.ts +47 -2
- package/src/commands/local-provider.ts +62 -0
- package/src/commands/notify.ts +1 -1
- package/src/commands/ralplan.ts +1 -0
- package/src/commands/telegram.ts +47 -0
- package/src/config/keybindings.ts +15 -6
- package/src/config/mcp-schema.json +4 -0
- package/src/config/model-registry.ts +55 -1
- package/src/config/models-config-schema.ts +9 -0
- package/src/config/settings-schema.ts +256 -4
- package/src/config/settings.ts +11 -0
- package/src/config/telegram-autostart.ts +107 -0
- package/src/config/telegram-env-bridge.ts +92 -0
- package/src/coordinator-mcp/server.ts +276 -34
- package/src/defaults/skc/skills/ralplan/SKILL.md +29 -20
- package/src/defaults/skc/skills/ultragoal/SKILL.md +37 -112
- package/src/defaults/skc-defaults.ts +16 -0
- package/src/discovery/builtin.ts +12 -0
- package/src/discovery/cursor.ts +5 -84
- package/src/discovery/gemini.ts +3 -89
- package/src/discovery/index.ts +4 -1
- package/src/discovery/mcp-json.ts +11 -0
- package/src/discovery/opencode.ts +5 -122
- package/src/discovery/windsurf.ts +4 -82
- package/src/edit/read-file.ts +9 -1
- package/src/extensibility/extensions/runner.ts +2 -1
- package/src/extensibility/extensions/types.ts +11 -0
- package/src/extensibility/skills.ts +30 -2
- package/src/hooks/skill-state.ts +33 -73
- package/src/i18n/messages/de.ts +1 -0
- package/src/i18n/messages/en.ts +1 -0
- package/src/i18n/messages/es.ts +1 -0
- package/src/i18n/messages/fr.ts +1 -0
- package/src/i18n/messages/ja.settings.ts +28 -0
- package/src/i18n/messages/ja.ts +1 -0
- package/src/i18n/messages/ko.settings.ts +28 -0
- package/src/i18n/messages/ko.ts +1 -0
- package/src/i18n/messages/zh.settings.ts +28 -0
- package/src/i18n/messages/zh.ts +1 -0
- package/src/internal-urls/docs-index.generated.ts +12 -7
- package/src/main.ts +5 -2
- package/src/migrate/executor.ts +23 -6
- package/src/modes/acp/acp-agent.ts +7 -6
- package/src/modes/components/agent-dashboard.ts +3 -35
- package/src/modes/components/custom-editor.ts +26 -0
- package/src/modes/components/custom-model-preset-wizard.ts +30 -189
- package/src/modes/components/extensions/extension-dashboard.ts +2 -48
- package/src/modes/components/hook-selector.ts +21 -3
- package/src/modes/components/model-selector.ts +246 -11
- package/src/modes/components/session-observer-overlay.ts +3 -3
- package/src/modes/components/session-selector.ts +19 -9
- package/src/modes/components/settings-selector.ts +551 -11
- package/src/modes/components/skill-message.ts +18 -10
- package/src/modes/components/status-line/presets.ts +11 -0
- package/src/modes/components/status-line/segments.ts +16 -19
- package/src/modes/components/status-line/types.ts +6 -2
- package/src/modes/components/status-line.ts +281 -117
- package/src/modes/components/tree-selector.ts +28 -8
- package/src/modes/components/two-column-body.ts +36 -0
- package/src/modes/components/welcome.ts +165 -30
- package/src/modes/controllers/command-controller.ts +26 -2
- package/src/modes/controllers/event-controller.ts +102 -4
- package/src/modes/controllers/extension-ui-controller.ts +21 -2
- package/src/modes/controllers/input-controller.ts +214 -47
- package/src/modes/controllers/selector-controller.ts +35 -33
- package/src/modes/interactive-mode.ts +36 -30
- package/src/modes/prompt-action-autocomplete.ts +48 -8
- package/src/modes/rpc/rpc-types.ts +1 -0
- package/src/modes/shared/agent-wire/deep-interview-gate.ts +72 -15
- package/src/modes/shared/agent-wire/workflow-gate-schema.ts +20 -0
- package/src/modes/slash-command-visibility.ts +9 -0
- package/src/modes/theme/theme.ts +5 -1
- package/src/modes/tmux-scroll.ts +92 -0
- package/src/modes/types.ts +8 -1
- package/src/modes/utils/hotkeys-markdown.ts +5 -2
- package/src/modes/utils/terminal-bell.ts +48 -0
- package/src/modes/utils/ui-helpers.ts +76 -3
- package/src/notifications/daemon-paths.ts +22 -0
- package/src/notifications/html-format.ts +75 -1
- package/src/notifications/index.ts +32 -2
- package/src/notifications/lifecycle-control-runtime.ts +12 -3
- package/src/notifications/recent-activity.ts +32 -8
- package/src/notifications/telegram-daemon-cli.ts +125 -13
- package/src/notifications/telegram-daemon-control.ts +9 -0
- package/src/notifications/telegram-daemon.ts +88 -46
- package/src/notifications/telegram-reference.ts +36 -14
- package/src/notifications/threaded-render.ts +14 -6
- package/src/prompts/agents/architect.md +7 -4
- package/src/prompts/agents/critic.md +15 -6
- package/src/prompts/agents/planner.md +8 -3
- package/src/prompts/system/system-prompt.md +20 -2
- package/src/prompts/tools/bash.md +1 -1
- package/src/runtime-mcp/config-writer.ts +28 -0
- package/src/runtime-mcp/config.ts +7 -0
- package/src/runtime-mcp/loader.ts +3 -0
- package/src/runtime-mcp/manager.ts +3 -0
- package/src/runtime-mcp/types.ts +6 -0
- package/src/sdk.ts +10 -11
- package/src/session/agent-session.ts +103 -13
- package/src/session/session-manager.ts +58 -18
- package/src/skc-runtime/launch-tmux.ts +34 -8
- package/src/skc-runtime/launch-worktree.ts +16 -2
- package/src/skc-runtime/ralplan-runtime.ts +22 -6
- package/src/skc-runtime/restricted-role-agent-bash.ts +1 -0
- package/src/skc-runtime/session-state-sidecar.ts +167 -24
- package/src/skc-runtime/state-runtime.ts +8 -5
- package/src/skc-runtime/state-writer.ts +5 -5
- package/src/skc-runtime/team-runtime.ts +21 -6
- package/src/skc-runtime/tmux-common.ts +16 -0
- package/src/skc-runtime/tmux-sessions.ts +13 -9
- package/src/skc-runtime/ultragoal-guard.ts +150 -24
- package/src/skc-runtime/ultragoal-runtime.ts +16 -124
- package/src/skc-runtime/workflow-manifest.generated.json +0 -8
- package/src/skc-runtime/workflow-manifest.ts +0 -1
- package/src/slash-commands/builtin-registry.ts +136 -23
- package/src/slash-commands/types.ts +2 -0
- package/src/system-prompt.ts +8 -0
- package/src/task/executor.ts +5 -0
- package/src/tools/ast-grep.ts +10 -7
- package/src/tools/bash.ts +39 -14
- package/src/tools/index.ts +8 -1
- package/src/tools/skill.ts +1 -1
- package/src/utils/cmux-workspace.ts +233 -0
- package/src/utils/title-generator.ts +2 -0
- package/src/web/kagi.ts +1 -1
- package/src/web/parallel.ts +2 -2
- package/src/web/search/index.ts +137 -52
- package/src/web/search/provider.ts +80 -0
- package/src/web/search/providers/anthropic.ts +1 -1
- package/src/web/search/providers/brave.ts +1 -1
- package/src/web/search/providers/codex.ts +1 -1
- package/src/web/search/providers/duckduckgo.ts +1 -1
- package/src/web/search/providers/exa.ts +1 -1
- package/src/web/search/providers/gemini.ts +7 -3
- package/src/web/search/providers/jina.ts +1 -1
- package/src/web/search/providers/kimi.ts +6 -1
- package/src/web/search/providers/openai-compatible.ts +1 -1
- package/src/web/search/providers/parallel.ts +1 -1
- package/src/web/search/providers/perplexity.ts +2 -2
- package/src/web/search/providers/searxng.ts +1 -1
- package/src/web/search/providers/synthetic.ts +1 -1
- package/src/web/search/providers/tavily.ts +1 -1
- package/src/web/search/providers/utils.ts +67 -17
- package/src/web/search/providers/xai.ts +1 -1
- package/src/web/search/providers/zai.ts +1 -1
- package/src/web/search/render.ts +3 -0
- package/src/workflow/workflow-intent-diff.ts +199 -0
- package/vendor/insane-search/MANIFEST.json +1 -3
- package/vendor/insane-search/engine/tests/test_u1.py +0 -28
- package/vendor/insane-search/engine/transport.py +2 -3
- package/vendor/insane-search/engine/validators.py +0 -14
- package/src/defaults/skc/rules/ponytail.md +0 -68
|
@@ -69,9 +69,12 @@ function isKnownUltragoalObjective(currentObjective: string): boolean {
|
|
|
69
69
|
);
|
|
70
70
|
}
|
|
71
71
|
|
|
72
|
-
async function ultragoalReadPaths(
|
|
73
|
-
|
|
74
|
-
|
|
72
|
+
async function ultragoalReadPaths(
|
|
73
|
+
cwd: string,
|
|
74
|
+
options: { sessionId?: string | null } = {},
|
|
75
|
+
): Promise<{ paths: UltragoalPaths; sessionId: string | null }> {
|
|
76
|
+
const explicitSessionId = options.sessionId?.trim() || process.env.SKC_SESSION_ID?.trim();
|
|
77
|
+
if (explicitSessionId) return { paths: getUltragoalPaths(cwd, explicitSessionId), sessionId: explicitSessionId };
|
|
75
78
|
try {
|
|
76
79
|
const session = await resolveSkcSessionForRead(cwd, { envSessionId: process.env.SKC_SESSION_ID });
|
|
77
80
|
return { paths: getUltragoalPaths(cwd, session.skcSessionId), sessionId: session.skcSessionId };
|
|
@@ -250,13 +253,6 @@ export function validateCompletionReceipt(input: {
|
|
|
250
253
|
goalId: input.goal.id,
|
|
251
254
|
};
|
|
252
255
|
}
|
|
253
|
-
if (hashStructuredValue(event.skcGoalJson) !== receipt.skcGoalSnapshotHash) {
|
|
254
|
-
return {
|
|
255
|
-
state: "active_stale_receipt",
|
|
256
|
-
message: `Ultragoal ${input.goal.id} receipt goal({"op":"get"}) snapshot hash does not match ledger.`,
|
|
257
|
-
goalId: input.goal.id,
|
|
258
|
-
};
|
|
259
|
-
}
|
|
260
256
|
if (input.goal.updatedAt !== receipt.verifiedAt) {
|
|
261
257
|
return {
|
|
262
258
|
state: "active_stale_receipt",
|
|
@@ -273,15 +269,28 @@ export function validateCompletionReceipt(input: {
|
|
|
273
269
|
goalId: input.goal.id,
|
|
274
270
|
};
|
|
275
271
|
}
|
|
276
|
-
const
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
}
|
|
272
|
+
for (const priorGoal of requiredGoals(input.plan)) {
|
|
273
|
+
if (priorGoal.id === input.goal.id) continue;
|
|
274
|
+
if (!priorGoal.completionVerification) {
|
|
275
|
+
return {
|
|
276
|
+
state: "active_missing_receipt",
|
|
277
|
+
message: `Ultragoal final receipt is missing per-goal evidence for: ${priorGoal.id}.`,
|
|
278
|
+
goalId: input.goal.id,
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
const priorDiagnostic = validateCompletionReceipt({
|
|
282
|
+
plan: input.plan,
|
|
283
|
+
ledger: input.ledger,
|
|
284
|
+
goal: priorGoal,
|
|
285
|
+
receiptKind: "per-goal",
|
|
286
|
+
});
|
|
287
|
+
if (priorDiagnostic.state !== "active_verified_complete") {
|
|
288
|
+
return {
|
|
289
|
+
state: priorDiagnostic.state,
|
|
290
|
+
message: `Ultragoal final receipt requires a valid per-goal receipt for ${priorGoal.id}: ${priorDiagnostic.message}`,
|
|
291
|
+
goalId: input.goal.id,
|
|
292
|
+
};
|
|
293
|
+
}
|
|
285
294
|
}
|
|
286
295
|
}
|
|
287
296
|
return {
|
|
@@ -372,11 +381,128 @@ export async function readUltragoalVerificationState(input: {
|
|
|
372
381
|
return receiptDiagnostic;
|
|
373
382
|
}
|
|
374
383
|
|
|
375
|
-
export async function
|
|
384
|
+
export async function verifyUltragoalDurableCompletionState(input: {
|
|
385
|
+
cwd: string;
|
|
386
|
+
sessionId?: string | null;
|
|
387
|
+
}): Promise<UltragoalGuardDiagnostic> {
|
|
388
|
+
let paths: UltragoalPaths;
|
|
389
|
+
let sessionId: string | null;
|
|
390
|
+
try {
|
|
391
|
+
({ paths, sessionId } = await ultragoalReadPaths(input.cwd, { sessionId: input.sessionId }));
|
|
392
|
+
} catch (error) {
|
|
393
|
+
return {
|
|
394
|
+
state: "unreadable_fail_closed",
|
|
395
|
+
message: `Unable to resolve durable Ultragoal state: ${error instanceof Error ? error.message : String(error)}`,
|
|
396
|
+
};
|
|
397
|
+
}
|
|
398
|
+
if (sessionId === null)
|
|
399
|
+
return { state: "inactive", message: "No active SKC session resolved; ultragoal is inactive." };
|
|
400
|
+
try {
|
|
401
|
+
await fs.stat(paths.dir);
|
|
402
|
+
} catch (error) {
|
|
403
|
+
if (isEnoent(error)) return { state: "inactive", message: "No durable .skc/ultragoal state exists." };
|
|
404
|
+
return {
|
|
405
|
+
state: "unreadable_fail_closed",
|
|
406
|
+
message: `Durable .skc/ultragoal state is present but unreadable: ${error instanceof Error ? error.message : String(error)}`,
|
|
407
|
+
};
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
let plan: UltragoalPlan | null;
|
|
411
|
+
let ledger: UltragoalLedgerEvent[];
|
|
412
|
+
try {
|
|
413
|
+
plan = await readUltragoalPlan(input.cwd, sessionId);
|
|
414
|
+
ledger = await readUltragoalLedger(input.cwd, sessionId);
|
|
415
|
+
} catch (error) {
|
|
416
|
+
return {
|
|
417
|
+
state: "unreadable_fail_closed",
|
|
418
|
+
message: `Unable to read durable Ultragoal state: ${error instanceof Error ? error.message : String(error)}`,
|
|
419
|
+
};
|
|
420
|
+
}
|
|
421
|
+
if (!plan) return { state: "inactive", message: "No Ultragoal plan exists." };
|
|
422
|
+
|
|
423
|
+
if (plan.goals.some(goal => goal.status === "review_blocked")) {
|
|
424
|
+
return {
|
|
425
|
+
state: "active_review_blocked_recorded",
|
|
426
|
+
message: "Ultragoal has recorded review blockers; complete blocker work and rerun verification.",
|
|
427
|
+
};
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
const runState = getUltragoalRunCompletionState(plan);
|
|
431
|
+
if (runState.incompleteGoals.some(goal => goal.status === "blocked" || goal.status === "failed")) {
|
|
432
|
+
return {
|
|
433
|
+
state: "active_dirty_quality_gate",
|
|
434
|
+
message: "Ultragoal has blocked or failed goals; record blockers or rerun verification.",
|
|
435
|
+
};
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
if (plan.skcGoalMode === "per-story") {
|
|
439
|
+
const incomplete = requiredGoals(plan).filter(goal => goal.status !== "complete");
|
|
440
|
+
if (incomplete.length > 0) {
|
|
441
|
+
return {
|
|
442
|
+
state: "active_missing_receipt",
|
|
443
|
+
message: `Ultragoal per-story completion requires all required stories to be complete; incomplete: ${incomplete.map(goal => goal.id).join(", ")}.`,
|
|
444
|
+
goalId: incomplete[0]?.id,
|
|
445
|
+
};
|
|
446
|
+
}
|
|
447
|
+
for (const goal of requiredGoals(plan)) {
|
|
448
|
+
const diagnostic = validateCompletionReceipt({
|
|
449
|
+
plan,
|
|
450
|
+
ledger,
|
|
451
|
+
goal,
|
|
452
|
+
receiptKind: "per-goal",
|
|
453
|
+
});
|
|
454
|
+
if (diagnostic.state !== "active_verified_complete") return diagnostic;
|
|
455
|
+
}
|
|
456
|
+
return {
|
|
457
|
+
state: "active_verified_complete",
|
|
458
|
+
message: "Ultragoal per-story run is verified complete.",
|
|
459
|
+
};
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
const ask = await isUltragoalAskBlocked(input.cwd, { sessionId });
|
|
463
|
+
if (!ask.active) {
|
|
464
|
+
return {
|
|
465
|
+
state: "active_verified_complete",
|
|
466
|
+
message: ask.reason,
|
|
467
|
+
goalId: ask.goalIds?.at(0),
|
|
468
|
+
};
|
|
469
|
+
}
|
|
470
|
+
if (ask.source === "durable_state_unreadable") {
|
|
471
|
+
return {
|
|
472
|
+
state: "unreadable_fail_closed",
|
|
473
|
+
message: ask.reason,
|
|
474
|
+
goalId: ask.goalIds?.at(0),
|
|
475
|
+
};
|
|
476
|
+
}
|
|
477
|
+
if (ask.reason.includes("recorded review blockers")) {
|
|
478
|
+
return {
|
|
479
|
+
state: "active_review_blocked_recorded",
|
|
480
|
+
message: ask.reason,
|
|
481
|
+
goalId: ask.goalIds?.at(0),
|
|
482
|
+
};
|
|
483
|
+
}
|
|
484
|
+
if (ask.reason.includes("incomplete required goals")) {
|
|
485
|
+
return {
|
|
486
|
+
state: "active_missing_final_receipt",
|
|
487
|
+
message: ask.reason,
|
|
488
|
+
goalId: ask.goalIds?.at(0),
|
|
489
|
+
};
|
|
490
|
+
}
|
|
491
|
+
return {
|
|
492
|
+
state: "active_missing_final_receipt",
|
|
493
|
+
message: ask.reason,
|
|
494
|
+
goalId: ask.goalIds?.at(0),
|
|
495
|
+
};
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
export async function isUltragoalAskBlocked(
|
|
499
|
+
cwd: string,
|
|
500
|
+
options: { sessionId?: string | null } = {},
|
|
501
|
+
): Promise<UltragoalAskBlockDiagnostic> {
|
|
376
502
|
let paths: UltragoalPaths;
|
|
377
503
|
let sessionId: string | null;
|
|
378
504
|
try {
|
|
379
|
-
({ paths, sessionId } = await ultragoalReadPaths(cwd));
|
|
505
|
+
({ paths, sessionId } = await ultragoalReadPaths(cwd, options));
|
|
380
506
|
} catch (error) {
|
|
381
507
|
return activeAskDiagnostic({
|
|
382
508
|
reason: `Unable to resolve durable Ultragoal state: ${error instanceof Error ? error.message : String(error)}`,
|
|
@@ -594,8 +720,8 @@ export async function assertCanCompleteCurrentGoal(input: {
|
|
|
594
720
|
sessionId?: string | null;
|
|
595
721
|
}): Promise<void> {
|
|
596
722
|
if (!input.cwd) return;
|
|
597
|
-
const diagnostic = await
|
|
598
|
-
if (["inactive", "
|
|
723
|
+
const diagnostic = await verifyUltragoalDurableCompletionState(input);
|
|
724
|
+
if (["inactive", "active_verified_complete"].includes(diagnostic.state)) return;
|
|
599
725
|
const nudge = await consumeUltragoalNudge({
|
|
600
726
|
cwd: input.cwd,
|
|
601
727
|
surface: "premature_complete",
|
|
@@ -604,7 +730,7 @@ export async function assertCanCompleteCurrentGoal(input: {
|
|
|
604
730
|
});
|
|
605
731
|
if (nudge.nudged) throw new Error(nudge.message);
|
|
606
732
|
throw new Error(
|
|
607
|
-
`${diagnostic.message} Run
|
|
733
|
+
`${diagnostic.message} Run \`skc ultragoal checkpoint --status complete --quality-gate-json <file>\` first, or record review blockers and rerun verification.`,
|
|
608
734
|
);
|
|
609
735
|
}
|
|
610
736
|
|
|
@@ -2,12 +2,10 @@ import * as crypto from "node:crypto";
|
|
|
2
2
|
import * as os from "node:os";
|
|
3
3
|
import * as path from "node:path";
|
|
4
4
|
import { inflateSync } from "node:zlib";
|
|
5
|
-
import { normalizeGoal } from "../goals/state";
|
|
6
|
-
import { buildSessionContext, loadEntriesFromFile, type SessionEntry } from "../session/session-manager";
|
|
7
5
|
import type { WorkflowHudSummary } from "../skill-state/active-state";
|
|
8
6
|
import { buildUltragoalHudSummary as buildWorkflowUltragoalHudSummary } from "../skill-state/workflow-hud";
|
|
9
7
|
import { renderCliWriteReceipt } from "./cli-write-receipt";
|
|
10
|
-
import { DEFAULT_ULTRAGOAL_OBJECTIVE
|
|
8
|
+
import { DEFAULT_ULTRAGOAL_OBJECTIVE } from "./goal-mode-request";
|
|
11
9
|
import { sessionUltragoalDir, skcRoot } from "./session-layout";
|
|
12
10
|
import { resolveSkcSessionForRead, resolveSkcSessionForWrite, writeSessionActivityMarker } from "./session-resolution";
|
|
13
11
|
import { renderUltragoalStatusMarkdown } from "./state-renderer";
|
|
@@ -68,7 +66,6 @@ export interface UltragoalCompletionVerification {
|
|
|
68
66
|
skcGoalMode: UltragoalSkcGoalMode;
|
|
69
67
|
skcObjective: string;
|
|
70
68
|
qualityGateHash: string;
|
|
71
|
-
skcGoalSnapshotHash: string;
|
|
72
69
|
planGeneration: string;
|
|
73
70
|
basis: {
|
|
74
71
|
planHashBeforeCheckpoint: string;
|
|
@@ -174,10 +171,9 @@ const ACCEPTED_PROOF_STATUSES = new Set([COVERED_STATUS, "passed", "verified"]);
|
|
|
174
171
|
const MIN_SUBSTANTIVE_EVIDENCE_WORDS = 5;
|
|
175
172
|
const MIN_SUBSTANTIVE_EVIDENCE_CHARS = 32;
|
|
176
173
|
|
|
177
|
-
const SKC_GOAL_SNAPSHOT_MAX_AGE_MILLISECONDS = 10 * 60 * 1000;
|
|
178
|
-
const SKC_GOAL_SNAPSHOT_MAX_FUTURE_SKEW_MILLISECONDS = 60 * 1000;
|
|
179
|
-
|
|
180
174
|
const SCHEDULABLE_STATUSES = new Set<UltragoalGoalStatus>(["pending", "active", "failed"]);
|
|
175
|
+
const COMPLETE_CHECKPOINT_ALLOWED_PRE_STATUSES = new Set<UltragoalGoalStatus>(["active", "failed"]);
|
|
176
|
+
|
|
181
177
|
const NATIVE_STEERING_KINDS = [
|
|
182
178
|
"add_subgoal",
|
|
183
179
|
"split_subgoal",
|
|
@@ -570,7 +566,6 @@ function buildCompletionReceipt(input: {
|
|
|
570
566
|
receiptKind: UltragoalReceiptKind;
|
|
571
567
|
beforeStatus: UltragoalGoalStatus;
|
|
572
568
|
qualityGateJson: JsonObject;
|
|
573
|
-
skcGoalJson: JsonObject;
|
|
574
569
|
now: string;
|
|
575
570
|
checkpointLedgerEventId: string;
|
|
576
571
|
}): UltragoalCompletionVerification {
|
|
@@ -593,7 +588,6 @@ function buildCompletionReceipt(input: {
|
|
|
593
588
|
skcGoalMode: input.plan.skcGoalMode,
|
|
594
589
|
skcObjective: input.plan.skcObjective,
|
|
595
590
|
qualityGateHash: hashStructuredValue(input.qualityGateJson),
|
|
596
|
-
skcGoalSnapshotHash: hashStructuredValue(input.skcGoalJson),
|
|
597
591
|
planGeneration: generation.planGeneration,
|
|
598
592
|
basis: generation.basis,
|
|
599
593
|
checkpointLedgerEventId: input.checkpointLedgerEventId,
|
|
@@ -2470,93 +2464,24 @@ async function readRequiredCompletionQualityGate(
|
|
|
2470
2464
|
return gate;
|
|
2471
2465
|
}
|
|
2472
2466
|
|
|
2473
|
-
function
|
|
2474
|
-
if (
|
|
2475
|
-
if (
|
|
2476
|
-
const trimmed = value.trim();
|
|
2477
|
-
if (/^\d+$/.test(trimmed)) {
|
|
2478
|
-
const parsed = Number.parseInt(trimmed, 10);
|
|
2479
|
-
return Number.isFinite(parsed) ? parsed : null;
|
|
2480
|
-
}
|
|
2481
|
-
const parsed = Date.parse(trimmed);
|
|
2482
|
-
return Number.isFinite(parsed) ? parsed : null;
|
|
2483
|
-
}
|
|
2484
|
-
|
|
2485
|
-
function singleSessionLeafId(entries: readonly SessionEntry[]): string | undefined {
|
|
2486
|
-
if (entries.length === 0) return undefined;
|
|
2487
|
-
const parentIds = new Set(
|
|
2488
|
-
entries.map(entry => entry.parentId).filter((parentId): parentId is string => typeof parentId === "string"),
|
|
2489
|
-
);
|
|
2490
|
-
const leafIds = entries.map(entry => entry.id).filter(id => !parentIds.has(id));
|
|
2491
|
-
return leafIds.length === 1 ? leafIds[0] : undefined;
|
|
2492
|
-
}
|
|
2493
|
-
|
|
2494
|
-
async function readCurrentSessionSkcGoalSnapshot(): Promise<unknown | undefined> {
|
|
2495
|
-
const sessionFile = process.env[SKC_SESSION_FILE_ENV]?.trim();
|
|
2496
|
-
if (!sessionFile) return undefined;
|
|
2497
|
-
const fileEntries = await loadEntriesFromFile(sessionFile);
|
|
2498
|
-
const entries = fileEntries.filter((entry): entry is SessionEntry => entry.type !== "session");
|
|
2499
|
-
const leafId = singleSessionLeafId(entries);
|
|
2500
|
-
if (!leafId) return undefined;
|
|
2501
|
-
const context = buildSessionContext(entries, leafId);
|
|
2502
|
-
if (context.mode !== "goal" && context.mode !== "goal_paused") return undefined;
|
|
2503
|
-
const goal = normalizeGoal(context.modeData?.goal);
|
|
2504
|
-
return goal ? { goal } : undefined;
|
|
2505
|
-
}
|
|
2506
|
-
async function readSkcGoalSnapshot(input: {
|
|
2507
|
-
cwd: string;
|
|
2508
|
-
value: string | undefined;
|
|
2509
|
-
plan: UltragoalPlan;
|
|
2510
|
-
goal?: UltragoalGoal;
|
|
2511
|
-
required: boolean;
|
|
2512
|
-
errorPrefix: string;
|
|
2513
|
-
allowCompletedLegacyBlocker?: boolean;
|
|
2514
|
-
}): Promise<unknown> {
|
|
2515
|
-
const snapshot = input.value?.trim()
|
|
2516
|
-
? await readStructuredValue(input.cwd, input.value)
|
|
2517
|
-
: await readCurrentSessionSkcGoalSnapshot();
|
|
2518
|
-
if (snapshot === undefined) {
|
|
2519
|
-
if (!input.required) return undefined;
|
|
2520
|
-
throw new Error(
|
|
2521
|
-
`${input.errorPrefix} require an active SKC goal-mode snapshot from the current session or --skc-goal-json; this is the SKC goal-mode receipt, not the .skc/ultragoal/goals.json goal record`,
|
|
2522
|
-
);
|
|
2523
|
-
}
|
|
2524
|
-
const snapshotObject = qualityGateObject(snapshot);
|
|
2525
|
-
const detailsObject = qualityGateObject(snapshotObject?.details);
|
|
2526
|
-
const goalObject = qualityGateObject(snapshotObject?.goal) ?? qualityGateObject(detailsObject?.goal);
|
|
2527
|
-
if (!goalObject)
|
|
2528
|
-
throw new Error(
|
|
2529
|
-
`${input.errorPrefix} require --skc-goal-json with a goal object from goal({"op":"get"}); pass the active SKC goal-mode snapshot, not the .skc/ultragoal/goals.json goal record`,
|
|
2530
|
-
);
|
|
2531
|
-
const updatedAt = snapshotUpdatedAtMilliseconds(goalObject.updatedAt);
|
|
2532
|
-
if (!updatedAt)
|
|
2467
|
+
function validateCompleteCheckpointTargetGoal(goal: UltragoalGoal): void {
|
|
2468
|
+
if (COMPLETE_CHECKPOINT_ALLOWED_PRE_STATUSES.has(goal.status)) return;
|
|
2469
|
+
if (goal.status === "pending") {
|
|
2533
2470
|
throw new Error(
|
|
2534
|
-
|
|
2471
|
+
`Cannot checkpoint ${goal.id} as complete while its durable goals.json status is pending; start the goal before completing it.`,
|
|
2535
2472
|
);
|
|
2536
|
-
const nowMilliseconds = Date.now();
|
|
2537
|
-
if (updatedAt < nowMilliseconds - SKC_GOAL_SNAPSHOT_MAX_AGE_MILLISECONDS) {
|
|
2538
|
-
throw new Error(`${input.errorPrefix} require a fresh --skc-goal-json snapshot`);
|
|
2539
|
-
}
|
|
2540
|
-
if (updatedAt > nowMilliseconds + SKC_GOAL_SNAPSHOT_MAX_FUTURE_SKEW_MILLISECONDS) {
|
|
2541
|
-
throw new Error(`${input.errorPrefix} require --skc-goal-json goal.updatedAt that is not from the future`);
|
|
2542
|
-
}
|
|
2543
|
-
const objective = typeof goalObject.objective === "string" ? goalObject.objective : "";
|
|
2544
|
-
const expectedObjectives = new Set([input.plan.skcObjective, ...(input.plan.skcObjectiveAliases ?? [])]);
|
|
2545
|
-
if (input.plan.skcGoalMode === "per-story" && input.goal?.objective) {
|
|
2546
|
-
expectedObjectives.add(input.goal.objective);
|
|
2547
|
-
}
|
|
2548
|
-
if (input.allowCompletedLegacyBlocker && goalObject.status === "complete" && !expectedObjectives.has(objective)) {
|
|
2549
|
-
return snapshot;
|
|
2550
2473
|
}
|
|
2551
|
-
if (
|
|
2474
|
+
if (goal.status === "complete") {
|
|
2552
2475
|
throw new Error(
|
|
2553
|
-
|
|
2476
|
+
`Cannot checkpoint ${goal.id} as complete with different evidence because its durable goals.json status is already complete.`,
|
|
2554
2477
|
);
|
|
2555
2478
|
}
|
|
2556
|
-
if (
|
|
2557
|
-
throw new Error(
|
|
2479
|
+
if (goal.status === "superseded") {
|
|
2480
|
+
throw new Error(`Cannot checkpoint ${goal.id} as complete because its durable goals.json status is superseded.`);
|
|
2558
2481
|
}
|
|
2559
|
-
|
|
2482
|
+
throw new Error(
|
|
2483
|
+
`Cannot checkpoint ${goal.id} as complete while its durable goals.json status is ${goal.status}; only active or retryable failed goals can be completed.`,
|
|
2484
|
+
);
|
|
2560
2485
|
}
|
|
2561
2486
|
|
|
2562
2487
|
export async function checkpointUltragoalGoal(input: {
|
|
@@ -2564,7 +2489,6 @@ export async function checkpointUltragoalGoal(input: {
|
|
|
2564
2489
|
goalId: string;
|
|
2565
2490
|
status: UltragoalGoalStatus;
|
|
2566
2491
|
evidence: string;
|
|
2567
|
-
skcGoalJson?: string;
|
|
2568
2492
|
qualityGateJson?: string;
|
|
2569
2493
|
}): Promise<UltragoalPlan> {
|
|
2570
2494
|
const plan = await readUltragoalPlan(input.cwd);
|
|
@@ -2593,6 +2517,7 @@ export async function checkpointUltragoalGoal(input: {
|
|
|
2593
2517
|
// instead of silently dropping it.
|
|
2594
2518
|
return plan;
|
|
2595
2519
|
}
|
|
2520
|
+
if (input.status === "complete") validateCompleteCheckpointTargetGoal(goal);
|
|
2596
2521
|
const changeSet = input.status === "complete" ? await computeCheckpointChangeSet(input.cwd) : undefined;
|
|
2597
2522
|
const qualityGateJson =
|
|
2598
2523
|
input.status === "complete"
|
|
@@ -2615,25 +2540,6 @@ export async function checkpointUltragoalGoal(input: {
|
|
|
2615
2540
|
}
|
|
2616
2541
|
}
|
|
2617
2542
|
const receiptKind = input.status === "complete" ? chooseReceiptKind(plan, goal, input.status) : null;
|
|
2618
|
-
const skcGoalJson =
|
|
2619
|
-
input.status === "complete"
|
|
2620
|
-
? await readSkcGoalSnapshot({
|
|
2621
|
-
cwd: input.cwd,
|
|
2622
|
-
value: input.skcGoalJson,
|
|
2623
|
-
plan,
|
|
2624
|
-
goal,
|
|
2625
|
-
required: true,
|
|
2626
|
-
errorPrefix: "complete checkpoints",
|
|
2627
|
-
})
|
|
2628
|
-
: await readSkcGoalSnapshot({
|
|
2629
|
-
cwd: input.cwd,
|
|
2630
|
-
value: input.skcGoalJson,
|
|
2631
|
-
plan,
|
|
2632
|
-
goal,
|
|
2633
|
-
required: false,
|
|
2634
|
-
errorPrefix: `${input.status} checkpoints`,
|
|
2635
|
-
allowCompletedLegacyBlocker: input.status === "blocked",
|
|
2636
|
-
});
|
|
2637
2543
|
const pendingCheckpointEventId = crypto.randomUUID();
|
|
2638
2544
|
if (input.status === "complete" && receiptKind && qualityGateJson && !Array.isArray(qualityGateJson)) {
|
|
2639
2545
|
goal.completionVerification = buildCompletionReceipt({
|
|
@@ -2643,7 +2549,6 @@ export async function checkpointUltragoalGoal(input: {
|
|
|
2643
2549
|
receiptKind,
|
|
2644
2550
|
beforeStatus,
|
|
2645
2551
|
qualityGateJson: qualityGateJson as JsonObject,
|
|
2646
|
-
skcGoalJson: skcGoalJson as JsonObject,
|
|
2647
2552
|
now,
|
|
2648
2553
|
checkpointLedgerEventId: pendingCheckpointEventId,
|
|
2649
2554
|
});
|
|
@@ -2662,7 +2567,6 @@ export async function checkpointUltragoalGoal(input: {
|
|
|
2662
2567
|
goalId: goal.id,
|
|
2663
2568
|
status: input.status,
|
|
2664
2569
|
evidence,
|
|
2665
|
-
skcGoalJson,
|
|
2666
2570
|
qualityGateJson,
|
|
2667
2571
|
completionVerification: goal.completionVerification,
|
|
2668
2572
|
});
|
|
@@ -2682,7 +2586,6 @@ export async function checkpointAndContinueUltragoalGoal(input: {
|
|
|
2682
2586
|
goalId: string;
|
|
2683
2587
|
status: UltragoalGoalStatus;
|
|
2684
2588
|
evidence: string;
|
|
2685
|
-
skcGoalJson?: string;
|
|
2686
2589
|
qualityGateJson?: string;
|
|
2687
2590
|
advanceNext?: boolean;
|
|
2688
2591
|
retryFailed?: boolean;
|
|
@@ -3091,19 +2994,14 @@ export async function recordUltragoalReviewBlockers(input: {
|
|
|
3091
2994
|
title: string;
|
|
3092
2995
|
objective: string;
|
|
3093
2996
|
evidence: string;
|
|
3094
|
-
skcGoalJson?: string;
|
|
3095
2997
|
}): Promise<UltragoalPlan> {
|
|
3096
2998
|
const objective = input.objective.trim();
|
|
3097
2999
|
if (!objective) throw new Error("record-review-blockers --objective is required");
|
|
3098
|
-
if (!input.skcGoalJson?.trim()) {
|
|
3099
|
-
throw new Error('record-review-blockers require --skc-goal-json from a fresh active goal({"op":"get"}) snapshot');
|
|
3100
|
-
}
|
|
3101
3000
|
const plan = await checkpointUltragoalGoal({
|
|
3102
3001
|
cwd: input.cwd,
|
|
3103
3002
|
goalId: input.goalId,
|
|
3104
3003
|
status: "review_blocked",
|
|
3105
3004
|
evidence: input.evidence,
|
|
3106
|
-
skcGoalJson: input.skcGoalJson,
|
|
3107
3005
|
});
|
|
3108
3006
|
const persistedPlan = await readUltragoalPlan(input.cwd);
|
|
3109
3007
|
if (persistedPlan?.state_revision !== undefined) plan.state_revision = persistedPlan.state_revision;
|
|
@@ -3551,7 +3449,6 @@ const FLAGS_WITH_VALUES = new Set([
|
|
|
3551
3449
|
"--goal-id",
|
|
3552
3450
|
"--status",
|
|
3553
3451
|
"--evidence",
|
|
3554
|
-
"--skc-goal-json",
|
|
3555
3452
|
"--quality-gate-json",
|
|
3556
3453
|
"--executor-qa-json",
|
|
3557
3454
|
"--executor-qa",
|
|
@@ -3605,15 +3502,12 @@ function renderUltragoalHelp(args: readonly string[]): string | null {
|
|
|
3605
3502
|
" --status=<value> pending|active|complete|failed|blocked|review_blocked|superseded",
|
|
3606
3503
|
" --evidence=<value> Completion or checkpoint evidence text",
|
|
3607
3504
|
" --quality-gate-json=<value> JSON string or path for complete checkpoints",
|
|
3608
|
-
" --skc-goal-json=<value> Optional JSON/path override for current goal snapshot; omitted complete checkpoints read current session goal state",
|
|
3609
3505
|
" --json Output a machine-readable receipt",
|
|
3610
3506
|
"",
|
|
3611
3507
|
"COMPLETE CHECKPOINT RECEIPTS",
|
|
3612
3508
|
" --quality-gate-json must be an object with architectReview, executorQa, and iteration.",
|
|
3613
3509
|
" executorQa.contractCoverage[] rows require an obligation field; description is not a substitute.",
|
|
3614
|
-
" Complete checkpoints
|
|
3615
|
-
" Explicit --skc-goal-json values must contain an active SKC goal-mode snapshot, not the .skc/ultragoal/goals.json goal record.",
|
|
3616
|
-
" goal.updatedAt may be epoch milliseconds or an ISO timestamp and must be fresh.",
|
|
3510
|
+
" Complete checkpoints validate the target durable goals.json record before writing a receipt.",
|
|
3617
3511
|
"",
|
|
3618
3512
|
"EXAMPLES",
|
|
3619
3513
|
' $ skc ultragoal checkpoint --goal-id G001 --status blocked --evidence "waiting on review"',
|
|
@@ -3949,7 +3843,6 @@ async function dispatchUltragoalCommand(args: string[], cwd: string): Promise<Ul
|
|
|
3949
3843
|
goalId,
|
|
3950
3844
|
status,
|
|
3951
3845
|
evidence,
|
|
3952
|
-
skcGoalJson: flagValue(args, "--skc-goal-json"),
|
|
3953
3846
|
qualityGateJson: flagValue(args, "--quality-gate-json"),
|
|
3954
3847
|
advanceNext: status === "complete",
|
|
3955
3848
|
});
|
|
@@ -3981,7 +3874,6 @@ async function dispatchUltragoalCommand(args: string[], cwd: string): Promise<Ul
|
|
|
3981
3874
|
title: flagValue(args, "--title") ?? "Resolve final code-review blockers",
|
|
3982
3875
|
objective: flagValue(args, "--objective") ?? "",
|
|
3983
3876
|
evidence: flagValue(args, "--evidence") ?? "",
|
|
3984
|
-
skcGoalJson: flagValue(args, "--skc-goal-json"),
|
|
3985
3877
|
});
|
|
3986
3878
|
const goal = plan.goals.at(-1);
|
|
3987
3879
|
return {
|
|
@@ -1479,14 +1479,6 @@
|
|
|
1479
1479
|
"required": true,
|
|
1480
1480
|
"type": "string"
|
|
1481
1481
|
},
|
|
1482
|
-
{
|
|
1483
|
-
"appliesToVerbs": [
|
|
1484
|
-
"checkpoint",
|
|
1485
|
-
"record-review-blockers"
|
|
1486
|
-
],
|
|
1487
|
-
"name": "skc-goal-json",
|
|
1488
|
-
"type": "string"
|
|
1489
|
-
},
|
|
1490
1482
|
{
|
|
1491
1483
|
"appliesToVerbs": [
|
|
1492
1484
|
"checkpoint"
|
|
@@ -271,7 +271,6 @@ export const WORKFLOW_MANIFEST: Record<CanonicalSkcWorkflowSkill, SkillManifest>
|
|
|
271
271
|
required: true,
|
|
272
272
|
appliesToVerbs: ["checkpoint", "record-review-blockers", "steer", "classify-blocker"],
|
|
273
273
|
},
|
|
274
|
-
{ name: "skc-goal-json", type: "string", appliesToVerbs: ["checkpoint", "record-review-blockers"] },
|
|
275
274
|
{ name: "quality-gate-json", type: "string", appliesToVerbs: ["checkpoint"] },
|
|
276
275
|
{ name: "goal-id", type: "string", appliesToVerbs: ["steer"] },
|
|
277
276
|
{ name: "goal-id", type: "string", appliesToVerbs: ["classify-blocker"] },
|