@codeledger/cli 0.2.1 → 0.5.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.
Files changed (137) hide show
  1. package/dist/artifacts/summary.d.ts +6 -0
  2. package/dist/artifacts/summary.d.ts.map +1 -0
  3. package/dist/artifacts/summary.js +49 -0
  4. package/dist/artifacts/summary.js.map +1 -0
  5. package/dist/commands/activate.d.ts.map +1 -1
  6. package/dist/commands/activate.js +155 -19
  7. package/dist/commands/activate.js.map +1 -1
  8. package/dist/commands/bundle.d.ts.map +1 -1
  9. package/dist/commands/bundle.js +28 -5
  10. package/dist/commands/bundle.js.map +1 -1
  11. package/dist/commands/checkpoint.d.ts +26 -0
  12. package/dist/commands/checkpoint.d.ts.map +1 -0
  13. package/dist/commands/checkpoint.js +382 -0
  14. package/dist/commands/checkpoint.js.map +1 -0
  15. package/dist/commands/cowork-snapshot.d.ts.map +1 -1
  16. package/dist/commands/cowork-snapshot.js +3 -2
  17. package/dist/commands/cowork-snapshot.js.map +1 -1
  18. package/dist/commands/doctor.d.ts +9 -0
  19. package/dist/commands/doctor.d.ts.map +1 -0
  20. package/dist/commands/doctor.js +169 -0
  21. package/dist/commands/doctor.js.map +1 -0
  22. package/dist/commands/init.d.ts.map +1 -1
  23. package/dist/commands/init.js +46 -12
  24. package/dist/commands/init.js.map +1 -1
  25. package/dist/commands/intent.d.ts +37 -0
  26. package/dist/commands/intent.d.ts.map +1 -0
  27. package/dist/commands/intent.js +408 -0
  28. package/dist/commands/intent.js.map +1 -0
  29. package/dist/commands/manifest.d.ts +8 -0
  30. package/dist/commands/manifest.d.ts.map +1 -0
  31. package/dist/commands/manifest.js +144 -0
  32. package/dist/commands/manifest.js.map +1 -0
  33. package/dist/commands/policy.d.ts +8 -0
  34. package/dist/commands/policy.d.ts.map +1 -0
  35. package/dist/commands/policy.js +27 -0
  36. package/dist/commands/policy.js.map +1 -0
  37. package/dist/commands/refine.d.ts.map +1 -1
  38. package/dist/commands/refine.js +16 -0
  39. package/dist/commands/refine.js.map +1 -1
  40. package/dist/commands/review-coverage.d.ts +12 -0
  41. package/dist/commands/review-coverage.d.ts.map +1 -0
  42. package/dist/commands/review-coverage.js +142 -0
  43. package/dist/commands/review-coverage.js.map +1 -0
  44. package/dist/commands/review-gate.d.ts +12 -0
  45. package/dist/commands/review-gate.d.ts.map +1 -0
  46. package/dist/commands/review-gate.js +130 -0
  47. package/dist/commands/review-gate.js.map +1 -0
  48. package/dist/commands/session-cleanup.js +1 -0
  49. package/dist/commands/session-cleanup.js.map +1 -1
  50. package/dist/commands/session-progress.d.ts.map +1 -1
  51. package/dist/commands/session-progress.js +29 -1
  52. package/dist/commands/session-progress.js.map +1 -1
  53. package/dist/commands/session-summary.d.ts.map +1 -1
  54. package/dist/commands/session-summary.js +620 -37
  55. package/dist/commands/session-summary.js.map +1 -1
  56. package/dist/commands/setup-ci.d.ts +9 -0
  57. package/dist/commands/setup-ci.d.ts.map +1 -0
  58. package/dist/commands/setup-ci.js +139 -0
  59. package/dist/commands/setup-ci.js.map +1 -0
  60. package/dist/commands/shared-summary.d.ts +15 -0
  61. package/dist/commands/shared-summary.d.ts.map +1 -0
  62. package/dist/commands/shared-summary.js +194 -0
  63. package/dist/commands/shared-summary.js.map +1 -0
  64. package/dist/commands/sign-manifest.d.ts +8 -0
  65. package/dist/commands/sign-manifest.d.ts.map +1 -0
  66. package/dist/commands/sign-manifest.js +58 -0
  67. package/dist/commands/sign-manifest.js.map +1 -0
  68. package/dist/commands/verify.d.ts +13 -0
  69. package/dist/commands/verify.d.ts.map +1 -0
  70. package/dist/commands/verify.js +288 -0
  71. package/dist/commands/verify.js.map +1 -0
  72. package/dist/index.d.ts.map +1 -1
  73. package/dist/index.js +153 -2
  74. package/dist/index.js.map +1 -1
  75. package/dist/integrations/github-actions.d.ts +17 -0
  76. package/dist/integrations/github-actions.d.ts.map +1 -0
  77. package/dist/integrations/github-actions.js +64 -0
  78. package/dist/integrations/github-actions.js.map +1 -0
  79. package/dist/manifest/build.d.ts +19 -0
  80. package/dist/manifest/build.d.ts.map +1 -0
  81. package/dist/manifest/build.js +82 -0
  82. package/dist/manifest/build.js.map +1 -0
  83. package/dist/manifest/schema.d.ts +2 -0
  84. package/dist/manifest/schema.d.ts.map +1 -0
  85. package/dist/manifest/schema.js +2 -0
  86. package/dist/manifest/schema.js.map +1 -0
  87. package/dist/manifest/write.d.ts +13 -0
  88. package/dist/manifest/write.d.ts.map +1 -0
  89. package/dist/manifest/write.js +69 -0
  90. package/dist/manifest/write.js.map +1 -0
  91. package/dist/policy/load.d.ts +21 -0
  92. package/dist/policy/load.d.ts.map +1 -0
  93. package/dist/policy/load.js +63 -0
  94. package/dist/policy/load.js.map +1 -0
  95. package/dist/policy/resolve.d.ts +18 -0
  96. package/dist/policy/resolve.d.ts.map +1 -0
  97. package/dist/policy/resolve.js +86 -0
  98. package/dist/policy/resolve.js.map +1 -0
  99. package/dist/policy/schema.d.ts +22 -0
  100. package/dist/policy/schema.d.ts.map +1 -0
  101. package/dist/policy/schema.js +82 -0
  102. package/dist/policy/schema.js.map +1 -0
  103. package/dist/session-paths.d.ts +8 -0
  104. package/dist/session-paths.d.ts.map +1 -1
  105. package/dist/session-paths.js +16 -0
  106. package/dist/session-paths.js.map +1 -1
  107. package/dist/signing/canonicalize.d.ts +17 -0
  108. package/dist/signing/canonicalize.d.ts.map +1 -0
  109. package/dist/signing/canonicalize.js +50 -0
  110. package/dist/signing/canonicalize.js.map +1 -0
  111. package/dist/signing/hmac.d.ts +8 -0
  112. package/dist/signing/hmac.d.ts.map +1 -0
  113. package/dist/signing/hmac.js +16 -0
  114. package/dist/signing/hmac.js.map +1 -0
  115. package/dist/signing/signer.d.ts +16 -0
  116. package/dist/signing/signer.d.ts.map +1 -0
  117. package/dist/signing/signer.js +2 -0
  118. package/dist/signing/signer.js.map +1 -0
  119. package/dist/templates/claude-md.d.ts.map +1 -1
  120. package/dist/templates/claude-md.js +9 -2
  121. package/dist/templates/claude-md.js.map +1 -1
  122. package/dist/templates/config.d.ts.map +1 -1
  123. package/dist/templates/config.js +48 -10
  124. package/dist/templates/config.js.map +1 -1
  125. package/dist/verify/evaluate.d.ts +10 -0
  126. package/dist/verify/evaluate.d.ts.map +1 -0
  127. package/dist/verify/evaluate.js +117 -0
  128. package/dist/verify/evaluate.js.map +1 -0
  129. package/dist/verify/policy-snapshot.d.ts +7 -0
  130. package/dist/verify/policy-snapshot.d.ts.map +1 -0
  131. package/dist/verify/policy-snapshot.js +36 -0
  132. package/dist/verify/policy-snapshot.js.map +1 -0
  133. package/dist/verify/report.d.ts +11 -0
  134. package/dist/verify/report.d.ts.map +1 -0
  135. package/dist/verify/report.js +64 -0
  136. package/dist/verify/report.js.map +1 -0
  137. package/package.json +10 -10
