@smithers-orchestrator/cli 0.16.9 → 0.18.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 (49) hide show
  1. package/dist/CliExitCode-dlFKbqup.d.ts +12 -0
  2. package/dist/HijackCandidate-FxLeKpcZ.d.ts +13 -0
  3. package/dist/SemanticToolDefinition-C1UT6pZk.d.ts +71 -0
  4. package/dist/SupervisorOptions-DtiPbGFx.d.ts +27 -0
  5. package/dist/agent-commands/agentAddWizard.d.ts +18 -0
  6. package/dist/agent-commands/regenerateAgentsTsIfPresent.d.ts +15 -0
  7. package/dist/agent-commands/runAgentAdd.d.ts +83 -0
  8. package/dist/agent-detection.d.ts +27 -0
  9. package/dist/ask.d.ts +29 -0
  10. package/dist/chat.d.ts +96 -0
  11. package/dist/diff.d.ts +64 -0
  12. package/dist/event-categories.d.ts +26 -0
  13. package/dist/find-db.d.ts +47 -0
  14. package/dist/format.d.ts +35 -0
  15. package/dist/hijack-session.d.ts +25 -0
  16. package/dist/hijack.d.ts +54 -0
  17. package/dist/index.d.ts +1 -0
  18. package/dist/mcp/semantic-server.d.ts +30 -0
  19. package/dist/mcp/semantic-tools.d.ts +4 -0
  20. package/dist/mdx-plugin.d.ts +3 -0
  21. package/dist/node-detail.d.ts +143 -0
  22. package/dist/output.d.ts +31 -0
  23. package/dist/resume-detached.d.ts +17 -0
  24. package/dist/rewind.d.ts +28 -0
  25. package/dist/scheduler.d.ts +3 -0
  26. package/dist/smithersRuntime.d.ts +23 -0
  27. package/dist/supervisor.d.ts +44 -0
  28. package/dist/tree.d.ts +51 -0
  29. package/dist/util/errorMessage.d.ts +40 -0
  30. package/dist/util/exitCodes.d.ts +19 -0
  31. package/dist/watch.d.ts +44 -0
  32. package/dist/why-diagnosis.d.ts +56 -0
  33. package/dist/workflow-pack.d.ts +38 -0
  34. package/dist/workflows.d.ts +35 -0
  35. package/package.json +25 -15
  36. package/src/InitWorkflowPackOptions.ts +2 -0
  37. package/src/InitWorkflowPackResult.ts +8 -2
  38. package/src/agent-commands/agentAddWizard.js +190 -0
  39. package/src/agent-commands/regenerateAgentsTsIfPresent.js +28 -0
  40. package/src/agent-commands/runAgentAdd.js +147 -0
  41. package/src/agent-detection.js +214 -17
  42. package/src/hijack-session.js +1 -1
  43. package/src/index.js +526 -23
  44. package/src/mcp/semantic-tools.js +1 -1
  45. package/src/mdx-plugin.js +6 -0
  46. package/src/output.js +52 -0
  47. package/src/smithersRuntime.js +2 -1
  48. package/src/util/logger.ts +97 -0
  49. package/src/workflow-pack.js +802 -34
@@ -1,10 +1,11 @@
1
1
  import { spawnSync } from "node:child_process";
2
+ import { createRequire } from "node:module";
2
3
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
3
4
  import { dirname, resolve } from "node:path";
4
5
  import { fileURLToPath } from "node:url";
5
6
  import { generateAgentsTs } from "./agent-detection.js";
6
7
  /**
7
- * @typedef {{ force?: boolean; rootDir?: string; skipInstall?: boolean; }} InitOptions
8
+ * @typedef {{ force?: boolean; rootDir?: string; skipInstall?: boolean; agentsOnly?: boolean; }} InitOptions
8
9
  */
9
10
  /**
10
11
  * @typedef {{ status: "ok" | "skipped" | "failed"; reason?: string; }} InitInstallResult
@@ -15,6 +16,12 @@ import { generateAgentsTs } from "./agent-detection.js";
15
16
  /**
16
17
  * @typedef {{ command: string; description: string; }} WorkflowCta
17
18
  */
19
+ /**
20
+ * @typedef {{ path: string; contents: string; preserveExisting?: boolean; }} TemplateFile
21
+ */
22
+
23
+ const FALLBACK_SMITHERS_SPEC = "latest";
24
+ const require = createRequire(import.meta.url);
18
25
 
19
26
  /**
20
27
  * @param {string} path
@@ -47,26 +54,33 @@ function readPackageVersion(path, fallback) {
47
54
  }
48
55
  }
49
56
  /**
50
- * Resolve `<spec>/package.json` via Node's module resolution (from this file's
51
- * location) and return its `version`. Uses `import.meta.resolve` so it works
52
- * whether the CLI is running from the monorepo checkout (deps under
53
- * `apps/cli/node_modules/`) or installed flat under a user's project
54
- * `node_modules/`. Falls back to the pin bundled at release time when the
55
- * package isn't installed in the user's project (typical for devDep-only
56
- * specs like `typescript` and `@types/*`).
57
+ * Resolve an installed dependency version from the current package layout.
57
58
  *
58
- * @param {string} spec
59
+ * @param {string} specifier
59
60
  * @param {string} fallback
60
61
  */
