contract-driven-delivery 2.0.10 → 2.0.11
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/CHANGELOG.md +39 -0
- package/dist/cli/index.js +102 -100
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,44 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [2.0.11] - 2026-05-04
|
|
4
|
+
|
|
5
|
+
Final portability fix in the digest series. After 2.0.10 made digests
|
|
6
|
+
repo-relative and content-keyed, a real consumer repo on Windows
|
|
7
|
+
(`core.autocrlf=true`) still produced different digests than the same
|
|
8
|
+
repo on Linux/Mac (`core.autocrlf=false`) — because the file BYTES
|
|
9
|
+
differ even when the file content is logically identical.
|
|
10
|
+
|
|
11
|
+
### Fixed
|
|
12
|
+
|
|
13
|
+
- **All hash inputs are now line-ending normalized**. `\r\n` and stand-alone
|
|
14
|
+
`\r` are converted to `\n` before SHA-256 is computed. Applied uniformly
|
|
15
|
+
across the four places that hash files for cdd-kit's digests:
|
|
16
|
+
- `inputsDigest()` in `src/commands/context-scan.ts`
|
|
17
|
+
(project-map / contracts-index)
|
|
18
|
+
- `inputDigest()` in `src/commands/doctor.ts`
|
|
19
|
+
(freshness check against committed indexes)
|
|
20
|
+
- `inputsDigest()` in `src/commands/new-change.ts`
|
|
21
|
+
(auto-rerun decision in /cdd-new flow)
|
|
22
|
+
- `computeSourcesDigest()` in `src/commands/code-map.ts`
|
|
23
|
+
(`# sources-digest:` header in code-map.yml)
|
|
24
|
+
|
|
25
|
+
All four now share `src/utils/digest.ts → sha256OfFileNormalized()`,
|
|
26
|
+
so the rule is in exactly one place.
|
|
27
|
+
|
|
28
|
+
### Migration
|
|
29
|
+
|
|
30
|
+
After upgrading, re-run **once**:
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
cdd-kit context-scan
|
|
34
|
+
cdd-kit code-map
|
|
35
|
+
git add specs/context/ .cdd/code-map.yml
|
|
36
|
+
git commit -m "chore: regenerate indexes & code-map (cdd-kit 2.0.11)"
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
From then on, fresh clones on any OS / autocrlf setting produce identical
|
|
40
|
+
digests, eliminating the last source of false-positive doctor warnings.
|
|
41
|
+
|
|
3
42
|
## [2.0.10] - 2026-05-04
|
|
4
43
|
|
|
5
44
|
Two more context-scan determinism bugs, both surfaced verifying the 2.0.9
|
package/dist/cli/index.js
CHANGED
|
@@ -376,27 +376,44 @@ var init_update = __esm({
|
|
|
376
376
|
}
|
|
377
377
|
});
|
|
378
378
|
|
|
379
|
-
// src/
|
|
380
|
-
|
|
381
|
-
__export(context_scan_exports, {
|
|
382
|
-
contextScan: () => contextScan
|
|
383
|
-
});
|
|
384
|
-
import { existsSync as existsSync7, mkdirSync as mkdirSync4, readFileSync as readFileSync6, readdirSync as readdirSync4, writeFileSync as writeFileSync3 } from "fs";
|
|
379
|
+
// src/utils/digest.ts
|
|
380
|
+
import { readFileSync as readFileSync6 } from "fs";
|
|
385
381
|
import { createHash as createHash2 } from "crypto";
|
|
386
|
-
|
|
387
|
-
|
|
382
|
+
function normalizeContentForHash(buf) {
|
|
383
|
+
if (!buf.includes(13))
|
|
384
|
+
return buf;
|
|
385
|
+
const text = buf.toString("utf8").replace(/\r\n/g, "\n").replace(/\r/g, "\n");
|
|
386
|
+
return Buffer.from(text, "utf8");
|
|
387
|
+
}
|
|
388
|
+
function sha256OfFileNormalized(path) {
|
|
389
|
+
let buf;
|
|
388
390
|
try {
|
|
389
|
-
|
|
391
|
+
buf = readFileSync6(path);
|
|
390
392
|
} catch {
|
|
391
393
|
return "";
|
|
392
394
|
}
|
|
395
|
+
return createHash2("sha256").update(normalizeContentForHash(buf)).digest("hex");
|
|
393
396
|
}
|
|
397
|
+
var init_digest = __esm({
|
|
398
|
+
"src/utils/digest.ts"() {
|
|
399
|
+
"use strict";
|
|
400
|
+
}
|
|
401
|
+
});
|
|
402
|
+
|
|
403
|
+
// src/commands/context-scan.ts
|
|
404
|
+
var context_scan_exports = {};
|
|
405
|
+
__export(context_scan_exports, {
|
|
406
|
+
contextScan: () => contextScan
|
|
407
|
+
});
|
|
408
|
+
import { existsSync as existsSync7, mkdirSync as mkdirSync4, readFileSync as readFileSync7, readdirSync as readdirSync4, writeFileSync as writeFileSync3 } from "fs";
|
|
409
|
+
import { createHash as createHash3 } from "crypto";
|
|
410
|
+
import { basename, dirname as dirname3, join as join8, relative as relative2 } from "path";
|
|
394
411
|
function inputsDigest(paths, cwd) {
|
|
395
412
|
const combined = paths.slice().sort().map((p) => {
|
|
396
413
|
const rel = relative2(cwd, p).replace(/\\/g, "/");
|
|
397
|
-
return `${rel}:${
|
|
414
|
+
return `${rel}:${sha256OfFileNormalized(p)}`;
|
|
398
415
|
}).join("\n");
|
|
399
|
-
return
|
|
416
|
+
return createHash3("sha256").update(combined).digest("hex");
|
|
400
417
|
}
|
|
401
418
|
function stripGlobSuffix(pattern) {
|
|
402
419
|
return pattern.replace(/\/\*\*$/, "").replace(/\/\*$/, "");
|
|
@@ -406,7 +423,7 @@ function getForbiddenPaths(cwd) {
|
|
|
406
423
|
const policyPath = join8(cwd, ".cdd", "context-policy.json");
|
|
407
424
|
try {
|
|
408
425
|
if (existsSync7(policyPath)) {
|
|
409
|
-
const policy = JSON.parse(
|
|
426
|
+
const policy = JSON.parse(readFileSync7(policyPath, "utf8"));
|
|
410
427
|
for (const pattern of policy.forbiddenPaths ?? []) {
|
|
411
428
|
forbidden.add(stripGlobSuffix(pattern));
|
|
412
429
|
}
|
|
@@ -580,7 +597,7 @@ async function contextScan(opts = {}) {
|
|
|
580
597
|
for (const file of contractFiles) {
|
|
581
598
|
const relPath = relative2(cwd, file).replace(/\\/g, "/");
|
|
582
599
|
const dir = dirname3(relPath).replace(/\\/g, "/");
|
|
583
|
-
const { title, summary, metadata } = parseContractMetadata(
|
|
600
|
+
const { title, summary, metadata } = parseContractMetadata(readFileSync7(file, "utf8"));
|
|
584
601
|
const contractType = deriveContractType(relPath, metadata);
|
|
585
602
|
const owner = metadata.owner ?? "unknown";
|
|
586
603
|
const surface2 = metadata.surface ?? dir;
|
|
@@ -647,6 +664,7 @@ var init_context_scan = __esm({
|
|
|
647
664
|
"src/commands/context-scan.ts"() {
|
|
648
665
|
"use strict";
|
|
649
666
|
init_logger();
|
|
667
|
+
init_digest();
|
|
650
668
|
DEFAULT_FORBIDDEN = [
|
|
651
669
|
".claude",
|
|
652
670
|
".git",
|
|
@@ -7482,7 +7500,7 @@ var require_dist = __commonJS({
|
|
|
7482
7500
|
});
|
|
7483
7501
|
|
|
7484
7502
|
// src/code-map/config.ts
|
|
7485
|
-
import { existsSync as existsSync10, readFileSync as
|
|
7503
|
+
import { existsSync as existsSync10, readFileSync as readFileSync9 } from "fs";
|
|
7486
7504
|
import { join as join11 } from "path";
|
|
7487
7505
|
import { load as yamlLoad } from "js-yaml";
|
|
7488
7506
|
function asStringArray(value, key, where) {
|
|
@@ -7509,7 +7527,7 @@ function loadCodeMapConfig(cwd) {
|
|
|
7509
7527
|
}
|
|
7510
7528
|
let text;
|
|
7511
7529
|
try {
|
|
7512
|
-
text =
|
|
7530
|
+
text = readFileSync9(filePath, "utf8");
|
|
7513
7531
|
} catch (err) {
|
|
7514
7532
|
throw new Error(`failed to read ${CONFIG_REL_PATH}: ${err.message}`);
|
|
7515
7533
|
}
|
|
@@ -7993,7 +8011,7 @@ __export(javascript_exports, {
|
|
|
7993
8011
|
parseJsSource: () => parseJsSource,
|
|
7994
8012
|
parseSourceWithPlugins: () => parseSourceWithPlugins
|
|
7995
8013
|
});
|
|
7996
|
-
import { readFileSync as
|
|
8014
|
+
import { readFileSync as readFileSync11 } from "fs";
|
|
7997
8015
|
import { parse } from "@babel/parser";
|
|
7998
8016
|
function parseSourceWithPlugins(source, plugins) {
|
|
7999
8017
|
return parse(source, {
|
|
@@ -8295,7 +8313,7 @@ var init_javascript = __esm({
|
|
|
8295
8313
|
async scan(absolutePath, repoRoot) {
|
|
8296
8314
|
let source;
|
|
8297
8315
|
try {
|
|
8298
|
-
source =
|
|
8316
|
+
source = readFileSync11(absolutePath, "utf8");
|
|
8299
8317
|
} catch (err) {
|
|
8300
8318
|
throw err;
|
|
8301
8319
|
}
|
|
@@ -8315,7 +8333,7 @@ var typescript_exports = {};
|
|
|
8315
8333
|
__export(typescript_exports, {
|
|
8316
8334
|
tsScanner: () => tsScanner
|
|
8317
8335
|
});
|
|
8318
|
-
import { readFileSync as
|
|
8336
|
+
import { readFileSync as readFileSync12 } from "fs";
|
|
8319
8337
|
var TypeScriptScanner, tsScanner;
|
|
8320
8338
|
var init_typescript = __esm({
|
|
8321
8339
|
"src/code-map/scanners/typescript.ts"() {
|
|
@@ -8327,7 +8345,7 @@ var init_typescript = __esm({
|
|
|
8327
8345
|
async scan(absolutePath, repoRoot) {
|
|
8328
8346
|
let source;
|
|
8329
8347
|
try {
|
|
8330
|
-
source =
|
|
8348
|
+
source = readFileSync12(absolutePath, "utf8");
|
|
8331
8349
|
} catch (err) {
|
|
8332
8350
|
throw err;
|
|
8333
8351
|
}
|
|
@@ -8363,7 +8381,7 @@ var vue_exports = {};
|
|
|
8363
8381
|
__export(vue_exports, {
|
|
8364
8382
|
vueScanner: () => vueScanner
|
|
8365
8383
|
});
|
|
8366
|
-
import { readFileSync as
|
|
8384
|
+
import { readFileSync as readFileSync13 } from "fs";
|
|
8367
8385
|
import { parse as parse2 } from "@vue/compiler-sfc";
|
|
8368
8386
|
var VueScanner, vueScanner;
|
|
8369
8387
|
var init_vue = __esm({
|
|
@@ -8376,7 +8394,7 @@ var init_vue = __esm({
|
|
|
8376
8394
|
async scan(absolutePath, repoRoot) {
|
|
8377
8395
|
let source;
|
|
8378
8396
|
try {
|
|
8379
|
-
source =
|
|
8397
|
+
source = readFileSync13(absolutePath, "utf8");
|
|
8380
8398
|
} catch (err) {
|
|
8381
8399
|
throw err;
|
|
8382
8400
|
}
|
|
@@ -8463,24 +8481,19 @@ __export(code_map_exports, {
|
|
|
8463
8481
|
codeMap: () => codeMap,
|
|
8464
8482
|
computeSourcesDigest: () => computeSourcesDigest
|
|
8465
8483
|
});
|
|
8466
|
-
import { existsSync as existsSync11, mkdirSync as mkdirSync5, readFileSync as
|
|
8484
|
+
import { existsSync as existsSync11, mkdirSync as mkdirSync5, readFileSync as readFileSync14, writeFileSync as writeFileSync6 } from "fs";
|
|
8467
8485
|
import { resolve, dirname as dirname4, relative as relative5 } from "path";
|
|
8468
|
-
import { createHash as
|
|
8486
|
+
import { createHash as createHash5 } from "crypto";
|
|
8469
8487
|
import { createRequire } from "module";
|
|
8470
8488
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
8471
8489
|
import { join as join14 } from "path";
|
|
8472
8490
|
function computeSourcesDigest(absolutePaths, cwd) {
|
|
8473
8491
|
const lines = absolutePaths.slice().sort().map((p) => {
|
|
8474
8492
|
const rel = relative5(cwd, p).replace(/\\/g, "/");
|
|
8475
|
-
|
|
8476
|
-
try {
|
|
8477
|
-
contentHash = createHash4("sha256").update(readFileSync13(p)).digest("hex");
|
|
8478
|
-
} catch {
|
|
8479
|
-
contentHash = "missing";
|
|
8480
|
-
}
|
|
8493
|
+
const contentHash = sha256OfFileNormalized(p) || "missing";
|
|
8481
8494
|
return `${rel}:${contentHash}`;
|
|
8482
8495
|
});
|
|
8483
|
-
return
|
|
8496
|
+
return createHash5("sha256").update(lines.join("\n")).digest("hex");
|
|
8484
8497
|
}
|
|
8485
8498
|
async function codeMap(opts) {
|
|
8486
8499
|
const root = resolve(process.cwd(), opts.path);
|
|
@@ -8552,7 +8565,7 @@ async function codeMap(opts) {
|
|
|
8552
8565
|
log.warn(`${w.path}: ${w.message}`);
|
|
8553
8566
|
}
|
|
8554
8567
|
if (opts.check) {
|
|
8555
|
-
const existing = existsSync11(opts.out) ?
|
|
8568
|
+
const existing = existsSync11(opts.out) ? readFileSync14(opts.out, "utf8") : "";
|
|
8556
8569
|
const normalize = (s) => s.replace(/^# generated: [^\n]+\n/m, "# generated: <normalized>\n");
|
|
8557
8570
|
if (normalize(existing) !== normalize(yamlBody)) {
|
|
8558
8571
|
log.error(`code-map out of date: ${opts.out} would change. Run \`cdd-kit code-map\` to regenerate.`);
|
|
@@ -8574,9 +8587,10 @@ var init_code_map = __esm({
|
|
|
8574
8587
|
init_yaml_writer();
|
|
8575
8588
|
init_orchestrator();
|
|
8576
8589
|
init_config();
|
|
8590
|
+
init_digest();
|
|
8577
8591
|
_require = createRequire(import.meta.url);
|
|
8578
8592
|
_pkgPath = join14(fileURLToPath2(import.meta.url), "..", "..", "..", "package.json");
|
|
8579
|
-
_pkg = JSON.parse(
|
|
8593
|
+
_pkg = JSON.parse(readFileSync14(_pkgPath, "utf8"));
|
|
8580
8594
|
}
|
|
8581
8595
|
});
|
|
8582
8596
|
|
|
@@ -8585,7 +8599,7 @@ var freshness_exports = {};
|
|
|
8585
8599
|
__export(freshness_exports, {
|
|
8586
8600
|
checkCodeMapFreshness: () => checkCodeMapFreshness
|
|
8587
8601
|
});
|
|
8588
|
-
import { existsSync as existsSync12, readFileSync as
|
|
8602
|
+
import { existsSync as existsSync12, readFileSync as readFileSync15, statSync as statSync3 } from "fs";
|
|
8589
8603
|
import { join as join15 } from "path";
|
|
8590
8604
|
function checkCodeMapFreshness(cwd, mapRel = ".cdd/code-map.yml", include, exclude) {
|
|
8591
8605
|
const mapPath = join15(cwd, mapRel);
|
|
@@ -8641,7 +8655,7 @@ function checkCodeMapFreshness(cwd, mapRel = ".cdd/code-map.yml", include, exclu
|
|
|
8641
8655
|
}
|
|
8642
8656
|
function readSourcesDigest(mapPath) {
|
|
8643
8657
|
try {
|
|
8644
|
-
const head =
|
|
8658
|
+
const head = readFileSync15(mapPath, "utf8").slice(0, 2048);
|
|
8645
8659
|
const m = head.match(/^# sources-digest:\s*([a-f0-9]+)/m);
|
|
8646
8660
|
return m ? m[1] : null;
|
|
8647
8661
|
} catch {
|
|
@@ -8663,7 +8677,7 @@ __export(migrate_exports, {
|
|
|
8663
8677
|
migrate: () => migrate
|
|
8664
8678
|
});
|
|
8665
8679
|
import { join as join18 } from "path";
|
|
8666
|
-
import { cpSync as cpSync2, existsSync as existsSync15, mkdirSync as mkdirSync7, readdirSync as readdirSync8, readFileSync as
|
|
8680
|
+
import { cpSync as cpSync2, existsSync as existsSync15, mkdirSync as mkdirSync7, readdirSync as readdirSync8, readFileSync as readFileSync18, renameSync, rmSync as rmSync2, writeFileSync as writeFileSync8 } from "fs";
|
|
8667
8681
|
import yaml3 from "js-yaml";
|
|
8668
8682
|
function backupChangeDir(cwd, changeId, sessionStamp) {
|
|
8669
8683
|
const backupRoot = join18(cwd, ".cdd", "migrate-backup", sessionStamp);
|
|
@@ -8812,7 +8826,7 @@ function migrateTasksFile(changeId, changeDir, enableContextGovernance, detected
|
|
|
8812
8826
|
warnings.push("tasks.md not found and tasks.yml missing \u2014 skipping tasks migration");
|
|
8813
8827
|
return;
|
|
8814
8828
|
}
|
|
8815
|
-
const raw =
|
|
8829
|
+
const raw = readFileSync18(legacyPath, "utf8");
|
|
8816
8830
|
const fm = parseLegacyFrontmatter(raw);
|
|
8817
8831
|
const bodyMatch = raw.match(/^---\r?\n[\s\S]*?\r?\n---\r?\n?([\s\S]*)$/);
|
|
8818
8832
|
const body = bodyMatch ? bodyMatch[1] : raw;
|
|
@@ -8906,7 +8920,7 @@ function migrateAgentLogs(changeDir, changed, pendingWrites, pendingDeletes) {
|
|
|
8906
8920
|
const yamlFull = join18(agentLogDir, yamlName);
|
|
8907
8921
|
if (existsSync15(yamlFull))
|
|
8908
8922
|
continue;
|
|
8909
|
-
const raw =
|
|
8923
|
+
const raw = readFileSync18(fullPath, "utf8");
|
|
8910
8924
|
const parsed = parseLegacyAgentLog(raw);
|
|
8911
8925
|
const yamlOut = yaml3.dump(parsed, { lineWidth: -1, noRefs: true });
|
|
8912
8926
|
pendingWrites.push({ path: yamlFull, content: yamlOut });
|
|
@@ -8922,7 +8936,7 @@ function migrateOne(changeId, changeDir, enableContextGovernance) {
|
|
|
8922
8936
|
let detectedTier = null;
|
|
8923
8937
|
const classifPath = join18(changeDir, "change-classification.md");
|
|
8924
8938
|
if (existsSync15(classifPath)) {
|
|
8925
|
-
const content =
|
|
8939
|
+
const content = readFileSync18(classifPath, "utf8");
|
|
8926
8940
|
const hasNewTierFormat = /^## Tier\s*\n\s*-\s*\d\s*$/m.test(content);
|
|
8927
8941
|
const oldMatch = content.match(/\*\*Tier[:\*]+\s*(?:Tier\s*)?(\d)/i) ?? content.match(/^-?\s*Tier:\s*(?:Tier\s*)?(\d)/mi);
|
|
8928
8942
|
if (oldMatch)
|
|
@@ -9087,7 +9101,7 @@ function ensureGitignoreEntry(cwd, entry) {
|
|
|
9087
9101
|
const re = new RegExp(`^\\s*${trimmed.replace(/[.*+?^${}()|[\\]\\\\]/g, "\\$&")}\\s*$`, "m");
|
|
9088
9102
|
let existing = "";
|
|
9089
9103
|
if (existsSync15(path))
|
|
9090
|
-
existing =
|
|
9104
|
+
existing = readFileSync18(path, "utf8");
|
|
9091
9105
|
if (re.test(existing))
|
|
9092
9106
|
return false;
|
|
9093
9107
|
const sep = existing.length > 0 && !existing.endsWith("\n") ? "\n" : "";
|
|
@@ -9112,7 +9126,7 @@ var upgrade_exports = {};
|
|
|
9112
9126
|
__export(upgrade_exports, {
|
|
9113
9127
|
upgrade: () => upgrade
|
|
9114
9128
|
});
|
|
9115
|
-
import { existsSync as existsSync16, mkdirSync as mkdirSync8, readdirSync as readdirSync9, copyFileSync as copyFileSync3, readFileSync as
|
|
9129
|
+
import { existsSync as existsSync16, mkdirSync as mkdirSync8, readdirSync as readdirSync9, copyFileSync as copyFileSync3, readFileSync as readFileSync19, writeFileSync as writeFileSync9 } from "fs";
|
|
9116
9130
|
import { dirname as dirname5, join as join19, relative as relative6 } from "path";
|
|
9117
9131
|
function planMissingFiles(srcDir, destDir, label, planned) {
|
|
9118
9132
|
if (!existsSync16(srcDir))
|
|
@@ -9203,7 +9217,7 @@ async function upgrade(opts = {}) {
|
|
|
9203
9217
|
if (existsSync16(modelPolicyPath)) {
|
|
9204
9218
|
let existing = {};
|
|
9205
9219
|
try {
|
|
9206
|
-
existing = JSON.parse(
|
|
9220
|
+
existing = JSON.parse(readFileSync19(modelPolicyPath, "utf8"));
|
|
9207
9221
|
} catch {
|
|
9208
9222
|
}
|
|
9209
9223
|
const merged = {
|
|
@@ -9243,11 +9257,11 @@ var refresh_exports = {};
|
|
|
9243
9257
|
__export(refresh_exports, {
|
|
9244
9258
|
refresh: () => refresh
|
|
9245
9259
|
});
|
|
9246
|
-
import { existsSync as existsSync17, mkdirSync as mkdirSync9, readdirSync as readdirSync10, copyFileSync as copyFileSync4, readFileSync as
|
|
9260
|
+
import { existsSync as existsSync17, mkdirSync as mkdirSync9, readdirSync as readdirSync10, copyFileSync as copyFileSync4, readFileSync as readFileSync20, writeFileSync as writeFileSync10 } from "fs";
|
|
9247
9261
|
import { dirname as dirname6, join as join20, relative as relative7 } from "path";
|
|
9248
|
-
import { createHash as
|
|
9262
|
+
import { createHash as createHash6 } from "crypto";
|
|
9249
9263
|
function fileHash2(filePath) {
|
|
9250
|
-
return
|
|
9264
|
+
return createHash6("sha256").update(readFileSync20(filePath)).digest("hex");
|
|
9251
9265
|
}
|
|
9252
9266
|
function planForceRefresh(srcDir, destDir, sectionLabel) {
|
|
9253
9267
|
const plan = [];
|
|
@@ -9299,7 +9313,7 @@ function ensureGitignoreEntry2(cwd, entry) {
|
|
|
9299
9313
|
const re = new RegExp(`^\\s*${trimmed.replace(/[.*+?^${}()|[\\]\\\\]/g, "\\$&")}\\s*$`, "m");
|
|
9300
9314
|
let existing = "";
|
|
9301
9315
|
if (existsSync17(path))
|
|
9302
|
-
existing =
|
|
9316
|
+
existing = readFileSync20(path, "utf8");
|
|
9303
9317
|
if (re.test(existing))
|
|
9304
9318
|
return false;
|
|
9305
9319
|
const sep = existing.length > 0 && !existing.endsWith("\n") ? "\n" : "";
|
|
@@ -9355,7 +9369,7 @@ function resyncModelPolicy(cwd) {
|
|
|
9355
9369
|
const desired = {};
|
|
9356
9370
|
const agentFiles = readdirSync10(AGENTS_HOME, { withFileTypes: true }).filter((d) => d.isFile() && d.name.endsWith(".md"));
|
|
9357
9371
|
for (const f of agentFiles) {
|
|
9358
|
-
const content =
|
|
9372
|
+
const content = readFileSync20(join20(AGENTS_HOME, f.name), "utf8");
|
|
9359
9373
|
const fm = parseAgentFrontmatter(content);
|
|
9360
9374
|
if (fm.name && fm.model)
|
|
9361
9375
|
desired[fm.name] = fm.model;
|
|
@@ -9365,7 +9379,7 @@ function resyncModelPolicy(cwd) {
|
|
|
9365
9379
|
let existing = {};
|
|
9366
9380
|
if (existsSync17(policyPath)) {
|
|
9367
9381
|
try {
|
|
9368
|
-
existing = JSON.parse(
|
|
9382
|
+
existing = JSON.parse(readFileSync20(policyPath, "utf8"));
|
|
9369
9383
|
} catch {
|
|
9370
9384
|
}
|
|
9371
9385
|
}
|
|
@@ -9574,8 +9588,8 @@ var doctor_exports = {};
|
|
|
9574
9588
|
__export(doctor_exports, {
|
|
9575
9589
|
doctor: () => doctor
|
|
9576
9590
|
});
|
|
9577
|
-
import { existsSync as existsSync18, readdirSync as readdirSync11, readFileSync as
|
|
9578
|
-
import { createHash as
|
|
9591
|
+
import { existsSync as existsSync18, readdirSync as readdirSync11, readFileSync as readFileSync21 } from "fs";
|
|
9592
|
+
import { createHash as createHash7 } from "crypto";
|
|
9579
9593
|
import { join as join21, relative as relative8 } from "path";
|
|
9580
9594
|
function fileExists(cwd, relPath) {
|
|
9581
9595
|
return existsSync18(join21(cwd, relPath));
|
|
@@ -9592,24 +9606,17 @@ function findFiles(dir, predicate, found = []) {
|
|
|
9592
9606
|
}
|
|
9593
9607
|
return found;
|
|
9594
9608
|
}
|
|
9595
|
-
function sha256OfFile3(path) {
|
|
9596
|
-
try {
|
|
9597
|
-
return createHash6("sha256").update(readFileSync20(path)).digest("hex");
|
|
9598
|
-
} catch {
|
|
9599
|
-
return "";
|
|
9600
|
-
}
|
|
9601
|
-
}
|
|
9602
9609
|
function inputDigest(paths, cwd) {
|
|
9603
9610
|
const combined = paths.slice().sort().map((p) => {
|
|
9604
9611
|
const rel = relative8(cwd, p).replace(/\\/g, "/");
|
|
9605
|
-
return `${rel}:${
|
|
9612
|
+
return `${rel}:${sha256OfFileNormalized(p)}`;
|
|
9606
9613
|
}).join("\n");
|
|
9607
|
-
return
|
|
9614
|
+
return createHash7("sha256").update(combined).digest("hex");
|
|
9608
9615
|
}
|
|
9609
9616
|
function readContextIndexMetadata(filePath) {
|
|
9610
9617
|
if (!existsSync18(filePath))
|
|
9611
9618
|
return {};
|
|
9612
|
-
const text =
|
|
9619
|
+
const text = readFileSync21(filePath, "utf8");
|
|
9613
9620
|
const out = {};
|
|
9614
9621
|
const digestMatch = text.match(/^inputs-digest:\s*([a-f0-9]+)/m);
|
|
9615
9622
|
if (digestMatch)
|
|
@@ -9674,7 +9681,7 @@ function checkContextFreshness(cwd) {
|
|
|
9674
9681
|
}
|
|
9675
9682
|
function readAgentModel(path) {
|
|
9676
9683
|
try {
|
|
9677
|
-
const text =
|
|
9684
|
+
const text = readFileSync21(path, "utf8");
|
|
9678
9685
|
const m = text.match(/^model:\s*(\S+)/m);
|
|
9679
9686
|
return m ? m[1] : null;
|
|
9680
9687
|
} catch {
|
|
@@ -9687,7 +9694,7 @@ function checkModelPolicyDrift(cwd) {
|
|
|
9687
9694
|
return [];
|
|
9688
9695
|
let policy;
|
|
9689
9696
|
try {
|
|
9690
|
-
policy = JSON.parse(
|
|
9697
|
+
policy = JSON.parse(readFileSync21(policyPath, "utf8"));
|
|
9691
9698
|
} catch {
|
|
9692
9699
|
return [{ level: "warning", message: ".cdd/model-policy.json is not valid JSON" }];
|
|
9693
9700
|
}
|
|
@@ -9788,7 +9795,7 @@ function checkCodeMap(cwd) {
|
|
|
9788
9795
|
const more = probe.staleCount > 3 ? ` (+${probe.staleCount - 3} more)` : "";
|
|
9789
9796
|
findings.push({ level: "warning", message: `code-map stale: ${top}${more}; run \`cdd-kit code-map\`` });
|
|
9790
9797
|
}
|
|
9791
|
-
const text =
|
|
9798
|
+
const text = readFileSync21(mapPath, "utf8");
|
|
9792
9799
|
const m = text.match(/^# files: (\d+), src-lines: (\d+), map-lines: (\d+), compression: ([\d.]+)x/m);
|
|
9793
9800
|
if (m) {
|
|
9794
9801
|
findings.push({ level: "ok", message: `code-map: ${m[1]} files, ${m[4]}x compression` });
|
|
@@ -9868,7 +9875,7 @@ async function attemptAutoFixes(cwd, report) {
|
|
|
9868
9875
|
try {
|
|
9869
9876
|
let existing = {};
|
|
9870
9877
|
try {
|
|
9871
|
-
existing = JSON.parse(
|
|
9878
|
+
existing = JSON.parse(readFileSync21(policyPath, "utf8"));
|
|
9872
9879
|
} catch {
|
|
9873
9880
|
}
|
|
9874
9881
|
const merged = {
|
|
@@ -9961,6 +9968,7 @@ var init_doctor = __esm({
|
|
|
9961
9968
|
init_logger();
|
|
9962
9969
|
init_provider();
|
|
9963
9970
|
init_freshness();
|
|
9971
|
+
init_digest();
|
|
9964
9972
|
}
|
|
9965
9973
|
});
|
|
9966
9974
|
|
|
@@ -9969,7 +9977,7 @@ var lint_agents_exports = {};
|
|
|
9969
9977
|
__export(lint_agents_exports, {
|
|
9970
9978
|
lintAgents: () => lintAgents
|
|
9971
9979
|
});
|
|
9972
|
-
import { readdirSync as readdirSync12, readFileSync as
|
|
9980
|
+
import { readdirSync as readdirSync12, readFileSync as readFileSync22 } from "fs";
|
|
9973
9981
|
import { join as join22 } from "path";
|
|
9974
9982
|
import { load as yamlLoad2 } from "js-yaml";
|
|
9975
9983
|
function extractRequiredArtifactsSection(content) {
|
|
@@ -10024,7 +10032,7 @@ async function lintAgents(opts) {
|
|
|
10024
10032
|
const filePath = join22(agentsDir, filename);
|
|
10025
10033
|
let content;
|
|
10026
10034
|
try {
|
|
10027
|
-
content =
|
|
10035
|
+
content = readFileSync22(filePath, "utf8");
|
|
10028
10036
|
} catch {
|
|
10029
10037
|
violations.push({
|
|
10030
10038
|
file: filename,
|
|
@@ -10137,7 +10145,7 @@ __export(archive_exports, {
|
|
|
10137
10145
|
archive: () => archive
|
|
10138
10146
|
});
|
|
10139
10147
|
import { join as join23 } from "path";
|
|
10140
|
-
import { existsSync as existsSync19, mkdirSync as mkdirSync10, renameSync as renameSync2, readFileSync as
|
|
10148
|
+
import { existsSync as existsSync19, mkdirSync as mkdirSync10, renameSync as renameSync2, readFileSync as readFileSync23, writeFileSync as writeFileSync11, appendFileSync, cpSync as cpSync3, rmSync as rmSync3 } from "fs";
|
|
10141
10149
|
import yaml4 from "js-yaml";
|
|
10142
10150
|
async function archive(changeId) {
|
|
10143
10151
|
const cwd = process.cwd();
|
|
@@ -10157,7 +10165,7 @@ async function archive(changeId) {
|
|
|
10157
10165
|
const tasksPath = join23(changeDir, "tasks.yml");
|
|
10158
10166
|
if (existsSync19(tasksPath)) {
|
|
10159
10167
|
try {
|
|
10160
|
-
const raw =
|
|
10168
|
+
const raw = readFileSync23(tasksPath, "utf8");
|
|
10161
10169
|
const data = yaml4.load(raw);
|
|
10162
10170
|
if (data?.status === "gate-blocked") {
|
|
10163
10171
|
log.warn("tasks.yml has status: gate-blocked \u2014 archiving anyway (change was paused).");
|
|
@@ -10213,7 +10221,7 @@ __export(abandon_exports, {
|
|
|
10213
10221
|
abandon: () => abandon
|
|
10214
10222
|
});
|
|
10215
10223
|
import { join as join24 } from "path";
|
|
10216
|
-
import { existsSync as existsSync20, readFileSync as
|
|
10224
|
+
import { existsSync as existsSync20, readFileSync as readFileSync24, writeFileSync as writeFileSync12, appendFileSync as appendFileSync2, mkdirSync as mkdirSync11 } from "fs";
|
|
10217
10225
|
import yaml5 from "js-yaml";
|
|
10218
10226
|
async function abandon(changeId, opts) {
|
|
10219
10227
|
const cwd = process.cwd();
|
|
@@ -10224,7 +10232,7 @@ async function abandon(changeId, opts) {
|
|
|
10224
10232
|
process.exit(1);
|
|
10225
10233
|
}
|
|
10226
10234
|
if (existsSync20(tasksPath)) {
|
|
10227
|
-
const raw =
|
|
10235
|
+
const raw = readFileSync24(tasksPath, "utf8");
|
|
10228
10236
|
const data = yaml5.load(raw) ?? {};
|
|
10229
10237
|
data["status"] = "abandoned";
|
|
10230
10238
|
if (!data["change-id"]) {
|
|
@@ -10267,7 +10275,7 @@ __export(list_changes_exports, {
|
|
|
10267
10275
|
listChanges: () => listChanges
|
|
10268
10276
|
});
|
|
10269
10277
|
import { join as join25 } from "path";
|
|
10270
|
-
import { existsSync as existsSync21, readdirSync as readdirSync13, readFileSync as
|
|
10278
|
+
import { existsSync as existsSync21, readdirSync as readdirSync13, readFileSync as readFileSync25 } from "fs";
|
|
10271
10279
|
import yaml6 from "js-yaml";
|
|
10272
10280
|
async function listChanges() {
|
|
10273
10281
|
const cwd = process.cwd();
|
|
@@ -10287,7 +10295,7 @@ async function listChanges() {
|
|
|
10287
10295
|
let pending = 0;
|
|
10288
10296
|
if (existsSync21(tasksPath)) {
|
|
10289
10297
|
try {
|
|
10290
|
-
const raw =
|
|
10298
|
+
const raw = readFileSync25(tasksPath, "utf8");
|
|
10291
10299
|
const data = yaml6.load(raw);
|
|
10292
10300
|
if (data?.status)
|
|
10293
10301
|
status = data.status;
|
|
@@ -10318,7 +10326,7 @@ __export(context_exports, {
|
|
|
10318
10326
|
rejectContextExpansion: () => rejectContextExpansion,
|
|
10319
10327
|
requestContextExpansion: () => requestContextExpansion
|
|
10320
10328
|
});
|
|
10321
|
-
import { existsSync as existsSync22, readFileSync as
|
|
10329
|
+
import { existsSync as existsSync22, readFileSync as readFileSync26, writeFileSync as writeFileSync13 } from "fs";
|
|
10322
10330
|
import { join as join26 } from "path";
|
|
10323
10331
|
function normalizePath(path) {
|
|
10324
10332
|
return path.replace(/\\/g, "/").replace(/^\.\//, "").trim();
|
|
@@ -10341,7 +10349,7 @@ function readManifest(changeId) {
|
|
|
10341
10349
|
log.error(`context manifest not found: specs/changes/${changeId}/context-manifest.md`);
|
|
10342
10350
|
process.exit(1);
|
|
10343
10351
|
}
|
|
10344
|
-
return
|
|
10352
|
+
return readFileSync26(manifestPath, "utf8");
|
|
10345
10353
|
}
|
|
10346
10354
|
function writeManifest(changeId, content) {
|
|
10347
10355
|
writeFileSync13(manifestPathFor(changeId), content.endsWith("\n") ? content : `${content}
|
|
@@ -10569,7 +10577,7 @@ var init_context = __esm({
|
|
|
10569
10577
|
});
|
|
10570
10578
|
|
|
10571
10579
|
// src/cli/index.ts
|
|
10572
|
-
import { readFileSync as
|
|
10580
|
+
import { readFileSync as readFileSync27 } from "fs";
|
|
10573
10581
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
10574
10582
|
import { dirname as dirname7, join as join27 } from "path";
|
|
10575
10583
|
import { Command } from "commander";
|
|
@@ -10992,24 +11000,18 @@ init_update();
|
|
|
10992
11000
|
// src/commands/new-change.ts
|
|
10993
11001
|
init_paths();
|
|
10994
11002
|
import { join as join9, relative as relative3 } from "path";
|
|
10995
|
-
import { createHash as
|
|
10996
|
-
import { existsSync as existsSync8, readFileSync as
|
|
11003
|
+
import { createHash as createHash4 } from "crypto";
|
|
11004
|
+
import { existsSync as existsSync8, readFileSync as readFileSync8, readdirSync as readdirSync5, writeFileSync as writeFileSync4 } from "fs";
|
|
10997
11005
|
import yaml from "js-yaml";
|
|
10998
11006
|
init_logger();
|
|
10999
11007
|
init_context_scan();
|
|
11000
|
-
|
|
11001
|
-
try {
|
|
11002
|
-
return createHash3("sha256").update(readFileSync7(path)).digest("hex");
|
|
11003
|
-
} catch {
|
|
11004
|
-
return "";
|
|
11005
|
-
}
|
|
11006
|
-
}
|
|
11008
|
+
init_digest();
|
|
11007
11009
|
function inputsDigest2(paths, cwd) {
|
|
11008
11010
|
const combined = paths.slice().sort().map((p) => {
|
|
11009
11011
|
const rel = relative3(cwd, p).replace(/\\/g, "/");
|
|
11010
|
-
return `${rel}:${
|
|
11012
|
+
return `${rel}:${sha256OfFileNormalized(p)}`;
|
|
11011
11013
|
}).join("\n");
|
|
11012
|
-
return
|
|
11014
|
+
return createHash4("sha256").update(combined).digest("hex");
|
|
11013
11015
|
}
|
|
11014
11016
|
function findContractFiles2(dir, found = []) {
|
|
11015
11017
|
if (!existsSync8(dir))
|
|
@@ -11027,7 +11029,7 @@ function findContractFiles2(dir, found = []) {
|
|
|
11027
11029
|
function readIndexDigest(filePath) {
|
|
11028
11030
|
if (!existsSync8(filePath))
|
|
11029
11031
|
return null;
|
|
11030
|
-
const m =
|
|
11032
|
+
const m = readFileSync8(filePath, "utf8").match(/^inputs-digest:\s*([a-f0-9]+)/m);
|
|
11031
11033
|
return m ? m[1] : null;
|
|
11032
11034
|
}
|
|
11033
11035
|
async function ensureFreshContextIndexes(cwd) {
|
|
@@ -11119,7 +11121,7 @@ async function newChange(name, opts) {
|
|
|
11119
11121
|
if (dependencies.length > 0) {
|
|
11120
11122
|
const tasksPath = join9(changeDir, "tasks.yml");
|
|
11121
11123
|
if (existsSync8(tasksPath)) {
|
|
11122
|
-
const raw =
|
|
11124
|
+
const raw = readFileSync8(tasksPath, "utf8");
|
|
11123
11125
|
const data = yaml.load(raw) ?? {};
|
|
11124
11126
|
data["depends-on"] = dependencies;
|
|
11125
11127
|
writeFileSync4(tasksPath, yaml.dump(data, { lineWidth: -1, noRefs: true }), "utf8");
|
|
@@ -11223,7 +11225,7 @@ async function validate(opts) {
|
|
|
11223
11225
|
var import_ajv = __toESM(require_ajv(), 1);
|
|
11224
11226
|
var import_ajv_formats = __toESM(require_dist(), 1);
|
|
11225
11227
|
init_logger();
|
|
11226
|
-
import { existsSync as existsSync13, readFileSync as
|
|
11228
|
+
import { existsSync as existsSync13, readFileSync as readFileSync16, readdirSync as readdirSync7 } from "fs";
|
|
11227
11229
|
import { homedir as homedir3 } from "os";
|
|
11228
11230
|
import { join as join16 } from "path";
|
|
11229
11231
|
import yaml2 from "js-yaml";
|
|
@@ -11411,7 +11413,7 @@ function extractRequiredArtifactTypes(cwd, agentName) {
|
|
|
11411
11413
|
if (!existsSync13(candidate))
|
|
11412
11414
|
continue;
|
|
11413
11415
|
try {
|
|
11414
|
-
content =
|
|
11416
|
+
content = readFileSync16(candidate, "utf8");
|
|
11415
11417
|
break;
|
|
11416
11418
|
} catch {
|
|
11417
11419
|
}
|
|
@@ -11452,7 +11454,7 @@ function loadContextPolicy(cwd) {
|
|
|
11452
11454
|
if (!existsSync13(policyPath))
|
|
11453
11455
|
return defaults;
|
|
11454
11456
|
try {
|
|
11455
|
-
const custom = JSON.parse(
|
|
11457
|
+
const custom = JSON.parse(readFileSync16(policyPath, "utf8"));
|
|
11456
11458
|
return {
|
|
11457
11459
|
...defaults,
|
|
11458
11460
|
...custom,
|
|
@@ -11466,7 +11468,7 @@ function loadContextPolicy(cwd) {
|
|
|
11466
11468
|
}
|
|
11467
11469
|
function loadYamlFile(path) {
|
|
11468
11470
|
try {
|
|
11469
|
-
const raw =
|
|
11471
|
+
const raw = readFileSync16(path, "utf8");
|
|
11470
11472
|
return { data: yaml2.load(raw), parseError: null };
|
|
11471
11473
|
} catch (err) {
|
|
11472
11474
|
return { data: null, parseError: err.message };
|
|
@@ -11528,7 +11530,7 @@ function lintTasksFile(tasksPath, errors, warnings) {
|
|
|
11528
11530
|
function resolveTier(changeDir) {
|
|
11529
11531
|
const classifPath = join16(changeDir, "change-classification.md");
|
|
11530
11532
|
const classificationPresent = existsSync13(classifPath);
|
|
11531
|
-
const classificationText = classificationPresent ?
|
|
11533
|
+
const classificationText = classificationPresent ? readFileSync16(classifPath, "utf8") : "";
|
|
11532
11534
|
const classificationHasLooseMarker = classificationPresent && TIER_PATTERN.test(classificationText);
|
|
11533
11535
|
const tasksPath = join16(changeDir, "tasks.yml");
|
|
11534
11536
|
if (existsSync13(tasksPath)) {
|
|
@@ -11593,7 +11595,7 @@ function enforceTierRequirements(changeDir, agentLogDir, errors, warnings) {
|
|
|
11593
11595
|
}
|
|
11594
11596
|
}
|
|
11595
11597
|
if (resolution.source === "tasks-frontmatter" && resolution.classificationPresent) {
|
|
11596
|
-
const text =
|
|
11598
|
+
const text = readFileSync16(join16(changeDir, "change-classification.md"), "utf8");
|
|
11597
11599
|
const structured = text.match(/^## Tier\s*\n\s*-\s*(\d)\s*$/m);
|
|
11598
11600
|
const bold = text.match(/\*\*Tier:\*\*\s*Tier\s*(\d)\b/i);
|
|
11599
11601
|
const classifTier = structured ? parseInt(structured[1], 10) : bold ? parseInt(bold[1], 10) : NaN;
|
|
@@ -11710,7 +11712,7 @@ async function gate(changeId, opts = {}) {
|
|
|
11710
11712
|
let approvedExpansions = [];
|
|
11711
11713
|
errors.push(...validateDependencies(cwd, changeId, changeDir));
|
|
11712
11714
|
if (hasManifest) {
|
|
11713
|
-
const manifest = parseContextManifest(
|
|
11715
|
+
const manifest = parseContextManifest(readFileSync16(manifestPath, "utf8"));
|
|
11714
11716
|
allowedPaths = manifest.allowedPaths;
|
|
11715
11717
|
approvedExpansions = manifest.approvedExpansions;
|
|
11716
11718
|
if (manifest.pendingExpansions > 0) {
|
|
@@ -11738,7 +11740,7 @@ async function gate(changeId, opts = {}) {
|
|
|
11738
11740
|
continue;
|
|
11739
11741
|
if (f === "tasks.yml")
|
|
11740
11742
|
continue;
|
|
11741
|
-
const content =
|
|
11743
|
+
const content = readFileSync16(join16(changeDir, f), "utf8");
|
|
11742
11744
|
const minChars = MIN_CHARS[f] ?? 100;
|
|
11743
11745
|
if (meaningfulChars(content) < minChars) {
|
|
11744
11746
|
errors.push(`${f}: appears to be a stub (< ${minChars} meaningful chars)`);
|
|
@@ -11840,7 +11842,7 @@ async function gate(changeId, opts = {}) {
|
|
|
11840
11842
|
}
|
|
11841
11843
|
const runtimeLog = join16(cwd, ".cdd", "runtime", `${changeId}-files-read.jsonl`);
|
|
11842
11844
|
if (existsSync13(runtimeLog)) {
|
|
11843
|
-
const runtimePaths =
|
|
11845
|
+
const runtimePaths = readFileSync16(runtimeLog, "utf8").split("\n").filter(Boolean).map((line) => {
|
|
11844
11846
|
try {
|
|
11845
11847
|
return JSON.parse(line).path;
|
|
11846
11848
|
} catch {
|
|
@@ -11926,7 +11928,7 @@ async function gate(changeId, opts = {}) {
|
|
|
11926
11928
|
// src/commands/install-hooks.ts
|
|
11927
11929
|
init_paths();
|
|
11928
11930
|
init_logger();
|
|
11929
|
-
import { existsSync as existsSync14, readFileSync as
|
|
11931
|
+
import { existsSync as existsSync14, readFileSync as readFileSync17, writeFileSync as writeFileSync7, chmodSync as chmodSync2, mkdirSync as mkdirSync6 } from "fs";
|
|
11930
11932
|
import { join as join17 } from "path";
|
|
11931
11933
|
var START_MARKER2 = "# cdd-kit-managed-block-start";
|
|
11932
11934
|
var END_MARKER2 = "# cdd-kit-managed-block-end";
|
|
@@ -11940,12 +11942,12 @@ async function installHooks() {
|
|
|
11940
11942
|
const hooksDir = join17(gitDir, "hooks");
|
|
11941
11943
|
mkdirSync6(hooksDir, { recursive: true });
|
|
11942
11944
|
const dest = join17(hooksDir, "pre-commit");
|
|
11943
|
-
const ourHook =
|
|
11945
|
+
const ourHook = readFileSync17(join17(ASSET.hooks, "pre-commit"), "utf8");
|
|
11944
11946
|
let final;
|
|
11945
11947
|
if (!existsSync14(dest)) {
|
|
11946
11948
|
final = ourHook;
|
|
11947
11949
|
} else {
|
|
11948
|
-
const existing =
|
|
11950
|
+
const existing = readFileSync17(dest, "utf8");
|
|
11949
11951
|
const startIdx = existing.indexOf(START_MARKER2);
|
|
11950
11952
|
const endIdx = existing.indexOf(END_MARKER2);
|
|
11951
11953
|
if (startIdx >= 0 && endIdx > startIdx) {
|
|
@@ -11980,7 +11982,7 @@ async function installHooks() {
|
|
|
11980
11982
|
|
|
11981
11983
|
// src/cli/index.ts
|
|
11982
11984
|
var __dirname2 = dirname7(fileURLToPath3(import.meta.url));
|
|
11983
|
-
var pkg = JSON.parse(
|
|
11985
|
+
var pkg = JSON.parse(readFileSync27(join27(__dirname2, "..", "..", "package.json"), "utf8"));
|
|
11984
11986
|
var program = new Command();
|
|
11985
11987
|
program.name("cdd-kit").description("Contract-Driven Delivery Kit CLI").version(pkg.version);
|
|
11986
11988
|
program.command("init").description(
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "contract-driven-delivery",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.11",
|
|
4
4
|
"description": "Contract-driven delivery kit for AI coding agents with deterministic context indexes, manifest-backed read-scope governance, and orchestrated contracts-first delivery.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"contract-driven",
|