@agent-native/core 0.44.3 → 0.45.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 (129) hide show
  1. package/dist/cli/connect.d.ts +2 -1
  2. package/dist/cli/connect.d.ts.map +1 -1
  3. package/dist/cli/connect.js +185 -5
  4. package/dist/cli/connect.js.map +1 -1
  5. package/dist/cli/index.js +27 -0
  6. package/dist/cli/index.js.map +1 -1
  7. package/dist/cli/plan-local.d.ts +43 -0
  8. package/dist/cli/plan-local.d.ts.map +1 -0
  9. package/dist/cli/plan-local.js +477 -0
  10. package/dist/cli/plan-local.js.map +1 -0
  11. package/dist/cli/pr-visual-recap-workflow.d.ts +1 -1
  12. package/dist/cli/pr-visual-recap-workflow.d.ts.map +1 -1
  13. package/dist/cli/pr-visual-recap-workflow.js +1 -1
  14. package/dist/cli/pr-visual-recap-workflow.js.map +1 -1
  15. package/dist/cli/recap.d.ts +164 -0
  16. package/dist/cli/recap.d.ts.map +1 -1
  17. package/dist/cli/recap.js +657 -10
  18. package/dist/cli/recap.js.map +1 -1
  19. package/dist/cli/skills.d.ts +6 -2
  20. package/dist/cli/skills.d.ts.map +1 -1
  21. package/dist/cli/skills.js +440 -37
  22. package/dist/cli/skills.js.map +1 -1
  23. package/dist/client/blocks/library/AnnotatedCodeBlock.d.ts.map +1 -1
  24. package/dist/client/blocks/library/AnnotatedCodeBlock.js +18 -4
  25. package/dist/client/blocks/library/AnnotatedCodeBlock.js.map +1 -1
  26. package/dist/client/blocks/library/ApiEndpointBlock.d.ts.map +1 -1
  27. package/dist/client/blocks/library/ApiEndpointBlock.js +11 -7
  28. package/dist/client/blocks/library/ApiEndpointBlock.js.map +1 -1
  29. package/dist/client/blocks/library/DiffBlock.d.ts.map +1 -1
  30. package/dist/client/blocks/library/DiffBlock.js +90 -7
  31. package/dist/client/blocks/library/DiffBlock.js.map +1 -1
  32. package/dist/client/blocks/library/HighlightedCode.d.ts +2 -1
  33. package/dist/client/blocks/library/HighlightedCode.d.ts.map +1 -1
  34. package/dist/client/blocks/library/HighlightedCode.js +2 -2
  35. package/dist/client/blocks/library/HighlightedCode.js.map +1 -1
  36. package/dist/client/blocks/library/JsonExplorerBlock.d.ts.map +1 -1
  37. package/dist/client/blocks/library/JsonExplorerBlock.js +57 -13
  38. package/dist/client/blocks/library/JsonExplorerBlock.js.map +1 -1
  39. package/dist/client/blocks/library/annotation-rail.d.ts +15 -5
  40. package/dist/client/blocks/library/annotation-rail.d.ts.map +1 -1
  41. package/dist/client/blocks/library/annotation-rail.js +35 -24
  42. package/dist/client/blocks/library/annotation-rail.js.map +1 -1
  43. package/dist/client/blocks/library/code-filename-label.d.ts +8 -0
  44. package/dist/client/blocks/library/code-filename-label.d.ts.map +1 -0
  45. package/dist/client/blocks/library/code-filename-label.js +15 -0
  46. package/dist/client/blocks/library/code-filename-label.js.map +1 -0
  47. package/dist/client/blocks/library/code.d.ts.map +1 -1
  48. package/dist/client/blocks/library/code.js +28 -6
  49. package/dist/client/blocks/library/code.js.map +1 -1
  50. package/dist/client/blocks/library/columns.d.ts.map +1 -1
  51. package/dist/client/blocks/library/columns.js +11 -1
  52. package/dist/client/blocks/library/columns.js.map +1 -1
  53. package/dist/client/blocks/library/diff.config.d.ts +1 -1
  54. package/dist/client/blocks/library/diff.config.js.map +1 -1
  55. package/dist/client/blocks/library/narrow-container.d.ts +13 -0
  56. package/dist/client/blocks/library/narrow-container.d.ts.map +1 -0
  57. package/dist/client/blocks/library/narrow-container.js +32 -0
  58. package/dist/client/blocks/library/narrow-container.js.map +1 -0
  59. package/dist/client/blocks/library/question-form.d.ts.map +1 -1
  60. package/dist/client/blocks/library/question-form.js +8 -9
  61. package/dist/client/blocks/library/question-form.js.map +1 -1
  62. package/dist/client/blocks/library/tabs.d.ts.map +1 -1
  63. package/dist/client/blocks/library/tabs.js +11 -5
  64. package/dist/client/blocks/library/tabs.js.map +1 -1
  65. package/dist/client/blocks/types.d.ts +2 -0
  66. package/dist/client/blocks/types.d.ts.map +1 -1
  67. package/dist/client/blocks/types.js.map +1 -1
  68. package/dist/client/composer/TiptapComposer.d.ts.map +1 -1
  69. package/dist/client/composer/TiptapComposer.js +4 -1
  70. package/dist/client/composer/TiptapComposer.js.map +1 -1
  71. package/dist/client/db-admin/TableEditor.d.ts.map +1 -1
  72. package/dist/client/db-admin/TableEditor.js +3 -1
  73. package/dist/client/db-admin/TableEditor.js.map +1 -1
  74. package/dist/db/client.d.ts +19 -0
  75. package/dist/db/client.d.ts.map +1 -1
  76. package/dist/db/client.js +43 -2
  77. package/dist/db/client.js.map +1 -1
  78. package/dist/db/migrations.d.ts.map +1 -1
  79. package/dist/db/migrations.js +9 -2
  80. package/dist/db/migrations.js.map +1 -1
  81. package/dist/deploy/build.d.ts.map +1 -1
  82. package/dist/deploy/build.js +8 -0
  83. package/dist/deploy/build.js.map +1 -1
  84. package/dist/extensions/html-shell.js +1 -1
  85. package/dist/extensions/html-shell.js.map +1 -1
  86. package/dist/jobs/scheduler.d.ts.map +1 -1
  87. package/dist/jobs/scheduler.js +5 -1
  88. package/dist/jobs/scheduler.js.map +1 -1
  89. package/dist/mcp/build-server.d.ts +1 -0
  90. package/dist/mcp/build-server.d.ts.map +1 -1
  91. package/dist/mcp/build-server.js +7 -3
  92. package/dist/mcp/build-server.js.map +1 -1
  93. package/dist/mcp/oauth-route.d.ts.map +1 -1
  94. package/dist/mcp/oauth-route.js +56 -19
  95. package/dist/mcp/oauth-route.js.map +1 -1
  96. package/dist/mcp/oauth-store.d.ts +1 -0
  97. package/dist/mcp/oauth-store.d.ts.map +1 -1
  98. package/dist/mcp/oauth-store.js +9 -0
  99. package/dist/mcp/oauth-store.js.map +1 -1
  100. package/dist/mcp/server.d.ts.map +1 -1
  101. package/dist/mcp/server.js +9 -4
  102. package/dist/mcp/server.js.map +1 -1
  103. package/dist/mcp-client/errors.js +3 -3
  104. package/dist/mcp-client/errors.js.map +1 -1
  105. package/dist/server/agent-chat-plugin.d.ts.map +1 -1
  106. package/dist/server/agent-chat-plugin.js +3 -1
  107. package/dist/server/agent-chat-plugin.js.map +1 -1
  108. package/dist/server/agent-teams.d.ts.map +1 -1
  109. package/dist/server/agent-teams.js +10 -2
  110. package/dist/server/agent-teams.js.map +1 -1
  111. package/dist/server/auth.d.ts.map +1 -1
  112. package/dist/server/auth.js +7 -3
  113. package/dist/server/auth.js.map +1 -1
  114. package/dist/server/recap-image-route.d.ts.map +1 -1
  115. package/dist/server/recap-image-route.js +3 -6
  116. package/dist/server/recap-image-route.js.map +1 -1
  117. package/dist/server/sentry.d.ts.map +1 -1
  118. package/dist/server/sentry.js +12 -5
  119. package/dist/server/sentry.js.map +1 -1
  120. package/dist/server/social-og-image.d.ts.map +1 -1
  121. package/dist/server/social-og-image.js +3 -1
  122. package/dist/server/social-og-image.js.map +1 -1
  123. package/dist/styles/blocks.css +36 -10
  124. package/dist/templates/workspace-core/.agents/skills/external-agents/SKILL.md +22 -6
  125. package/docs/content/plan-plugin.md +18 -1
  126. package/docs/content/pr-visual-recap.md +37 -10
  127. package/docs/content/template-plan.md +45 -1
  128. package/package.json +1 -1
  129. package/src/templates/workspace-core/.agents/skills/external-agents/SKILL.md +22 -6