61
- function resolveDependencyVersion(spec, fallback) {
62
+ function resolveInstalledPackageVersion(specifier, fallback) {
62
63
  try {
63
- const url = import.meta.resolve(`${spec}/package.json`);
64
- return readPackageVersion(fileURLToPath(url), fallback);
64
+ const resolved = require.resolve(`${specifier}/package.json`);
65
+ return readPackageVersion(resolved, fallback);
65
66
  }
66
67
  catch {
67
68
  return fallback;
68
69
  }
69
70
  }
71
+ /**
72
+ * @returns {string | undefined}
73
+ */
74
+ function readOwnPackageVersion() {
75
+ try {
76
+ const ownPackagePath = fileURLToPath(new URL("../package.json", import.meta.url));
77
+ const version = readJson(ownPackagePath).version;
78
+ return typeof version === "string" && version.length > 0 ? version : undefined;
79
+ }
80
+ catch {
81
+ return undefined;
82
+ }
83
+ }
70
84
  /**
71
85
  * Pins shipped with this release for devDep-only specs that won't be in the
72
86
  * user's `node_modules` after `bunx smithers-orchestrator@latest init`. Bump
@@ -78,24 +92,29 @@ const BUNDLED_VERSION_PINS = {
78
92
  reactTypes: "19.2.14",
79
93
  reactDomTypes: "19.2.3",
80
94
  mdxTypes: "2.0.13",
95
+ nodeTypes: "25.6.0",
81
96
  };
82
97
  /**
83
98
  * @returns {DependencyVersions}
84
99
  */
85
100
  function readDependencyVersions() {
86
101
  return {
87
- smithersVersion: readPackageVersion(fileURLToPath(new URL("../package.json", import.meta.url)), "0.0.0"),
88
- zodVersion: resolveDependencyVersion("zod", BUNDLED_VERSION_PINS.zod),
89
- typescriptVersion: resolveDependencyVersion("typescript", BUNDLED_VERSION_PINS.typescript),
90
- reactTypesVersion: resolveDependencyVersion("@types/react", BUNDLED_VERSION_PINS.reactTypes),
91
- reactDomTypesVersion: resolveDependencyVersion("@types/react-dom", BUNDLED_VERSION_PINS.reactDomTypes),
92
- mdxTypesVersion: resolveDependencyVersion("@types/mdx", BUNDLED_VERSION_PINS.mdxTypes),
102
+ smithersVersion: readOwnPackageVersion(),
103
+ zodVersion: resolveInstalledPackageVersion("zod", BUNDLED_VERSION_PINS.zod),
104
+ typescriptVersion: resolveInstalledPackageVersion("typescript", BUNDLED_VERSION_PINS.typescript),
105
+ reactTypesVersion: resolveInstalledPackageVersion("@types/react", BUNDLED_VERSION_PINS.reactTypes),
106
+ reactDomTypesVersion: resolveInstalledPackageVersion("@types/react-dom", BUNDLED_VERSION_PINS.reactDomTypes),
107
+ mdxTypesVersion: resolveInstalledPackageVersion("@types/mdx", BUNDLED_VERSION_PINS.mdxTypes),
108
+ nodeTypesVersion: resolveInstalledPackageVersion("@types/node", BUNDLED_VERSION_PINS.nodeTypes),
93
109
  };
94
110
  }
95
111
  /**
96
112
  * @param {DependencyVersions} versions
97
113
  */
98
114
  function renderPackageJson(versions) {
115
+ const smithersSpec = versions.smithersVersion
116
+ ? `^${versions.smithersVersion}`
117
+ : FALLBACK_SMITHERS_SPEC;
99
118
  return JSON.stringify({
100
119
  name: "smithers-workflows",
101
120
  private: true,
@@ -108,7 +127,7 @@ function renderPackageJson(versions) {
108
127
  },
109
128
  dependencies: {
110
129
  skills: "github:mattpocock/skills",
111
- "smithers-orchestrator": "latest",
130
+ "smithers-orchestrator": smithersSpec,
112
131
  zod: versions.zodVersion,
113
132
  },
114
133
  devDependencies: {
@@ -116,6 +135,7 @@ function renderPackageJson(versions) {
116
135
  "@types/react": versions.reactTypesVersion,
117
136
  "@types/react-dom": versions.reactDomTypesVersion,
118
137
  "@types/mdx": versions.mdxTypesVersion,
138
+ "@types/node": versions.nodeTypesVersion,
119
139
  },
120
140
  }, null, 2) + "\n";
121
141
  }
@@ -143,6 +163,104 @@ function renderTsconfig() {
143
163
  exclude: ["./executions/**/*"],
144
164
  }, null, 2) + "\n";
145
165
  }
166
+ /**
167
+ * @returns {TemplateFile[]}
168
+ */
169
+ function renderAgentScaffoldFiles() {
170
+ return [
171
+ {
172
+ path: ".smithers/agents/claude-code.ts",
173
+ preserveExisting: true,
174
+ contents: [
175
+ 'import { ClaudeCodeAgent as SmithersClaudeCodeAgent } from "smithers-orchestrator";',
176
+ "",
177
+ '// Built-in Claude Code CLI agent (cliEngine: "claude-code").',
178
+ "// Tweak `model`, `cwd`, or uncomment extra options below to match your setup.",
179
+ "export const ClaudeCodeAgent = new SmithersClaudeCodeAgent({",
180
+ ' model: "claude-opus-4-6",',
181
+ " cwd: process.cwd(),",
182
+ ' // systemPrompt: "Add shared instructions for every Claude run.",',
183
+ " // timeoutMs: 10 * 60 * 1000,",
184
+ " // dangerouslySkipPermissions: true,",
185
+ "});",
186
+ "",
187
+ ].join("\n"),
188
+ },
189
+ {
190
+ path: ".smithers/agents/codex.ts",
191
+ preserveExisting: true,
192
+ contents: [
193
+ 'import { CodexAgent as SmithersCodexAgent } from "smithers-orchestrator";',
194
+ "",
195
+ '// Built-in Codex CLI agent (cliEngine: "codex").',
196
+ "// Tweak `model`, `cwd`, or uncomment extra options below to match your setup.",
197
+ "export const CodexAgent = new SmithersCodexAgent({",
198
+ ' model: "gpt-5.3-codex",',
199
+ " cwd: process.cwd(),",
200
+ " skipGitRepoCheck: true,",
201
+ ' // systemPrompt: "Add shared instructions for every Codex run.",',
202
+ ' // sandbox: "workspace-write",',
203
+ " // fullAuto: true,",
204
+ "});",
205
+ "",
206
+ ].join("\n"),
207
+ },
208
+ {
209
+ path: ".smithers/agents/gemini.ts",
210
+ preserveExisting: true,
211
+ contents: [
212
+ 'import { GeminiAgent as SmithersGeminiAgent } from "smithers-orchestrator";',
213
+ "",
214
+ '// Built-in Gemini CLI agent (cliEngine: "gemini").',
215
+ "// Tweak `model`, `cwd`, or uncomment extra options below to match your setup.",
216
+ "export const GeminiAgent = new SmithersGeminiAgent({",
217
+ ' model: "gemini-3.1-pro-preview",',
218
+ " cwd: process.cwd(),",
219
+ ' // systemPrompt: "Add shared instructions for every Gemini run.",',
220
+ ' // approvalMode: "yolo",',
221
+ ' // allowedTools: ["read_file", "write_file"],',
222
+ "});",
223
+ "",
224
+ ].join("\n"),
225
+ },
226
+ {
227
+ path: ".smithers/agents/index.ts",
228
+ preserveExisting: true,
229
+ contents: [
230
+ 'export { ClaudeCodeAgent } from "./claude-code";',
231
+ 'export { CodexAgent } from "./codex";',
232
+ 'export { GeminiAgent } from "./gemini";',
233
+ "",
234
+ ].join("\n"),
235
+ },
236
+ {
237
+ path: ".smithers/agents/README.md",
238
+ preserveExisting: true,
239
+ contents: [
240
+ "# Agent Config",
241
+ "",
242
+ "These files export the configured agent instances used by your Smithers workflows.",
243
+ "",
244
+ "- `claude-code.ts`, `codex.ts`, and `gemini.ts` are user-owned config.",
245
+ "- Edit them to pin models, set `cwd`, add a shared `systemPrompt`, or enable engine-specific flags.",
246
+ "- `index.ts` re-exports all three so root-level files can import from `./agents`.",
247
+ "",
248
+ "Examples:",
249
+ "",
250
+ "```ts",
251
+ 'import { ClaudeCodeAgent } from "./agents";',
252
+ 'import { CodexAgent } from "./agents/codex";',
253
+ "```",
254
+ "",
255
+ "Inside `.smithers/workflows/*`, use `../agents` or `../agents/<name>` instead.",
256
+ "",
257
+ "`smithers init` and `smithers init --agents-only` only create missing files in this directory.",
258
+ "Existing files here are left alone so your custom agent config is preserved.",
259
+ "",
260
+ ].join("\n"),
261
+ },
262
+ ];
263
+ }
146
264
  /**
147
265
  * @returns {TemplateFile[]}
148
266
  */
