@gajae-code/coding-agent 0.18.7 → 0.18.8

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 (279) hide show
  1. package/CHANGELOG.md +279 -1
  2. package/dist/types/async/job-manager.d.ts +1 -1
  3. package/dist/types/capability/mcp.d.ts +2 -0
  4. package/dist/types/capability/skill.d.ts +2 -0
  5. package/dist/types/capability/types.d.ts +2 -0
  6. package/dist/types/cli/public-command-errors.d.ts +3 -1
  7. package/dist/types/cli/setup-cli.d.ts +1 -1
  8. package/dist/types/commands/deep-interview.d.ts +3 -0
  9. package/dist/types/config/autorouting-tier-map.d.ts +348 -9
  10. package/dist/types/config/file-lock.d.ts +1 -0
  11. package/dist/types/config/model-bindings-applier.d.ts +2 -0
  12. package/dist/types/config/model-profile-activation.d.ts +11 -3
  13. package/dist/types/config/model-registry.d.ts +4 -9
  14. package/dist/types/config/skill-settings-defaults.d.ts +2 -0
  15. package/dist/types/cursor-delete-path.d.ts +1 -0
  16. package/dist/types/cursor-mcp-name.d.ts +8 -0
  17. package/dist/types/customization/inventory.d.ts +2 -0
  18. package/dist/types/debug/report-bundle-parent.d.ts +2 -0
  19. package/dist/types/defaults/gjc/extensions/grok-cli-vendor/src/provider/stream.d.ts +1 -0
  20. package/dist/types/defaults/gjc/extensions/grok-cli-vendor/src/provider/version-manager.d.ts +31 -0
  21. package/dist/types/defaults/gjc-skills.generated.d.ts +5 -6
  22. package/dist/types/discovery/helpers.d.ts +6 -1
  23. package/dist/types/export/html/css-color.d.ts +2 -0
  24. package/dist/types/export/html/template.generated.d.ts +1 -1
  25. package/dist/types/extensibility/extensions/types.d.ts +7 -0
  26. package/dist/types/extensibility/gjc-plugins/runtime-adapters.d.ts +14 -1
  27. package/dist/types/extensibility/plugins/marketplace/cache.d.ts +2 -0
  28. package/dist/types/extensibility/plugins/plugin-link-path.d.ts +2 -0
  29. package/dist/types/extensibility/runtime-skill-discovery.d.ts +3 -1
  30. package/dist/types/extensibility/skill-management.d.ts +3 -1
  31. package/dist/types/extensibility/skills.d.ts +2 -0
  32. package/dist/types/gjc-runtime/deep-interview-crystallize.d.ts +74 -0
  33. package/dist/types/gjc-runtime/deep-interview-runtime.d.ts +11 -0
  34. package/dist/types/gjc-runtime/gc-runtime.d.ts +5 -0
  35. package/dist/types/gjc-runtime/ralplan-runtime.d.ts +0 -5
  36. package/dist/types/gjc-runtime/state-argv.d.ts +1 -1
  37. package/dist/types/gjc-runtime/state-migrations.d.ts +8 -0
  38. package/dist/types/gjc-runtime/state-runtime.d.ts +95 -0
  39. package/dist/types/gjc-runtime/state-writer.d.ts +11 -1
  40. package/dist/types/gjc-runtime/ultragoal-runtime.d.ts +8 -0
  41. package/dist/types/internal-urls/skill-path.d.ts +1 -0
  42. package/dist/types/lsp/workspace-path.d.ts +1 -0
  43. package/dist/types/modes/acp/acp-agent.d.ts +7 -0
  44. package/dist/types/modes/components/custom-editor.d.ts +6 -0
  45. package/dist/types/modes/components/customization/customization-dashboard.d.ts +1 -0
  46. package/dist/types/modes/components/extensions/state-manager.d.ts +4 -3
  47. package/dist/types/modes/controllers/input-controller.d.ts +9 -0
  48. package/dist/types/modes/interactive-mode.d.ts +1 -0
  49. package/dist/types/modes/utils/interrupt-ownership.d.ts +3 -0
  50. package/dist/types/modes/utils/keybinding-matchers.d.ts +1 -0
  51. package/dist/types/modes/utils/macos-option-forwarding.d.ts +17 -0
  52. package/dist/types/progress/collect-project-progress.d.ts +38 -0
  53. package/dist/types/progress/progress-contract.d.ts +154 -0
  54. package/dist/types/progress/progress-snapshot.d.ts +11 -0
  55. package/dist/types/progress/project-progress.d.ts +141 -0
  56. package/dist/types/progress/render-progress.d.ts +28 -0
  57. package/dist/types/runtime-mcp/oauth-public-url.d.ts +1 -0
  58. package/dist/types/runtime-mcp/plugin-network-boundary.d.ts +1 -0
  59. package/dist/types/runtime-mcp/smithery-registry.d.ts +24 -0
  60. package/dist/types/runtime-mcp/types.d.ts +6 -0
  61. package/dist/types/sdk/acp/adapter.d.ts +1 -1
  62. package/dist/types/sdk/broker/broker-exit.d.ts +6 -2
  63. package/dist/types/sdk/broker/broker.d.ts +18 -1
  64. package/dist/types/sdk/broker/session-index.d.ts +12 -6
  65. package/dist/types/sdk/bus/index.d.ts +12 -1
  66. package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +6 -1
  67. package/dist/types/sdk/client/client.d.ts +2 -0
  68. package/dist/types/sdk/host/query/handlers.d.ts +3 -0
  69. package/dist/types/sdk/index.d.ts +1 -0
  70. package/dist/types/sdk/router/session-router.d.ts +2 -0
  71. package/dist/types/sdk/session-list.d.ts +1 -1
  72. package/dist/types/sdk/session.d.ts +8 -0
  73. package/dist/types/session/agent-session.d.ts +38 -9
  74. package/dist/types/session/artifacts.d.ts +2 -2
  75. package/dist/types/session/internal/managed-gc-retirement-codec.d.ts +32 -0
  76. package/dist/types/session/internal/managed-session-scope.d.ts +79 -2
  77. package/dist/types/session/internal/managed-session-storage.d.ts +26 -1
  78. package/dist/types/session/internal/managed-task-owner-cleanup.d.ts +62 -0
  79. package/dist/types/session/internal/task-artifact-owner-access.d.ts +2 -0
  80. package/dist/types/session/internal/task-artifact-owner-transcript.d.ts +9 -1
  81. package/dist/types/session/session-manager.d.ts +31 -5
  82. package/dist/types/session/session-retirement.d.ts +31 -7
  83. package/dist/types/session/session-storage.d.ts +62 -2
  84. package/dist/types/session/task-artifact-owner-codec.d.ts +2 -0
  85. package/dist/types/setup/credential-auto-import.d.ts +1 -1
  86. package/dist/types/setup/credential-import.d.ts +6 -4
  87. package/dist/types/skill-state/active-state.d.ts +38 -0
  88. package/dist/types/slash-commands/helpers/progress-report.d.ts +8 -0
  89. package/dist/types/task/executor.d.ts +3 -0
  90. package/dist/types/tools/bash-skill-urls.d.ts +1 -1
  91. package/dist/types/tools/index.d.ts +3 -0
  92. package/dist/types/tools/python.d.ts +9 -1
  93. package/dist/types/tools/recipe/recipe-guard.d.ts +7 -0
  94. package/dist/types/tools/restricted-cwd.d.ts +1 -0
  95. package/dist/types/tools/todo-write.d.ts +6 -0
  96. package/dist/types/utils/tools-checksum.d.ts +7 -0
  97. package/package.json +7 -7
  98. package/scripts/generate-sdk-operation-inventory.ts +21 -0
  99. package/scripts/verify-managed-empty-stop.ts +28 -0
  100. package/src/async/job-manager.ts +228 -122
  101. package/src/capability/index.ts +17 -0
  102. package/src/capability/mcp.ts +2 -0
  103. package/src/capability/skill.ts +2 -0
  104. package/src/capability/types.ts +2 -0
  105. package/src/cli/auth-broker-cli.ts +1 -1
  106. package/src/cli/customize-doctor.ts +15 -2
  107. package/src/cli/public-command-errors.ts +21 -1
  108. package/src/cli/setup-cli.ts +4 -4
  109. package/src/cli/skills-cli.ts +5 -0
  110. package/src/commands/deep-interview.ts +47 -8
  111. package/src/commands/sdk.ts +28 -8
  112. package/src/config/autorouting-tier-map.ts +117 -7
  113. package/src/config/file-lock.ts +307 -68
  114. package/src/config/mcp-schema.json +4 -0
  115. package/src/config/model-bindings-applier.ts +20 -0
  116. package/src/config/model-profile-activation.ts +240 -46
  117. package/src/config/model-registry.ts +47 -153
  118. package/src/config/skill-settings-defaults.ts +7 -0
  119. package/src/coordinator-mcp/policy.ts +3 -2
  120. package/src/cursor-delete-path.ts +10 -0
  121. package/src/cursor-mcp-name.ts +15 -0
  122. package/src/cursor.ts +13 -2
  123. package/src/customization/inventory.ts +3 -0
  124. package/src/debug/report-bundle-parent.ts +22 -0
  125. package/src/debug/report-bundle.ts +3 -0
  126. package/src/debug/system-info.ts +13 -1
  127. package/src/defaults/gjc/extensions/grok-cli-vendor/src/provider/stream.ts +67 -4
  128. package/src/defaults/gjc/extensions/grok-cli-vendor/src/provider/version-manager.ts +80 -0
  129. package/src/defaults/gjc/skills/deep-interview/SKILL.md +105 -10
  130. package/src/defaults/gjc/skills/ralplan/SKILL.md +6 -4
  131. package/src/defaults/gjc-defaults.test.ts +18 -1
  132. package/src/defaults/gjc-defaults.ts +3 -0
  133. package/src/defaults/gjc-skills.generated.ts +58 -36
  134. package/src/discovery/agents.ts +6 -1
  135. package/src/discovery/builtin.ts +10 -0
  136. package/src/discovery/helpers.ts +159 -20
  137. package/src/discovery/mcp-json.ts +6 -0
  138. package/src/discovery/ssh.ts +1 -1
  139. package/src/discovery/substitute-plugin-root.ts +3 -1
  140. package/src/edit/file-read-cache.ts +1 -1
  141. package/src/eval/py/executor.ts +380 -120
  142. package/src/export/html/css-color.ts +9 -0
  143. package/src/export/html/index.ts +5 -4
  144. package/src/export/html/template.generated.ts +1 -1
  145. package/src/extensibility/extensions/runner.ts +4 -0
  146. package/src/extensibility/extensions/types.ts +7 -0
  147. package/src/extensibility/gjc-plugins/runtime-adapters.ts +73 -8
  148. package/src/extensibility/plugins/manager.ts +3 -2
  149. package/src/extensibility/plugins/marketplace/cache.ts +15 -0
  150. package/src/extensibility/plugins/marketplace/manager.ts +11 -3
  151. package/src/extensibility/plugins/plugin-link-path.ts +12 -0
  152. package/src/extensibility/runtime-skill-discovery.ts +20 -2
  153. package/src/extensibility/skill-management.ts +6 -2
  154. package/src/extensibility/skills.ts +6 -1
  155. package/src/gjc-runtime/deep-interview-crystallize.ts +1814 -0
  156. package/src/gjc-runtime/deep-interview-recorder.ts +20 -2
  157. package/src/gjc-runtime/deep-interview-runtime.ts +1415 -13
  158. package/src/gjc-runtime/deep-interview-stage.ts +98 -21
  159. package/src/gjc-runtime/gc-runtime.ts +206 -9
  160. package/src/gjc-runtime/launch-worktree.ts +14 -4
  161. package/src/gjc-runtime/ralplan-runtime.ts +447 -52
  162. package/src/gjc-runtime/state-argv.ts +2 -0
  163. package/src/gjc-runtime/state-migrations.ts +91 -29
  164. package/src/gjc-runtime/state-runtime.ts +3716 -135
  165. package/src/gjc-runtime/state-writer.ts +83 -8
  166. package/src/gjc-runtime/ultragoal-runtime.ts +16 -4
  167. package/src/gjc-runtime/workflow-manifest.generated.json +70 -42
  168. package/src/gjc-runtime/workflow-manifest.ts +54 -21
  169. package/src/harness-control-plane/operate.ts +12 -4
  170. package/src/harness-control-plane/owner.ts +5 -1
  171. package/src/hashline/parser.ts +48 -9
  172. package/src/hashline/recovery.ts +13 -4
  173. package/src/internal-urls/docs-index.generated.ts +18 -17
  174. package/src/internal-urls/skill-path.ts +19 -0
  175. package/src/internal-urls/skill-protocol.ts +2 -0
  176. package/src/lsp/edits.ts +6 -0
  177. package/src/lsp/workspace-path.ts +51 -0
  178. package/src/main.ts +195 -31
  179. package/src/modes/acp/acp-agent.ts +88 -4
  180. package/src/modes/components/custom-editor.ts +14 -0
  181. package/src/modes/components/customization/customization-dashboard.ts +12 -0
  182. package/src/modes/components/extensions/extension-dashboard.ts +2 -2
  183. package/src/modes/components/extensions/state-manager.ts +15 -5
  184. package/src/modes/components/model-selector.ts +1 -4
  185. package/src/modes/components/provider-onboarding-selector.ts +1 -1
  186. package/src/modes/components/status-line/segments.ts +4 -0
  187. package/src/modes/controllers/event-controller.ts +11 -4
  188. package/src/modes/controllers/input-controller.ts +74 -29
  189. package/src/modes/controllers/runtime-mcp-command-controller.ts +8 -3
  190. package/src/modes/controllers/selector-controller.ts +28 -15
  191. package/src/modes/interactive-mode.ts +4 -0
  192. package/src/modes/runtime-init.ts +4 -0
  193. package/src/modes/utils/abort-message.ts +3 -1
  194. package/src/modes/utils/interrupt-ownership.ts +12 -0
  195. package/src/modes/utils/keybinding-matchers.ts +4 -0
  196. package/src/modes/utils/macos-option-forwarding.ts +80 -0
  197. package/src/progress/collect-project-progress.ts +153 -0
  198. package/src/progress/progress-contract.ts +172 -0
  199. package/src/progress/progress-snapshot.ts +140 -0
  200. package/src/progress/project-progress.ts +446 -0
  201. package/src/progress/render-progress.ts +209 -0
  202. package/src/prompts/system/master-mode.md +1 -0
  203. package/src/prompts/tools/skill-discovery.md +1 -1
  204. package/src/runtime-mcp/config-writer.ts +10 -6
  205. package/src/runtime-mcp/config.ts +1 -0
  206. package/src/runtime-mcp/oauth-flow.ts +5 -0
  207. package/src/runtime-mcp/oauth-public-url.ts +8 -0
  208. package/src/runtime-mcp/plugin-network-boundary.ts +4 -0
  209. package/src/runtime-mcp/smithery-registry.ts +22 -3
  210. package/src/runtime-mcp/transports/http.ts +2 -2
  211. package/src/runtime-mcp/types.ts +6 -0
  212. package/src/sdk/acp/adapter.ts +93 -8
  213. package/src/sdk/broker/broker-exit.ts +52 -23
  214. package/src/sdk/broker/broker.ts +75 -6
  215. package/src/sdk/broker/ensure.ts +2 -0
  216. package/src/sdk/broker/lifecycle.ts +711 -66
  217. package/src/sdk/broker/session-index.ts +191 -81
  218. package/src/sdk/bus/chat-daemon-cli.ts +38 -1
  219. package/src/sdk/bus/chat-daemon-control.ts +2 -2
  220. package/src/sdk/bus/index.ts +238 -43
  221. package/src/sdk/bus/telegram-daemon-contract.ts +6 -1
  222. package/src/sdk/cli/session-cli.ts +17 -3
  223. package/src/sdk/client/client.ts +28 -8
  224. package/src/sdk/host/query/handlers.ts +9 -1
  225. package/src/sdk/host/session-runtime.test.ts +2647 -294
  226. package/src/sdk/host/session-runtime.ts +426 -109
  227. package/src/sdk/host/surface-policy.ts +1 -0
  228. package/src/sdk/index.ts +8 -0
  229. package/src/sdk/lifecycle-session.ts +3 -1
  230. package/src/sdk/prompt-deadline-manager.ts +15 -0
  231. package/src/sdk/protocol/operation-inventory.generated.json +146 -0
  232. package/src/sdk/protocol/operation-registry.ts +9 -2
  233. package/src/sdk/router/session-router.ts +75 -12
  234. package/src/sdk/session-list.ts +1 -0
  235. package/src/sdk/session.ts +67 -38
  236. package/src/session/agent-session.ts +623 -224
  237. package/src/session/artifacts.ts +174 -70
  238. package/src/session/internal/managed-gc-retirement-codec.ts +319 -0
  239. package/src/session/internal/managed-session-scope.ts +3457 -462
  240. package/src/session/internal/managed-session-storage.ts +376 -61
  241. package/src/session/internal/managed-task-owner-cleanup.ts +473 -0
  242. package/src/session/internal/task-artifact-owner-access.ts +19 -3
  243. package/src/session/internal/task-artifact-owner-transcript.ts +547 -1
  244. package/src/session/session-manager.ts +939 -133
  245. package/src/session/session-retirement.ts +573 -14
  246. package/src/session/session-storage.ts +417 -14
  247. package/src/session/task-artifact-owner-codec.ts +71 -8
  248. package/src/session/task-artifact-owner-retirement.ts +21 -19
  249. package/src/setup/credential-auto-import.ts +12 -3
  250. package/src/setup/credential-import.ts +176 -5
  251. package/src/setup/paseo/announce.ts +6 -1
  252. package/src/skill-state/active-state.ts +132 -2
  253. package/src/slash-commands/builtin-registry.ts +39 -9
  254. package/src/slash-commands/helpers/progress-report.ts +23 -0
  255. package/src/stt/setup.ts +3 -5
  256. package/src/task/commands.ts +3 -1
  257. package/src/task/executor.ts +4 -0
  258. package/src/task/index.ts +6 -0
  259. package/src/tools/ask.ts +205 -6
  260. package/src/tools/ast-edit.ts +4 -0
  261. package/src/tools/bash-allowed-prefixes.ts +15 -0
  262. package/src/tools/bash-skill-urls.ts +56 -3
  263. package/src/tools/bash.ts +48 -79
  264. package/src/tools/descriptors.ts +5 -1
  265. package/src/tools/eval.ts +56 -36
  266. package/src/tools/image-gen.ts +1 -1
  267. package/src/tools/index.ts +3 -0
  268. package/src/tools/python.ts +187 -66
  269. package/src/tools/recipe/index.ts +9 -0
  270. package/src/tools/recipe/recipe-guard.ts +19 -0
  271. package/src/tools/restricted-cwd.ts +10 -0
  272. package/src/tools/skill-discovery.ts +5 -0
  273. package/src/tools/skill.ts +28 -1
  274. package/src/tools/todo-write.ts +10 -1
  275. package/src/tools/tool-catalog.generated.ts +1 -1
  276. package/src/tools/write.ts +1 -0
  277. package/src/utils/tools-checksum.ts +34 -0
  278. package/src/utils/tools-manager.ts +24 -6
  279. package/src/web/search/providers/codex.ts +3 -2
