@lsctech/polaris 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (316) hide show
  1. package/README.md +87 -0
  2. package/dist/__mocks__/tool-server-linear.js +34 -0
  3. package/dist/cli/adopt-approve.js +60 -0
  4. package/dist/cli/adopt-cognition.js +199 -0
  5. package/dist/cli/adopt-instructions.js +143 -0
  6. package/dist/cli/adopt-rules.js +169 -0
  7. package/dist/cli/adopt-scan.js +25 -0
  8. package/dist/cli/adopt-smartdocs.js +192 -0
  9. package/dist/cli/adoption-inventory.js +350 -0
  10. package/dist/cli/adoption-plan.js +222 -0
  11. package/dist/cli/args.js +28 -0
  12. package/dist/cli/args.test.js +30 -0
  13. package/dist/cli/branding.js +41 -0
  14. package/dist/cli/graph.js +358 -0
  15. package/dist/cli/index.js +127 -0
  16. package/dist/cli/index.test.js +162 -0
  17. package/dist/cli/init-detect.js +61 -0
  18. package/dist/cli/init.js +492 -0
  19. package/dist/cli/init.test.js +259 -0
  20. package/dist/cli/librarian.js +32 -0
  21. package/dist/cli/medic.js +96 -0
  22. package/dist/cli/startup.test.js +23 -0
  23. package/dist/cli/tracker.js +130 -0
  24. package/dist/cli/tracker.test.js +126 -0
  25. package/dist/cli/version.js +10 -0
  26. package/dist/cli/version.test.js +11 -0
  27. package/dist/cli/worker.js +287 -0
  28. package/dist/cluster-state/fixtures/test-clusters.json +19 -0
  29. package/dist/cluster-state/store.js +302 -0
  30. package/dist/cluster-state/store.test.js +145 -0
  31. package/dist/cluster-state/types.js +2 -0
  32. package/dist/cognition/archive.js +158 -0
  33. package/dist/cognition/closeout-librarian-types.js +161 -0
  34. package/dist/cognition/cognition.test.js +623 -0
  35. package/dist/cognition/index.js +41 -0
  36. package/dist/cognition/librarian-dispatch.js +391 -0
  37. package/dist/cognition/librarian-dispatch.test.js +247 -0
  38. package/dist/cognition/librarian-packet.js +313 -0
  39. package/dist/cognition/librarian-types.js +57 -0
  40. package/dist/cognition/route-cognition-delta.js +350 -0
  41. package/dist/cognition/summary-delta.js +279 -0
  42. package/dist/cognition/validate.js +162 -0
  43. package/dist/config/defaults.js +98 -0
  44. package/dist/config/doctor.js +273 -0
  45. package/dist/config/index.js +14 -0
  46. package/dist/config/loader.js +58 -0
  47. package/dist/config/provider-detect.js +60 -0
  48. package/dist/config/provider-detect.test.js +119 -0
  49. package/dist/config/schema.js +2 -0
  50. package/dist/config/show.js +44 -0
  51. package/dist/config/show.test.js +42 -0
  52. package/dist/config/validator.js +675 -0
  53. package/dist/config/validator.test.js +77 -0
  54. package/dist/docs/canon-check.js +334 -0
  55. package/dist/docs/canon-check.test.js +207 -0
  56. package/dist/docs/doctrine.js +133 -0
  57. package/dist/docs/doctrine.test.js +139 -0
  58. package/dist/docs/index.js +168 -0
  59. package/dist/docs/index.test.js +83 -0
  60. package/dist/docs/ingest.js +295 -0
  61. package/dist/docs/ingest.test.js +71 -0
  62. package/dist/docs/migrate.js +165 -0
  63. package/dist/docs/migrate.test.js +136 -0
  64. package/dist/docs/seed-instructions.js +147 -0
  65. package/dist/docs/seed-instructions.test.js +137 -0
  66. package/dist/docs/validate-instructions.js +267 -0
  67. package/dist/docs/validate-instructions.test.js +238 -0
  68. package/dist/finalize/artifact-policy.js +214 -0
  69. package/dist/finalize/artifact-policy.test.js +84 -0
  70. package/dist/finalize/delivery-integrity.js +141 -0
  71. package/dist/finalize/finalize.test.js +210 -0
  72. package/dist/finalize/github.js +41 -0
  73. package/dist/finalize/index.js +474 -0
  74. package/dist/finalize/linear.js +213 -0
  75. package/dist/finalize/run-report.js +47 -0
  76. package/dist/finalize/steps/01-map-update.js +11 -0
  77. package/dist/finalize/steps/02-map-validate.js +11 -0
  78. package/dist/finalize/steps/03-schema-validate.js +11 -0
  79. package/dist/finalize/steps/04-run-checks.js +118 -0
  80. package/dist/finalize/steps/05-generate-report.js +14 -0
  81. package/dist/finalize/steps/06-commit.js +39 -0
  82. package/dist/finalize/steps/07-push.js +10 -0
  83. package/dist/finalize/steps/08-create-pr.js +9 -0
  84. package/dist/finalize/steps/09-update-state.js +9 -0
  85. package/dist/finalize/steps/10-append-jsonl.js +23 -0
  86. package/dist/finalize/steps/11-update-linear.js +22 -0
  87. package/dist/finalize/steps/12-archive.js +21 -0
  88. package/dist/graph/adapter/c/adapter.js +45 -0
  89. package/dist/graph/adapter/c/extract.js +135 -0
  90. package/dist/graph/adapter/c/index.js +10 -0
  91. package/dist/graph/adapter/c/runtime.js +67 -0
  92. package/dist/graph/adapter/cpp/adapter.js +45 -0
  93. package/dist/graph/adapter/cpp/extract.js +159 -0
  94. package/dist/graph/adapter/cpp/index.js +10 -0
  95. package/dist/graph/adapter/cpp/runtime.js +61 -0
  96. package/dist/graph/adapter/csharp/adapter.js +45 -0
  97. package/dist/graph/adapter/csharp/extract.js +127 -0
  98. package/dist/graph/adapter/csharp/index.js +10 -0
  99. package/dist/graph/adapter/csharp/runtime.js +61 -0
  100. package/dist/graph/adapter/dart/adapter.js +56 -0
  101. package/dist/graph/adapter/dart/extract.js +130 -0
  102. package/dist/graph/adapter/dart/index.js +10 -0
  103. package/dist/graph/adapter/dart/runtime.js +61 -0
  104. package/dist/graph/adapter/go/adapter.js +45 -0
  105. package/dist/graph/adapter/go/extract.js +112 -0
  106. package/dist/graph/adapter/go/index.js +10 -0
  107. package/dist/graph/adapter/go/runtime.js +58 -0
  108. package/dist/graph/adapter/kotlin-java/extract-common.js +83 -0
  109. package/dist/graph/adapter/kotlin-java/index.js +15 -0
  110. package/dist/graph/adapter/kotlin-java/java-adapter.js +45 -0
  111. package/dist/graph/adapter/kotlin-java/java-extract.js +42 -0
  112. package/dist/graph/adapter/kotlin-java/kotlin-adapter.js +48 -0
  113. package/dist/graph/adapter/kotlin-java/kotlin-extract.js +58 -0
  114. package/dist/graph/adapter/kotlin-java/runtime.js +75 -0
  115. package/dist/graph/adapter/python/adapter.js +45 -0
  116. package/dist/graph/adapter/python/extract.js +121 -0
  117. package/dist/graph/adapter/python/index.js +10 -0
  118. package/dist/graph/adapter/python/runtime.js +70 -0
  119. package/dist/graph/adapter/registry.js +82 -0
  120. package/dist/graph/adapter/rust/adapter.js +45 -0
  121. package/dist/graph/adapter/rust/extract.js +110 -0
  122. package/dist/graph/adapter/rust/index.js +10 -0
  123. package/dist/graph/adapter/rust/runtime.js +74 -0
  124. package/dist/graph/adapter/shell/adapter.js +34 -0
  125. package/dist/graph/adapter/shell/extract.js +98 -0
  126. package/dist/graph/adapter/shell/index.js +8 -0
  127. package/dist/graph/adapter/svelte/adapter.js +91 -0
  128. package/dist/graph/adapter/svelte/extract.js +41 -0
  129. package/dist/graph/adapter/svelte/index.js +8 -0
  130. package/dist/graph/adapter/swift/adapter.js +45 -0
  131. package/dist/graph/adapter/swift/extract.js +120 -0
  132. package/dist/graph/adapter/swift/index.js +10 -0
  133. package/dist/graph/adapter/swift/runtime.js +70 -0
  134. package/dist/graph/adapter/types.js +2 -0
  135. package/dist/graph/adapter/typescript-javascript/adapter.js +72 -0
  136. package/dist/graph/adapter/typescript-javascript/extract.js +115 -0
  137. package/dist/graph/adapter/typescript-javascript/index.js +10 -0
  138. package/dist/graph/adapter/typescript-javascript/runtime.js +97 -0
  139. package/dist/graph/capability/index.js +105 -0
  140. package/dist/graph/governance.js +104 -0
  141. package/dist/graph/parser/extract.js +5 -0
  142. package/dist/graph/parser/loader.js +2 -0
  143. package/dist/graph/parser/pipeline.js +194 -0
  144. package/dist/graph/query/index.js +277 -0
  145. package/dist/graph/query/types.js +5 -0
  146. package/dist/graph/resolver/build-edges.js +211 -0
  147. package/dist/graph/resolver/index.js +23 -0
  148. package/dist/graph/resolver/resolve-imports.js +168 -0
  149. package/dist/graph/store/adapter.js +132 -0
  150. package/dist/graph/store/queries.js +124 -0
  151. package/dist/graph/store/types.js +4 -0
  152. package/dist/ignore/defaults.js +41 -0
  153. package/dist/ignore/parser.js +39 -0
  154. package/dist/lint/map-reference-validator.js +72 -0
  155. package/dist/loop/abort.js +188 -0
  156. package/dist/loop/abort.test.js +151 -0
  157. package/dist/loop/adapters/__tests__/terminal-cli.test.js +92 -0
  158. package/dist/loop/adapters/agent-subtask.js +137 -0
  159. package/dist/loop/adapters/agent-subtask.test.js +151 -0
  160. package/dist/loop/adapters/cli-subtask-bridge.js +108 -0
  161. package/dist/loop/adapters/index.js +9 -0
  162. package/dist/loop/adapters/registry.js +17 -0
  163. package/dist/loop/adapters/terminal-cli.js +403 -0
  164. package/dist/loop/adapters/types.js +2 -0
  165. package/dist/loop/adapters/worker-instructions.js +85 -0
  166. package/dist/loop/body-parser.js +226 -0
  167. package/dist/loop/bootstrap-packet.js +97 -0
  168. package/dist/loop/bootstrap-packet.test.js +171 -0
  169. package/dist/loop/branch-governance.js +54 -0
  170. package/dist/loop/budget.js +63 -0
  171. package/dist/loop/budget.test.js +163 -0
  172. package/dist/loop/checkpoint.js +325 -0
  173. package/dist/loop/compact-return.js +59 -0
  174. package/dist/loop/continue.js +645 -0
  175. package/dist/loop/continue.test.js +337 -0
  176. package/dist/loop/dispatch-boundary.js +335 -0
  177. package/dist/loop/dispatch-state.js +223 -0
  178. package/dist/loop/dispatch-state.test.js +186 -0
  179. package/dist/loop/dispatch.js +1236 -0
  180. package/dist/loop/dispatch.test.js +499 -0
  181. package/dist/loop/ephemeral-smoke.test.js +160 -0
  182. package/dist/loop/evidence-backfill.js +216 -0
  183. package/dist/loop/execution-adapter.js +141 -0
  184. package/dist/loop/execution-adapter.test.js +66 -0
  185. package/dist/loop/finalize-evidence.js +169 -0
  186. package/dist/loop/git-custody.js +250 -0
  187. package/dist/loop/impl-packet-input.js +38 -0
  188. package/dist/loop/index.js +270 -0
  189. package/dist/loop/ledger.js +49 -0
  190. package/dist/loop/ledger.test.js +127 -0
  191. package/dist/loop/lifecycle-dispatch.js +348 -0
  192. package/dist/loop/lifecycle-dispatch.test.js +318 -0
  193. package/dist/loop/lifecycle.js +93 -0
  194. package/dist/loop/lifecycle.test.js +150 -0
  195. package/dist/loop/orphan-recovery.js +276 -0
  196. package/dist/loop/orphan-recovery.test.js +236 -0
  197. package/dist/loop/parent.js +1566 -0
  198. package/dist/loop/parent.test.js +738 -0
  199. package/dist/loop/provider-sealed-packet.test.js +164 -0
  200. package/dist/loop/resume.js +295 -0
  201. package/dist/loop/resume.test.js +172 -0
  202. package/dist/loop/run-bootstrap.js +234 -0
  203. package/dist/loop/run-preflight.js +203 -0
  204. package/dist/loop/status.js +665 -0
  205. package/dist/loop/status.test.js +235 -0
  206. package/dist/loop/wait.js +86 -0
  207. package/dist/loop/worker-packet.js +406 -0
  208. package/dist/loop/worker-packet.test.js +243 -0
  209. package/dist/loop/worker-prompt.js +260 -0
  210. package/dist/loop/worker-prompt.test.js +247 -0
  211. package/dist/loop/worker.js +498 -0
  212. package/dist/loop/worker.test.js +372 -0
  213. package/dist/map/atlas.js +77 -0
  214. package/dist/map/backfill.js +171 -0
  215. package/dist/map/backfill.test.js +120 -0
  216. package/dist/map/index.js +255 -0
  217. package/dist/map/inference.js +73 -0
  218. package/dist/map/inference.test.js +35 -0
  219. package/dist/map/query.js +157 -0
  220. package/dist/map/query.test.js +174 -0
  221. package/dist/map/update.js +355 -0
  222. package/dist/map/update.test.js +67 -0
  223. package/dist/map/validate.js +153 -0
  224. package/dist/map/validate.test.js +90 -0
  225. package/dist/map/welfare.js +86 -0
  226. package/dist/mcp/lib/invoke.js +35 -0
  227. package/dist/mcp/lib/redact.js +24 -0
  228. package/dist/mcp/lib/redact.test.js +112 -0
  229. package/dist/mcp/lib/root.js +41 -0
  230. package/dist/mcp/lib/root.test.js +114 -0
  231. package/dist/mcp/server.js +15 -0
  232. package/dist/mcp/tools/claim-child.js +119 -0
  233. package/dist/mcp/tools/claim-child.test.js +69 -0
  234. package/dist/mcp/tools/current-state.js +73 -0
  235. package/dist/mcp/tools/current-state.test.js +114 -0
  236. package/dist/mcp/tools/dispatch-result.js +186 -0
  237. package/dist/mcp/tools/dispatch-result.test.js +109 -0
  238. package/dist/mcp/tools/index.js +222 -0
  239. package/dist/mcp/tools/index.test.js +172 -0
  240. package/dist/mcp/tools/linear.js +74 -0
  241. package/dist/mcp/tools/loop-continue.js +159 -0
  242. package/dist/mcp/tools/loop-continue.test.js +186 -0
  243. package/dist/mcp/tools/loop-dry-run.js +15 -0
  244. package/dist/mcp/tools/status.js +65 -0
  245. package/dist/medic/chart-id.js +75 -0
  246. package/dist/medic/chart-schema.js +139 -0
  247. package/dist/runs/index.js +119 -0
  248. package/dist/runs/index.test.js +184 -0
  249. package/dist/runs/ledger-tail.js +15 -0
  250. package/dist/runs/list.js +29 -0
  251. package/dist/runs/reconcile.js +74 -0
  252. package/dist/runs/show.js +10 -0
  253. package/dist/runtime/audit/logger.js +15 -0
  254. package/dist/runtime/audit/reader.js +42 -0
  255. package/dist/runtime/checkpoint.js +89 -0
  256. package/dist/runtime/continuation/confirmed.js +225 -0
  257. package/dist/runtime/continuation/confirmed.test.js +421 -0
  258. package/dist/runtime/continuation/dry-run.js +74 -0
  259. package/dist/runtime/execution-window.js +106 -0
  260. package/dist/runtime/scheduling/child-selector.js +16 -0
  261. package/dist/runtime/state.js +43 -0
  262. package/dist/runtime/status.js +46 -0
  263. package/dist/runtime/verification/envelope.js +80 -0
  264. package/dist/runtime/verification/fingerprint.js +26 -0
  265. package/dist/skill-packet/cli.test.js +105 -0
  266. package/dist/skill-packet/generator.js +225 -0
  267. package/dist/skill-packet/generator.test.js +159 -0
  268. package/dist/skill-packet/index.js +49 -0
  269. package/dist/skill-packet/types.js +2 -0
  270. package/dist/smartdocs-engine/audit.js +194 -0
  271. package/dist/smartdocs-engine/audit.test.js +122 -0
  272. package/dist/smartdocs-engine/canon-check.js +340 -0
  273. package/dist/smartdocs-engine/canon-check.test.js +234 -0
  274. package/dist/smartdocs-engine/doctrine.js +633 -0
  275. package/dist/smartdocs-engine/doctrine.test.js +374 -0
  276. package/dist/smartdocs-engine/index.js +456 -0
  277. package/dist/smartdocs-engine/index.test.js +82 -0
  278. package/dist/smartdocs-engine/ingest.js +495 -0
  279. package/dist/smartdocs-engine/ingest.test.js +163 -0
  280. package/dist/smartdocs-engine/migrate.js +217 -0
  281. package/dist/smartdocs-engine/migrate.test.js +200 -0
  282. package/dist/smartdocs-engine/seed-instructions.js +335 -0
  283. package/dist/smartdocs-engine/seed-instructions.test.js +275 -0
  284. package/dist/smartdocs-engine/smartdoc-ignore.js +258 -0
  285. package/dist/smartdocs-engine/smartdoc-ignore.test.js +146 -0
  286. package/dist/smartdocs-engine/validate-instructions.js +366 -0
  287. package/dist/smartdocs-engine/validate-instructions.test.js +283 -0
  288. package/dist/tracker/adapters/github/index.js +305 -0
  289. package/dist/tracker/adapters/jira/index.js +293 -0
  290. package/dist/tracker/adapters/linear/http-errors.test.js +51 -0
  291. package/dist/tracker/adapters/linear/index.js +621 -0
  292. package/dist/tracker/adapters/linear/index.test.js +238 -0
  293. package/dist/tracker/adapters/linear.js +50 -0
  294. package/dist/tracker/adapters/local/index.js +10 -0
  295. package/dist/tracker/adapters/local-file/index.js +159 -0
  296. package/dist/tracker/adapters/mcp-bridge.js +158 -0
  297. package/dist/tracker/adapters/mcp-bridge.test.js +12 -0
  298. package/dist/tracker/adapters/spec/index.js +146 -0
  299. package/dist/tracker/capabilities.js +2 -0
  300. package/dist/tracker/index.js +49 -0
  301. package/dist/tracker/lifecycle-policy.js +139 -0
  302. package/dist/tracker/lifecycle-transition.js +164 -0
  303. package/dist/tracker/local-graph.js +98 -0
  304. package/dist/tracker/migration.js +56 -0
  305. package/dist/tracker/schema.js +86 -0
  306. package/dist/tracker/sync/index.js +404 -0
  307. package/dist/tracker/sync/queue-store.js +95 -0
  308. package/dist/tracker/sync/queue-store.test.js +104 -0
  309. package/dist/tracker/types.js +11 -0
  310. package/dist/types/linear.js +3 -0
  311. package/dist/types/result-packet.js +12 -0
  312. package/dist/types/runtime-state.js +2 -0
  313. package/dist/types/work-contract.js +2 -0
  314. package/dist/utils/monotonic-timestamp.js +13 -0
  315. package/dist/utils/monotonic-timestamp.test.js +19 -0
  316. package/package.json +64 -0
