patchwork-os 0.2.0-beta.1 → 0.2.0-beta.3

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 (191) hide show
  1. package/README.bridge.md +5 -5
  2. package/README.md +156 -12
  3. package/deploy/deploy-dashboard.sh +25 -1
  4. package/deploy/macos/README.md +153 -0
  5. package/deploy/macos/com.patchwork.bridge.plist.template +54 -0
  6. package/deploy/macos/com.patchwork.tunnel.plist.template +76 -0
  7. package/deploy/macos/install-mac-bridge.sh +244 -0
  8. package/deploy/macos/uninstall-mac-bridge.sh +22 -0
  9. package/dist/activityLog.d.ts +6 -0
  10. package/dist/activityLog.js +8 -0
  11. package/dist/activityLog.js.map +1 -1
  12. package/dist/analyticsPrefs.d.ts +35 -2
  13. package/dist/analyticsPrefs.js +120 -21
  14. package/dist/analyticsPrefs.js.map +1 -1
  15. package/dist/analyticsSend.js +5 -1
  16. package/dist/analyticsSend.js.map +1 -1
  17. package/dist/approvalHttp.d.ts +14 -0
  18. package/dist/approvalHttp.js +172 -1
  19. package/dist/approvalHttp.js.map +1 -1
  20. package/dist/approvalQueue.d.ts +27 -2
  21. package/dist/approvalQueue.js +44 -7
  22. package/dist/approvalQueue.js.map +1 -1
  23. package/dist/automation.d.ts +34 -3
  24. package/dist/automation.js +85 -10
  25. package/dist/automation.js.map +1 -1
  26. package/dist/bridge.d.ts +2 -0
  27. package/dist/bridge.js +114 -8
  28. package/dist/bridge.js.map +1 -1
  29. package/dist/bridgeLockDiscovery.d.ts +27 -1
  30. package/dist/bridgeLockDiscovery.js +37 -11
  31. package/dist/bridgeLockDiscovery.js.map +1 -1
  32. package/dist/claudeOrchestrator.js +5 -2
  33. package/dist/claudeOrchestrator.js.map +1 -1
  34. package/dist/commands/patchworkInit.d.ts +5 -0
  35. package/dist/commands/patchworkInit.js +86 -7
  36. package/dist/commands/patchworkInit.js.map +1 -1
  37. package/dist/commands/recipe.d.ts +51 -0
  38. package/dist/commands/recipe.js +363 -3
  39. package/dist/commands/recipe.js.map +1 -1
  40. package/dist/commands/recipeInstall.js +6 -3
  41. package/dist/commands/recipeInstall.js.map +1 -1
  42. package/dist/commands/task.js +2 -2
  43. package/dist/commands/task.js.map +1 -1
  44. package/dist/config.d.ts +17 -2
  45. package/dist/config.js +54 -17
  46. package/dist/config.js.map +1 -1
  47. package/dist/connectors/baseConnector.js +25 -3
  48. package/dist/connectors/baseConnector.js.map +1 -1
  49. package/dist/connectors/tokenStorage.js +46 -10
  50. package/dist/connectors/tokenStorage.js.map +1 -1
  51. package/dist/drivers/gemini/index.d.ts +22 -0
  52. package/dist/drivers/gemini/index.js +240 -129
  53. package/dist/drivers/gemini/index.js.map +1 -1
  54. package/dist/drivers/local/index.d.ts +17 -0
  55. package/dist/drivers/local/index.js +99 -0
  56. package/dist/drivers/local/index.js.map +1 -1
  57. package/dist/drivers/openai/index.js +30 -2
  58. package/dist/drivers/openai/index.js.map +1 -1
  59. package/dist/extensionClient.d.ts +8 -0
  60. package/dist/extensionClient.js +24 -2
  61. package/dist/extensionClient.js.map +1 -1
  62. package/dist/featureFlags.d.ts +76 -0
  63. package/dist/featureFlags.js +166 -2
  64. package/dist/featureFlags.js.map +1 -1
  65. package/dist/fp/automationInterpreter.d.ts +9 -1
  66. package/dist/fp/automationInterpreter.js +151 -34
  67. package/dist/fp/automationInterpreter.js.map +1 -1
  68. package/dist/fp/automationProgram.d.ts +30 -0
  69. package/dist/fp/automationProgram.js.map +1 -1
  70. package/dist/fp/automationState.d.ts +23 -4
  71. package/dist/fp/automationState.js +28 -4
  72. package/dist/fp/automationState.js.map +1 -1
  73. package/dist/fp/interpreterContext.d.ts +66 -1
  74. package/dist/fp/interpreterContext.js +140 -1
  75. package/dist/fp/interpreterContext.js.map +1 -1
  76. package/dist/fp/policyParser.js +29 -1
  77. package/dist/fp/policyParser.js.map +1 -1
  78. package/dist/index.js +765 -69
  79. package/dist/index.js.map +1 -1
  80. package/dist/lockfile.js +4 -1
  81. package/dist/lockfile.js.map +1 -1
  82. package/dist/oauth.d.ts +9 -0
  83. package/dist/oauth.js +33 -0
  84. package/dist/oauth.js.map +1 -1
  85. package/dist/patchworkConfig.d.ts +16 -0
  86. package/dist/patchworkConfig.js +5 -0
  87. package/dist/patchworkConfig.js.map +1 -1
  88. package/dist/recipeOrchestration.js +35 -1
  89. package/dist/recipeOrchestration.js.map +1 -1
  90. package/dist/recipeRoutes.d.ts +36 -0
  91. package/dist/recipeRoutes.js +231 -32
  92. package/dist/recipeRoutes.js.map +1 -1
  93. package/dist/recipes/agentExecutor.d.ts +25 -5
  94. package/dist/recipes/agentExecutor.js.map +1 -1
  95. package/dist/recipes/chainedRunner.js +16 -2
  96. package/dist/recipes/chainedRunner.js.map +1 -1
  97. package/dist/recipes/connectorPreflight.d.ts +53 -0
  98. package/dist/recipes/connectorPreflight.js +79 -0
  99. package/dist/recipes/connectorPreflight.js.map +1 -0
  100. package/dist/recipes/githubInstallSource.d.ts +62 -0
  101. package/dist/recipes/githubInstallSource.js +125 -0
  102. package/dist/recipes/githubInstallSource.js.map +1 -0
  103. package/dist/recipes/haltCategory.d.ts +80 -0
  104. package/dist/recipes/haltCategory.js +125 -0
  105. package/dist/recipes/haltCategory.js.map +1 -0
  106. package/dist/recipes/idempotencyKey.d.ts +126 -0
  107. package/dist/recipes/idempotencyKey.js +298 -0
  108. package/dist/recipes/idempotencyKey.js.map +1 -0
  109. package/dist/recipes/judgeSummary.d.ts +50 -0
  110. package/dist/recipes/judgeSummary.js +47 -0
  111. package/dist/recipes/judgeSummary.js.map +1 -0
  112. package/dist/recipes/judgeVerdict.d.ts +48 -0
  113. package/dist/recipes/judgeVerdict.js +174 -0
  114. package/dist/recipes/judgeVerdict.js.map +1 -0
  115. package/dist/recipes/migrations/index.d.ts +9 -0
  116. package/dist/recipes/migrations/index.js +133 -0
  117. package/dist/recipes/migrations/index.js.map +1 -1
  118. package/dist/recipes/runBudget.d.ts +70 -0
  119. package/dist/recipes/runBudget.js +109 -0
  120. package/dist/recipes/runBudget.js.map +1 -0
  121. package/dist/recipes/scheduler.d.ts +7 -0
  122. package/dist/recipes/scheduler.js +31 -14
  123. package/dist/recipes/scheduler.js.map +1 -1
  124. package/dist/recipes/schema.d.ts +36 -0
  125. package/dist/recipes/toolRegistry.js +19 -0
  126. package/dist/recipes/toolRegistry.js.map +1 -1
  127. package/dist/recipes/tools/file.js +5 -2
  128. package/dist/recipes/tools/file.js.map +1 -1
  129. package/dist/recipes/tools/http.d.ts +10 -0
  130. package/dist/recipes/tools/http.js +176 -0
  131. package/dist/recipes/tools/http.js.map +1 -0
  132. package/dist/recipes/tools/index.d.ts +1 -0
  133. package/dist/recipes/tools/index.js +1 -0
  134. package/dist/recipes/tools/index.js.map +1 -1
  135. package/dist/recipes/validation.js +1 -1
  136. package/dist/recipes/validation.js.map +1 -1
  137. package/dist/recipes/yamlRunner.d.ts +88 -7
  138. package/dist/recipes/yamlRunner.js +216 -25
  139. package/dist/recipes/yamlRunner.js.map +1 -1
  140. package/dist/recipesHttp.d.ts +3 -1
  141. package/dist/recipesHttp.js +9 -3
  142. package/dist/recipesHttp.js.map +1 -1
  143. package/dist/runLog.d.ts +28 -0
  144. package/dist/runLog.js +5 -0
  145. package/dist/runLog.js.map +1 -1
  146. package/dist/server.d.ts +111 -1
  147. package/dist/server.js +480 -6
  148. package/dist/server.js.map +1 -1
  149. package/dist/streamableHttp.d.ts +9 -4
  150. package/dist/streamableHttp.js +34 -15
  151. package/dist/streamableHttp.js.map +1 -1
  152. package/dist/tools/bridgeDoctor.js +6 -2
  153. package/dist/tools/bridgeDoctor.js.map +1 -1
  154. package/dist/tools/ccRoutines.d.ts +221 -0
  155. package/dist/tools/ccRoutines.js +264 -0
  156. package/dist/tools/ccRoutines.js.map +1 -0
  157. package/dist/tools/getCodeCoverage.js +7 -3
  158. package/dist/tools/getCodeCoverage.js.map +1 -1
  159. package/dist/tools/index.js +6 -0
  160. package/dist/tools/index.js.map +1 -1
  161. package/dist/tools/openInBrowser.js +6 -1
  162. package/dist/tools/openInBrowser.js.map +1 -1
  163. package/dist/tools/recentTracesDigest.js +56 -11
  164. package/dist/tools/recentTracesDigest.js.map +1 -1
  165. package/dist/tools/testRunners/vitestJest.js +3 -1
  166. package/dist/tools/testRunners/vitestJest.js.map +1 -1
  167. package/dist/tools/utils.js +13 -7
  168. package/dist/tools/utils.js.map +1 -1
  169. package/package.json +16 -5
  170. package/scripts/postinstall.mjs +27 -0
  171. package/scripts/smoke/run-all.mjs +162 -0
  172. package/scripts/start-all.mjs +513 -0
  173. package/scripts/start-all.ps1 +209 -0
  174. package/scripts/start-all.sh +73 -17
  175. package/scripts/start-orchestrator.ps1 +158 -0
  176. package/scripts/start-remote.mjs +122 -0
  177. package/templates/automation-policies/recipe-authoring.json +1 -1
  178. package/templates/automation-policies/security-first.json +1 -1
  179. package/templates/automation-policies/strict-lint.json +1 -1
  180. package/templates/automation-policies/test-driven.json +1 -1
  181. package/templates/automation-policy.example.json +1 -1
  182. package/templates/co.patchwork-os.bridge.plist +1 -1
  183. package/templates/recipes/approval-queue-ui-test.yaml +1 -1
  184. package/templates/recipes/ctx-loop-test.yaml +1 -1
  185. package/templates/recipes/webhook/apple-watch-health-log.yaml +145 -0
  186. package/dist/commands/marketplace.d.ts +0 -16
  187. package/dist/commands/marketplace.js +0 -32
  188. package/dist/commands/marketplace.js.map +0 -1
  189. package/dist/recipes/legacyRecipeCompat.d.ts +0 -10
  190. package/dist/recipes/legacyRecipeCompat.js +0 -131
  191. package/dist/recipes/legacyRecipeCompat.js.map +0 -1