@@ -0,0 +1,446 @@
1
+ /**
2
+ * Read-only project progress projection. One report is re-shaped into the
3
+ * machine-facing `session.progress` SDK snapshot (`progress-snapshot.ts`), and
4
+ * the human-facing `/progress` view renders that same snapshot
5
+ * (`render-progress.ts`) in the TUI, text mode, and ACP.
6
+ *
7
+ * The completion indicator is derived exclusively from countable units that
8
+ * carry a durable completion status. It never weights units by size, never
9
+ * credits partial work on an open unit, and never infers progress from
10
+ * conversation text, elapsed time, or token spend. When no countable basis
11
+ * exists the indicator is reported as unknown instead of guessed.
12
+ */
13
+ import type { SubagentLifecycle } from "../async/job-manager";
14
+ import { selectNextSchedulableGoal, type UltragoalGoalStatus } from "../gjc-runtime/ultragoal-runtime";
15
+ import type { GoalStatus } from "../goals/state";
16
+ import type { WorkflowHudSeverity } from "../skill-state/active-state";
17
+ import type { TodoStatus } from "../tools/todo-write";
18
+ import type {
19
+ ProgressAgentCounts,
20
+ ProgressBasisKind,
21
+ ProgressDurableSource,
22
+ ProgressHeadline,
23
+ ProgressReviewVerdict,
24
+ ProgressSignalKind,
25
+ ProgressSignalSource,
26
+ } from "./progress-contract";
27
+
28
+ export interface ProgressGoalInput {
29
+ objective: string;
30
+ status: GoalStatus;
31
+ timeUsedSeconds: number;
32
+ }
33
+
34
+ export interface ProgressTodoInput {
35
+ content: string;
36
+ status: TodoStatus;
37
+ }
38
+
39
+ export interface ProgressWorkflowChip {
40
+ label: string;
41
+ value?: string;
42
+ severity?: WorkflowHudSeverity;
43
+ }
44
+
45
+ export interface ProgressWorkflowInput {
46
+ skill: string;
47
+ phase: string;
48
+ summary?: string;
49
+ chips: ProgressWorkflowChip[];
50
+ }
51
+
52
+ export interface ProgressStoryInput {
53
+ id: string;
54
+ title: string;
55
+ status: UltragoalGoalStatus;
56
+ /** A completion-verification (quality-gate) receipt is recorded on the story. */
57
+ hasVerificationReceipt: boolean;
58
+ }
59
+
60
+ export interface ProgressUltragoalInput {
61
+ objective: string;
62
+ stories: ProgressStoryInput[];
63
+ }
64
+
65
+ export const PROGRESS_SOURCE_LABELS: Record<ProgressDurableSource, string> = {
66
+ "workflow-state": "Workflow state",
67
+ "ultragoal-plan": "Ultragoal plan",
68
+ };
69
+
70
+ export interface ProjectProgressInput {
71
+ goal?: ProgressGoalInput;
72
+ todos: ProgressTodoInput[];
73
+ workflows: ProgressWorkflowInput[];
74
+ ultragoal?: ProgressUltragoalInput;
75
+ subagents: SubagentLifecycle[];
76
+ /** Whether session-scoped `.gjc` state (workflows, ultragoal plan) was consulted at all. */
77
+ sessionStateRead: boolean;
78
+ /** Durable sources that exist but could not be read; excluded from the estimate. */
79
+ unreadable: ProgressDurableSource[];
80
+ /** Durable sources whose primary record was unreadable but whose contents were recovered from authoritative per-entry records. */
81
+ recovered: ProgressDurableSource[];
82
+ /** Durable sources whose readable derived snapshot listed entries the authoritative per-entry records do not; those stale entries were discarded. */
83
+ discarded: ProgressDurableSource[];
84
+ /** Durable sources listing entries whose authority could not be established; those entries are excluded and their absence is unconfirmed. */
85
+ unresolved: ProgressDurableSource[];
86
+ }
87
+
88
+ export interface ProgressSignal {
89
+ kind: ProgressSignalKind;
90
+ /** Which durable state produced the signal. */
91
+ source: ProgressSignalSource;
92
+ /** Stable identifier inside the source: story id, workflow skill, or durable source key. */
93
+ ref?: string;
94
+ text: string;
95
+ }
96
+
97
+ export interface ProgressCompletion {
98
+ basis: ProgressBasisKind;
99
+ done: number;
100
+ total: number;
101
+ /**
102
+ * Displayed whole percent, or undefined when no countable basis exists.
103
+ * Never rounds up to 100 while a unit is open, nor down to 0 once a unit is done.
104
+ */
105
+ percent: number | undefined;
106
+ }
107
+
108
+ export interface ProgressStoryCounts {
109
+ total: number;
110
+ complete: number;
111
+ active: number;
112
+ pending: number;
113
+ blocked: number;
114
+ superseded: number;
115
+ verified: number;
116
+ /** Active story, else the next pending one; blocked, review-blocked, and failed stories are never selected. */
117
+ current?: { id: string; title: string };
118
+ }
119
+
120
+ export interface ProgressTodoCounts {
121
+ total: number;
122
+ completed: number;
123
+ inProgress: number;
124
+ pending: number;
125
+ abandoned: number;
126
+ current?: string;
127
+ }
128
+
129
+ /** Verification evidence that durable state actually records; nothing is inferred. */
130
+ export interface ProgressVerification {
131
+ /** Complete stories that carry a quality-gate completion receipt (only when stories are complete). */
132
+ storyReceipts?: { complete: number; withReceipt: number };
133
+ /** Latest review verdict chips published by active workflows. */
134
+ reviewVerdicts: ProgressReviewVerdict[];
135
+ }
136
+
137
+ export interface ProjectProgressReport {
138
+ headline: ProgressHeadline;
139
+ completion: ProgressCompletion;
140
+ /** How the completion indicator was derived, in plain words. */
141
+ basisExplanation: string;
142
+ goal?: ProgressGoalInput;
143
+ /** The ultragoal plan the story counts were derived from. */
144
+ ultragoal?: ProgressUltragoalInput;
145
+ stories?: ProgressStoryCounts;
146
+ todoItems: ProgressTodoInput[];
147
+ todos?: ProgressTodoCounts;
148
+ workflows: ProgressWorkflowInput[];
149
+ agents: ProgressAgentCounts;
150
+ verification: ProgressVerification;
151
+ signals: ProgressSignal[];
152
+ sessionStateRead: boolean;
153
+ unreadable: ProgressDurableSource[];
154
+ recovered: ProgressDurableSource[];
155
+ discarded: ProgressDurableSource[];
156
+ unresolved: ProgressDurableSource[];
157
+ }
158
+
159
+ const BLOCKING_STORY_STATUSES: ReadonlySet<UltragoalGoalStatus> = new Set(["failed", "blocked", "review_blocked"]);
160
+
161
+ function plural(count: number, singular: string, pluralForm = `${singular}s`): string {
162
+ return `${count} ${count === 1 ? singular : pluralForm}`;
163
+ }
164
+
165
+ function countStories(ultragoal: ProgressUltragoalInput): ProgressStoryCounts {
166
+ const counts: ProgressStoryCounts = {
167
+ total: 0,
168
+ complete: 0,
169
+ active: 0,
170
+ pending: 0,
171
+ blocked: 0,
172
+ superseded: 0,
173
+ verified: 0,
174
+ };
175
+ for (const story of ultragoal.stories) {
176
+ if (story.status === "superseded") {
177
+ counts.superseded++;
178
+ continue;
179
+ }
180
+ counts.total++;
181
+ if (story.status === "complete") {
182
+ counts.complete++;
183
+ if (story.hasVerificationReceipt) counts.verified++;
184
+ } else if (story.status === "active") counts.active++;
185
+ else if (story.status === "pending") counts.pending++;
186
+ else if (BLOCKING_STORY_STATUSES.has(story.status)) counts.blocked++;
187
+ }
188
+ // Mirror the Ultragoal scheduler. A read-only projection never opts into
189
+ // `--retry-failed`, so failed stories are not presented as next work.
190
+ const current = selectNextSchedulableGoal(ultragoal.stories, false);
191
+ if (current) counts.current = { id: current.id, title: current.title };
192
+ return counts;
193
+ }
194
+
195
+ function countTodos(todos: readonly ProgressTodoInput[]): ProgressTodoCounts {
196
+ const counts: ProgressTodoCounts = { total: 0, completed: 0, inProgress: 0, pending: 0, abandoned: 0 };
197
+ for (const todo of todos) {
198
+ if (todo.status === "abandoned") {
199
+ counts.abandoned++;
200
+ continue;
201
+ }
202
+ counts.total++;
203
+ if (todo.status === "completed") counts.completed++;
204
+ else if (todo.status === "in_progress") counts.inProgress++;
205
+ else counts.pending++;
206
+ }
207
+ const current = todos.find(todo => todo.status === "in_progress") ?? todos.find(todo => todo.status === "pending");
208
+ if (current) counts.current = current.content;
209
+ return counts;
210
+ }
211
+
212
+ function countAgents(statuses: readonly SubagentLifecycle[]): ProgressAgentCounts {
213
+ const counts: ProgressAgentCounts = {
214
+ total: statuses.length,
215
+ running: 0,
216
+ waiting: 0,
217
+ completed: 0,
218
+ failed: 0,
219
+ cancelled: 0,
220
+ };
221
+ for (const status of statuses) {
222
+ if (status === "running") counts.running++;
223
+ else if (status === "queued" || status === "paused") counts.waiting++;
224
+ else if (status === "completed") counts.completed++;
225
+ else if (status === "failed") counts.failed++;
226
+ else counts.cancelled++;
227
+ }
228
+ return counts;
229
+ }
230
+
231
+ /** Whole percent that never overstates (no 100 while open) nor erases (no 0 once started) progress. */
232
+ export function displayPercent(done: number, total: number): number | undefined {
233
+ if (total <= 0) return undefined;
234
+ if (done >= total) return 100;
235
+ if (done <= 0) return 0;
236
+ return Math.min(99, Math.max(1, Math.round((done / total) * 100)));
237
+ }
238
+
239
+ function selectCompletion(
240
+ stories: ProgressStoryCounts | undefined,
241
+ todos: ProgressTodoCounts | undefined,
242
+ goal: ProgressGoalInput | undefined,
243
+ ): { completion: ProgressCompletion; explanation: string } {
244
+ if (stories && stories.total > 0) {
245
+ return {
246
+ completion: {
247
+ basis: "ultragoal-stories",
248
+ done: stories.complete,
249
+ total: stories.total,
250
+ percent: displayPercent(stories.complete, stories.total),
251
+ },
252
+ explanation:
253
+ "Ultragoal stories marked complete in the durable plan (goals.json). Every story counts equally regardless of size; superseded stories are excluded; open stories earn no partial credit.",
254
+ };
255
+ }
256
+ if (todos && todos.total > 0) {
257
+ return {
258
+ completion: {
259
+ basis: "todos",
260
+ done: todos.completed,
261
+ total: todos.total,
262
+ percent: displayPercent(todos.completed, todos.total),
263
+ },
264
+ explanation:
265
+ "Session todo items marked completed. Every item counts equally regardless of size; abandoned items are excluded; in-progress items earn no partial credit.",
266
+ };
267
+ }
268
+ if (goal?.status === "complete") {
269
+ return {
270
+ completion: { basis: "goal-status", done: 1, total: 1, percent: 100 },
271
+ explanation: "The session goal is marked complete; no finer-grained plan or todo list was recorded.",
272
+ };
273
+ }
274
+ return {
275
+ completion: { basis: "none", done: 0, total: 0, percent: undefined },
276
+ explanation:
277
+ "No ultragoal plan or todo list is recorded, so there is nothing countable to measure. Create a plan (/skill:ultragoal) or a todo list to get a completion estimate.",
278
+ };
279
+ }
280
+
281
+ function workflowSignals(workflows: readonly ProgressWorkflowInput[]): ProgressSignal[] {
282
+ const signals: ProgressSignal[] = [];
283
+ for (const workflow of workflows) {
284
+ for (const chip of workflow.chips) {
285
+ const text = `${workflow.skill}: ${chip.label}${chip.value ? ` ${chip.value}` : ""}`;
286
+ const base = { source: "workflow" as const, ref: workflow.skill, text };
287
+ if (chip.severity === "blocked" || chip.severity === "error") signals.push({ kind: "blocker", ...base });
288
+ else if (chip.severity === "warning") signals.push({ kind: "pending", ...base });
289
+ }
290
+ }
291
+ return signals;
292
+ }
293
+
294
+ function collectVerification(
295
+ stories: ProgressStoryCounts | undefined,
296
+ workflows: readonly ProgressWorkflowInput[],
297
+ ): ProgressVerification {
298
+ const reviewVerdicts: ProgressReviewVerdict[] = [];
299
+ for (const workflow of workflows) {
300
+ const verdict = workflow.chips.find(chip => chip.label === "verdict" && chip.value);
301
+ if (verdict?.value) reviewVerdicts.push({ skill: workflow.skill, verdict: verdict.value });
302
+ }
303
+ return {
304
+ ...(stories && stories.complete > 0
305
+ ? { storyReceipts: { complete: stories.complete, withReceipt: stories.verified } }
306
+ : {}),
307
+ reviewVerdicts,
308
+ };
309
+ }
310
+
311
+ export function computeProjectProgress(input: ProjectProgressInput): ProjectProgressReport {
312
+ const stories = input.ultragoal ? countStories(input.ultragoal) : undefined;
313
+ const todos = input.todos.length > 0 ? countTodos(input.todos) : undefined;
314
+ const agents = countAgents(input.subagents);
315
+ const { completion, explanation } = selectCompletion(stories, todos, input.goal);
316
+
317
+ const signals: ProgressSignal[] = [];
318
+ if (input.ultragoal) {
319
+ for (const story of input.ultragoal.stories) {
320
+ if (BLOCKING_STORY_STATUSES.has(story.status)) {
321
+ signals.push({
322
+ kind: "blocker",
323
+ source: "ultragoal",
324
+ ref: story.id,
325
+ text: `Story ${story.id} is ${story.status.replace("_", " ")}: ${story.title}`,
326
+ });
327
+ }
328
+ }
329
+ }
330
+ // The ultragoal HUD chips summarize the same goals.json stories reported above;
331
+ // repeating them would list every blocked story twice.
332
+ signals.push(
333
+ ...workflowSignals(
334
+ input.ultragoal ? input.workflows.filter(workflow => workflow.skill !== "ultragoal") : input.workflows,
335
+ ),
336
+ );
337
+ if (agents.running + agents.waiting > 0) {
338
+ signals.push({
339
+ kind: "pending",
340
+ source: "subagents",
341
+ text: `${plural(agents.running + agents.waiting, "subagent")} still running, queued, or paused; their work counts only once it is recorded in the plan or todos.`,
342
+ });
343
+ }
344
+ if (agents.failed > 0)
345
+ signals.push({
346
+ kind: "note",
347
+ source: "subagents",
348
+ text: `${plural(agents.failed, "subagent")} failed this session.`,
349
+ });
350
+ if (completion.basis === "ultragoal-stories" && todos && todos.inProgress + todos.pending > 0) {
351
+ signals.push({
352
+ kind: "note",
353
+ source: "todos",
354
+ text: `Open todos (${todos.inProgress + todos.pending}) are tracked separately; the estimate follows the ultragoal plan.`,
355
+ });
356
+ }
357
+ for (const source of input.unreadable) {
358
+ signals.push({
359
+ kind: "note",
360
+ source: "state",
361
+ ref: source,
362
+ text: `${PROGRESS_SOURCE_LABELS[source]} could not be read and is excluded from the estimate.`,
363
+ });
364
+ }
365
+ for (const source of input.recovered) {
366
+ signals.push({
367
+ kind: "note",
368
+ source: "state",
369
+ ref: source,
370
+ text: `${PROGRESS_SOURCE_LABELS[source]} snapshot could not be read; its contents were recovered from authoritative per-entry records.`,
371
+ });
372
+ }
373
+ for (const source of input.discarded) {
374
+ signals.push({
375
+ kind: "note",
376
+ source: "state",
377
+ ref: source,
378
+ text: `${PROGRESS_SOURCE_LABELS[source]} snapshot is stale; workflows it lists without an authoritative per-entry record were discarded, not reported as active.`,
379
+ });
380
+ }
381
+ for (const source of input.unresolved) {
382
+ signals.push({
383
+ kind: "note",
384
+ source: "state",
385
+ ref: source,
386
+ text: `${PROGRESS_SOURCE_LABELS[source]} lists workflows whose authority cannot be established; they are not reported as active, and their absence is unconfirmed.`,
387
+ });
388
+ }
389
+
390
+ const headline = selectHeadline(input, completion, stories, todos, agents, signals);
391
+ if (headline === "awaiting-completion" && input.goal && input.goal.status !== "complete") {
392
+ signals.push({
393
+ kind: "pending",
394
+ source: "goal",
395
+ text: "All tracked units are done, but the session goal is not marked complete yet.",
396
+ });
397
+ }
398
+
399
+ return {
400
+ headline,
401
+ completion,
402
+ basisExplanation: explanation,
403
+ ...(input.goal ? { goal: input.goal } : {}),
404
+ ...(input.ultragoal ? { ultragoal: input.ultragoal } : {}),
405
+ ...(stories ? { stories } : {}),
406
+ todoItems: input.todos,
407
+ ...(todos ? { todos } : {}),
408
+ workflows: input.workflows,
409
+ agents,
410
+ verification: collectVerification(stories, input.workflows),
411
+ signals,
412
+ sessionStateRead: input.sessionStateRead,
413
+ unreadable: input.unreadable,
414
+ recovered: input.recovered,
415
+ discarded: input.discarded,
416
+ unresolved: input.unresolved,
417
+ };
418
+ }
419
+
420
+ function selectHeadline(
421
+ input: ProjectProgressInput,
422
+ completion: ProgressCompletion,
423
+ stories: ProgressStoryCounts | undefined,
424
+ todos: ProgressTodoCounts | undefined,
425
+ agents: ProgressAgentCounts,
426
+ signals: readonly ProgressSignal[],
427
+ ): ProgressHeadline {
428
+ const goalStatus = input.goal?.status;
429
+ if (goalStatus === "dropped") return "dropped";
430
+ if (signals.some(signal => signal.kind === "blocker")) return "blocked";
431
+ const allDone = completion.total > 0 && completion.done >= completion.total;
432
+ if (allDone) {
433
+ const goalOpen = goalStatus !== undefined && goalStatus !== "complete";
434
+ return goalOpen || agents.running + agents.waiting > 0 ? "awaiting-completion" : "complete";
435
+ }
436
+ if (goalStatus === "paused") return "paused";
437
+ const somethingMoving =
438
+ completion.done > 0 ||
439
+ (stories?.active ?? 0) > 0 ||
440
+ (todos?.inProgress ?? 0) > 0 ||
441
+ agents.running > 0 ||
442
+ goalStatus === "active";
443
+ if (somethingMoving) return "in-progress";
444
+ if (completion.total > 0) return "not-started";
445
+ return input.workflows.length > 0 ? "in-progress" : "no-tracked-work";
446
+ }
@@ -0,0 +1,209 @@
1
+ /**
2
+ * Human-readable `/progress` view. It renders the machine-facing
3
+ * `session.progress` snapshot, so the TUI, text mode, and ACP (which reads the
4
+ * snapshot over the SDK) show exactly what an orchestrator receives.
5
+ *
6
+ * Like `progress-contract.ts`, this module must stay a dependency leaf: the ACP
7
+ * agent imports it, and the ACP entrypoint may not reach the session graph.
8
+ */
9
+ import {
10
+ boundSnapshotText,
11
+ type ProgressBasisKind,
12
+ type ProgressHeadline,
13
+ type ProjectProgressSnapshot,
14
+ } from "./progress-contract";
15
+
16
+ /** ACP palette copy for `/progress`, shared by the builtin registry and the production ACP agent. */
17
+ export const PROGRESS_COMMAND_ACP_DESCRIPTION = "Show read-only project progress overview";
18
+
19
+ export const PROGRESS_HEADLINE_LABELS: Record<ProgressHeadline, string> = {
20
+ complete: "Complete",
21
+ "awaiting-completion": "Tracked work done, awaiting completion",
22
+ blocked: "Blocked",
23
+ "in-progress": "In progress",
24
+ "not-started": "Not started",
25
+ paused: "Paused",
26
+ dropped: "Dropped",
27
+ "no-tracked-work": "No tracked work",
28
+ };
29
+
30
+ const BASIS_UNIT_LABELS: Record<Exclude<ProgressBasisKind, "none">, string> = {
31
+ "ultragoal-stories": "ultragoal stories complete",
32
+ todos: "todo items completed",
33
+ "goal-status": "session goal complete",
34
+ };
35
+
36
+ /** Styling hooks so the TUI can colour output while ACP/text mode stays plain. */
37
+ export interface ProgressRenderStyle {
38
+ bold(text: string): string;
39
+ fg(color: "accent" | "success" | "warning" | "error" | "dim" | "muted", text: string): string;
40
+ }
41
+
42
+ export interface ProgressRenderOptions {
43
+ style?: ProgressRenderStyle;
44
+ /**
45
+ * Sanitizes and bounds durable text to one display line of at most `max` columns.
46
+ * Defaults to a character-bounded plain-text clip for ACP and text mode.
47
+ */
48
+ clip?: (text: string, max: number) => string;
49
+ barWidth?: number;
50
+ }
51
+
52
+ const PLAIN_STYLE: ProgressRenderStyle = { bold: text => text, fg: (_color, text) => text };
53
+
54
+ function plural(count: number, singular: string, pluralForm = `${singular}s`): string {
55
+ return `${count} ${count === 1 ? singular : pluralForm}`;
56
+ }
57
+
58
+ function headlineColor(headline: ProgressHeadline): "success" | "warning" | "error" | "accent" | "muted" {
59
+ switch (headline) {
60
+ case "complete":
61
+ return "success";
62
+ case "blocked":
63
+ return "error";
64
+ case "awaiting-completion":
65
+ case "paused":
66
+ return "warning";
67
+ case "dropped":
68
+ case "no-tracked-work":
69
+ return "muted";
70
+ default:
71
+ return "accent";
72
+ }
73
+ }
74
+
75
+ function formatElapsed(seconds: number): string {
76
+ const minutes = Math.floor(Math.max(0, seconds) / 60);
77
+ if (minutes < 1) return "<1m";
78
+ const hours = Math.floor(minutes / 60);
79
+ return hours > 0 ? `${hours}h ${minutes % 60}m` : `${minutes}m`;
80
+ }
81
+
82
+ /** Human-readable verification lines; the structured form is `snapshot.verification`. */
83
+ function verificationLines(verification: ProjectProgressSnapshot["verification"]): string[] {
84
+ const lines: string[] = [];
85
+ const receipts = verification.storyReceipts;
86
+ if (receipts) {
87
+ lines.push(
88
+ `${receipts.withReceipt} of ${plural(receipts.complete, "complete story", "complete stories")} carry a recorded quality-gate receipt`,
89
+ );
90
+ }
91
+ for (const { skill, verdict } of verification.reviewVerdicts) lines.push(`${skill} review verdict: ${verdict}`);
92
+ return lines;
93
+ }
94
+
95
+ /** Render the overview as display lines (no trailing newline). */
96
+ export function renderProgressSnapshot(
97
+ snapshot: ProjectProgressSnapshot,
98
+ options: ProgressRenderOptions = {},
99
+ ): string[] {
100
+ const style = options.style ?? PLAIN_STYLE;
101
+ const clip = options.clip ?? boundSnapshotText;
102
+ const width = options.barWidth ?? 24;
103
+ const label = (text: string) => style.fg("dim", text.padEnd(14));
104
+ const lines: string[] = [];
105
+ const color = headlineColor(snapshot.state);
106
+
107
+ lines.push(`${style.bold("Project progress")} ${style.fg(color, PROGRESS_HEADLINE_LABELS[snapshot.state])}`);
108
+ const { completion } = snapshot;
109
+ if (completion.percent === null || completion.basis === "none") {
110
+ lines.push(`${style.fg("dim", `[${"·".repeat(width)}]`)} unknown`);
111
+ } else {
112
+ const filled = Math.round((completion.percent / 100) * width);
113
+ const bar = `${style.fg(color, "█".repeat(filled))}${style.fg("dim", "░".repeat(width - filled))}`;
114
+ const unit = BASIS_UNIT_LABELS[completion.basis];
115
+ lines.push(
116
+ `[${bar}] ~${completion.percent}% ${style.fg("muted", `${completion.done} of ${completion.total} ${unit}`)}`,
117
+ );
118
+ }
119
+ lines.push(style.fg("dim", `Basis: ${completion.explanation}`));
120
+ lines.push("");
121
+
122
+ const { goal, ultragoal, todos } = snapshot.execution;
123
+ const activeWork = snapshot.activeWork;
124
+ if (goal) {
125
+ lines.push(
126
+ `${label("Goal")}${goal.status} · ${formatElapsed(goal.timeUsedSeconds)} · ${clip(goal.objective, 100)}`,
127
+ );
128
+ }
129
+ if (ultragoal) {
130
+ const s = ultragoal.counts;
131
+ const parts = [`${s.complete}/${s.total} complete`];
132
+ if (s.active > 0) parts.push(`${s.active} active`);
133
+ if (s.pending > 0) parts.push(`${s.pending} pending`);
134
+ if (s.blocked > 0) parts.push(style.fg("error", `${s.blocked} blocked`));
135
+ if (s.superseded > 0) parts.push(`${s.superseded} superseded`);
136
+ lines.push(`${label("Ultragoal")}${parts.join(" · ")}`);
137
+ if (activeWork.story) {
138
+ lines.push(`${label("")}next: ${clip(activeWork.story.id, 24)} ${clip(activeWork.story.title, 90)}`);
139
+ }
140
+ }
141
+ if (todos) {
142
+ const t = todos.counts;
143
+ const parts = [`${t.completed}/${t.total} completed`];
144
+ if (t.inProgress > 0) parts.push(`${t.inProgress} in progress`);
145
+ if (t.pending > 0) parts.push(`${t.pending} pending`);
146
+ if (t.abandoned > 0) parts.push(`${t.abandoned} abandoned`);
147
+ lines.push(`${label("Todos")}${parts.join(" · ")}`);
148
+ if (activeWork.todo) lines.push(`${label("")}now: ${clip(activeWork.todo, 90)}`);
149
+ }
150
+ if (activeWork.workflows.length === 0 && snapshot.sources.unreadable.includes("workflow-state")) {
151
+ lines.push(`${label("Workflows")}${style.fg("warning", "unknown: workflow state could not be read")}`);
152
+ } else if (activeWork.workflows.length === 0 && snapshot.sources.unresolved.includes("workflow-state")) {
153
+ lines.push(`${label("Workflows")}${style.fg("warning", "unknown: workflow state could not be reconciled")}`);
154
+ } else if (activeWork.workflows.length === 0 && activeWork.omittedWorkflows === 0) {
155
+ lines.push(`${label("Workflows")}${style.fg("dim", "none active")}`);
156
+ } else {
157
+ activeWork.workflows.forEach((workflow, index) => {
158
+ const chips = workflow.chips
159
+ .filter(chip => chip.value)
160
+ .slice(0, 4)
161
+ .map(chip => `${chip.label} ${clip(chip.value ?? "", 40)}`);
162
+ const detail = [workflow.phase, ...chips].join(" · ");
163
+ lines.push(`${label(index === 0 ? "Workflows" : "")}${clip(workflow.skill, 24)}: ${clip(detail, 110)}`);
164
+ if (workflow.summary) lines.push(`${label("")}${style.fg("muted", clip(workflow.summary, 100))}`);
165
+ });
166
+ if (activeWork.omittedWorkflows > 0) {
167
+ lines.push(
168
+ `${label(activeWork.workflows.length === 0 ? "Workflows" : "")}${style.fg("dim", `… ${plural(activeWork.omittedWorkflows, "more active workflow")} not shown`)}`,
169
+ );
170
+ }
171
+ }
172
+ const a = activeWork.agents;
173
+ if (a.total === 0) {
174
+ lines.push(`${label("Agents")}${style.fg("dim", "no subagents this session")}`);
175
+ } else {
176
+ const parts: string[] = [];
177
+ if (a.running > 0) parts.push(`${a.running} running`);
178
+ if (a.waiting > 0) parts.push(`${a.waiting} queued/paused`);
179
+ if (a.completed > 0) parts.push(`${a.completed} completed`);
180
+ if (a.failed > 0) parts.push(style.fg("error", `${a.failed} failed`));
181
+ if (a.cancelled > 0) parts.push(`${a.cancelled} cancelled`);
182
+ lines.push(`${label("Agents")}${parts.join(" · ")}`);
183
+ }
184
+ const verification = verificationLines(snapshot.verification);
185
+ if (verification.length === 0) {
186
+ lines.push(`${label("Verification")}${style.fg("dim", "no durable verification evidence recorded")}`);
187
+ } else {
188
+ verification.forEach((line, index) => {
189
+ lines.push(`${label(index === 0 ? "Verification" : "")}${clip(line, 110)}`);
190
+ });
191
+ }
192
+
193
+ if (snapshot.attention.length > 0 || snapshot.omittedAttention > 0) {
194
+ lines.push("", style.bold("Attention"));
195
+ for (const item of snapshot.attention) {
196
+ const marker =
197
+ item.kind === "blocker"
198
+ ? style.fg("error", "✖")
199
+ : item.kind === "pending"
200
+ ? style.fg("warning", "…")
201
+ : style.fg("dim", "·");
202
+ lines.push(` ${marker} ${clip(item.text, 140)}`);
203
+ }
204
+ if (snapshot.omittedAttention > 0) {
205
+ lines.push(` ${style.fg("dim", `… ${plural(snapshot.omittedAttention, "more attention item")} not shown`)}`);
206
+ }
207
+ }
208
+ return lines;
209
+ }
@@ -25,4 +25,5 @@ You are running as a GJC master session. You coordinate peer GJC sessions throug
25
25
  ## Delegation discipline