@@ -0,0 +1,477 @@
1
+ /**
2
+ * DB-free local plan helpers.
3
+ *
4
+ * These commands are intentionally separate from the Plan app actions. They do
5
+ * not call MCP, HTTP, SQLite, or the Plan template runtime; they only read and
6
+ * write local files so privacy-focused users have an auditable no-DB path.
7
+ */
8
+ import fs from "node:fs";
9
+ import path from "node:path";
10
+ import { pathToFileURL } from "node:url";
11
+ function parseArgs(argv) {
12
+ const out = {};
13
+ for (let i = 0; i < argv.length; i += 1) {
14
+ const token = argv[i];
15
+ if (!token.startsWith("--"))
16
+ continue;
17
+ const key = token.slice(2);
18
+ const next = argv[i + 1];
19
+ if (next === undefined || next.startsWith("--"))
20
+ out[key] = true;
21
+ else {
22
+ out[key] = next;
23
+ i += 1;
24
+ }
25
+ }
26
+ return out;
27
+ }
28
+ function stringArg(args, key) {
29
+ const value = args[key];
30
+ if (typeof value !== "string" || value.length === 0) {
31
+ throw new Error(`Missing --${key}`);
32
+ }
33
+ return value;
34
+ }
35
+ function optionalArg(args, key) {
36
+ const value = args[key];
37
+ return typeof value === "string" && value.length > 0 ? value : undefined;
38
+ }
39
+ function boolArg(args, key) {
40
+ return args[key] === true;
41
+ }
42
+ export function localPlanFolderName(title) {
43
+ const slug = title
44
+ .normalize("NFKD")
45
+ .replace(/[\u0300-\u036f]/g, "")
46
+ .toLowerCase()
47
+ .replace(/[^a-z0-9]+/g, "-")
48
+ .replace(/^-+|-+$/g, "")
49
+ .slice(0, 64)
50
+ .replace(/-+$/g, "");
51
+ return slug || "untitled-plan";
52
+ }
53
+ function normalizeKind(value) {
54
+ if (!value)
55
+ return "plan";
56
+ if (value === "plan" || value === "recap")
57
+ return value;
58
+ throw new Error(`Invalid --kind "${value}" (expected plan or recap)`);
59
+ }
60
+ function defaultPlansDir() {
61
+ return path.resolve(process.env.PLAN_LOCAL_DIR || "plans");
62
+ }
63
+ function escapeHtml(value) {
64
+ return value
65
+ .replace(/&/g, "&amp;")
66
+ .replace(/</g, "&lt;")
67
+ .replace(/>/g, "&gt;")
68
+ .replace(/"/g, "&quot;")
69
+ .replace(/'/g, "&#39;");
70
+ }
71
+ function stripFrontmatter(source) {
72
+ if (!source.startsWith("---\n"))
73
+ return { body: source, frontmatter: {} };
74
+ const end = source.indexOf("\n---", 4);
75
+ if (end < 0)
76
+ return { body: source, frontmatter: {} };
77
+ const frontmatterSource = source.slice(4, end).trim();
78
+ const body = source.slice(end + 4).replace(/^\r?\n/, "");
79
+ const frontmatter = {};
80
+ for (const line of frontmatterSource.split(/\r?\n/)) {
81
+ const match = line.match(/^([A-Za-z0-9_-]+):\s*(.*)$/);
82
+ if (!match)
83
+ continue;
84
+ const value = match[2]
85
+ .trim()
86
+ .replace(/^['"]|['"]$/g, "")
87
+ .trim();
88
+ frontmatter[match[1]] = value;
89
+ }
90
+ return { body, frontmatter };
91
+ }
92
+ function firstHeading(source) {
93
+ for (const line of source.split(/\r?\n/)) {
94
+ const match = line.match(/^#{1,3}\s+(.+)$/);
95
+ if (match)
96
+ return match[1].trim();
97
+ }
98
+ return null;
99
+ }
100
+ function splitMdxBlocks(source) {
101
+ const blocks = [];
102
+ const lines = source.split(/\r?\n/);
103
+ let markdown = [];
104
+ let component = null;
105
+ let componentName = "";
106
+ function flushMarkdown() {
107
+ const value = markdown.join("\n").trim();
108
+ if (value)
109
+ blocks.push({ type: "markdown", value });
110
+ markdown = [];
111
+ }
112
+ function flushComponent() {
113
+ if (!component)
114
+ return;
115
+ blocks.push({
116
+ type: "component",
117
+ name: componentName || "MDX component",
118
+ value: component.join("\n").trim(),
119
+ });
120
+ component = null;
121
+ componentName = "";
122
+ }
123
+ for (const line of lines) {
124
+ const start = line.match(/^<([A-Z][A-Za-z0-9_]*)\b/);
125
+ if (!component && start) {
126
+ flushMarkdown();
127
+ component = [line];
128
+ componentName = start[1];
129
+ if (/\/>\s*$/.test(line))
130
+ flushComponent();
131
+ continue;
132
+ }
133
+ if (component) {
134
+ component.push(line);
135
+ if (new RegExp(`</${componentName}>\\s*$`).test(line)) {
136
+ flushComponent();
137
+ }
138
+ continue;
139
+ }
140
+ markdown.push(line);
141
+ }
142
+ flushComponent();
143
+ flushMarkdown();
144
+ return blocks;
145
+ }
146
+ function renderMarkdownish(source) {
147
+ const lines = source.split(/\r?\n/);
148
+ const html = [];
149
+ let inCode = false;
150
+ let codeLines = [];
151
+ let listLines = [];
152
+ function flushList() {
153
+ if (listLines.length === 0)
154
+ return;
155
+ html.push(`<ul>${listLines.map((line) => `<li>${escapeHtml(line)}</li>`).join("")}</ul>`);
156
+ listLines = [];
157
+ }
158
+ function flushCode() {
159
+ if (!inCode)
160
+ return;
161
+ html.push(`<pre><code>${escapeHtml(codeLines.join("\n"))}</code></pre>`);
162
+ codeLines = [];
163
+ inCode = false;
164
+ }
165
+ for (const line of lines) {
166
+ if (line.startsWith("```")) {
167
+ if (inCode)
168
+ flushCode();
169
+ else {
170
+ flushList();
171
+ inCode = true;
172
+ codeLines = [];
173
+ }
174
+ continue;
175
+ }
176
+ if (inCode) {
177
+ codeLines.push(line);
178
+ continue;
179
+ }
180
+ const heading = line.match(/^(#{1,4})\s+(.+)$/);
181
+ if (heading) {
182
+ flushList();
183
+ const level = Math.min(heading[1].length, 4);
184
+ html.push(`<h${level}>${escapeHtml(heading[2].trim())}</h${level}>`);
185
+ continue;
186
+ }
187
+ const bullet = line.match(/^\s*[-*]\s+(.+)$/);
188
+ if (bullet) {
189
+ listLines.push(bullet[1]);
190
+ continue;
191
+ }
192
+ if (!line.trim()) {
193
+ flushList();
194
+ continue;
195
+ }
196
+ flushList();
197
+ html.push(`<p>${escapeHtml(line.trim())}</p>`);
198
+ }
199
+ flushCode();
200
+ flushList();
201
+ return html.join("\n");
202
+ }
203
+ export function readLocalPlanFiles(dir) {
204
+ const resolved = path.resolve(dir);
205
+ const planPath = path.join(resolved, "plan.mdx");
206
+ if (!fs.existsSync(planPath)) {
207
+ throw new Error(`Missing local plan source: ${planPath}`);
208
+ }
209
+ const readOptional = (file) => {
210
+ const abs = path.join(resolved, file);
211
+ return fs.existsSync(abs) ? fs.readFileSync(abs, "utf-8") : undefined;
212
+ };
213
+ return {
214
+ dir: resolved,
215
+ planMdx: fs.readFileSync(planPath, "utf-8"),
216
+ canvasMdx: readOptional("canvas.mdx"),
217
+ prototypeMdx: readOptional("prototype.mdx"),
218
+ stateJson: readOptional(".plan-state.json"),
219
+ };
220
+ }
221
+ export function buildLocalPlanPreviewHtml(input) {
222
+ const files = readLocalPlanFiles(input.dir);
223
+ const parsed = stripFrontmatter(files.planMdx);
224
+ const title = input.title ||
225
+ parsed.frontmatter.title ||
226
+ firstHeading(parsed.body) ||
227
+ path.basename(files.dir);
228
+ const brief = input.brief || parsed.frontmatter.brief || "";
229
+ const kind = input.kind || normalizeKind(parsed.frontmatter.kind);
230
+ const blocks = splitMdxBlocks(parsed.body);
231
+ const sourceFiles = [
232
+ ["plan.mdx", files.planMdx],
233
+ ["canvas.mdx", files.canvasMdx],
234
+ ["prototype.mdx", files.prototypeMdx],
235
+ [".plan-state.json", files.stateJson],
236
+ ].filter((entry) => Boolean(entry[1]));
237
+ return `<!doctype html>
238
+ <html lang="en">
239
+ <head>
240
+ <meta charset="utf-8" />
241
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
242
+ <title>${escapeHtml(title)}</title>
243
+ <style>
244
+ :root {
245
+ color-scheme: light dark;
246
+ --bg: #f8fafc;
247
+ --paper: #ffffff;
248
+ --ink: #0f172a;
249
+ --muted: #64748b;
250
+ --line: #cbd5e1;
251
+ --accent: #2563eb;
252
+ --soft: #eff6ff;
253
+ font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
254
+ }
255
+ @media (prefers-color-scheme: dark) {
256
+ :root {
257
+ --bg: #020617;
258
+ --paper: #0f172a;
259
+ --ink: #e2e8f0;
260
+ --muted: #94a3b8;
261
+ --line: #334155;
262
+ --soft: #172554;
263
+ }
264
+ }
265
+ body { margin: 0; background: var(--bg); color: var(--ink); }
266
+ main { max-width: 1040px; margin: 0 auto; padding: 40px 20px 56px; }
267
+ header { margin-bottom: 28px; }
268
+ h1 { font-size: clamp(2rem, 5vw, 3.5rem); line-height: 1; margin: 0 0 12px; }
269
+ h2 { font-size: 1.35rem; margin: 28px 0 10px; }
270
+ h3 { font-size: 1.05rem; margin: 0 0 8px; }
271
+ p, li { line-height: 1.65; }
272
+ .meta { display: flex; flex-wrap: wrap; gap: 8px; color: var(--muted); font-size: 0.9rem; }
273
+ .pill { border: 1px solid var(--line); border-radius: 999px; padding: 4px 10px; background: var(--paper); }
274
+ .notice { background: var(--soft); border: 1px solid var(--line); border-radius: 8px; padding: 12px 14px; margin: 18px 0; }
275
+ .block, details { background: var(--paper); border: 1px solid var(--line); border-radius: 8px; padding: 18px; margin: 14px 0; }
276
+ .component summary { cursor: pointer; color: var(--accent); font-weight: 650; }
277
+ pre { overflow: auto; border-radius: 8px; border: 1px solid var(--line); padding: 14px; background: rgba(148, 163, 184, 0.12); }
278
+ code { font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace; font-size: 0.9rem; }
279
+ .source-tabs { display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap: 14px; }
280
+ </style>
281
+ </head>
282
+ <body>
283
+ <main>
284
+ <header>
285
+ <div class="meta">
286
+ <span class="pill">${kind === "recap" ? "Visual recap" : "Visual plan"}</span>
287
+ <span class="pill">Local-files mode</span>
288
+ <span class="pill">No DB writes</span>
289
+ </div>
290
+ <h1>${escapeHtml(title)}</h1>
291
+ ${brief ? `<p>${escapeHtml(brief)}</p>` : ""}
292
+ <div class="notice">
293
+ This preview was generated entirely from local files. It does not call
294
+ the Plan MCP server, the Plan app action surface, a hosted service, or a
295
+ database. Edit the MDX files and regenerate this preview to update it.
296
+ </div>
297
+ </header>
298
+ <section>
299
+ ${blocks
300
+ .map((block) => block.type === "component"
301
+ ? `<details class="component block" open><summary>${escapeHtml(block.name || "MDX component")}</summary><pre><code>${escapeHtml(block.value)}</code></pre></details>`
302
+ : `<article class="block">${renderMarkdownish(block.value)}</article>`)
303
+ .join("\n")}
304
+ </section>
305
+ <section>
306
+ <h2>Local Source Files</h2>
307
+ <div class="source-tabs">
308
+ ${sourceFiles
309
+ .map(([name, source]) => `<details><summary>${escapeHtml(name)}</summary><pre><code>${escapeHtml(source)}</code></pre></details>`)
310
+ .join("\n")}
311
+ </div>
312
+ </section>
313
+ </main>
314
+ </body>
315
+ </html>
316
+ `;
317
+ }
318
+ export function writeLocalPlanPreview(input) {
319
+ const dir = path.resolve(input.dir);
320
+ const parsed = stripFrontmatter(readLocalPlanFiles(dir).planMdx);
321
+ const kind = input.kind || normalizeKind(parsed.frontmatter.kind);
322
+ const title = input.title ||
323
+ parsed.frontmatter.title ||
324
+ firstHeading(parsed.body) ||
325
+ path.basename(dir);
326
+ const out = path.resolve(input.out || path.join(dir, "preview.html"));
327
+ fs.mkdirSync(path.dirname(out), { recursive: true });
328
+ fs.writeFileSync(out, buildLocalPlanPreviewHtml({ ...input, dir, kind }));
329
+ const files = [
330
+ "plan.mdx",
331
+ "canvas.mdx",
332
+ "prototype.mdx",
333
+ ".plan-state.json",
334
+ ].filter((file) => fs.existsSync(path.join(dir, file)));
335
+ return {
336
+ ok: true,
337
+ dir,
338
+ out,
339
+ url: pathToFileURL(out).href,
340
+ title,
341
+ kind,
342
+ files,
343
+ };
344
+ }
345
+ function writeLocalPlanSkeleton(input) {
346
+ const dir = path.resolve(input.dir || path.join(defaultPlansDir(), localPlanFolderName(input.title)));
347
+ const planPath = path.join(dir, "plan.mdx");
348
+ if (fs.existsSync(planPath) && !input.force) {
349
+ throw new Error(`${planPath} already exists. Pass --force to replace the skeleton.`);
350
+ }
351
+ fs.mkdirSync(dir, { recursive: true });
352
+ const title = input.title;
353
+ const brief = input.brief ||
354
+ (input.kind === "recap"
355
+ ? "Local visual recap generated without Plan app database writes."
356
+ : "Local visual plan generated without Plan app database writes.");
357
+ const mdx = [
358
+ "---",
359
+ `title: "${title.replace(/"/g, '\\"')}"`,
360
+ `brief: "${brief.replace(/"/g, '\\"')}"`,
361
+ `kind: "${input.kind}"`,
362
+ "localOnly: true",
363
+ "---",
364
+ "",
365
+ `# ${title}`,
366
+ "",
367
+ brief,
368
+ "",
369
+ "## Review Surface",
370
+ "",
371
+ "Author the structured plan or recap here. You can add Agent-Native Plan MDX",
372
+ "blocks such as `<WireframeBlock />`, `<Diagram />`, `<TabsBlock />`,",
373
+ "`<FileTree />`, or `<Diff />`; the local preview will show the source",
374
+ "without publishing it to the Plan app.",
375
+ "",
376
+ ].join("\n");
377
+ fs.writeFileSync(planPath, mdx, "utf-8");
378
+ fs.writeFileSync(path.join(dir, ".plan-state.json"), JSON.stringify({
379
+ localOnly: true,
380
+ kind: input.kind,
381
+ createdAt: new Date().toISOString(),
382
+ }, null, 2) + "\n", "utf-8");
383
+ return { ok: true, dir, files: ["plan.mdx", ".plan-state.json"] };
384
+ }
385
+ function runInit(args) {
386
+ const title = optionalArg(args, "title") || "Untitled local visual plan";
387
+ const result = writeLocalPlanSkeleton({
388
+ dir: optionalArg(args, "dir"),
389
+ title,
390
+ brief: optionalArg(args, "brief"),
391
+ kind: normalizeKind(optionalArg(args, "kind")),
392
+ force: boolArg(args, "force"),
393
+ });
394
+ process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
395
+ }
396
+ function runCheck(args) {
397
+ const dir = stringArg(args, "dir");
398
+ const files = readLocalPlanFiles(dir);
399
+ const parsed = stripFrontmatter(files.planMdx);
400
+ const result = {
401
+ ok: true,
402
+ noDb: true,
403
+ dir: files.dir,
404
+ title: parsed.frontmatter.title || firstHeading(parsed.body),
405
+ kind: normalizeKind(parsed.frontmatter.kind),
406
+ files: {
407
+ "plan.mdx": Buffer.byteLength(files.planMdx),
408
+ ...(files.canvasMdx
409
+ ? { "canvas.mdx": Buffer.byteLength(files.canvasMdx) }
410
+ : {}),
411
+ ...(files.prototypeMdx
412
+ ? { "prototype.mdx": Buffer.byteLength(files.prototypeMdx) }
413
+ : {}),
414
+ ...(files.stateJson
415
+ ? { ".plan-state.json": Buffer.byteLength(files.stateJson) }
416
+ : {}),
417
+ },
418
+ };
419
+ process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
420
+ }
421
+ function runPreview(args) {
422
+ const result = writeLocalPlanPreview({
423
+ dir: stringArg(args, "dir"),
424
+ out: optionalArg(args, "out"),
425
+ title: optionalArg(args, "title"),
426
+ brief: optionalArg(args, "brief"),
427
+ kind: optionalArg(args, "kind")
428
+ ? normalizeKind(optionalArg(args, "kind"))
429
+ : undefined,
430
+ });
431
+ process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
432
+ }
433
+ const HELP = `agent-native plan — local Agent-Native Plan helpers
434
+
435
+ Usage:
436
+ agent-native plan local init --title <title> [--brief <text>] [--kind plan|recap] [--dir <folder>] [--force]
437
+ agent-native plan local check --dir <folder>
438
+ agent-native plan local preview --dir <folder> [--out preview.html] [--kind plan|recap]
439
+
440
+ The local subcommands are the privacy-focused no-DB path. They only read and
441
+ write local files: plan.mdx, optional canvas.mdx, optional prototype.mdx, and
442
+ optional .plan-state.json. They do not call the Plan MCP server, the Plan app
443
+ actions, hosted services, or SQLite.
444
+
445
+ Common flow:
446
+ agent-native plan local init --title "Checkout review" --kind plan
447
+ agent-native plan local preview --dir plans/checkout-review
448
+ `;
449
+ export async function runPlan(argv) {
450
+ const [area, sub, ...rest] = argv;
451
+ if (area !== "local") {
452
+ process.stdout.write(HELP);
453
+ return;
454
+ }
455
+ const args = parseArgs(rest);
456
+ switch (sub) {
457
+ case "init":
458
+ runInit(args);
459
+ return;
460
+ case "check":
461
+ runCheck(args);
462
+ return;
463
+ case "preview":
464
+ runPreview(args);
465
+ return;
466
+ case "help":
467
+ case "--help":
468
+ case "-h":
469
+ case undefined:
470
+ process.stdout.write(HELP);
471
+ return;
472
+ default:
473
+ process.stderr.write(`Unknown plan local subcommand: ${sub}\n${HELP}`);
474
+ process.exit(1);
475
+ }
476
+ }
477
+ //# sourceMappingURL=plan-local.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plan-local.js","sourceRoot":"","sources":["../../src/cli/plan-local.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * DB-free local plan helpers.\n *\n * These commands are intentionally separate from the Plan app actions. They do\n * not call MCP, HTTP, SQLite, or the Plan template runtime; they only read and\n * write local files so privacy-focused users have an auditable no-DB path.\n */\n\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport { pathToFileURL } from \"node:url\";\n\ntype LocalPlanKind = \"plan\" | \"recap\";\n\ntype LocalPlanFiles = {\n dir: string;\n planMdx: string;\n canvasMdx?: string;\n prototypeMdx?: string;\n stateJson?: string;\n};\n\ntype LocalPlanPreviewInput = {\n dir: string;\n kind?: LocalPlanKind;\n title?: string;\n brief?: string;\n};\n\ntype LocalPlanPreviewResult = {\n ok: true;\n dir: string;\n out: string;\n url: string;\n title: string;\n kind: LocalPlanKind;\n files: string[];\n};\n\nfunction parseArgs(argv: string[]): Record<string, string | boolean> {\n const out: Record<string, string | boolean> = {};\n for (let i = 0; i < argv.length; i += 1) {\n const token = argv[i];\n if (!token.startsWith(\"--\")) continue;\n const key = token.slice(2);\n const next = argv[i + 1];\n if (next === undefined || next.startsWith(\"--\")) out[key] = true;\n else {\n out[key] = next;\n i += 1;\n }\n }\n return out;\n}\n\nfunction stringArg(\n args: Record<string, string | boolean>,\n key: string,\n): string {\n const value = args[key];\n if (typeof value !== \"string\" || value.length === 0) {\n throw new Error(`Missing --${key}`);\n }\n return value;\n}\n\nfunction optionalArg(\n args: Record<string, string | boolean>,\n key: string,\n): string | undefined {\n const value = args[key];\n return typeof value === \"string\" && value.length > 0 ? value : undefined;\n}\n\nfunction boolArg(args: Record<string, string | boolean>, key: string): boolean {\n return args[key] === true;\n}\n\nexport function localPlanFolderName(title: string): string {\n const slug = title\n .normalize(\"NFKD\")\n .replace(/[\\u0300-\\u036f]/g, \"\")\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/^-+|-+$/g, \"\")\n .slice(0, 64)\n .replace(/-+$/g, \"\");\n return slug || \"untitled-plan\";\n}\n\nfunction normalizeKind(value: string | undefined): LocalPlanKind {\n if (!value) return \"plan\";\n if (value === \"plan\" || value === \"recap\") return value;\n throw new Error(`Invalid --kind \"${value}\" (expected plan or recap)`);\n}\n\nfunction defaultPlansDir(): string {\n return path.resolve(process.env.PLAN_LOCAL_DIR || \"plans\");\n}\n\nfunction escapeHtml(value: string): string {\n return value\n .replace(/&/g, \"&amp;\")\n .replace(/</g, \"&lt;\")\n .replace(/>/g, \"&gt;\")\n .replace(/\"/g, \"&quot;\")\n .replace(/'/g, \"&#39;\");\n}\n\nfunction stripFrontmatter(source: string): {\n body: string;\n frontmatter: Record<string, string>;\n} {\n if (!source.startsWith(\"---\\n\")) return { body: source, frontmatter: {} };\n const end = source.indexOf(\"\\n---\", 4);\n if (end < 0) return { body: source, frontmatter: {} };\n const frontmatterSource = source.slice(4, end).trim();\n const body = source.slice(end + 4).replace(/^\\r?\\n/, \"\");\n const frontmatter: Record<string, string> = {};\n for (const line of frontmatterSource.split(/\\r?\\n/)) {\n const match = line.match(/^([A-Za-z0-9_-]+):\\s*(.*)$/);\n if (!match) continue;\n const value = match[2]\n .trim()\n .replace(/^['\"]|['\"]$/g, \"\")\n .trim();\n frontmatter[match[1]] = value;\n }\n return { body, frontmatter };\n}\n\nfunction firstHeading(source: string): string | null {\n for (const line of source.split(/\\r?\\n/)) {\n const match = line.match(/^#{1,3}\\s+(.+)$/);\n if (match) return match[1].trim();\n }\n return null;\n}\n\nfunction splitMdxBlocks(source: string): Array<{\n type: \"markdown\" | \"component\";\n name?: string;\n value: string;\n}> {\n const blocks: Array<{\n type: \"markdown\" | \"component\";\n name?: string;\n value: string;\n }> = [];\n const lines = source.split(/\\r?\\n/);\n let markdown: string[] = [];\n let component: string[] | null = null;\n let componentName = \"\";\n\n function flushMarkdown() {\n const value = markdown.join(\"\\n\").trim();\n if (value) blocks.push({ type: \"markdown\", value });\n markdown = [];\n }\n\n function flushComponent() {\n if (!component) return;\n blocks.push({\n type: \"component\",\n name: componentName || \"MDX component\",\n value: component.join(\"\\n\").trim(),\n });\n component = null;\n componentName = \"\";\n }\n\n for (const line of lines) {\n const start = line.match(/^<([A-Z][A-Za-z0-9_]*)\\b/);\n if (!component && start) {\n flushMarkdown();\n component = [line];\n componentName = start[1];\n if (/\\/>\\s*$/.test(line)) flushComponent();\n continue;\n }\n\n if (component) {\n component.push(line);\n if (new RegExp(`</${componentName}>\\\\s*$`).test(line)) {\n flushComponent();\n }\n continue;\n }\n\n markdown.push(line);\n }\n\n flushComponent();\n flushMarkdown();\n return blocks;\n}\n\nfunction renderMarkdownish(source: string): string {\n const lines = source.split(/\\r?\\n/);\n const html: string[] = [];\n let inCode = false;\n let codeLines: string[] = [];\n let listLines: string[] = [];\n\n function flushList() {\n if (listLines.length === 0) return;\n html.push(\n `<ul>${listLines.map((line) => `<li>${escapeHtml(line)}</li>`).join(\"\")}</ul>`,\n );\n listLines = [];\n }\n\n function flushCode() {\n if (!inCode) return;\n html.push(`<pre><code>${escapeHtml(codeLines.join(\"\\n\"))}</code></pre>`);\n codeLines = [];\n inCode = false;\n }\n\n for (const line of lines) {\n if (line.startsWith(\"```\")) {\n if (inCode) flushCode();\n else {\n flushList();\n inCode = true;\n codeLines = [];\n }\n continue;\n }\n if (inCode) {\n codeLines.push(line);\n continue;\n }\n\n const heading = line.match(/^(#{1,4})\\s+(.+)$/);\n if (heading) {\n flushList();\n const level = Math.min(heading[1].length, 4);\n html.push(`<h${level}>${escapeHtml(heading[2].trim())}</h${level}>`);\n continue;\n }\n\n const bullet = line.match(/^\\s*[-*]\\s+(.+)$/);\n if (bullet) {\n listLines.push(bullet[1]);\n continue;\n }\n\n if (!line.trim()) {\n flushList();\n continue;\n }\n\n flushList();\n html.push(`<p>${escapeHtml(line.trim())}</p>`);\n }\n\n flushCode();\n flushList();\n return html.join(\"\\n\");\n}\n\nexport function readLocalPlanFiles(dir: string): LocalPlanFiles {\n const resolved = path.resolve(dir);\n const planPath = path.join(resolved, \"plan.mdx\");\n if (!fs.existsSync(planPath)) {\n throw new Error(`Missing local plan source: ${planPath}`);\n }\n const readOptional = (file: string) => {\n const abs = path.join(resolved, file);\n return fs.existsSync(abs) ? fs.readFileSync(abs, \"utf-8\") : undefined;\n };\n return {\n dir: resolved,\n planMdx: fs.readFileSync(planPath, \"utf-8\"),\n canvasMdx: readOptional(\"canvas.mdx\"),\n prototypeMdx: readOptional(\"prototype.mdx\"),\n stateJson: readOptional(\".plan-state.json\"),\n };\n}\n\nexport function buildLocalPlanPreviewHtml(\n input: LocalPlanPreviewInput,\n): string {\n const files = readLocalPlanFiles(input.dir);\n const parsed = stripFrontmatter(files.planMdx);\n const title =\n input.title ||\n parsed.frontmatter.title ||\n firstHeading(parsed.body) ||\n path.basename(files.dir);\n const brief = input.brief || parsed.frontmatter.brief || \"\";\n const kind = input.kind || normalizeKind(parsed.frontmatter.kind);\n const blocks = splitMdxBlocks(parsed.body);\n const sourceFiles = [\n [\"plan.mdx\", files.planMdx],\n [\"canvas.mdx\", files.canvasMdx],\n [\"prototype.mdx\", files.prototypeMdx],\n [\".plan-state.json\", files.stateJson],\n ].filter((entry): entry is [string, string] => Boolean(entry[1]));\n\n return `<!doctype html>\n<html lang=\"en\">\n<head>\n <meta charset=\"utf-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n <title>${escapeHtml(title)}</title>\n <style>\n :root {\n color-scheme: light dark;\n --bg: #f8fafc;\n --paper: #ffffff;\n --ink: #0f172a;\n --muted: #64748b;\n --line: #cbd5e1;\n --accent: #2563eb;\n --soft: #eff6ff;\n font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif;\n }\n @media (prefers-color-scheme: dark) {\n :root {\n --bg: #020617;\n --paper: #0f172a;\n --ink: #e2e8f0;\n --muted: #94a3b8;\n --line: #334155;\n --soft: #172554;\n }\n }\n body { margin: 0; background: var(--bg); color: var(--ink); }\n main { max-width: 1040px; margin: 0 auto; padding: 40px 20px 56px; }\n header { margin-bottom: 28px; }\n h1 { font-size: clamp(2rem, 5vw, 3.5rem); line-height: 1; margin: 0 0 12px; }\n h2 { font-size: 1.35rem; margin: 28px 0 10px; }\n h3 { font-size: 1.05rem; margin: 0 0 8px; }\n p, li { line-height: 1.65; }\n .meta { display: flex; flex-wrap: wrap; gap: 8px; color: var(--muted); font-size: 0.9rem; }\n .pill { border: 1px solid var(--line); border-radius: 999px; padding: 4px 10px; background: var(--paper); }\n .notice { background: var(--soft); border: 1px solid var(--line); border-radius: 8px; padding: 12px 14px; margin: 18px 0; }\n .block, details { background: var(--paper); border: 1px solid var(--line); border-radius: 8px; padding: 18px; margin: 14px 0; }\n .component summary { cursor: pointer; color: var(--accent); font-weight: 650; }\n pre { overflow: auto; border-radius: 8px; border: 1px solid var(--line); padding: 14px; background: rgba(148, 163, 184, 0.12); }\n code { font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace; font-size: 0.9rem; }\n .source-tabs { display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap: 14px; }\n </style>\n</head>\n<body>\n <main>\n <header>\n <div class=\"meta\">\n <span class=\"pill\">${kind === \"recap\" ? \"Visual recap\" : \"Visual plan\"}</span>\n <span class=\"pill\">Local-files mode</span>\n <span class=\"pill\">No DB writes</span>\n </div>\n <h1>${escapeHtml(title)}</h1>\n ${brief ? `<p>${escapeHtml(brief)}</p>` : \"\"}\n <div class=\"notice\">\n This preview was generated entirely from local files. It does not call\n the Plan MCP server, the Plan app action surface, a hosted service, or a\n database. Edit the MDX files and regenerate this preview to update it.\n </div>\n </header>\n <section>\n ${blocks\n .map((block) =>\n block.type === \"component\"\n ? `<details class=\"component block\" open><summary>${escapeHtml(\n block.name || \"MDX component\",\n )}</summary><pre><code>${escapeHtml(block.value)}</code></pre></details>`\n : `<article class=\"block\">${renderMarkdownish(block.value)}</article>`,\n )\n .join(\"\\n\")}\n </section>\n <section>\n <h2>Local Source Files</h2>\n <div class=\"source-tabs\">\n ${sourceFiles\n .map(\n ([name, source]) =>\n `<details><summary>${escapeHtml(name)}</summary><pre><code>${escapeHtml(\n source,\n )}</code></pre></details>`,\n )\n .join(\"\\n\")}\n </div>\n </section>\n </main>\n</body>\n</html>\n`;\n}\n\nexport function writeLocalPlanPreview(input: {\n dir: string;\n out?: string;\n kind?: LocalPlanKind;\n title?: string;\n brief?: string;\n}): LocalPlanPreviewResult {\n const dir = path.resolve(input.dir);\n const parsed = stripFrontmatter(readLocalPlanFiles(dir).planMdx);\n const kind = input.kind || normalizeKind(parsed.frontmatter.kind);\n const title =\n input.title ||\n parsed.frontmatter.title ||\n firstHeading(parsed.body) ||\n path.basename(dir);\n const out = path.resolve(input.out || path.join(dir, \"preview.html\"));\n fs.mkdirSync(path.dirname(out), { recursive: true });\n fs.writeFileSync(out, buildLocalPlanPreviewHtml({ ...input, dir, kind }));\n const files = [\n \"plan.mdx\",\n \"canvas.mdx\",\n \"prototype.mdx\",\n \".plan-state.json\",\n ].filter((file) => fs.existsSync(path.join(dir, file)));\n return {\n ok: true,\n dir,\n out,\n url: pathToFileURL(out).href,\n title,\n kind,\n files,\n };\n}\n\nfunction writeLocalPlanSkeleton(input: {\n dir?: string;\n title: string;\n brief?: string;\n kind: LocalPlanKind;\n force?: boolean;\n}): { ok: true; dir: string; files: string[] } {\n const dir = path.resolve(\n input.dir || path.join(defaultPlansDir(), localPlanFolderName(input.title)),\n );\n const planPath = path.join(dir, \"plan.mdx\");\n if (fs.existsSync(planPath) && !input.force) {\n throw new Error(\n `${planPath} already exists. Pass --force to replace the skeleton.`,\n );\n }\n fs.mkdirSync(dir, { recursive: true });\n const title = input.title;\n const brief =\n input.brief ||\n (input.kind === \"recap\"\n ? \"Local visual recap generated without Plan app database writes.\"\n : \"Local visual plan generated without Plan app database writes.\");\n const mdx = [\n \"---\",\n `title: \"${title.replace(/\"/g, '\\\\\"')}\"`,\n `brief: \"${brief.replace(/\"/g, '\\\\\"')}\"`,\n `kind: \"${input.kind}\"`,\n \"localOnly: true\",\n \"---\",\n \"\",\n `# ${title}`,\n \"\",\n brief,\n \"\",\n \"## Review Surface\",\n \"\",\n \"Author the structured plan or recap here. You can add Agent-Native Plan MDX\",\n \"blocks such as `<WireframeBlock />`, `<Diagram />`, `<TabsBlock />`,\",\n \"`<FileTree />`, or `<Diff />`; the local preview will show the source\",\n \"without publishing it to the Plan app.\",\n \"\",\n ].join(\"\\n\");\n fs.writeFileSync(planPath, mdx, \"utf-8\");\n fs.writeFileSync(\n path.join(dir, \".plan-state.json\"),\n JSON.stringify(\n {\n localOnly: true,\n kind: input.kind,\n createdAt: new Date().toISOString(),\n },\n null,\n 2,\n ) + \"\\n\",\n \"utf-8\",\n );\n return { ok: true, dir, files: [\"plan.mdx\", \".plan-state.json\"] };\n}\n\nfunction runInit(args: Record<string, string | boolean>): void {\n const title = optionalArg(args, \"title\") || \"Untitled local visual plan\";\n const result = writeLocalPlanSkeleton({\n dir: optionalArg(args, \"dir\"),\n title,\n brief: optionalArg(args, \"brief\"),\n kind: normalizeKind(optionalArg(args, \"kind\")),\n force: boolArg(args, \"force\"),\n });\n process.stdout.write(`${JSON.stringify(result, null, 2)}\\n`);\n}\n\nfunction runCheck(args: Record<string, string | boolean>): void {\n const dir = stringArg(args, \"dir\");\n const files = readLocalPlanFiles(dir);\n const parsed = stripFrontmatter(files.planMdx);\n const result = {\n ok: true,\n noDb: true,\n dir: files.dir,\n title: parsed.frontmatter.title || firstHeading(parsed.body),\n kind: normalizeKind(parsed.frontmatter.kind),\n files: {\n \"plan.mdx\": Buffer.byteLength(files.planMdx),\n ...(files.canvasMdx\n ? { \"canvas.mdx\": Buffer.byteLength(files.canvasMdx) }\n : {}),\n ...(files.prototypeMdx\n ? { \"prototype.mdx\": Buffer.byteLength(files.prototypeMdx) }\n : {}),\n ...(files.stateJson\n ? { \".plan-state.json\": Buffer.byteLength(files.stateJson) }\n : {}),\n },\n };\n process.stdout.write(`${JSON.stringify(result, null, 2)}\\n`);\n}\n\nfunction runPreview(args: Record<string, string | boolean>): void {\n const result = writeLocalPlanPreview({\n dir: stringArg(args, \"dir\"),\n out: optionalArg(args, \"out\"),\n title: optionalArg(args, \"title\"),\n brief: optionalArg(args, \"brief\"),\n kind: optionalArg(args, \"kind\")\n ? normalizeKind(optionalArg(args, \"kind\"))\n : undefined,\n });\n process.stdout.write(`${JSON.stringify(result, null, 2)}\\n`);\n}\n\nconst HELP = `agent-native plan — local Agent-Native Plan helpers\n\nUsage:\n agent-native plan local init --title <title> [--brief <text>] [--kind plan|recap] [--dir <folder>] [--force]\n agent-native plan local check --dir <folder>\n agent-native plan local preview --dir <folder> [--out preview.html] [--kind plan|recap]\n\nThe local subcommands are the privacy-focused no-DB path. They only read and\nwrite local files: plan.mdx, optional canvas.mdx, optional prototype.mdx, and\noptional .plan-state.json. They do not call the Plan MCP server, the Plan app\nactions, hosted services, or SQLite.\n\nCommon flow:\n agent-native plan local init --title \"Checkout review\" --kind plan\n agent-native plan local preview --dir plans/checkout-review\n`;\n\nexport async function runPlan(argv: string[]): Promise<void> {\n const [area, sub, ...rest] = argv;\n if (area !== \"local\") {\n process.stdout.write(HELP);\n return;\n }\n const args = parseArgs(rest);\n switch (sub) {\n case \"init\":\n runInit(args);\n return;\n case \"check\":\n runCheck(args);\n return;\n case \"preview\":\n runPreview(args);\n return;\n case \"help\":\n case \"--help\":\n case \"-h\":\n case undefined:\n process.stdout.write(HELP);\n return;\n default:\n process.stderr.write(`Unknown plan local subcommand: ${sub}\\n${HELP}`);\n process.exit(1);\n }\n}\n"]}