@gajae-code/coding-agent 0.3.0 → 0.3.2

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 (213) hide show
  1. package/CHANGELOG.md +32 -0
  2. package/README.md +1 -1
  3. package/dist/types/async/job-manager.d.ts +7 -0
  4. package/dist/types/cli/args.d.ts +3 -1
  5. package/dist/types/commands/deep-interview.d.ts +3 -0
  6. package/dist/types/commands/launch.d.ts +6 -0
  7. package/dist/types/config/keybindings.d.ts +5 -0
  8. package/dist/types/config/model-profile-activation.d.ts +30 -0
  9. package/dist/types/config/model-profiles.d.ts +19 -0
  10. package/dist/types/config/model-registry.d.ts +8 -0
  11. package/dist/types/config/model-resolver.d.ts +1 -1
  12. package/dist/types/config/models-config-schema.d.ts +47 -0
  13. package/dist/types/config/settings-schema.d.ts +14 -4
  14. package/dist/types/debug/crash-diagnostics.d.ts +45 -0
  15. package/dist/types/debug/runtime-gauges.d.ts +6 -0
  16. package/dist/types/deep-interview/render-middleware.d.ts +1 -0
  17. package/dist/types/eval/py/executor.d.ts +2 -0
  18. package/dist/types/eval/py/kernel.d.ts +2 -0
  19. package/dist/types/exec/bash-executor.d.ts +10 -0
  20. package/dist/types/gjc-runtime/cli-write-receipt.d.ts +24 -0
  21. package/dist/types/gjc-runtime/deep-interview-runtime.d.ts +1 -0
  22. package/dist/types/gjc-runtime/state-migrations.d.ts +9 -0
  23. package/dist/types/gjc-runtime/state-schema.d.ts +317 -0
  24. package/dist/types/gjc-runtime/state-writer.d.ts +10 -0
  25. package/dist/types/gjc-runtime/ultragoal-runtime.d.ts +2 -1
  26. package/dist/types/gjc-runtime/workflow-command-ref.d.ts +43 -0
  27. package/dist/types/harness-control-plane/control-endpoint.d.ts +3 -2
  28. package/dist/types/hooks/skill-state.d.ts +21 -0
  29. package/dist/types/internal-urls/agent-protocol.d.ts +2 -2
  30. package/dist/types/internal-urls/artifact-protocol.d.ts +2 -2
  31. package/dist/types/internal-urls/registry-helpers.d.ts +8 -7
  32. package/dist/types/internal-urls/types.d.ts +4 -0
  33. package/dist/types/lsp/index.d.ts +10 -10
  34. package/dist/types/main.d.ts +10 -1
  35. package/dist/types/modes/bridge/auth.d.ts +12 -0
  36. package/dist/types/modes/bridge/bridge-client-bridge.d.ts +9 -0
  37. package/dist/types/modes/bridge/bridge-mode.d.ts +44 -0
  38. package/dist/types/modes/bridge/bridge-ui-context.d.ts +88 -0
  39. package/dist/types/modes/bridge/event-stream.d.ts +8 -0
  40. package/dist/types/modes/components/custom-editor.d.ts +6 -0
  41. package/dist/types/modes/components/custom-provider-wizard.d.ts +10 -0
  42. package/dist/types/modes/components/jobs-overlay-model.d.ts +31 -0
  43. package/dist/types/modes/components/jobs-overlay.d.ts +30 -0
  44. package/dist/types/modes/components/model-selector.d.ts +6 -1
  45. package/dist/types/modes/components/provider-onboarding-selector.d.ts +1 -1
  46. package/dist/types/modes/components/status-line/types.d.ts +2 -0
  47. package/dist/types/modes/components/status-line.d.ts +2 -0
  48. package/dist/types/modes/controllers/input-controller.d.ts +1 -0
  49. package/dist/types/modes/controllers/selector-controller.d.ts +9 -0
  50. package/dist/types/modes/index.d.ts +1 -0
  51. package/dist/types/modes/interactive-mode.d.ts +1 -0
  52. package/dist/types/modes/jobs-observer.d.ts +57 -0
  53. package/dist/types/modes/rpc/host-tools.d.ts +1 -16
  54. package/dist/types/modes/rpc/host-uris.d.ts +1 -38
  55. package/dist/types/modes/shared/agent-wire/command-dispatch.d.ts +20 -0
  56. package/dist/types/modes/shared/agent-wire/command-validation.d.ts +2 -0
  57. package/dist/types/modes/shared/agent-wire/event-envelope.d.ts +24 -0
  58. package/dist/types/modes/shared/agent-wire/handshake.d.ts +46 -0
  59. package/dist/types/modes/shared/agent-wire/host-tool-bridge.d.ts +16 -0
  60. package/dist/types/modes/shared/agent-wire/host-uri-bridge.d.ts +17 -0
  61. package/dist/types/modes/shared/agent-wire/protocol.d.ts +44 -0
  62. package/dist/types/modes/shared/agent-wire/responses.d.ts +4 -0
  63. package/dist/types/modes/shared/agent-wire/scopes.d.ts +18 -0
  64. package/dist/types/modes/shared/agent-wire/ui-request-broker.d.ts +42 -0
  65. package/dist/types/modes/shared/agent-wire/ui-result.d.ts +27 -0
  66. package/dist/types/modes/types.d.ts +2 -0
  67. package/dist/types/sdk.d.ts +3 -1
  68. package/dist/types/session/agent-session.d.ts +11 -1
  69. package/dist/types/skill-state/workflow-state-contract.d.ts +1 -2
  70. package/dist/types/skill-state/workflow-state-version.d.ts +3 -0
  71. package/dist/types/task/executor.d.ts +1 -0
  72. package/dist/types/task/id.d.ts +7 -0
  73. package/dist/types/task/index.d.ts +5 -0
  74. package/dist/types/task/receipt.d.ts +85 -0
  75. package/dist/types/task/spawn-gate.d.ts +38 -0
  76. package/dist/types/task/types.d.ts +143 -11
  77. package/dist/types/tools/cron.d.ts +6 -0
  78. package/dist/types/tools/hindsight-recall.d.ts +0 -2
  79. package/dist/types/tools/hindsight-reflect.d.ts +0 -2
  80. package/dist/types/tools/hindsight-retain.d.ts +0 -2
  81. package/dist/types/tools/index.d.ts +6 -4
  82. package/dist/types/tools/path-utils.d.ts +1 -0
  83. package/dist/types/tools/subagent.d.ts +15 -0
  84. package/package.json +7 -7
  85. package/scripts/build-binary.ts +7 -0
  86. package/src/async/job-manager.ts +36 -0
  87. package/src/cli/args.ts +19 -2
  88. package/src/commands/deep-interview.ts +1 -0
  89. package/src/commands/harness.ts +289 -19
  90. package/src/commands/launch.ts +10 -2
  91. package/src/commands/state.ts +2 -1
  92. package/src/commands/team.ts +22 -4
  93. package/src/config/keybindings.ts +6 -0
  94. package/src/config/model-profile-activation.ts +157 -0
  95. package/src/config/model-profiles.ts +155 -0
  96. package/src/config/model-registry.ts +19 -0
  97. package/src/config/model-resolver.ts +3 -2
  98. package/src/config/models-config-schema.ts +36 -0
  99. package/src/config/settings-schema.ts +16 -3
  100. package/src/dap/client.ts +17 -3
  101. package/src/debug/crash-diagnostics.ts +223 -0
  102. package/src/debug/runtime-gauges.ts +20 -0
  103. package/src/deep-interview/render-middleware.ts +6 -0
  104. package/src/defaults/gjc/skills/deep-interview/SKILL.md +1 -1
  105. package/src/defaults/gjc/skills/ralplan/SKILL.md +31 -2
  106. package/src/defaults/gjc/skills/ultragoal/SKILL.md +39 -3
  107. package/src/defaults/gjc/skills/ultragoal/ai-slop-cleaner.md +61 -0
  108. package/src/defaults/gjc-defaults.ts +7 -0
  109. package/src/eval/py/executor.ts +21 -1
  110. package/src/eval/py/kernel.ts +15 -0
  111. package/src/exec/bash-executor.ts +41 -0
  112. package/src/gjc-runtime/cli-write-receipt.ts +31 -0
  113. package/src/gjc-runtime/deep-interview-runtime.ts +69 -32
  114. package/src/gjc-runtime/ralplan-runtime.ts +213 -36
  115. package/src/gjc-runtime/state-migrations.ts +54 -7
  116. package/src/gjc-runtime/state-runtime.ts +461 -64
  117. package/src/gjc-runtime/state-schema.ts +192 -0
  118. package/src/gjc-runtime/state-writer.ts +32 -1
  119. package/src/gjc-runtime/team-runtime.ts +177 -105
  120. package/src/gjc-runtime/ultragoal-runtime.ts +231 -38
  121. package/src/gjc-runtime/workflow-command-ref.ts +239 -0
  122. package/src/gjc-runtime/workflow-manifest.generated.json +108 -4
  123. package/src/gjc-runtime/workflow-manifest.ts +3 -1
  124. package/src/harness-control-plane/control-endpoint.ts +19 -8
  125. package/src/harness-control-plane/owner.ts +57 -10
  126. package/src/harness-control-plane/state-machine.ts +2 -1
  127. package/src/hooks/skill-state.ts +176 -26
  128. package/src/internal-urls/agent-protocol.ts +68 -21
  129. package/src/internal-urls/artifact-protocol.ts +12 -17
  130. package/src/internal-urls/docs-index.generated.ts +8 -10
  131. package/src/internal-urls/registry-helpers.ts +19 -16
  132. package/src/internal-urls/types.ts +4 -0
  133. package/src/lsp/client.ts +18 -2
  134. package/src/main.ts +88 -6
  135. package/src/modes/bridge/auth.ts +41 -0
  136. package/src/modes/bridge/bridge-client-bridge.ts +47 -0
  137. package/src/modes/bridge/bridge-mode.ts +520 -0
  138. package/src/modes/bridge/bridge-ui-context.ts +200 -0
  139. package/src/modes/bridge/event-stream.ts +70 -0
  140. package/src/modes/components/custom-editor.ts +101 -0
  141. package/src/modes/components/custom-provider-wizard.ts +318 -0
  142. package/src/modes/components/hook-selector.ts +61 -18
  143. package/src/modes/components/jobs-overlay-model.ts +109 -0
  144. package/src/modes/components/jobs-overlay.ts +172 -0
  145. package/src/modes/components/model-selector.ts +108 -18
  146. package/src/modes/components/provider-onboarding-selector.ts +6 -1
  147. package/src/modes/components/status-line/presets.ts +7 -5
  148. package/src/modes/components/status-line/segments.ts +25 -0
  149. package/src/modes/components/status-line/types.ts +2 -0
  150. package/src/modes/components/status-line.ts +9 -1
  151. package/src/modes/controllers/extension-ui-controller.ts +39 -3
  152. package/src/modes/controllers/input-controller.ts +97 -9
  153. package/src/modes/controllers/selector-controller.ts +86 -1
  154. package/src/modes/index.ts +1 -0
  155. package/src/modes/interactive-mode.ts +27 -0
  156. package/src/modes/jobs-observer.ts +204 -0
  157. package/src/modes/rpc/host-tools.ts +1 -186
  158. package/src/modes/rpc/host-uris.ts +1 -235
  159. package/src/modes/rpc/rpc-client.ts +25 -10
  160. package/src/modes/rpc/rpc-mode.ts +12 -381
  161. package/src/modes/shared/agent-wire/command-dispatch.ts +341 -0
  162. package/src/modes/shared/agent-wire/command-validation.ts +131 -0
  163. package/src/modes/shared/agent-wire/event-envelope.ts +108 -0
  164. package/src/modes/shared/agent-wire/handshake.ts +117 -0
  165. package/src/modes/shared/agent-wire/host-tool-bridge.ts +194 -0
  166. package/src/modes/shared/agent-wire/host-uri-bridge.ts +236 -0
  167. package/src/modes/shared/agent-wire/protocol.ts +96 -0
  168. package/src/modes/shared/agent-wire/responses.ts +17 -0
  169. package/src/modes/shared/agent-wire/scopes.ts +89 -0
  170. package/src/modes/shared/agent-wire/ui-request-broker.ts +150 -0
  171. package/src/modes/shared/agent-wire/ui-result.ts +48 -0
  172. package/src/modes/types.ts +2 -0
  173. package/src/prompts/memories/consolidation.md +1 -1
  174. package/src/prompts/memories/read-path.md +6 -7
  175. package/src/prompts/memories/unavailable.md +2 -2
  176. package/src/prompts/tools/bash.md +1 -1
  177. package/src/prompts/tools/irc.md +1 -1
  178. package/src/prompts/tools/read.md +2 -2
  179. package/src/prompts/tools/recall.md +1 -0
  180. package/src/prompts/tools/reflect.md +1 -0
  181. package/src/prompts/tools/retain.md +1 -0
  182. package/src/prompts/tools/subagent.md +12 -7
  183. package/src/prompts/tools/task-summary.md +3 -9
  184. package/src/prompts/tools/task.md +5 -1
  185. package/src/sdk.ts +5 -1
  186. package/src/session/agent-session.ts +214 -38
  187. package/src/skill-state/deep-interview-mutation-guard.ts +23 -4
  188. package/src/skill-state/workflow-state-contract.ts +7 -4
  189. package/src/skill-state/workflow-state-version.ts +3 -0
  190. package/src/slash-commands/builtin-registry.ts +9 -1
  191. package/src/task/executor.ts +31 -5
  192. package/src/task/id.ts +33 -0
  193. package/src/task/index.ts +259 -67
  194. package/src/task/output-manager.ts +5 -4
  195. package/src/task/receipt.ts +297 -0
  196. package/src/task/render.ts +48 -131
  197. package/src/task/spawn-gate.ts +132 -0
  198. package/src/task/types.ts +48 -7
  199. package/src/tools/ask.ts +73 -33
  200. package/src/tools/ast-edit.ts +1 -0
  201. package/src/tools/ast-grep.ts +1 -0
  202. package/src/tools/bash.ts +1 -1
  203. package/src/tools/cron.ts +48 -0
  204. package/src/tools/find.ts +4 -1
  205. package/src/tools/hindsight-recall.ts +0 -2
  206. package/src/tools/hindsight-reflect.ts +0 -2
  207. package/src/tools/hindsight-retain.ts +0 -2
  208. package/src/tools/index.ts +6 -18
  209. package/src/tools/path-utils.ts +3 -2
  210. package/src/tools/read.ts +4 -3
  211. package/src/tools/search.ts +1 -0
  212. package/src/tools/skill.ts +6 -1
  213. package/src/tools/subagent.ts +237 -84