26
26
 
27
27
  - Give each spawned child one bounded task with explicit acceptance criteria in the seed prompt; monitor via broker-routed status/tail and aggregate results yourself.
28
+ - `gjc sdk session send` only starts a new turn; on a child that is mid-turn it fails with `busy` and was not applied. Correct a running child you spawned with `gjc sdk session raw control <id> --op turn.steer --json-input '{"text":"<instruction>"}' --idempotency-key <key>`, and stop its active turn with `--op turn.abort --json-input '{}'`. There are no `session.steer` or `session.abort` operations.
28
29
  - Clean up children you created with `gjc sdk session raw global --op session.close --json-input '{"sessionId":"<id>"}' --idempotency-key <key>` when their work is integrated. Orphaned children are reaped automatically after the configured grace period, but explicit close is the polite default.
@@ -3,7 +3,7 @@ Discover bundled GJC workflow, project, and user runtime skills without loading
3
3
  <instruction>
4
4
  - Includes the four bundled GJC workflow skills (`autoresearch`, `deep-interview`, `ralplan`, `ultragoal`) as `source: "bundled"` candidates from the generated bundled catalog; they are always available, cannot be replaced by filesystem copies, and are included for `source: "all"` but excluded by explicit `source: "project"`/`"user"` requests.
5
5
  - Returns thin metadata only: name, description, source scope, path, and use conditions when present. Bundled paths are stable catalog identifiers, not filesystem paths.
