@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.
- 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 +7 -1
- package/dist/commands/adf.d.ts.map +1 -1
- package/dist/commands/adf.js +109 -472
- package/dist/commands/adf.js.map +1 -1
- package/dist/commands/bootstrap.d.ts +9 -0
- package/dist/commands/bootstrap.d.ts.map +1 -0
- package/dist/commands/bootstrap.js +660 -0
- package/dist/commands/bootstrap.js.map +1 -0
- 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/commands/setup.d.ts +103 -0
- package/dist/commands/setup.d.ts.map +1 -1
- package/dist/commands/setup.js +7 -0
- package/dist/commands/setup.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -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;
|
|
@@ -38,16 +38,20 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
38
38
|
};
|
|
39
39
|
})();
|
|
40
40
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
41
|
+
exports.POINTER_AGENTS_MD = exports.POINTER_CURSORRULES = exports.POINTER_CLAUDE_MD = exports.STATE_SCAFFOLD = exports.CORE_SCAFFOLD = exports.MANIFEST_SCAFFOLD = void 0;
|
|
41
42
|
exports.adfCommand = adfCommand;
|
|
42
43
|
const fs = __importStar(require("node:fs"));
|
|
43
44
|
const path = __importStar(require("node:path"));
|
|
44
|
-
const crypto = __importStar(require("node:crypto"));
|
|
45
45
|
const adf_1 = require("@stackbilt/adf");
|
|
46
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");
|
|
47
51
|
// ============================================================================
|
|
48
52
|
// Scaffold Content
|
|
49
53
|
// ============================================================================
|
|
50
|
-
|
|
54
|
+
exports.MANIFEST_SCAFFOLD = `ADF: 0.1
|
|
51
55
|
\u{1F3AF} ROLE: Repo context router
|
|
52
56
|
|
|
53
57
|
\u{1F4E6} DEFAULT_LOAD:
|
|
@@ -62,22 +66,27 @@ const MANIFEST_SCAFFOLD = `ADF: 0.1
|
|
|
62
66
|
- Prefer smallest relevant module set.
|
|
63
67
|
- Never assume unseen modules were loaded.
|
|
64
68
|
`;
|
|
65
|
-
|
|
66
|
-
\u{1F3AF} TASK: Define universal repository rules
|
|
69
|
+
exports.CORE_SCAFFOLD = `ADF: 0.1
|
|
67
70
|
|
|
68
|
-
\u{
|
|
69
|
-
-
|
|
70
|
-
-
|
|
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
|
|
71
80
|
|
|
72
|
-
\
|
|
73
|
-
-
|
|
74
|
-
-
|
|
75
|
-
-
|
|
81
|
+
\u26A0\uFE0F CONSTRAINTS [load-bearing]:
|
|
82
|
+
- Use Conventional Commits (feat, fix, docs, chore)
|
|
83
|
+
- Never commit secrets or credentials
|
|
84
|
+
- Pure functions in library code; side effects only in entry points
|
|
76
85
|
|
|
77
|
-
\
|
|
86
|
+
\uD83D\uDCCA METRICS:
|
|
78
87
|
entry_loc: 0 / 500 [lines]
|
|
79
88
|
`;
|
|
80
|
-
|
|
89
|
+
exports.STATE_SCAFFOLD = `ADF: 0.1
|
|
81
90
|
\u{1F9E0} STATE:
|
|
82
91
|
CURRENT: Repository initialized with ADF context system
|
|
83
92
|
NEXT: Configure on-demand modules for your stack
|
|
@@ -100,15 +109,48 @@ async function adfCommand(options, args) {
|
|
|
100
109
|
case 'patch':
|
|
101
110
|
return adfPatch(options, restArgs);
|
|
102
111
|
case 'bundle':
|
|
103
|
-
return adfBundle(options, restArgs);
|
|
112
|
+
return (0, adf_bundle_1.adfBundle)(options, restArgs);
|
|
104
113
|
case 'sync':
|
|
105
|
-
return adfSync(options, restArgs);
|
|
114
|
+
return (0, adf_sync_1.adfSync)(options, restArgs);
|
|
106
115
|
case 'evidence':
|
|
107
|
-
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);
|
|
108
119
|
default:
|
|
109
|
-
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`);
|
|
110
121
|
}
|
|
111
122
|
}
|
|
123
|
+
// -- Thin pointer file content --
|
|
124
|
+
exports.POINTER_CLAUDE_MD = `# Project Context
|
|
125
|
+
|
|
126
|
+
> This project uses [ADF](https://github.com/Stackbilt-dev/charter) for AI agent context management.
|
|
127
|
+
> All stack rules, constraints, and architectural guidance live in \`.ai/\`.
|
|
128
|
+
> **Do not duplicate ADF rules here.** Only pre-ADF bootstrap content belongs in this file.
|
|
129
|
+
|
|
130
|
+
See \`.ai/manifest.adf\` for the module routing manifest.
|
|
131
|
+
|
|
132
|
+
## Environment
|
|
133
|
+
<!-- Add runtime/OS/shell-specific notes here (not stack rules) -->
|
|
134
|
+
`;
|
|
135
|
+
exports.POINTER_CURSORRULES = `# Cursor Rules
|
|
136
|
+
|
|
137
|
+
This project uses ADF (Attention-Directed Format) for context management.
|
|
138
|
+
All rules and constraints are in .ai/ \u2014 see .ai/manifest.adf for routing.
|
|
139
|
+
|
|
140
|
+
Do not add stack rules here. This file exists only as a pointer.
|
|
141
|
+
See: .ai/core.adf for universal constraints.
|
|
142
|
+
`;
|
|
143
|
+
exports.POINTER_AGENTS_MD = `# Agent Guidelines
|
|
144
|
+
|
|
145
|
+
This project uses ADF for structured agent context.
|
|
146
|
+
All architectural rules, constraints, and guidance live in \`.ai/\`.
|
|
147
|
+
|
|
148
|
+
Module manifest: .ai/manifest.adf
|
|
149
|
+
Universal rules: .ai/core.adf
|
|
150
|
+
Current state: .ai/state.adf
|
|
151
|
+
|
|
152
|
+
Do not duplicate rules from .ai/ modules into this file or other agent config files.
|
|
153
|
+
`;
|
|
112
154
|
function adfInit(options, args) {
|
|
113
155
|
const force = options.yes || args.includes('--force');
|
|
114
156
|
const aiDir = getFlag(args, '--ai-dir') || '.ai';
|
|
@@ -125,14 +167,45 @@ function adfInit(options, args) {
|
|
|
125
167
|
return index_1.EXIT_CODE.SUCCESS;
|
|
126
168
|
}
|
|
127
169
|
fs.mkdirSync(aiDir, { recursive: true });
|
|
128
|
-
fs.writeFileSync(path.join(aiDir, 'manifest.adf'), MANIFEST_SCAFFOLD);
|
|
129
|
-
fs.writeFileSync(path.join(aiDir, 'core.adf'), CORE_SCAFFOLD);
|
|
130
|
-
fs.writeFileSync(path.join(aiDir, 'state.adf'), STATE_SCAFFOLD);
|
|
170
|
+
fs.writeFileSync(path.join(aiDir, 'manifest.adf'), exports.MANIFEST_SCAFFOLD);
|
|
171
|
+
fs.writeFileSync(path.join(aiDir, 'core.adf'), exports.CORE_SCAFFOLD);
|
|
172
|
+
fs.writeFileSync(path.join(aiDir, 'state.adf'), exports.STATE_SCAFFOLD);
|
|
131
173
|
const result = {
|
|
132
174
|
created: true,
|
|
133
175
|
aiDir,
|
|
134
176
|
files: ['manifest.adf', 'core.adf', 'state.adf'],
|
|
135
177
|
};
|
|
178
|
+
// --emit-pointers: generate thin pointer files that redirect to .ai/
|
|
179
|
+
const emitPointers = args.includes('--emit-pointers') || args.includes('--pointers');
|
|
180
|
+
if (emitPointers) {
|
|
181
|
+
const pointerSpecs = [
|
|
182
|
+
{ file: 'CLAUDE.md', content: exports.POINTER_CLAUDE_MD, label: 'CLAUDE.md (thin pointer)' },
|
|
183
|
+
{ file: '.cursorrules', content: exports.POINTER_CURSORRULES, label: '.cursorrules (thin pointer)' },
|
|
184
|
+
{ file: 'agents.md', content: exports.POINTER_AGENTS_MD, label: 'agents.md (thin pointer)' },
|
|
185
|
+
];
|
|
186
|
+
const createdPointers = [];
|
|
187
|
+
const skippedPointers = [];
|
|
188
|
+
for (const spec of pointerSpecs) {
|
|
189
|
+
if (fs.existsSync(spec.file)) {
|
|
190
|
+
skippedPointers.push(spec.file);
|
|
191
|
+
}
|
|
192
|
+
else {
|
|
193
|
+
fs.writeFileSync(spec.file, spec.content);
|
|
194
|
+
createdPointers.push(spec.file);
|
|
195
|
+
result.files.push(spec.file);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
result.pointers = createdPointers;
|
|
199
|
+
if (options.format !== 'json') {
|
|
200
|
+
for (const p of createdPointers) {
|
|
201
|
+
const label = pointerSpecs.find(s => s.file === p)?.label ?? p;
|
|
202
|
+
console.log(` Generated ${label}`);
|
|
203
|
+
}
|
|
204
|
+
for (const p of skippedPointers) {
|
|
205
|
+
console.log(` Skipped ${p} (already exists)`);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
136
209
|
if (options.format === 'json') {
|
|
137
210
|
console.log(JSON.stringify({
|
|
138
211
|
...result,
|
|
@@ -258,456 +331,6 @@ function adfPatch(options, args) {
|
|
|
258
331
|
}
|
|
259
332
|
}
|
|
260
333
|
// ============================================================================
|
|
261
|
-
// adf bundle
|
|
262
|
-
// ============================================================================
|
|
263
|
-
function adfBundle(options, args) {
|
|
264
|
-
const task = getFlag(args, '--task');
|
|
265
|
-
if (!task) {
|
|
266
|
-
throw new index_1.CLIError('adf bundle requires --task "<prompt>". Usage: charter adf bundle --task "Fix React component"');
|
|
267
|
-
}
|
|
268
|
-
const aiDir = getFlag(args, '--ai-dir') || '.ai';
|
|
269
|
-
const manifestPath = path.join(aiDir, 'manifest.adf');
|
|
270
|
-
if (!fs.existsSync(manifestPath)) {
|
|
271
|
-
throw new index_1.CLIError(`manifest.adf not found at ${manifestPath}. Run: charter adf init`);
|
|
272
|
-
}
|
|
273
|
-
const manifestContent = fs.readFileSync(manifestPath, 'utf-8');
|
|
274
|
-
const manifestDoc = (0, adf_1.parseAdf)(manifestContent);
|
|
275
|
-
const manifest = (0, adf_1.parseManifest)(manifestDoc);
|
|
276
|
-
// Tokenize task into keywords (simple word split)
|
|
277
|
-
const keywords = task
|
|
278
|
-
.split(/[\s,;:()[\]{}]+/)
|
|
279
|
-
.filter(w => w.length > 1)
|
|
280
|
-
.map(w => w.replace(/[^a-zA-Z0-9]/g, ''));
|
|
281
|
-
const modulePaths = (0, adf_1.resolveModules)(manifest, keywords);
|
|
282
|
-
const readFile = (p) => fs.readFileSync(p, 'utf-8');
|
|
283
|
-
try {
|
|
284
|
-
const result = (0, adf_1.bundleModules)(aiDir, modulePaths, readFile, keywords);
|
|
285
|
-
if (options.format === 'json') {
|
|
286
|
-
const jsonOut = {
|
|
287
|
-
task,
|
|
288
|
-
keywords,
|
|
289
|
-
resolvedModules: result.resolvedModules,
|
|
290
|
-
tokenEstimate: result.tokenEstimate,
|
|
291
|
-
tokenBudget: result.tokenBudget,
|
|
292
|
-
tokenUtilization: result.tokenUtilization,
|
|
293
|
-
perModuleTokens: result.perModuleTokens,
|
|
294
|
-
triggerMatches: result.triggerMatches,
|
|
295
|
-
};
|
|
296
|
-
if (result.unmatchedModules.length > 0) {
|
|
297
|
-
jsonOut.unmatchedModules = result.unmatchedModules;
|
|
298
|
-
}
|
|
299
|
-
if (result.moduleBudgetOverruns.length > 0) {
|
|
300
|
-
jsonOut.moduleBudgetOverruns = result.moduleBudgetOverruns;
|
|
301
|
-
}
|
|
302
|
-
if (result.advisoryOnlyModules.length > 0) {
|
|
303
|
-
jsonOut.advisoryOnlyModules = result.advisoryOnlyModules;
|
|
304
|
-
}
|
|
305
|
-
if (result.manifest.cadence.length > 0) {
|
|
306
|
-
jsonOut.cadence = result.manifest.cadence;
|
|
307
|
-
}
|
|
308
|
-
console.log(JSON.stringify(jsonOut, null, 2));
|
|
309
|
-
}
|
|
310
|
-
else {
|
|
311
|
-
console.log(` Task: "${task}"`);
|
|
312
|
-
console.log(` Keywords: ${keywords.join(', ')}`);
|
|
313
|
-
console.log(` Resolved modules: ${result.resolvedModules.join(', ')}`);
|
|
314
|
-
console.log(` Token estimate: ~${result.tokenEstimate}`);
|
|
315
|
-
if (result.tokenBudget !== null) {
|
|
316
|
-
const pct = result.tokenUtilization !== null
|
|
317
|
-
? ` (${(result.tokenUtilization * 100).toFixed(0)}%)`
|
|
318
|
-
: '';
|
|
319
|
-
console.log(` Token budget: ${result.tokenBudget}${pct}`);
|
|
320
|
-
}
|
|
321
|
-
console.log('');
|
|
322
|
-
if (result.moduleBudgetOverruns.length > 0) {
|
|
323
|
-
console.log(' Module budget overruns:');
|
|
324
|
-
for (const o of result.moduleBudgetOverruns) {
|
|
325
|
-
console.log(` [!] ${o.module}: ~${o.tokens} tokens (budget: ${o.budget})`);
|
|
326
|
-
}
|
|
327
|
-
console.log('');
|
|
328
|
-
}
|
|
329
|
-
if (result.triggerMatches.length > 0) {
|
|
330
|
-
console.log(' Trigger report:');
|
|
331
|
-
for (const tm of result.triggerMatches) {
|
|
332
|
-
const icon = tm.matched ? '+' : '-';
|
|
333
|
-
const kw = tm.matchedKeywords.length > 0 ? ` [${tm.matchedKeywords.join(', ')}]` : '';
|
|
334
|
-
console.log(` [${icon}] ${tm.module} (${tm.trigger})${kw}`);
|
|
335
|
-
}
|
|
336
|
-
console.log('');
|
|
337
|
-
}
|
|
338
|
-
if (result.unmatchedModules.length > 0) {
|
|
339
|
-
console.log(' Unmatched modules (not loaded):');
|
|
340
|
-
for (const m of result.unmatchedModules) {
|
|
341
|
-
console.log(` [-] ${m}`);
|
|
342
|
-
}
|
|
343
|
-
console.log('');
|
|
344
|
-
}
|
|
345
|
-
if (result.advisoryOnlyModules.length > 0) {
|
|
346
|
-
console.log(' Advisory-only modules:');
|
|
347
|
-
for (const m of result.advisoryOnlyModules) {
|
|
348
|
-
console.log(` [!] ${m}: no load-bearing sections`);
|
|
349
|
-
}
|
|
350
|
-
console.log('');
|
|
351
|
-
}
|
|
352
|
-
if (result.manifest.cadence.length > 0) {
|
|
353
|
-
console.log(' Cadence schedule:');
|
|
354
|
-
for (const c of result.manifest.cadence) {
|
|
355
|
-
console.log(` ${c.check}: ${c.frequency}`);
|
|
356
|
-
}
|
|
357
|
-
console.log('');
|
|
358
|
-
}
|
|
359
|
-
// Output merged document
|
|
360
|
-
const output = (0, adf_1.formatAdf)(result.mergedDocument);
|
|
361
|
-
console.log(' --- Merged Context ---');
|
|
362
|
-
console.log(output);
|
|
363
|
-
}
|
|
364
|
-
return index_1.EXIT_CODE.SUCCESS;
|
|
365
|
-
}
|
|
366
|
-
catch (e) {
|
|
367
|
-
if (e instanceof Error && e.name === 'AdfBundleError') {
|
|
368
|
-
if (options.format === 'json') {
|
|
369
|
-
console.log(JSON.stringify({ error: e.message }, null, 2));
|
|
370
|
-
}
|
|
371
|
-
else {
|
|
372
|
-
console.error(` [error] ${e.message}`);
|
|
373
|
-
}
|
|
374
|
-
return index_1.EXIT_CODE.RUNTIME_ERROR;
|
|
375
|
-
}
|
|
376
|
-
throw e;
|
|
377
|
-
}
|
|
378
|
-
}
|
|
379
|
-
function adfSync(options, args) {
|
|
380
|
-
const aiDir = getFlag(args, '--ai-dir') || '.ai';
|
|
381
|
-
const checkMode = args.includes('--check');
|
|
382
|
-
const writeMode = args.includes('--write');
|
|
383
|
-
if (!checkMode && !writeMode) {
|
|
384
|
-
throw new index_1.CLIError('adf sync requires --check or --write. Usage: charter adf sync --check');
|
|
385
|
-
}
|
|
386
|
-
const manifestPath = path.join(aiDir, 'manifest.adf');
|
|
387
|
-
if (!fs.existsSync(manifestPath)) {
|
|
388
|
-
throw new index_1.CLIError(`manifest.adf not found at ${manifestPath}. Run: charter adf init`);
|
|
389
|
-
}
|
|
390
|
-
const manifestContent = fs.readFileSync(manifestPath, 'utf-8');
|
|
391
|
-
const manifestDoc = (0, adf_1.parseAdf)(manifestContent);
|
|
392
|
-
const manifest = (0, adf_1.parseManifest)(manifestDoc);
|
|
393
|
-
if (manifest.sync.length === 0) {
|
|
394
|
-
const result = {
|
|
395
|
-
aiDir,
|
|
396
|
-
lockFile: path.join(aiDir, '.adf.lock'),
|
|
397
|
-
entries: [],
|
|
398
|
-
allInSync: true,
|
|
399
|
-
written: false,
|
|
400
|
-
};
|
|
401
|
-
if (options.format === 'json') {
|
|
402
|
-
console.log(JSON.stringify(result, null, 2));
|
|
403
|
-
}
|
|
404
|
-
else {
|
|
405
|
-
console.log(' No SYNC entries in manifest. Nothing to check.');
|
|
406
|
-
}
|
|
407
|
-
return index_1.EXIT_CODE.SUCCESS;
|
|
408
|
-
}
|
|
409
|
-
const lockFile = path.join(aiDir, '.adf.lock');
|
|
410
|
-
const locked = loadLockFile(lockFile);
|
|
411
|
-
const entries = [];
|
|
412
|
-
for (const entry of manifest.sync) {
|
|
413
|
-
const sourcePath = path.join(aiDir, entry.source);
|
|
414
|
-
if (!fs.existsSync(sourcePath)) {
|
|
415
|
-
throw new index_1.CLIError(`Sync source not found: ${sourcePath}`);
|
|
416
|
-
}
|
|
417
|
-
const sourceContent = fs.readFileSync(sourcePath, 'utf-8');
|
|
418
|
-
const sourceHash = hashContent(sourceContent);
|
|
419
|
-
const lockedHash = locked[entry.source] ?? null;
|
|
420
|
-
entries.push({
|
|
421
|
-
source: entry.source,
|
|
422
|
-
target: entry.target,
|
|
423
|
-
sourceHash,
|
|
424
|
-
lockedHash,
|
|
425
|
-
inSync: lockedHash === sourceHash,
|
|
426
|
-
});
|
|
427
|
-
}
|
|
428
|
-
const allInSync = entries.every(e => e.inSync);
|
|
429
|
-
if (writeMode) {
|
|
430
|
-
const newLock = {};
|
|
431
|
-
for (const e of entries) {
|
|
432
|
-
newLock[e.source] = e.sourceHash;
|
|
433
|
-
}
|
|
434
|
-
fs.writeFileSync(lockFile, JSON.stringify(newLock, null, 2) + '\n');
|
|
435
|
-
const result = {
|
|
436
|
-
aiDir,
|
|
437
|
-
lockFile,
|
|
438
|
-
entries,
|
|
439
|
-
allInSync: true,
|
|
440
|
-
written: true,
|
|
441
|
-
};
|
|
442
|
-
if (options.format === 'json') {
|
|
443
|
-
console.log(JSON.stringify(result, null, 2));
|
|
444
|
-
}
|
|
445
|
-
else {
|
|
446
|
-
console.log(` [ok] Updated ${lockFile} with ${entries.length} hash${entries.length === 1 ? '' : 'es'}.`);
|
|
447
|
-
}
|
|
448
|
-
return index_1.EXIT_CODE.SUCCESS;
|
|
449
|
-
}
|
|
450
|
-
// --check mode
|
|
451
|
-
const result = {
|
|
452
|
-
aiDir,
|
|
453
|
-
lockFile,
|
|
454
|
-
entries,
|
|
455
|
-
allInSync,
|
|
456
|
-
written: false,
|
|
457
|
-
};
|
|
458
|
-
if (options.format === 'json') {
|
|
459
|
-
const syncOut = { ...result };
|
|
460
|
-
if (!allInSync) {
|
|
461
|
-
syncOut.nextActions = ['Regenerate targets from source .adf files', 'charter adf sync --write'];
|
|
462
|
-
}
|
|
463
|
-
console.log(JSON.stringify(syncOut, null, 2));
|
|
464
|
-
}
|
|
465
|
-
else {
|
|
466
|
-
for (const e of entries) {
|
|
467
|
-
if (e.inSync) {
|
|
468
|
-
console.log(` [ok] ${e.source} -> ${e.target} (in sync)`);
|
|
469
|
-
}
|
|
470
|
-
else if (e.lockedHash === null) {
|
|
471
|
-
console.log(` [warn] ${e.source} -> ${e.target} (no lock entry — run: charter adf sync --write)`);
|
|
472
|
-
}
|
|
473
|
-
else {
|
|
474
|
-
console.log(` [fail] ${e.source} -> ${e.target} (source changed since last sync)`);
|
|
475
|
-
}
|
|
476
|
-
}
|
|
477
|
-
if (!allInSync) {
|
|
478
|
-
console.log('');
|
|
479
|
-
console.log(' Source .adf files have changed. Regenerate targets and run: charter adf sync --write');
|
|
480
|
-
}
|
|
481
|
-
}
|
|
482
|
-
return allInSync ? index_1.EXIT_CODE.SUCCESS : index_1.EXIT_CODE.POLICY_VIOLATION;
|
|
483
|
-
}
|
|
484
|
-
function hashContent(content) {
|
|
485
|
-
return crypto.createHash('sha256').update(content).digest('hex').slice(0, 16);
|
|
486
|
-
}
|
|
487
|
-
function loadLockFile(lockFile) {
|
|
488
|
-
if (!fs.existsSync(lockFile))
|
|
489
|
-
return {};
|
|
490
|
-
try {
|
|
491
|
-
return JSON.parse(fs.readFileSync(lockFile, 'utf-8'));
|
|
492
|
-
}
|
|
493
|
-
catch {
|
|
494
|
-
return {};
|
|
495
|
-
}
|
|
496
|
-
}
|
|
497
|
-
function adfEvidence(options, args) {
|
|
498
|
-
const task = getFlag(args, '--task');
|
|
499
|
-
const aiDir = getFlag(args, '--ai-dir') || '.ai';
|
|
500
|
-
const contextJson = getFlag(args, '--context');
|
|
501
|
-
const contextFile = getFlag(args, '--context-file');
|
|
502
|
-
const autoMeasure = args.includes('--auto-measure');
|
|
503
|
-
const manifestPath = path.join(aiDir, 'manifest.adf');
|
|
504
|
-
if (!fs.existsSync(manifestPath)) {
|
|
505
|
-
throw new index_1.CLIError(`manifest.adf not found at ${manifestPath}. Run: charter adf init`);
|
|
506
|
-
}
|
|
507
|
-
const manifestContent = fs.readFileSync(manifestPath, 'utf-8');
|
|
508
|
-
const manifestDoc = (0, adf_1.parseAdf)(manifestContent);
|
|
509
|
-
const manifest = (0, adf_1.parseManifest)(manifestDoc);
|
|
510
|
-
// Resolve modules
|
|
511
|
-
let modulePaths;
|
|
512
|
-
let keywords = [];
|
|
513
|
-
if (task) {
|
|
514
|
-
keywords = task
|
|
515
|
-
.split(/[\s,;:()[\]{}]+/)
|
|
516
|
-
.filter(w => w.length > 1)
|
|
517
|
-
.map(w => w.replace(/[^a-zA-Z0-9]/g, ''));
|
|
518
|
-
modulePaths = (0, adf_1.resolveModules)(manifest, keywords);
|
|
519
|
-
}
|
|
520
|
-
else {
|
|
521
|
-
modulePaths = [...manifest.defaultLoad];
|
|
522
|
-
}
|
|
523
|
-
const readFile = (p) => fs.readFileSync(p, 'utf-8');
|
|
524
|
-
let context;
|
|
525
|
-
const rawContext = contextFile ? readJsonFlag(contextFile, '--context-file') : contextJson;
|
|
526
|
-
if (rawContext) {
|
|
527
|
-
try {
|
|
528
|
-
const parsed = JSON.parse(rawContext);
|
|
529
|
-
if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {
|
|
530
|
-
throw new Error('must be a JSON object');
|
|
531
|
-
}
|
|
532
|
-
context = parsed;
|
|
533
|
-
}
|
|
534
|
-
catch (e) {
|
|
535
|
-
const msg = e instanceof Error ? e.message : String(e);
|
|
536
|
-
throw new index_1.CLIError(`Invalid --context JSON: ${msg}`);
|
|
537
|
-
}
|
|
538
|
-
}
|
|
539
|
-
// Auto-measure: count lines in files referenced by manifest METRICS
|
|
540
|
-
// Manifest keys are UPPERCASE (parser map disambiguation), metric keys are lowercase.
|
|
541
|
-
const autoMeasured = [];
|
|
542
|
-
if (autoMeasure && manifest.metrics.length > 0) {
|
|
543
|
-
const measured = {};
|
|
544
|
-
for (const ms of manifest.metrics) {
|
|
545
|
-
const metricKey = ms.key.toLowerCase();
|
|
546
|
-
const filePath = path.resolve(ms.path);
|
|
547
|
-
if (fs.existsSync(filePath)) {
|
|
548
|
-
const content = fs.readFileSync(filePath, 'utf-8');
|
|
549
|
-
const lines = content.split('\n').length;
|
|
550
|
-
measured[metricKey] = lines;
|
|
551
|
-
autoMeasured.push({ metric: metricKey, path: ms.path, lines });
|
|
552
|
-
}
|
|
553
|
-
else {
|
|
554
|
-
autoMeasured.push({ metric: metricKey, path: ms.path, lines: null, error: 'file not found' });
|
|
555
|
-
}
|
|
556
|
-
}
|
|
557
|
-
// Merge: explicit --context wins over auto-measured
|
|
558
|
-
context = { ...measured, ...context };
|
|
559
|
-
}
|
|
560
|
-
try {
|
|
561
|
-
const bundle = (0, adf_1.bundleModules)(aiDir, modulePaths, readFile, keywords);
|
|
562
|
-
const evidence = (0, adf_1.validateConstraints)(bundle.mergedDocument, context);
|
|
563
|
-
// Check sync status
|
|
564
|
-
const lockFile = path.join(aiDir, '.adf.lock');
|
|
565
|
-
const locked = loadLockFile(lockFile);
|
|
566
|
-
const syncEntries = [];
|
|
567
|
-
for (const entry of manifest.sync) {
|
|
568
|
-
const sourcePath = path.join(aiDir, entry.source);
|
|
569
|
-
if (fs.existsSync(sourcePath)) {
|
|
570
|
-
const sourceContent = fs.readFileSync(sourcePath, 'utf-8');
|
|
571
|
-
const sourceHash = hashContent(sourceContent);
|
|
572
|
-
const lockedHash = locked[entry.source] ?? null;
|
|
573
|
-
syncEntries.push({ source: entry.source, inSync: lockedHash === sourceHash });
|
|
574
|
-
}
|
|
575
|
-
}
|
|
576
|
-
const allInSync = syncEntries.length === 0 || syncEntries.every(e => e.inSync);
|
|
577
|
-
const staleCount = syncEntries.filter(e => !e.inSync).length;
|
|
578
|
-
if (options.format === 'json') {
|
|
579
|
-
const jsonOut = {
|
|
580
|
-
aiDir,
|
|
581
|
-
resolvedModules: bundle.resolvedModules,
|
|
582
|
-
tokenEstimate: bundle.tokenEstimate,
|
|
583
|
-
tokenBudget: bundle.tokenBudget,
|
|
584
|
-
tokenUtilization: bundle.tokenUtilization,
|
|
585
|
-
constraints: evidence.constraints,
|
|
586
|
-
weightSummary: evidence.weightSummary,
|
|
587
|
-
allPassing: evidence.allPassing,
|
|
588
|
-
failCount: evidence.failCount,
|
|
589
|
-
warnCount: evidence.warnCount,
|
|
590
|
-
syncStatus: { allInSync, staleCount },
|
|
591
|
-
};
|
|
592
|
-
if (task) {
|
|
593
|
-
jsonOut.task = task;
|
|
594
|
-
jsonOut.keywords = keywords;
|
|
595
|
-
}
|
|
596
|
-
if (bundle.advisoryOnlyModules.length > 0) {
|
|
597
|
-
jsonOut.advisoryOnlyModules = bundle.advisoryOnlyModules;
|
|
598
|
-
}
|
|
599
|
-
if (autoMeasured.length > 0) {
|
|
600
|
-
jsonOut.autoMeasured = autoMeasured;
|
|
601
|
-
}
|
|
602
|
-
// Suggest logical next steps based on results
|
|
603
|
-
const nextActions = [];
|
|
604
|
-
if (!evidence.allPassing) {
|
|
605
|
-
nextActions.push('Fix failing constraints before merging');
|
|
606
|
-
}
|
|
607
|
-
if (!allInSync) {
|
|
608
|
-
nextActions.push('charter adf sync --write');
|
|
609
|
-
}
|
|
610
|
-
if (evidence.warnCount > 0) {
|
|
611
|
-
nextActions.push('Review metrics at ceiling boundary');
|
|
612
|
-
}
|
|
613
|
-
if (nextActions.length > 0) {
|
|
614
|
-
jsonOut.nextActions = nextActions;
|
|
615
|
-
}
|
|
616
|
-
console.log(JSON.stringify(jsonOut, null, 2));
|
|
617
|
-
}
|
|
618
|
-
else {
|
|
619
|
-
console.log('');
|
|
620
|
-
console.log(' ADF Evidence Report');
|
|
621
|
-
console.log(' ===================');
|
|
622
|
-
console.log(` Modules loaded: ${bundle.resolvedModules.join(', ')}`);
|
|
623
|
-
console.log(` Token estimate: ~${bundle.tokenEstimate}`);
|
|
624
|
-
if (bundle.tokenBudget !== null) {
|
|
625
|
-
const pct = bundle.tokenUtilization !== null
|
|
626
|
-
? ` (${(bundle.tokenUtilization * 100).toFixed(0)}%)`
|
|
627
|
-
: '';
|
|
628
|
-
console.log(` Token budget: ${bundle.tokenBudget}${pct}`);
|
|
629
|
-
}
|
|
630
|
-
console.log('');
|
|
631
|
-
// Auto-measured metrics
|
|
632
|
-
if (autoMeasured.length > 0) {
|
|
633
|
-
console.log(' Auto-measured:');
|
|
634
|
-
for (const m of autoMeasured) {
|
|
635
|
-
if (m.lines !== null) {
|
|
636
|
-
console.log(` ${m.metric}: ${m.lines} lines (${m.path})`);
|
|
637
|
-
}
|
|
638
|
-
else {
|
|
639
|
-
console.log(` ${m.metric}: [file not found] (${m.path})`);
|
|
640
|
-
}
|
|
641
|
-
}
|
|
642
|
-
console.log('');
|
|
643
|
-
}
|
|
644
|
-
// Weight summary
|
|
645
|
-
console.log(' Section weights:');
|
|
646
|
-
console.log(` Load-bearing: ${evidence.weightSummary.loadBearing}`);
|
|
647
|
-
console.log(` Advisory: ${evidence.weightSummary.advisory}`);
|
|
648
|
-
console.log(` Unweighted: ${evidence.weightSummary.unweighted}`);
|
|
649
|
-
console.log('');
|
|
650
|
-
// Advisory-only module warnings
|
|
651
|
-
if (bundle.advisoryOnlyModules.length > 0) {
|
|
652
|
-
console.log(' Advisory-only modules:');
|
|
653
|
-
for (const m of bundle.advisoryOnlyModules) {
|
|
654
|
-
console.log(` [!] ${m}: no load-bearing sections`);
|
|
655
|
-
}
|
|
656
|
-
console.log('');
|
|
657
|
-
}
|
|
658
|
-
// Constraints
|
|
659
|
-
if (evidence.constraints.length > 0) {
|
|
660
|
-
console.log(' Constraints:');
|
|
661
|
-
for (const c of evidence.constraints) {
|
|
662
|
-
const icon = c.status === 'pass' ? 'ok' : c.status === 'warn' ? 'WARN' : 'FAIL';
|
|
663
|
-
console.log(` [${icon}] ${c.message}`);
|
|
664
|
-
}
|
|
665
|
-
}
|
|
666
|
-
else {
|
|
667
|
-
console.log(' Constraints: (none)');
|
|
668
|
-
}
|
|
669
|
-
console.log('');
|
|
670
|
-
// Sync status
|
|
671
|
-
if (syncEntries.length > 0) {
|
|
672
|
-
if (allInSync) {
|
|
673
|
-
console.log(' Sync: all sources in sync');
|
|
674
|
-
}
|
|
675
|
-
else {
|
|
676
|
-
console.log(` Sync: ${staleCount} source${staleCount === 1 ? '' : 's'} out of sync`);
|
|
677
|
-
}
|
|
678
|
-
}
|
|
679
|
-
else {
|
|
680
|
-
console.log(' Sync: no sync entries configured');
|
|
681
|
-
}
|
|
682
|
-
console.log('');
|
|
683
|
-
// Verdict
|
|
684
|
-
const verdict = evidence.allPassing ? 'PASS' : 'FAIL';
|
|
685
|
-
console.log(` Verdict: ${verdict}`);
|
|
686
|
-
if (evidence.warnCount > 0) {
|
|
687
|
-
console.log(` (${evidence.warnCount} warning${evidence.warnCount === 1 ? '' : 's'} — at ceiling boundary)`);
|
|
688
|
-
}
|
|
689
|
-
console.log('');
|
|
690
|
-
}
|
|
691
|
-
// CI mode: exit 1 on constraint failures
|
|
692
|
-
if (options.ciMode && !evidence.allPassing) {
|
|
693
|
-
return index_1.EXIT_CODE.POLICY_VIOLATION;
|
|
694
|
-
}
|
|
695
|
-
return index_1.EXIT_CODE.SUCCESS;
|
|
696
|
-
}
|
|
697
|
-
catch (e) {
|
|
698
|
-
if (e instanceof Error && e.name === 'AdfBundleError') {
|
|
699
|
-
if (options.format === 'json') {
|
|
700
|
-
console.log(JSON.stringify({ error: e.message }, null, 2));
|
|
701
|
-
}
|
|
702
|
-
else {
|
|
703
|
-
console.error(` [error] ${e.message}`);
|
|
704
|
-
}
|
|
705
|
-
return index_1.EXIT_CODE.RUNTIME_ERROR;
|
|
706
|
-
}
|
|
707
|
-
throw e;
|
|
708
|
-
}
|
|
709
|
-
}
|
|
710
|
-
// ============================================================================
|
|
711
334
|
// Helpers
|
|
712
335
|
// ============================================================================
|
|
713
336
|
function getFlag(args, flag) {
|
|
@@ -728,8 +351,9 @@ function printHelp() {
|
|
|
728
351
|
console.log(' charter adf — Attention-Directed Format tools');
|
|
729
352
|
console.log('');
|
|
730
353
|
console.log(' Usage:');
|
|
731
|
-
console.log(' charter adf init [--ai-dir <dir>] [--force]');
|
|
354
|
+
console.log(' charter adf init [--ai-dir <dir>] [--force] [--emit-pointers]');
|
|
732
355
|
console.log(' Scaffold .ai/ directory with manifest, core, and state modules.');
|
|
356
|
+
console.log(' --emit-pointers: also generate thin pointer files (CLAUDE.md, .cursorrules, agents.md)');
|
|
733
357
|
console.log('');
|
|
734
358
|
console.log(' charter adf fmt <file> [--check] [--write]');
|
|
735
359
|
console.log(' Parse and reformat an ADF file to canonical form.');
|
|
@@ -750,6 +374,9 @@ function printHelp() {
|
|
|
750
374
|
console.log(' charter adf sync --write [--ai-dir <dir>]');
|
|
751
375
|
console.log(' Update .adf.lock with current source hashes.');
|
|
752
376
|
console.log('');
|
|
377
|
+
console.log(' charter adf sync --explain');
|
|
378
|
+
console.log(' Show .adf.lock schema documentation.');
|
|
379
|
+
console.log('');
|
|
753
380
|
console.log(' charter adf evidence [--task "<prompt>"] [--ai-dir <dir>] [--auto-measure]');
|
|
754
381
|
console.log(' [--context \'{"key": value}\']');
|
|
755
382
|
console.log(' Validate metric constraints and produce a structured evidence report.');
|
|
@@ -759,5 +386,15 @@ function printHelp() {
|
|
|
759
386
|
console.log(' --context-file: read --context JSON from a file instead.');
|
|
760
387
|
console.log(' In --ci mode, exit 1 if any constraint fails.');
|
|
761
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('');
|
|
762
399
|
}
|
|
763
400
|
//# sourceMappingURL=adf.js.map
|