@@ -1,10 +1,18 @@
1
1
  import * as crypto from "node:crypto";
2
2
  import * as path from "node:path";
3
- import type { WorkflowHudSummary } from "../skill-state/active-state";
3
+ import { syncSkillActiveState, type WorkflowHudSummary } from "../skill-state/active-state";
4
4
  import { buildUltragoalHudSummary as buildWorkflowUltragoalHudSummary } from "../skill-state/workflow-hud";
5
+ import { WORKFLOW_STATE_VERSION, workflowStateStoragePath } from "../skill-state/workflow-state-contract";
6
+ import { renderCliWriteReceipt } from "./cli-write-receipt";
5
7
  import { DEFAULT_ULTRAGOAL_OBJECTIVE } from "./goal-mode-request";
6
8
  import { renderUltragoalStatusMarkdown } from "./state-renderer";
7
- import { appendJsonl, writeArtifact, writeJsonAtomic } from "./state-writer";
9
+ import {
10
+ appendJsonl,
11
+ readExistingStateForMutation,
12
+ writeArtifact,
13
+ writeJsonAtomic,
14
+ writeWorkflowEnvelopeAtomic,
15
+ } from "./state-writer";
8
16
 
9
17
  export type UltragoalGjcGoalMode = "aggregate" | "per-story";