6
- - In trusted Git projects, `.gjc/skills` may be a symlink to a directory inside the same repository (for example `../.agents/skills`). Link and target identities remain checked through body loading; external targets and symlinked user/profile roots are refused. Do not replace a safe project link with copied files to make discovery work.
6
+ - In trusted Git projects, `.gjc/skills` may be a symlink to a directory inside the same repository (for example `../.agents/skills`). Project link and target identities remain checked through body loading; external project targets are refused. An individual user-scope skill symlink inside `~/.gjc/agent/skills` or a configured custom directory may resolve outside its scan root when user/global `skills.trustUserSkills` is enabled; project settings cannot grant this permission. Its link and target identities remain checked through body loading. Symlinked user/profile authority roots remain refused. Do not replace a safe project link with copied files to make discovery work.
7
7
  - Claude Code (`.claude/skills`) and Codex (`.codex/skills`) layouts are explicit import sources into `.gjc`, never invokable candidates. They are not returned as candidates; instead, each convention skill found in a trusted scope is reported in `diagnostics` with the exact copy command that enables it (copy into `.gjc/skills`), so a skill placed in a documented convention location is discoverable in a normal session without being silently loaded.
8
8
  - Discovery is on by default in a normal session. `query` matching is conjunctive substring: every whitespace-separated term must appear in a skill's name, description, source, or use conditions, unless a term equals the exact skill name — one keyword that appears nowhere in the candidate's metadata drops every candidate, so query with exact names or few terms known to appear literally, not topic keyword lists. When zero candidates are returned, the result carries a `notice` if discovery config disabled a requested scope, a non-empty query filtered out scanned skills, or diagnostics explain observed skills that were skipped or filtered. Only an empty result without a `notice` means no skills were scanned and no diagnostics were produced; never report a zero-candidate result as proof no skill exists.
9
9
  - When skills were scanned but not advertised (protected-name collision with a bundled workflow skill, include/ignore/disable policy filters, invalid frontmatter, shadowing), the result carries a bounded `diagnostics` list explaining why.