@lsctech/polaris 0.3.28 → 0.4.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.
package/dist/cli/init.js CHANGED
@@ -21,9 +21,15 @@ const index_js_1 = require("../map/index.js");
21
21
  const adopt_instructions_js_1 = require("./adopt-instructions.js");
22
22
  const adopt_workspace_js_1 = require("./adopt-workspace.js");
23
23
  const adopt_assets_js_1 = require("./adopt-assets.js");
24
+ const agent_setup_js_1 = require("./agent-setup.js");
25
+ const runner_js_1 = require("./setup-interview/runner.js");
26
+ const generate_js_1 = require("./setup-interview/generate.js");
27
+ const generator_js_1 = require("../skill-packet/generator.js");
28
+ const foreman_dispatch_js_1 = require("../loop/adapters/foreman-dispatch.js");
24
29
  const adopt_genesis_js_1 = require("./adopt-genesis.js");
25
30
  const adopt_report_js_1 = require("./adopt-report.js");
26
31
  const artifact_policy_js_1 = require("../finalize/artifact-policy.js");
32
+ const report_js_1 = require("./setup-interview/report.js");
27
33
  var init_detect_js_2 = require("./init-detect.js");
28
34
  Object.defineProperty(exports, "detectRepoState", { enumerable: true, get: function () { return init_detect_js_2.detectRepoState; } });
29
35
  const PLAN_COMPLETE_STATUSES = new Set(["completed", "skipped"]);
@@ -377,6 +383,50 @@ async function runInit(options = {}) {
377
383
  process.stdout.write("This repo has existing content. Run `polaris init --adopt` to begin adoption.\nThis repo has existing content. Run polaris init --adopt to begin adoption.\n");
378
384
  return;
379
385
  }
386
+ // New/empty repo: run the setup interview and generate artifacts behind an approval gate.
387
+ if (!options.adopt && (repoState === "empty" || repoState === "new")) {
388
+ const interviewFn = options.runInterview ?? runner_js_1.runInterview;
389
+ let record;
390
+ try {
391
+ record = await interviewFn(repoRoot, { now: options.now });
392
+ }
393
+ catch (err) {
394
+ const msg = err instanceof Error ? err.message : String(err);
395
+ process.stdout.write(`${msg}\n`);
396
+ return;
397
+ }
398
+ const detected = detectCompaction(repoRoot);
399
+ const detectedRepoAnalysis = detectRepoAnalysis(repoRoot);
400
+ const generateFn = options.generateSetupArtifacts ?? generate_js_1.generateSetupArtifacts;
401
+ await generateFn(record, {
402
+ repoRoot,
403
+ dryRun: options.dryRun,
404
+ yes: options.yes,
405
+ now: options.now,
406
+ detectedProviders: detected,
407
+ detectedRepoAnalysis,
408
+ scaffoldRootSurfaces: options.scaffoldRootSurfaces,
409
+ generatePolarisRules: options.generatePolarisRules,
410
+ migrateSmartDocs: options.migrateSmartDocs,
411
+ runMapIndex: options.runMapIndex,
412
+ });
413
+ // Post-setup validation and checkpoint report
414
+ if (!options.dryRun) {
415
+ const checkpointReport = (0, report_js_1.buildCheckpointReport)({
416
+ repoRoot,
417
+ record,
418
+ now: options.now,
419
+ });
420
+ (0, report_js_1.writeCheckpointReport)(repoRoot, checkpointReport);
421
+ (0, report_js_1.printCheckpointReport)(checkpointReport);
422
+ // Check for validation failures
423
+ const failedValidations = Object.entries(checkpointReport.validationResults).filter(([_, result]) => !result.passed);
424
+ if (failedValidations.length > 0) {
425
+ process.stdout.write(`\nWarning: ${failedValidations.length} file(s) failed validation. Check the report above for details.\n`);
426
+ }
427
+ }
428
+ return;
429
+ }
380
430
  // Load existing config (if any) so we preserve user-authored fields.
381
431
  let existing = {};