10
18
  export type UltragoalGoalStatus =
@@ -473,14 +481,145 @@ export function buildUltragoalHudSummary(
473
481
  updatedAt: new Date().toISOString(),
474
482
  });
475
483
  }
484
+ function currentSessionId(): string | undefined {
485
+ const sessionId = process.env.GJC_SESSION_ID?.trim();
486
+ return sessionId || undefined;
487
+ }
476
488
 
477
- function titleFromBrief(brief: string): string {
478
- const firstLine = brief
489
+ function ultragoalModeStateFromSummary(
490
+ summary: UltragoalStatusSummary,
491
+ latestLedger: UltragoalLedgerEvent | undefined,
492
+ existing: Record<string, unknown> | undefined,
493
+ sessionId: string | undefined,
494
+ ): Record<string, unknown> {
495
+ const updatedAt = new Date().toISOString();
496
+ return {
497
+ ...(existing ?? {}),
498
+ skill: "ultragoal",
499
+ version: WORKFLOW_STATE_VERSION,
500
+ active: summary.exists && summary.status !== "complete",
501
+ current_phase: summary.status,
502
+ status: summary.status,
503
+ active_goal_id: summary.currentGoal?.id,
504
+ counts: summary.counts,
505
+ brief_path: summary.paths.briefPath,
506
+ ledger_path: summary.paths.ledgerPath,
507
+ goals_path: summary.paths.goalsPath,
508
+ latest_ledger_event: latestLedger?.event,
509
+ latest_ledger_event_id: latestLedger?.eventId,
510
+ updated_at: updatedAt,
511
+ ...(sessionId ? { session_id: sessionId } : {}),
512
+ };
513
+ }
514
+
515
+ export async function syncUltragoalWorkflowState(cwd: string): Promise<void> {
516
+ const summary = await getUltragoalStatus(cwd);
517
+ const ledger = await readUltragoalLedger(cwd);
518
+ const latestLedger = ledger.at(-1);
519
+ const sessionId = currentSessionId();
520
+ const modeStateActive = summary.exists && summary.status !== "complete";
521
+ const syncModeState = async (targetSessionId: string | undefined): Promise<void> => {
522
+ const statePath = workflowStateStoragePath(cwd, "ultragoal", targetSessionId);
523
+ const existing = await readExistingStateForMutation(statePath);
524
+ if (existing.kind === "corrupt" && modeStateActive)
525
+ throw new Error(`Cannot sync corrupt ultragoal mode-state: ${existing.error}`);
526
+ const existingValue = existing.kind === "valid" ? existing.value : undefined;
527
+ await writeWorkflowEnvelopeAtomic(
528
+ statePath,
529
+ ultragoalModeStateFromSummary(summary, latestLedger, existingValue, targetSessionId),
530
+ {
531
+ cwd,
532
+ receipt: {
533
+ cwd,
534
+ skill: "ultragoal",
535
+ owner: "gjc-runtime",
536
+ command: "gjc ultragoal sync",
537
+ sessionId: targetSessionId,
538
+ },
539
+ audit: {
540
+ category: "state",
541
+ verb: "sync",
542
+ owner: "gjc-runtime",
543
+ skill: "ultragoal",
544
+ fromPhase: typeof existingValue?.current_phase === "string" ? existingValue.current_phase : undefined,
545
+ toPhase: summary.status,
546
+ },
547
+ },
548
+ );
549
+ };
550
+ await syncModeState(undefined);
551
+ if (sessionId) await syncModeState(sessionId);
552
+ await syncSkillActiveState({
553
+ cwd,
554
+ skill: "ultragoal",
555
+ active: summary.exists && summary.status !== "complete",
556
+ phase: summary.status,
557
+ sessionId: currentSessionId(),
558
+ hud: buildUltragoalHudSummary(summary, latestLedger),
559
+ source: "gjc-ultragoal",
560
+ });
561
+ }
562
+
563
+ async function syncUltragoalWorkflowStateBestEffort(cwd: string): Promise<void> {
564
+ try {
565
+ await syncUltragoalWorkflowState(cwd);
566
+ } catch {
567
+ // HUD and mode-state sync are best-effort and must not change command semantics.
568
+ }
569
+ }
570
+
571
+ function clampTitle(title: string): string {
572
+ return title.length > 80 ? `${title.slice(0, 77)}...` : title;
573
+ }
574
+
575
+ function firstNonEmptyLine(text: string): string | undefined {
576
+ return text
479
577
  .split(/\r?\n/)
480
578
  .map(line => line.trim())
481
579
  .find(line => line.length > 0);
580
+ }
581
+
582
+ function titleFromBrief(brief: string): string {
583
+ const firstLine = firstNonEmptyLine(brief);
482
584
  if (!firstLine) return "Complete ultragoal brief";
483
- return firstLine.length > 80 ? `${firstLine.slice(0, 77)}...` : firstLine;
585
+ return clampTitle(firstLine);
586
+ }
587
+
588
+ // A reserved, column-0 (unindented) `@goal` line opens a story. The character
589
+ // right after `@goal` must be `:`, an ASCII space or tab, or end-of-line, so
590
+ // `@goalish`, `@goals:`, `@goal-foo`, `@goal.foo`, `@goal/foo`, a non-breaking
591
+ // space, and indented or mid-line `@goal:` are all ordinary objective text and
592
+ // never delimiters.
593
+ const GOAL_DELIMITER = /^@goal(?::|[ \t]+|$)[ \t]*(.*)$/;
594
+
595
+ interface ParsedGoal {
596
+ title: string;
597
+ objective: string;
598
+ }
599
+
600
+ function parseGoalsFromBrief(brief: string): ParsedGoal[] {
601
+ const sections: { title: string; body: string[] }[] = [];
602
+ let current: { title: string; body: string[] } | undefined;
603
+ for (const line of brief.split(/\r?\n/)) {
604
+ const match = GOAL_DELIMITER.exec(line);
605
+ if (match) {
606
+ current = { title: match[1].trim(), body: [] };
607
+ sections.push(current);
608
+ continue;
609
+ }
610
+ current?.body.push(line);
611
+ }
612
+ if (sections.length === 0) {
613
+ return [{ title: titleFromBrief(brief), objective: brief.trim() }];
614
+ }
615
+ return sections.map((section, index) => {
616
+ const body = section.body.join("\n").trim();
617
+ const title = section.title || firstNonEmptyLine(body) || "";
618
+ if (!title && !body) {
619
+ throw new Error(`ultragoal @goal block ${index + 1} has no title or objective`);
620
+ }
621
+ return { title: clampTitle(title), objective: body || title };
622
+ });
484
623
  }
485
624
 
486
625
  export async function createUltragoalPlan(input: {
@@ -491,21 +630,23 @@ export async function createUltragoalPlan(input: {
491
630
  const brief = input.brief.trim();
492
631
  if (!brief) throw new Error("ultragoal brief is required");
493
632
  const now = new Date().toISOString();
633
+ // Parse the untrimmed brief so the raw-line delimiter contract holds: a
634
+ // leading-indented `@goal` on the first line must stay objective text rather
635
+ // than being promoted to column 0 by trimming.
636
+ const goals: UltragoalGoal[] = parseGoalsFromBrief(input.brief).map((goal, index) => ({
637
+ id: `G${String(index + 1).padStart(3, "0")}`,
638
+ title: goal.title,
639
+ objective: goal.objective,
640
+ status: "pending",
641
+ createdAt: now,
642
+ updatedAt: now,
643
+ }));
494
644
  const plan: UltragoalPlan = {
495
645
  version: 1,
496
646
  brief,
497
647
  gjcGoalMode: input.gjcGoalMode ?? "aggregate",
498
648
  gjcObjective: DEFAULT_ULTRAGOAL_OBJECTIVE,
499
- goals: [
500
- {
501
- id: "G001",
502
- title: titleFromBrief(brief),
503
- objective: brief,
504
- status: "pending",
505
- createdAt: now,
506
- updatedAt: now,
507
- },
508
- ],
649
+ goals,
509
650
  createdAt: now,
510
651
  updatedAt: now,
511
652
  };
@@ -1235,16 +1376,26 @@ function renderStatus(summary: UltragoalStatusSummary, json: boolean): string {
1235
1376
  function renderCompleteHandoff(
1236
1377
  result: { plan: UltragoalPlan; goal?: UltragoalGoal; allComplete: boolean },
1237
1378
  json: boolean,
1379
+ cwd: string,
1238
1380
  ): string {
1239
- if (json) return `${JSON.stringify(result, null, 2)}\n`;
1240
- if (result.allComplete) return "All ultragoal goals are complete.\n";
1241
- if (!result.goal) return "No schedulable ultragoal goal found.\n";
1381
+ if (json) {
1382
+ return renderCliWriteReceipt({
1383
+ ok: true,
1384
+ all_complete: result.allComplete,
1385
+ next_action: result.allComplete ? "none" : "execute-goal",
1386
+ goal_id: result.goal?.id,
1387
+ goal_status: result.goal?.status,
1388
+ gjc_objective: result.plan.gjcObjective,
1389
+ goals_path: getUltragoalPaths(cwd).goalsPath,
1390
+ });
1391
+ }
1392
+ if (result.allComplete) return "ultragoal complete all=true\n";
1393
+ if (!result.goal) return "ultragoal next-action=none\n";
1242
1394
  return [
1243
- `Ultragoal handoff: ${result.goal.id} — ${result.goal.title}`,
1244
- `Objective: ${result.goal.objective}`,
1245
- `GJC objective: ${result.plan.gjcObjective}`,
1246
- 'Call goal({"op":"get"}); call goal({"op":"create","objective":"<printed objective>"}) only if no active GJC goal exists, then keep the GJC goal active while this Ultragoal story is verified and checkpointed.',
1247
- 'Before checkpointing complete, obtain a passing architectReview (architecture/product/code CLEAR + APPROVE) and executorQa (e2e/red-team passed with contractCoverage, surfaceEvidence, adversarialCases, and artifactRefs matrix evidence), then checkpoint with --quality-gate-json and a fresh active goal snapshot; record blockers instead of completing on any finding, plan/code mismatch, shallow evidence, or missing artifact link; call goal({"op":"complete"}) only after the final aggregate receipt exists.',
1395
+ `ultragoal next-action=execute-goal goal-id=${result.goal.id}`,
1396
+ `objective=${result.goal.objective}`,
1397
+ `gjc-objective=${result.plan.gjcObjective}`,
1398
+ "checkpoint requires=architectReview:CLEAR+APPROVE,executorQa:passed",
1248
1399
  "",
1249
1400
  ].join("\n");
1250
1401
  }
@@ -1253,29 +1404,40 @@ export async function runNativeUltragoalCommand(args: string[], cwd = process.cw
1253
1404
  try {
1254
1405
  const command = commandName(args);
1255
1406
  const json = hasFlag(args, "--json");
1407
+ let result: UltragoalCommandResult;
1256
1408
  switch (command) {
1257
1409
  case "status":
1258
- return { status: 0, stdout: renderStatus(await getUltragoalStatus(cwd), json) };
1410
+ await syncUltragoalWorkflowStateBestEffort(cwd);
1411
+ result = { status: 0, stdout: renderStatus(await getUltragoalStatus(cwd), json) };
1412
+ break;
1259
1413
  case "create":
1260
1414
  case "create-goals": {
1261
1415
  const mode = flagValue(args, "--gjc-goal-mode") === "per-story" ? "per-story" : "aggregate";
1262
1416
  const plan = await createUltragoalPlan({ cwd, brief: await readBrief(cwd, args), gjcGoalMode: mode });
1263
- return {
1417
+ await syncUltragoalWorkflowStateBestEffort(cwd);
1418
+ result = {
1264
1419
  status: 0,
1265
1420
  createdPlan: true,
1266
1421
  stdout: json
1267
- ? `${JSON.stringify(plan, null, 2)}\n`
1268
- : `Created ultragoal plan with ${plan.goals.length} goal at ${getUltragoalPaths(cwd).goalsPath}.\n`,
1422
+ ? renderCliWriteReceipt({
1423
+ ok: true,
1424
+ goals_count: plan.goals.length,
1425
+ goal_ids: plan.goals.map(goal => goal.id),
1426
+ goals_path: getUltragoalPaths(cwd).goalsPath,
1427
+ })
1428
+ : `Created ultragoal plan with ${plan.goals.length} goal${plan.goals.length === 1 ? "" : "s"} at ${getUltragoalPaths(cwd).goalsPath}.\n`,
1269
1429
  };
1430
+ break;
1270
1431
  }
1271
- case "complete-goals":
1272
- return {
1432
+ case "complete-goals": {
1433
+ const handoff = await startNextUltragoalGoal({ cwd, retryFailed: hasFlag(args, "--retry-failed") });
1434
+ await syncUltragoalWorkflowStateBestEffort(cwd);
1435
+ result = {
1273
1436
  status: 0,
1274
- stdout: renderCompleteHandoff(
1275
- await startNextUltragoalGoal({ cwd, retryFailed: hasFlag(args, "--retry-failed") }),
1276
- json,
1277
- ),
1437
+ stdout: renderCompleteHandoff(handoff, json, cwd),
1278
1438
  };
1439
+ break;
1440
+ }
1279
1441
  case "checkpoint": {
1280
1442
  const goalId = flagValue(args, "--goal-id") ?? "";
1281
1443
  const status = parseGoalStatus(flagValue(args, "--status"));
@@ -1288,10 +1450,22 @@ export async function runNativeUltragoalCommand(args: string[], cwd = process.cw
1288
1450
  gjcGoalJson: flagValue(args, "--gjc-goal-json"),
1289
1451
  qualityGateJson: flagValue(args, "--quality-gate-json"),
1290
1452
  });
1291
- return {
1453
+ await syncUltragoalWorkflowStateBestEffort(cwd);
1454
+ const goal = plan.goals.find(item => item.id === goalId);
1455
+ result = {
1292
1456
  status: 0,
1293
- stdout: json ? `${JSON.stringify(plan, null, 2)}\n` : `Checkpointed ${goalId} as ${status}.\n`,
1457
+ stdout: json
1458
+ ? renderCliWriteReceipt({
1459
+ ok: true,
1460
+ goal_id: goalId,
1461
+ status,
1462
+ goals_path: getUltragoalPaths(cwd).goalsPath,
1463
+ completion_receipt_kind: goal?.completionVerification?.receiptKind,
1464
+ quality_gate_hash: goal?.completionVerification?.qualityGateHash,
1465
+ })
1466
+ : `ultragoal checkpoint goal-id=${goalId} status=${status}\n`,
1294
1467
  };
1468
+ break;
1295
1469
  }
1296
1470
  case "steer": {
1297
1471
  const kind = flagValue(args, "--kind");
@@ -1303,10 +1477,20 @@ export async function runNativeUltragoalCommand(args: string[], cwd = process.cw
1303
1477
  evidence: flagValue(args, "--evidence") ?? "",
1304
1478
  rationale: flagValue(args, "--rationale") ?? "",
1305
1479
  });
1306
- return {
1480
+ await syncUltragoalWorkflowStateBestEffort(cwd);
1481
+ const goal = plan.goals.at(-1);
1482
+ result = {
1307
1483
  status: 0,
1308
- stdout: json ? `${JSON.stringify(plan, null, 2)}\n` : "Accepted add_subgoal steering.\n",
1484
+ stdout: json
1485
+ ? renderCliWriteReceipt({
1486
+ ok: true,
1487
+ kind,
1488
+ goal_id: goal?.id,
1489
+ goals_path: getUltragoalPaths(cwd).goalsPath,
1490
+ })
1491
+ : "Accepted add_subgoal steering.\n",
1309
1492
  };
1493
+ break;
1310
1494
  }
1311
1495
  case "record-review-blockers": {
1312
1496
  const plan = await recordUltragoalReviewBlockers({
@@ -1317,11 +1501,20 @@ export async function runNativeUltragoalCommand(args: string[], cwd = process.cw
1317
1501
  evidence: flagValue(args, "--evidence") ?? "",
1318
1502
  gjcGoalJson: flagValue(args, "--gjc-goal-json"),
1319
1503
  });
1320
- return { status: 0, stdout: json ? `${JSON.stringify(plan, null, 2)}\n` : "Recorded review blockers.\n" };
1504
+ await syncUltragoalWorkflowStateBestEffort(cwd);
1505
+ const goal = plan.goals.at(-1);
1506
+ result = {
1507
+ status: 0,
1508
+ stdout: json
1509
+ ? renderCliWriteReceipt({ ok: true, goal_id: goal?.id, goals_path: getUltragoalPaths(cwd).goalsPath })
1510
+ : "Recorded review blockers.\n",
1511
+ };
1512
+ break;
1321
1513
  }
1322
1514
  default:
1323
1515
  return { status: 1, stderr: `Unknown gjc ultragoal command: ${command}\n` };
1324
1516
  }
1517
+ return result;
1325
1518
  } catch (error) {
1326
1519
  return { status: 1, stderr: `${error instanceof Error ? error.message : String(error)}\n` };
1327
1520
  }
@@ -0,0 +1,239 @@
1
+ import type { CanonicalGjcWorkflowSkill } from "../skill-state/active-state";
2
+ import { CANONICAL_GJC_WORKFLOW_SKILLS } from "../skill-state/active-state";
3
+
4
+ export type CommandRefVisibility = "public" | "hidden" | "planned";
5
+ export type CommandRefIncludeWhen = "implemented-only" | "planned";
6
+
7
+ export interface CommandRefCommand {
8
+ tokens: string[];
9
+ rendered: string;
10
+ visibility: CommandRefVisibility;
11
+ includeWhen: CommandRefIncludeWhen;
12
+ note?: string;
13
+ }
14
+
15
+ export interface CommandRefExample {
16
+ label?: string;
17
+ bytes: string;
18
+ }
19
+
20
+ export interface CommandRefBridge {
21
+ from: string;
22
+ to: string;
23
+ rendered: string;
24
+ }
25
+
26
+ export interface CommandRefBlock {
27
+ skill: CanonicalGjcWorkflowSkill;
28
+ blockId: string;
29
+ sourcePath: string;
30
+ renderOrder: number;
31
+ markers: {
32
+ start: string;
33
+ end: string;
34
+ };
35
+ commands: CommandRefCommand[];
36
+ examples: CommandRefExample[];
37
+ aliasesAndBridges: CommandRefBridge[];
38
+ notes: string[];
39
+ }
40
+
41
+ export interface RenderedCommandRefBlock {
42
+ skill: CanonicalGjcWorkflowSkill;
43
+ blockId: string;
44
+ markers: CommandRefBlock["markers"];
45
+ bytes: string;
46
+ }
47
+
48
+ const skillPath = (skill: CanonicalGjcWorkflowSkill): string =>
49
+ `packages/coding-agent/src/defaults/gjc/skills/${skill}/SKILL.md`;
50
+
51
+ const stateWrite = (skill: CanonicalGjcWorkflowSkill): CommandRefCommand => ({
52
+ tokens: ["gjc", "state", skill, "write", "--input", `'{"current_phase":"handoff"}'`, "--json"],
53
+ rendered: `gjc state ${skill} write --input '{"current_phase":"handoff"}' --json`,
54
+ visibility: "public",
55
+ includeWhen: "implemented-only",
56
+ note: "Marks the workflow ready for the skill-tool chain guard.",
57
+ });
58
+
59
+ const stateHandoff = (
60
+ skill: CanonicalGjcWorkflowSkill,
61
+ targets: readonly CanonicalGjcWorkflowSkill[],
62
+ ): CommandRefCommand => ({
63
+ tokens: ["gjc", "state", skill, "handoff", "--to", `<${targets.join("|")}>`, "--json"],
64
+ rendered: `gjc state ${skill} handoff --to <${targets.join("|")}> --json`,
65
+ visibility: "public",
66
+ includeWhen: "implemented-only",
67
+ note: "Bridge command run in-process by the skill tool after slash-skill dispatch.",
68
+ });
69
+
70
+ export const WORKFLOW_COMMAND_REF_BLOCKS: readonly CommandRefBlock[] = [
71
+ {
72
+ skill: "deep-interview",
73
+ blockId: "state",
74
+ sourcePath: skillPath("deep-interview"),
75
+ renderOrder: 10,
76
+ markers: {
77
+ start: "<!-- gjc:cmdref:start state -->",
78
+ end: "<!-- gjc:cmdref:end state -->",
79
+ },
80
+ commands: [
81
+ stateWrite("deep-interview"),
82
+ {
83
+ tokens: [
84
+ "gjc",
85
+ "deep-interview",
86
+ "--write",
87
+ "--stage",
88
+ "final",
89
+ "--slug",
90
+ "{slug}",
91
+ "--spec",
92
+ "<markdown-or-path>",
93
+ "--deliberate",
94
+ "--json",
95
+ ],
96
+ rendered:
97
+ "gjc deep-interview --write --stage final --slug {slug} --spec <markdown-or-path> --deliberate --json",
98
+ visibility: "public",
99
+ includeWhen: "implemented-only",
100
+ note: "Sanctioned deliberate deep-interview to ralplan bridge.",
101
+ },
102
+ ],
103
+ examples: [
104
+ {
105
+ label: "handoff state write",
106
+ bytes: '```\ngjc state deep-interview write --input \'{"current_phase":"handoff"}\' --json\n```',
107
+ },
108
+ {
109
+ label: "deliberate bridge",
110
+ bytes: "```\ngjc \\\ndeep-interview --write --stage final --slug {slug} --spec <markdown-or-path> --deliberate --json\n```",
111
+ },
112
+ ],
113
+ aliasesAndBridges: [
114
+ {
115
+ from: "deep-interview",
116
+ to: "ralplan",
117
+ rendered:
118
+ "gjc deep-interview --write --stage final --slug {slug} --spec <markdown-or-path> --deliberate --json",
119
+ },
120
+ ],
121
+ notes: [
122
+ "Before invoking `/skill:ralplan`, `/skill:team`, or `/skill:ultragoal`, persist the final spec and mark deep-interview ready for handoff.",
123
+ ],
124
+ },
125
+ {
126
+ skill: "ralplan",
127
+ blockId: "state",
128
+ sourcePath: skillPath("ralplan"),
129
+ renderOrder: 10,
130
+ markers: { start: "<!-- gjc:cmdref:start state -->", end: "<!-- gjc:cmdref:end state -->" },
131
+ commands: [stateWrite("ralplan"), stateHandoff("ralplan", ["team", "ultragoal"])],
132
+ examples: [
133
+ {
134
+ label: "handoff state write",
135
+ bytes: '```\ngjc state ralplan write --input \'{"current_phase":"handoff"}\' --json\n```',
136
+ },
137
+ ],
138
+ aliasesAndBridges: [
139
+ { from: "ralplan", to: "team|ultragoal", rendered: "gjc state ralplan handoff --to <team|ultragoal> --json" },
140
+ ],
141
+ notes: [
142
+ "Before invoking `/skill:team` or `/skill:ultragoal`, mark ralplan ready for handoff so the skill tool's chain guard permits the transition.",
143
+ ],
144
+ },
145
+ {
146
+ skill: "ultragoal",
147
+ blockId: "state",
148
+ sourcePath: skillPath("ultragoal"),
149
+ renderOrder: 10,
150
+ markers: { start: "<!-- gjc:cmdref:start state -->", end: "<!-- gjc:cmdref:end state -->" },
151
+ commands: [stateWrite("ultragoal"), stateHandoff("ultragoal", ["ralplan", "deep-interview"])],
152
+ examples: [
153
+ {
154
+ label: "handoff state write",
155
+ bytes: '```\ngjc state ultragoal write --input \'{"current_phase":"handoff"}\' --json\n```',
156
+ },
157
+ ],
158
+ aliasesAndBridges: [
159
+ {
160
+ from: "ultragoal",
161
+ to: "ralplan|deep-interview",
162
+ rendered: "gjc state ultragoal handoff --to <ralplan|deep-interview> --json",
163
+ },
164
+ ],
165
+ notes: [
166
+ "When the aggregate ultragoal is complete OR the user requests return to planning/clarification, mark ultragoal ready for handoff.",
167
+ ],
168
+ },
169
+ {
170
+ skill: "team",
171
+ blockId: "state",
172
+ sourcePath: skillPath("team"),
173
+ renderOrder: 10,
174
+ markers: { start: "<!-- gjc:cmdref:start state -->", end: "<!-- gjc:cmdref:end state -->" },
175
+ commands: [stateWrite("team"), stateHandoff("team", ["ralplan", "deep-interview", "ultragoal"])],
176
+ examples: [
177
+ {
178
+ label: "handoff state write",
179
+ bytes: '```\ngjc state team write --input \'{"current_phase":"handoff"}\' --json\n```',
180
+ },
181
+ ],
182
+ aliasesAndBridges: [
183
+ {
184
+ from: "team",
185
+ to: "ralplan|deep-interview|ultragoal",
186
+ rendered: "gjc state team handoff --to <ralplan|deep-interview|ultragoal> --json",
187
+ },
188
+ ],
189
+ notes: [
190
+ "When the team task-set completes OR the user requests return to planning/persistence, mark team ready for handoff.",
191
+ ],
192
+ },
193
+ ] as const;
194
+
195
+ export function listCommandRefBlocks(skill?: CanonicalGjcWorkflowSkill): CommandRefBlock[] {
196
+ const blocks =
197
+ skill === undefined
198
+ ? WORKFLOW_COMMAND_REF_BLOCKS
199
+ : WORKFLOW_COMMAND_REF_BLOCKS.filter(block => block.skill === skill);
200
+ return [...blocks].sort(
201
+ (a, b) => a.skill.localeCompare(b.skill) || a.renderOrder - b.renderOrder || a.blockId.localeCompare(b.blockId),
202
+ );
203
+ }
204
+
205
+ export function renderCommandRefBlock(skill: CanonicalGjcWorkflowSkill, blockId = "state"): RenderedCommandRefBlock {
206
+ const block = WORKFLOW_COMMAND_REF_BLOCKS.find(item => item.skill === skill && item.blockId === blockId);
207
+ if (block === undefined) throw new Error(`Unknown command-reference block: ${skill}/${blockId}`);
208
+
209
+ const lines: string[] = [];
210
+ lines.push(block.markers.start);
211
+ lines.push(`### Generated command reference: ${block.blockId}`);
212
+ lines.push("");
213
+ for (const note of block.notes) lines.push(note);
214
+ lines.push("");
215
+ lines.push("Commands:");
216
+ for (const command of block.commands.filter(
217
+ item => item.visibility === "public" && item.includeWhen === "implemented-only",
218
+ )) {
219
+ lines.push(`- \`${command.rendered}\``);
220
+ if (command.note !== undefined) lines.push(` - ${command.note}`);
221
+ }
222
+ lines.push("");
223
+ lines.push("Examples:");
224
+ for (const example of block.examples) {
225
+ if (example.label !== undefined) lines.push(`- ${example.label}:`);
226
+ lines.push(example.bytes);
227
+ }
228
+ lines.push("");
229
+ lines.push("Aliases and bridges:");
230
+ for (const bridge of block.aliasesAndBridges) lines.push(`- ${bridge.from} -> ${bridge.to}: \`${bridge.rendered}\``);
231
+ lines.push(block.markers.end);
232
+ lines.push("");
233
+
234
+ return { skill, blockId: block.blockId, markers: block.markers, bytes: lines.join("\n") };
235
+ }
236
+
237
+ export function isCanonicalGjcWorkflowSkill(value: string): value is CanonicalGjcWorkflowSkill {
238
+ return (CANONICAL_GJC_WORKFLOW_SKILLS as readonly string[]).includes(value);
239
+ }