vigiles 2.0.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/.claude/settings.json +46 -0
- package/.claude/settings.local.json +8 -0
- package/.claude-plugin/hooks/post-edit.sh +34 -0
- package/.claude-plugin/hooks/pre-edit.sh +40 -0
- package/.claude-plugin/hooks/session-start.sh +38 -0
- package/.claude-plugin/marketplace.json +14 -0
- package/.claude-plugin/plugin.json +47 -0
- package/.github/workflows/ci.yml +81 -0
- package/.prettierignore +1 -0
- package/.vigiles/generated.d.ts +205 -0
- package/CLAUDE.md +95 -0
- package/CLAUDE.md.spec.ts +142 -0
- package/CONTRIBUTING.md +121 -0
- package/LICENSE +21 -0
- package/README.md +377 -0
- package/action.yml +25 -0
- package/dist/action.d.ts +7 -0
- package/dist/action.d.ts.map +1 -0
- package/dist/action.js +180 -0
- package/dist/action.js.map +1 -0
- package/dist/cli.d.ts +12 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +1267 -0
- package/dist/cli.js.map +1 -0
- package/dist/cli.test.d.ts +2 -0
- package/dist/cli.test.d.ts.map +1 -0
- package/dist/cli.test.js +650 -0
- package/dist/cli.test.js.map +1 -0
- package/dist/compile.d.ts +101 -0
- package/dist/compile.d.ts.map +1 -0
- package/dist/compile.js +503 -0
- package/dist/compile.js.map +1 -0
- package/dist/evolve.d.ts +132 -0
- package/dist/evolve.d.ts.map +1 -0
- package/dist/evolve.js +599 -0
- package/dist/evolve.js.map +1 -0
- package/dist/freshness.d.ts +67 -0
- package/dist/freshness.d.ts.map +1 -0
- package/dist/freshness.js +244 -0
- package/dist/freshness.js.map +1 -0
- package/dist/freshness.test.d.ts +2 -0
- package/dist/freshness.test.d.ts.map +1 -0
- package/dist/freshness.test.js +356 -0
- package/dist/freshness.test.js.map +1 -0
- package/dist/generate-types.d.ts +34 -0
- package/dist/generate-types.d.ts.map +1 -0
- package/dist/generate-types.js +381 -0
- package/dist/generate-types.js.map +1 -0
- package/dist/inline.d.ts +58 -0
- package/dist/inline.d.ts.map +1 -0
- package/dist/inline.js +142 -0
- package/dist/inline.js.map +1 -0
- package/dist/inline.test.d.ts +5 -0
- package/dist/inline.test.d.ts.map +1 -0
- package/dist/inline.test.js +152 -0
- package/dist/inline.test.js.map +1 -0
- package/dist/linters.d.ts +38 -0
- package/dist/linters.d.ts.map +1 -0
- package/dist/linters.js +588 -0
- package/dist/linters.js.map +1 -0
- package/dist/proofs.d.ts +272 -0
- package/dist/proofs.d.ts.map +1 -0
- package/dist/proofs.js +622 -0
- package/dist/proofs.js.map +1 -0
- package/dist/proofs.test.d.ts +9 -0
- package/dist/proofs.test.d.ts.map +1 -0
- package/dist/proofs.test.js +952 -0
- package/dist/proofs.test.js.map +1 -0
- package/dist/spec.d.ts +258 -0
- package/dist/spec.d.ts.map +1 -0
- package/dist/spec.js +113 -0
- package/dist/spec.js.map +1 -0
- package/dist/spec.test.d.ts +2 -0
- package/dist/spec.test.d.ts.map +1 -0
- package/dist/spec.test.js +1222 -0
- package/dist/spec.test.js.map +1 -0
- package/dist/types.d.ts +101 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- package/dist/validate.d.ts +10 -0
- package/dist/validate.d.ts.map +1 -0
- package/dist/validate.js +286 -0
- package/dist/validate.js.map +1 -0
- package/dist/validate.test.d.ts +2 -0
- package/dist/validate.test.d.ts.map +1 -0
- package/dist/validate.test.js +531 -0
- package/dist/validate.test.js.map +1 -0
- package/docs/agent-setup.md +85 -0
- package/docs/agent-workflows.md +103 -0
- package/docs/comparison.md +71 -0
- package/docs/freshness.md +124 -0
- package/docs/inline-mode.md +119 -0
- package/docs/linter-support.md +166 -0
- package/docs/spec-format.md +194 -0
- package/eslint.config.mjs +79 -0
- package/examples/CLAUDE.md +54 -0
- package/examples/CLAUDE.md.spec.ts +65 -0
- package/examples/SKILL.md +50 -0
- package/examples/SKILL.md.spec.ts +57 -0
- package/fixtures/example-project/CLAUDE.md +11 -0
- package/fixtures/example-project/package.json +9 -0
- package/fixtures/example-project/src/index.ts +3 -0
- package/fixtures/example-project/src/utils.test.ts +2 -0
- package/fixtures/example-project/src/utils.ts +3 -0
- package/logo.png +0 -0
- package/package.json +42 -0
- package/research/adoption-strategy.md +111 -0
- package/research/agent-integration.md +145 -0
- package/research/ai-code-quality.md +197 -0
- package/research/code-search-for-agents.md +313 -0
- package/research/competitive-landscape.md +163 -0
- package/research/doc-freshness.md +516 -0
- package/research/executable-specs.md +368 -0
- package/research/feature-ideas.md +464 -0
- package/research/formal-proofs-for-agents.md +338 -0
- package/research/fp-for-agent-harness.md +150 -0
- package/research/fp-for-deterministic-ai.md +131 -0
- package/research/self-evolving-specs.md +298 -0
- package/schemas/claude-md-strict.yml +18 -0
- package/schemas/claude-md.yml +6 -0
- package/schemas/skill-strict.yml +12 -0
- package/schemas/skill.yml +5 -0
- package/skills/audit-feedback-loop/SKILL.md +76 -0
- package/skills/edit-spec/SKILL.md +131 -0
- package/skills/enforce-rules-format/SKILL.md +71 -0
- package/skills/generate-logo/SKILL.md +102 -0
- package/skills/generate-rule/SKILL.md +90 -0
- package/skills/linter-docs/clippy.md +241 -0
- package/skills/linter-docs/eslint.md +384 -0
- package/skills/linter-docs/pylint.md +288 -0
- package/skills/linter-docs/rubocop.md +277 -0
- package/skills/linter-docs/ruff.md +187 -0
- package/skills/linter-docs/stylelint.md +247 -0
- package/skills/migrate-to-spec/SKILL.md +124 -0
- package/skills/pr-to-lint-rule/SKILL.md +97 -0
- package/skills/strengthen/SKILL.md +168 -0
- package/src/action.ts +214 -0
- package/src/cli.test.ts +914 -0
- package/src/cli.ts +1631 -0
- package/src/compile.ts +691 -0
- package/src/evolve.ts +781 -0
- package/src/freshness.test.ts +449 -0
- package/src/freshness.ts +299 -0
- package/src/generate-types.ts +448 -0
- package/src/inline.test.ts +206 -0
- package/src/inline.ts +164 -0
- package/src/linters.ts +739 -0
- package/src/proofs.test.ts +1314 -0
- package/src/proofs.ts +849 -0
- package/src/spec.test.ts +1471 -0
- package/src/spec.ts +427 -0
- package/src/types.ts +117 -0
- package/src/validate.test.ts +701 -0
- package/src/validate.ts +381 -0
- package/tsconfig.json +23 -0
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Freshness detection for compiled instruction files.
|
|
3
|
+
*
|
|
4
|
+
* Three modes:
|
|
5
|
+
* - "strict": recompile in memory, diff against existing output (zero false positives)
|
|
6
|
+
* - "input-hash": hash tracked input files, compare to stored fingerprint (fast)
|
|
7
|
+
* - "output-hash": existing behavior — only detects hand-edits to compiled .md
|
|
8
|
+
*/
|
|
9
|
+
import type { FreshnessMode } from "./types.js";
|
|
10
|
+
import type { ClaudeSpec } from "./spec.js";
|
|
11
|
+
/**
|
|
12
|
+
* Detect lock files present at `basePath`.
|
|
13
|
+
* Returns all found (a project may have multiple ecosystems).
|
|
14
|
+
*/
|
|
15
|
+
export declare function detectLockFiles(basePath: string): string[];
|
|
16
|
+
/**
|
|
17
|
+
* Detect linter config files present at `basePath`.
|
|
18
|
+
*/
|
|
19
|
+
export declare function detectLinterConfigs(basePath: string): string[];
|
|
20
|
+
export interface DiscoveredInputs {
|
|
21
|
+
/** All input file paths (relative to basePath), sorted. */
|
|
22
|
+
files: string[];
|
|
23
|
+
/** Which lock files were detected. */
|
|
24
|
+
lockFiles: string[];
|
|
25
|
+
/** Which linter configs were detected. */
|
|
26
|
+
linterConfigs: string[];
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Discover all input files that affect a compiled spec's output.
|
|
30
|
+
*
|
|
31
|
+
* Categories:
|
|
32
|
+
* 1. Spec source file
|
|
33
|
+
* 2. Linter configuration files
|
|
34
|
+
* 3. Package manifest (package.json)
|
|
35
|
+
* 4. Lock files (per-ecosystem)
|
|
36
|
+
* 5. Referenced files from keyFiles
|
|
37
|
+
* 6. Generated types (.vigiles/generated.d.ts)
|
|
38
|
+
* 7. Extra files from freshnessInputs config
|
|
39
|
+
*/
|
|
40
|
+
export declare function discoverInputs(specFile: string, spec: ClaudeSpec, basePath: string, extraInputs?: string[]): DiscoveredInputs;
|
|
41
|
+
/**
|
|
42
|
+
* Compute a combined SHA-256 fingerprint of all input files.
|
|
43
|
+
* Missing files hash to "MISSING:<path>" so deletion changes the hash.
|
|
44
|
+
*/
|
|
45
|
+
export declare function computeInputHash(inputFiles: string[], basePath: string): string;
|
|
46
|
+
/** Embed input hash as an HTML comment in compiled markdown. */
|
|
47
|
+
export declare function addInputHash(markdown: string, inputHash: string): string;
|
|
48
|
+
/** Extract stored input hash from compiled markdown. */
|
|
49
|
+
export declare function extractInputHash(content: string): string | null;
|
|
50
|
+
export interface FreshnessResult {
|
|
51
|
+
fresh: boolean;
|
|
52
|
+
mode: FreshnessMode;
|
|
53
|
+
reason?: string;
|
|
54
|
+
/** Files that changed (input-hash mode only). */
|
|
55
|
+
changedFiles?: string[];
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Check freshness of a compiled file using output-hash mode.
|
|
59
|
+
* Only detects hand-edits to the compiled markdown.
|
|
60
|
+
*/
|
|
61
|
+
export declare function checkOutputHashFreshness(content: string): FreshnessResult;
|
|
62
|
+
/**
|
|
63
|
+
* Check freshness using input-hash mode.
|
|
64
|
+
* Compares stored input fingerprint against current file state.
|
|
65
|
+
*/
|
|
66
|
+
export declare function checkInputHashFreshness(content: string, inputFiles: string[], basePath: string): FreshnessResult;
|
|
67
|
+
//# sourceMappingURL=freshness.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"freshness.d.ts","sourceRoot":"","sources":["../src/freshness.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAMH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAChD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAoE5C;;;GAGG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,EAAE,CAE1D;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,EAAE,CAE9D;AAMD,MAAM,WAAW,gBAAgB;IAC/B,2DAA2D;IAC3D,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,sCAAsC;IACtC,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,0CAA0C;IAC1C,aAAa,EAAE,MAAM,EAAE,CAAC;CACzB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,cAAc,CAC5B,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,UAAU,EAChB,QAAQ,EAAE,MAAM,EAChB,WAAW,CAAC,EAAE,MAAM,EAAE,GACrB,gBAAgB,CAsClB;AAQD;;;GAGG;AACH,wBAAgB,gBAAgB,CAC9B,UAAU,EAAE,MAAM,EAAE,EACpB,QAAQ,EAAE,MAAM,GACf,MAAM,CAWR;AAED,gEAAgE;AAChE,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CASxE;AAED,wDAAwD;AACxD,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAG/D;AAMD,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,OAAO,CAAC;IACf,IAAI,EAAE,aAAa,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,iDAAiD;IACjD,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;CACzB;AAED;;;GAGG;AACH,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,MAAM,GAAG,eAAe,CA+BzE;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,MAAM,EAAE,EACpB,QAAQ,EAAE,MAAM,GACf,eAAe,CA8BjB"}
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Freshness detection for compiled instruction files.
|
|
4
|
+
*
|
|
5
|
+
* Three modes:
|
|
6
|
+
* - "strict": recompile in memory, diff against existing output (zero false positives)
|
|
7
|
+
* - "input-hash": hash tracked input files, compare to stored fingerprint (fast)
|
|
8
|
+
* - "output-hash": existing behavior — only detects hand-edits to compiled .md
|
|
9
|
+
*/
|
|
10
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
+
exports.detectLockFiles = detectLockFiles;
|
|
12
|
+
exports.detectLinterConfigs = detectLinterConfigs;
|
|
13
|
+
exports.discoverInputs = discoverInputs;
|
|
14
|
+
exports.computeInputHash = computeInputHash;
|
|
15
|
+
exports.addInputHash = addInputHash;
|
|
16
|
+
exports.extractInputHash = extractInputHash;
|
|
17
|
+
exports.checkOutputHashFreshness = checkOutputHashFreshness;
|
|
18
|
+
exports.checkInputHashFreshness = checkInputHashFreshness;
|
|
19
|
+
const node_crypto_1 = require("node:crypto");
|
|
20
|
+
const node_fs_1 = require("node:fs");
|
|
21
|
+
const node_path_1 = require("node:path");
|
|
22
|
+
// ---------------------------------------------------------------------------
|
|
23
|
+
// Lock file detection
|
|
24
|
+
// ---------------------------------------------------------------------------
|
|
25
|
+
/** Known lock files, ordered by ecosystem then preference. */
|
|
26
|
+
const KNOWN_LOCK_FILES = [
|
|
27
|
+
// Node.js
|
|
28
|
+
"package-lock.json",
|
|
29
|
+
"yarn.lock",
|
|
30
|
+
"pnpm-lock.yaml",
|
|
31
|
+
"bun.lockb",
|
|
32
|
+
// Ruby
|
|
33
|
+
"Gemfile.lock",
|
|
34
|
+
// Python
|
|
35
|
+
"poetry.lock",
|
|
36
|
+
"uv.lock",
|
|
37
|
+
"pdm.lock",
|
|
38
|
+
"requirements.txt",
|
|
39
|
+
// Rust
|
|
40
|
+
"Cargo.lock",
|
|
41
|
+
// Go
|
|
42
|
+
"go.sum",
|
|
43
|
+
// PHP
|
|
44
|
+
"composer.lock",
|
|
45
|
+
// .NET
|
|
46
|
+
"packages.lock.json",
|
|
47
|
+
// Swift
|
|
48
|
+
"Package.resolved",
|
|
49
|
+
// Elixir
|
|
50
|
+
"mix.lock",
|
|
51
|
+
];
|
|
52
|
+
/** Known linter configuration files. */
|
|
53
|
+
const KNOWN_LINTER_CONFIGS = [
|
|
54
|
+
// ESLint
|
|
55
|
+
"eslint.config.mjs",
|
|
56
|
+
"eslint.config.js",
|
|
57
|
+
"eslint.config.ts",
|
|
58
|
+
"eslint.config.cjs",
|
|
59
|
+
".eslintrc.json",
|
|
60
|
+
".eslintrc.js",
|
|
61
|
+
".eslintrc.yml",
|
|
62
|
+
".eslintrc.yaml",
|
|
63
|
+
".eslintrc.cjs",
|
|
64
|
+
// Stylelint
|
|
65
|
+
".stylelintrc.json",
|
|
66
|
+
".stylelintrc.js",
|
|
67
|
+
".stylelintrc.yml",
|
|
68
|
+
".stylelintrc.yaml",
|
|
69
|
+
"stylelint.config.js",
|
|
70
|
+
"stylelint.config.cjs",
|
|
71
|
+
"stylelint.config.mjs",
|
|
72
|
+
// Python
|
|
73
|
+
"pyproject.toml",
|
|
74
|
+
"ruff.toml",
|
|
75
|
+
".pylintrc",
|
|
76
|
+
"setup.cfg",
|
|
77
|
+
// Rust
|
|
78
|
+
"Cargo.toml",
|
|
79
|
+
"clippy.toml",
|
|
80
|
+
".clippy.toml",
|
|
81
|
+
// Ruby
|
|
82
|
+
".rubocop.yml",
|
|
83
|
+
".rubocop.yaml",
|
|
84
|
+
];
|
|
85
|
+
/**
|
|
86
|
+
* Detect lock files present at `basePath`.
|
|
87
|
+
* Returns all found (a project may have multiple ecosystems).
|
|
88
|
+
*/
|
|
89
|
+
function detectLockFiles(basePath) {
|
|
90
|
+
return KNOWN_LOCK_FILES.filter((f) => (0, node_fs_1.existsSync)((0, node_path_1.resolve)(basePath, f)));
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Detect linter config files present at `basePath`.
|
|
94
|
+
*/
|
|
95
|
+
function detectLinterConfigs(basePath) {
|
|
96
|
+
return KNOWN_LINTER_CONFIGS.filter((f) => (0, node_fs_1.existsSync)((0, node_path_1.resolve)(basePath, f)));
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Discover all input files that affect a compiled spec's output.
|
|
100
|
+
*
|
|
101
|
+
* Categories:
|
|
102
|
+
* 1. Spec source file
|
|
103
|
+
* 2. Linter configuration files
|
|
104
|
+
* 3. Package manifest (package.json)
|
|
105
|
+
* 4. Lock files (per-ecosystem)
|
|
106
|
+
* 5. Referenced files from keyFiles
|
|
107
|
+
* 6. Generated types (.vigiles/generated.d.ts)
|
|
108
|
+
* 7. Extra files from freshnessInputs config
|
|
109
|
+
*/
|
|
110
|
+
function discoverInputs(specFile, spec, basePath, extraInputs) {
|
|
111
|
+
const files = new Set();
|
|
112
|
+
// 1. Spec source
|
|
113
|
+
files.add(specFile);
|
|
114
|
+
// 2. Linter configs
|
|
115
|
+
const linterConfigs = detectLinterConfigs(basePath);
|
|
116
|
+
for (const cfg of linterConfigs)
|
|
117
|
+
files.add(cfg);
|
|
118
|
+
// 3. Package manifest
|
|
119
|
+
if ((0, node_fs_1.existsSync)((0, node_path_1.resolve)(basePath, "package.json"))) {
|
|
120
|
+
files.add("package.json");
|
|
121
|
+
}
|
|
122
|
+
// 4. Lock files
|
|
123
|
+
const lockFiles = detectLockFiles(basePath);
|
|
124
|
+
for (const lf of lockFiles)
|
|
125
|
+
files.add(lf);
|
|
126
|
+
// 5. Referenced files from keyFiles
|
|
127
|
+
if (spec.keyFiles) {
|
|
128
|
+
for (const filePath of Object.keys(spec.keyFiles)) {
|
|
129
|
+
files.add(filePath);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
// 6. Generated types
|
|
133
|
+
if ((0, node_fs_1.existsSync)((0, node_path_1.resolve)(basePath, ".vigiles/generated.d.ts"))) {
|
|
134
|
+
files.add(".vigiles/generated.d.ts");
|
|
135
|
+
}
|
|
136
|
+
// 7. Extra configured inputs
|
|
137
|
+
if (extraInputs) {
|
|
138
|
+
for (const f of extraInputs)
|
|
139
|
+
files.add(f);
|
|
140
|
+
}
|
|
141
|
+
const sorted = [...files].sort();
|
|
142
|
+
return { files: sorted, lockFiles, linterConfigs };
|
|
143
|
+
}
|
|
144
|
+
// ---------------------------------------------------------------------------
|
|
145
|
+
// Input hash computation
|
|
146
|
+
// ---------------------------------------------------------------------------
|
|
147
|
+
const INPUT_HASH_RE = /^<!-- vigiles:inputs:([a-f0-9]+) -->\r?\n?/m;
|
|
148
|
+
/**
|
|
149
|
+
* Compute a combined SHA-256 fingerprint of all input files.
|
|
150
|
+
* Missing files hash to "MISSING:<path>" so deletion changes the hash.
|
|
151
|
+
*/
|
|
152
|
+
function computeInputHash(inputFiles, basePath) {
|
|
153
|
+
const fileHashes = inputFiles.map((f) => {
|
|
154
|
+
const fullPath = (0, node_path_1.resolve)(basePath, f);
|
|
155
|
+
if (!(0, node_fs_1.existsSync)(fullPath))
|
|
156
|
+
return `MISSING:${f}`;
|
|
157
|
+
const content = (0, node_fs_1.readFileSync)(fullPath);
|
|
158
|
+
return (0, node_crypto_1.createHash)("sha256").update(content).digest("hex");
|
|
159
|
+
});
|
|
160
|
+
return (0, node_crypto_1.createHash)("sha256")
|
|
161
|
+
.update(fileHashes.join("\n"))
|
|
162
|
+
.digest("hex")
|
|
163
|
+
.slice(0, 16);
|
|
164
|
+
}
|
|
165
|
+
/** Embed input hash as an HTML comment in compiled markdown. */
|
|
166
|
+
function addInputHash(markdown, inputHash) {
|
|
167
|
+
// Insert after the existing vigiles:sha256 comment (first line)
|
|
168
|
+
const lines = markdown.split("\n");
|
|
169
|
+
if (lines[0].startsWith("<!-- vigiles:sha256:")) {
|
|
170
|
+
lines.splice(1, 0, `<!-- vigiles:inputs:${inputHash} -->`);
|
|
171
|
+
return lines.join("\n");
|
|
172
|
+
}
|
|
173
|
+
// Fallback: prepend
|
|
174
|
+
return `<!-- vigiles:inputs:${inputHash} -->\n${markdown}`;
|
|
175
|
+
}
|
|
176
|
+
/** Extract stored input hash from compiled markdown. */
|
|
177
|
+
function extractInputHash(content) {
|
|
178
|
+
const match = content.match(INPUT_HASH_RE);
|
|
179
|
+
return match?.[1] ?? null;
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Check freshness of a compiled file using output-hash mode.
|
|
183
|
+
* Only detects hand-edits to the compiled markdown.
|
|
184
|
+
*/
|
|
185
|
+
function checkOutputHashFreshness(content) {
|
|
186
|
+
// Re-use existing hash verification
|
|
187
|
+
const hashLine = content.match(/^<!-- vigiles:sha256:([a-f0-9]+) compiled from (.+) -->/);
|
|
188
|
+
if (!hashLine) {
|
|
189
|
+
return {
|
|
190
|
+
fresh: true,
|
|
191
|
+
mode: "output-hash",
|
|
192
|
+
reason: "No hash found (hand-written file)",
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
const expectedHash = hashLine[1];
|
|
196
|
+
const body = content
|
|
197
|
+
.replace(/^<!-- vigiles:sha256:[a-f0-9]+ compiled from .+ -->\r?\n\r?\n?/, "")
|
|
198
|
+
.replace(INPUT_HASH_RE, "");
|
|
199
|
+
const actualHash = (0, node_crypto_1.createHash)("sha256")
|
|
200
|
+
.update(body)
|
|
201
|
+
.digest("hex")
|
|
202
|
+
.slice(0, 16);
|
|
203
|
+
if (actualHash !== expectedHash) {
|
|
204
|
+
return {
|
|
205
|
+
fresh: false,
|
|
206
|
+
mode: "output-hash",
|
|
207
|
+
reason: "Compiled file was manually edited (hash mismatch)",
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
return { fresh: true, mode: "output-hash" };
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Check freshness using input-hash mode.
|
|
214
|
+
* Compares stored input fingerprint against current file state.
|
|
215
|
+
*/
|
|
216
|
+
function checkInputHashFreshness(content, inputFiles, basePath) {
|
|
217
|
+
const storedHash = extractInputHash(content);
|
|
218
|
+
if (!storedHash) {
|
|
219
|
+
return {
|
|
220
|
+
fresh: false,
|
|
221
|
+
mode: "input-hash",
|
|
222
|
+
reason: "No input hash found — run `vigiles compile` to generate one",
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
const currentHash = computeInputHash(inputFiles, basePath);
|
|
226
|
+
if (storedHash !== currentHash) {
|
|
227
|
+
// Report missing files (we can't identify other changes without
|
|
228
|
+
// storing per-file hashes, but missing files are obvious)
|
|
229
|
+
const changedFiles = [];
|
|
230
|
+
for (const f of inputFiles) {
|
|
231
|
+
if (!(0, node_fs_1.existsSync)((0, node_path_1.resolve)(basePath, f))) {
|
|
232
|
+
changedFiles.push(`${f} (deleted)`);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
return {
|
|
236
|
+
fresh: false,
|
|
237
|
+
mode: "input-hash",
|
|
238
|
+
reason: "Inputs changed since last compile — run `vigiles compile`",
|
|
239
|
+
changedFiles: changedFiles.length > 0 ? changedFiles : undefined,
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
return { fresh: true, mode: "input-hash" };
|
|
243
|
+
}
|
|
244
|
+
//# sourceMappingURL=freshness.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"freshness.js","sourceRoot":"","sources":["../src/freshness.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"freshness.test.d.ts","sourceRoot":"","sources":["../src/freshness.test.ts"],"names":[],"mappings":""}
|