@@ -1,4 +1,6 @@
1
- import { copyFileSync, existsSync, mkdirSync, readdirSync } from "node:fs";
1
+ import { spawnSync } from "node:child_process";
2
+ import { randomBytes } from "node:crypto";
3
+ import { copyFileSync, existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync, } from "node:fs";
2
4
  import { homedir } from "node:os";
3
5
  import { dirname, join, resolve } from "node:path";
4
6
  import { fileURLToPath } from "node:url";
@@ -49,6 +51,59 @@ What it does:
49
51
  5. Print next steps
50
52
  `);
51
53
  }
54
+ function findDashboardDir() {
55
+ const here = dirname(fileURLToPath(import.meta.url));
56
+ const candidates = [
57
+ join(here, "..", "..", "dashboard"),
58
+ join(here, "..", "..", "..", "dashboard"),
59
+ ];
60
+ for (const c of candidates) {
61
+ if (existsSync(join(c, "package.json")))
62
+ return c;
63
+ }
64
+ return null;
65
+ }
66
+ export function ensureDashboardEnv(patchworkDir) {
67
+ const envPath = join(patchworkDir, ".env");
68
+ let existing = "";
69
+ if (existsSync(envPath))
70
+ existing = readFileSync(envPath, "utf8");
71
+ const hasPassword = /^DASHBOARD_PASSWORD=.+$/m.test(existing);
72
+ const hasSecret = /^DASHBOARD_SESSION_SECRET=.+$/m.test(existing);
73
+ if (hasPassword && hasSecret)
74
+ return { password: "[already set]", generated: false };
75
+ const password = randomBytes(8).toString("hex");
76
+ const secret = randomBytes(32).toString("hex");
77
+ const additions = [];
78
+ if (!hasPassword)
79
+ additions.push(`DASHBOARD_PASSWORD=${password}`);
80
+ if (!hasSecret)
81
+ additions.push(`DASHBOARD_SESSION_SECRET=${secret}`);
82
+ const sep = existing.length > 0 && !existing.endsWith("\n") ? "\n" : "";
83
+ writeFileSync(envPath, `${existing + sep + additions.join("\n")}\n`, {
84
+ mode: 0o600,
85
+ });
86
+ // Also write dashboard/.env.local so Next.js picks up credentials without start-all sourcing
87
+ const dashDir = findDashboardDir();
88
+ if (dashDir) {
89
+ const localPath = join(dashDir, ".env.local");
90
+ let local = existsSync(localPath) ? readFileSync(localPath, "utf8") : "";
91
+ if (/^DASHBOARD_PASSWORD=/m.test(local)) {
92
+ local = local.replace(/^DASHBOARD_PASSWORD=.*$/m, `DASHBOARD_PASSWORD=${password}`);
93
+ }
94
+ else {
95
+ local += `${local.endsWith("\n") ? "" : "\n"}DASHBOARD_PASSWORD=${password}\n`;
96
+ }
97
+ if (/^DASHBOARD_SESSION_SECRET=/m.test(local)) {
98
+ local = local.replace(/^DASHBOARD_SESSION_SECRET=.*$/m, `DASHBOARD_SESSION_SECRET=${secret}`);
99
+ }
100
+ else {
101
+ local += `DASHBOARD_SESSION_SECRET=${secret}\n`;
102
+ }
103
+ writeFileSync(localPath, local, { mode: 0o600 });
104
+ }
105
+ return { password, generated: true };
106
+ }
52
107
  function findTemplatesDir() {
53
108
  const here = dirname(fileURLToPath(import.meta.url));
54
109
  // dist/commands/patchworkInit.js → ../../templates/recipes
@@ -62,6 +117,18 @@ function findTemplatesDir() {
62
117
  return c;
63
118
  return null;
64
119
  }
120
+ function detectGeminiCli() {
121
+ try {
122
+ const r = spawnSync("gemini", ["--version"], {
123
+ stdio: "pipe",
124
+ timeout: 3000,
125
+ });
126
+ return r.status === 0;
127
+ }
128
+ catch {
129
+ return false;
130
+ }
131
+ }
65
132
  async function detectOllama(timeoutMs = 500) {
66
133
  try {
67
134
  const controller = new AbortController();
@@ -94,6 +161,9 @@ export async function runPatchworkInit(argv, opts = {}) {
94
161
  mkdirSync(dir, { recursive: true });
95
162
  }
96
163
  log(` ✓ ~/.patchwork scaffolded\n`);
164
+ const dashEnv = ensureDashboardEnv(patchworkDir);
165
+ if (dashEnv.generated)
166
+ log(` ✓ ~/.patchwork/.env created with dashboard credentials\n`);
97
167
  const templatesDir = findTemplatesDir();
98
168
  let recipesCopied = 0;
99
169
  let recipesSkipped = 0;
@@ -124,6 +194,10 @@ export async function runPatchworkInit(argv, opts = {}) {
124
194
  else {
125
195
  log(` ! recipe templates not found (expected templates/recipes/)\n`);
126
196
  }
197
+ const geminiCliDetected = detectGeminiCli();
198
+ log(geminiCliDetected
199
+ ? ` ✓ Gemini CLI detected — driver will default to gemini\n`
200
+ : ` · Gemini CLI not detected (install from https://github.com/google-gemini/gemini-cli)\n`);
127
201
  let ollamaDetected = false;
128
202
  if (!parsed.skipOllama) {
129
203
  ollamaDetected = await detectOllama();
@@ -157,6 +231,7 @@ export async function runPatchworkInit(argv, opts = {}) {
157
231
  const fresh = {
158
232
  model: ollamaDetected ? "local" : "claude",
159
233
  recipesDir,
234
+ ...(geminiCliDetected ? { driver: "gemini" } : {}),
160
235
  dashboard: {
161
236
  port: 3200,
162
237
  requireApproval: ["high"],
@@ -202,18 +277,22 @@ export async function runPatchworkInit(argv, opts = {}) {
202
277
  const restartLine = preToolUseHook === "added"
203
278
  ? `\n ⚠ Restart Claude Code so the new PreToolUse hook takes effect.\n (CC reads hooks at session start — existing sessions won't see the change.)\n`
204
279
  : "";
205
- log(`${restartLine}\nNext:
206
- 1. patchwork start # launch bridge + Claude + dashboard (one command)
207
- 2. patchwork-os recipe run daily-status # zero-config: yesterday's commits + today's hints
208
- 3. patchwork-os # terminal dashboard (TUI, alternative to web)
209
- 4. patchwork-os recipe list # browse installed recipes
210
- 5. patchwork-os init --with-connectors # add gmail/github/linear/etc. recipes\n`);
280
+ const dashPasswordLine = dashEnv.generated
281
+ ? `\n Dashboard password: ${dashEnv.password} (saved to ~/.patchwork/.env)\n`
282
+ : "";
283
+ log(`${restartLine}${dashPasswordLine}
284
+ Next:
285
+ 1. patchwork start # launch bridge + dashboard
286
+ 2. open http://localhost:3200 # dashboard (use password printed above)
287
+ 3. patchwork-os recipe run daily-status # zero-config: yesterday's commits + today's hints
288
+ 4. patchwork-os init --with-connectors # add gmail/github/linear/etc. recipes\n`);
211
289
  return {
212
290
  configPath,
213
291
  recipesDir,
214
292
  recipesCopied,
215
293
  recipesSkipped,
216
294
  ollamaDetected,
295
+ geminiCliDetected,
217
296
  configAction,
218
297
  preToolUseHook,
219
298
  };
@@ -1 +1 @@
1
- {"version":3,"file":"patchworkInit.js","sourceRoot":"","sources":["../../src/commands/patchworkInit.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"patchworkInit.js","sourceRoot":"","sources":["../../src/commands/patchworkInit.ts"],"names":[],"mappings":"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"}
@@ -17,6 +17,43 @@ export declare function runNew(options: NewOptions): {
17
17
  content: string;
18
18
  };
19
19
  export declare function listTemplates(): string[];
20
+ export interface InteractivePromptDeps {
21
+ /** Free-text prompt. Returns the user's trimmed answer. */
22
+ ask: (question: string) => Promise<string>;
23
+ /**
24
+ * List-select prompt. Returns the 1-based index of the choice the
25
+ * user picked. Implementations must reject 0 / out-of-range / NaN.
26
+ */
27
+ pickFromList: (question: string, options: string[]) => Promise<number>;
28
+ /** Y/N prompt. Returns true on yes. */
29
+ confirm: (question: string) => Promise<boolean>;
30
+ /** Optional preview hook — called once before the final write confirm. */
31
+ preview?: (yaml: string) => void;
32
+ }
33
+ export interface InteractiveNewOptions {
34
+ outputDir?: string;
35
+ deps: InteractivePromptDeps;
36
+ /**
37
+ * AI-suggest path. Injected so tests can stub the bridge-discovery +
38
+ * fetch pair without touching the network or the lock-file dir. When
39
+ * omitted, runNewInteractive uses the production defaults (findBridgeLock
40
+ * + global fetch). The CLI dispatch passes nothing.
41
+ */
42
+ aiSuggest?: {
43
+ findBridge?: () => {
44
+ port: number;
45
+ authToken: string;
46
+ } | null;
47
+ fetch?: typeof globalThis.fetch;
48
+ };
49
+ }
50
+ export interface InteractiveNewResult {
51
+ path: string;
52
+ content: string;
53
+ /** Lint issues surfaced by validateRecipeDefinition (warnings only — write proceeds). */
54
+ warnings: LintIssue[];
55
+ }
56
+ export declare function runNewInteractive(options: InteractiveNewOptions): Promise<InteractiveNewResult>;
20
57
  export interface SchemaWriteResult {
21
58
  outputDir: string;
22
59
  filesWritten: string[];
@@ -61,6 +98,20 @@ export interface RunRecipeOptions {
61
98
  vars?: Record<string, string>;
62
99
  workdir?: string;
63
100
  deps?: Partial<RunnerDeps>;
101
+ /**
102
+ * PR5c — stable id for one *logical* execution attempt. Forwarded to
103
+ * RunnerDeps so the runner constructs a disk-backed WriteEffectLedger
104
+ * scoped by `${recipe.name}:${manualRunId}`. Re-using the same id on
105
+ * a retry replays prior dedup records and skips already-completed
106
+ * write tools (resume semantics).
107
+ */
108
+ manualRunId?: string;
109
+ /**
110
+ * PR5c — directory holding `effect_ledger.jsonl`. Required for the
111
+ * disk-backed ledger; without it the ledger stays in-memory even
112
+ * when manualRunId is set.
113
+ */
114
+ ledgerDir?: string;
64
115
  }
65
116
  export interface RunRecipeStepSelection {
66
117
  query: string;
@@ -11,10 +11,10 @@ import { parse as parseYaml, stringify as stringifyYaml } from "yaml";
11
11
  import "../recipes/tools/index.js";
12
12
  import { loadFixtureLibrary } from "../connectors/fixtureLibrary.js";
13
13
  import { MockConnector } from "../connectors/mockConnector.js";
14
- import { defaultDeprecationWarn, normalizeRecipeForRuntime, } from "../recipes/legacyRecipeCompat.js";
14
+ import { defaultDeprecationWarn, normalizeRecipeForRuntime, } from "../recipes/migrations/index.js";
15
15
  import { tryResolveRecipePath } from "../recipes/resolveRecipePath.js";
16
16
  import { generateSchemaSet, writeSchemas } from "../recipes/schemaGenerator.js";
17
- import { getTool, isConnectorNamespace, seedToolOutputPreviewContext, } from "../recipes/toolRegistry.js";
17
+ import { getTool, isConnectorNamespace, listConnectorNamespaces, listTools, seedToolOutputPreviewContext, } from "../recipes/toolRegistry.js";
18
18
  import { validateRecipeDefinition, } from "../recipes/validation.js";
19
19
  import { buildChainedDeps, dispatchRecipe, loadYamlRecipe, render, runYamlRecipe, } from "../recipes/yamlRunner.js";
20
20
  import { findYamlRecipePath } from "../recipesHttp.js";
@@ -113,6 +113,355 @@ export function runNew(options) {
113
113
  export function listTemplates() {
114
114
  return Object.keys(TEMPLATES);
115
115
  }
116
+ // ============================================================================
117
+ // patchwork recipe new --interactive
118
+ //
119
+ // Connector-aware prompt tree. Writes a valid recipe YAML based on
120
+ // user choices. Use a `node:readline/promises` adapter in real CLI;
121
+ // tests inject a stub `InteractivePromptDeps`.
122
+ // ============================================================================
123
+ const RECIPE_NAME_RE = /^[a-z0-9][a-z0-9_-]{1,63}$/;
124
+ const CRON_SHAPE_RE = /^(@every\s+\d+\s*(ms|s|m|h)|\S+\s+\S+\s+\S+\s+\S+\s+\S+)$/i;
125
+ async function askWithValidation(deps, question, validate) {
126
+ for (let attempt = 0; attempt < 5; attempt++) {
127
+ const answer = await deps.ask(question);
128
+ const error = validate(answer);
129
+ if (!error)
130
+ return answer;
131
+ // Surface error and re-ask via the question prompt itself.
132
+ question = `${error} ${question}`;
133
+ }
134
+ throw new Error("Too many invalid answers");
135
+ }
136
+ function yamlScalar(value) {
137
+ // Quote unless the string is a simple identifier-ish token. Conservative
138
+ // — when in doubt, JSON-encode (always parses as a YAML string).
139
+ if (/^[A-Za-z0-9_.\-/:]+$/.test(value))
140
+ return value;
141
+ return JSON.stringify(value);
142
+ }
143
+ /**
144
+ * Prompt the user for the tool's required JSON Schema fields. Optional
145
+ * properties are skipped — the user can hand-edit the YAML afterwards
146
+ * if they want to set defaults. `into` is excluded since the generator
147
+ * supplies its own name.
148
+ *
149
+ * Returns an ordered list of `[key, value]` pairs so the YAML output is
150
+ * stable across runs (insertion order of object keys is sufficient in
151
+ * V8 for string keys, but an array makes the contract explicit).
152
+ */
153
+ async function collectRequiredParams(deps, tool) {
154
+ const schema = tool.paramsSchema;
155
+ if (!schema || typeof schema !== "object")
156
+ return [];
157
+ const required = Array.isArray(schema.required)
158
+ ? schema.required.filter((k) => typeof k === "string" && k !== "into")
159
+ : [];
160
+ if (required.length === 0)
161
+ return [];
162
+ const out = [];
163
+ const properties = schema.properties ?? {};
164
+ for (const key of required) {
165
+ const propMeta = properties[key];
166
+ const hint = propMeta?.description ? ` — ${propMeta.description}` : "";
167
+ const value = await askWithValidation(deps, `${tool.id} → ${key}${hint}`, (a) => (a.trim().length > 0 ? null : "required, cannot be empty."));
168
+ out.push([key, value.trim()]);
169
+ }
170
+ return out;
171
+ }
172
+ /**
173
+ * AI-suggest path. Discovers the running bridge via lock file, POSTs
174
+ * the user's natural-language goal to `/recipes/generate`, writes the
175
+ * bridge's raw YAML response to disk (no form normalization — per
176
+ * memory project_recipe_audit_2026_05_06_part2), and validates
177
+ * post-hoc as warnings only.
178
+ *
179
+ * Failure modes:
180
+ * - No bridge running → clear actionable error
181
+ * - Bridge returns 503 → "AI generation unavailable — start the
182
+ * bridge with --driver subprocess"
183
+ * - Bridge returns 4xx → surface server error message
184
+ * - Bridge returns 200 but `result.yaml` is empty → write nothing,
185
+ * throw with the raw refusal text
186
+ */
187
+ async function runNewAiSuggest(options) {
188
+ const { deps } = options;
189
+ const name = await askWithValidation(deps, "Recipe name", (a) => RECIPE_NAME_RE.test(a)
190
+ ? null
191
+ : 'must match /^[a-z0-9][a-z0-9_-]{1,63}$/ (e.g. "morning-brief").');
192
+ const goal = await askWithValidation(deps, "Describe what the recipe should do (one-paragraph natural language)", (a) => (a.trim().length > 0 ? null : "goal cannot be empty."));
193
+ // Resolve bridge discovery + fetch — production defaults or test injection.
194
+ const findBridge = options.aiSuggest?.findBridge ??
195
+ (() => {
196
+ // Lazy import so non-AI paths don't pay the cost.
197
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
198
+ const mod = require("../bridgeLockDiscovery.js");
199
+ return mod.findBridgeLock();
200
+ });
201
+ const doFetch = options.aiSuggest?.fetch ?? globalThis.fetch;
202
+ const lock = findBridge();
203
+ if (!lock) {
204
+ throw new Error("AI generation requires a running bridge. Start one with `patchwork start` " +
205
+ "(or `claude-ide-bridge --driver subprocess` for the bridge-only mode), " +
206
+ "then retry.");
207
+ }
208
+ let response;
209
+ try {
210
+ response = await doFetch(`http://127.0.0.1:${lock.port}/recipes/generate`, {
211
+ method: "POST",
212
+ headers: {
213
+ "Content-Type": "application/json",
214
+ Authorization: `Bearer ${lock.authToken}`,
215
+ },
216
+ body: JSON.stringify({ prompt: goal.trim() }),
217
+ });
218
+ }
219
+ catch (err) {
220
+ throw new Error(`Could not reach the bridge at port ${lock.port}: ${err instanceof Error ? err.message : String(err)}`);
221
+ }
222
+ let payload;
223
+ try {
224
+ payload = (await response.json());
225
+ }
226
+ catch {
227
+ throw new Error(`Bridge returned non-JSON response (status ${response.status})`);
228
+ }
229
+ if (response.status === 503 || payload.unavailable) {
230
+ throw new Error("AI generation unavailable — start the bridge with `--driver subprocess`.");
231
+ }
232
+ if (!payload.ok) {
233
+ throw new Error(payload.error ?? `Bridge returned ${response.status}`);
234
+ }
235
+ const yamlBody = (payload.yaml ?? "").trim();
236
+ if (!yamlBody) {
237
+ throw new Error("Bridge returned empty YAML — try a more specific goal.");
238
+ }
239
+ // Ensure the SchemaStore pragma is present at line 1 so the file
240
+ // gets autocomplete on first open. If the bridge already prepended
241
+ // one, leave it.
242
+ const hasPragma = /^#\s*yaml-language-server:/.test(yamlBody);
243
+ const content = hasPragma
244
+ ? `${yamlBody}\n`
245
+ : `${RECIPE_SCHEMA_HEADER}\n${yamlBody}\n`;
246
+ if (deps.preview)
247
+ deps.preview(content);
248
+ const shouldWrite = await deps.confirm("Write to disk?");
249
+ if (!shouldWrite) {
250
+ throw new Error("Cancelled by user");
251
+ }
252
+ const outputDir = options.outputDir ?? RECIPES_DIR;
253
+ if (!existsSync(outputDir)) {
254
+ mkdirSync(outputDir, { recursive: true });
255
+ }
256
+ const outputPath = join(outputDir, `${name}.yaml`);
257
+ if (existsSync(outputPath)) {
258
+ throw new Error(`Recipe already exists: ${outputPath}`);
259
+ }
260
+ writeFileSync(outputPath, content);
261
+ // Lint post-hoc. Surface as warnings; never block — the user asked
262
+ // for a draft, the bridge produced it, the file is on disk.
263
+ let warnings = [];
264
+ try {
265
+ const parsed = parseYaml(content);
266
+ const lintResult = validateRecipeDefinition(parsed);
267
+ warnings = lintResult.issues ?? [];
268
+ }
269
+ catch (err) {
270
+ warnings = [
271
+ {
272
+ level: "warning",
273
+ message: `AI-generated YAML did not parse cleanly: ${err instanceof Error ? err.message : String(err)}`,
274
+ },
275
+ ];
276
+ }
277
+ return { path: outputPath, content, warnings };
278
+ }
279
+ export async function runNewInteractive(options) {
280
+ const { deps } = options;
281
+ // Mode pick — Guided (full prompt tree), Template (existing runNew
282
+ // templates: minimal | daily | inbox), or AI-suggest (asks the running
283
+ // bridge's /recipes/generate endpoint and writes the raw response).
284
+ //
285
+ // AI-suggest skips the form normalizer per memory
286
+ // project_recipe_audit_2026_05_06_part2 — applyAiYaml on the dashboard
287
+ // was lossy. Here we writeFileSync the bridge's `result.yaml` verbatim
288
+ // (only the SchemaStore pragma is prepended if absent) and validate
289
+ // post-hoc as warnings, never blocking the write.
290
+ const templates = listTemplates();
291
+ const modeChoices = [
292
+ "Guided — full prompt tree (recommended)",
293
+ `Template — pick from ${templates.length} starters (${templates.join(", ")})`,
294
+ "AI suggest — describe what you want; the bridge drafts a YAML",
295
+ ];
296
+ const modeIdx = await deps.pickFromList("How do you want to start?", modeChoices);
297
+ // AI-suggest mode: route through the running bridge's /recipes/generate.
298
+ if (modeIdx === 3) {
299
+ return runNewAiSuggest(options);
300
+ }
301
+ // Template mode: re-use the existing runNew() flow with the picked template.
302
+ if (modeIdx === 2) {
303
+ const name = await askWithValidation(deps, "Recipe name", (a) => RECIPE_NAME_RE.test(a)
304
+ ? null
305
+ : 'must match /^[a-z0-9][a-z0-9_-]{1,63}$/ (e.g. "morning-brief").');
306
+ const description = await askWithValidation(deps, "One-line description", (a) => (a.trim().length > 0 ? null : "cannot be empty."));
307
+ const templateIdx = await deps.pickFromList("Pick a template", templates);
308
+ const template = templates[templateIdx - 1];
309
+ if (!template)
310
+ throw new Error("Invalid template selection");
311
+ const result = runNew({
312
+ name,
313
+ description,
314
+ template,
315
+ ...(options.outputDir ? { outputDir: options.outputDir } : {}),
316
+ });
317
+ if (deps.preview)
318
+ deps.preview(result.content);
319
+ const shouldWrite = await deps.confirm("Keep this recipe?");
320
+ if (!shouldWrite) {
321
+ // runNew already wrote the file — clean up to honor "cancel."
322
+ try {
323
+ if (existsSync(result.path)) {
324
+ const { unlinkSync } = await import("node:fs");
325
+ unlinkSync(result.path);
326
+ }
327
+ }
328
+ catch {
329
+ // best effort
330
+ }
331
+ throw new Error("Cancelled by user");
332
+ }
333
+ return { path: result.path, content: result.content, warnings: [] };
334
+ }
335
+ // Guided mode (default — modeIdx === 1).
336
+ const name = await askWithValidation(deps, "Recipe name", (a) => RECIPE_NAME_RE.test(a)
337
+ ? null
338
+ : 'must match /^[a-z0-9][a-z0-9_-]{1,63}$/ (e.g. "morning-brief").');
339
+ const description = await askWithValidation(deps, "One-line description", (a) => (a.trim().length > 0 ? null : "cannot be empty."));
340
+ const triggerTypes = ["manual", "cron"];
341
+ const triggerIdx = await deps.pickFromList("Trigger type", triggerTypes.slice());
342
+ const triggerType = triggerTypes[triggerIdx - 1];
343
+ if (!triggerType)
344
+ throw new Error("Invalid trigger selection");
345
+ const triggerLines = [`trigger:`, ` type: ${triggerType}`];
346
+ if (triggerType === "cron") {
347
+ const cron = await askWithValidation(deps, "Cron expression (e.g. '0 8 * * *' for 8am daily)", (a) => CRON_SHAPE_RE.test(a.trim())
348
+ ? null
349
+ : 'expected 5-field cron or "@every Ns|Nm|Nh".');
350
+ triggerLines.push(` at: ${yamlScalar(cron.trim())}`);
351
+ }
352
+ // Step loop. The user adds tool / agent steps until they pick "done".
353
+ // Each tool pick reads its paramsSchema and prompts for required fields,
354
+ // so the generated recipe is closer to runnable than the MVP slice.
355
+ const namespaces = listConnectorNamespaces();
356
+ const stepLines = ["steps:"];
357
+ const intoNames = [];
358
+ let lastAgentInto = null;
359
+ let toolStepCount = 0;
360
+ let agentStepCount = 0;
361
+ const stepKindChoices = [
362
+ "Add a tool step (calls a registered tool — gmail.fetch_unread, github.list_issues, …)",
363
+ "Add an agent step (LLM prompt — drafts, classifies, summarizes)",
364
+ "Done — preview and write",
365
+ ];
366
+ for (let stepIdx = 0; stepIdx < 20; stepIdx++) {
367
+ const kind = await deps.pickFromList(stepIdx === 0
368
+ ? "Build steps — what's next?"
369
+ : "Add another step? (or pick Done)", stepKindChoices);
370
+ if (kind === 3)
371
+ break;
372
+ if (kind === 1) {
373
+ // Tool step
374
+ const nsIdx = await deps.pickFromList("Pick a connector", namespaces.slice());
375
+ const ns = namespaces[nsIdx - 1];
376
+ if (!ns)
377
+ throw new Error("Invalid connector selection");
378
+ const tools = listTools(ns);
379
+ const labels = tools.map((t) => `${t.id} — ${t.description}`);
380
+ const toolIdx = await deps.pickFromList(`Pick a ${ns} tool`, labels);
381
+ const tool = tools[toolIdx - 1];
382
+ if (!tool)
383
+ throw new Error(`Invalid tool selection for ${ns}`);
384
+ toolStepCount += 1;
385
+ const intoName = toolStepCount === 1 ? `${ns}_result` : `${ns}_result_${toolStepCount}`;
386
+ intoNames.push(intoName);
387
+ stepLines.push(` - tool: ${tool.id}`);
388
+ // Prompt for required params from the tool's JSON schema.
389
+ const params = await collectRequiredParams(deps, tool);
390
+ for (const [key, value] of params) {
391
+ stepLines.push(` ${key}: ${yamlScalar(value)}`);
392
+ }
393
+ stepLines.push(` into: ${intoName}`);
394
+ }
395
+ else if (kind === 2) {
396
+ // Agent step
397
+ const prompt = await askWithValidation(deps, "Agent prompt", (a) => a.trim().length > 0 ? null : "cannot be empty.");
398
+ agentStepCount += 1;
399
+ const intoName = agentStepCount === 1
400
+ ? "agent_output"
401
+ : `agent_output_${agentStepCount}`;
402
+ lastAgentInto = intoName;
403
+ stepLines.push(` - agent: true`);
404
+ stepLines.push(` prompt: |`);
405
+ for (const line of prompt.split("\n")) {
406
+ stepLines.push(` ${line}`);
407
+ }
408
+ stepLines.push(` into: ${intoName}`);
409
+ }
410
+ }
411
+ // Always tail with file.write to inbox so the user sees output somewhere.
412
+ // Reference the most recent agent output when one exists, otherwise fall
413
+ // back to the most recent tool result (or a TODO comment if neither).
414
+ const tailRef = lastAgentInto ?? intoNames[intoNames.length - 1] ?? null;
415
+ stepLines.push(` - tool: file.write`);
416
+ stepLines.push(` path: "~/.patchwork/inbox/${name}-{{date}}.md"`);
417
+ stepLines.push(` content: |`);
418
+ stepLines.push(` # ${name} — {{date}}`);
419
+ stepLines.push(``);
420
+ if (tailRef) {
421
+ stepLines.push(` {{${tailRef}}}`);
422
+ }
423
+ else {
424
+ stepLines.push(` (no steps configured — fill in)`);
425
+ }
426
+ const content = `${RECIPE_SCHEMA_HEADER}\n` +
427
+ `version: 1.0.0\n` +
428
+ `name: ${name}\n` +
429
+ `description: ${yamlScalar(description)}\n` +
430
+ `${triggerLines.join("\n")}\n` +
431
+ `${stepLines.join("\n")}\n`;
432
+ // Lint pre-write. validateRecipeDefinition expects parsed YAML.
433
+ let warnings = [];
434
+ try {
435
+ const parsed = parseYaml(content);
436
+ const lintResult = validateRecipeDefinition(parsed);
437
+ warnings = lintResult.issues ?? [];
438
+ }
439
+ catch {
440
+ // Parse failure here is a bug in the generator — surface but don't block.
441
+ warnings = [
442
+ {
443
+ level: "error",
444
+ message: "Generated YAML failed to parse — please report this.",
445
+ },
446
+ ];
447
+ }
448
+ if (deps.preview)
449
+ deps.preview(content);
450
+ const shouldWrite = await deps.confirm("Write to disk?");
451
+ if (!shouldWrite) {
452
+ throw new Error("Cancelled by user");
453
+ }
454
+ const outputDir = options.outputDir ?? RECIPES_DIR;
455
+ if (!existsSync(outputDir)) {
456
+ mkdirSync(outputDir, { recursive: true });
457
+ }
458
+ const outputPath = join(outputDir, `${name}.yaml`);
459
+ if (existsSync(outputPath)) {
460
+ throw new Error(`Recipe already exists: ${outputPath}`);
461
+ }
462
+ writeFileSync(outputPath, content);
463
+ return { path: outputPath, content, warnings };
464
+ }
116
465
  export async function runSchema(outputDir) {
117
466
  const resolvedOutputDir = resolve(outputDir);
118
467
  const schemas = generateSchemaSet();
@@ -420,6 +769,8 @@ export async function runRecipe(recipeRef, options = {}) {
420
769
  const runnerDeps = {
421
770
  ...options.deps,
422
771
  workdir: options.workdir ?? options.deps?.workdir ?? process.cwd(),
772
+ ...(options.manualRunId && { manualRunId: options.manualRunId }),
773
+ ...(options.ledgerDir && { ledgerDir: options.ledgerDir }),
423
774
  };
424
775
  if (options.dryRun) {
425
776
  throw new Error("runRecipeDryPlan must be used for dry-run execution");
@@ -832,7 +1183,16 @@ export async function runPreflight(recipeRef, options = {}) {
832
1183
  }
833
1184
  const plan = await runRecipeDryPlan(recipeRef, options);
834
1185
  const requireWriteAck = options.requireWriteAck ?? true;
835
- const allowlist = new Set(options.allowWrites ?? []);
1186
+ // Merge the recipe's declared allowWrites (YAML) with any caller-supplied
1187
+ // entries (e.g. --allow-write CLI flag). Either source is sufficient.
1188
+ const recipeForWrites = loadYamlRecipe(recipePath);
1189
+ const recipeAllowWrites = Array.isArray(recipeForWrites.allowWrites)
1190
+ ? recipeForWrites.allowWrites.filter((entry) => typeof entry === "string")
1191
+ : [];
1192
+ const allowlist = new Set([
1193
+ ...recipeAllowWrites,
1194
+ ...(options.allowWrites ?? []),
1195
+ ]);
836
1196
  for (const step of plan.steps) {
837
1197
  if (step.type === "tool" && step.tool && step.resolved === false) {
838
1198
  issues.push({