ramstack 0.1.0

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 (215) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +44 -0
  3. package/agents/comment-sicko.md +32 -0
  4. package/agents/poteto-agent.md +9 -0
  5. package/cli/rstack.mjs +539 -0
  6. package/package.json +29 -0
  7. package/skills/architect/README.md +25 -0
  8. package/skills/architect/SKILL.md +83 -0
  9. package/skills/architect/agents/openai.yaml +5 -0
  10. package/skills/architect/references/design-red-flags.md +33 -0
  11. package/skills/architect/references/rationale-template.md +35 -0
  12. package/skills/architect/references/runner-prompt.md +20 -0
  13. package/skills/arena/README.md +21 -0
  14. package/skills/arena/SKILL.md +71 -0
  15. package/skills/arena/agents/openai.yaml +5 -0
  16. package/skills/automate-me/README.md +21 -0
  17. package/skills/automate-me/SKILL.md +109 -0
  18. package/skills/automate-me/agents/openai.yaml +5 -0
  19. package/skills/blast-radius/README.md +21 -0
  20. package/skills/blast-radius/SKILL.md +50 -0
  21. package/skills/blast-radius/agents/openai.yaml +5 -0
  22. package/skills/bro/README.md +16 -0
  23. package/skills/bro/SKILL.md +7 -0
  24. package/skills/bro/agents/openai.yaml +5 -0
  25. package/skills/create-verification-skill/README.md +22 -0
  26. package/skills/create-verification-skill/SKILL.md +44 -0
  27. package/skills/create-verification-skill/agents/openai.yaml +5 -0
  28. package/skills/create-verification-skill/references/feature-map-example/README.md +47 -0
  29. package/skills/create-verification-skill/references/feature-map-example/create-note.md +39 -0
  30. package/skills/create-verification-skill/references/feature-map-example/search.md +45 -0
  31. package/skills/figure-it-out/README.md +22 -0
  32. package/skills/figure-it-out/SKILL.md +55 -0
  33. package/skills/figure-it-out/agents/openai.yaml +5 -0
  34. package/skills/how/README.md +25 -0
  35. package/skills/how/SKILL.md +134 -0
  36. package/skills/how/agents/openai.yaml +3 -0
  37. package/skills/how/references/critic-prompt.md +59 -0
  38. package/skills/how/references/critique-rubric.md +58 -0
  39. package/skills/how/references/explainer-prompt.md +55 -0
  40. package/skills/how/references/explorer-prompt.md +52 -0
  41. package/skills/interrogate/README.md +21 -0
  42. package/skills/interrogate/SKILL.md +112 -0
  43. package/skills/interrogate/agents/openai.yaml +5 -0
  44. package/skills/interrogate/references/code-quality-review.md +47 -0
  45. package/skills/interrogate/references/lead-judgment.md +58 -0
  46. package/skills/interrogate/references/reviewer-prompt.md +72 -0
  47. package/skills/interrogate/references/rubric.md +77 -0
  48. package/skills/maintain-verification-skill/README.md +21 -0
  49. package/skills/maintain-verification-skill/SKILL.md +39 -0
  50. package/skills/maintain-verification-skill/agents/openai.yaml +5 -0
  51. package/skills/no-comments/README.md +22 -0
  52. package/skills/no-comments/SKILL.md +24 -0
  53. package/skills/no-comments/agents/openai.yaml +5 -0
  54. package/skills/poteto-mode/README.md +26 -0
  55. package/skills/poteto-mode/SKILL.md +140 -0
  56. package/skills/poteto-mode/agents/openai.yaml +5 -0
  57. package/skills/poteto-mode/playbooks/authoring-a-skill.md +13 -0
  58. package/skills/poteto-mode/playbooks/autonomous-run.md +13 -0
  59. package/skills/poteto-mode/playbooks/autopilot-full.md +13 -0
  60. package/skills/poteto-mode/playbooks/autopilot-stack.md +16 -0
  61. package/skills/poteto-mode/playbooks/babysit.md +27 -0
  62. package/skills/poteto-mode/playbooks/bug-fix.md +17 -0
  63. package/skills/poteto-mode/playbooks/eval.md +27 -0
  64. package/skills/poteto-mode/playbooks/feature.md +21 -0
  65. package/skills/poteto-mode/playbooks/hillclimb.md +21 -0
  66. package/skills/poteto-mode/playbooks/investigation.md +14 -0
  67. package/skills/poteto-mode/playbooks/multi-phase-plan.md +3 -0
  68. package/skills/poteto-mode/playbooks/opening-a-pr.md +11 -0
  69. package/skills/poteto-mode/playbooks/orchestrate.md +113 -0
  70. package/skills/poteto-mode/playbooks/pause-safely.md +10 -0
  71. package/skills/poteto-mode/playbooks/perf-issue.md +24 -0
  72. package/skills/poteto-mode/playbooks/prototype.md +14 -0
  73. package/skills/poteto-mode/playbooks/refactoring.md +16 -0
  74. package/skills/poteto-mode/playbooks/runtime-forensics.md +11 -0
  75. package/skills/poteto-mode/playbooks/session-pickup.md +13 -0
  76. package/skills/poteto-mode/playbooks/shipping.md +20 -0
  77. package/skills/poteto-mode/playbooks/trace-forensics.md +14 -0
  78. package/skills/poteto-mode/playbooks/visual-parity.md +11 -0
  79. package/skills/poteto-mode/playbooks/worktree-cleanup.md +14 -0
  80. package/skills/poteto-mode/references/bugbot-triage.md +142 -0
  81. package/skills/poteto-mode/references/plan.md +105 -0
  82. package/skills/poteto-mode/scripts/bootstrap.ts +62 -0
  83. package/skills/poteto-mode/scripts/bun.lock +67 -0
  84. package/skills/poteto-mode/scripts/orch/orch.test.ts +634 -0
  85. package/skills/poteto-mode/scripts/orch/orch.ts +578 -0
  86. package/skills/poteto-mode/scripts/orch/store.ts +1607 -0
  87. package/skills/poteto-mode/scripts/package.json +16 -0
  88. package/skills/poteto-mode/scripts/watch-pr/cli.test.ts +224 -0
  89. package/skills/poteto-mode/scripts/watch-pr/cli.ts +223 -0
  90. package/skills/poteto-mode/scripts/watch-pr/fakes.test-helper.ts +118 -0
  91. package/skills/poteto-mode/scripts/watch-pr/github.test.ts +306 -0
  92. package/skills/poteto-mode/scripts/watch-pr/github.ts +699 -0
  93. package/skills/poteto-mode/scripts/watch-pr/policy.test.ts +420 -0
  94. package/skills/poteto-mode/scripts/watch-pr/policy.ts +832 -0
  95. package/skills/poteto-mode/scripts/watch-pr/render.ts +169 -0
  96. package/skills/poteto-mode/scripts/watch-pr/tsconfig.json +13 -0
  97. package/skills/poteto-mode/scripts/watch-pr/types.compile.ts +93 -0
  98. package/skills/poteto-mode/scripts/watch-pr/types.ts +401 -0
  99. package/skills/poteto-mode/scripts/watch-pr/watch-pr +6 -0
  100. package/skills/poteto-mode/scripts/worktree-audit.sh +86 -0
  101. package/skills/principle-boundary-discipline/README.md +17 -0
  102. package/skills/principle-boundary-discipline/SKILL.md +34 -0
  103. package/skills/principle-boundary-discipline/agents/openai.yaml +5 -0
  104. package/skills/principle-build-the-lever/README.md +17 -0
  105. package/skills/principle-build-the-lever/SKILL.md +23 -0
  106. package/skills/principle-build-the-lever/agents/openai.yaml +5 -0
  107. package/skills/principle-encode-lessons-in-structure/README.md +17 -0
  108. package/skills/principle-encode-lessons-in-structure/SKILL.md +31 -0
  109. package/skills/principle-encode-lessons-in-structure/agents/openai.yaml +5 -0
  110. package/skills/principle-exhaust-the-design-space/README.md +17 -0
  111. package/skills/principle-exhaust-the-design-space/SKILL.md +21 -0
  112. package/skills/principle-exhaust-the-design-space/agents/openai.yaml +5 -0
  113. package/skills/principle-experience-first/README.md +17 -0
  114. package/skills/principle-experience-first/SKILL.md +19 -0
  115. package/skills/principle-experience-first/agents/openai.yaml +5 -0
  116. package/skills/principle-fix-root-causes/README.md +17 -0
  117. package/skills/principle-fix-root-causes/SKILL.md +23 -0
  118. package/skills/principle-fix-root-causes/agents/openai.yaml +5 -0
  119. package/skills/principle-foundational-thinking/README.md +17 -0
  120. package/skills/principle-foundational-thinking/SKILL.md +21 -0
  121. package/skills/principle-foundational-thinking/agents/openai.yaml +5 -0
  122. package/skills/principle-guard-the-context-window/README.md +17 -0
  123. package/skills/principle-guard-the-context-window/SKILL.md +17 -0
  124. package/skills/principle-guard-the-context-window/agents/openai.yaml +5 -0
  125. package/skills/principle-laziness-protocol/README.md +17 -0
  126. package/skills/principle-laziness-protocol/SKILL.md +18 -0
  127. package/skills/principle-laziness-protocol/agents/openai.yaml +5 -0
  128. package/skills/principle-make-operations-idempotent/README.md +17 -0
  129. package/skills/principle-make-operations-idempotent/SKILL.md +24 -0
  130. package/skills/principle-make-operations-idempotent/agents/openai.yaml +5 -0
  131. package/skills/principle-migrate-callers-then-delete-legacy-apis/README.md +17 -0
  132. package/skills/principle-migrate-callers-then-delete-legacy-apis/SKILL.md +22 -0
  133. package/skills/principle-migrate-callers-then-delete-legacy-apis/agents/openai.yaml +5 -0
  134. package/skills/principle-minimize-reader-load/README.md +17 -0
  135. package/skills/principle-minimize-reader-load/SKILL.md +23 -0
  136. package/skills/principle-minimize-reader-load/agents/openai.yaml +5 -0
  137. package/skills/principle-model-the-domain/README.md +17 -0
  138. package/skills/principle-model-the-domain/SKILL.md +26 -0
  139. package/skills/principle-model-the-domain/agents/openai.yaml +5 -0
  140. package/skills/principle-never-block-on-the-human/README.md +16 -0
  141. package/skills/principle-never-block-on-the-human/SKILL.md +23 -0
  142. package/skills/principle-never-block-on-the-human/agents/openai.yaml +5 -0
  143. package/skills/principle-outcome-oriented-execution/README.md +16 -0
  144. package/skills/principle-outcome-oriented-execution/SKILL.md +22 -0
  145. package/skills/principle-outcome-oriented-execution/agents/openai.yaml +5 -0
  146. package/skills/principle-prove-it-works/README.md +18 -0
  147. package/skills/principle-prove-it-works/SKILL.md +33 -0
  148. package/skills/principle-prove-it-works/agents/openai.yaml +5 -0
  149. package/skills/principle-redesign-from-first-principles/README.md +16 -0
  150. package/skills/principle-redesign-from-first-principles/SKILL.md +16 -0
  151. package/skills/principle-redesign-from-first-principles/agents/openai.yaml +5 -0
  152. package/skills/principle-separate-before-serializing-shared-state/README.md +16 -0
  153. package/skills/principle-separate-before-serializing-shared-state/SKILL.md +16 -0
  154. package/skills/principle-separate-before-serializing-shared-state/agents/openai.yaml +5 -0
  155. package/skills/principle-sequence-verifiable-units/README.md +18 -0
  156. package/skills/principle-sequence-verifiable-units/SKILL.md +22 -0
  157. package/skills/principle-sequence-verifiable-units/agents/openai.yaml +5 -0
  158. package/skills/principle-subtract-before-you-add/README.md +17 -0
  159. package/skills/principle-subtract-before-you-add/SKILL.md +22 -0
  160. package/skills/principle-subtract-before-you-add/agents/openai.yaml +5 -0
  161. package/skills/principle-type-system-discipline/README.md +19 -0
  162. package/skills/principle-type-system-discipline/SKILL.md +31 -0
  163. package/skills/principle-type-system-discipline/agents/openai.yaml +5 -0
  164. package/skills/recall/README.md +23 -0
  165. package/skills/recall/SKILL.md +35 -0
  166. package/skills/recall/agents/openai.yaml +5 -0
  167. package/skills/reflect/README.md +24 -0
  168. package/skills/reflect/SKILL.md +77 -0
  169. package/skills/reflect/agents/openai.yaml +5 -0
  170. package/skills/reflect/references/divergent-reviewer.md +43 -0
  171. package/skills/reflect/references/judgment-reviewer.md +42 -0
  172. package/skills/reflect/references/synthesizer.md +56 -0
  173. package/skills/reflect/references/tooling-reviewer.md +57 -0
  174. package/skills/setup-rstack/README.md +25 -0
  175. package/skills/setup-rstack/SKILL.md +81 -0
  176. package/skills/setup-rstack/agents/openai.yaml +3 -0
  177. package/skills/show-me-your-work/README.md +23 -0
  178. package/skills/show-me-your-work/SKILL.md +82 -0
  179. package/skills/show-me-your-work/agents/openai.yaml +5 -0
  180. package/skills/show-me-your-work/references/decision-log-template.tsv +1 -0
  181. package/skills/show-me-your-work/scripts/log.sh +40 -0
  182. package/skills/swarm/README.md +21 -0
  183. package/skills/swarm/SKILL.md +46 -0
  184. package/skills/swarm/agents/openai.yaml +5 -0
  185. package/skills/tdd/README.md +21 -0
  186. package/skills/tdd/SKILL.md +44 -0
  187. package/skills/tdd/agents/openai.yaml +5 -0
  188. package/skills/teach/README.md +22 -0
  189. package/skills/teach/SKILL.md +21 -0
  190. package/skills/teach/agents/openai.yaml +5 -0
  191. package/skills/technical-writing/README.md +23 -0
  192. package/skills/technical-writing/SKILL.md +130 -0
  193. package/skills/technical-writing/agents/openai.yaml +5 -0
  194. package/skills/typescript-best-practices/README.md +22 -0
  195. package/skills/typescript-best-practices/SKILL.md +28 -0
  196. package/skills/typescript-best-practices/agents/openai.yaml +3 -0
  197. package/skills/typescript-best-practices/references/patterns.md +292 -0
  198. package/skills/unslop/README.md +21 -0
  199. package/skills/unslop/SKILL.md +80 -0
  200. package/skills/unslop/agents/openai.yaml +3 -0
  201. package/skills/why/README.md +26 -0
  202. package/skills/why/SKILL.md +229 -0
  203. package/skills/why/agents/openai.yaml +3 -0
  204. package/skills/why/references/epistemics.md +144 -0
  205. package/skills/why/references/investigator-prompt.md +103 -0
  206. package/skills/why/references/source-playbook.md +17 -0
  207. package/skills/why/references/sources/code-archaeology.md +88 -0
  208. package/skills/why/references/sources/databricks.md +70 -0
  209. package/skills/why/references/sources/datadog.md +99 -0
  210. package/skills/why/references/sources/incident-postmortem.md +15 -0
  211. package/skills/why/references/sources/linear.md +48 -0
  212. package/skills/why/references/sources/notion.md +55 -0
  213. package/skills/why/references/sources/sentry.md +100 -0
  214. package/skills/why/references/sources/slack.md +54 -0
  215. package/skills/why/references/synthesizer-prompt.md +135 -0