@@ -0,0 +1,64 @@
1
+ import { appendFileSync } from 'node:fs';
2
+ /**
3
+ * Detect if running inside GitHub Actions.
4
+ */
5
+ export function isGitHubActions() {
6
+ return process.env['GITHUB_ACTIONS'] === 'true';
7
+ }
8
+ /**
9
+ * Emit GitHub Actions annotations for violations.
10
+ *
11
+ * - DENY_PATH_MATCH → ::error file=<path>,line=1:: (annotates the file)
12
+ * - All other codes → ::error:: (no file reference, aggregate metric)
13
+ */
14
+ export function emitAnnotations(report) {
15
+ for (const v of report.violations) {
16
+ if (v.code === 'DENY_PATH_MATCH' && v.path) {
17
+ console.log(`::error file=${v.path},line=1::${v.message}`);
18
+ }
19
+ else {
20
+ console.log(`::error::${v.message}`);
21
+ }
22
+ }
23
+ }
24
+ /**
25
+ * Write a step summary to $GITHUB_STEP_SUMMARY as markdown.
26
+ */
27
+ export function writeStepSummary(report, _summaryText) {
28
+ const summaryFile = process.env['GITHUB_STEP_SUMMARY'];
29
+ if (!summaryFile)
30
+ return;
31
+ const status = report.decision.passed ? 'PASS' : 'FAIL';
32
+ const mode = report.decision.mode;
33
+ const icon = report.decision.passed ? ':white_check_mark:' : ':x:';
34
+ const lines = [];
35
+ lines.push(`## ${icon} CodeLedger Verify — ${status} (${mode} mode)`);
36
+ lines.push('');
37
+ lines.push('| Metric | Value | Status |');
38
+ lines.push('|--------|-------|--------|');
39
+ const confFail = report.violations.some((v) => v.code === 'LOW_CONFIDENCE');
40
+ const driftFail = report.violations.some((v) => v.code === 'HIGH_DRIFT');
41
+ const fileFail = report.violations.some((v) => v.code === 'BUNDLE_TOO_LARGE' && v.dimension === 'file_count');
42
+ const byteFail = report.violations.some((v) => v.code === 'BUNDLE_TOO_LARGE' && v.dimension === 'byte_size');
43
+ lines.push(`| Confidence | ${report.metrics.overall_confidence.toFixed(2)} | ${confFail ? ':x:' : ':white_check_mark:'} |`);
44
+ lines.push(`| Drift | ${report.metrics.drift_score.toFixed(2)} | ${driftFail ? ':x:' : ':white_check_mark:'} |`);
45
+ lines.push(`| File count | ${report.metrics.bundle_file_count} | ${fileFail ? ':x:' : ':white_check_mark:'} |`);
46
+ const sizeKb = (report.metrics.bundle_total_bytes / 1024).toFixed(1);
47
+ lines.push(`| Size | ${sizeKb} KB | ${byteFail ? ':x:' : ':white_check_mark:'} |`);
48
+ if (report.violations.length > 0) {
49
+ lines.push('');
50
+ lines.push(`### Violations (${report.violations.length})`);
51
+ lines.push('');
52
+ for (const v of report.violations) {
53
+ lines.push(`- **${v.code}**: ${v.message}`);
54
+ }
55
+ }
56
+ lines.push('');
57
+ try {
58
+ appendFileSync(summaryFile, lines.join('\n') + '\n');
59
+ }
60
+ catch {
61
+ // Silently ignore write errors — may not have permission
62
+ }
63
+ }
64
+ //# sourceMappingURL=github-actions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"github-actions.js","sourceRoot":"","sources":["../../src/integrations/github-actions.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ import type { ContextBundle, ContextManifestV1 } from '@codeledger/types';
2
+ export interface BuildManifestOptions {
3
+ bundle: ContextBundle;
4
+ repoRoot: string;
5
+ repoName: string;
6
+ commitSha: string | null;
7
+ codeledgerVersion: string;
8
+ intentNormalized: Record<string, unknown>;
9
+ intentHash: string;
10
+ driftScore: number;
11
+ }
12
+ /**
13
+ * Build a ContextManifestV1 from an existing bundle and repo metadata.
14
+ *
15
+ * The output is deterministic: same repo state + same intent = identical
16
+ * payload under stable serialization. No timestamps or volatile metadata.
17
+ */
18
+ export declare function buildManifest(opts: BuildManifestOptions): ContextManifestV1;
19
+ //# sourceMappingURL=build.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"build.d.ts","sourceRoot":"","sources":["../../src/manifest/build.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,aAAa,EACb,iBAAiB,EAElB,MAAM,mBAAmB,CAAC;AAW3B,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,aAAa,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC1C,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,oBAAoB,GAAG,iBAAiB,CA2E3E"}
@@ -0,0 +1,82 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { readFileSync, statSync } from 'node:fs';
3
+ import { join } from 'node:path';
4
+ import { MANIFEST_SCHEMA_VERSION } from './schema.js';
5
+ /**
6
+ * Compute SHA-256 hex digest of a file's contents.
7
+ */
8
+ function sha256File(filePath) {
9
+ const content = readFileSync(filePath);
10
+ return createHash('sha256').update(content).digest('hex');
11
+ }
12
+ /**
13
+ * Build a ContextManifestV1 from an existing bundle and repo metadata.
14
+ *
15
+ * The output is deterministic: same repo state + same intent = identical
16
+ * payload under stable serialization. No timestamps or volatile metadata.
17
+ */
18
+ export function buildManifest(opts) {
19
+ const { bundle, repoRoot, repoName, commitSha, codeledgerVersion, intentNormalized, intentHash: hash, driftScore, } = opts;
20
+ // Build per-file entries, sorted by path (lexicographic, Array.sort default)
21
+ const files = bundle.files
22
+ .map((f) => {
23
+ const fullPath = join(repoRoot, f.path);
24
+ let bytes;
25
+ let sha256;
26
+ try {
27
+ bytes = statSync(fullPath).size;
28
+ sha256 = sha256File(fullPath);
29
+ }
30
+ catch {
31
+ // File may have been deleted since bundle generation — use content length
32
+ bytes = Buffer.byteLength(f.content, 'utf-8');
33
+ sha256 = createHash('sha256').update(f.content, 'utf-8').digest('hex');
34
+ }
35
+ return {
36
+ path: f.path,
37
+ sha256,
38
+ bytes,
39
+ score: f.score,
40
+ };
41
+ })
42
+ .sort((a, b) => a.path.localeCompare(b.path));
43
+ const totalBytes = files.reduce((sum, f) => sum + f.bytes, 0);
44
+ // Extract confidence envelope (guaranteed by assessConfidence)
45
+ const confidence = bundle.confidence;
46
+ const envelope = confidence?.envelope ?? {
47
+ structural_coverage: 0,
48
+ test_presence: 0,
49
+ churn_recency: 0,
50
+ keyword_saturation: 0,
51
+ dependency_depth: 0,
52
+ };
53
+ return {
54
+ schema_version: MANIFEST_SCHEMA_VERSION,
55
+ codeledger_version: codeledgerVersion,
56
+ repo: {
57
+ name: repoName,
58
+ commit_sha: commitSha,
59
+ },
60
+ intent: {
61
+ normalized: intentNormalized,
62
+ intent_hash: hash,
63
+ drift_score: driftScore,
64
+ },
65
+ bundle: {
66
+ file_count: files.length,
67
+ total_bytes: totalBytes,
68
+ files,
69
+ },
70
+ confidence: {
71
+ overall: confidence?.score ?? 0,
72
+ envelope: {
73
+ structural_coverage: envelope.structural_coverage,
74
+ test_presence: envelope.test_presence,
75
+ churn_recency: envelope.churn_recency,
76
+ keyword_saturation: envelope.keyword_saturation,
77
+ dependency_depth: envelope.dependency_depth,
78
+ },
79
+ },
80
+ };
81
+ }
82
+ //# sourceMappingURL=build.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"build.js","sourceRoot":"","sources":["../../src/manifest/build.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACjD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAMjC,OAAO,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAC;AAEtD;;GAEG;AACH,SAAS,UAAU,CAAC,QAAgB;IAClC,MAAM,OAAO,GAAG,YAAY,CAAC,QAAQ,CAAC,CAAC;IACvC,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AAC5D,CAAC;AAaD;;;;;GAKG;AACH,MAAM,UAAU,aAAa,CAAC,IAA0B;IACtD,MAAM,EACJ,MAAM,EACN,QAAQ,EACR,QAAQ,EACR,SAAS,EACT,iBAAiB,EACjB,gBAAgB,EAChB,UAAU,EAAE,IAAI,EAChB,UAAU,GACX,GAAG,IAAI,CAAC;IAET,6EAA6E;IAC7E,MAAM,KAAK,GAAqB,MAAM,CAAC,KAAK;SACzC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;QACT,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC;QACxC,IAAI,KAAa,CAAC;QAClB,IAAI,MAAc,CAAC;QACnB,IAAI,CAAC;YACH,KAAK,GAAG,QAAQ,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC;YAChC,MAAM,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC;QAChC,CAAC;QAAC,MAAM,CAAC;YACP,0EAA0E;YAC1E,KAAK,GAAG,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;YAC9C,MAAM,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACzE,CAAC;QACD,OAAO;YACL,IAAI,EAAE,CAAC,CAAC,IAAI;YACZ,MAAM;YACN,KAAK;YACL,KAAK,EAAE,CAAC,CAAC,KAAK;SACf,CAAC;IACJ,CAAC,CAAC;SACD,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;IAEhD,MAAM,UAAU,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;IAE9D,+DAA+D;IAC/D,MAAM,UAAU,GAAG,MAAM,CAAC,UAAU,CAAC;IACrC,MAAM,QAAQ,GAAG,UAAU,EAAE,QAAQ,IAAI;QACvC,mBAAmB,EAAE,CAAC;QACtB,aAAa,EAAE,CAAC;QAChB,aAAa,EAAE,CAAC;QAChB,kBAAkB,EAAE,CAAC;QACrB,gBAAgB,EAAE,CAAC;KACpB,CAAC;IAEF,OAAO;QACL,cAAc,EAAE,uBAAuB;QACvC,kBAAkB,EAAE,iBAAiB;QACrC,IAAI,EAAE;YACJ,IAAI,EAAE,QAAQ;YACd,UAAU,EAAE,SAAS;SACtB;QACD,MAAM,EAAE;YACN,UAAU,EAAE,gBAAgB;YAC5B,WAAW,EAAE,IAAI;YACjB,WAAW,EAAE,UAAU;SACxB;QACD,MAAM,EAAE;YACN,UAAU,EAAE,KAAK,CAAC,MAAM;YACxB,WAAW,EAAE,UAAU;YACvB,KAAK;SACN;QACD,UAAU,EAAE;YACV,OAAO,EAAE,UAAU,EAAE,KAAK,IAAI,CAAC;YAC/B,QAAQ,EAAE;gBACR,mBAAmB,EAAE,QAAQ,CAAC,mBAAmB;gBACjD,aAAa,EAAE,QAAQ,CAAC,aAAa;gBACrC,aAAa,EAAE,QAAQ,CAAC,aAAa;gBACrC,kBAAkB,EAAE,QAAQ,CAAC,kBAAkB;gBAC/C,gBAAgB,EAAE,QAAQ,CAAC,gBAAgB;aAC5C;SACF;KACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,2 @@
1
+ export declare const MANIFEST_SCHEMA_VERSION: "mustang/manifest/v1";
2
+ //# sourceMappingURL=schema.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../src/manifest/schema.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,uBAAuB,EAAG,qBAA8B,CAAC"}
@@ -0,0 +1,2 @@
1
+ export const MANIFEST_SCHEMA_VERSION = 'mustang/manifest/v1';
2
+ //# sourceMappingURL=schema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.js","sourceRoot":"","sources":["../../src/manifest/schema.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,uBAAuB,GAAG,qBAA8B,CAAC"}
@@ -0,0 +1,13 @@
1
+ import type { ContextManifestV1 } from '@codeledger/types';
2
+ /**
3
+ * Serialize a manifest with stable key ordering and write to disk.
4
+ *
5
+ * Stable ordering is achieved by constructing the object in explicit key order
6
+ * (matching the schema definition). Arrays are already sorted in buildManifest().
7
+ * Output ends with a trailing newline for POSIX compatibility.
8
+ *
9
+ * Phase 2 will introduce strict JCS canonicalization; this stable output is
10
+ * designed to be close to that form already.
11
+ */
12
+ export declare function writeManifest(manifest: ContextManifestV1, outPath: string): void;
13
+ //# sourceMappingURL=write.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"write.d.ts","sourceRoot":"","sources":["../../src/manifest/write.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAE3D;;;;;;;;;GASG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,iBAAiB,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAsChF"}
@@ -0,0 +1,69 @@
1
+ import { writeFileSync, mkdirSync } from 'node:fs';
2
+ import { dirname } from 'node:path';
3
+ /**
4
+ * Serialize a manifest with stable key ordering and write to disk.
5
+ *
6
+ * Stable ordering is achieved by constructing the object in explicit key order
7
+ * (matching the schema definition). Arrays are already sorted in buildManifest().
8
+ * Output ends with a trailing newline for POSIX compatibility.
9
+ *
10
+ * Phase 2 will introduce strict JCS canonicalization; this stable output is
11
+ * designed to be close to that form already.
12
+ */
13
+ export function writeManifest(manifest, outPath) {
14
+ // Construct with explicit key order for deterministic serialization
15
+ const ordered = {
16
+ schema_version: manifest.schema_version,
17
+ codeledger_version: manifest.codeledger_version,
18
+ repo: {
19
+ name: manifest.repo.name,
20
+ commit_sha: manifest.repo.commit_sha,
21
+ },
22
+ intent: {
23
+ normalized: sortObject(manifest.intent.normalized),
24
+ intent_hash: manifest.intent.intent_hash,
25
+ drift_score: manifest.intent.drift_score,
26
+ },
27
+ bundle: {
28
+ file_count: manifest.bundle.file_count,
29
+ total_bytes: manifest.bundle.total_bytes,
30
+ files: manifest.bundle.files.map((f) => ({
31
+ path: f.path,
32
+ sha256: f.sha256,
33
+ bytes: f.bytes,
34
+ score: f.score,
35
+ })),
36
+ },
37
+ confidence: {
38
+ overall: manifest.confidence.overall,
39
+ envelope: {
40
+ structural_coverage: manifest.confidence.envelope.structural_coverage,
41
+ test_presence: manifest.confidence.envelope.test_presence,
42
+ churn_recency: manifest.confidence.envelope.churn_recency,
43
+ keyword_saturation: manifest.confidence.envelope.keyword_saturation,
44
+ dependency_depth: manifest.confidence.envelope.dependency_depth,
45
+ },
46
+ },
47
+ };
48
+ mkdirSync(dirname(outPath), { recursive: true });
49
+ writeFileSync(outPath, JSON.stringify(ordered, null, 2) + '\n');
50
+ }
51
+ /**
52
+ * Recursively sort object keys for deterministic output.
53
+ * Arrays preserve order; nested objects get sorted keys.
54
+ */
55
+ function sortObject(obj) {
56
+ if (obj === null || obj === undefined)
57
+ return obj;
58
+ if (Array.isArray(obj))
59
+ return obj.map(sortObject);
60
+ if (typeof obj === 'object') {
61
+ const sorted = {};
62
+ for (const key of Object.keys(obj).sort()) {
63
+ sorted[key] = sortObject(obj[key]);
64
+ }
65
+ return sorted;
66
+ }
67
+ return obj;
68
+ }
69
+ //# sourceMappingURL=write.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"write.js","sourceRoot":"","sources":["../../src/manifest/write.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAGpC;;;;;;;;;GASG;AACH,MAAM,UAAU,aAAa,CAAC,QAA2B,EAAE,OAAe;IACxE,oEAAoE;IACpE,MAAM,OAAO,GAAG;QACd,cAAc,EAAE,QAAQ,CAAC,cAAc;QACvC,kBAAkB,EAAE,QAAQ,CAAC,kBAAkB;QAC/C,IAAI,EAAE;YACJ,IAAI,EAAE,QAAQ,CAAC,IAAI,CAAC,IAAI;YACxB,UAAU,EAAE,QAAQ,CAAC,IAAI,CAAC,UAAU;SACrC;QACD,MAAM,EAAE;YACN,UAAU,EAAE,UAAU,CAAC,QAAQ,CAAC,MAAM,CAAC,UAAU,CAAC;YAClD,WAAW,EAAE,QAAQ,CAAC,MAAM,CAAC,WAAW;YACxC,WAAW,EAAE,QAAQ,CAAC,MAAM,CAAC,WAAW;SACzC;QACD,MAAM,EAAE;YACN,UAAU,EAAE,QAAQ,CAAC,MAAM,CAAC,UAAU;YACtC,WAAW,EAAE,QAAQ,CAAC,MAAM,CAAC,WAAW;YACxC,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;gBACvC,IAAI,EAAE,CAAC,CAAC,IAAI;gBACZ,MAAM,EAAE,CAAC,CAAC,MAAM;gBAChB,KAAK,EAAE,CAAC,CAAC,KAAK;gBACd,KAAK,EAAE,CAAC,CAAC,KAAK;aACf,CAAC,CAAC;SACJ;QACD,UAAU,EAAE;YACV,OAAO,EAAE,QAAQ,CAAC,UAAU,CAAC,OAAO;YACpC,QAAQ,EAAE;gBACR,mBAAmB,EAAE,QAAQ,CAAC,UAAU,CAAC,QAAQ,CAAC,mBAAmB;gBACrE,aAAa,EAAE,QAAQ,CAAC,UAAU,CAAC,QAAQ,CAAC,aAAa;gBACzD,aAAa,EAAE,QAAQ,CAAC,UAAU,CAAC,QAAQ,CAAC,aAAa;gBACzD,kBAAkB,EAAE,QAAQ,CAAC,UAAU,CAAC,QAAQ,CAAC,kBAAkB;gBACnE,gBAAgB,EAAE,QAAQ,CAAC,UAAU,CAAC,QAAQ,CAAC,gBAAgB;aAChE;SACF;KACF,CAAC;IAEF,SAAS,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACjD,aAAa,CAAC,OAAO,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;AAClE,CAAC;AAED;;;GAGG;AACH,SAAS,UAAU,CAAC,GAAY;IAC9B,IAAI,GAAG,KAAK,IAAI,IAAI,GAAG,KAAK,SAAS;QAAE,OAAO,GAAG,CAAC;IAClD,IAAI,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC;QAAE,OAAO,GAAG,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;IACnD,IAAI,OAAO,GAAG,KAAK,QAAQ,EAAE,CAAC;QAC5B,MAAM,MAAM,GAA4B,EAAE,CAAC;QAC3C,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,GAA8B,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC;YACrE,MAAM,CAAC,GAAG,CAAC,GAAG,UAAU,CAAE,GAA+B,CAAC,GAAG,CAAC,CAAC,CAAC;QAClE,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC"}
@@ -0,0 +1,21 @@
1
+ import type { MustangPolicyV1 } from '@codeledger/types';
2
+ /**
3
+ * Load and parse a policy JSON file. Returns the parsed object.
4
+ * Throws with a descriptive error if the file is malformed.
5
+ * Returns null if the file does not exist.
6
+ */
7
+ export declare function loadPolicyFile(filePath: string): Partial<MustangPolicyV1> | null;
8
+ /**
9
+ * Load the org default policy file from a policy root directory.
10
+ * Returns null if org/default.json does not exist.
11
+ * Throws on malformed JSON (fatal error per spec).
12
+ */
13
+ export declare function loadOrgDefault(policyRoot: string): Partial<MustangPolicyV1> | null;
14
+ /**
15
+ * Load a repo-specific policy override from the policy root directory.
16
+ * Matches case-insensitively against repos/<name>.json filenames.
17
+ * Returns null if no matching file exists.
18
+ * Throws on malformed JSON (fatal error per spec).
19
+ */
20
+ export declare function loadRepoOverride(policyRoot: string, repoName: string): Partial<MustangPolicyV1> | null;
21
+ //# sourceMappingURL=load.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"load.d.ts","sourceRoot":"","sources":["../../src/policy/load.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAGzD;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC,GAAG,IAAI,CAuBhF;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC,GAAG,IAAI,CAElF;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC,GAAG,IAAI,CAetG"}
@@ -0,0 +1,63 @@
1
+ import { readFileSync, existsSync, readdirSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import { validatePolicy } from './schema.js';
4
+ /**
5
+ * Load and parse a policy JSON file. Returns the parsed object.
6
+ * Throws with a descriptive error if the file is malformed.
7
+ * Returns null if the file does not exist.
8
+ */
9
+ export function loadPolicyFile(filePath) {
10
+ if (!existsSync(filePath))
11
+ return null;
12
+ let raw;
13
+ try {
14
+ raw = readFileSync(filePath, 'utf-8');
15
+ }
16
+ catch (err) {
17
+ throw new Error(`Cannot read policy file: ${filePath} (${err.message})`);
18
+ }
19
+ let parsed;
20
+ try {
21
+ parsed = JSON.parse(raw);
22
+ }
23
+ catch {
24
+ throw new Error(`Malformed JSON in policy file: ${filePath}`);
25
+ }
26
+ const validationError = validatePolicy(parsed);
27
+ if (validationError) {
28
+ throw new Error(`Invalid policy in ${filePath}: ${validationError}`);
29
+ }
30
+ return parsed;
31
+ }
32
+ /**
33
+ * Load the org default policy file from a policy root directory.
34
+ * Returns null if org/default.json does not exist.
35
+ * Throws on malformed JSON (fatal error per spec).
36
+ */
37
+ export function loadOrgDefault(policyRoot) {
38
+ return loadPolicyFile(join(policyRoot, 'org', 'default.json'));
39
+ }
40
+ /**
41
+ * Load a repo-specific policy override from the policy root directory.
42
+ * Matches case-insensitively against repos/<name>.json filenames.
43
+ * Returns null if no matching file exists.
44
+ * Throws on malformed JSON (fatal error per spec).
45
+ */
46
+ export function loadRepoOverride(policyRoot, repoName) {
47
+ const reposDir = join(policyRoot, 'repos');
48
+ if (!existsSync(reposDir))
49
+ return null;
50
+ // Case-insensitive match against filenames
51
+ const lowerName = repoName.toLowerCase();
52
+ const entries = readdirSync(reposDir);
53
+ const match = entries.find((f) => {
54
+ if (!f.endsWith('.json'))
55
+ return false;
56
+ const baseName = f.slice(0, -5); // strip .json
57
+ return baseName.toLowerCase() === lowerName;
58
+ });
59
+ if (!match)
60
+ return null;
61
+ return loadPolicyFile(join(reposDir, match));
62
+ }
63
+ //# sourceMappingURL=load.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"load.js","sourceRoot":"","sources":["../../src/policy/load.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAChE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAE7C;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAAC,QAAgB;IAC7C,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC;QAAE,OAAO,IAAI,CAAC;IAEvC,IAAI,GAAW,CAAC;IAChB,IAAI,CAAC;QACH,GAAG,GAAG,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IACxC,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CAAC,4BAA4B,QAAQ,KAAM,GAAa,CAAC,OAAO,GAAG,CAAC,CAAC;IACtF,CAAC;IAED,IAAI,MAAe,CAAC;IACpB,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC3B,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,KAAK,CAAC,kCAAkC,QAAQ,EAAE,CAAC,CAAC;IAChE,CAAC;IAED,MAAM,eAAe,GAAG,cAAc,CAAC,MAAM,CAAC,CAAC;IAC/C,IAAI,eAAe,EAAE,CAAC;QACpB,MAAM,IAAI,KAAK,CAAC,qBAAqB,QAAQ,KAAK,eAAe,EAAE,CAAC,CAAC;IACvE,CAAC;IAED,OAAO,MAAkC,CAAC;AAC5C,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAAC,UAAkB;IAC/C,OAAO,cAAc,CAAC,IAAI,CAAC,UAAU,EAAE,KAAK,EAAE,cAAc,CAAC,CAAC,CAAC;AACjE,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,gBAAgB,CAAC,UAAkB,EAAE,QAAgB;IACnE,MAAM,QAAQ,GAAG,IAAI,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;IAC3C,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC;QAAE,OAAO,IAAI,CAAC;IAEvC,2CAA2C;IAC3C,MAAM,SAAS,GAAG,QAAQ,CAAC,WAAW,EAAE,CAAC;IACzC,MAAM,OAAO,GAAG,WAAW,CAAC,QAAQ,CAAC,CAAC;IACtC,MAAM,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE;QAC/B,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC;YAAE,OAAO,KAAK,CAAC;QACvC,MAAM,QAAQ,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,cAAc;QAC/C,OAAO,QAAQ,CAAC,WAAW,EAAE,KAAK,SAAS,CAAC;IAC9C,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,KAAK;QAAE,OAAO,IAAI,CAAC;IACxB,OAAO,cAAc,CAAC,IAAI,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC,CAAC;AAC/C,CAAC"}
@@ -0,0 +1,18 @@
1
+ import type { ResolvedPolicyV1 } from '@codeledger/types';
2
+ export interface ResolvePolicyOptions {
3
+ /** Explicit policy root from --policy-root flag (highest precedence). */
4
+ policyRoot?: string;
5
+ /** Working directory for git detection and default path. */
6
+ cwd: string;
7
+ }
8
+ /**
9
+ * Resolve the effective policy through the cascade:
10
+ * 1. Hard-coded safe defaults
11
+ * 2. Org default (org/default.json) — deep merge
12
+ * 3. Repo override (repos/<name>.json) — deep merge
13
+ *
14
+ * Policy root resolution order:
15
+ * --policy-root flag > CODELEDGER_POLICY_PATH env > .codeledger/policy/ > safe defaults
16
+ */
17
+ export declare function resolvePolicy(opts: ResolvePolicyOptions): ResolvedPolicyV1;
18
+ //# sourceMappingURL=resolve.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolve.d.ts","sourceRoot":"","sources":["../../src/policy/resolve.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAI1D,MAAM,WAAW,oBAAoB;IACnC,yEAAyE;IACzE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,4DAA4D;IAC5D,GAAG,EAAE,MAAM,CAAC;CACb;AAED;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,oBAAoB,GAAG,gBAAgB,CA+B1E"}
@@ -0,0 +1,86 @@
1
+ import { existsSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import { execSync } from 'node:child_process';
4
+ import { SAFE_DEFAULTS, mergePolicy, toResolved } from './schema.js';
5
+ import { loadOrgDefault, loadRepoOverride } from './load.js';
6
+ /**
7
+ * Resolve the effective policy through the cascade:
8
+ * 1. Hard-coded safe defaults
9
+ * 2. Org default (org/default.json) — deep merge
10
+ * 3. Repo override (repos/<name>.json) — deep merge
11
+ *
12
+ * Policy root resolution order:
13
+ * --policy-root flag > CODELEDGER_POLICY_PATH env > .codeledger/policy/ > safe defaults
14
+ */
15
+ export function resolvePolicy(opts) {
16
+ const { cwd } = opts;
17
+ // Determine policy root directory
18
+ const policyRoot = determinePolicyRoot(opts);
19
+ // If no policy root found, return safe defaults
20
+ if (!policyRoot) {
21
+ return toResolved(SAFE_DEFAULTS, 'hardcoded-defaults');
22
+ }
23
+ // Layer 1: start from safe defaults
24
+ let policy = { ...SAFE_DEFAULTS };
25
+ let source = 'hardcoded-defaults';
26
+ // Layer 2: org default
27
+ const orgDefault = loadOrgDefault(policyRoot);
28
+ if (orgDefault) {
29
+ policy = mergePolicy(policy, orgDefault);
30
+ source = 'org-default';
31
+ }
32
+ // Layer 3: repo override
33
+ const repoName = getRepoName(cwd);
34
+ const repoOverride = loadRepoOverride(policyRoot, repoName);
35
+ if (repoOverride) {
36
+ policy = mergePolicy(policy, repoOverride);
37
+ source = 'repo-override';
38
+ }
39
+ return toResolved(policy, source);
40
+ }
41
+ /**
42
+ * Determine the policy root directory from the cascade:
43
+ * --policy-root > CODELEDGER_POLICY_PATH env > .codeledger/policy/
44
+ */
45
+ function determinePolicyRoot(opts) {
46
+ // Highest precedence: explicit flag
47
+ if (opts.policyRoot) {
48
+ if (!existsSync(opts.policyRoot))
49
+ return null;
50
+ return opts.policyRoot;
51
+ }
52
+ // Second: env var
53
+ const envPath = process.env['CODELEDGER_POLICY_PATH'];
54
+ if (envPath) {
55
+ if (!existsSync(envPath))
56
+ return null;
57
+ return envPath;
58
+ }
59
+ // Third: default location
60
+ const defaultPath = join(opts.cwd, '.codeledger', 'policy');
61
+ if (existsSync(defaultPath))
62
+ return defaultPath;
63
+ return null;
64
+ }
65
+ /**
66
+ * Get the repo name from git origin remote URL.
67
+ * Falls back to working directory name if no origin remote.
68
+ */
69
+ function getRepoName(cwd) {
70
+ try {
71
+ const url = execSync('git remote get-url origin', {
72
+ cwd,
73
+ encoding: 'utf-8',
74
+ stdio: ['pipe', 'pipe', 'pipe'],
75
+ }).trim();
76
+ // Extract last path component, strip .git suffix
77
+ const parts = url.replace(/\.git$/, '').split('/');
78
+ return parts[parts.length - 1] ?? 'unknown';
79
+ }
80
+ catch {
81
+ // No origin remote — use directory name
82
+ const parts = cwd.replace(/\/$/, '').split('/');
83
+ return parts[parts.length - 1] ?? 'unknown';
84
+ }
85
+ }
86
+ //# sourceMappingURL=resolve.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolve.js","sourceRoot":"","sources":["../../src/policy/resolve.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ import type { MustangPolicyV1, ResolvedPolicyV1 } from '@codeledger/types';
2
+ export declare const POLICY_SCHEMA_VERSION: "mustang/policy/v1";
3
+ /**
4
+ * Hard-coded safe defaults. When no policy files exist, this is the effective
5
+ * policy. Observe mode, no enforcement, maximally permissive thresholds.
6
+ */
7
+ export declare const SAFE_DEFAULTS: MustangPolicyV1;
8
+ /**
9
+ * Validate a parsed policy object. Returns an error message string if invalid,
10
+ * or null if valid.
11
+ */
12
+ export declare function validatePolicy(obj: unknown): string | null;
13
+ /**
14
+ * Deep-merge a partial policy onto a base policy. Override wins for set fields.
15
+ * Arrays replace (not concatenate) — deny_paths in override replaces base.
16
+ */
17
+ export declare function mergePolicy(base: MustangPolicyV1, override: Partial<MustangPolicyV1>): MustangPolicyV1;
18
+ /**
19
+ * Convert a MustangPolicyV1 to a ResolvedPolicyV1 with provenance.
20
+ */
21
+ export declare function toResolved(policy: MustangPolicyV1, resolvedFrom: ResolvedPolicyV1['resolved_from']): ResolvedPolicyV1;
22
+ //# sourceMappingURL=schema.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../src/policy/schema.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAc,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAEvF,eAAO,MAAM,qBAAqB,EAAG,mBAA4B,CAAC;AAElE;;;GAGG;AACH,eAAO,MAAM,aAAa,EAAE,eAW3B,CAAC;AAKF;;;GAGG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAqC1D;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,eAAe,EAAE,QAAQ,EAAE,OAAO,CAAC,eAAe,CAAC,GAAG,eAAe,CAetG;AAED;;GAEG;AACH,wBAAgB,UAAU,CACxB,MAAM,EAAE,eAAe,EACvB,YAAY,EAAE,gBAAgB,CAAC,eAAe,CAAC,GAC9C,gBAAgB,CAElB"}
@@ -0,0 +1,82 @@
1
+ export const POLICY_SCHEMA_VERSION = 'mustang/policy/v1';
2
+ /**
3
+ * Hard-coded safe defaults. When no policy files exist, this is the effective
4
+ * policy. Observe mode, no enforcement, maximally permissive thresholds.
5
+ */
6
+ export const SAFE_DEFAULTS = {
7
+ schema_version: POLICY_SCHEMA_VERSION,
8
+ mode: 'observe',
9
+ min_confidence: 0.0,
10
+ max_drift: 1.0,
11
+ require_tests: false,
12
+ deny_paths: [],
13
+ bundle_max_files: 500,
14
+ bundle_max_bytes: 5_000_000,
15
+ redact_actor_identity: true,
16
+ store_file_paths: true,
17
+ };
18
+ /** All valid policy modes. */
19
+ const VALID_MODES = new Set(['observe', 'warn', 'block']);
20
+ /**
21
+ * Validate a parsed policy object. Returns an error message string if invalid,
22
+ * or null if valid.
23
+ */
24
+ export function validatePolicy(obj) {
25
+ if (typeof obj !== 'object' || obj === null || Array.isArray(obj)) {
26
+ return 'Policy must be a JSON object';
27
+ }
28
+ const policy = obj;
29
+ if (policy['mode'] !== undefined) {
30
+ if (typeof policy['mode'] !== 'string' || !VALID_MODES.has(policy['mode'])) {
31
+ return `Invalid policy mode: "${policy['mode']}". Must be one of: observe, warn, block`;
32
+ }
33
+ }
34
+ if (policy['min_confidence'] !== undefined) {
35
+ if (typeof policy['min_confidence'] !== 'number' || policy['min_confidence'] < 0 || policy['min_confidence'] > 1) {
36
+ return 'min_confidence must be a number between 0 and 1';
37
+ }
38
+ }
39
+ if (policy['max_drift'] !== undefined) {
40
+ if (typeof policy['max_drift'] !== 'number' || policy['max_drift'] < 0 || policy['max_drift'] > 1) {
41
+ return 'max_drift must be a number between 0 and 1';
42
+ }
43
+ }
44
+ if (policy['deny_paths'] !== undefined) {
45
+ if (!Array.isArray(policy['deny_paths'])) {
46
+ return 'deny_paths must be an array of strings';
47
+ }
48
+ for (const p of policy['deny_paths']) {
49
+ if (typeof p !== 'string') {
50
+ return 'deny_paths entries must be strings';
51
+ }
52
+ }
53
+ }
54
+ return null;
55
+ }
56
+ /**
57
+ * Deep-merge a partial policy onto a base policy. Override wins for set fields.
58
+ * Arrays replace (not concatenate) — deny_paths in override replaces base.
59
+ */
60
+ export function mergePolicy(base, override) {
61
+ return {
62
+ schema_version: POLICY_SCHEMA_VERSION,
63
+ mode: (override.mode ?? base.mode),
64
+ min_confidence: override.min_confidence ?? base.min_confidence,
65
+ max_drift: override.max_drift ?? base.max_drift,
66
+ require_tests: override.require_tests ?? base.require_tests,
67
+ deny_paths: override.deny_paths ?? base.deny_paths,
68
+ bundle_max_files: override.bundle_max_files ?? base.bundle_max_files,
69
+ bundle_max_bytes: override.bundle_max_bytes ?? base.bundle_max_bytes,
70
+ redact_actor_identity: override.redact_actor_identity ?? base.redact_actor_identity,
71
+ store_file_paths: override.store_file_paths ?? base.store_file_paths,
72
+ min_review_coverage: override.min_review_coverage ?? base.min_review_coverage,
73
+ block_post_hoc_reads: override.block_post_hoc_reads ?? base.block_post_hoc_reads,
74
+ };
75
+ }
76
+ /**
77
+ * Convert a MustangPolicyV1 to a ResolvedPolicyV1 with provenance.
78
+ */
79
+ export function toResolved(policy, resolvedFrom) {
80
+ return { ...policy, resolved_from: resolvedFrom };
81
+ }
82
+ //# sourceMappingURL=schema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.js","sourceRoot":"","sources":["../../src/policy/schema.ts"],"names":[],"mappings":"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"}
@@ -14,10 +14,18 @@ export declare function sessionStartRef(cwd: string, sessionId: SessionId | null
14
14
  export declare function sessionProgressPath(cwd: string, sessionId: SessionId | null): string;
15
15
  /** Path to the hook-reminded marker. */
16
16
  export declare function sessionHookRemindedPath(cwd: string, sessionId: SessionId | null): string;
17
+ /** Path to the per-task activation history (JSONL, one record per activate call). */
18
+ export declare function sessionTaskHistoryPath(cwd: string, sessionId: SessionId | null): string;
17
19
  /** Legacy fallback bundle path (always .codeledger/active-bundle.md). */
18
20
  export declare function legacyBundlePath(cwd: string): string;
19
21
  /** Sessions root directory. */
20
22
  export declare function sessionsRootDir(cwd: string): string;
21
23
  /** Registry file path. */
22
24
  export declare function registryPath(cwd: string): string;
25
+ /** Path to the read log (JSONL, one record per file read). */
26
+ export declare function sessionReadLogPath(cwd: string, sessionId: SessionId | null): string;
27
+ /** Path to the review config (written when --mode review is used). */
28
+ export declare function sessionReviewConfigPath(cwd: string, sessionId: SessionId | null): string;
29
+ /** Path to the coverage report checkpoint (written by gatekeeper). */
30
+ export declare function sessionCoverageReportPath(cwd: string, sessionId: SessionId | null): string;
23
31
  //# sourceMappingURL=session-paths.d.ts.map