@@ -486,6 +604,183 @@ function renderPrompts() {
486
604
  "",
487
605
  ].join("\n"),
488
606
  },
607
+ {
608
+ path: ".smithers/prompts/mission-plan.mdx",
609
+ contents: [
610
+ "# Mission Plan",
611
+ "",
612
+ "You are the mission orchestrator for a long-running Smithers workflow.",
613
+ "Scope the goal with the user before execution. If critical requirements, constraints, or acceptance criteria are missing, ask one question at a time using the ask-user command from the instructions you were given.",
614
+ "",
615
+ "Design the mission as serial milestones with targeted parallelism inside each milestone.",
616
+ "Each milestone must be a meaningful checkpoint that can be validated before the next milestone begins.",
617
+ "Each feature should be narrow enough for a fresh worker session to execute without needing the full mission history.",
618
+ "Include explicit validation checks for every milestone: tests, lint/typecheck/build commands, integration checks, and UI/browser walkthroughs when the repo has an app surface.",
619
+ "Capture risks, assumptions, out-of-scope items, and anything the user should approve before work starts.",
620
+ "",
621
+ "REQUEST:",
622
+ "{props.prompt}",
623
+ "",
624
+ "LIMITS:",
625
+ "- Max milestones: {props.maxMilestones}",
626
+ "- Max features per milestone: {props.maxFeaturesPerMilestone}",
627
+ "",
628
+ "REQUIRED OUTPUT:",
629
+ "{props.schema}",
630
+ "",
631
+ ].join("\n"),
632
+ },
633
+ {
634
+ path: ".smithers/prompts/mission-worker.mdx",
635
+ contents: [
636
+ "# Mission Worker",
637
+ "",
638
+ "You are a focused feature worker in a larger mission. Treat this as a fresh context window: use the mission plan below, execute only your assigned feature, and keep handoff notes precise.",
639
+ "",
640
+ "Rules:",
641
+ "1. Stay within the assigned feature scope unless you must make a small adjacent change to keep the repo working.",
642
+ "2. Prefer existing repo patterns and run the most relevant checks you can.",
643
+ "3. Record files changed, commands run, unresolved issues, and reusable learnings for later workers.",
644
+ "4. If the feature cannot be completed, make the best safe partial progress and explain exactly what blocks it.",
645
+ "",
646
+ "MISSION GOAL:",
647
+ "{props.missionGoal}",
648
+ "",
649
+ "MILESTONE:",
650
+ "```json",
651
+ "{JSON.stringify(props.milestone, null, 2)}",
652
+ "```",
653
+ "",
654
+ "FEATURE:",
655
+ "```json",
656
+ "{JSON.stringify(props.feature, null, 2)}",
657
+ "```",
658
+ "",
659
+ "{props.previousSummary ? `PREVIOUS MILESTONE SUMMARY:\\n${props.previousSummary}` : \"\"}",
660
+ "",
661
+ "REQUIRED OUTPUT:",
662
+ "{props.schema}",
663
+ "",
664
+ ].join("\n"),
665
+ },
666
+ {
667
+ path: ".smithers/prompts/mission-integrate.mdx",
668
+ contents: [
669
+ "# Mission Integrate",
670
+ "",
671
+ "You are integrating feature-worker results for one milestone.",
672
+ "",
673
+ "{props.useWorktrees ? \"The feature work may live on per-feature worktree branches. Inspect the branches, merge successful work back into the main workspace, resolve conflicts carefully, and leave conflicted or unsafe branches unmerged with a clear explanation.\" : \"Feature workers ran in the main workspace. Inspect their results and make any small integration fixes needed before validation.\"}",
674
+ "",
675
+ "MISSION GOAL:",
676
+ "{props.missionGoal}",
677
+ "",
678
+ "MILESTONE:",
679
+ "```json",
680
+ "{JSON.stringify(props.milestone, null, 2)}",
681
+ "```",
682
+ "",
683
+ "FEATURE RESULTS:",
684
+ "```json",
685
+ "{JSON.stringify(props.results, null, 2)}",
686
+ "```",
687
+ "",
688
+ "REQUIRED OUTPUT:",
689
+ "{props.schema}",
690
+ "",
691
+ ].join("\n"),
692
+ },
693
+ {
694
+ path: ".smithers/prompts/mission-validate.mdx",
695
+ contents: [
696
+ "# Mission Validate",
697
+ "",
698
+ "You are a validation worker for a mission milestone.",
699
+ "Validate accumulated work before the orchestrator moves to the next milestone.",
700
+ "",
701
+ "Run the strongest checks that fit the repo: tests, lint, typecheck, build, smoke tests, and integration checks.",
702
+ "If the repo has a UI, launch it and exercise core flows like a user would. Check render correctness, navigation, state transitions, and obvious layout regressions.",
703
+ "If a check cannot run because the repo lacks commands or setup, report that as a validation limitation instead of inventing results.",
704
+ "",
705
+ "MISSION GOAL:",
706
+ "{props.missionGoal}",
707
+ "",
708
+ "MILESTONE:",
709
+ "```json",
710
+ "{JSON.stringify(props.milestone, null, 2)}",
711
+ "```",
712
+ "",
713
+ "INTEGRATION RESULT:",
714
+ "```json",
715
+ "{JSON.stringify(props.integration, null, 2)}",
716
+ "```",
717
+ "",
718
+ "{props.followUp ? `FOLLOW-UP RESULT:\\n${JSON.stringify(props.followUp, null, 2)}` : \"\"}",
719
+ "",
720
+ "REQUIRED OUTPUT:",
721
+ "{props.schema}",
722
+ "",
723
+ ].join("\n"),
724
+ },
725
+ {
726
+ path: ".smithers/prompts/mission-follow-up.mdx",
727
+ contents: [
728
+ "# Mission Follow-Up",
729
+ "",
730
+ "Validation found issues in the current milestone. Fix the concrete regressions and gaps before the mission proceeds.",
731
+ "Keep changes targeted. Do not begin the next milestone.",
732
+ "",
733
+ "MISSION GOAL:",
734
+ "{props.missionGoal}",
735
+ "",
736
+ "MILESTONE:",
737
+ "```json",
738
+ "{JSON.stringify(props.milestone, null, 2)}",
739
+ "```",
740
+ "",
741
+ "VALIDATION RESULT:",
742
+ "```json",
743
+ "{JSON.stringify(props.validation, null, 2)}",
744
+ "```",
745
+ "",
746
+ "REQUIRED OUTPUT:",
747
+ "{props.schema}",
748
+ "",
749
+ ].join("\n"),
750
+ },
751
+ {
752
+ path: ".smithers/prompts/mission-final.mdx",
753
+ contents: [
754
+ "# Mission Final Report",
755
+ "",
756
+ "Write the final mission report. Summarize what shipped, what was validated, what remains risky, and the recommended next actions.",
757
+ "Be concrete about files, commands, validation gaps, and any milestone that did not pass validation.",
758
+ "",
759
+ "MISSION PLAN:",
760
+ "```json",
761
+ "{JSON.stringify(props.plan, null, 2)}",
762
+ "```",
763
+ "",
764
+ "FEATURE RESULTS:",
765
+ "```json",
766
+ "{JSON.stringify(props.featureResults, null, 2)}",
767
+ "```",
768
+ "",
769
+ "INTEGRATION RESULTS:",
770
+ "```json",
771
+ "{JSON.stringify(props.integrationResults, null, 2)}",
772
+ "```",
773
+ "",
774
+ "VALIDATION RESULTS:",
775
+ "```json",
776
+ "{JSON.stringify(props.validationResults, null, 2)}",
777
+ "```",
778
+ "",
779
+ "REQUIRED OUTPUT:",
780
+ "{props.schema}",
781
+ "",
782
+ ].join("\n"),
783
+ },
489
784
  {
490
785
  path: ".smithers/prompts/tickets-create.mdx",
491
786
  contents: [
@@ -1870,6 +2165,440 @@ function renderWorkflows() {
1870
2165
  " </Workflow>",
1871
2166
  "));",
1872
2167
  ]),
2168
+ renderWorkflowFile("mission", "Mission", [
2169
+ ...sharedImports,
2170
+ 'import AskUserInstructions from "../prompts/ask-user-instructions.mdx";',
2171
+ 'import MissionPlanPrompt from "../prompts/mission-plan.mdx";',
2172
+ 'import MissionWorkerPrompt from "../prompts/mission-worker.mdx";',
2173
+ 'import MissionIntegratePrompt from "../prompts/mission-integrate.mdx";',
2174
+ 'import MissionValidatePrompt from "../prompts/mission-validate.mdx";',
2175
+ 'import MissionFollowUpPrompt from "../prompts/mission-follow-up.mdx";',
2176
+ 'import MissionFinalPrompt from "../prompts/mission-final.mdx";',
2177
+ "",
2178
+ "const missionFeatureSchema = z.looseObject({",
2179
+ ' id: z.string().default("feature"),',
2180
+ ' title: z.string().default("Feature"),',
2181
+ ' instructions: z.string().default("Complete the assigned feature."),',
2182
+ " files: z.array(z.string()).default([]),",
2183
+ " validation: z.array(z.string()).default([]),",
2184
+ ' workerType: z.enum(["implementation", "test", "docs", "research"]).default("implementation"),',
2185
+ " canRunInParallel: z.boolean().default(true),",
2186
+ "});",
2187
+ "",
2188
+ "const missionMilestoneSchema = z.looseObject({",
2189
+ ' id: z.string().default("milestone"),',
2190
+ ' title: z.string().default("Milestone"),',
2191
+ ' objective: z.string().default("Complete this milestone."),',
2192
+ " features: z.array(missionFeatureSchema).default([]),",
2193
+ " validationPlan: z.array(z.string()).default([]),",
2194
+ "});",
2195
+ "",
2196
+ "const missionPlanSchema = z.looseObject({",
2197
+ ' goal: z.string().default(""),',
2198
+ ' summary: z.string().default("Mission plan created."),',
2199
+ " milestones: z.array(missionMilestoneSchema).default([]),",
2200
+ " assumptions: z.array(z.string()).default([]),",
2201
+ " risks: z.array(z.string()).default([]),",
2202
+ " outOfScope: z.array(z.string()).default([]),",
2203
+ ' approvalNotes: z.string().nullable().default(null),',
2204
+ "});",
2205
+ "",
2206
+ "const missionApprovalSchema = z.looseObject({",
2207
+ " approved: z.boolean().default(false),",
2208
+ " note: z.string().nullable().default(null),",
2209
+ " decidedBy: z.string().nullable().default(null),",
2210
+ " decidedAt: z.string().nullable().default(null),",
2211
+ "});",
2212
+ "",
2213
+ "const missionFeatureResultSchema = z.looseObject({",
2214
+ ' featureId: z.string().default("feature"),',
2215
+ ' status: z.enum(["success", "partial", "failed"]).default("partial"),',
2216
+ ' summary: z.string().default("Feature worker completed."),',
2217
+ " filesChanged: z.array(z.string()).default([]),",
2218
+ " commandsRun: z.array(z.string()).default([]),",
2219
+ " blockers: z.array(z.string()).default([]),",
2220
+ " reusableLearnings: z.array(z.string()).default([]),",
2221
+ "});",
2222
+ "",
2223
+ "const milestoneIntegrationSchema = z.looseObject({",
2224
+ ' milestoneId: z.string().default("milestone"),',
2225
+ ' status: z.enum(["integrated", "partial", "blocked"]).default("integrated"),',
2226
+ ' summary: z.string().default("Milestone integrated."),',
2227
+ " mergedBranches: z.array(z.string()).default([]),",
2228
+ " conflictedBranches: z.array(z.string()).default([]),",
2229
+ " filesChanged: z.array(z.string()).default([]),",
2230
+ "});",
2231
+ "",
2232
+ "const milestoneValidationSchema = z.looseObject({",
2233
+ ' milestoneId: z.string().default("milestone"),',
2234
+ " passed: z.boolean().default(true),",
2235
+ ' summary: z.string().default("Validation completed."),',
2236
+ " checks: z.array(z.object({",
2237
+ " name: z.string(),",
2238
+ ' status: z.enum(["passed", "failed", "skipped"]),',
2239
+ " details: z.string().nullable().default(null),",
2240
+ " })).default([]),",
2241
+ " regressions: z.array(z.string()).default([]),",
2242
+ " followUps: z.array(z.string()).default([]),",
2243
+ "});",
2244
+ "",
2245
+ "const missionFinalSchema = z.looseObject({",
2246
+ ' status: z.enum(["completed", "partial", "cancelled"]).default("completed"),',
2247
+ ' summary: z.string().default("Mission complete."),',
2248
+ " completedMilestones: z.number().int().default(0),",
2249
+ " totalMilestones: z.number().int().default(0),",
2250
+ " validationPassed: z.boolean().default(true),",
2251
+ " remainingRisks: z.array(z.string()).default([]),",
2252
+ " nextActions: z.array(z.string()).default([]),",
2253
+ " markdownBody: z.string().default(\"\"),",
2254
+ "});",
2255
+ "",
2256
+ "const inputSchema = z.object({",
2257
+ ' prompt: z.string().default("Describe the mission goal."),',
2258
+ " requirePlanApproval: z.boolean().default(true),",
2259
+ " maxMilestones: z.number().int().min(1).max(20).default(6),",
2260
+ " maxFeaturesPerMilestone: z.number().int().min(1).max(20).default(6),",
2261
+ " maxConcurrency: z.number().int().min(1).max(10).default(3),",
2262
+ " useWorktrees: z.boolean().default(false),",
2263
+ ' baseBranch: z.string().default("main"),',
2264
+ "});",
2265
+ "",
2266
+ "const { Workflow, Task, Sequence, Parallel, Approval, Worktree, smithers, outputs } = createSmithers({",
2267
+ " input: inputSchema,",
2268
+ " missionPlan: missionPlanSchema,",
2269
+ " missionApproval: missionApprovalSchema,",
2270
+ " missionFeature: missionFeatureResultSchema,",
2271
+ " milestoneIntegration: milestoneIntegrationSchema,",
2272
+ " milestoneValidation: milestoneValidationSchema,",
2273
+ " missionFinal: missionFinalSchema,",
2274
+ "});",
2275
+ "",
2276
+ 'const missionMemory = { kind: "workflow", id: "mission" } as const;',
2277
+ "",
2278
+ "function slugify(value: unknown, fallback: string): string {",
2279
+ ' const normalized = String(value ?? "")',
2280
+ " .toLowerCase()",
2281
+ ' .replace(/[^a-z0-9]+/g, "-")',
2282
+ ' .replace(/^-+|-+$/g, "");',
2283
+ " return normalized.length > 0 ? normalized : fallback;",
2284
+ "}",
2285
+ "",
2286
+ "function asStringArray(value: unknown): string[] {",
2287
+ " return Array.isArray(value) ? value.map((entry) => String(entry)).filter(Boolean) : [];",
2288
+ "}",
2289
+ "",
2290
+ "function normalizeFeature(feature: any, index: number): any {",
2291
+ " const title = typeof feature?.title === \"string\" && feature.title.length > 0",
2292
+ " ? feature.title",
2293
+ " : `Feature ${index + 1}`;",
2294
+ " return {",
2295
+ " id: slugify(feature?.id ?? title, `feature-${index + 1}`),",
2296
+ " title,",
2297
+ " instructions: typeof feature?.instructions === \"string\" && feature.instructions.length > 0",
2298
+ " ? feature.instructions",
2299
+ " : `Complete ${title}.`,",
2300
+ " files: asStringArray(feature?.files),",
2301
+ " validation: asStringArray(feature?.validation),",
2302
+ " workerType: [\"implementation\", \"test\", \"docs\", \"research\"].includes(feature?.workerType)",
2303
+ " ? feature.workerType",
2304
+ ' : "implementation",',
2305
+ " canRunInParallel: feature?.canRunInParallel !== false,",
2306
+ " };",
2307
+ "}",
2308
+ "",
2309
+ "function normalizeMilestones(plan: any, maxMilestones: number, maxFeaturesPerMilestone: number): any[] {",
2310
+ " return (Array.isArray(plan?.milestones) ? plan.milestones : [])",
2311
+ " .slice(0, maxMilestones)",
2312
+ " .map((milestone: any, index: number) => {",
2313
+ " const title = typeof milestone?.title === \"string\" && milestone.title.length > 0",
2314
+ " ? milestone.title",
2315
+ " : `Milestone ${index + 1}`;",
2316
+ " const features = (Array.isArray(milestone?.features) ? milestone.features : [])",
2317
+ " .slice(0, maxFeaturesPerMilestone)",
2318
+ " .map((feature: any, featureIndex: number) => normalizeFeature(feature, featureIndex));",
2319
+ " return {",
2320
+ " id: slugify(milestone?.id ?? title, `milestone-${index + 1}`),",
2321
+ " title,",
2322
+ " objective: typeof milestone?.objective === \"string\" && milestone.objective.length > 0",
2323
+ " ? milestone.objective",
2324
+ " : title,",
2325
+ " validationPlan: asStringArray(milestone?.validationPlan),",
2326
+ " features: features.length > 0",
2327
+ " ? features",
2328
+ " : [normalizeFeature({ title, instructions: milestone?.objective ?? title }, 0)],",
2329
+ " };",
2330
+ " });",
2331
+ "}",
2332
+ "",
2333
+ "function featureTaskId(milestoneIndex: number, feature: any): string {",
2334
+ " return `mission:milestone:${milestoneIndex + 1}:feature:${feature.id}`;",
2335
+ "}",
2336
+ "",
2337
+ "function milestoneIntegrateId(milestoneIndex: number): string {",
2338
+ " return `mission:milestone:${milestoneIndex + 1}:integrate`;",
2339
+ "}",
2340
+ "",
2341
+ "function milestoneValidationId(milestoneIndex: number): string {",
2342
+ " return `mission:milestone:${milestoneIndex + 1}:validate`;",
2343
+ "}",
2344
+ "",
2345
+ "function milestoneFollowUpId(milestoneIndex: number): string {",
2346
+ " return `mission:milestone:${milestoneIndex + 1}:follow-up`;",
2347
+ "}",
2348
+ "",
2349
+ "function milestoneRevalidationId(milestoneIndex: number): string {",
2350
+ " return `mission:milestone:${milestoneIndex + 1}:revalidate`;",
2351
+ "}",
2352
+ "",
2353
+ "function featureNeeds(milestoneIndex: number, features: any[]): Record<string, string> {",
2354
+ " return Object.fromEntries(features.map((feature, index) => [`feature${index}`, featureTaskId(milestoneIndex, feature)]));",
2355
+ "}",
2356
+ "",
2357
+ "function featureDeps(features: any[]): Record<string, typeof missionFeatureResultSchema> {",
2358
+ " return Object.fromEntries(features.map((_, index) => [`feature${index}`, outputs.missionFeature]));",
2359
+ "}",
2360
+ "",
2361
+ "function workerAgentsFor(feature: any): any {",
2362
+ " if (feature.workerType === \"research\") return agents.smartTool;",
2363
+ " if (feature.workerType === \"docs\") return agents.cheapFast;",
2364
+ " return agents.smart;",
2365
+ "}",
2366
+ "",
2367
+ "function previousMilestoneSummary(ctx: any): string {",
2368
+ " const integrations = ctx.outputs.milestoneIntegration ?? [];",
2369
+ " const validations = ctx.outputs.milestoneValidation ?? [];",
2370
+ " return [",
2371
+ " ...integrations.map((entry: any) => `Integration: ${entry.summary}`),",
2372
+ " ...validations.map((entry: any) => `Validation: ${entry.passed ? \"passed\" : \"failed\"} - ${entry.summary}`),",
2373
+ " ].slice(-8).join(\"\\n\");",
2374
+ "}",
2375
+ "",
2376
+ "function milestoneIsTerminal(ctx: any, milestoneIndex: number): boolean {",
2377
+ " const revalidation = ctx.outputMaybe(\"milestoneValidation\", { nodeId: milestoneRevalidationId(milestoneIndex) });",
2378
+ " if (revalidation) return true;",
2379
+ " const validation = ctx.outputMaybe(\"milestoneValidation\", { nodeId: milestoneValidationId(milestoneIndex) });",
2380
+ " return Boolean(validation && validation.passed !== false);",
2381
+ "}",
2382
+ "",
2383
+ "function activeMilestoneIndex(ctx: any, milestones: any[]): number {",
2384
+ " for (let index = 0; index < milestones.length; index += 1) {",
2385
+ " if (!milestoneIsTerminal(ctx, index)) return index;",
2386
+ " }",
2387
+ " return milestones.length;",
2388
+ "}",
2389
+ "",
2390
+ "function renderFeatureWorker(ctx: any, plan: any, milestone: any, milestoneIndex: number, feature: any) {",
2391
+ " const taskId = featureTaskId(milestoneIndex, feature);",
2392
+ " const workerTask = (",
2393
+ " <Task",
2394
+ " key={taskId}",
2395
+ " id={taskId}",
2396
+ " output={outputs.missionFeature}",
2397
+ " agent={workerAgentsFor(feature)}",
2398
+ " timeoutMs={3_600_000}",
2399
+ " heartbeatTimeoutMs={900_000}",
2400
+ " continueOnFail",
2401
+ " memory={{",
2402
+ " recall: { namespace: missionMemory, query: `${plan.goal} ${milestone.title} ${feature.title}`, topK: 5 },",
2403
+ " remember: { namespace: missionMemory, key: taskId },",
2404
+ " }}",
2405
+ " >",
2406
+ " <MissionWorkerPrompt",
2407
+ " missionGoal={plan.goal || ctx.input.prompt}",
2408
+ " milestone={milestone}",
2409
+ " feature={feature}",
2410
+ " previousSummary={previousMilestoneSummary(ctx)}",
2411
+ " />",
2412
+ " </Task>",
2413
+ " );",
2414
+ "",
2415
+ " if (!(ctx.input.useWorktrees ?? false)) return workerTask;",
2416
+ "",
2417
+ " return (",
2418
+ " <Worktree",
2419
+ " key={taskId}",
2420
+ " id={`mission-worktree-${milestoneIndex + 1}-${feature.id}`}",
2421
+ " path={`.worktrees/mission-${milestoneIndex + 1}-${feature.id}`}",
2422
+ " branch={`mission/${milestoneIndex + 1}/${feature.id}`}",
2423
+ " baseBranch={ctx.input.baseBranch ?? \"main\"}",
2424
+ " >",
2425
+ " {workerTask}",
2426
+ " </Worktree>",
2427
+ " );",
2428
+ "}",
2429
+ "",
2430
+ "function renderMilestone(ctx: any, plan: any, milestone: any, milestoneIndex: number) {",
2431
+ " const features = milestone.features;",
2432
+ " const integrationId = milestoneIntegrateId(milestoneIndex);",
2433
+ " const validationId = milestoneValidationId(milestoneIndex);",
2434
+ " const integration = ctx.outputMaybe(\"milestoneIntegration\", { nodeId: integrationId });",
2435
+ " const validation = ctx.outputMaybe(\"milestoneValidation\", { nodeId: validationId });",
2436
+ " const needsFollowUp = Boolean(validation && validation.passed === false);",
2437
+ "",
2438
+ " return (",
2439
+ " <Sequence>",
2440
+ " <Parallel maxConcurrency={Math.min(ctx.input.maxConcurrency ?? 3, features.length)}>",
2441
+ " {features.map((feature: any) => renderFeatureWorker(ctx, plan, milestone, milestoneIndex, feature))}",
2442
+ " </Parallel>",
2443
+ " <Task",
2444
+ " id={integrationId}",
2445
+ " output={outputs.milestoneIntegration}",
2446
+ " agent={agents.smartTool}",
2447
+ " needs={featureNeeds(milestoneIndex, features)}",
2448
+ " deps={featureDeps(features)}",
2449
+ " timeoutMs={1_800_000}",
2450
+ " memory={{ remember: { namespace: missionMemory, key: integrationId } }}",
2451
+ " >",
2452
+ " {(deps: any) => {",
2453
+ " const results = features.map((_: any, index: number) => deps[`feature${index}`]);",
2454
+ " return (",
2455
+ " <MissionIntegratePrompt",
2456
+ " missionGoal={plan.goal || ctx.input.prompt}",
2457
+ " milestone={milestone}",
2458
+ " results={results}",
2459
+ " useWorktrees={ctx.input.useWorktrees ?? false}",
2460
+ " />",
2461
+ " );",
2462
+ " }}",
2463
+ " </Task>",
2464
+ " <Task",
2465
+ " id={validationId}",
2466
+ " output={outputs.milestoneValidation}",
2467
+ " agent={agents.smart}",
2468
+ " needs={{ integration: integrationId }}",
2469
+ " deps={{ integration: outputs.milestoneIntegration }}",
2470
+ " timeoutMs={1_800_000}",
2471
+ " heartbeatTimeoutMs={900_000}",
2472
+ " memory={{ remember: { namespace: missionMemory, key: validationId } }}",
2473
+ " >",
2474
+ " {(deps: any) => (",
2475
+ " <MissionValidatePrompt",
2476
+ " missionGoal={plan.goal || ctx.input.prompt}",
2477
+ " milestone={milestone}",
2478
+ " integration={deps.integration}",
2479
+ " />",
2480
+ " )}",
2481
+ " </Task>",
2482
+ " {needsFollowUp && (",
2483
+ " <Sequence>",
2484
+ " <Task",
2485
+ " id={milestoneFollowUpId(milestoneIndex)}",
2486
+ " output={outputs.missionFeature}",
2487
+ " agent={agents.smart}",
2488
+ " needs={{ validation: validationId }}",
2489
+ " deps={{ validation: outputs.milestoneValidation }}",
2490
+ " timeoutMs={1_800_000}",
2491
+ " memory={{ remember: { namespace: missionMemory, key: milestoneFollowUpId(milestoneIndex) } }}",
2492
+ " >",
2493
+ " {(deps: any) => (",
2494
+ " <MissionFollowUpPrompt",
2495
+ " missionGoal={plan.goal || ctx.input.prompt}",
2496
+ " milestone={milestone}",
2497
+ " validation={deps.validation}",
2498
+ " />",
2499
+ " )}",
2500
+ " </Task>",
2501
+ " <Task",
2502
+ " id={milestoneRevalidationId(milestoneIndex)}",
2503
+ " output={outputs.milestoneValidation}",
2504
+ " agent={agents.smart}",
2505
+ " needs={{ followUp: milestoneFollowUpId(milestoneIndex) }}",
2506
+ " deps={{ followUp: outputs.missionFeature }}",
2507
+ " timeoutMs={1_800_000}",
2508
+ " heartbeatTimeoutMs={900_000}",
2509
+ " memory={{ remember: { namespace: missionMemory, key: milestoneRevalidationId(milestoneIndex) } }}",
2510
+ " >",
2511
+ " {(deps: any) => (",
2512
+ " <MissionValidatePrompt",
2513
+ " missionGoal={plan.goal || ctx.input.prompt}",
2514
+ " milestone={milestone}",
2515
+ " integration={integration}",
2516
+ " followUp={deps.followUp}",
2517
+ " />",
2518
+ " )}",
2519
+ " </Task>",
2520
+ " </Sequence>",
2521
+ " )}",
2522
+ " </Sequence>",
2523
+ " );",
2524
+ "}",
2525
+ "",
2526
+ "function renderFinal(ctx: any, plan: any, milestones: any[]) {",
2527
+ " return (",
2528
+ " <Task id=\"mission:final\" output={outputs.missionFinal} agent={agents.smartTool}>",
2529
+ " <MissionFinalPrompt",
2530
+ " plan={{ ...plan, milestones }}",
2531
+ " featureResults={ctx.outputs.missionFeature ?? []}",
2532
+ " integrationResults={ctx.outputs.milestoneIntegration ?? []}",
2533
+ " validationResults={ctx.outputs.milestoneValidation ?? []}",
2534
+ " />",
2535
+ " </Task>",
2536
+ " );",
2537
+ "}",
2538
+ "",
2539
+ "export default smithers((ctx) => {",
2540
+ " const plan = ctx.outputMaybe(\"missionPlan\", { nodeId: \"mission:plan\" });",
2541
+ " const approval = ctx.outputMaybe(\"missionApproval\", { nodeId: \"mission:approve-plan\" });",
2542
+ " const approvalRequired = ctx.input.requirePlanApproval;",
2543
+ " const approvalDenied = approvalRequired && approval && approval.approved === false;",
2544
+ " const approved = !approvalRequired || approval?.approved === true;",
2545
+ " const milestones = normalizeMilestones(plan, ctx.input.maxMilestones ?? 6, ctx.input.maxFeaturesPerMilestone ?? 6);",
2546
+ " const activeIndex = approved ? activeMilestoneIndex(ctx, milestones) : 0;",
2547
+ "",
2548
+ " return (",
2549
+ " <Workflow name=\"mission\">",
2550
+ " <Sequence>",
2551
+ " <Task",
2552
+ " id=\"mission:plan\"",
2553
+ " output={outputs.missionPlan}",
2554
+ " agent={agents.smartTool}",
2555
+ " timeoutMs={1_800_000}",
2556
+ " heartbeatTimeoutMs={900_000}",
2557
+ " memory={{ remember: { namespace: missionMemory, key: \"mission:plan\" } }}",
2558
+ " >",
2559
+ " <AskUserInstructions />",
2560
+ " <MissionPlanPrompt",
2561
+ " prompt={ctx.input.prompt}",
2562
+ " maxMilestones={ctx.input.maxMilestones ?? 6}",
2563
+ " maxFeaturesPerMilestone={ctx.input.maxFeaturesPerMilestone ?? 6}",
2564
+ " />",
2565
+ " </Task>",
2566
+ "",
2567
+ " {plan && approvalRequired && !approval && (",
2568
+ " <Approval",
2569
+ " id=\"mission:approve-plan\"",
2570
+ " output={outputs.missionApproval}",
2571
+ " request={{",
2572
+ " title: \"Approve mission plan?\",",
2573
+ " summary: plan.summary || \"Review the scoped mission plan before workers begin.\",",
2574
+ " metadata: { milestones: milestones.length, risks: plan.risks ?? [] },",
2575
+ " }}",
2576
+ " onDeny=\"continue\"",
2577
+ " />",
2578
+ " )}",
2579
+ "",
2580
+ " {approvalDenied && (",
2581
+ " <Task id=\"mission:cancelled\" output={outputs.missionFinal}>",
2582
+ " {{",
2583
+ " status: \"cancelled\",",
2584
+ " summary: `Mission plan was not approved. ${approval?.note ?? \"\"}`.trim(),",
2585
+ " completedMilestones: 0,",
2586
+ " totalMilestones: milestones.length,",
2587
+ " validationPassed: false,",
2588
+ " remainingRisks: plan?.risks ?? [],",
2589
+ " nextActions: [\"Revise the mission scope and rerun the workflow.\"],",
2590
+ " markdownBody: \"# Mission Cancelled\\n\\nThe plan was not approved.\",",
2591
+ " }}",
2592
+ " </Task>",
2593
+ " )}",
2594
+ "",
2595
+ " {plan && approved && activeIndex < milestones.length && renderMilestone(ctx, plan, milestones[activeIndex], activeIndex)}",
2596
+ " {plan && approved && activeIndex >= milestones.length && renderFinal(ctx, plan, milestones)}",
2597
+ " </Sequence>",
2598
+ " </Workflow>",
2599
+ " );",
2600
+ "});",
2601
+ ]),
1873
2602
  {
1874
2603
  path: ".smithers/workflows/kanban.tsx",
1875
2604
  contents: [
@@ -2046,6 +2775,21 @@ function renderTemplateFiles(versions, env) {
2046
2775
  path: ".smithers/tsconfig.json",
2047
2776
  contents: renderTsconfig(),
2048
2777
  },
2778
+ {
2779
+ path: ".smithers/types/assets.d.ts",
2780
+ contents: [
2781
+ 'declare module "*.md" {',
2782
+ " const Component: any;",
2783
+ " export default Component;",
2784
+ "}",
2785
+ "",
2786
+ 'declare module "*.mdx" {',
2787
+ " const Component: any;",
2788
+ " export default Component;",
2789
+ "}",
2790
+ "",
2791
+ ].join("\n"),
2792
+ },
2049
2793
  {
2050
2794
  path: ".smithers/bunfig.toml",
2051
2795
  contents: ['preload = ["./preload.ts"]', ""].join("\n"),
@@ -2054,6 +2798,7 @@ function renderTemplateFiles(versions, env) {
2054
2798
  path: ".smithers/preload.ts",
2055
2799
  contents: ['import { mdxPlugin } from "smithers-orchestrator";', "", "mdxPlugin();", ""].join("\n"),
2056
2800
  },
2801
+ ...renderAgentScaffoldFiles(),
2057
2802
  {
2058
2803
  path: ".smithers/agents.ts",
2059
2804
  contents: generateAgentsTs(env),
@@ -2085,35 +2830,50 @@ function renderTemplateFiles(versions, env) {
2085
2830
  * @returns {InitResult}
2086
2831
  */
2087
2832
  export function initWorkflowPack(options = {}) {
2088
- const rootDir = resolve(options.rootDir ?? process.cwd(), ".smithers");
2833
+ const projectRoot = options.rootDir ?? process.cwd();
2834
+ const rootDir = resolve(projectRoot, ".smithers");
2089
2835
  const writtenFiles = [];
2090
2836
  const skippedFiles = [];
2091
2837
  const preservedPaths = [];
2092
- const versions = readDependencyVersions();
2093
- const env = process.env;
2094
2838
  ensureDir(rootDir);
2095
- ensureDir(resolve(rootDir, "prompts"));
2096
- ensureDir(resolve(rootDir, "components"));
2097
- ensureDir(resolve(rootDir, "workflows"));
2098
- ensureDir(resolve(rootDir, "tickets"));
2099
- const executionsDir = resolve(rootDir, "executions");
2100
- if (existsSync(executionsDir)) {
2101
- preservedPaths.push(executionsDir);
2839
+ ensureDir(resolve(rootDir, "agents"));
2840
+ /** @type {TemplateFile[]} */
2841
+ let templateFiles;
2842
+ if (options.agentsOnly) {
2843
+ templateFiles = renderAgentScaffoldFiles();
2102
2844
  }
2103
2845
  else {
2104
- ensureDir(executionsDir);
2846
+ const versions = readDependencyVersions();
2847
+ const env = process.env;
2848
+ ensureDir(resolve(rootDir, "prompts"));
2849
+ ensureDir(resolve(rootDir, "components"));
2850
+ ensureDir(resolve(rootDir, "workflows"));
2851
+ ensureDir(resolve(rootDir, "tickets"));
2852
+ const executionsDir = resolve(rootDir, "executions");
2853
+ if (existsSync(executionsDir)) {
2854
+ preservedPaths.push(executionsDir);
2855
+ }
2856
+ else {
2857
+ ensureDir(executionsDir);
2858
+ }
2859
+ templateFiles = renderTemplateFiles(versions, env);
2105
2860
  }
2106
- for (const file of renderTemplateFiles(versions, env)) {
2107
- const absolutePath = resolve(options.rootDir ?? process.cwd(), file.path);
2861
+ for (const file of templateFiles) {
2862
+ const absolutePath = resolve(projectRoot, file.path);
2108
2863
  ensureParent(absolutePath);
2109
- if (existsSync(absolutePath) && !options.force) {
2864
+ if (existsSync(absolutePath) && (file.preserveExisting || !options.force)) {
2110
2865
  skippedFiles.push(absolutePath);
2866
+ if (file.preserveExisting) {
2867
+ process.stderr.write(`[smithers:init] ${file.path} skipped: already exists\n`);
2868
+ }
2111
2869
  continue;
2112
2870
  }
2113
2871
  writeFileSync(absolutePath, file.contents, "utf8");
2114
2872
  writtenFiles.push(absolutePath);
2115
2873
  }
2116
- const install = runBunInstall(rootDir, options.skipInstall ?? false);
2874
+ const install = options.agentsOnly
2875
+ ? { status: "skipped", reason: "agents-only" }
2876
+ : runBunInstall(rootDir, options.skipInstall ?? false);
2117
2877
  return {
2118
2878
  rootDir,
2119
2879
  writtenFiles,
@@ -2156,8 +2916,10 @@ const WORKFLOW_FOLLOW_UPS = {
2156
2916
  "research": [
2157
2917
  { command: "workflow run write-a-prd", description: "Formalize findings into a PRD" },
2158
2918
  { command: "workflow run plan", description: "Turn research into an implementation plan" },
2919
+ { command: "workflow run mission", description: "Run a scoped long-horizon mission" },
2159
2920
  ],
2160
2921
  "plan": [
2922
+ { command: "workflow run mission", description: "Execute as a milestone-gated mission" },
2161
2923
  { command: "workflow run research-plan-implement", description: "Research, plan, and execute" },
2162
2924
  { command: "workflow run implement", description: "Execute the plan" },
2163
2925
  { command: "workflow run tickets-create", description: "Break plan into tickets" },
@@ -2180,6 +2942,7 @@ const WORKFLOW_FOLLOW_UPS = {
2180
2942
  { command: "workflow run tickets-create", description: "Break directly into tickets" },
2181
2943
  ],
2182
2944
  "write-a-prd": [
2945
+ { command: "workflow run mission", description: "Execute the PRD as a mission" },
2183
2946
  { command: "workflow run tickets-create", description: "Break PRD into implementable tickets" },
2184
2947
  { command: "workflow run plan", description: "Turn PRD into a phased plan" },
2185
2948
  { command: "workflow run implement", description: "Start building from the PRD" },
@@ -2196,6 +2959,11 @@ const WORKFLOW_FOLLOW_UPS = {
2196
2959
  { command: "workflow run review", description: "Review the changes" },
2197
2960
  { command: "workflow run improve-test-coverage", description: "Improve test coverage" },
2198
2961
  ],
2962
+ "mission": [
2963
+ { command: "workflow run review", description: "Review mission changes" },
2964
+ { command: "workflow run improve-test-coverage", description: "Fill validation gaps" },
2965
+ { command: "workflow run audit", description: "Audit completed feature areas" },
2966
+ ],
2199
2967
  "review": [
2200
2968
  { command: "workflow run implement", description: "Address review feedback" },
2201
2969
  ],