382
432
  if ((0, node_fs_1.existsSync)(configPath)) {
@@ -415,6 +465,24 @@ async function runInit(options = {}) {
415
465
  process.stdout.write(`polaris.config.json written to ${configPath}\n${providerSummary}\n`);
416
466
  }
417
467
  if (!options.adopt) {
468
+ // Launch Foreman with setup-bootstrap packet when execution providers are configured.
469
+ const initExecution = asRecord(updated.execution);
470
+ const initProviders = asRecord(initExecution.providers);
471
+ if (Object.keys(initProviders).length > 0) {
472
+ try {
473
+ const binding = await (0, agent_setup_js_1.resolveForeman)(repoRoot, updated);
474
+ const setupPacket = (0, generator_js_1.generateSetupBootstrapPacket)("init");
475
+ await (0, foreman_dispatch_js_1.dispatchForeman)({
476
+ packet: setupPacket,
477
+ provider: binding.provider,
478
+ executionConfig: updated.execution,
479
+ dryRun: options.dryRun,
480
+ });
481
+ }
482
+ catch {
483
+ // Foreman dispatch is best-effort during init — don't block setup.
484
+ }
485
+ }
418
486
  return;
419
487
  }
420
488
  const adoptionPlanPath = (0, node_path_1.join)(repoRoot, ".polaris", "adoption-plan.json");
@@ -548,7 +616,7 @@ function createInitCommand(options = {}) {
548
616
  .option("--status", "detect and print current repository state without writing files")
549
617
  .option("--adopt", "run existing repository adoption flow")
550
618
  .option("--resume", "resume an approved adoption plan without prompting")
551
- .option("--yes", "auto-approve adoption plan when used with --adopt")
619
+ .option("--yes", "auto-approve setup or adoption plan without prompting")
552
620
  .option("--commit", "create an adoption commit when used with --adopt")
553
621
  .action(async (cmdOptions) => {
554
622
  await runInit({
@@ -0,0 +1,380 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.generateSetupArtifacts = generateSetupArtifacts;
4
+ const node_fs_1 = require("node:fs");
5
+ const node_path_1 = require("node:path");
6
+ const adopt_approve_js_1 = require("../adopt-approve.js");
7
+ const adopt_rules_js_1 = require("../adopt-rules.js");
8
+ const adopt_smartdocs_js_1 = require("../adopt-smartdocs.js");
9
+ const adopt_workspace_js_1 = require("../adopt-workspace.js");
10
+ const index_js_1 = require("../../map/index.js");
11
+ const store_js_1 = require("./store.js");
12
+ function asRecord(value) {
13
+ return typeof value === "object" && value !== null && !Array.isArray(value)
14
+ ? value
15
+ : {};
16
+ }
17
+ function buildSetupConfig(existing, record, detectedProviders, detectedRepoAnalysis) {
18
+ const updated = {
19
+ ...existing,
20
+ version: typeof existing.version === "string" ? existing.version : "1.0",
21
+ };
22
+ const providers = {};
23
+ const existingProviders = asRecord(existing.providers);
24
+ if (detectedProviders.length > 0) {
25
+ providers.compactionProviders = detectedProviders;
26
+ }
27
+ else if (existingProviders.compactionProviders) {
28
+ providers.compactionProviders = existingProviders.compactionProviders;
29
+ }
30
+ if (detectedRepoAnalysis.length > 0) {
31
+ const existingRepoAnalysis = asRecord(existingProviders.repoAnalysis);
32
+ providers.repoAnalysis = { ...existingRepoAnalysis, preferred: detectedRepoAnalysis[0] };
33
+ }
34
+ else if (existingProviders.repoAnalysis) {
35
+ providers.repoAnalysis = existingProviders.repoAnalysis;
36
+ }
37
+ if (Object.keys(providers).length > 0) {
38
+ updated.providers = { ...existingProviders, ...providers };
39
+ }
40
+ const existingRepo = asRecord(existing.repo);
41
+ updated.repo = {
42
+ ...existingRepo,
43
+ sourceRoots: record.answers.source_roots,
44
+ docsRoots: record.answers.canonical_doc_folders,
45
+ };
46
+ const providersByRole = record.answers.providers_by_role;
47
+ if (providersByRole && Object.keys(providersByRole).length > 0) {
48
+ const existingExecution = asRecord(existing.execution);
49
+ const existingExecutionProviders = asRecord(existingExecution.providers);
50
+ const executionProviders = { ...existingExecutionProviders };
51
+ for (const [role, provider] of Object.entries(providersByRole)) {
52
+ executionProviders[role] = { command: provider };
53
+ }
54
+ updated.execution = {
55
+ ...existingExecution,
56
+ providers: executionProviders,
57
+ };
58
+ }
59
+ return updated;
60
+ }
61
+ function buildSetupInventory(record) {
62
+ const candidates = (record.answers.canonical_doc_folders ?? []).map((folder) => ({
63
+ path: folder,
64
+ kind: "doc",
65
+ suggested_destination: `smartdocs/raw/${folder}`,
66
+ confidence: 0.9,
67
+ has_frontmatter: false,
68
+ estimated_risk: "low",
69
+ }));
70
+ return {
71
+ scan_date: new Date().toISOString(),
72
+ repo_state: "new",
73
+ package_manager: null,
74
+ source_roots: record.answers.source_roots ?? [],
75
+ docs_roots: record.answers.canonical_doc_folders ?? [],
76
+ test_commands: [],
77
+ build_commands: [],
78
+ package_scripts: {},
79
+ generated_roots: [],
80
+ cache_roots: [],
81
+ fixture_roots: [],
82
+ agent_instruction_files: [],
83
+ existing_smartdocs_dirs: [],
84
+ architecture_notes: [record.answers.project_purpose ?? ""].filter(Boolean),
85
+ likely_canonical_folders: record.answers.canonical_doc_folders ?? [],
86
+ smartdocs_candidates: candidates,
87
+ ignore_candidates: record.answers.never_touch ?? [],
88
+ };
89
+ }
90
+ function* walkMarkdownFiles(dir, root) {
91
+ for (const entry of (0, node_fs_1.readdirSync)(dir, { withFileTypes: true })) {
92
+ const full = (0, node_path_1.join)(dir, entry.name);
93
+ if (entry.isDirectory()) {
94
+ yield* walkMarkdownFiles(full, root);
95
+ }
96
+ else if (entry.isFile() && entry.name.endsWith(".md")) {
97
+ yield (0, node_path_1.relative)(root, full).replace(/\\/g, "/");
98
+ }
99
+ }
100
+ }
101
+ function scanCanonicalDocs(repoRoot, folders) {
102
+ const candidates = [];
103
+ for (const folder of folders) {
104
+ const absFolder = (0, node_path_1.join)(repoRoot, folder);
105
+ if (!(0, node_fs_1.existsSync)(absFolder))
106
+ continue;
107
+ for (const relPath of walkMarkdownFiles(absFolder, repoRoot)) {
108
+ candidates.push({
109
+ path: relPath,
110
+ kind: "doc",
111
+ suggested_destination: `smartdocs/raw/${relPath}`,
112
+ confidence: 0.9,
113
+ has_frontmatter: false,
114
+ estimated_risk: "low",
115
+ });
116
+ }
117
+ }
118
+ return candidates;
119
+ }
120
+ function buildSetupPlan(record, repoRoot, now) {
121
+ const candidates = scanCanonicalDocs(repoRoot, record.answers.canonical_doc_folders ?? []);
122
+ const steps = [];
123
+ let order = 1;
124
+ steps.push({
125
+ step_id: "setup-genesis",
126
+ order: order++,
127
+ phase: "A",
128
+ category: "scaffold",
129
+ action: "create",
130
+ dest_path: "GENESIS.md",
131
+ description: "Write initial project genesis from interview answers.",
132
+ destructive: false,
133
+ requires_approval: false,
134
+ estimated_risk: "low",
135
+ status: "pending",
136
+ });
137
+ steps.push({
138
+ step_id: "setup-config",
139
+ order: order++,
140
+ phase: "A",
141
+ category: "provider-config",
142
+ action: "modify",
143
+ dest_path: "polaris.config.json",
144
+ description: "Write polaris.config.json from interview answers and detected providers.",
145
+ destructive: true,
146
+ requires_approval: false,
147
+ estimated_risk: "low",
148
+ status: "pending",
149
+ });
150
+ steps.push({
151
+ step_id: "setup-rules",
152
+ order: order++,
153
+ phase: "A",
154
+ category: "scaffold",
155
+ action: "create",
156
+ dest_path: "POLARIS_RULES.md",
157
+ description: "Generate Polaris rules from interview answers.",
158
+ destructive: false,
159
+ requires_approval: false,
160
+ estimated_risk: "low",
161
+ status: "pending",
162
+ });
163
+ steps.push({
164
+ step_id: "setup-root-surfaces",
165
+ order: order++,
166
+ phase: "A",
167
+ category: "scaffold",
168
+ action: "create",
169
+ dest_path: "POLARIS.md, SUMMARY.md, CLAUDE.md, AGENTS.md, .github/copilot-instructions.md",
170
+ description: "Create root route surfaces if missing.",
171
+ destructive: false,
172
+ requires_approval: false,
173
+ estimated_risk: "low",
174
+ status: "pending",
175
+ });
176
+ for (const candidate of candidates) {
177
+ steps.push({
178
+ step_id: `setup-smartdocs-migrate-${order.toString().padStart(3, "0")}`,
179
+ order: order++,
180
+ phase: "C",
181
+ category: "smartdocs-migrate",
182
+ action: "move",
183
+ source_path: candidate.path,
184
+ dest_path: candidate.suggested_destination,
185
+ description: `Move ${candidate.path} to ${candidate.suggested_destination}.`,
186
+ destructive: true,
187
+ requires_approval: true,
188
+ estimated_risk: "low",
189
+ status: "pending",
190
+ });
191
+ }
192
+ steps.push({
193
+ step_id: "setup-atlas-generate",
194
+ order: order++,
195
+ phase: "C",
196
+ category: "atlas-generate",
197
+ action: "modify",
198
+ dest_path: ".polaris/map/index.json",
199
+ description: "Run polaris map index to generate initial route atlas.",
200
+ destructive: true,
201
+ requires_approval: true,
202
+ estimated_risk: "medium",
203
+ status: "pending",
204
+ });
205
+ const filesToCreate = steps.filter((s) => s.action === "create").length;
206
+ const filesToMove = steps.filter((s) => s.action === "move").length;
207
+ const filesToModify = steps.filter((s) => s.action === "modify" || s.action === "append").length;
208
+ return {
209
+ plan_id: `setup-${now.toISOString().replaceAll(":", "-")}`,
210
+ generated_at: now.toISOString(),
211
+ repo_state: "new",
212
+ approved: false,
213
+ approved_at: null,
214
+ dry_run: false,
215
+ steps,
216
+ impact_summary: {
217
+ files_to_create: filesToCreate,
218
+ files_to_move: filesToMove,
219
+ files_to_modify: filesToModify,
220
+ instruction_files_affected: 0,
221
+ smartdocs_candidates_moved: candidates.length,
222
+ cognition_files_to_generate: 0,
223
+ },
224
+ };
225
+ }
226
+ function renderSetupPlanMarkdown(record, plan) {
227
+ const a = record.answers;
228
+ const scaffoldSteps = plan.steps.filter((s) => s.category === "scaffold" && s.action === "create");
229
+ const scaffoldPaths = scaffoldSteps.map((s) => s.dest_path).join(", ");
230
+ const lines = [
231
+ "# Setup Plan",
232
+ "",
233
+ `**Project purpose:** ${a.project_purpose ?? "(not specified)"}`,
234
+ "",
235
+ `**Source roots:** ${(a.source_roots ?? []).join(", ") || "(none)"}`,
236
+ "",
237
+ `**Languages / frameworks:** ${(a.languages ?? []).join(", ") || "(none)"}`,
238
+ "",
239
+ `**Canonical documentation folders:** ${(a.canonical_doc_folders ?? []).join(", ") || "(none)"}`,
240
+ "",
241
+ `**Never touch:** ${(a.never_touch ?? []).join(", ") || "(none)"}`,
242
+ "",
243
+ `**Providers by role:** ${Object.entries(a.providers_by_role ?? {}).map(([k, v]) => `${k}: ${v}`).join(", ") || "(none)"}`,
244
+ "",
245
+ "## Files to generate",
246
+ "",
247
+ `- GENESIS.md`,
248
+ `- polaris.config.json`,
249
+ `- POLARIS_RULES.md`,
250
+ ...scaffoldPaths.split(", ").map((p) => `- ${p.trim()}`),
251
+ "",
252
+ "## SmartDocs intake",
253
+ "",
254
+ `Markdown files in canonical documentation folders will be migrated to smartdocs/raw/ (${plan.impact_summary.smartdocs_candidates_moved} candidates).`,
255
+ "",
256
+ `**Operations:** ${plan.steps.length} steps (${plan.impact_summary.files_to_create} create, ${plan.impact_summary.files_to_move} move, ${plan.impact_summary.files_to_modify} modify).`,
257
+ ];
258
+ return lines.join("\n");
259
+ }
260
+ function buildGenesisContent(record) {
261
+ const a = record.answers;
262
+ return [
263
+ "# Genesis",
264
+ "",
265
+ "> Initial project genesis generated by Polaris setup.",
266
+ "",
267
+ "## Purpose",
268
+ "",
269
+ a.project_purpose ?? "",
270
+ "",
271
+ "## Source Roots",
272
+ "",
273
+ ...(a.source_roots ?? []).length > 0
274
+ ? (a.source_roots ?? []).map((root) => `- ${root}`)
275
+ : ["- (none)"],
276
+ "",
277
+ "## Languages / Frameworks",
278
+ "",
279
+ ...(a.languages ?? []).length > 0
280
+ ? (a.languages ?? []).map((lang) => `- ${lang}`)
281
+ : ["- (none)"],
282
+ "",
283
+ "## Canonical Documentation Folders",
284
+ "",
285
+ ...(a.canonical_doc_folders ?? []).length > 0
286
+ ? (a.canonical_doc_folders ?? []).map((folder) => `- ${folder}`)
287
+ : ["- (none)"],
288
+ "",
289
+ "## Governance",
290
+ "",
291
+ "- POLARIS_RULES.md — Polaris routing and governance rules",
292
+ "- POLARIS.md — operational guidance",
293
+ "- SUMMARY.md — informational context",
294
+ "",
295
+ ].join("\n");
296
+ }
297
+ function writeGenesis(repoRoot, record) {
298
+ const genesisPath = (0, node_path_1.join)(repoRoot, "GENESIS.md");
299
+ if ((0, node_fs_1.existsSync)(genesisPath))
300
+ return;
301
+ (0, node_fs_1.writeFileSync)(genesisPath, buildGenesisContent(record), "utf-8");
302
+ }
303
+ function writeSetupConfig(repoRoot, record, detectedProviders, detectedRepoAnalysis) {
304
+ const configPath = (0, node_path_1.join)(repoRoot, "polaris.config.json");
305
+ let existing = {};
306
+ if ((0, node_fs_1.existsSync)(configPath)) {
307
+ try {
308
+ existing = JSON.parse((0, node_fs_1.readFileSync)(configPath, "utf-8"));
309
+ }
310
+ catch {
311
+ existing = {};
312
+ }
313
+ }
314
+ const updated = buildSetupConfig(existing, record, detectedProviders, detectedRepoAnalysis);
315
+ (0, node_fs_1.writeFileSync)(configPath, `${JSON.stringify(updated, null, 2)}\n`, "utf-8");
316
+ }
317
+ /**
318
+ * Generate setup artifacts from a completed interview record.
319
+ *
320
+ * - Builds a preview plan from the interview answers.
321
+ * - In dry-run mode, prints the plan and returns without writing.
322
+ * - With --yes, bypasses the approval prompt.
323
+ * - Otherwise prompts the operator for approval.
324
+ * - On approval, writes GENESIS.md, polaris.config.json, POLARIS_RULES.md,
325
+ * root route surfaces, migrates SmartDocs candidates, and runs the map index.
326
+ */
327
+ async function generateSetupArtifacts(record, options = {}) {
328
+ const repoRoot = options.repoRoot ?? (0, node_path_1.resolve)(process.cwd());
329
+ const dryRun = options.dryRun ?? false;
330
+ const yes = options.yes ?? false;
331
+ const now = options.now ?? new Date();
332
+ const stdout = options.stdout ?? process.stdout;
333
+ const stdin = options.stdin ?? process.stdin;
334
+ const detectedProviders = options.detectedProviders ?? [];
335
+ const detectedRepoAnalysis = options.detectedRepoAnalysis ?? [];
336
+ const scaffoldFn = options.scaffoldRootSurfaces ?? adopt_workspace_js_1.scaffoldRootSurfaces;
337
+ const rulesFn = options.generatePolarisRules ?? adopt_rules_js_1.generatePolarisRules;
338
+ const migrateFn = options.migrateSmartDocs ?? adopt_smartdocs_js_1.migrateSmartDocs;
339
+ const mapFn = options.runMapIndex ?? index_js_1.runMapIndex;
340
+ const promptFn = options.promptApproval ?? adopt_approve_js_1.promptApproval;
341
+ const plan = buildSetupPlan(record, repoRoot, now);
342
+ const markdown = renderSetupPlanMarkdown(record, plan);
343
+ if (dryRun) {
344
+ stdout.write(`${markdown}\n\n`);
345
+ stdout.write("Setup dry run: no files written.\n");
346
+ return;
347
+ }
348
+ let approved = false;
349
+ if (yes) {
350
+ stdout.write(`${markdown}\n\n`);
351
+ stdout.write("Setup approval bypassed via --yes.\n");
352
+ approved = true;
353
+ }
354
+ else {
355
+ approved = await promptFn(plan, {
356
+ repoRoot,
357
+ stdin,
358
+ stdout,
359
+ now,
360
+ markdown,
361
+ persist: (_plan, root, approvalNow) => {
362
+ const approvedRecord = (0, store_js_1.markApproved)(record, approvalNow);
363
+ (0, store_js_1.saveInterview)(root, approvedRecord);
364
+ },
365
+ });
366
+ }
367
+ if (!approved) {
368
+ stdout.write("Setup aborted: explicit approval required.\n");
369
+ return;
370
+ }
371
+ writeGenesis(repoRoot, record);
372
+ writeSetupConfig(repoRoot, record, detectedProviders, detectedRepoAnalysis);
373
+ await rulesFn(repoRoot, buildSetupInventory(record), {
374
+ workspaceDir: (0, node_path_1.resolve)(__dirname, "../../workspace"),
375
+ });
376
+ scaffoldFn(repoRoot);
377
+ await migrateFn(plan, repoRoot);
378
+ mapFn(repoRoot, false, false, { seedCognition: false, skipThreshold: true });
379
+ stdout.write("Setup generation complete.\n");
380
+ }
@@ -0,0 +1,138 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.buildCheckpointReport = buildCheckpointReport;
4
+ exports.writeCheckpointReport = writeCheckpointReport;
5
+ exports.printCheckpointReport = printCheckpointReport;
6
+ const node_fs_1 = require("node:fs");
7
+ const node_path_1 = require("node:path");
8
+ const node_child_process_1 = require("node:child_process");
9
+ function getGeneratedFiles(repoRoot) {
10
+ const files = [];
11
+ const candidates = [
12
+ "GENESIS.md",
13
+ "polaris.config.json",
14
+ "POLARIS_RULES.md",
15
+ "POLARIS.md",
16
+ "SUMMARY.md",
17
+ "CLAUDE.md",
18
+ "AGENTS.md",
19
+ ".github/copilot-instructions.md",
20
+ ".polaris/map/index.json",
21
+ ];
22
+ for (const file of candidates) {
23
+ if ((0, node_fs_1.existsSync)((0, node_path_1.join)(repoRoot, file))) {
24
+ files.push(file);
25
+ }
26
+ }
27
+ // Check for SmartDocs migrated files
28
+ const smartdocsDir = (0, node_path_1.join)(repoRoot, "smartdocs", "raw");
29
+ if ((0, node_fs_1.existsSync)(smartdocsDir)) {
30
+ try {
31
+ const smartdocsFiles = (0, node_child_process_1.execFileSync)("find", ["smartdocs/raw", "-name", "*.md", "-type", "f"], {
32
+ cwd: repoRoot,
33
+ encoding: "utf-8",
34
+ })
35
+ .trim()
36
+ .split("\n")
37
+ .filter(Boolean);
38
+ files.push(...smartdocsFiles);
39
+ }
40
+ catch {
41
+ // find command failed, ignore
42
+ }
43
+ }
44
+ return files.sort();
45
+ }
46
+ function validateGeneratedFiles(repoRoot, files) {
47
+ const results = {};
48
+ for (const file of files) {
49
+ try {
50
+ const filePath = (0, node_path_1.join)(repoRoot, file);
51
+ const content = (0, node_fs_1.readFileSync)(filePath, "utf-8");
52
+ if (content.trim().length === 0) {
53
+ results[file] = { passed: false, message: "File is empty" };
54
+ }
55
+ else {
56
+ results[file] = { passed: true };
57
+ }
58
+ }
59
+ catch (error) {
60
+ results[file] = {
61
+ passed: false,
62
+ message: error instanceof Error ? error.message : "Unknown error"
63
+ };
64
+ }
65
+ }
66
+ return results;
67
+ }
68
+ function buildNextSteps(record) {
69
+ const steps = [];
70
+ steps.push("Review generated files in your repository");
71
+ steps.push("Run `polaris status` to verify setup");
72
+ if (record.answers.canonical_doc_folders && record.answers.canonical_doc_folders.length > 0) {
73
+ steps.push("Check migrated documentation in smartdocs/raw/");
74
+ }
75
+ steps.push("Start using Polaris: run `polaris analyze` to explore your codebase");
76
+ return steps;
77
+ }
78
+ function buildCheckpointReport(options) {
79
+ const { repoRoot, record, now } = options;
80
+ const timestamp = (now ?? new Date()).toISOString();
81
+ const createdFiles = getGeneratedFiles(repoRoot);
82
+ const validationResults = validateGeneratedFiles(repoRoot, createdFiles);
83
+ const nextSteps = buildNextSteps(record);
84
+ return {
85
+ timestamp,
86
+ interviewStatus: record.status,
87
+ createdFiles,
88
+ validationResults,
89
+ nextSteps,
90
+ };
91
+ }
92
+ function writeCheckpointReport(repoRoot, report) {
93
+ // Safe timestamp: replace ":" with "-" and "." with "-"
94
+ // e.g. "2026-06-09T12:00:00.000Z" → "checkpoint-report-2026-06-09T12-00-00-000Z.json"
95
+ const safeTimestamp = report.timestamp.replace(/:/g, "-").replace(/\./g, "-");
96
+ const fileName = `checkpoint-report-${safeTimestamp}.json`;
97
+ const runsDir = (0, node_path_1.join)(repoRoot, ".polaris", "runs");
98
+ if (!(0, node_fs_1.existsSync)(runsDir)) {
99
+ (0, node_fs_1.mkdirSync)(runsDir, { recursive: true });
100
+ }
101
+ const reportPath = (0, node_path_1.join)(runsDir, fileName);
102
+ (0, node_fs_1.writeFileSync)(reportPath, JSON.stringify(report, null, 2), "utf-8");
103
+ }
104
+ function printCheckpointReport(report) {
105
+ // Print structured summary to stdout
106
+ console.log("Setup Checkpoint Report:");
107
+ console.log(` Timestamp: ${report.timestamp}`);
108
+ console.log(` Interview Status: ${report.interviewStatus}`);
109
+ console.log("");
110
+ console.log("Generated Files:");
111
+ if (report.createdFiles.length === 0) {
112
+ console.log(" (none)");
113
+ }
114
+ else {
115
+ for (const file of report.createdFiles) {
116
+ const validation = report.validationResults[file];
117
+ const status = validation.passed ? "✓" : "✗";
118
+ console.log(` ${status} ${file}`);
119
+ if (validation.message) {
120
+ console.log(` ${validation.message}`);
121
+ }
122
+ }
123
+ }
124
+ console.log("");
125
+ const failedValidations = Object.entries(report.validationResults).filter(([_, result]) => !result.passed);
126
+ if (failedValidations.length > 0) {
127
+ console.log("Validation Failures:");
128
+ for (const [file, result] of failedValidations) {
129
+ console.log(` ${file}: ${result.message}`);
130
+ }
131
+ console.log("");
132
+ }
133
+ console.log("Next Steps:");
134
+ for (const step of report.nextSteps) {
135
+ console.log(` • ${step}`);
136
+ }
137
+ console.log("");
138
+ }