@stackbilt/cli 0.3.2 → 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.
Files changed (36) hide show
  1. package/README.md +18 -5
  2. package/dist/commands/adf-bundle.d.ts +8 -0
  3. package/dist/commands/adf-bundle.d.ts.map +1 -0
  4. package/dist/commands/adf-bundle.js +169 -0
  5. package/dist/commands/adf-bundle.js.map +1 -0
  6. package/dist/commands/adf-evidence.d.ts +8 -0
  7. package/dist/commands/adf-evidence.d.ts.map +1 -0
  8. package/dist/commands/adf-evidence.js +273 -0
  9. package/dist/commands/adf-evidence.js.map +1 -0
  10. package/dist/commands/adf-migrate.d.ts +10 -0
  11. package/dist/commands/adf-migrate.d.ts.map +1 -0
  12. package/dist/commands/adf-migrate.js +383 -0
  13. package/dist/commands/adf-migrate.js.map +1 -0
  14. package/dist/commands/adf-sync.d.ts +10 -0
  15. package/dist/commands/adf-sync.d.ts.map +1 -0
  16. package/dist/commands/adf-sync.js +213 -0
  17. package/dist/commands/adf-sync.js.map +1 -0
  18. package/dist/commands/adf.d.ts +7 -1
  19. package/dist/commands/adf.d.ts.map +1 -1
  20. package/dist/commands/adf.js +109 -472
  21. package/dist/commands/adf.js.map +1 -1
  22. package/dist/commands/bootstrap.d.ts +9 -0
  23. package/dist/commands/bootstrap.d.ts.map +1 -0
  24. package/dist/commands/bootstrap.js +660 -0
  25. package/dist/commands/bootstrap.js.map +1 -0
  26. package/dist/commands/hook.d.ts.map +1 -1
  27. package/dist/commands/hook.js +69 -10
  28. package/dist/commands/hook.js.map +1 -1
  29. package/dist/commands/setup.d.ts +103 -0
  30. package/dist/commands/setup.d.ts.map +1 -1
  31. package/dist/commands/setup.js +7 -0
  32. package/dist/commands/setup.js.map +1 -1
  33. package/dist/index.d.ts.map +1 -1
  34. package/dist/index.js +9 -2
  35. package/dist/index.js.map +1 -1
  36. package/package.json +8 -8