@@ -0,0 +1,147 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DRAFT_MARKER = void 0;
4
+ exports.hasDraftMarker = hasDraftMarker;
5
+ exports.generateDraft = generateDraft;
6
+ exports.seedInstructions = seedInstructions;
7
+ exports.seedInstructionsAll = seedInstructionsAll;
8
+ const node_fs_1 = require("node:fs");
9
+ const node_path_1 = require("node:path");
10
+ const loader_js_1 = require("../config/loader.js");
11
+ const atlas_js_1 = require("../map/atlas.js");
12
+ exports.DRAFT_MARKER = "<!-- polaris:draft -->";
13
+ function hasDraftMarker(filePath) {
14
+ try {
15
+ const content = (0, node_fs_1.readFileSync)(filePath, "utf-8");
16
+ return content.includes(exports.DRAFT_MARKER);
17
+ }
18
+ catch {
19
+ return false;
20
+ }
21
+ }
22
+ function collectDirs(dir, root) {
23
+ const dirs = [];
24
+ try {
25
+ for (const entry of (0, node_fs_1.readdirSync)(dir, { withFileTypes: true })) {
26
+ if (entry.isDirectory() && !entry.name.startsWith(".") && entry.name !== "node_modules" && entry.name !== "dist") {
27
+ const full = (0, node_path_1.join)(dir, entry.name);
28
+ const rel = (0, node_path_1.relative)(root, full).replace(/\\/g, "/");
29
+ dirs.push(rel);
30
+ dirs.push(...collectDirs(full, root));
31
+ }
32
+ }
33
+ }
34
+ catch {
35
+ // ignore unreadable dirs
36
+ }
37
+ return dirs;
38
+ }
39
+ function generateDraft(targetDir, repoRoot, allRoutes) {
40
+ const filesInDir = Object.entries(allRoutes).filter(([filePath]) => {
41
+ const fileDir = (0, node_path_1.dirname)(filePath);
42
+ const normDir = targetDir.replace(/\\/g, "/");
43
+ return fileDir === normDir || (normDir === "." && !filePath.includes("/"));
44
+ });
45
+ const domains = [...new Set(filesInDir.map(([, e]) => e.domain))].filter(Boolean);
46
+ const routes = [...new Set(filesInDir.map(([, e]) => e.route))].filter(Boolean);
47
+ const taskchains = [...new Set(filesInDir.map(([, e]) => e.taskchain))].filter(Boolean);
48
+ // Nearby doc references: .md files in atlas whose route overlaps with this dir's routes
49
+ const nearbyDocs = Object.entries(allRoutes)
50
+ .filter(([p]) => p.endsWith(".md") || p.startsWith("docs/"))
51
+ .filter(([, e]) => routes.some((r) => e.route === r || e.domain === domains[0]))
52
+ .map(([p]) => p)
53
+ .slice(0, 5);
54
+ const dirLabel = (0, node_path_1.basename)(targetDir) || (0, node_path_1.basename)(repoRoot);
55
+ const lines = [
56
+ exports.DRAFT_MARKER,
57
+ `# ${dirLabel}`,
58
+ "",
59
+ "> Polaris draft — review and remove the `<!-- polaris:draft -->` marker to promote.",
60
+ "",
61
+ "## Purpose",
62
+ "",
63
+ "<!-- Replace with a description of what this directory does and its key responsibilities. -->",
64
+ "",
65
+ ];
66
+ if (domains.length > 0)
67
+ lines.push(`**Domain:** ${domains.join(", ")}`);
68
+ if (routes.length > 0)
69
+ lines.push(`**Route:** ${routes.join(", ")}`);
70
+ if (taskchains.length > 0)
71
+ lines.push(`**Taskchain:** ${taskchains.join(", ")}`);
72
+ if (domains.length + routes.length + taskchains.length > 0)
73
+ lines.push("");
74
+ if (filesInDir.length > 0) {
75
+ lines.push("## Files", "");
76
+ for (const [filePath, entry] of filesInDir) {
77
+ lines.push(`- \`${(0, node_path_1.basename)(filePath)}\` — ${entry.route} (${entry.domain})`);
78
+ }
79
+ lines.push("");
80
+ }
81
+ if (nearbyDocs.length > 0) {
82
+ lines.push("## Nearby docs", "");
83
+ for (const docPath of nearbyDocs) {
84
+ lines.push(`- \`${docPath}\``);
85
+ }
86
+ lines.push("");
87
+ }
88
+ return lines.join("\n");
89
+ }
90
+ function seedInstructions(targetPath, repoRoot, opts = {}) {
91
+ const absTarget = (0, node_path_1.resolve)(repoRoot, targetPath);
92
+ // Path traversal check: ensure absTarget is within repoRoot
93
+ const relCheck = (0, node_path_1.relative)(repoRoot, absTarget);
94
+ if (relCheck.startsWith("..") || relCheck.startsWith("/")) {
95
+ throw new Error(`Path traversal detected: target path is outside repo root`);
96
+ }
97
+ const outFile = (0, node_path_1.join)(absTarget, "POLARIS.md");
98
+ if ((0, node_fs_1.existsSync)(outFile)) {
99
+ if (hasDraftMarker(outFile)) {
100
+ return "skipped-draft";
101
+ }
102
+ return "skipped-exists";
103
+ }
104
+ const config = (0, loader_js_1.loadConfig)(repoRoot);
105
+ const atlasPath = (0, node_path_1.resolve)(repoRoot, config.repo.sidecarOutputPath ?? ".polaris/map");
106
+ const allRoutes = {
107
+ ...(0, atlas_js_1.readFileRoutes)(atlasPath),
108
+ ...(0, atlas_js_1.readNeedsReview)(atlasPath),
109
+ };
110
+ const relTarget = (0, node_path_1.relative)(repoRoot, absTarget).replace(/\\/g, "/");
111
+ const content = generateDraft(relTarget, repoRoot, allRoutes);
112
+ if (!opts.dryRun) {
113
+ (0, node_fs_1.writeFileSync)(outFile, content, "utf-8");
114
+ }
115
+ return "written";
116
+ }
117
+ function seedInstructionsAll(repoRoot, opts = {}) {
118
+ const config = (0, loader_js_1.loadConfig)(repoRoot);
119
+ const atlasPath = (0, node_path_1.resolve)(repoRoot, config.repo.sidecarOutputPath ?? ".polaris/map");
120
+ const allRoutes = {
121
+ ...(0, atlas_js_1.readFileRoutes)(atlasPath),
122
+ ...(0, atlas_js_1.readNeedsReview)(atlasPath),
123
+ };
124
+ const dirs = collectDirs(repoRoot, repoRoot);
125
+ const written = [];
126
+ const skippedExists = [];
127
+ const skippedDraft = [];
128
+ for (const relDir of dirs) {
129
+ const absDir = (0, node_path_1.resolve)(repoRoot, relDir);
130
+ const outFile = (0, node_path_1.join)(absDir, "POLARIS.md");
131
+ if ((0, node_fs_1.existsSync)(outFile)) {
132
+ if (hasDraftMarker(outFile)) {
133
+ skippedDraft.push(relDir);
134
+ }
135
+ else {
136
+ skippedExists.push(relDir);
137
+ }
138
+ continue;
139
+ }
140
+ const content = generateDraft(relDir, repoRoot, allRoutes);
141
+ if (!opts.dryRun) {
142
+ (0, node_fs_1.writeFileSync)(outFile, content, "utf-8");
143
+ }
144
+ written.push(relDir);
145
+ }
146
+ return { written, skippedExists, skippedDraft };
147
+ }
@@ -0,0 +1,137 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const vitest_1 = require("vitest");
4
+ const node_fs_1 = require("node:fs");
5
+ const node_path_1 = require("node:path");
6
+ const seed_instructions_js_1 = require("./seed-instructions.js");
7
+ const TMP = (0, node_path_1.join)(process.cwd(), ".test-docs-seed-tmp");
8
+ function makeEntry(overrides = {}) {
9
+ return {
10
+ domain: "test-domain",
11
+ route: "test-route",
12
+ taskchain: "test-chain",
13
+ confidence: 0.9,
14
+ classification: "indexed",
15
+ last_updated: "2026-01-01T00:00:00Z",
16
+ updated_by: "test",
17
+ tags: [],
18
+ ...overrides,
19
+ };
20
+ }
21
+ function setup() {
22
+ (0, node_fs_1.mkdirSync)((0, node_path_1.join)(TMP, "src/map"), { recursive: true });
23
+ (0, node_fs_1.mkdirSync)((0, node_path_1.join)(TMP, "src/cli"), { recursive: true });
24
+ (0, node_fs_1.mkdirSync)((0, node_path_1.join)(TMP, ".polaris/map"), { recursive: true });
25
+ // Minimal polaris.config.json so loadConfig doesn't fail
26
+ (0, node_fs_1.writeFileSync)((0, node_path_1.join)(TMP, "polaris.config.json"), JSON.stringify({ repo: {}, map: {} }));
27
+ // Empty atlas files
28
+ (0, node_fs_1.writeFileSync)((0, node_path_1.join)(TMP, ".polaris/map/file-routes.json"), JSON.stringify({}));
29
+ (0, node_fs_1.writeFileSync)((0, node_path_1.join)(TMP, ".polaris/map/needs-review.json"), JSON.stringify({}));
30
+ }
31
+ function teardown() {
32
+ (0, node_fs_1.rmSync)(TMP, { recursive: true, force: true });
33
+ }
34
+ (0, vitest_1.describe)("hasDraftMarker", () => {
35
+ (0, vitest_1.beforeEach)(setup);
36
+ (0, vitest_1.afterEach)(teardown);
37
+ (0, vitest_1.it)("returns true when file starts with draft marker", () => {
38
+ (0, node_fs_1.writeFileSync)((0, node_path_1.join)(TMP, "POLARIS.md"), `${seed_instructions_js_1.DRAFT_MARKER}\n# title`);
39
+ (0, vitest_1.expect)((0, seed_instructions_js_1.hasDraftMarker)((0, node_path_1.join)(TMP, "POLARIS.md"))).toBe(true);
40
+ });
41
+ (0, vitest_1.it)("returns false when file has no draft marker", () => {
42
+ (0, node_fs_1.writeFileSync)((0, node_path_1.join)(TMP, "POLARIS.md"), "# Human-written doc");
43
+ (0, vitest_1.expect)((0, seed_instructions_js_1.hasDraftMarker)((0, node_path_1.join)(TMP, "POLARIS.md"))).toBe(false);
44
+ });
45
+ (0, vitest_1.it)("returns false for nonexistent file", () => {
46
+ (0, vitest_1.expect)((0, seed_instructions_js_1.hasDraftMarker)((0, node_path_1.join)(TMP, "nonexistent.md"))).toBe(false);
47
+ });
48
+ });
49
+ (0, vitest_1.describe)("generateDraft", () => {
50
+ (0, vitest_1.it)("includes draft marker at top", () => {
51
+ const content = (0, seed_instructions_js_1.generateDraft)("src/map", TMP, {});
52
+ (0, vitest_1.expect)(content.startsWith(seed_instructions_js_1.DRAFT_MARKER)).toBe(true);
53
+ });
54
+ (0, vitest_1.it)("uses directory basename as heading", () => {
55
+ const content = (0, seed_instructions_js_1.generateDraft)("src/map", TMP, {});
56
+ (0, vitest_1.expect)(content).toContain("# map");
57
+ });
58
+ (0, vitest_1.it)("includes domain and route from atlas entries in that directory", () => {
59
+ const routes = {
60
+ "src/map/atlas.ts": makeEntry({ domain: "atlas", route: "polaris-map" }),
61
+ };
62
+ const content = (0, seed_instructions_js_1.generateDraft)("src/map", TMP, routes);
63
+ (0, vitest_1.expect)(content).toContain("atlas");
64
+ (0, vitest_1.expect)(content).toContain("polaris-map");
65
+ });
66
+ (0, vitest_1.it)("lists files in the directory", () => {
67
+ const routes = {
68
+ "src/map/atlas.ts": makeEntry({ domain: "atlas", route: "polaris-map" }),
69
+ "src/map/update.ts": makeEntry({ domain: "atlas", route: "polaris-map" }),
70
+ };
71
+ const content = (0, seed_instructions_js_1.generateDraft)("src/map", TMP, routes);
72
+ (0, vitest_1.expect)(content).toContain("`atlas.ts`");
73
+ (0, vitest_1.expect)(content).toContain("`update.ts`");
74
+ });
75
+ (0, vitest_1.it)("does not include files from other directories", () => {
76
+ const routes = {
77
+ "src/cli/index.ts": makeEntry({ domain: "cli", route: "polaris-cli" }),
78
+ "src/map/atlas.ts": makeEntry({ domain: "atlas", route: "polaris-map" }),
79
+ };
80
+ const content = (0, seed_instructions_js_1.generateDraft)("src/map", TMP, routes);
81
+ (0, vitest_1.expect)(content).not.toContain("`index.ts`");
82
+ });
83
+ });
84
+ (0, vitest_1.describe)("seedInstructions", () => {
85
+ (0, vitest_1.beforeEach)(setup);
86
+ (0, vitest_1.afterEach)(teardown);
87
+ (0, vitest_1.it)("writes a POLARIS.md draft and returns written", () => {
88
+ const result = (0, seed_instructions_js_1.seedInstructions)("src/map", TMP);
89
+ (0, vitest_1.expect)(result).toBe("written");
90
+ const outPath = (0, node_path_1.join)(TMP, "src/map/POLARIS.md");
91
+ (0, vitest_1.expect)((0, node_fs_1.existsSync)(outPath)).toBe(true);
92
+ const content = (0, node_fs_1.readFileSync)(outPath, "utf-8");
93
+ (0, vitest_1.expect)(content).toContain(seed_instructions_js_1.DRAFT_MARKER);
94
+ });
95
+ (0, vitest_1.it)("returns skipped-exists when human-edited POLARIS.md present", () => {
96
+ (0, node_fs_1.writeFileSync)((0, node_path_1.join)(TMP, "src/map/POLARIS.md"), "# Human-edited");
97
+ const result = (0, seed_instructions_js_1.seedInstructions)("src/map", TMP);
98
+ (0, vitest_1.expect)(result).toBe("skipped-exists");
99
+ });
100
+ (0, vitest_1.it)("returns skipped-draft when draft POLARIS.md already present", () => {
101
+ (0, node_fs_1.writeFileSync)((0, node_path_1.join)(TMP, "src/map/POLARIS.md"), `${seed_instructions_js_1.DRAFT_MARKER}\n# Draft`);
102
+ const result = (0, seed_instructions_js_1.seedInstructions)("src/map", TMP);
103
+ (0, vitest_1.expect)(result).toBe("skipped-draft");
104
+ });
105
+ (0, vitest_1.it)("does not write file in dry-run mode", () => {
106
+ const result = (0, seed_instructions_js_1.seedInstructions)("src/map", TMP, { dryRun: true });
107
+ (0, vitest_1.expect)(result).toBe("written");
108
+ (0, vitest_1.expect)((0, node_fs_1.existsSync)((0, node_path_1.join)(TMP, "src/map/POLARIS.md"))).toBe(false);
109
+ });
110
+ });
111
+ (0, vitest_1.describe)("seedInstructionsAll", () => {
112
+ (0, vitest_1.beforeEach)(setup);
113
+ (0, vitest_1.afterEach)(teardown);
114
+ (0, vitest_1.it)("generates drafts for directories without POLARIS.md", () => {
115
+ const { written } = (0, seed_instructions_js_1.seedInstructionsAll)(TMP);
116
+ (0, vitest_1.expect)(written).toContain("src/map");
117
+ (0, vitest_1.expect)(written).toContain("src/cli");
118
+ });
119
+ (0, vitest_1.it)("skips directories with human-edited POLARIS.md", () => {
120
+ (0, node_fs_1.writeFileSync)((0, node_path_1.join)(TMP, "src/map/POLARIS.md"), "# Human-edited");
121
+ const { written, skippedExists } = (0, seed_instructions_js_1.seedInstructionsAll)(TMP);
122
+ (0, vitest_1.expect)(skippedExists).toContain("src/map");
123
+ (0, vitest_1.expect)(written).not.toContain("src/map");
124
+ });
125
+ (0, vitest_1.it)("skips directories with draft POLARIS.md", () => {
126
+ (0, node_fs_1.writeFileSync)((0, node_path_1.join)(TMP, "src/map/POLARIS.md"), `${seed_instructions_js_1.DRAFT_MARKER}\n# Draft`);
127
+ const { skippedDraft } = (0, seed_instructions_js_1.seedInstructionsAll)(TMP);
128
+ (0, vitest_1.expect)(skippedDraft).toContain("src/map");
129
+ });
130
+ (0, vitest_1.it)("does not write files in dry-run mode", () => {
131
+ const { written } = (0, seed_instructions_js_1.seedInstructionsAll)(TMP, { dryRun: true });
132
+ (0, vitest_1.expect)(written.length).toBeGreaterThan(0);
133
+ for (const dir of written) {
134
+ (0, vitest_1.expect)((0, node_fs_1.existsSync)((0, node_path_1.join)(TMP, dir, "POLARIS.md"))).toBe(false);
135
+ }
136
+ });
137
+ });
@@ -0,0 +1,267 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getLastGitModDate = getLastGitModDate;
4
+ exports.getFilesChangedAfter = getFilesChangedAfter;
5
+ exports.parseReadBeforeEditingLinks = parseReadBeforeEditingLinks;
6
+ exports.validateDir = validateDir;
7
+ exports.validateInstructions = validateInstructions;
8
+ exports.printReport = printReport;
9
+ const node_fs_1 = require("node:fs");
10
+ const node_path_1 = require("node:path");
11
+ const node_child_process_1 = require("node:child_process");
12
+ const loader_js_1 = require("../config/loader.js");
13
+ const atlas_js_1 = require("../map/atlas.js");
14
+ const seed_instructions_js_1 = require("./seed-instructions.js");
15
+ /**
16
+ * Get the last git modification date of a file.
17
+ * Returns null if the file is untracked or git is unavailable.
18
+ */
19
+ function getLastGitModDate(filePath, repoRoot) {
20
+ try {
21
+ const rel = (0, node_path_1.relative)(repoRoot, filePath).replace(/\\/g, "/");
22
+ const out = (0, node_child_process_1.execFileSync)("git", ["-C", repoRoot, "log", "-1", "--format=%cI", "--", rel], {
23
+ stdio: ["pipe", "pipe", "pipe"],
24
+ encoding: "utf-8",
25
+ }).trim();
26
+ if (!out)
27
+ return null;
28
+ return new Date(out);
29
+ }
30
+ catch {
31
+ return null;
32
+ }
33
+ }
34
+ /**
35
+ * Get files in a directory that were modified after a given date (via git log).
36
+ * Only considers files directly in the directory (not subdirs).
37
+ */
38
+ function getFilesChangedAfter(dir, since, repoRoot) {
39
+ try {
40
+ const relDir = (0, node_path_1.relative)(repoRoot, dir).replace(/\\/g, "/");
41
+ const pattern = relDir ? `${relDir}/*` : "*";
42
+ const isoDate = since.toISOString();
43
+ const out = (0, node_child_process_1.execFileSync)("git", ["-C", repoRoot, "log", `--after=${isoDate}`, "--name-only", "--format=", "--", pattern], { stdio: ["pipe", "pipe", "pipe"], encoding: "utf-8" }).trim();
44
+ if (!out)
45
+ return [];
46
+ const files = out
47
+ .split("\n")
48
+ .map((f) => f.trim())
49
+ .filter(Boolean)
50
+ .filter((f) => {
51
+ // Only files directly in the target dir (not nested subdirs)
52
+ const fileDir = (0, node_path_1.dirname)(f);
53
+ const normRelDir = relDir || ".";
54
+ return fileDir === normRelDir || (normRelDir === "." && !f.includes("/"));
55
+ });
56
+ // Deduplicate
57
+ return [...new Set(files)];
58
+ }
59
+ catch {
60
+ return [];
61
+ }
62
+ }
63
+ /**
64
+ * Parse "Read before editing" section of a POLARIS.md and extract linked file paths.
65
+ * Looks for markdown links: [text](path)
66
+ */
67
+ function parseReadBeforeEditingLinks(content) {
68
+ const links = [];
69
+ const sectionRe = /^##\s+Read before editing/im;
70
+ const nextSectionRe = /^##\s+/im;
71
+ const sectionMatch = sectionRe.exec(content);
72
+ if (!sectionMatch)
73
+ return links;
74
+ const sectionStart = sectionMatch.index + sectionMatch[0].length;
75
+ const remainder = content.slice(sectionStart);
76
+ const nextMatch = nextSectionRe.exec(remainder);
77
+ const sectionText = nextMatch ? remainder.slice(0, nextMatch.index) : remainder;
78
+ // Match markdown links: [text](path)
79
+ const mdLinkRe = /\[([^\]]*)\]\(([^)]+)\)/g;
80
+ let m;
81
+ while ((m = mdLinkRe.exec(sectionText)) !== null) {
82
+ const href = m[2].trim();
83
+ // Skip external URLs
84
+ if (!href.startsWith("http://") && !href.startsWith("https://")) {
85
+ links.push(href);
86
+ }
87
+ }
88
+ return links;
89
+ }
90
+ function getRequiredDirs(repoRoot) {
91
+ const config = (0, loader_js_1.loadConfig)(repoRoot);
92
+ // The config type may not yet have a docs key; access dynamically
93
+ const docsConfig = config["docs"];
94
+ return docsConfig?.instructionFiles?.required ?? [];
95
+ }
96
+ function collectDirs(dir, root) {
97
+ const dirs = [];
98
+ try {
99
+ for (const entry of (0, node_fs_1.readdirSync)(dir, { withFileTypes: true })) {
100
+ if (entry.isDirectory() &&
101
+ !entry.name.startsWith(".") &&
102
+ entry.name !== "node_modules" &&
103
+ entry.name !== "dist") {
104
+ const full = (0, node_path_1.join)(dir, entry.name);
105
+ const rel = (0, node_path_1.relative)(root, full).replace(/\\/g, "/");
106
+ dirs.push(rel);
107
+ dirs.push(...collectDirs(full, root));
108
+ }
109
+ }
110
+ }
111
+ catch {
112
+ // ignore unreadable dirs
113
+ }
114
+ return dirs;
115
+ }
116
+ /**
117
+ * Validate a single directory's POLARIS.md.
118
+ */
119
+ function validateDir(relDir, repoRoot, allRoutes) {
120
+ const absDir = (0, node_path_1.resolve)(repoRoot, relDir);
121
+ const polarisFile = (0, node_path_1.join)(absDir, "POLARIS.md");
122
+ const polarisRel = (0, node_path_1.relative)(repoRoot, polarisFile).replace(/\\/g, "/");
123
+ if (!(0, node_fs_1.existsSync)(polarisFile)) {
124
+ return {
125
+ dir: relDir,
126
+ polarisFile: null,
127
+ status: "MISSING",
128
+ findings: [{ severity: "MISSING", message: `No POLARIS.md in ${relDir || "."}` }],
129
+ };
130
+ }
131
+ const content = (0, node_fs_1.readFileSync)(polarisFile, "utf-8");
132
+ const findings = [];
133
+ // Signal 1: ≥3 files in directory changed since last POLARIS.md git modification
134
+ const lastMod = getLastGitModDate(polarisFile, repoRoot);
135
+ if (lastMod !== null) {
136
+ const changed = getFilesChangedAfter(absDir, lastMod, repoRoot);
137
+ const relevant = changed.filter((f) => !f.endsWith("POLARIS.md") && !f.endsWith("POLARIS.draft.md"));
138
+ if (relevant.length >= 3) {
139
+ findings.push({
140
+ severity: "WARN",
141
+ message: `${relevant.length} files changed since last POLARIS.md update`,
142
+ });
143
+ }
144
+ }
145
+ // Signal 2: broken links in "Read before editing"
146
+ const links = parseReadBeforeEditingLinks(content);
147
+ for (const link of links) {
148
+ const absLink = (0, node_path_1.resolve)(absDir, link);
149
+ if (!(0, node_fs_1.existsSync)(absLink)) {
150
+ findings.push({
151
+ severity: "ERROR",
152
+ message: `Broken link in "Read before editing": ${link}`,
153
+ });
154
+ }
155
+ }
156
+ // Signal 3: instructionFile pointer in atlas references a non-existent path
157
+ const normRelDir = relDir.replace(/\\/g, "/");
158
+ for (const [filePath, entry] of Object.entries(allRoutes)) {
159
+ const fileDir = (0, node_path_1.dirname)(filePath).replace(/\\/g, "/");
160
+ if (fileDir !== normRelDir && !(normRelDir === "." && !filePath.includes("/")))
161
+ continue;
162
+ if (!entry.instructionFile)
163
+ continue;
164
+ const absInstr = (0, node_path_1.resolve)(repoRoot, entry.instructionFile);
165
+ if (!(0, node_fs_1.existsSync)(absInstr)) {
166
+ findings.push({
167
+ severity: "ERROR",
168
+ message: `Atlas entry "${filePath}" has instructionFile "${entry.instructionFile}" that does not exist`,
169
+ });
170
+ }
171
+ }
172
+ if (findings.length === 0) {
173
+ return { dir: relDir, polarisFile: polarisRel, status: "OK", findings: [] };
174
+ }
175
+ const hasError = findings.some((f) => f.severity === "ERROR");
176
+ return {
177
+ dir: relDir,
178
+ polarisFile: polarisRel,
179
+ status: hasError ? "ERROR" : "WARN",
180
+ findings,
181
+ };
182
+ }
183
+ function validateInstructions(opts = {}) {
184
+ const repoRoot = opts.repoRoot ?? process.cwd();
185
+ const config = (0, loader_js_1.loadConfig)(repoRoot);
186
+ const atlasPath = (0, node_path_1.resolve)(repoRoot, config.repo.sidecarOutputPath ?? ".polaris/map");
187
+ const allRoutes = {
188
+ ...(0, atlas_js_1.readFileRoutes)(atlasPath),
189
+ ...(0, atlas_js_1.readNeedsReview)(atlasPath),
190
+ };
191
+ const requiredDirs = getRequiredDirs(repoRoot);
192
+ let dirsToCheck;
193
+ if (opts.path) {
194
+ const absPath = (0, node_path_1.resolve)(repoRoot, opts.path);
195
+ const relPath = (0, node_path_1.relative)(repoRoot, absPath).replace(/\\/g, "/");
196
+ // Path traversal check
197
+ if (relPath.startsWith("..") || relPath.startsWith("/")) {
198
+ throw new Error(`Path traversal detected: path is outside repo root: ${opts.path}`);
199
+ }
200
+ dirsToCheck = [relPath];
201
+ }
202
+ else {
203
+ // Include root dir and all subdirs
204
+ dirsToCheck = [".", ...collectDirs(repoRoot, repoRoot)];
205
+ }
206
+ const results = [];
207
+ for (const relDir of dirsToCheck) {
208
+ const result = validateDir(relDir, repoRoot, allRoutes);
209
+ // Upgrade MISSING → ERROR for required dirs
210
+ if (result.status === "MISSING" && requiredDirs.includes(relDir)) {
211
+ result.status = "ERROR";
212
+ }
213
+ results.push(result);
214
+ }
215
+ if (opts.fix) {
216
+ for (const result of results) {
217
+ if (result.status !== "OK") {
218
+ const absDir = (0, node_path_1.resolve)(repoRoot, result.dir);
219
+ const draftPath = (0, node_path_1.join)(absDir, "POLARIS.draft.md");
220
+ // Path traversal check for draft path
221
+ const relDraft = (0, node_path_1.relative)(repoRoot, draftPath);
222
+ if (relDraft.startsWith("..") || relDraft.startsWith("/")) {
223
+ result.findings.push({
224
+ severity: "ERROR",
225
+ message: "Cannot write draft: path traversal detected",
226
+ });
227
+ continue;
228
+ }
229
+ // Don't clobber existing drafts in fix mode
230
+ if ((0, node_fs_1.existsSync)(draftPath)) {
231
+ result.findings.push({
232
+ severity: "WARN",
233
+ message: "Draft already exists at POLARIS.draft.md; skipping write",
234
+ });
235
+ continue;
236
+ }
237
+ const content = (0, seed_instructions_js_1.generateDraft)(result.dir, repoRoot, allRoutes);
238
+ (0, node_fs_1.writeFileSync)(draftPath, content, "utf-8");
239
+ result.findings.push({
240
+ severity: "WARN",
241
+ message: "Draft written to POLARIS.draft.md",
242
+ });
243
+ }
244
+ }
245
+ }
246
+ const hasErrors = results.some((r) => r.status === "ERROR");
247
+ return { results, hasErrors };
248
+ }
249
+ const DRAFT_WRITTEN_MSG = "Draft written to POLARIS.draft.md";
250
+ function printReport(report) {
251
+ console.log("POLARIS.md validation report:");
252
+ for (const result of report.results) {
253
+ const label = result.polarisFile ?? `${result.dir || "."}/`;
254
+ const statusPad = result.status.padEnd(7);
255
+ console.log(` ${label.padEnd(40)} ${statusPad}`);
256
+ for (const finding of result.findings) {
257
+ if (finding.message === DRAFT_WRITTEN_MSG)
258
+ continue;
259
+ const prefix = finding.severity === "ERROR" ? "ERROR:" : "WARN: ";
260
+ console.log(` ${prefix} ${finding.message}`);
261
+ }
262
+ const draftFinding = result.findings.find((f) => f.message === DRAFT_WRITTEN_MSG);
263
+ if (draftFinding) {
264
+ console.log(` → Draft written to POLARIS.draft.md`);
265
+ }
266
+ }
267
+ }