@@ -0,0 +1,578 @@
1
+ #!/usr/bin/env bun
2
+
3
+ import { ensureDependenciesInstalled } from "../bootstrap.ts";
4
+ import {
5
+ NotFoundError,
6
+ UsageError,
7
+ openStore,
8
+ parseVerdict,
9
+ type Counts,
10
+ type Frontier,
11
+ type InboxPointer,
12
+ type OpenGate,
13
+ type StandingLine,
14
+ type StatusReport,
15
+ type Store,
16
+ type Unit,
17
+ type Verdict,
18
+ } from "./store.ts";
19
+
20
+ ensureDependenciesInstalled();
21
+ const {
22
+ Command: CommanderCommand,
23
+ CommanderError,
24
+ InvalidArgumentError,
25
+ Option,
26
+ } = await import("commander");
27
+ type Command = InstanceType<typeof CommanderCommand>;
28
+
29
+ const DISPLAY_LIMIT = 4;
30
+
31
+ interface Io {
32
+ readonly stdout: (value: string) => void;
33
+ readonly stderr: (value: string) => void;
34
+ }
35
+
36
+ interface GlobalOptions {
37
+ readonly store?: string;
38
+ readonly json: boolean;
39
+ readonly force: boolean;
40
+ }
41
+
42
+ interface UnitAddOptions {
43
+ readonly track: string;
44
+ readonly brief?: string;
45
+ }
46
+
47
+ interface UnitSetOptions {
48
+ readonly state: string;
49
+ readonly branch?: string;
50
+ readonly pr?: number;
51
+ readonly sha?: string;
52
+ }
53
+
54
+ interface UnitListOptions {
55
+ readonly state?: string;
56
+ readonly track?: string;
57
+ }
58
+
59
+ interface LedgerRecordOptions {
60
+ readonly evidence: string;
61
+ readonly verifier?: string;
62
+ }
63
+
64
+ interface InboxPushOptions {
65
+ readonly report?: string;
66
+ }
67
+
68
+ interface InboxDrainOptions {
69
+ readonly peek: boolean;
70
+ }
71
+
72
+ interface GateParkOptions {
73
+ readonly question: string;
74
+ readonly options: string;
75
+ readonly default: string;
76
+ }
77
+
78
+ interface GateResolveOptions {
79
+ readonly answer: string;
80
+ }
81
+
82
+ interface FrontierSetOptions {
83
+ readonly repo?: string;
84
+ readonly prs?: readonly number[];
85
+ }
86
+
87
+ function message(error: unknown): string {
88
+ return error instanceof Error ? error.message : String(error);
89
+ }
90
+
91
+ function positiveInteger(value: string): number {
92
+ const parsed = Number(value);
93
+ if (!/^[1-9]\d*$/.test(value) || !Number.isSafeInteger(parsed)) {
94
+ throw new InvalidArgumentError("must be a positive integer");
95
+ }
96
+ return parsed;
97
+ }
98
+
99
+ function prList(value: string): readonly number[] {
100
+ const parts = value.split(",");
101
+ if (parts.some((part) => part.length === 0)) {
102
+ throw new InvalidArgumentError("requires a comma-separated PR list");
103
+ }
104
+ return parts.map(positiveInteger);
105
+ }
106
+
107
+ function countLine(value: Counts): string {
108
+ const entries = Object.entries(value);
109
+ return entries.length === 0
110
+ ? "none"
111
+ : entries.map(([name, count]) => `${name}=${count}`).join(", ");
112
+ }
113
+
114
+ function unitLine(unit: Unit): string {
115
+ return [
116
+ unit.id,
117
+ unit.track,
118
+ unit.state,
119
+ unit.branch,
120
+ unit.pr,
121
+ unit.sha,
122
+ unit.brief,
123
+ ].join("\t");
124
+ }
125
+
126
+ function pointerLine(pointer: InboxPointer): string {
127
+ return [
128
+ pointer.ts,
129
+ pointer.agent,
130
+ pointer.unit,
131
+ pointer.status,
132
+ pointer.report,
133
+ ].join("\t");
134
+ }
135
+
136
+ function gateLine(gate: OpenGate): string {
137
+ return [
138
+ gate.id,
139
+ gate.question,
140
+ gate.options,
141
+ gate.defaultAnswer,
142
+ ].join("\t");
143
+ }
144
+
145
+ function compactRows<T>(
146
+ rows: readonly T[],
147
+ format: (row: T) => string,
148
+ empty: string,
149
+ limit: number | null = DISPLAY_LIMIT
150
+ ): string {
151
+ if (rows.length === 0) {
152
+ return empty;
153
+ }
154
+ const visible = limit === null ? rows : rows.slice(0, limit);
155
+ const lines = visible.map(format);
156
+ if (limit !== null && rows.length > limit) {
157
+ lines.push(`... ${rows.length - limit} more; use --json`);
158
+ }
159
+ return lines.join("\n");
160
+ }
161
+
162
+ function frontierLine(value: Frontier): string {
163
+ const prs =
164
+ value.prs.length === 0
165
+ ? "none"
166
+ : value.prs
167
+ .map(
168
+ (row) =>
169
+ `${row.branches}#${row.pr}@${row.sha}:${row.state}`
170
+ )
171
+ .join(",");
172
+ return `generation=${value.generation} prs=${prs} lowest-unmerged=${value.lowestUnmerged ?? "none"}`;
173
+ }
174
+
175
+ function statusLines(report: StatusReport): string {
176
+ const visible = report.summary.openGateIds.slice(0, DISPLAY_LIMIT);
177
+ const more =
178
+ report.summary.openGateIds.length > DISPLAY_LIMIT
179
+ ? `,+${report.summary.openGateIds.length - DISPLAY_LIMIT} more`
180
+ : "";
181
+ return [
182
+ `counts: units=${report.units.length}; states=${countLine(report.summary.unitStates)}; ledger=${countLine(report.summary.ledgerVerdicts)}`,
183
+ `changed: ${report.changed}`,
184
+ `gates open: ${report.summary.openGateIds.length}${
185
+ visible.length > 0 ? `; ids=${visible.join(",")}${more}` : ""
186
+ }`,
187
+ ].join("\n");
188
+ }
189
+
190
+ function emit<T>(
191
+ io: Io,
192
+ json: boolean,
193
+ value: T,
194
+ compact: (result: T) => string,
195
+ jsonValue: (result: T) => unknown = (result) => result
196
+ ): void {
197
+ const rendered = json
198
+ ? JSON.stringify(jsonValue(value), null, 2)
199
+ : compact(value);
200
+ io.stdout(rendered.endsWith("\n") ? rendered : `${rendered}\n`);
201
+ }
202
+
203
+ function storeDirectory(program: Command): string {
204
+ const value = program.opts<GlobalOptions>().store;
205
+ if (value === undefined || value.trim().length === 0) {
206
+ throw new UsageError("set --store <dir> or ORCH_STORE");
207
+ }
208
+ return value;
209
+ }
210
+
211
+ function frontierRepo(options: FrontierSetOptions): string {
212
+ const value = options.repo;
213
+ if (value === undefined || value.trim().length === 0) {
214
+ throw new UsageError("set --repo <dir> or ORCH_REPO");
215
+ }
216
+ return value;
217
+ }
218
+
219
+ async function runStore<T>(
220
+ program: Command,
221
+ io: Io,
222
+ operation: (store: Store) => Promise<T>,
223
+ compact: (result: T) => string,
224
+ jsonValue?: (result: T) => unknown
225
+ ): Promise<void> {
226
+ const options = program.opts<GlobalOptions>();
227
+ const store = openStore(storeDirectory(program), {
228
+ force: options.force,
229
+ onLockStolen: (holder) =>
230
+ io.stderr(`stealing store lock held by pid ${holder}\n`),
231
+ onStaleLock: (holder) =>
232
+ io.stderr(`replacing stale store lock (pid ${holder} is dead)\n`),
233
+ });
234
+ try {
235
+ const result = await operation(store);
236
+ emit(io, options.json, result, compact, jsonValue);
237
+ } finally {
238
+ await store.close();
239
+ }
240
+ }
241
+
242
+ function leaf(parent: Command, name: string, description: string): Command {
243
+ return parent
244
+ .command(name)
245
+ .description(description)
246
+ .allowExcessArguments(false);
247
+ }
248
+
249
+ function requireSubcommand(program: Command): never {
250
+ storeDirectory(program);
251
+ throw new UsageError("a valid command is required");
252
+ }
253
+
254
+ function createProgram(io: Io): Command {
255
+ const program = new CommanderCommand("orch")
256
+ .description("Plain-file orchestrate bookkeeping")
257
+ .usage("[--store <dir>] [--json] [--force] <command>")
258
+ .configureOutput({ writeOut: io.stdout, writeErr: io.stderr })
259
+ .exitOverride()
260
+ .showHelpAfterError()
261
+ .allowExcessArguments(false)
262
+ .addOption(
263
+ new Option("--store <dir>", "store directory (or ORCH_STORE)").env(
264
+ "ORCH_STORE"
265
+ )
266
+ )
267
+ .option("--json", "print complete rows as JSON", false)
268
+ .option("--force", "steal an existing store lock", false);
269
+
270
+ leaf(program, "init", "initialize the store").action(() =>
271
+ runStore(
272
+ program,
273
+ io,
274
+ (store) => store.init(),
275
+ (result) => `initialized ${result.store}`
276
+ )
277
+ );
278
+
279
+ const unit = program
280
+ .command("unit")
281
+ .description("manage work units")
282
+ .action(() => requireSubcommand(program));
283
+ leaf(unit, "add <id>", "add a unit")
284
+ .requiredOption("--track <track>", "unit track")
285
+ .option("--brief <path>", "brief path")
286
+ .action((id: string, options: UnitAddOptions) =>
287
+ runStore(
288
+ program,
289
+ io,
290
+ (store) =>
291
+ store.units.add({
292
+ id,
293
+ track: options.track,
294
+ brief: options.brief,
295
+ }),
296
+ unitLine
297
+ )
298
+ );
299
+ leaf(unit, "set <id>", "update a unit")
300
+ .requiredOption("--state <state>", "unit state")
301
+ .option("--branch <branch>", "branch name")
302
+ .option("--pr <number>", "pull request number", positiveInteger)
303
+ .option("--sha <sha>", "commit SHA")
304
+ .action((id: string, options: UnitSetOptions) =>
305
+ runStore(
306
+ program,
307
+ io,
308
+ (store) =>
309
+ store.units.set({
310
+ id,
311
+ state: options.state,
312
+ branch: options.branch,
313
+ pr: options.pr,
314
+ sha: options.sha,
315
+ }),
316
+ unitLine
317
+ )
318
+ );
319
+ leaf(unit, "get <id>", "get a unit").action((id: string) =>
320
+ runStore(program, io, (store) => store.units.get(id), unitLine)
321
+ );
322
+ leaf(unit, "list", "list units")
323
+ .option("--state <state>", "filter by state")
324
+ .option("--track <track>", "filter by track")
325
+ .action((options: UnitListOptions) =>
326
+ runStore(
327
+ program,
328
+ io,
329
+ (store) => store.units.list(options),
330
+ (rows) => compactRows(rows, unitLine, "(no units)")
331
+ )
332
+ );
333
+ leaf(unit, "counts", "count units by state").action(() =>
334
+ runStore(program, io, (store) => store.units.counts(), countLine)
335
+ );
336
+
337
+ const ledger = program
338
+ .command("ledger")
339
+ .description("manage verification records")
340
+ .action(() => requireSubcommand(program));
341
+ leaf(ledger, "record", "record a verification verdict")
342
+ .argument("<pr>", "pull request number", positiveInteger)
343
+ .argument("<sha>", "commit SHA")
344
+ .argument("<verdict>", "verification verdict", parseVerdict)
345
+ .requiredOption("--evidence <path>", "evidence path")
346
+ .option("--verifier <name>", "verifier name")
347
+ .action(
348
+ (
349
+ pr: number,
350
+ sha: string,
351
+ verdict: Verdict,
352
+ options: LedgerRecordOptions
353
+ ) =>
354
+ runStore(
355
+ program,
356
+ io,
357
+ (store) =>
358
+ store.ledger.record({
359
+ pr,
360
+ sha,
361
+ verdict,
362
+ evidence: options.evidence,
363
+ verifier: options.verifier,
364
+ }),
365
+ (row) => `${row.pr}\t${row.sha}\t${row.verdict}`
366
+ )
367
+ );
368
+ leaf(ledger, "check", "check a verification verdict")
369
+ .argument("<pr>", "pull request number", positiveInteger)
370
+ .argument("<sha>", "commit SHA")
371
+ .action((pr: number, sha: string) =>
372
+ runStore(
373
+ program,
374
+ io,
375
+ (store) => store.ledger.check({ pr, sha }),
376
+ (row) => row.verdict
377
+ )
378
+ );
379
+ leaf(ledger, "summary", "count verification verdicts").action(() =>
380
+ runStore(program, io, (store) => store.ledger.summary(), countLine)
381
+ );
382
+
383
+ const inbox = program
384
+ .command("inbox")
385
+ .description("manage agent pointers")
386
+ .action(() => requireSubcommand(program));
387
+ leaf(inbox, "push <agent> <unit> <status>", "push an inbox pointer")
388
+ .option("--report <path>", "report path")
389
+ .action(
390
+ (
391
+ agent: string,
392
+ unitId: string,
393
+ status: string,
394
+ options: InboxPushOptions
395
+ ) =>
396
+ runStore(
397
+ program,
398
+ io,
399
+ (store) =>
400
+ store.inbox.push({
401
+ agent,
402
+ unit: unitId,
403
+ status,
404
+ report: options.report,
405
+ }),
406
+ (result) =>
407
+ `${result.pointer.unit}\t${result.pointer.status}\t${result.filename}`,
408
+ (result) => result.pointer
409
+ )
410
+ );
411
+ leaf(inbox, "drain", "drain inbox pointers")
412
+ .option("--peek", "read without draining", false)
413
+ .action((options: InboxDrainOptions) =>
414
+ runStore(
415
+ program,
416
+ io,
417
+ (store) =>
418
+ options.peek ? store.inbox.peek() : store.inbox.drain(),
419
+ (rows) => compactRows(rows, pointerLine, "(empty)", null)
420
+ )
421
+ );
422
+ leaf(inbox, "count", "count inbox pointers").action(() =>
423
+ runStore(
424
+ program,
425
+ io,
426
+ (store) => store.inbox.count(),
427
+ String,
428
+ (count) => ({ count })
429
+ )
430
+ );
431
+
432
+ const gate = program
433
+ .command("gate")
434
+ .description("manage decision gates")
435
+ .action(() => requireSubcommand(program));
436
+ leaf(gate, "park <id>", "park a decision gate")
437
+ .requiredOption("--question <question>", "gate question")
438
+ .requiredOption("--options <options>", "gate options")
439
+ .requiredOption("--default <answer>", "default answer")
440
+ .action((id: string, options: GateParkOptions) =>
441
+ runStore(
442
+ program,
443
+ io,
444
+ (store) =>
445
+ store.gates.park({
446
+ id,
447
+ question: options.question,
448
+ options: options.options,
449
+ defaultAnswer: options.default,
450
+ }),
451
+ (result) => `${result.id}\topen`
452
+ )
453
+ );
454
+ leaf(gate, "list", "list open decision gates").action(() =>
455
+ runStore(
456
+ program,
457
+ io,
458
+ (store) => store.gates.list(),
459
+ (rows) => compactRows(rows, gateLine, "(no open gates)")
460
+ )
461
+ );
462
+ leaf(gate, "resolve <id>", "resolve a decision gate")
463
+ .requiredOption("--answer <answer>", "chosen answer")
464
+ .action((id: string, options: GateResolveOptions) =>
465
+ runStore(
466
+ program,
467
+ io,
468
+ (store) => store.gates.resolve({ id, answer: options.answer }),
469
+ (result) => `${result.id}\tresolved\t${result.answer}`
470
+ )
471
+ );
472
+
473
+ const frontier = program
474
+ .command("frontier")
475
+ .description("manage the Graphite stack frontier")
476
+ .action(() => requireSubcommand(program));
477
+ leaf(frontier, "set", "discover the Graphite stack and set the frontier")
478
+ .addOption(
479
+ new Option(
480
+ "--repo <dir>",
481
+ "repository directory (or ORCH_REPO)"
482
+ ).env("ORCH_REPO")
483
+ )
484
+ .option(
485
+ "--prs <n,...>",
486
+ "optional expected pull request order pin",
487
+ prList
488
+ )
489
+ .action((options: FrontierSetOptions) =>
490
+ runStore(
491
+ program,
492
+ io,
493
+ (store) =>
494
+ store.frontier.set({
495
+ repo: frontierRepo(options),
496
+ prs: options.prs,
497
+ }),
498
+ frontierLine
499
+ )
500
+ );
501
+ leaf(frontier, "show", "show the frontier").action(() =>
502
+ runStore(program, io, (store) => store.frontier.show(), frontierLine)
503
+ );
504
+
505
+ leaf(program, "status", "render status.md and print a summary").action(() =>
506
+ runStore(program, io, (store) => store.status.render(), statusLines)
507
+ );
508
+
509
+ const standing = program
510
+ .command("standing")
511
+ .description("manage standing orders")
512
+ .action(() => requireSubcommand(program));
513
+ leaf(standing, "show", "show standing orders").action(() =>
514
+ runStore(
515
+ program,
516
+ io,
517
+ (store) => store.standing.show(),
518
+ (rows) =>
519
+ compactRows(
520
+ rows,
521
+ (item: StandingLine) => `${item.number}. ${item.line}`,
522
+ "(no standing orders)"
523
+ )
524
+ )
525
+ );
526
+ leaf(standing, "add <line>", "add a standing order").action((line: string) =>
527
+ runStore(
528
+ program,
529
+ io,
530
+ (store) => store.standing.add({ line }),
531
+ (item) => `${item.number}. ${item.line}`
532
+ )
533
+ );
534
+
535
+ program.action(() => requireSubcommand(program));
536
+ return program;
537
+ }
538
+
539
+ function handleError(error: unknown, program: Command, io: Io): number {
540
+ if (error instanceof CommanderError) {
541
+ return error.exitCode === 0 ? 0 : 1;
542
+ }
543
+ const json = program.opts<GlobalOptions>().json;
544
+ if (error instanceof NotFoundError) {
545
+ const output = error.output;
546
+ if (output === undefined) {
547
+ io.stderr(`error: ${error.message}\n`);
548
+ } else {
549
+ emit(io, json, output.json, () => output.compact);
550
+ }
551
+ return 2;
552
+ }
553
+ io.stderr(`error: ${message(error)}\n`);
554
+ if (error instanceof UsageError) {
555
+ io.stderr(program.helpInformation());
556
+ }
557
+ return 1;
558
+ }
559
+
560
+ export async function main(
561
+ argv: readonly string[],
562
+ io: Io = {
563
+ stdout: (value) => process.stdout.write(value),
564
+ stderr: (value) => process.stderr.write(value),
565
+ }
566
+ ): Promise<number> {
567
+ const program = createProgram(io);
568
+ try {
569
+ await program.parseAsync(argv, { from: "user" });
570
+ return 0;
571
+ } catch (error) {
572
+ return handleError(error, program, io);
573
+ }
574
+ }
575
+
576
+ if (import.meta.main) {
577
+ process.exitCode = await main(process.argv.slice(2));
578
+ }