vigiles 30.0.0 → 30.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adapters/claude-code/agent-runtime.js +3 -1
- package/dist/adapters/claude-code/effect-region.js +3 -1
- package/dist/adapters/claude-code/skill-runtime.js +3 -1
- package/dist/cli-main.js +5 -0
- package/dist/core/guards.js +3 -1
- package/dist/coverage-artifact.d.ts +3 -2
- package/dist/coverage-artifact.js +6 -5
- package/dist/eval-cache.d.ts +6 -1
- package/dist/eval-cache.js +11 -1
- package/dist/eval.js +2 -1
- package/dist/hook-runtime.js +4 -2
- package/dist/hook-state-store.js +3 -1
- package/dist/local-files-tracked.d.ts +17 -0
- package/dist/local-files-tracked.js +70 -0
- package/dist/local-files.d.ts +62 -0
- package/dist/local-files.js +183 -0
- package/dist/observe.d.ts +3 -2
- package/dist/observe.js +7 -6
- package/package.json +1 -1
|
@@ -40,6 +40,7 @@ const node_fs_1 = require("node:fs");
|
|
|
40
40
|
const node_path_1 = require("node:path");
|
|
41
41
|
const effects_js_1 = require("../../core/effects.js");
|
|
42
42
|
const dialect_js_1 = require("./dialect.js");
|
|
43
|
+
const local_files_js_1 = require("../../local-files.js");
|
|
43
44
|
const effect_region_js_1 = require("./effect-region.js");
|
|
44
45
|
const agent_tools_js_1 = require("./agent-tools.js");
|
|
45
46
|
Object.defineProperty(exports, "parseAgentTools", { enumerable: true, get: function () { return agent_tools_js_1.parseAgentTools; } });
|
|
@@ -90,7 +91,7 @@ function decidePreToolUse(allowed, tool) {
|
|
|
90
91
|
// STACK: push on dispatch, pop on SubagentStop (back to the parent), gate on the
|
|
91
92
|
// stack TOP. Counterexample the flat model fails and the stack model passes:
|
|
92
93
|
// Open(writer); Open(writer); Stop; Call(Bash).
|
|
93
|
-
const ACTIVE_PATH =
|
|
94
|
+
const ACTIVE_PATH = `${local_files_js_1.VIGILES_DIR}/${local_files_js_1.ACTIVE_AGENT_FILE}`;
|
|
94
95
|
/**
|
|
95
96
|
* Read the active-agent stack (oldest → newest; the dispatched subagent chain).
|
|
96
97
|
* Back-compat: a legacy single-slot `{ agent: string }` marker reads as a one-frame
|
|
@@ -122,6 +123,7 @@ function writeActiveStack(cwd, stack) {
|
|
|
122
123
|
return;
|
|
123
124
|
}
|
|
124
125
|
(0, node_fs_1.mkdirSync)((0, node_path_1.dirname)(p), { recursive: true });
|
|
126
|
+
(0, local_files_js_1.ensureLocalFilesIgnored)((0, node_path_1.dirname)(p));
|
|
125
127
|
(0, node_fs_1.writeFileSync)(p, JSON.stringify({ stack }) + "\n");
|
|
126
128
|
}
|
|
127
129
|
/**
|
|
@@ -12,11 +12,13 @@ exports.hasEffectBoundary = hasEffectBoundary;
|
|
|
12
12
|
*/
|
|
13
13
|
const node_fs_1 = require("node:fs");
|
|
14
14
|
const node_path_1 = require("node:path");
|
|
15
|
-
const
|
|
15
|
+
const local_files_js_1 = require("../../local-files.js");
|
|
16
|
+
const EFFECT_ACTIVE_PATH = `${local_files_js_1.VIGILES_DIR}/${local_files_js_1.EFFECT_ACTIVE_FILE}`;
|
|
16
17
|
/** Record that the agent has entered an effect boundary. */
|
|
17
18
|
function setEffectActive(cwd) {
|
|
18
19
|
const p = (0, node_path_1.resolve)(cwd, EFFECT_ACTIVE_PATH);
|
|
19
20
|
(0, node_fs_1.mkdirSync)((0, node_path_1.dirname)(p), { recursive: true });
|
|
21
|
+
(0, local_files_js_1.ensureLocalFilesIgnored)((0, node_path_1.dirname)(p));
|
|
20
22
|
(0, node_fs_1.writeFileSync)(p, JSON.stringify({ active: true }) + "\n");
|
|
21
23
|
}
|
|
22
24
|
/** Clear the effect-active marker (the agent exited the effect boundary). */
|
|
@@ -38,6 +38,7 @@ const node_fs_1 = require("node:fs");
|
|
|
38
38
|
const node_path_1 = require("node:path");
|
|
39
39
|
const effects_js_1 = require("../../core/effects.js");
|
|
40
40
|
const dialect_js_1 = require("./dialect.js");
|
|
41
|
+
const local_files_js_1 = require("../../local-files.js");
|
|
41
42
|
const STEP_RE = /^###\s+Step\s+(\d+)/;
|
|
42
43
|
const GATE_CMD_RE = /<!--\s*vigiles:gate\s+"([^"]*)"(?:\s+retry:(\d+))?\s*-->/;
|
|
43
44
|
const GATE_FILE_RE = /<!--\s*vigiles:gate\s+file:(\S+)\s*-->/;
|
|
@@ -235,11 +236,12 @@ function runSkillGates(gates, cwd) {
|
|
|
235
236
|
// (Claude Code hooks don't surface the active skill, so we record it). Wiring
|
|
236
237
|
// `skill-start` to fire automatically is the integration step; the decision
|
|
237
238
|
// logic below is harness-agnostic and fully testable.
|
|
238
|
-
const ACTIVE_PATH =
|
|
239
|
+
const ACTIVE_PATH = `${local_files_js_1.VIGILES_DIR}/${local_files_js_1.ACTIVE_SKILL_FILE}`;
|
|
239
240
|
/** Record the skill the agent is currently executing. */
|
|
240
241
|
function setActiveSkill(cwd, skillPath) {
|
|
241
242
|
const p = (0, node_path_1.resolve)(cwd, ACTIVE_PATH);
|
|
242
243
|
(0, node_fs_1.mkdirSync)((0, node_path_1.dirname)(p), { recursive: true });
|
|
244
|
+
(0, local_files_js_1.ensureLocalFilesIgnored)((0, node_path_1.dirname)(p));
|
|
243
245
|
(0, node_fs_1.writeFileSync)(p, JSON.stringify({ skill: skillPath }) + "\n");
|
|
244
246
|
}
|
|
245
247
|
/** Clear the active-skill marker (the skill finished). */
|
package/dist/cli-main.js
CHANGED
|
@@ -38,6 +38,7 @@ const surface_discovery_js_1 = require("./core/surface-discovery.js");
|
|
|
38
38
|
const scan_trigger_suggest_js_1 = require("./scan-trigger-suggest.js");
|
|
39
39
|
const install_reader_js_1 = require("./core/install-reader.js");
|
|
40
40
|
const plugin_declaration_js_1 = require("./plugin-declaration.js");
|
|
41
|
+
const local_files_tracked_js_1 = require("./local-files-tracked.js");
|
|
41
42
|
const scan_behavioral_js_1 = require("./scan-behavioral.js");
|
|
42
43
|
const adapter_registry_js_1 = require("./adapter-registry.js");
|
|
43
44
|
const skill_harness_js_1 = require("./skill-harness.js");
|
|
@@ -5034,6 +5035,10 @@ async function handleRunScripts(kind, args, restArgs, excludes) {
|
|
|
5034
5035
|
// a flag: the run already happened, and this is the runner recording what it
|
|
5035
5036
|
// saw — the same shape as the flight-recorder ledger it already appends to.
|
|
5036
5037
|
recordRunCoverage(cwd, results, kind, harnessFlagFrom(args));
|
|
5038
|
+
// The ignore file keeps NEW copies of `.vigiles/` local files out of git, but
|
|
5039
|
+
// cannot untrack one a repo already committed. Said here, on the CLI, because
|
|
5040
|
+
// it spawns `git` — never from a hook runtime.
|
|
5041
|
+
(0, local_files_tracked_js_1.warnTrackedLocalFiles)(cwd);
|
|
5037
5042
|
console.log("\n" + (0, run_scripts_js_1.formatScriptSummary)(results));
|
|
5038
5043
|
if ((0, run_scripts_js_1.anyFailed)(results))
|
|
5039
5044
|
process.exit(1);
|
package/dist/core/guards.js
CHANGED
|
@@ -41,6 +41,7 @@ exports.runGuardHook = runGuardHook;
|
|
|
41
41
|
const node_fs_1 = require("node:fs");
|
|
42
42
|
const node_path_1 = require("node:path");
|
|
43
43
|
const arg_match_js_1 = require("../arg-match.js");
|
|
44
|
+
const local_files_js_1 = require("../local-files.js");
|
|
44
45
|
/** Ergonomic builders for the closed vocabulary. */
|
|
45
46
|
exports.guard = {
|
|
46
47
|
block: (target, reason) => ({
|
|
@@ -237,7 +238,7 @@ function parseGuards(json) {
|
|
|
237
238
|
// Runtime IO — load the guard set, read/append the session ledger
|
|
238
239
|
// ---------------------------------------------------------------------------
|
|
239
240
|
const GUARDS_FILE = ".vigiles/guards.json";
|
|
240
|
-
const LEDGER_FILE =
|
|
241
|
+
const LEDGER_FILE = `${local_files_js_1.VIGILES_DIR}/${local_files_js_1.GUARD_LEDGER_FILE}`;
|
|
241
242
|
/** Load the declared guard set from `.vigiles/guards.json` (absent → none). */
|
|
242
243
|
function loadGuards(cwd) {
|
|
243
244
|
const p = (0, node_path_1.resolve)(cwd, GUARDS_FILE);
|
|
@@ -273,6 +274,7 @@ function recordGuardCall(cwd, event) {
|
|
|
273
274
|
const calls = readGuardLedger(cwd);
|
|
274
275
|
calls.push({ tool: event.tool, input: event.input });
|
|
275
276
|
(0, node_fs_1.mkdirSync)((0, node_path_1.dirname)(p), { recursive: true });
|
|
277
|
+
(0, local_files_js_1.ensureLocalFilesIgnored)((0, node_path_1.dirname)(p));
|
|
276
278
|
(0, node_fs_1.writeFileSync)(p, JSON.stringify({ calls }, null, 2));
|
|
277
279
|
}
|
|
278
280
|
/** Parse a PreToolUse event JSON (the hook's stdin) into a {@link ToolEvent}. */
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import type { ProbeOrigin, SurfaceProbe } from "./check-count.js";
|
|
2
|
+
import { COVERAGE_ARTIFACT_FILE } from "./local-files.js";
|
|
2
3
|
import type { Surface, SurfaceKind } from "./test-coverage.js";
|
|
3
4
|
/** Bumped when the record shape changes in a non-additive way. */
|
|
4
5
|
export declare const COVERAGE_ARTIFACT_VERSION = 1;
|
|
5
|
-
/** The artifact filename under `.vigiles
|
|
6
|
-
export
|
|
6
|
+
/** The artifact filename under `.vigiles/` — defined on the one local-files list. */
|
|
7
|
+
export { COVERAGE_ARTIFACT_FILE };
|
|
7
8
|
/**
|
|
8
9
|
* Which runner produced a record. Mirrors the discovery split in
|
|
9
10
|
* `test-coverage.ts`: `vigiles test` runs `*.harness.*` (free, every push),
|
|
@@ -58,10 +58,10 @@ const node_crypto_1 = require("node:crypto");
|
|
|
58
58
|
const node_fs_1 = require("node:fs");
|
|
59
59
|
const node_path_1 = require("node:path");
|
|
60
60
|
const coverage_evidence_js_1 = require("./coverage-evidence.js");
|
|
61
|
+
const local_files_js_1 = require("./local-files.js");
|
|
62
|
+
Object.defineProperty(exports, "COVERAGE_ARTIFACT_FILE", { enumerable: true, get: function () { return local_files_js_1.COVERAGE_ARTIFACT_FILE; } });
|
|
61
63
|
/** Bumped when the record shape changes in a non-additive way. */
|
|
62
64
|
exports.COVERAGE_ARTIFACT_VERSION = 1;
|
|
63
|
-
/** The artifact filename under `.vigiles/`. */
|
|
64
|
-
exports.COVERAGE_ARTIFACT_FILE = "coverage.json";
|
|
65
65
|
/**
|
|
66
66
|
* Every {@link RunAttribution}, as data — the artifact's validator reads it, so
|
|
67
67
|
* the accepted set and the type cannot drift apart into a record shape the
|
|
@@ -576,7 +576,7 @@ function mergeRuns(previous, next, executed) {
|
|
|
576
576
|
}
|
|
577
577
|
/** Read the artifact, or `undefined` when there is none / it is not one. */
|
|
578
578
|
function readCoverageArtifact(root) {
|
|
579
|
-
const file = (0, node_path_1.resolve)(root,
|
|
579
|
+
const file = (0, node_path_1.resolve)(root, local_files_js_1.VIGILES_DIR, local_files_js_1.COVERAGE_ARTIFACT_FILE);
|
|
580
580
|
if (!(0, node_fs_1.existsSync)(file))
|
|
581
581
|
return undefined;
|
|
582
582
|
let value;
|
|
@@ -618,9 +618,10 @@ function isCoverageRun(value) {
|
|
|
618
618
|
/** Write the artifact. Best-effort: recording must never fail a green run. */
|
|
619
619
|
function writeCoverageArtifact(root, artifact) {
|
|
620
620
|
try {
|
|
621
|
-
const dir = (0, node_path_1.resolve)(root,
|
|
621
|
+
const dir = (0, node_path_1.resolve)(root, local_files_js_1.VIGILES_DIR);
|
|
622
622
|
(0, node_fs_1.mkdirSync)(dir, { recursive: true });
|
|
623
|
-
(0,
|
|
623
|
+
(0, local_files_js_1.ensureLocalFilesIgnored)(dir);
|
|
624
|
+
(0, node_fs_1.writeFileSync)((0, node_path_1.resolve)(dir, local_files_js_1.COVERAGE_ARTIFACT_FILE), JSON.stringify(artifact, null, 2) + "\n");
|
|
624
625
|
}
|
|
625
626
|
catch {
|
|
626
627
|
/* best-effort — a scratch-dir failure is not a test failure */
|
package/dist/eval-cache.d.ts
CHANGED
|
@@ -74,7 +74,12 @@ export declare function cacheKey(input: CacheKeyInput): SHA256Hash;
|
|
|
74
74
|
* gate must surface, not mask. The message tells you how to recover.
|
|
75
75
|
*/
|
|
76
76
|
export declare function readCache(dir: string, key: SHA256Hash): CacheRecord | null;
|
|
77
|
-
/**
|
|
77
|
+
/**
|
|
78
|
+
* Write a cached record by key (creating the cache dir as needed). When `dir` is
|
|
79
|
+
* the default `.vigiles/eval-cache`, the recorded runs are per-checkout, so the
|
|
80
|
+
* `.vigiles/.gitignore` is kept current; a `cacheDir` the spec chose elsewhere is
|
|
81
|
+
* the author's to place and is left alone.
|
|
82
|
+
*/
|
|
78
83
|
export declare function writeCache(dir: string, key: SHA256Hash, record: CacheRecord): void;
|
|
79
84
|
/** Snapshot the text files under `cwd` as `relativePath → contents` (bounded). */
|
|
80
85
|
export declare function snapshotDir(cwd: string): Record<string, string>;
|
package/dist/eval-cache.js
CHANGED
|
@@ -28,6 +28,7 @@ exports.hashDir = hashDir;
|
|
|
28
28
|
const node_fs_1 = require("node:fs");
|
|
29
29
|
const node_path_1 = require("node:path");
|
|
30
30
|
const hash_js_1 = require("./core/hash.js");
|
|
31
|
+
const local_files_js_1 = require("./local-files.js");
|
|
31
32
|
const MAX_SNAPSHOT_FILE_BYTES = 1024 * 1024;
|
|
32
33
|
const SKIP_DIRS = new Set(["node_modules", ".git"]);
|
|
33
34
|
/**
|
|
@@ -113,9 +114,18 @@ function readCache(dir, key) {
|
|
|
113
114
|
throw new Error(`eval cache: corrupt record ${path} (invalid JSON) — delete it or clear the cache dir`);
|
|
114
115
|
}
|
|
115
116
|
}
|
|
116
|
-
/**
|
|
117
|
+
/**
|
|
118
|
+
* Write a cached record by key (creating the cache dir as needed). When `dir` is
|
|
119
|
+
* the default `.vigiles/eval-cache`, the recorded runs are per-checkout, so the
|
|
120
|
+
* `.vigiles/.gitignore` is kept current; a `cacheDir` the spec chose elsewhere is
|
|
121
|
+
* the author's to place and is left alone.
|
|
122
|
+
*/
|
|
117
123
|
function writeCache(dir, key, record) {
|
|
118
124
|
(0, node_fs_1.mkdirSync)(dir, { recursive: true });
|
|
125
|
+
if ((0, node_path_1.basename)(dir) === local_files_js_1.EVAL_CACHE_DIR &&
|
|
126
|
+
(0, node_path_1.basename)((0, node_path_1.dirname)(dir)) === local_files_js_1.VIGILES_DIR) {
|
|
127
|
+
(0, local_files_js_1.ensureLocalFilesIgnored)((0, node_path_1.dirname)(dir));
|
|
128
|
+
}
|
|
119
129
|
(0, node_fs_1.writeFileSync)((0, node_path_1.join)(dir, `${key}.json`), JSON.stringify(record));
|
|
120
130
|
}
|
|
121
131
|
/** Snapshot the text files under `cwd` as `relativePath → contents` (bounded). */
|
package/dist/eval.js
CHANGED
|
@@ -79,6 +79,7 @@ const proofs_js_1 = require("./core/proofs.js");
|
|
|
79
79
|
const model_access_js_1 = require("./adapters/claude-code/model-access.js");
|
|
80
80
|
const harness_test_js_1 = require("./harness-test.js");
|
|
81
81
|
const eval_cache_js_1 = require("./eval-cache.js");
|
|
82
|
+
const local_files_js_1 = require("./local-files.js");
|
|
82
83
|
const eval_lock_js_1 = require("./eval-lock.js");
|
|
83
84
|
const stats_js_1 = require("./stats.js");
|
|
84
85
|
const check_count_js_1 = require("./check-count.js");
|
|
@@ -1331,7 +1332,7 @@ async function runEvalWith(spec, runner) {
|
|
|
1331
1332
|
tools: spec.allowedTools ?? ["Read", "Edit", "Write", "Bash"],
|
|
1332
1333
|
timeoutMs: spec.timeoutMs ?? 240000,
|
|
1333
1334
|
cache: spec.cache ?? "off",
|
|
1334
|
-
cacheDir: spec.cacheDir ?? (0, node_path_1.resolve)(process.cwd(),
|
|
1335
|
+
cacheDir: spec.cacheDir ?? (0, node_path_1.resolve)(process.cwd(), local_files_js_1.VIGILES_DIR, local_files_js_1.EVAL_CACHE_DIR),
|
|
1335
1336
|
};
|
|
1336
1337
|
if (cfg.cache !== "off" && !isDatedModel(cfg.model)) {
|
|
1337
1338
|
warnFloatingModel(cfg.model);
|
package/dist/hook-runtime.js
CHANGED
|
@@ -50,6 +50,7 @@ const hook_install_js_1 = require("./hook-install.js");
|
|
|
50
50
|
const hook_providers_js_1 = require("./core/hook-providers.js");
|
|
51
51
|
const hook_state_store_js_1 = require("./hook-state-store.js");
|
|
52
52
|
const observe_js_1 = require("./observe.js");
|
|
53
|
+
const local_files_js_1 = require("./local-files.js");
|
|
53
54
|
const load_hook_js_1 = require("./load-hook.js");
|
|
54
55
|
const event_capability_js_1 = require("./core/event-capability.js");
|
|
55
56
|
/**
|
|
@@ -168,8 +169,9 @@ async function loadProviderRegistry(root) {
|
|
|
168
169
|
/** Append an observe-mode record to `.vigiles/hook-observations.jsonl` (best-effort). */
|
|
169
170
|
function recordObservation(file, on, would, reason, root) {
|
|
170
171
|
try {
|
|
171
|
-
const dir = (0, node_path_1.resolve)(root,
|
|
172
|
+
const dir = (0, node_path_1.resolve)(root, local_files_js_1.VIGILES_DIR);
|
|
172
173
|
(0, node_fs_1.mkdirSync)(dir, { recursive: true });
|
|
174
|
+
(0, local_files_js_1.ensureLocalFilesIgnored)(dir);
|
|
173
175
|
const line = JSON.stringify({
|
|
174
176
|
ts: new Date().toISOString(),
|
|
175
177
|
hook: file,
|
|
@@ -177,7 +179,7 @@ function recordObservation(file, on, would, reason, root) {
|
|
|
177
179
|
would,
|
|
178
180
|
reason,
|
|
179
181
|
}) + "\n";
|
|
180
|
-
(0, node_fs_1.appendFileSync)((0, node_path_1.resolve)(dir,
|
|
182
|
+
(0, node_fs_1.appendFileSync)((0, node_path_1.resolve)(dir, local_files_js_1.HOOK_OBSERVATIONS_FILE), line);
|
|
181
183
|
}
|
|
182
184
|
catch {
|
|
183
185
|
/* recording is best-effort — never let it break a live session */
|
package/dist/hook-state-store.js
CHANGED
|
@@ -57,6 +57,7 @@ const node_path_1 = require("node:path");
|
|
|
57
57
|
const hash_js_1 = require("./core/hash.js");
|
|
58
58
|
const hook_state_js_1 = require("./core/hook-state.js");
|
|
59
59
|
const hook_install_js_1 = require("./hook-install.js");
|
|
60
|
+
const local_files_js_1 = require("./local-files.js");
|
|
60
61
|
/**
|
|
61
62
|
* The directory a hook's recorded facts live in — the SCOPE of `state()`/`record()`.
|
|
62
63
|
*
|
|
@@ -78,7 +79,7 @@ function hookStateDir(file, cwd = process.cwd()) {
|
|
|
78
79
|
const dir = (0, node_path_1.dirname)((0, node_path_1.resolve)(cwd, file));
|
|
79
80
|
const rel = (0, node_path_1.relative)(cwd, dir);
|
|
80
81
|
const inside = rel !== "" && !rel.startsWith("..") && !(0, node_path_1.isAbsolute)(rel);
|
|
81
|
-
return (0, node_path_1.resolve)(cwd,
|
|
82
|
+
return (0, node_path_1.resolve)(cwd, local_files_js_1.VIGILES_DIR, local_files_js_1.HOOK_STATE_DIR, inside ? rel : `external-${(0, hash_js_1.sha256short)(dir)}`);
|
|
82
83
|
}
|
|
83
84
|
/** Read one recorded fact for a hook, or `null` if it was never recorded. */
|
|
84
85
|
function readHookState(file, key, cwd = process.cwd()) {
|
|
@@ -112,6 +113,7 @@ function writeHookState(file, w, opts = {}) {
|
|
|
112
113
|
by: (0, hook_install_js_1.normalizeHookRef)(file, cwd),
|
|
113
114
|
};
|
|
114
115
|
(0, node_fs_1.mkdirSync)(dir, { recursive: true });
|
|
116
|
+
(0, local_files_js_1.ensureLocalFilesIgnored)((0, node_path_1.resolve)(cwd, local_files_js_1.VIGILES_DIR));
|
|
115
117
|
const tmp = `${target}.${String(process.pid)}.tmp`;
|
|
116
118
|
(0, node_fs_1.writeFileSync)(tmp, JSON.stringify(entry, null, 2) + "\n");
|
|
117
119
|
(0, node_fs_1.renameSync)(tmp, target);
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The per-checkout files under `<root>/.vigiles/` that git tracks, as
|
|
3
|
+
* repo-relative paths from `root`. Empty when nothing is tracked, when `root` is
|
|
4
|
+
* not in a git work tree, or when `git` is missing — silence is the answer to
|
|
5
|
+
* every "cannot tell".
|
|
6
|
+
*/
|
|
7
|
+
export declare function trackedLocalFiles(root: string): string[];
|
|
8
|
+
/**
|
|
9
|
+
* The one-line warning for {@link trackedLocalFiles}, or `null` when there is
|
|
10
|
+
* nothing to say. Files inside a tracked local DIRECTORY (`state/`,
|
|
11
|
+
* `eval-cache/`) are named by that directory, so one line stays one line and the
|
|
12
|
+
* command it prints untracks all of them.
|
|
13
|
+
*/
|
|
14
|
+
export declare function formatTrackedLocalFiles(tracked: readonly string[]): string | null;
|
|
15
|
+
/** Print the warning for `root` to stderr when there is one. CLI-only. */
|
|
16
|
+
export declare function warnTrackedLocalFiles(root: string): void;
|
|
17
|
+
//# sourceMappingURL=local-files-tracked.d.ts.map
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.trackedLocalFiles = trackedLocalFiles;
|
|
4
|
+
exports.formatTrackedLocalFiles = formatTrackedLocalFiles;
|
|
5
|
+
exports.warnTrackedLocalFiles = warnTrackedLocalFiles;
|
|
6
|
+
/**
|
|
7
|
+
* The half `.gitignore` cannot do: tell the owner when a per-checkout file under
|
|
8
|
+
* `.vigiles/` is ALREADY tracked.
|
|
9
|
+
*
|
|
10
|
+
* `ensureLocalFilesIgnored` (`local-files.ts`) keeps new copies out of git, but
|
|
11
|
+
* an ignore rule does not untrack a file git already follows — a repo that
|
|
12
|
+
* committed `.vigiles/coverage.json` before this existed keeps committing every
|
|
13
|
+
* rewrite of it. Only the owner can fix that (`git rm --cached`), and changing
|
|
14
|
+
* the index is theirs to decide, so this reports and never acts.
|
|
15
|
+
*
|
|
16
|
+
* CLI-only, by construction: it spawns `git`, and a hook decision must never pay
|
|
17
|
+
* for a child process. That is why it is a separate module from the ignore half,
|
|
18
|
+
* which hook runtimes import.
|
|
19
|
+
*/
|
|
20
|
+
const node_child_process_1 = require("node:child_process");
|
|
21
|
+
const node_path_1 = require("node:path");
|
|
22
|
+
const local_files_js_1 = require("./local-files.js");
|
|
23
|
+
/**
|
|
24
|
+
* The per-checkout files under `<root>/.vigiles/` that git tracks, as
|
|
25
|
+
* repo-relative paths from `root`. Empty when nothing is tracked, when `root` is
|
|
26
|
+
* not in a git work tree, or when `git` is missing — silence is the answer to
|
|
27
|
+
* every "cannot tell".
|
|
28
|
+
*/
|
|
29
|
+
function trackedLocalFiles(root) {
|
|
30
|
+
const specs = local_files_js_1.LOCAL_FILES.map((f) => node_path_1.posix.join(local_files_js_1.VIGILES_DIR, f.name));
|
|
31
|
+
try {
|
|
32
|
+
const r = (0, node_child_process_1.spawnSync)("git", ["ls-files", "-z", "--", ...specs], {
|
|
33
|
+
cwd: root,
|
|
34
|
+
encoding: "utf-8",
|
|
35
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
36
|
+
});
|
|
37
|
+
if (r.status !== 0 || typeof r.stdout !== "string")
|
|
38
|
+
return [];
|
|
39
|
+
return r.stdout.split("\0").filter((p) => p !== "");
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
return [];
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* The one-line warning for {@link trackedLocalFiles}, or `null` when there is
|
|
47
|
+
* nothing to say. Files inside a tracked local DIRECTORY (`state/`,
|
|
48
|
+
* `eval-cache/`) are named by that directory, so one line stays one line and the
|
|
49
|
+
* command it prints untracks all of them.
|
|
50
|
+
*/
|
|
51
|
+
function formatTrackedLocalFiles(tracked) {
|
|
52
|
+
if (tracked.length === 0)
|
|
53
|
+
return null;
|
|
54
|
+
// `.vigiles/state/.claude/hooks/x.json` → `.vigiles/state`: the list entry.
|
|
55
|
+
const entries = [
|
|
56
|
+
...new Set(tracked.map((p) => p.split("/").slice(0, 2).join("/"))),
|
|
57
|
+
];
|
|
58
|
+
const one = entries.length === 1;
|
|
59
|
+
return (`⚠ ${entries.join(", ")} ${one ? "is" : "are"} tracked by git, but ` +
|
|
60
|
+
`${local_files_js_1.VIGILES_DIR}/ local files describe one checkout and would credit a ` +
|
|
61
|
+
`machine where nothing ran. .gitignore does not untrack a tracked file; ` +
|
|
62
|
+
`untrack ${one ? "it" : "them"} once: git rm -r --cached ${entries.join(" ")}`);
|
|
63
|
+
}
|
|
64
|
+
/** Print the warning for `root` to stderr when there is one. CLI-only. */
|
|
65
|
+
function warnTrackedLocalFiles(root) {
|
|
66
|
+
const line = formatTrackedLocalFiles(trackedLocalFiles(root));
|
|
67
|
+
if (line)
|
|
68
|
+
process.stderr.write(line + "\n");
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=local-files-tracked.js.map
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/** vigiles's own directory in a project. */
|
|
2
|
+
export declare const VIGILES_DIR = ".vigiles";
|
|
3
|
+
/** The execution tier of coverage (`coverage-artifact.ts`). */
|
|
4
|
+
export declare const COVERAGE_ARTIFACT_FILE = "coverage.json";
|
|
5
|
+
/** The flight-recorder ledger (`observe.ts`). */
|
|
6
|
+
export declare const LEDGER_FILE = "runs.jsonl";
|
|
7
|
+
/** Compiled hooks' named state, `record()`/`state()` (`hook-state-store.ts`). */
|
|
8
|
+
export declare const HOOK_STATE_DIR = "state";
|
|
9
|
+
/** The skill in progress (`adapters/claude-code/skill-runtime.ts`). */
|
|
10
|
+
export declare const ACTIVE_SKILL_FILE = "active-skill.json";
|
|
11
|
+
/** The subagent stack in progress (`adapters/claude-code/agent-runtime.ts`). */
|
|
12
|
+
export declare const ACTIVE_AGENT_FILE = "active-agent.json";
|
|
13
|
+
/** Inside-an-effect-boundary marker (`adapters/claude-code/effect-region.ts`). */
|
|
14
|
+
export declare const EFFECT_ACTIVE_FILE = "effect-active.json";
|
|
15
|
+
/** The calls a guard allowed this session (`core/guards.ts`). */
|
|
16
|
+
export declare const GUARD_LEDGER_FILE = "guard-ledger.json";
|
|
17
|
+
/** What an `observe`-mode hook would have blocked (`hook-runtime.ts`). */
|
|
18
|
+
export declare const HOOK_OBSERVATIONS_FILE = "hook-observations.jsonl";
|
|
19
|
+
/** Recorded model runs for eval replay, the default `cacheDir` (`eval.ts`). */
|
|
20
|
+
export declare const EVAL_CACHE_DIR = "eval-cache";
|
|
21
|
+
/** One per-checkout path under `.vigiles/`. */
|
|
22
|
+
export interface LocalFile {
|
|
23
|
+
/** The name directly under `.vigiles/`. */
|
|
24
|
+
readonly name: string;
|
|
25
|
+
/** A directory (its whole subtree is local) rather than a single file. */
|
|
26
|
+
readonly dir: boolean;
|
|
27
|
+
}
|
|
28
|
+
/** Every per-checkout path vigiles writes under `.vigiles/`. The one list. */
|
|
29
|
+
export declare const LOCAL_FILES: readonly LocalFile[];
|
|
30
|
+
/**
|
|
31
|
+
* The names under `.vigiles/` that ARE the project's and must stay committable.
|
|
32
|
+
* Not used at runtime — it is the other half of the classification the guard in
|
|
33
|
+
* `local-files.test.ts` holds the source to, and the list that test checks is
|
|
34
|
+
* NOT ignored.
|
|
35
|
+
*/
|
|
36
|
+
export declare const COMMITTED_PATHS: readonly string[];
|
|
37
|
+
/** The header comment on a `.vigiles/.gitignore` vigiles creates. */
|
|
38
|
+
export declare const LOCAL_GITIGNORE_HEADER: readonly string[];
|
|
39
|
+
/** The ignore lines, in order: every local path, then the file itself. */
|
|
40
|
+
export declare function localIgnoreEntries(): string[];
|
|
41
|
+
/**
|
|
42
|
+
* Keep `<vigilesDir>/.gitignore` listing every local path. Call it where a
|
|
43
|
+
* local file is written — not from `vigiles init`, which a project may never
|
|
44
|
+
* run, while the write always happens.
|
|
45
|
+
*
|
|
46
|
+
* - Absent → created with {@link LOCAL_GITIGNORE_HEADER} and every entry.
|
|
47
|
+
* - Every entry IN EFFECT → not touched (no rewrite, no mtime change).
|
|
48
|
+
* - Otherwise the entries not in effect are appended and nothing is removed
|
|
49
|
+
* or reordered. Appending is enough because git's last matching rule wins.
|
|
50
|
+
*
|
|
51
|
+
* "In effect" is judged the way git reads the file, not by text membership:
|
|
52
|
+
* lines in order, a later `!/entry` cancels an earlier `/entry`, and leading
|
|
53
|
+
* whitespace is part of the pattern (only trailing whitespace is dropped).
|
|
54
|
+
* Both cases were measured with `git check-ignore`: `/coverage.json` followed
|
|
55
|
+
* by `!/coverage.json`, and ` /coverage.json`, each leave the file NOT
|
|
56
|
+
* ignored while a trimmed set would call the entry present.
|
|
57
|
+
*
|
|
58
|
+
* Costs one read on the hot path. Best-effort: any fs error is swallowed —
|
|
59
|
+
* keeping git tidy must never break a hook, a test run or an audit.
|
|
60
|
+
*/
|
|
61
|
+
export declare function ensureLocalFilesIgnored(vigilesDir: string): void;
|
|
62
|
+
//# sourceMappingURL=local-files.d.ts.map
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.LOCAL_GITIGNORE_HEADER = exports.COMMITTED_PATHS = exports.LOCAL_FILES = exports.EVAL_CACHE_DIR = exports.HOOK_OBSERVATIONS_FILE = exports.GUARD_LEDGER_FILE = exports.EFFECT_ACTIVE_FILE = exports.ACTIVE_AGENT_FILE = exports.ACTIVE_SKILL_FILE = exports.HOOK_STATE_DIR = exports.LEDGER_FILE = exports.COVERAGE_ARTIFACT_FILE = exports.VIGILES_DIR = void 0;
|
|
4
|
+
exports.localIgnoreEntries = localIgnoreEntries;
|
|
5
|
+
exports.ensureLocalFilesIgnored = ensureLocalFilesIgnored;
|
|
6
|
+
/**
|
|
7
|
+
* The files vigiles writes into a project's `.vigiles/` that describe ONE
|
|
8
|
+
* checkout on ONE machine — and the `.gitignore` that keeps them out of git.
|
|
9
|
+
*
|
|
10
|
+
* `.vigiles/` holds two kinds of file. Some are the project's own and are
|
|
11
|
+
* committed: hook sources and their stamps (`hooks/`), eval locks
|
|
12
|
+
* (`eval-locks/`), providers, the eval baseline, spec sidecars. The rest are a
|
|
13
|
+
* record of what happened HERE — which run exercised which surface, which skill
|
|
14
|
+
* is active right now, when a throttled hook last spoke — and committing one
|
|
15
|
+
* transplants that record onto a machine where none of it happened. A committed
|
|
16
|
+
* `coverage.json` credits coverage nobody ran; a committed `state/` silences a
|
|
17
|
+
* teammate's hook for a window they never saw; a committed `guard-ledger.json`
|
|
18
|
+
* satisfies a `requireBefore` guard with a prerequisite someone else ran.
|
|
19
|
+
*
|
|
20
|
+
* ## Why this file exists
|
|
21
|
+
*
|
|
22
|
+
* The rule "do not commit these" lived as one sentence in
|
|
23
|
+
* `docs/rules/untested-skill.md`, and vigiles honoured it only in its OWN repo's
|
|
24
|
+
* root `.gitignore`. Consumers committed them anyway: two repos carried
|
|
25
|
+
* `.vigiles/coverage.json` for weeks, one built a `merge=ours` git driver to
|
|
26
|
+
* paper over the conflicts it caused, and the other shipped `coverage.json` and
|
|
27
|
+
* `runs.jsonl` inside its npm tarball. A rule that exists only as prose enforces
|
|
28
|
+
* nothing, so the writer now enforces it where the write happens.
|
|
29
|
+
*
|
|
30
|
+
* ## The mechanism: a `.gitignore` INSIDE `.vigiles/`
|
|
31
|
+
*
|
|
32
|
+
* Tools that keep local state in a project leave the project's root
|
|
33
|
+
* `.gitignore` alone and drop one inside their own directory — `ruff` writes
|
|
34
|
+
* `.ruff_cache/.gitignore` containing `*`. `*` is wrong here, because the same
|
|
35
|
+
* directory holds committed files, so the file lists the local paths instead,
|
|
36
|
+
* each anchored with a leading `/` so it cannot match a same-named file deeper
|
|
37
|
+
* down. It also lists ITSELF, like ruff's `*` covers its own file: it then
|
|
38
|
+
* leaves no footprint in the project's history and never churns on upgrade.
|
|
39
|
+
*
|
|
40
|
+
* `.gitignore` does not untrack a file that is already tracked. That half is
|
|
41
|
+
* `local-files-tracked.ts`, which runs from the CLI only — it spawns `git`, and
|
|
42
|
+
* nothing here may, because {@link ensureLocalFilesIgnored} is called from hook
|
|
43
|
+
* runtimes on the decision path.
|
|
44
|
+
*
|
|
45
|
+
* ## One list, and the constants live IN it
|
|
46
|
+
*
|
|
47
|
+
* Every writer imports its file name from here rather than spelling it, so a
|
|
48
|
+
* name cannot drift from the ignore entry. The constants were defined in their
|
|
49
|
+
* writer modules before; the direction is inverted on purpose. With the list
|
|
50
|
+
* importing from the writers, each writer would import this module back (for
|
|
51
|
+
* the ensure call), a cycle in which a top-level list reads a `const` still in
|
|
52
|
+
* its temporal dead zone — and a hook deciding a `Bash` call would load
|
|
53
|
+
* `eval.ts` and `coverage-artifact.ts` just to learn two file names. This module
|
|
54
|
+
* imports nothing from the package, so every writer can depend on it for free.
|
|
55
|
+
* `local-files.test.ts` guards the other direction: every `.vigiles/` path the
|
|
56
|
+
* source spells must be either on this list or on {@link COMMITTED_PATHS}.
|
|
57
|
+
*
|
|
58
|
+
* Node-only (it reads and writes a file).
|
|
59
|
+
*/
|
|
60
|
+
const node_fs_1 = require("node:fs");
|
|
61
|
+
const node_path_1 = require("node:path");
|
|
62
|
+
/** vigiles's own directory in a project. */
|
|
63
|
+
exports.VIGILES_DIR = ".vigiles";
|
|
64
|
+
/** The execution tier of coverage (`coverage-artifact.ts`). */
|
|
65
|
+
exports.COVERAGE_ARTIFACT_FILE = "coverage.json";
|
|
66
|
+
/** The flight-recorder ledger (`observe.ts`). */
|
|
67
|
+
exports.LEDGER_FILE = "runs.jsonl";
|
|
68
|
+
/** Compiled hooks' named state, `record()`/`state()` (`hook-state-store.ts`). */
|
|
69
|
+
exports.HOOK_STATE_DIR = "state";
|
|
70
|
+
/** The skill in progress (`adapters/claude-code/skill-runtime.ts`). */
|
|
71
|
+
exports.ACTIVE_SKILL_FILE = "active-skill.json";
|
|
72
|
+
/** The subagent stack in progress (`adapters/claude-code/agent-runtime.ts`). */
|
|
73
|
+
exports.ACTIVE_AGENT_FILE = "active-agent.json";
|
|
74
|
+
/** Inside-an-effect-boundary marker (`adapters/claude-code/effect-region.ts`). */
|
|
75
|
+
exports.EFFECT_ACTIVE_FILE = "effect-active.json";
|
|
76
|
+
/** The calls a guard allowed this session (`core/guards.ts`). */
|
|
77
|
+
exports.GUARD_LEDGER_FILE = "guard-ledger.json";
|
|
78
|
+
/** What an `observe`-mode hook would have blocked (`hook-runtime.ts`). */
|
|
79
|
+
exports.HOOK_OBSERVATIONS_FILE = "hook-observations.jsonl";
|
|
80
|
+
/** Recorded model runs for eval replay, the default `cacheDir` (`eval.ts`). */
|
|
81
|
+
exports.EVAL_CACHE_DIR = "eval-cache";
|
|
82
|
+
/** Every per-checkout path vigiles writes under `.vigiles/`. The one list. */
|
|
83
|
+
exports.LOCAL_FILES = [
|
|
84
|
+
{ name: exports.COVERAGE_ARTIFACT_FILE, dir: false },
|
|
85
|
+
{ name: exports.LEDGER_FILE, dir: false },
|
|
86
|
+
{ name: exports.HOOK_STATE_DIR, dir: true },
|
|
87
|
+
{ name: exports.ACTIVE_SKILL_FILE, dir: false },
|
|
88
|
+
{ name: exports.ACTIVE_AGENT_FILE, dir: false },
|
|
89
|
+
{ name: exports.EFFECT_ACTIVE_FILE, dir: false },
|
|
90
|
+
{ name: exports.GUARD_LEDGER_FILE, dir: false },
|
|
91
|
+
{ name: exports.HOOK_OBSERVATIONS_FILE, dir: false },
|
|
92
|
+
{ name: exports.EVAL_CACHE_DIR, dir: true },
|
|
93
|
+
];
|
|
94
|
+
/**
|
|
95
|
+
* The names under `.vigiles/` that ARE the project's and must stay committable.
|
|
96
|
+
* Not used at runtime — it is the other half of the classification the guard in
|
|
97
|
+
* `local-files.test.ts` holds the source to, and the list that test checks is
|
|
98
|
+
* NOT ignored.
|
|
99
|
+
*/
|
|
100
|
+
exports.COMMITTED_PATHS = [
|
|
101
|
+
"hooks", // hook sources + their stamps (`hook-install.ts`)
|
|
102
|
+
"providers", // registered hook-context providers
|
|
103
|
+
"eval-locks", // committed eval staleness stamps (`eval-lock.ts`)
|
|
104
|
+
"eval-baseline.json", // the committed regression baseline (`eval-baseline.ts`)
|
|
105
|
+
"generated.d.ts", // linter-rule types for specs (`vigiles init` / `generate types`)
|
|
106
|
+
"schema.json", // YAML-LSP frontmatter schema (`vigiles init`)
|
|
107
|
+
"guards.json", // the declared guard set (`core/guards.ts`)
|
|
108
|
+
"action-gates.json", // declared action gates (`action-gate.ts`)
|
|
109
|
+
];
|
|
110
|
+
/** The header comment on a `.vigiles/.gitignore` vigiles creates. */
|
|
111
|
+
exports.LOCAL_GITIGNORE_HEADER = [
|
|
112
|
+
"# Written by vigiles. These files describe one checkout on one machine and are",
|
|
113
|
+
"# never committed. Everything else in .vigiles/ is the project's: commit it.",
|
|
114
|
+
];
|
|
115
|
+
/** The ignore lines, in order: every local path, then the file itself. */
|
|
116
|
+
function localIgnoreEntries() {
|
|
117
|
+
return [
|
|
118
|
+
...exports.LOCAL_FILES.map((f) => `/${f.name}${f.dir ? "/" : ""}`),
|
|
119
|
+
"/.gitignore",
|
|
120
|
+
];
|
|
121
|
+
}
|
|
122
|
+
/** Is `entry` ignoring its path after git reads `lines` top to bottom. */
|
|
123
|
+
function inEffect(lines, entry) {
|
|
124
|
+
let on = false;
|
|
125
|
+
for (const line of lines) {
|
|
126
|
+
if (line === entry)
|
|
127
|
+
on = true;
|
|
128
|
+
else if (line === `!${entry}`)
|
|
129
|
+
on = false;
|
|
130
|
+
}
|
|
131
|
+
return on;
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Keep `<vigilesDir>/.gitignore` listing every local path. Call it where a
|
|
135
|
+
* local file is written — not from `vigiles init`, which a project may never
|
|
136
|
+
* run, while the write always happens.
|
|
137
|
+
*
|
|
138
|
+
* - Absent → created with {@link LOCAL_GITIGNORE_HEADER} and every entry.
|
|
139
|
+
* - Every entry IN EFFECT → not touched (no rewrite, no mtime change).
|
|
140
|
+
* - Otherwise the entries not in effect are appended and nothing is removed
|
|
141
|
+
* or reordered. Appending is enough because git's last matching rule wins.
|
|
142
|
+
*
|
|
143
|
+
* "In effect" is judged the way git reads the file, not by text membership:
|
|
144
|
+
* lines in order, a later `!/entry` cancels an earlier `/entry`, and leading
|
|
145
|
+
* whitespace is part of the pattern (only trailing whitespace is dropped).
|
|
146
|
+
* Both cases were measured with `git check-ignore`: `/coverage.json` followed
|
|
147
|
+
* by `!/coverage.json`, and ` /coverage.json`, each leave the file NOT
|
|
148
|
+
* ignored while a trimmed set would call the entry present.
|
|
149
|
+
*
|
|
150
|
+
* Costs one read on the hot path. Best-effort: any fs error is swallowed —
|
|
151
|
+
* keeping git tidy must never break a hook, a test run or an audit.
|
|
152
|
+
*/
|
|
153
|
+
function ensureLocalFilesIgnored(vigilesDir) {
|
|
154
|
+
try {
|
|
155
|
+
const file = (0, node_path_1.resolve)(vigilesDir, ".gitignore");
|
|
156
|
+
const entries = localIgnoreEntries();
|
|
157
|
+
let current;
|
|
158
|
+
try {
|
|
159
|
+
current = (0, node_fs_1.readFileSync)(file, "utf-8");
|
|
160
|
+
}
|
|
161
|
+
catch {
|
|
162
|
+
current = undefined;
|
|
163
|
+
}
|
|
164
|
+
if (current === undefined) {
|
|
165
|
+
(0, node_fs_1.mkdirSync)(vigilesDir, { recursive: true });
|
|
166
|
+
(0, node_fs_1.writeFileSync)(file, [...exports.LOCAL_GITIGNORE_HEADER, ...entries, ""].join("\n"));
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
const lines = current.split(/\r?\n/).map((l) => l.replace(/\s+$/, ""));
|
|
170
|
+
const missing = entries.filter((e) => !inEffect(lines, e));
|
|
171
|
+
if (missing.length === 0)
|
|
172
|
+
return;
|
|
173
|
+
const header = lines.includes(exports.LOCAL_GITIGNORE_HEADER[0])
|
|
174
|
+
? []
|
|
175
|
+
: exports.LOCAL_GITIGNORE_HEADER;
|
|
176
|
+
const sep = current === "" || current.endsWith("\n") ? "" : "\n";
|
|
177
|
+
(0, node_fs_1.appendFileSync)(file, sep + [...header, ...missing, ""].join("\n"));
|
|
178
|
+
}
|
|
179
|
+
catch {
|
|
180
|
+
/* best-effort — an unwritable .vigiles/ is not a failure of anything */
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
//# sourceMappingURL=local-files.js.map
|
package/dist/observe.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
import { LEDGER_FILE } from "./local-files.js";
|
|
1
2
|
/** Bumped when the record shape changes in a non-additive way. */
|
|
2
3
|
export declare const OBSERVE_VERSION = 1;
|
|
3
|
-
/** The ledger filename under the `.vigiles/` directory. */
|
|
4
|
-
export
|
|
4
|
+
/** The ledger filename under the `.vigiles/` directory — defined on the one local-files list. */
|
|
5
|
+
export { LEDGER_FILE };
|
|
5
6
|
/** Fields every record carries; `v`/`ts` are stamped by the writer, not the caller. */
|
|
6
7
|
export interface ObservationBase {
|
|
7
8
|
/** schema version (`OBSERVE_VERSION`) */
|
package/dist/observe.js
CHANGED
|
@@ -24,10 +24,10 @@ exports.formatLedgerSummary = formatLedgerSummary;
|
|
|
24
24
|
*/
|
|
25
25
|
const node_fs_1 = require("node:fs");
|
|
26
26
|
const node_path_1 = require("node:path");
|
|
27
|
+
const local_files_js_1 = require("./local-files.js");
|
|
28
|
+
Object.defineProperty(exports, "LEDGER_FILE", { enumerable: true, get: function () { return local_files_js_1.LEDGER_FILE; } });
|
|
27
29
|
/** Bumped when the record shape changes in a non-additive way. */
|
|
28
30
|
exports.OBSERVE_VERSION = 1;
|
|
29
|
-
/** The ledger filename under the `.vigiles/` directory. */
|
|
30
|
-
exports.LEDGER_FILE = "runs.jsonl";
|
|
31
31
|
/** Serialize one record to a single JSONL line (trailing newline included). */
|
|
32
32
|
function formatObservation(record) {
|
|
33
33
|
return JSON.stringify(record) + "\n";
|
|
@@ -39,14 +39,15 @@ function formatObservation(record) {
|
|
|
39
39
|
*/
|
|
40
40
|
function appendObservation(input, cwd = process.cwd()) {
|
|
41
41
|
try {
|
|
42
|
-
const dir = (0, node_path_1.resolve)(cwd,
|
|
42
|
+
const dir = (0, node_path_1.resolve)(cwd, local_files_js_1.VIGILES_DIR);
|
|
43
43
|
(0, node_fs_1.mkdirSync)(dir, { recursive: true });
|
|
44
|
+
(0, local_files_js_1.ensureLocalFilesIgnored)(dir);
|
|
44
45
|
const record = {
|
|
45
46
|
v: exports.OBSERVE_VERSION,
|
|
46
47
|
ts: new Date().toISOString(),
|
|
47
48
|
...input,
|
|
48
49
|
};
|
|
49
|
-
(0, node_fs_1.appendFileSync)((0, node_path_1.resolve)(dir,
|
|
50
|
+
(0, node_fs_1.appendFileSync)((0, node_path_1.resolve)(dir, local_files_js_1.LEDGER_FILE), formatObservation(record));
|
|
50
51
|
}
|
|
51
52
|
catch {
|
|
52
53
|
/* best-effort — recording is never allowed to break a session */
|
|
@@ -59,7 +60,7 @@ function appendObservation(input, cwd = process.cwd()) {
|
|
|
59
60
|
function readObservations(cwd = process.cwd()) {
|
|
60
61
|
let raw;
|
|
61
62
|
try {
|
|
62
|
-
raw = (0, node_fs_1.readFileSync)((0, node_path_1.resolve)(cwd,
|
|
63
|
+
raw = (0, node_fs_1.readFileSync)((0, node_path_1.resolve)(cwd, local_files_js_1.VIGILES_DIR, local_files_js_1.LEDGER_FILE), "utf8");
|
|
63
64
|
}
|
|
64
65
|
catch {
|
|
65
66
|
return [];
|
|
@@ -129,7 +130,7 @@ function formatLedgerSummary(records, committedLocks) {
|
|
|
129
130
|
if (records.length === 0)
|
|
130
131
|
return "";
|
|
131
132
|
const lines = [
|
|
132
|
-
`Flight recorder — ${records.length} record${records.length === 1 ? "" : "s"} in .vigiles/${
|
|
133
|
+
`Flight recorder — ${records.length} record${records.length === 1 ? "" : "s"} in .vigiles/${local_files_js_1.LEDGER_FILE}`,
|
|
133
134
|
];
|
|
134
135
|
const counts = new Map();
|
|
135
136
|
for (const r of records)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vigiles",
|
|
3
|
-
"version": "30.0.
|
|
3
|
+
"version": "30.0.1",
|
|
4
4
|
"description": "Audit, test and measure the harness your AI agent runs on — grade your CLAUDE.md / AGENTS.md, skills, subagents and hooks, run them against a scripted model, and measure whether they actually fire.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"claude-code",
|