@@ -0,0 +1,660 @@
1
+ "use strict";
2
+ /**
3
+ * charter bootstrap
4
+ *
5
+ * One-command repo onboarding: detect + setup + ADF init + install + doctor.
6
+ * Replaces the multi-step manual process with a single orchestrated flow.
7
+ */
8
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
9
+ if (k2 === undefined) k2 = k;
10
+ var desc = Object.getOwnPropertyDescriptor(m, k);
11
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
12
+ desc = { enumerable: true, get: function() { return m[k]; } };
13
+ }
14
+ Object.defineProperty(o, k2, desc);
15
+ }) : (function(o, m, k, k2) {
16
+ if (k2 === undefined) k2 = k;
17
+ o[k2] = m[k];
18
+ }));
19
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
20
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
21
+ }) : function(o, v) {
22
+ o["default"] = v;
23
+ });
24
+ var __importStar = (this && this.__importStar) || (function () {
25
+ var ownKeys = function(o) {
26
+ ownKeys = Object.getOwnPropertyNames || function (o) {
27
+ var ar = [];
28
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
29
+ return ar;
30
+ };
31
+ return ownKeys(o);
32
+ };
33
+ return function (mod) {
34
+ if (mod && mod.__esModule) return mod;
35
+ var result = {};
36
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
37
+ __setModuleDefault(result, mod);
38
+ return result;
39
+ };
40
+ })();
41
+ Object.defineProperty(exports, "__esModule", { value: true });
42
+ exports.bootstrapCommand = bootstrapCommand;
43
+ const fs = __importStar(require("node:fs"));
44
+ const path = __importStar(require("node:path"));
45
+ const crypto = __importStar(require("node:crypto"));
46
+ const node_child_process_1 = require("node:child_process");
47
+ const index_1 = require("../index");
48
+ const init_1 = require("./init");
49
+ const setup_1 = require("./setup");
50
+ const adf_1 = require("./adf");
51
+ const config_1 = require("../config");
52
+ const adf_2 = require("@stackbilt/adf");
53
+ // ============================================================================
54
+ // Command Entry
55
+ // ============================================================================
56
+ async function bootstrapCommand(options, args) {
57
+ const ciTarget = getFlag(args, '--ci');
58
+ const presetFlag = getFlag(args, '--preset');
59
+ const skipInstall = args.includes('--skip-install');
60
+ const skipDoctor = args.includes('--skip-doctor');
61
+ const force = options.yes;
62
+ if (ciTarget && ciTarget !== 'github') {
63
+ throw new index_1.CLIError(`Unsupported CI target: ${ciTarget}. Supported: github`);
64
+ }
65
+ if (presetFlag && !isValidPreset(presetFlag)) {
66
+ throw new index_1.CLIError(`Invalid --preset value: ${presetFlag}. Use worker|frontend|backend|fullstack.`);
67
+ }
68
+ const result = {
69
+ status: 'success',
70
+ steps: [],
71
+ nextSteps: [],
72
+ };
73
+ let warnings = 0;
74
+ // ========================================================================
75
+ // Phase 1: Detect
76
+ // ========================================================================
77
+ const detectResult = runDetectPhase(options, presetFlag);
78
+ result.steps.push(detectResult.step);
79
+ if (detectResult.step.status === 'fail')
80
+ warnings++;
81
+ const selectedPreset = detectResult.selectedPreset;
82
+ const detection = detectResult.detection;
83
+ const contexts = detectResult.contexts;
84
+ const packageManager = detectResult.packageManager;
85
+ if (options.format === 'text') {
86
+ console.log('[1/5] Detecting stack...');
87
+ console.log(` Stack: ${selectedPreset} (${detection.confidence} confidence)`);
88
+ console.log(` Monorepo: ${detection.monorepo ? 'yes' : 'no'}${detection.monorepo && detection.signals.hasPnpm ? ' (pnpm workspace)' : ''}`);
89
+ if (detection.warnings.length > 0) {
90
+ for (const w of detection.warnings) {
91
+ console.log(` Warning: ${w}`);
92
+ }
93
+ }
94
+ console.log('');
95
+ }
96
+ // ========================================================================
97
+ // Phase 2: Setup
98
+ // ========================================================================
99
+ const setupResult = runSetupPhase(options, selectedPreset, detection, contexts, ciTarget, packageManager, force);
100
+ result.steps.push(setupResult.step);
101
+ if (setupResult.step.status === 'fail')
102
+ warnings++;
103
+ if (options.format === 'text') {
104
+ console.log('[2/5] Setting up governance...');
105
+ for (const f of (setupResult.step.details.created || [])) {
106
+ console.log(` Created ${f}`);
107
+ }
108
+ for (const f of (setupResult.step.details.updated || [])) {
109
+ console.log(` Updated ${f}`);
110
+ }
111
+ console.log('');
112
+ }
113
+ // ========================================================================
114
+ // Phase 3: ADF Init
115
+ // ========================================================================
116
+ const adfResult = runAdfInitPhase(options, force);
117
+ result.steps.push(adfResult.step);
118
+ if (adfResult.step.status === 'fail')
119
+ warnings++;
120
+ if (options.format === 'text') {
121
+ console.log('[3/5] Initializing ADF context...');
122
+ for (const f of (adfResult.step.details.files || [])) {
123
+ console.log(` Created ${f}`);
124
+ }
125
+ for (const f of (adfResult.step.details.pointers || [])) {
126
+ console.log(` Generated ${f}`);
127
+ }
128
+ console.log('');
129
+ }
130
+ // ========================================================================
131
+ // Phase 4: Install
132
+ // ========================================================================
133
+ const installResult = runInstallPhase(options, skipInstall);
134
+ result.steps.push(installResult.step);
135
+ if (installResult.step.status === 'fail')
136
+ warnings++;
137
+ if (options.format === 'text') {
138
+ console.log('[4/5] Installing dependencies...');
139
+ if (skipInstall) {
140
+ console.log(' Skipped (--skip-install)');
141
+ }
142
+ else {
143
+ console.log(` Detected: ${installResult.step.details.packageManager}`);
144
+ console.log(` Running: ${installResult.step.details.command}`);
145
+ if (installResult.step.status === 'pass') {
146
+ console.log(' Done');
147
+ }
148
+ else {
149
+ console.log(` Failed: ${installResult.step.details.error}`);
150
+ console.log(' (non-fatal: run install manually)');
151
+ }
152
+ }
153
+ console.log('');
154
+ }
155
+ // ========================================================================
156
+ // Phase 5: Doctor
157
+ // ========================================================================
158
+ const doctorResult = runDoctorPhase(options, skipDoctor);
159
+ result.steps.push(doctorResult.step);
160
+ if (doctorResult.step.status === 'fail')
161
+ warnings++;
162
+ if (options.format === 'text') {
163
+ console.log('[5/5] Running health check...');
164
+ if (skipDoctor) {
165
+ console.log(' Skipped (--skip-doctor)');
166
+ }
167
+ else {
168
+ const checks = doctorResult.step.details.checks || [];
169
+ for (const check of checks) {
170
+ const icon = check.status === 'PASS' ? '[ok]' : '[warn]';
171
+ console.log(` ${icon} ${check.name}`);
172
+ }
173
+ }
174
+ console.log('');
175
+ }
176
+ // ========================================================================
177
+ // Summary
178
+ // ========================================================================
179
+ const failCount = result.steps.filter(s => s.status === 'fail').length;
180
+ result.status = failCount === 0 ? 'success' : failCount < result.steps.length ? 'partial' : 'failure';
181
+ // Build next steps
182
+ result.nextSteps.push({
183
+ cmd: 'Review .charter/patterns/ and customize for your stack',
184
+ required: false,
185
+ reason: 'Customize blessed stack patterns',
186
+ });
187
+ result.nextSteps.push({
188
+ cmd: 'Add project-specific rules to .ai/core.adf',
189
+ required: false,
190
+ reason: 'Add project-specific ADF rules',
191
+ });
192
+ result.nextSteps.push({
193
+ cmd: 'git add .charter .ai CLAUDE.md .cursorrules agents.md && git commit -m "chore: bootstrap charter governance"',
194
+ required: false,
195
+ reason: 'Commit governance baseline',
196
+ });
197
+ if (options.format === 'json') {
198
+ console.log(JSON.stringify(result, null, 2));
199
+ }
200
+ else {
201
+ console.log(`Bootstrap complete. ${warnings} warning${warnings === 1 ? '' : 's'}.`);
202
+ console.log('');
203
+ console.log('Next steps:');
204
+ result.nextSteps.forEach((step, i) => {
205
+ console.log(` ${i + 1}. ${step.cmd}`);
206
+ });
207
+ }
208
+ return index_1.EXIT_CODE.SUCCESS;
209
+ }
210
+ // ============================================================================
211
+ // Phase 1: Detect
212
+ // ============================================================================
213
+ function runDetectPhase(_options, presetFlag) {
214
+ const warnings = [];
215
+ try {
216
+ const contexts = (0, setup_1.loadPackageContexts)();
217
+ const detection = (0, setup_1.detectStack)(contexts);
218
+ const packageManager = (0, setup_1.detectPackageManager)(contexts);
219
+ const selectedPreset = isValidPreset(presetFlag) ? presetFlag : detection.suggestedPreset;
220
+ warnings.push(...detection.warnings);
221
+ return {
222
+ step: {
223
+ name: 'detect',
224
+ status: 'pass',
225
+ details: {
226
+ stack: selectedPreset,
227
+ confidence: detection.confidence,
228
+ monorepo: detection.monorepo,
229
+ runtime: detection.runtime,
230
+ frameworks: detection.frameworks,
231
+ },
232
+ warnings,
233
+ },
234
+ selectedPreset,
235
+ detection,
236
+ contexts,
237
+ packageManager,
238
+ };
239
+ }
240
+ catch (err) {
241
+ const msg = err instanceof Error ? err.message : String(err);
242
+ warnings.push(`Detection failed: ${msg}`);
243
+ const emptyContexts = loadPackageContextsSafe();
244
+ return {
245
+ step: {
246
+ name: 'detect',
247
+ status: 'fail',
248
+ details: { error: msg },
249
+ warnings,
250
+ },
251
+ selectedPreset: isValidPreset(presetFlag) ? presetFlag : 'fullstack',
252
+ detection: {
253
+ runtime: [],
254
+ frameworks: [],
255
+ state: [],
256
+ sources: [],
257
+ agentStandards: [],
258
+ monorepo: false,
259
+ signals: {
260
+ hasFrontend: false,
261
+ hasBackend: false,
262
+ hasWorker: false,
263
+ hasCloudflare: false,
264
+ hasHono: false,
265
+ hasReact: false,
266
+ hasVite: false,
267
+ hasPnpm: false,
268
+ },
269
+ mixedStack: false,
270
+ confidence: 'LOW',
271
+ suggestedPreset: 'fullstack',
272
+ warnings: [],
273
+ },
274
+ contexts: emptyContexts,
275
+ packageManager: 'npm',
276
+ };
277
+ }
278
+ }
279
+ function loadPackageContextsSafe() {
280
+ try {
281
+ return (0, setup_1.loadPackageContexts)();
282
+ }
283
+ catch {
284
+ return [];
285
+ }
286
+ }
287
+ // ============================================================================
288
+ // Phase 2: Setup
289
+ // ============================================================================
290
+ function runSetupPhase(options, selectedPreset, detection, contexts, ciTarget, packageManager, force) {
291
+ const warnings = [];
292
+ const created = [];
293
+ const updated = [];
294
+ try {
295
+ // Initialize .charter/ directory
296
+ const initResult = (0, init_1.initializeCharter)(options.configPath, force, {
297
+ preset: selectedPreset,
298
+ projectName: (0, setup_1.inferProjectName)(contexts),
299
+ features: {
300
+ cloudflare: detection.signals.hasCloudflare,
301
+ hono: detection.signals.hasHono,
302
+ react: detection.signals.hasReact,
303
+ vite: detection.signals.hasVite,
304
+ },
305
+ });
306
+ if (initResult.created) {
307
+ for (const f of initResult.files) {
308
+ created.push(path.join(options.configPath, f));
309
+ }
310
+ }
311
+ // Generate CI workflow if requested
312
+ if (ciTarget === 'github') {
313
+ const workflowPath = path.join('.github', 'workflows', 'charter-governance.yml');
314
+ const workflowWrite = (0, setup_1.applyManagedFile)(workflowPath, (0, setup_1.getGithubWorkflow)(packageManager), true);
315
+ if (workflowWrite.created) {
316
+ created.push(workflowPath);
317
+ }
318
+ else if (workflowWrite.updated) {
319
+ updated.push(workflowPath);
320
+ }
321
+ }
322
+ // Sync package.json scripts + devDependency
323
+ const manifestApplied = (0, setup_1.syncPackageManifest)(selectedPreset, true, true);
324
+ if (manifestApplied.legacy.scripts.updated) {
325
+ updated.push('package.json (scripts)');
326
+ }
327
+ if (manifestApplied.legacy.dependencies.updated) {
328
+ updated.push('package.json (devDependencies)');
329
+ }
330
+ return {
331
+ step: {
332
+ name: 'setup',
333
+ status: 'pass',
334
+ details: { created, updated },
335
+ warnings,
336
+ },
337
+ };
338
+ }
339
+ catch (err) {
340
+ const msg = err instanceof Error ? err.message : String(err);
341
+ warnings.push(`Setup failed: ${msg}`);
342
+ return {
343
+ step: {
344
+ name: 'setup',
345
+ status: 'fail',
346
+ details: { created, updated, error: msg },
347
+ warnings,
348
+ },
349
+ };
350
+ }
351
+ }
352
+ // ============================================================================
353
+ // Phase 3: ADF Init
354
+ // ============================================================================
355
+ function runAdfInitPhase(options, force) {
356
+ const warnings = [];
357
+ const files = [];
358
+ const pointers = [];
359
+ try {
360
+ const aiDir = '.ai';
361
+ const manifestPath = path.join(aiDir, 'manifest.adf');
362
+ // Create .ai/ scaffolds
363
+ const alreadyExists = fs.existsSync(manifestPath);
364
+ const hasCustomContent = alreadyExists && hasCustomAdfContent(aiDir);
365
+ if (!alreadyExists) {
366
+ // Greenfield: write scaffolds
367
+ fs.mkdirSync(aiDir, { recursive: true });
368
+ fs.writeFileSync(path.join(aiDir, 'manifest.adf'), adf_1.MANIFEST_SCAFFOLD);
369
+ fs.writeFileSync(path.join(aiDir, 'core.adf'), adf_1.CORE_SCAFFOLD);
370
+ fs.writeFileSync(path.join(aiDir, 'state.adf'), adf_1.STATE_SCAFFOLD);
371
+ files.push('.ai/manifest.adf', '.ai/core.adf', '.ai/state.adf');
372
+ // Write .adf.lock
373
+ const lockData = {};
374
+ for (const mod of ['core.adf', 'state.adf']) {
375
+ const content = fs.readFileSync(path.join(aiDir, mod), 'utf-8');
376
+ lockData[mod] = hashContent(content);
377
+ }
378
+ fs.writeFileSync(path.join(aiDir, '.adf.lock'), JSON.stringify(lockData, null, 2) + '\n');
379
+ files.push('.ai/.adf.lock');
380
+ }
381
+ else if (hasCustomContent && !force) {
382
+ // Custom ADF content exists — don't overwrite, suggest migrate
383
+ warnings.push('.ai/ contains custom ADF content; skipping scaffold overwrite');
384
+ warnings.push("Run 'charter adf migrate' to consolidate agent configs into ADF");
385
+ }
386
+ else if (force) {
387
+ // Force overwrite
388
+ fs.mkdirSync(aiDir, { recursive: true });
389
+ fs.writeFileSync(path.join(aiDir, 'manifest.adf'), adf_1.MANIFEST_SCAFFOLD);
390
+ fs.writeFileSync(path.join(aiDir, 'core.adf'), adf_1.CORE_SCAFFOLD);
391
+ fs.writeFileSync(path.join(aiDir, 'state.adf'), adf_1.STATE_SCAFFOLD);
392
+ files.push('.ai/manifest.adf', '.ai/core.adf', '.ai/state.adf');
393
+ const lockData = {};
394
+ for (const mod of ['core.adf', 'state.adf']) {
395
+ const content = fs.readFileSync(path.join(aiDir, mod), 'utf-8');
396
+ lockData[mod] = hashContent(content);
397
+ }
398
+ fs.writeFileSync(path.join(aiDir, '.adf.lock'), JSON.stringify(lockData, null, 2) + '\n');
399
+ files.push('.ai/.adf.lock');
400
+ }
401
+ else {
402
+ warnings.push('.ai/ already exists; skipping scaffold (use --yes to overwrite)');
403
+ }
404
+ // Generate thin pointer files
405
+ const pointerFiles = [
406
+ { name: 'CLAUDE.md', content: adf_1.POINTER_CLAUDE_MD, label: 'CLAUDE.md (thin pointer)' },
407
+ { name: '.cursorrules', content: adf_1.POINTER_CURSORRULES, label: '.cursorrules (thin pointer)' },
408
+ { name: 'agents.md', content: adf_1.POINTER_AGENTS_MD, label: 'agents.md (thin pointer)' },
409
+ ];
410
+ for (const pf of pointerFiles) {
411
+ const pointerPath = path.resolve(pf.name);
412
+ const exists = fs.existsSync(pointerPath);
413
+ if (!exists) {
414
+ fs.writeFileSync(pointerPath, pf.content);
415
+ pointers.push(pf.label);
416
+ }
417
+ else if (exists && !isAlreadyThinPointer(pointerPath)) {
418
+ // File has custom content — don't overwrite, suggest migrate
419
+ warnings.push(`${pf.name} has custom content; skipping pointer (use 'charter adf migrate' first)`);
420
+ }
421
+ else if (force) {
422
+ fs.writeFileSync(pointerPath, pf.content);
423
+ pointers.push(pf.label);
424
+ }
425
+ else {
426
+ warnings.push(`${pf.name} already exists; skipping (use --yes to overwrite)`);
427
+ }
428
+ }
429
+ return {
430
+ step: {
431
+ name: 'adf-init',
432
+ status: 'pass',
433
+ details: { files, pointers },
434
+ warnings,
435
+ },
436
+ };
437
+ }
438
+ catch (err) {
439
+ const msg = err instanceof Error ? err.message : String(err);
440
+ warnings.push(`ADF init failed: ${msg}`);
441
+ return {
442
+ step: {
443
+ name: 'adf-init',
444
+ status: 'fail',
445
+ details: { files, pointers, error: msg },
446
+ warnings,
447
+ },
448
+ };
449
+ }
450
+ }
451
+ // ============================================================================
452
+ // Phase 4: Install
453
+ // ============================================================================
454
+ function runInstallPhase(_options, skipInstall) {
455
+ const warnings = [];
456
+ if (skipInstall) {
457
+ return {
458
+ step: {
459
+ name: 'install',
460
+ status: 'skip',
461
+ details: { skipped: true, reason: '--skip-install' },
462
+ warnings,
463
+ },
464
+ };
465
+ }
466
+ // Detect package manager from lockfiles
467
+ const pm = detectPackageManagerFromLockfiles();
468
+ const command = `${pm} install`;
469
+ try {
470
+ (0, node_child_process_1.execSync)(command, {
471
+ stdio: 'pipe',
472
+ env: { ...process.env, CI: 'true' },
473
+ timeout: 120_000,
474
+ });
475
+ return {
476
+ step: {
477
+ name: 'install',
478
+ status: 'pass',
479
+ details: { packageManager: pm, command },
480
+ warnings,
481
+ },
482
+ };
483
+ }
484
+ catch (err) {
485
+ const msg = err instanceof Error ? err.message : String(err);
486
+ warnings.push(`Install failed: ${msg}`);
487
+ return {
488
+ step: {
489
+ name: 'install',
490
+ status: 'fail',
491
+ details: { packageManager: pm, command, error: msg },
492
+ warnings,
493
+ },
494
+ };
495
+ }
496
+ }
497
+ function detectPackageManagerFromLockfiles() {
498
+ if (fs.existsSync(path.resolve('pnpm-lock.yaml')))
499
+ return 'pnpm';
500
+ if (fs.existsSync(path.resolve('yarn.lock')))
501
+ return 'yarn';
502
+ if (fs.existsSync(path.resolve('package-lock.json')))
503
+ return 'npm';
504
+ return 'npm';
505
+ }
506
+ // ============================================================================
507
+ // Phase 5: Doctor
508
+ // ============================================================================
509
+ function runDoctorPhase(options, skipDoctor) {
510
+ const warnings = [];
511
+ if (skipDoctor) {
512
+ return {
513
+ step: {
514
+ name: 'doctor',
515
+ status: 'skip',
516
+ details: { skipped: true, reason: '--skip-doctor' },
517
+ warnings,
518
+ },
519
+ };
520
+ }
521
+ const checks = [];
522
+ try {
523
+ // Git repository check
524
+ let inGitRepo = false;
525
+ try {
526
+ (0, node_child_process_1.execSync)('git rev-parse --is-inside-work-tree', { stdio: 'ignore' });
527
+ inGitRepo = true;
528
+ }
529
+ catch {
530
+ // not in a git repo
531
+ }
532
+ checks.push({
533
+ name: 'Git repository',
534
+ status: inGitRepo ? 'PASS' : 'WARN',
535
+ details: inGitRepo ? 'Repository detected.' : 'Not inside a git repository.',
536
+ });
537
+ // config.json check
538
+ const configFile = path.join(options.configPath, 'config.json');
539
+ const hasConfig = fs.existsSync(configFile);
540
+ checks.push({
541
+ name: 'config.json',
542
+ status: hasConfig ? 'PASS' : 'WARN',
543
+ details: hasConfig ? `${configFile} exists.` : `${configFile} not found.`,
544
+ });
545
+ // Patterns check
546
+ const patterns = (0, config_1.loadPatterns)(options.configPath);
547
+ checks.push({
548
+ name: 'Patterns loaded',
549
+ status: patterns.length > 0 ? 'PASS' : 'WARN',
550
+ details: patterns.length > 0
551
+ ? `${patterns.length} pattern(s) loaded.`
552
+ : 'No patterns found.',
553
+ });
554
+ // ADF manifest check
555
+ const manifestPath = path.join('.ai', 'manifest.adf');
556
+ const hasManifest = fs.existsSync(manifestPath);
557
+ checks.push({
558
+ name: 'ADF manifest',
559
+ status: hasManifest ? 'PASS' : 'WARN',
560
+ details: hasManifest ? `${manifestPath} exists.` : `${manifestPath} not found.`,
561
+ });
562
+ // ADF sync lock check
563
+ if (hasManifest) {
564
+ try {
565
+ const manifestContent = fs.readFileSync(manifestPath, 'utf-8');
566
+ const manifestDoc = (0, adf_2.parseAdf)(manifestContent);
567
+ const manifest = (0, adf_2.parseManifest)(manifestDoc);
568
+ if (manifest.sync.length > 0) {
569
+ const lockFile = path.join('.ai', '.adf.lock');
570
+ const hasLock = fs.existsSync(lockFile);
571
+ checks.push({
572
+ name: 'ADF sync lock',
573
+ status: hasLock ? 'PASS' : 'WARN',
574
+ details: hasLock ? `${lockFile} exists.` : `${lockFile} not found.`,
575
+ });
576
+ }
577
+ }
578
+ catch {
579
+ // Manifest parse failed — just check for lock file presence
580
+ const lockFile = path.join('.ai', '.adf.lock');
581
+ const hasLock = fs.existsSync(lockFile);
582
+ checks.push({
583
+ name: 'ADF sync lock',
584
+ status: hasLock ? 'PASS' : 'WARN',
585
+ details: hasLock ? `${lockFile} exists.` : `${lockFile} not found.`,
586
+ });
587
+ }
588
+ }
589
+ const hasWarn = checks.some(c => c.status === 'WARN');
590
+ if (hasWarn) {
591
+ warnings.push('Some health checks returned warnings.');
592
+ }
593
+ return {
594
+ step: {
595
+ name: 'doctor',
596
+ status: hasWarn ? 'fail' : 'pass',
597
+ details: { checks },
598
+ warnings,
599
+ },
600
+ };
601
+ }
602
+ catch (err) {
603
+ const msg = err instanceof Error ? err.message : String(err);
604
+ warnings.push(`Doctor failed: ${msg}`);
605
+ return {
606
+ step: {
607
+ name: 'doctor',
608
+ status: 'fail',
609
+ details: { checks, error: msg },
610
+ warnings,
611
+ },
612
+ };
613
+ }
614
+ }
615
+ // ============================================================================
616
+ // Helpers
617
+ // ============================================================================
618
+ function getFlag(args, flag) {
619
+ const idx = args.indexOf(flag);
620
+ if (idx !== -1 && idx + 1 < args.length) {
621
+ return args[idx + 1];
622
+ }
623
+ return undefined;
624
+ }
625
+ function isValidPreset(value) {
626
+ return value === 'worker' || value === 'frontend' || value === 'backend' || value === 'fullstack';
627
+ }
628
+ function hashContent(content) {
629
+ return crypto.createHash('sha256').update(content).digest('hex').slice(0, 16);
630
+ }
631
+ /**
632
+ * Check if .ai/core.adf has content beyond the scaffold template.
633
+ */
634
+ function hasCustomAdfContent(aiDir) {
635
+ const coreAdfPath = path.join(aiDir, 'core.adf');
636
+ if (!fs.existsSync(coreAdfPath))
637
+ return false;
638
+ try {
639
+ const content = fs.readFileSync(coreAdfPath, 'utf-8');
640
+ // Check if the file has been modified from default scaffold
641
+ // A custom file will have different content than the CORE_SCAFFOLD
642
+ return content.trim() !== adf_1.CORE_SCAFFOLD.trim();
643
+ }
644
+ catch {
645
+ return false;
646
+ }
647
+ }
648
+ /**
649
+ * Check if a file is already a thin ADF pointer.
650
+ */
651
+ function isAlreadyThinPointer(filePath) {
652
+ try {
653
+ const content = fs.readFileSync(filePath, 'utf-8');
654
+ return content.includes('Do not duplicate ADF rules here');
655
+ }
656
+ catch {
657
+ return false;
658
+ }
659
+ }
660
+ //# sourceMappingURL=bootstrap.js.map