@stackbilt/cli 0.3.3 → 0.4.1
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/README.md +18 -5
- package/dist/commands/adf-bundle.d.ts +8 -0
- package/dist/commands/adf-bundle.d.ts.map +1 -0
- package/dist/commands/adf-bundle.js +169 -0
- package/dist/commands/adf-bundle.js.map +1 -0
- package/dist/commands/adf-evidence.d.ts +8 -0
- package/dist/commands/adf-evidence.d.ts.map +1 -0
- package/dist/commands/adf-evidence.js +273 -0
- package/dist/commands/adf-evidence.js.map +1 -0
- package/dist/commands/adf-migrate.d.ts +10 -0
- package/dist/commands/adf-migrate.d.ts.map +1 -0
- package/dist/commands/adf-migrate.js +383 -0
- package/dist/commands/adf-migrate.js.map +1 -0
- package/dist/commands/adf-sync.d.ts +10 -0
- package/dist/commands/adf-sync.d.ts.map +1 -0
- package/dist/commands/adf-sync.js +213 -0
- package/dist/commands/adf-sync.js.map +1 -0
- package/dist/commands/adf.d.ts +2 -2
- package/dist/commands/adf.d.ts.map +1 -1
- package/dist/commands/adf.js +31 -525
- package/dist/commands/adf.js.map +1 -1
- package/dist/commands/bootstrap.js +61 -2
- package/dist/commands/bootstrap.js.map +1 -1
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +30 -0
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/hook.d.ts.map +1 -1
- package/dist/commands/hook.js +69 -10
- package/dist/commands/hook.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -2
- package/dist/index.js.map +1 -1
- package/package.json +8 -8
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"adf.d.ts","sourceRoot":"","sources":["../../src/commands/adf.ts"],"names":[],"mappings":"AAAA;;;;GAIG;
|
|
1
|
+
{"version":3,"file":"adf.d.ts","sourceRoot":"","sources":["../../src/commands/adf.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAUH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAW3C,eAAO,MAAM,iBAAiB,0VAc7B,CAAC;AAEF,eAAO,MAAM,aAAa,k3BAmBzB,CAAC;AAEF,eAAO,MAAM,cAAc,mJAI1B,CAAC;AAMF,wBAAsB,UAAU,CAAC,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CA2BrF;AAeD,eAAO,MAAM,iBAAiB,2bAU7B,CAAC;AAEF,eAAO,MAAM,mBAAmB,oSAO/B,CAAC;AAEF,eAAO,MAAM,iBAAiB,8UAU7B,CAAC"}
|
package/dist/commands/adf.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* charter adf
|
|
4
4
|
*
|
|
5
|
-
* ADF (Attention-Directed Format) subcommands: init, fmt, patch, bundle, sync.
|
|
5
|
+
* ADF (Attention-Directed Format) subcommands: init, fmt, patch, bundle, sync, evidence, migrate.
|
|
6
6
|
*/
|
|
7
7
|
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
8
8
|
if (k2 === undefined) k2 = k;
|
|
@@ -42,9 +42,12 @@ exports.POINTER_AGENTS_MD = exports.POINTER_CURSORRULES = exports.POINTER_CLAUDE
|
|
|
42
42
|
exports.adfCommand = adfCommand;
|
|
43
43
|
const fs = __importStar(require("node:fs"));
|
|
44
44
|
const path = __importStar(require("node:path"));
|
|
45
|
-
const crypto = __importStar(require("node:crypto"));
|
|
46
45
|
const adf_1 = require("@stackbilt/adf");
|
|
47
46
|
const index_1 = require("../index");
|
|
47
|
+
const adf_migrate_1 = require("./adf-migrate");
|
|
48
|
+
const adf_bundle_1 = require("./adf-bundle");
|
|
49
|
+
const adf_sync_1 = require("./adf-sync");
|
|
50
|
+
const adf_evidence_1 = require("./adf-evidence");
|
|
48
51
|
// ============================================================================
|
|
49
52
|
// Scaffold Content
|
|
50
53
|
// ============================================================================
|
|
@@ -65,35 +68,15 @@ exports.MANIFEST_SCAFFOLD = `ADF: 0.1
|
|
|
65
68
|
`;
|
|
66
69
|
exports.CORE_SCAFFOLD = `ADF: 0.1
|
|
67
70
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
#
|
|
78
|
-
# Stack-specific operational rule? (wrangler flags, D1 batching)
|
|
79
|
-
# \u2192 domain-specific .adf module (backend.adf, frontend.adf)
|
|
80
|
-
#
|
|
81
|
-
# Agent identity/behavior? ("You ARE the X agent", bias toward action)
|
|
82
|
-
# \u2192 core.adf CONTEXT
|
|
83
|
-
#
|
|
84
|
-
# Language/tooling discipline? (type checks, enum safety)
|
|
85
|
-
# \u2192 core.adf CONSTRAINTS or dedicated section
|
|
86
|
-
#
|
|
87
|
-
# Section weight guide:
|
|
88
|
-
# [load-bearing] = violation causes incorrect output
|
|
89
|
-
# [advisory] = best practice, not enforced
|
|
90
|
-
# (no tag) = informational context
|
|
91
|
-
#
|
|
92
|
-
# Section types (open taxonomy, custom sections allowed):
|
|
93
|
-
# CONTEXT \u2014 factual project/agent identity
|
|
94
|
-
# CONSTRAINTS \u2014 must/never rules
|
|
95
|
-
# ADVISORY \u2014 should/prefer guidance
|
|
96
|
-
# METRICS \u2014 quantitative ceilings (key: value / ceiling [unit])
|
|
71
|
+
\u{1F4D6} GUIDE [advisory]:
|
|
72
|
+
- Pure runtime/environment? (OS, line endings) \u2192 CLAUDE.md, not ADF
|
|
73
|
+
- Universal architecture constraint? \u2192 core.adf CONSTRAINTS [load-bearing]
|
|
74
|
+
- Stack-specific operational rule? \u2192 domain .adf module (backend.adf, frontend.adf)
|
|
75
|
+
- Agent identity/behavior? \u2192 core.adf CONTEXT
|
|
76
|
+
- Language/tooling discipline? \u2192 core.adf CONSTRAINTS or dedicated section
|
|
77
|
+
- [load-bearing] = violation causes incorrect output
|
|
78
|
+
- [advisory] = best practice, not enforced
|
|
79
|
+
- Section types are open: CONTEXT, CONSTRAINTS, ADVISORY, METRICS, custom
|
|
97
80
|
|
|
98
81
|
\u26A0\uFE0F CONSTRAINTS [load-bearing]:
|
|
99
82
|
- Use Conventional Commits (feat, fix, docs, chore)
|
|
@@ -126,16 +109,18 @@ async function adfCommand(options, args) {
|
|
|
126
109
|
case 'patch':
|
|
127
110
|
return adfPatch(options, restArgs);
|
|
128
111
|
case 'bundle':
|
|
129
|
-
return adfBundle(options, restArgs);
|
|
112
|
+
return (0, adf_bundle_1.adfBundle)(options, restArgs);
|
|
130
113
|
case 'sync':
|
|
131
|
-
return adfSync(options, restArgs);
|
|
114
|
+
return (0, adf_sync_1.adfSync)(options, restArgs);
|
|
132
115
|
case 'evidence':
|
|
133
|
-
return adfEvidence(options, restArgs);
|
|
116
|
+
return (0, adf_evidence_1.adfEvidence)(options, restArgs);
|
|
117
|
+
case 'migrate':
|
|
118
|
+
return (0, adf_migrate_1.adfMigrateCommand)(options, restArgs);
|
|
134
119
|
default:
|
|
135
|
-
throw new index_1.CLIError(`Unknown adf subcommand: ${subcommand}. Supported: init, fmt, patch, bundle, sync, evidence`);
|
|
120
|
+
throw new index_1.CLIError(`Unknown adf subcommand: ${subcommand}. Supported: init, fmt, patch, bundle, sync, evidence, migrate`);
|
|
136
121
|
}
|
|
137
122
|
}
|
|
138
|
-
//
|
|
123
|
+
// -- Thin pointer file content --
|
|
139
124
|
exports.POINTER_CLAUDE_MD = `# Project Context
|
|
140
125
|
|
|
141
126
|
> This project uses [ADF](https://github.com/Stackbilt-dev/charter) for AI agent context management.
|
|
@@ -346,495 +331,6 @@ function adfPatch(options, args) {
|
|
|
346
331
|
}
|
|
347
332
|
}
|
|
348
333
|
// ============================================================================
|
|
349
|
-
// adf bundle
|
|
350
|
-
// ============================================================================
|
|
351
|
-
function adfBundle(options, args) {
|
|
352
|
-
const task = getFlag(args, '--task');
|
|
353
|
-
if (!task) {
|
|
354
|
-
throw new index_1.CLIError('adf bundle requires --task "<prompt>". Usage: charter adf bundle --task "Fix React component"');
|
|
355
|
-
}
|
|
356
|
-
const aiDir = getFlag(args, '--ai-dir') || '.ai';
|
|
357
|
-
const manifestPath = path.join(aiDir, 'manifest.adf');
|
|
358
|
-
if (!fs.existsSync(manifestPath)) {
|
|
359
|
-
throw new index_1.CLIError(`manifest.adf not found at ${manifestPath}. Run: charter adf init`);
|
|
360
|
-
}
|
|
361
|
-
const manifestContent = fs.readFileSync(manifestPath, 'utf-8');
|
|
362
|
-
const manifestDoc = (0, adf_1.parseAdf)(manifestContent);
|
|
363
|
-
const manifest = (0, adf_1.parseManifest)(manifestDoc);
|
|
364
|
-
// Tokenize task into keywords (simple word split)
|
|
365
|
-
const keywords = task
|
|
366
|
-
.split(/[\s,;:()[\]{}]+/)
|
|
367
|
-
.filter(w => w.length > 1)
|
|
368
|
-
.map(w => w.replace(/[^a-zA-Z0-9]/g, ''));
|
|
369
|
-
const modulePaths = (0, adf_1.resolveModules)(manifest, keywords);
|
|
370
|
-
const readFile = (p) => fs.readFileSync(p, 'utf-8');
|
|
371
|
-
try {
|
|
372
|
-
const result = (0, adf_1.bundleModules)(aiDir, modulePaths, readFile, keywords);
|
|
373
|
-
if (options.format === 'json') {
|
|
374
|
-
const jsonOut = {
|
|
375
|
-
task,
|
|
376
|
-
keywords,
|
|
377
|
-
resolvedModules: result.resolvedModules,
|
|
378
|
-
tokenEstimate: result.tokenEstimate,
|
|
379
|
-
tokenBudget: result.tokenBudget,
|
|
380
|
-
tokenUtilization: result.tokenUtilization,
|
|
381
|
-
perModuleTokens: result.perModuleTokens,
|
|
382
|
-
triggerMatches: result.triggerMatches,
|
|
383
|
-
};
|
|
384
|
-
if (result.unmatchedModules.length > 0) {
|
|
385
|
-
jsonOut.unmatchedModules = result.unmatchedModules;
|
|
386
|
-
}
|
|
387
|
-
if (result.moduleBudgetOverruns.length > 0) {
|
|
388
|
-
jsonOut.moduleBudgetOverruns = result.moduleBudgetOverruns;
|
|
389
|
-
}
|
|
390
|
-
if (result.advisoryOnlyModules.length > 0) {
|
|
391
|
-
jsonOut.advisoryOnlyModules = result.advisoryOnlyModules;
|
|
392
|
-
}
|
|
393
|
-
if (result.manifest.cadence.length > 0) {
|
|
394
|
-
jsonOut.cadence = result.manifest.cadence;
|
|
395
|
-
}
|
|
396
|
-
console.log(JSON.stringify(jsonOut, null, 2));
|
|
397
|
-
}
|
|
398
|
-
else {
|
|
399
|
-
console.log(` Task: "${task}"`);
|
|
400
|
-
console.log(` Keywords: ${keywords.join(', ')}`);
|
|
401
|
-
console.log(` Resolved modules: ${result.resolvedModules.join(', ')}`);
|
|
402
|
-
console.log(` Token estimate: ~${result.tokenEstimate}`);
|
|
403
|
-
if (result.tokenBudget !== null) {
|
|
404
|
-
const pct = result.tokenUtilization !== null
|
|
405
|
-
? ` (${(result.tokenUtilization * 100).toFixed(0)}%)`
|
|
406
|
-
: '';
|
|
407
|
-
console.log(` Token budget: ${result.tokenBudget}${pct}`);
|
|
408
|
-
}
|
|
409
|
-
console.log('');
|
|
410
|
-
if (result.moduleBudgetOverruns.length > 0) {
|
|
411
|
-
console.log(' Module budget overruns:');
|
|
412
|
-
for (const o of result.moduleBudgetOverruns) {
|
|
413
|
-
console.log(` [!] ${o.module}: ~${o.tokens} tokens (budget: ${o.budget})`);
|
|
414
|
-
}
|
|
415
|
-
console.log('');
|
|
416
|
-
}
|
|
417
|
-
if (result.triggerMatches.length > 0) {
|
|
418
|
-
console.log(' Trigger report:');
|
|
419
|
-
for (const tm of result.triggerMatches) {
|
|
420
|
-
const icon = tm.matched ? '+' : '-';
|
|
421
|
-
const kw = tm.matchedKeywords.length > 0 ? ` [${tm.matchedKeywords.join(', ')}]` : '';
|
|
422
|
-
console.log(` [${icon}] ${tm.module} (${tm.trigger})${kw}`);
|
|
423
|
-
}
|
|
424
|
-
console.log('');
|
|
425
|
-
}
|
|
426
|
-
if (result.unmatchedModules.length > 0) {
|
|
427
|
-
console.log(' Unmatched modules (not loaded):');
|
|
428
|
-
for (const m of result.unmatchedModules) {
|
|
429
|
-
console.log(` [-] ${m}`);
|
|
430
|
-
}
|
|
431
|
-
console.log('');
|
|
432
|
-
}
|
|
433
|
-
if (result.advisoryOnlyModules.length > 0) {
|
|
434
|
-
console.log(' Advisory-only modules:');
|
|
435
|
-
for (const m of result.advisoryOnlyModules) {
|
|
436
|
-
console.log(` [!] ${m}: no load-bearing sections`);
|
|
437
|
-
}
|
|
438
|
-
console.log('');
|
|
439
|
-
}
|
|
440
|
-
if (result.manifest.cadence.length > 0) {
|
|
441
|
-
console.log(' Cadence schedule:');
|
|
442
|
-
for (const c of result.manifest.cadence) {
|
|
443
|
-
console.log(` ${c.check}: ${c.frequency}`);
|
|
444
|
-
}
|
|
445
|
-
console.log('');
|
|
446
|
-
}
|
|
447
|
-
// Output merged document
|
|
448
|
-
const output = (0, adf_1.formatAdf)(result.mergedDocument);
|
|
449
|
-
console.log(' --- Merged Context ---');
|
|
450
|
-
console.log(output);
|
|
451
|
-
}
|
|
452
|
-
return index_1.EXIT_CODE.SUCCESS;
|
|
453
|
-
}
|
|
454
|
-
catch (e) {
|
|
455
|
-
if (e instanceof Error && e.name === 'AdfBundleError') {
|
|
456
|
-
if (options.format === 'json') {
|
|
457
|
-
console.log(JSON.stringify({ error: e.message }, null, 2));
|
|
458
|
-
}
|
|
459
|
-
else {
|
|
460
|
-
console.error(` [error] ${e.message}`);
|
|
461
|
-
}
|
|
462
|
-
return index_1.EXIT_CODE.RUNTIME_ERROR;
|
|
463
|
-
}
|
|
464
|
-
throw e;
|
|
465
|
-
}
|
|
466
|
-
}
|
|
467
|
-
function adfSync(options, args) {
|
|
468
|
-
// --explain: output lockfile schema documentation and exit
|
|
469
|
-
if (args.includes('--explain')) {
|
|
470
|
-
const explanation = {
|
|
471
|
-
format: '.adf.lock',
|
|
472
|
-
description: 'Flat JSON map of ADF source files to SHA-256 hash prefixes (16 hex chars)',
|
|
473
|
-
schema: {
|
|
474
|
-
type: 'object',
|
|
475
|
-
pattern: '{ "<filename>.adf": "<sha256-prefix-16>" }',
|
|
476
|
-
example: {
|
|
477
|
-
'core.adf': '54d5c9a146d6da3c',
|
|
478
|
-
'state.adf': 'a1b2c3d4e5f67890',
|
|
479
|
-
},
|
|
480
|
-
},
|
|
481
|
-
hashAlgorithm: 'SHA-256, first 16 hex characters',
|
|
482
|
-
commands: {
|
|
483
|
-
check: 'charter adf sync --check \u2014 verify sources match locked hashes',
|
|
484
|
-
write: 'charter adf sync --write \u2014 update lock with current hashes',
|
|
485
|
-
},
|
|
486
|
-
location: '.ai/.adf.lock (relative to AI directory)',
|
|
487
|
-
purpose: 'Detect when ADF source files have changed since last sync. Used in CI to enforce governance drift checks.',
|
|
488
|
-
};
|
|
489
|
-
if (options.format === 'json') {
|
|
490
|
-
console.log(JSON.stringify(explanation, null, 2));
|
|
491
|
-
}
|
|
492
|
-
else {
|
|
493
|
-
console.log('ADF Sync Lock Format (.adf.lock)');
|
|
494
|
-
console.log('================================\n');
|
|
495
|
-
console.log('Format: Flat JSON map of source files to hash prefixes\n');
|
|
496
|
-
console.log('Schema: { "<filename>.adf": "<sha256-prefix-16>" }\n');
|
|
497
|
-
console.log('Hash: SHA-256, first 16 hex characters\n');
|
|
498
|
-
console.log('Location: .ai/.adf.lock\n');
|
|
499
|
-
console.log('Commands:');
|
|
500
|
-
console.log(' sync --check Verify sources match locked hashes');
|
|
501
|
-
console.log(' sync --write Update lock with current hashes');
|
|
502
|
-
console.log(' sync --explain Show this schema documentation\n');
|
|
503
|
-
console.log('Purpose: Detect ADF source drift. Used in CI governance checks.');
|
|
504
|
-
}
|
|
505
|
-
return index_1.EXIT_CODE.SUCCESS;
|
|
506
|
-
}
|
|
507
|
-
const aiDir = getFlag(args, '--ai-dir') || '.ai';
|
|
508
|
-
const checkMode = args.includes('--check');
|
|
509
|
-
const writeMode = args.includes('--write');
|
|
510
|
-
if (!checkMode && !writeMode) {
|
|
511
|
-
throw new index_1.CLIError('adf sync requires --check, --write, or --explain. Usage: charter adf sync --check');
|
|
512
|
-
}
|
|
513
|
-
const manifestPath = path.join(aiDir, 'manifest.adf');
|
|
514
|
-
if (!fs.existsSync(manifestPath)) {
|
|
515
|
-
throw new index_1.CLIError(`manifest.adf not found at ${manifestPath}. Run: charter adf init`);
|
|
516
|
-
}
|
|
517
|
-
const manifestContent = fs.readFileSync(manifestPath, 'utf-8');
|
|
518
|
-
const manifestDoc = (0, adf_1.parseAdf)(manifestContent);
|
|
519
|
-
const manifest = (0, adf_1.parseManifest)(manifestDoc);
|
|
520
|
-
if (manifest.sync.length === 0) {
|
|
521
|
-
const result = {
|
|
522
|
-
aiDir,
|
|
523
|
-
lockFile: path.join(aiDir, '.adf.lock'),
|
|
524
|
-
entries: [],
|
|
525
|
-
allInSync: true,
|
|
526
|
-
written: false,
|
|
527
|
-
};
|
|
528
|
-
if (options.format === 'json') {
|
|
529
|
-
console.log(JSON.stringify(result, null, 2));
|
|
530
|
-
}
|
|
531
|
-
else {
|
|
532
|
-
console.log(' No SYNC entries in manifest. Nothing to check.');
|
|
533
|
-
}
|
|
534
|
-
return index_1.EXIT_CODE.SUCCESS;
|
|
535
|
-
}
|
|
536
|
-
const lockFile = path.join(aiDir, '.adf.lock');
|
|
537
|
-
const locked = loadLockFile(lockFile);
|
|
538
|
-
const entries = [];
|
|
539
|
-
for (const entry of manifest.sync) {
|
|
540
|
-
const sourcePath = path.join(aiDir, entry.source);
|
|
541
|
-
if (!fs.existsSync(sourcePath)) {
|
|
542
|
-
throw new index_1.CLIError(`Sync source not found: ${sourcePath}`);
|
|
543
|
-
}
|
|
544
|
-
const sourceContent = fs.readFileSync(sourcePath, 'utf-8');
|
|
545
|
-
const sourceHash = hashContent(sourceContent);
|
|
546
|
-
const lockedHash = locked[entry.source] ?? null;
|
|
547
|
-
entries.push({
|
|
548
|
-
source: entry.source,
|
|
549
|
-
target: entry.target,
|
|
550
|
-
sourceHash,
|
|
551
|
-
lockedHash,
|
|
552
|
-
inSync: lockedHash === sourceHash,
|
|
553
|
-
});
|
|
554
|
-
}
|
|
555
|
-
const allInSync = entries.every(e => e.inSync);
|
|
556
|
-
if (writeMode) {
|
|
557
|
-
const newLock = {};
|
|
558
|
-
for (const e of entries) {
|
|
559
|
-
newLock[e.source] = e.sourceHash;
|
|
560
|
-
}
|
|
561
|
-
fs.writeFileSync(lockFile, JSON.stringify(newLock, null, 2) + '\n');
|
|
562
|
-
const result = {
|
|
563
|
-
aiDir,
|
|
564
|
-
lockFile,
|
|
565
|
-
entries,
|
|
566
|
-
allInSync: true,
|
|
567
|
-
written: true,
|
|
568
|
-
};
|
|
569
|
-
if (options.format === 'json') {
|
|
570
|
-
console.log(JSON.stringify(result, null, 2));
|
|
571
|
-
}
|
|
572
|
-
else {
|
|
573
|
-
console.log(` [ok] Updated ${lockFile} with ${entries.length} hash${entries.length === 1 ? '' : 'es'}.`);
|
|
574
|
-
}
|
|
575
|
-
return index_1.EXIT_CODE.SUCCESS;
|
|
576
|
-
}
|
|
577
|
-
// --check mode
|
|
578
|
-
const result = {
|
|
579
|
-
aiDir,
|
|
580
|
-
lockFile,
|
|
581
|
-
entries,
|
|
582
|
-
allInSync,
|
|
583
|
-
written: false,
|
|
584
|
-
};
|
|
585
|
-
if (options.format === 'json') {
|
|
586
|
-
const syncOut = { ...result };
|
|
587
|
-
if (!allInSync) {
|
|
588
|
-
syncOut.nextActions = ['Regenerate targets from source .adf files', 'charter adf sync --write'];
|
|
589
|
-
}
|
|
590
|
-
console.log(JSON.stringify(syncOut, null, 2));
|
|
591
|
-
}
|
|
592
|
-
else {
|
|
593
|
-
for (const e of entries) {
|
|
594
|
-
if (e.inSync) {
|
|
595
|
-
console.log(` [ok] ${e.source} -> ${e.target} (in sync)`);
|
|
596
|
-
}
|
|
597
|
-
else if (e.lockedHash === null) {
|
|
598
|
-
console.log(` [warn] ${e.source} -> ${e.target} (no lock entry — run: charter adf sync --write)`);
|
|
599
|
-
}
|
|
600
|
-
else {
|
|
601
|
-
console.log(` [fail] ${e.source} -> ${e.target} (source changed since last sync)`);
|
|
602
|
-
}
|
|
603
|
-
}
|
|
604
|
-
if (!allInSync) {
|
|
605
|
-
console.log('');
|
|
606
|
-
console.log(' Source .adf files have changed. Regenerate targets and run: charter adf sync --write');
|
|
607
|
-
}
|
|
608
|
-
}
|
|
609
|
-
return allInSync ? index_1.EXIT_CODE.SUCCESS : index_1.EXIT_CODE.POLICY_VIOLATION;
|
|
610
|
-
}
|
|
611
|
-
function hashContent(content) {
|
|
612
|
-
return crypto.createHash('sha256').update(content).digest('hex').slice(0, 16);
|
|
613
|
-
}
|
|
614
|
-
function loadLockFile(lockFile) {
|
|
615
|
-
if (!fs.existsSync(lockFile))
|
|
616
|
-
return {};
|
|
617
|
-
try {
|
|
618
|
-
return JSON.parse(fs.readFileSync(lockFile, 'utf-8'));
|
|
619
|
-
}
|
|
620
|
-
catch {
|
|
621
|
-
return {};
|
|
622
|
-
}
|
|
623
|
-
}
|
|
624
|
-
function adfEvidence(options, args) {
|
|
625
|
-
const task = getFlag(args, '--task');
|
|
626
|
-
const aiDir = getFlag(args, '--ai-dir') || '.ai';
|
|
627
|
-
const contextJson = getFlag(args, '--context');
|
|
628
|
-
const contextFile = getFlag(args, '--context-file');
|
|
629
|
-
const autoMeasure = args.includes('--auto-measure');
|
|
630
|
-
const manifestPath = path.join(aiDir, 'manifest.adf');
|
|
631
|
-
if (!fs.existsSync(manifestPath)) {
|
|
632
|
-
throw new index_1.CLIError(`manifest.adf not found at ${manifestPath}. Run: charter adf init`);
|
|
633
|
-
}
|
|
634
|
-
const manifestContent = fs.readFileSync(manifestPath, 'utf-8');
|
|
635
|
-
const manifestDoc = (0, adf_1.parseAdf)(manifestContent);
|
|
636
|
-
const manifest = (0, adf_1.parseManifest)(manifestDoc);
|
|
637
|
-
// Resolve modules
|
|
638
|
-
let modulePaths;
|
|
639
|
-
let keywords = [];
|
|
640
|
-
if (task) {
|
|
641
|
-
keywords = task
|
|
642
|
-
.split(/[\s,;:()[\]{}]+/)
|
|
643
|
-
.filter(w => w.length > 1)
|
|
644
|
-
.map(w => w.replace(/[^a-zA-Z0-9]/g, ''));
|
|
645
|
-
modulePaths = (0, adf_1.resolveModules)(manifest, keywords);
|
|
646
|
-
}
|
|
647
|
-
else {
|
|
648
|
-
modulePaths = [...manifest.defaultLoad];
|
|
649
|
-
}
|
|
650
|
-
const readFile = (p) => fs.readFileSync(p, 'utf-8');
|
|
651
|
-
let context;
|
|
652
|
-
const rawContext = contextFile ? readJsonFlag(contextFile, '--context-file') : contextJson;
|
|
653
|
-
if (rawContext) {
|
|
654
|
-
try {
|
|
655
|
-
const parsed = JSON.parse(rawContext);
|
|
656
|
-
if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {
|
|
657
|
-
throw new Error('must be a JSON object');
|
|
658
|
-
}
|
|
659
|
-
context = parsed;
|
|
660
|
-
}
|
|
661
|
-
catch (e) {
|
|
662
|
-
const msg = e instanceof Error ? e.message : String(e);
|
|
663
|
-
throw new index_1.CLIError(`Invalid --context JSON: ${msg}`);
|
|
664
|
-
}
|
|
665
|
-
}
|
|
666
|
-
// Auto-measure: count lines in files referenced by manifest METRICS
|
|
667
|
-
// Manifest keys are UPPERCASE (parser map disambiguation), metric keys are lowercase.
|
|
668
|
-
const autoMeasured = [];
|
|
669
|
-
if (autoMeasure && manifest.metrics.length > 0) {
|
|
670
|
-
const measured = {};
|
|
671
|
-
for (const ms of manifest.metrics) {
|
|
672
|
-
const metricKey = ms.key.toLowerCase();
|
|
673
|
-
const filePath = path.resolve(ms.path);
|
|
674
|
-
if (fs.existsSync(filePath)) {
|
|
675
|
-
const content = fs.readFileSync(filePath, 'utf-8');
|
|
676
|
-
const lines = content.split('\n').length;
|
|
677
|
-
measured[metricKey] = lines;
|
|
678
|
-
autoMeasured.push({ metric: metricKey, path: ms.path, lines });
|
|
679
|
-
}
|
|
680
|
-
else {
|
|
681
|
-
autoMeasured.push({ metric: metricKey, path: ms.path, lines: null, error: 'file not found' });
|
|
682
|
-
}
|
|
683
|
-
}
|
|
684
|
-
// Merge: explicit --context wins over auto-measured
|
|
685
|
-
context = { ...measured, ...context };
|
|
686
|
-
}
|
|
687
|
-
try {
|
|
688
|
-
const bundle = (0, adf_1.bundleModules)(aiDir, modulePaths, readFile, keywords);
|
|
689
|
-
const evidence = (0, adf_1.validateConstraints)(bundle.mergedDocument, context);
|
|
690
|
-
// Check sync status
|
|
691
|
-
const lockFile = path.join(aiDir, '.adf.lock');
|
|
692
|
-
const locked = loadLockFile(lockFile);
|
|
693
|
-
const syncEntries = [];
|
|
694
|
-
for (const entry of manifest.sync) {
|
|
695
|
-
const sourcePath = path.join(aiDir, entry.source);
|
|
696
|
-
if (fs.existsSync(sourcePath)) {
|
|
697
|
-
const sourceContent = fs.readFileSync(sourcePath, 'utf-8');
|
|
698
|
-
const sourceHash = hashContent(sourceContent);
|
|
699
|
-
const lockedHash = locked[entry.source] ?? null;
|
|
700
|
-
syncEntries.push({ source: entry.source, inSync: lockedHash === sourceHash });
|
|
701
|
-
}
|
|
702
|
-
}
|
|
703
|
-
const allInSync = syncEntries.length === 0 || syncEntries.every(e => e.inSync);
|
|
704
|
-
const staleCount = syncEntries.filter(e => !e.inSync).length;
|
|
705
|
-
if (options.format === 'json') {
|
|
706
|
-
const jsonOut = {
|
|
707
|
-
aiDir,
|
|
708
|
-
resolvedModules: bundle.resolvedModules,
|
|
709
|
-
tokenEstimate: bundle.tokenEstimate,
|
|
710
|
-
tokenBudget: bundle.tokenBudget,
|
|
711
|
-
tokenUtilization: bundle.tokenUtilization,
|
|
712
|
-
constraints: evidence.constraints,
|
|
713
|
-
weightSummary: evidence.weightSummary,
|
|
714
|
-
allPassing: evidence.allPassing,
|
|
715
|
-
failCount: evidence.failCount,
|
|
716
|
-
warnCount: evidence.warnCount,
|
|
717
|
-
syncStatus: { allInSync, staleCount },
|
|
718
|
-
};
|
|
719
|
-
if (task) {
|
|
720
|
-
jsonOut.task = task;
|
|
721
|
-
jsonOut.keywords = keywords;
|
|
722
|
-
}
|
|
723
|
-
if (bundle.advisoryOnlyModules.length > 0) {
|
|
724
|
-
jsonOut.advisoryOnlyModules = bundle.advisoryOnlyModules;
|
|
725
|
-
}
|
|
726
|
-
if (autoMeasured.length > 0) {
|
|
727
|
-
jsonOut.autoMeasured = autoMeasured;
|
|
728
|
-
}
|
|
729
|
-
// Suggest logical next steps based on results
|
|
730
|
-
const nextActions = [];
|
|
731
|
-
if (!evidence.allPassing) {
|
|
732
|
-
nextActions.push('Fix failing constraints before merging');
|
|
733
|
-
}
|
|
734
|
-
if (!allInSync) {
|
|
735
|
-
nextActions.push('charter adf sync --write');
|
|
736
|
-
}
|
|
737
|
-
if (evidence.warnCount > 0) {
|
|
738
|
-
nextActions.push('Review metrics at ceiling boundary');
|
|
739
|
-
}
|
|
740
|
-
if (nextActions.length > 0) {
|
|
741
|
-
jsonOut.nextActions = nextActions;
|
|
742
|
-
}
|
|
743
|
-
console.log(JSON.stringify(jsonOut, null, 2));
|
|
744
|
-
}
|
|
745
|
-
else {
|
|
746
|
-
console.log('');
|
|
747
|
-
console.log(' ADF Evidence Report');
|
|
748
|
-
console.log(' ===================');
|
|
749
|
-
console.log(` Modules loaded: ${bundle.resolvedModules.join(', ')}`);
|
|
750
|
-
console.log(` Token estimate: ~${bundle.tokenEstimate}`);
|
|
751
|
-
if (bundle.tokenBudget !== null) {
|
|
752
|
-
const pct = bundle.tokenUtilization !== null
|
|
753
|
-
? ` (${(bundle.tokenUtilization * 100).toFixed(0)}%)`
|
|
754
|
-
: '';
|
|
755
|
-
console.log(` Token budget: ${bundle.tokenBudget}${pct}`);
|
|
756
|
-
}
|
|
757
|
-
console.log('');
|
|
758
|
-
// Auto-measured metrics
|
|
759
|
-
if (autoMeasured.length > 0) {
|
|
760
|
-
console.log(' Auto-measured:');
|
|
761
|
-
for (const m of autoMeasured) {
|
|
762
|
-
if (m.lines !== null) {
|
|
763
|
-
console.log(` ${m.metric}: ${m.lines} lines (${m.path})`);
|
|
764
|
-
}
|
|
765
|
-
else {
|
|
766
|
-
console.log(` ${m.metric}: [file not found] (${m.path})`);
|
|
767
|
-
}
|
|
768
|
-
}
|
|
769
|
-
console.log('');
|
|
770
|
-
}
|
|
771
|
-
// Weight summary
|
|
772
|
-
console.log(' Section weights:');
|
|
773
|
-
console.log(` Load-bearing: ${evidence.weightSummary.loadBearing}`);
|
|
774
|
-
console.log(` Advisory: ${evidence.weightSummary.advisory}`);
|
|
775
|
-
console.log(` Unweighted: ${evidence.weightSummary.unweighted}`);
|
|
776
|
-
console.log('');
|
|
777
|
-
// Advisory-only module warnings
|
|
778
|
-
if (bundle.advisoryOnlyModules.length > 0) {
|
|
779
|
-
console.log(' Advisory-only modules:');
|
|
780
|
-
for (const m of bundle.advisoryOnlyModules) {
|
|
781
|
-
console.log(` [!] ${m}: no load-bearing sections`);
|
|
782
|
-
}
|
|
783
|
-
console.log('');
|
|
784
|
-
}
|
|
785
|
-
// Constraints
|
|
786
|
-
if (evidence.constraints.length > 0) {
|
|
787
|
-
console.log(' Constraints:');
|
|
788
|
-
for (const c of evidence.constraints) {
|
|
789
|
-
const icon = c.status === 'pass' ? 'ok' : c.status === 'warn' ? 'WARN' : 'FAIL';
|
|
790
|
-
console.log(` [${icon}] ${c.message}`);
|
|
791
|
-
}
|
|
792
|
-
}
|
|
793
|
-
else {
|
|
794
|
-
console.log(' Constraints: (none)');
|
|
795
|
-
}
|
|
796
|
-
console.log('');
|
|
797
|
-
// Sync status
|
|
798
|
-
if (syncEntries.length > 0) {
|
|
799
|
-
if (allInSync) {
|
|
800
|
-
console.log(' Sync: all sources in sync');
|
|
801
|
-
}
|
|
802
|
-
else {
|
|
803
|
-
console.log(` Sync: ${staleCount} source${staleCount === 1 ? '' : 's'} out of sync`);
|
|
804
|
-
}
|
|
805
|
-
}
|
|
806
|
-
else {
|
|
807
|
-
console.log(' Sync: no sync entries configured');
|
|
808
|
-
}
|
|
809
|
-
console.log('');
|
|
810
|
-
// Verdict
|
|
811
|
-
const verdict = evidence.allPassing ? 'PASS' : 'FAIL';
|
|
812
|
-
console.log(` Verdict: ${verdict}`);
|
|
813
|
-
if (evidence.warnCount > 0) {
|
|
814
|
-
console.log(` (${evidence.warnCount} warning${evidence.warnCount === 1 ? '' : 's'} — at ceiling boundary)`);
|
|
815
|
-
}
|
|
816
|
-
console.log('');
|
|
817
|
-
}
|
|
818
|
-
// CI mode: exit 1 on constraint failures
|
|
819
|
-
if (options.ciMode && !evidence.allPassing) {
|
|
820
|
-
return index_1.EXIT_CODE.POLICY_VIOLATION;
|
|
821
|
-
}
|
|
822
|
-
return index_1.EXIT_CODE.SUCCESS;
|
|
823
|
-
}
|
|
824
|
-
catch (e) {
|
|
825
|
-
if (e instanceof Error && e.name === 'AdfBundleError') {
|
|
826
|
-
if (options.format === 'json') {
|
|
827
|
-
console.log(JSON.stringify({ error: e.message }, null, 2));
|
|
828
|
-
}
|
|
829
|
-
else {
|
|
830
|
-
console.error(` [error] ${e.message}`);
|
|
831
|
-
}
|
|
832
|
-
return index_1.EXIT_CODE.RUNTIME_ERROR;
|
|
833
|
-
}
|
|
834
|
-
throw e;
|
|
835
|
-
}
|
|
836
|
-
}
|
|
837
|
-
// ============================================================================
|
|
838
334
|
// Helpers
|
|
839
335
|
// ============================================================================
|
|
840
336
|
function getFlag(args, flag) {
|
|
@@ -890,5 +386,15 @@ function printHelp() {
|
|
|
890
386
|
console.log(' --context-file: read --context JSON from a file instead.');
|
|
891
387
|
console.log(' In --ci mode, exit 1 if any constraint fails.');
|
|
892
388
|
console.log('');
|
|
389
|
+
console.log(' charter adf migrate [--dry-run] [--source <file>] [--no-backup]');
|
|
390
|
+
console.log(' [--merge-strategy append|dedupe|replace] [--ai-dir <dir>]');
|
|
391
|
+
console.log(' Ingest existing agent config files (CLAUDE.md, .cursorrules, etc.) and');
|
|
392
|
+
console.log(' migrate their content into structured ADF modules. Replaces originals');
|
|
393
|
+
console.log(' with thin pointers that retain environment-specific rules.');
|
|
394
|
+
console.log(' --dry-run: preview migration plan without writing files');
|
|
395
|
+
console.log(' --source: migrate a single file instead of scanning all agent configs');
|
|
396
|
+
console.log(' --no-backup: skip creating .pre-adf-migrate.bak backups');
|
|
397
|
+
console.log(' --merge-strategy: append (always add), dedupe (skip duplicates, default), replace');
|
|
398
|
+
console.log('');
|
|
893
399
|
}
|
|
894
400
|
//# sourceMappingURL=adf.js.map
|