@theglitchking/semantic-memory 1.6.1 → 1.7.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-plugin/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +1 -1
- package/CHANGELOG.md +61 -0
- package/LICENSES/Apache-2.0.txt +202 -0
- package/README.md +19 -2
- package/THIRD_PARTY_NOTICES.md +17 -0
- package/commands/jev-mode.md +18 -0
- package/commands/skill-loop.md +23 -0
- package/dist/{chunk-TMMTOFHI.js → chunk-34VN47IW.js} +2 -2
- package/dist/chunk-3FPUBAEW.js +113 -0
- package/dist/chunk-3FPUBAEW.js.map +1 -0
- package/dist/{chunk-ZLIHQZ4C.js → chunk-6TIKWOY4.js} +5 -5
- package/dist/{chunk-KSH2QO6V.js → chunk-7UAVDOI6.js} +5 -5
- package/dist/{chunk-PGVIBUL3.js → chunk-AIXQFTSB.js} +5 -5
- package/dist/chunk-BEACL25M.js +224 -0
- package/dist/chunk-BEACL25M.js.map +1 -0
- package/dist/chunk-CATPGINC.js +186 -0
- package/dist/chunk-CATPGINC.js.map +1 -0
- package/dist/chunk-DBFD3MTT.js +130 -0
- package/dist/chunk-DBFD3MTT.js.map +1 -0
- package/dist/{chunk-HZ7B5KHY.js → chunk-EXAUVIGW.js} +4 -4
- package/dist/chunk-GYR7U6KA.js +80 -0
- package/dist/chunk-GYR7U6KA.js.map +1 -0
- package/dist/chunk-IEZVFKUT.js +157 -0
- package/dist/chunk-IEZVFKUT.js.map +1 -0
- package/dist/chunk-MQS7JEBD.js +137 -0
- package/dist/chunk-MQS7JEBD.js.map +1 -0
- package/dist/chunk-NCGM36NX.js +118 -0
- package/dist/chunk-NCGM36NX.js.map +1 -0
- package/dist/chunk-NIZLBNH4.js +165 -0
- package/dist/chunk-NIZLBNH4.js.map +1 -0
- package/dist/chunk-OT3MV7XF.js +123 -0
- package/dist/chunk-OT3MV7XF.js.map +1 -0
- package/dist/chunk-P3GLY42T.js +291 -0
- package/dist/chunk-P3GLY42T.js.map +1 -0
- package/dist/chunk-PJXYCCI2.js +177 -0
- package/dist/chunk-PJXYCCI2.js.map +1 -0
- package/dist/chunk-R6GNP7DS.js +87 -0
- package/dist/chunk-R6GNP7DS.js.map +1 -0
- package/dist/chunk-TKHWVISV.js +71 -0
- package/dist/chunk-TKHWVISV.js.map +1 -0
- package/dist/{chunk-GYBUBJYM.js → chunk-VBLCUWT4.js} +17 -8
- package/dist/chunk-VBLCUWT4.js.map +1 -0
- package/dist/chunk-VQCNWRTL.js +121 -0
- package/dist/chunk-VQCNWRTL.js.map +1 -0
- package/dist/{chunk-HUG7E25D.js → chunk-Y7H76Q7F.js} +1 -1
- package/dist/chunk-Y7H76Q7F.js.map +1 -0
- package/dist/chunk-ZSWI3Y4Y.js +232 -0
- package/dist/chunk-ZSWI3Y4Y.js.map +1 -0
- package/dist/cli/index.js +381 -52
- package/dist/cli/index.js.map +1 -1
- package/dist/core/features.d.ts +38 -0
- package/dist/core/features.js +28 -0
- package/dist/core/index.d.ts +285 -2
- package/dist/core/index.js +170 -21
- package/dist/core/index.js.map +1 -1
- package/dist/core/jev.d.ts +35 -0
- package/dist/core/jev.js +20 -0
- package/dist/core/skill-curator.d.ts +1 -0
- package/dist/core/skill-curator.js +41 -0
- package/dist/core/skill-evidence.d.ts +25 -0
- package/dist/core/skill-evidence.js +23 -0
- package/dist/core/skill-queue.d.ts +40 -0
- package/dist/core/skill-queue.js +38 -0
- package/dist/core/skill-review.d.ts +3 -0
- package/dist/core/skill-review.js +29 -0
- package/dist/core/skill-worker.d.ts +21 -0
- package/dist/core/skill-worker.js +70 -0
- package/dist/core/skill-worker.js.map +1 -0
- package/dist/{dossier-EN26WDKC.js → dossier-RXBLQOXC.js} +3 -3
- package/dist/failure-eval-IUJO47UC.js +18 -0
- package/dist/failure-eval-IUJO47UC.js.map +1 -0
- package/dist/{healthcheck-53OVXUQQ.js → healthcheck-S7D6YNTA.js} +7 -7
- package/dist/healthcheck-S7D6YNTA.js.map +1 -0
- package/dist/{lexicon-XNA5IFIA.js → lexicon-SEJSLBHO.js} +4 -4
- package/dist/lexicon-SEJSLBHO.js.map +1 -0
- package/dist/{lint-O4RQ47NX.js → lint-3JNLRM5U.js} +6 -6
- package/dist/lint-3JNLRM5U.js.map +1 -0
- package/dist/mcp/server.js +19 -17
- package/dist/mcp/server.js.map +1 -1
- package/dist/{profile-SIWAVD7O.js → profile-SNULTJ27.js} +4 -4
- package/dist/profile-SNULTJ27.js.map +1 -0
- package/dist/{schema-UJNYYRZZ.js → schema-RDYUK6CC.js} +2 -2
- package/dist/schema-RDYUK6CC.js.map +1 -0
- package/dist/skill-curator-D7HHZaUI.d.ts +71 -0
- package/dist/skill-land-XU2APPBJ.js +31 -0
- package/dist/skill-land-XU2APPBJ.js.map +1 -0
- package/dist/skill-ledger-AYXVNFX7.js +18 -0
- package/dist/skill-ledger-AYXVNFX7.js.map +1 -0
- package/dist/skill-merge-GXBQLBZA.js +24 -0
- package/dist/skill-merge-GXBQLBZA.js.map +1 -0
- package/dist/skill-review-BaW5n0Zg.d.ts +179 -0
- package/dist/skill-schema-RVB3EHEF.js +30 -0
- package/dist/skill-schema-RVB3EHEF.js.map +1 -0
- package/hooks/hooks.json +26 -1
- package/hooks/jev-bridge.py +109 -0
- package/hooks/session-start.js +5 -0
- package/hooks/skill-loop.js +65 -0
- package/hooks/vault-context.js +104 -6
- package/package.json +4 -2
- package/dist/chunk-5F5ZIAW2.js +0 -79
- package/dist/chunk-5F5ZIAW2.js.map +0 -1
- package/dist/chunk-GYBUBJYM.js.map +0 -1
- package/dist/chunk-HUG7E25D.js.map +0 -1
- package/dist/failure-eval-37JRNTIW.js +0 -10
- /package/dist/{chunk-TMMTOFHI.js.map → chunk-34VN47IW.js.map} +0 -0
- /package/dist/{chunk-ZLIHQZ4C.js.map → chunk-6TIKWOY4.js.map} +0 -0
- /package/dist/{chunk-KSH2QO6V.js.map → chunk-7UAVDOI6.js.map} +0 -0
- /package/dist/{chunk-PGVIBUL3.js.map → chunk-AIXQFTSB.js.map} +0 -0
- /package/dist/{chunk-HZ7B5KHY.js.map → chunk-EXAUVIGW.js.map} +0 -0
- /package/dist/{dossier-EN26WDKC.js.map → core/features.js.map} +0 -0
- /package/dist/{failure-eval-37JRNTIW.js.map → core/jev.js.map} +0 -0
- /package/dist/{healthcheck-53OVXUQQ.js.map → core/skill-curator.js.map} +0 -0
- /package/dist/{lexicon-XNA5IFIA.js.map → core/skill-evidence.js.map} +0 -0
- /package/dist/{lint-O4RQ47NX.js.map → core/skill-queue.js.map} +0 -0
- /package/dist/{profile-SIWAVD7O.js.map → core/skill-review.js.map} +0 -0
- /package/dist/{schema-UJNYYRZZ.js.map → dossier-RXBLQOXC.js.map} +0 -0
package/dist/cli/index.js
CHANGED
|
@@ -1,10 +1,34 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
tuneDescription
|
|
4
|
+
} from "../chunk-OT3MV7XF.js";
|
|
5
|
+
import {
|
|
6
|
+
buildEvalSet,
|
|
7
|
+
runTrigger
|
|
8
|
+
} from "../chunk-MQS7JEBD.js";
|
|
9
|
+
import {
|
|
10
|
+
runTaskEval
|
|
11
|
+
} from "../chunk-DBFD3MTT.js";
|
|
12
|
+
import "../chunk-IEZVFKUT.js";
|
|
13
|
+
import {
|
|
14
|
+
parseSkill
|
|
15
|
+
} from "../chunk-ZSWI3Y4Y.js";
|
|
16
|
+
import "../chunk-J7LYDRML.js";
|
|
17
|
+
import {
|
|
18
|
+
ENV,
|
|
19
|
+
describeFeature,
|
|
20
|
+
featureState,
|
|
21
|
+
setRepoConfig,
|
|
22
|
+
setSettingsEnv
|
|
23
|
+
} from "../chunk-VQCNWRTL.js";
|
|
24
|
+
import "../chunk-GYR7U6KA.js";
|
|
25
|
+
import "../chunk-H2BES6FU.js";
|
|
2
26
|
import "../chunk-PZ5AY32C.js";
|
|
3
27
|
|
|
4
28
|
// src/cli/index.ts
|
|
5
29
|
import { program } from "commander";
|
|
6
|
-
import { resolve as
|
|
7
|
-
import { existsSync as existsSync3, readFileSync, writeFileSync, rmSync } from "fs";
|
|
30
|
+
import { resolve as resolve4, join as join3, dirname, relative } from "path";
|
|
31
|
+
import { existsSync as existsSync3, readFileSync as readFileSync2, writeFileSync, rmSync } from "fs";
|
|
8
32
|
import { createRequire } from "module";
|
|
9
33
|
import { spawnSync } from "child_process";
|
|
10
34
|
import { pathToFileURL } from "url";
|
|
@@ -71,15 +95,15 @@ async function listShippedSkills(sourceRoot) {
|
|
|
71
95
|
const skillsDir = join(sourceRoot, "skills");
|
|
72
96
|
if (!existsSync(skillsDir)) return [];
|
|
73
97
|
const entries = await readdir(skillsDir, { withFileTypes: true });
|
|
74
|
-
const
|
|
98
|
+
const out2 = [];
|
|
75
99
|
for (const e of entries) {
|
|
76
100
|
if (!e.isDirectory()) continue;
|
|
77
101
|
const dir = join(skillsDir, e.name);
|
|
78
102
|
const skillFile = join(dir, "SKILL.md");
|
|
79
103
|
if (!existsSync(skillFile)) continue;
|
|
80
|
-
|
|
104
|
+
out2.push({ name: e.name, dir });
|
|
81
105
|
}
|
|
82
|
-
return
|
|
106
|
+
return out2;
|
|
83
107
|
}
|
|
84
108
|
async function sha256OfFile(path) {
|
|
85
109
|
const buf = await readFile(path);
|
|
@@ -305,6 +329,240 @@ function safeMtime(path) {
|
|
|
305
329
|
}
|
|
306
330
|
}
|
|
307
331
|
|
|
332
|
+
// src/cli/features.ts
|
|
333
|
+
var NEXT = "takes effect in the next Claude Code session (settings env is read at session start)";
|
|
334
|
+
function toggle(feature, on, opts) {
|
|
335
|
+
const cwd = process.cwd();
|
|
336
|
+
try {
|
|
337
|
+
if (opts.repo) {
|
|
338
|
+
const path = setRepoConfig(cwd, "skill_loop", opts.clear ? void 0 : on ? "on" : "off");
|
|
339
|
+
console.log(`\u2713 ${opts.clear ? "cleared" : "set"} skill_loop${opts.clear ? "" : `: ${on ? "on" : "off"}`} in ${path}`);
|
|
340
|
+
} else {
|
|
341
|
+
const { path, backup } = setSettingsEnv(ENV[feature], on ? "1" : void 0);
|
|
342
|
+
console.log(`\u2713 ${on ? "set" : "removed"} ${ENV[feature]}${on ? "=1" : ""} in ${path}${backup ? ` (previous file: ${backup})` : ""}`);
|
|
343
|
+
console.log(` ${NEXT}`);
|
|
344
|
+
}
|
|
345
|
+
} catch (e) {
|
|
346
|
+
console.error(`\u2717 ${e.message}`);
|
|
347
|
+
process.exit(1);
|
|
348
|
+
}
|
|
349
|
+
const env = { ...process.env };
|
|
350
|
+
if (!opts.repo) {
|
|
351
|
+
if (on) env[ENV[feature]] = "1";
|
|
352
|
+
else delete env[ENV[feature]];
|
|
353
|
+
}
|
|
354
|
+
console.log(` now: ${describeFeature(featureState(feature, cwd, env))}`);
|
|
355
|
+
}
|
|
356
|
+
function registerFeatureCommands(program2, extraStatus = {}) {
|
|
357
|
+
for (const [feature, name, what] of [
|
|
358
|
+
["skill_loop", "skill-loop", "the skill loop (learned skills between sessions)"],
|
|
359
|
+
["jev", "jev-mode", "jev mode (Jev through jev-loop for closed-set decisions)"]
|
|
360
|
+
]) {
|
|
361
|
+
const cmd = program2.commands.find((c) => c.name() === name) ?? program2.command(name).description(`Turn ${what} on or off, or show its state.`);
|
|
362
|
+
const on = cmd.command("on").description(`Turn ${what} on (sets ${ENV[feature]}=1 in ~/.claude/settings.json env).`);
|
|
363
|
+
const off = cmd.command("off").description(`Turn ${what} off (removes ${ENV[feature]} from ~/.claude/settings.json env).`);
|
|
364
|
+
if (feature === "skill_loop") {
|
|
365
|
+
for (const c of [on, off]) {
|
|
366
|
+
c.option("--repo", "Write the per-repo override in .claude/.semantic-memory/config instead (beats the global switch)");
|
|
367
|
+
c.option("--clear", "With --repo: remove the override so the global switch applies");
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
on.action((opts) => toggle(feature, true, opts));
|
|
371
|
+
off.action((opts) => toggle(feature, false, opts));
|
|
372
|
+
cmd.command("status").description(`Show whether ${what} is on, and why.`).option("--json", "JSON output").action(async (opts) => {
|
|
373
|
+
const cwd = process.cwd();
|
|
374
|
+
const s = featureState(feature, cwd);
|
|
375
|
+
const extra = await extraStatus[feature]?.(cwd) ?? [];
|
|
376
|
+
if (opts.json) console.log(JSON.stringify({ ...s, details: extra }, null, 2));
|
|
377
|
+
else for (const line of [describeFeature(s), ...extra.map((l) => ` ${l}`)]) console.log(line);
|
|
378
|
+
process.exit(0);
|
|
379
|
+
});
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
// src/cli/skill-loop.ts
|
|
384
|
+
import { resolve as resolve2 } from "path";
|
|
385
|
+
function registerSkillLoopCommands(program2) {
|
|
386
|
+
const cmd = program2.commands.find((c) => c.name() === "skill-loop");
|
|
387
|
+
registerReviewCommands(cmd);
|
|
388
|
+
registerCuratorCommands(cmd);
|
|
389
|
+
cmd.command("worker").description("Drain this repo's review queue (started detached by the SessionEnd hook; safe to run by hand).").option("--repo <dir>", "Repo root (default: cwd)").action(async (opts) => {
|
|
390
|
+
const repo = resolve2(opts.repo ?? process.cwd());
|
|
391
|
+
const { featureOn } = await import("../core/features.js");
|
|
392
|
+
const { log } = await import("../core/skill-queue.js");
|
|
393
|
+
if (!featureOn("skill_loop", repo)) {
|
|
394
|
+
log(repo, "worker: skill loop is off \u2014 queue left as it is");
|
|
395
|
+
process.exit(0);
|
|
396
|
+
}
|
|
397
|
+
const { drain } = await import("../core/skill-worker.js");
|
|
398
|
+
const { reviewSession } = await import("../core/skill-review.js");
|
|
399
|
+
const r = await drain(repo, (job) => reviewSession(job));
|
|
400
|
+
console.log(r.locked ? "another worker holds this repo's lock" : `reviewed ${r.reviewed} session(s)${r.capped ? "; daily cap reached" : ""}${r.failed ? `; ${r.failed} set aside` : ""}`);
|
|
401
|
+
process.exit(0);
|
|
402
|
+
});
|
|
403
|
+
}
|
|
404
|
+
function registerReviewCommands(cmd) {
|
|
405
|
+
cmd.command("review").description("List skill drafts with their evidence (and diff, for patches); approve, reject or evaluate one.").option("--json", "JSON output").option("--approve <id>", "Land a draft (writes the skill and a ledger entry)").option("--promote", "With --approve: land it as a global skill replacing the project copies (needs a promotion)").option("--override <why>", "With --approve: land it although its eval gate did not adopt it (the reason is ledgered)").option("--reject <id>", "Drop a draft (ledgered)").option("--eval <id>", "Run the trigger eval on a draft and record the scores and the gate's verdict").option("--task", "With --eval: also replay the task with and without the skill (runs the draft's Verify command)").action(async (opts) => {
|
|
406
|
+
const { repoRoot } = await import("../core/skill-evidence.js");
|
|
407
|
+
const land = await import("../skill-land-XU2APPBJ.js");
|
|
408
|
+
const repo = repoRoot(process.cwd());
|
|
409
|
+
try {
|
|
410
|
+
if (opts.approve) {
|
|
411
|
+
const e = land.approveDraft(repo, opts.approve, { promote: opts.promote, override: opts.override });
|
|
412
|
+
console.log(`\u2713 ${e.action}: ${e.files.map((f) => f.path).join(", ")} (ledger ${e.id}; undo: skill-loop rollback ${e.id})`);
|
|
413
|
+
} else if (opts.reject) {
|
|
414
|
+
const e = land.rejectDraft(repo, opts.reject);
|
|
415
|
+
console.log(`\u2713 rejected ${e.skill} (ledger ${e.id})`);
|
|
416
|
+
} else if (opts.eval) {
|
|
417
|
+
const ev = await land.evalDraft(repo, opts.eval, { task: opts.task });
|
|
418
|
+
console.log(opts.json ? JSON.stringify(ev, null, 2) : `gate: ${ev.gate.verdict} \u2014 ${ev.gate.reasons.join("; ")} (${ev.prompts} prompts, ${ev.runs} claude runs)`);
|
|
419
|
+
} else {
|
|
420
|
+
const drafts = land.describeDrafts(repo);
|
|
421
|
+
if (opts.json) console.log(JSON.stringify(drafts, null, 2));
|
|
422
|
+
else if (!drafts.length) console.log("no skill drafts waiting");
|
|
423
|
+
else
|
|
424
|
+
for (const d of drafts) {
|
|
425
|
+
console.log(`
|
|
426
|
+
${d.id} ${d.kind} ${d.name} \u2192 ${d.destination}`);
|
|
427
|
+
console.log(` scope: ${d.scope}${d.scope_why ? ` (${d.scope_why})` : ""}${d.promotion?.length ? `; also in ${d.promotion.map((p) => p.repo).join(", ")}: --promote offers one global skill` : ""}`);
|
|
428
|
+
console.log(` evidence: sessions ${d.provenance?.sessions.join(", ")}; failures ${d.provenance?.failures.join(", ") || "-"}; corrections ${d.provenance?.corrections.join(", ") || "-"}`);
|
|
429
|
+
if (d.verify.kind === "command") console.log(` verify command (a --task eval runs it): ${d.verify.command}`);
|
|
430
|
+
if (d.evals) console.log(` evals: ${JSON.stringify(d.evals.gate)}`);
|
|
431
|
+
for (const w of d.warnings) console.log(` ! ${w}`);
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
} catch (e) {
|
|
435
|
+
console.error(`\u2717 ${e.message}`);
|
|
436
|
+
process.exit(1);
|
|
437
|
+
}
|
|
438
|
+
process.exit(0);
|
|
439
|
+
});
|
|
440
|
+
cmd.command("rollback <ledger-id>").description("Undo one ledgered change exactly (refuses if a file changed since).").action(async (id) => {
|
|
441
|
+
const { repoRoot } = await import("../core/skill-evidence.js");
|
|
442
|
+
const { rollback } = await import("../skill-ledger-AYXVNFX7.js");
|
|
443
|
+
try {
|
|
444
|
+
const e = rollback(repoRoot(process.cwd()), id);
|
|
445
|
+
console.log(`\u2713 ${e.why} (ledger ${e.id})`);
|
|
446
|
+
} catch (err) {
|
|
447
|
+
console.error(`\u2717 ${err.message}`);
|
|
448
|
+
process.exit(1);
|
|
449
|
+
}
|
|
450
|
+
process.exit(0);
|
|
451
|
+
});
|
|
452
|
+
}
|
|
453
|
+
function registerCuratorCommands(cmd) {
|
|
454
|
+
const repoOf = async () => (await import("../core/skill-evidence.js")).repoRoot(process.cwd());
|
|
455
|
+
cmd.command("curate").description("Mark learned skills unused 14 days stale and archive those unused 30 days (pinned / cited / new ones skipped).").option("--dry-run", "Show what would change, change nothing").option("--merge", "Also propose merges of overlapping learned skills as review drafts (opt-in)").option("--auto", "Started by SessionStart: quiet, logs to skill-loop.log").action(async (opts) => {
|
|
456
|
+
const repo = await repoOf();
|
|
457
|
+
const { curate } = await import("../core/skill-curator.js");
|
|
458
|
+
const { log } = await import("../core/skill-queue.js");
|
|
459
|
+
const r = curate(repo, { dryRun: opts.dryRun });
|
|
460
|
+
const changes = r.actions.filter((a) => a.action !== "skip" && a.action !== "keep");
|
|
461
|
+
if (opts.auto) log(repo, `curator: ${changes.map((a) => `${a.action} ${a.skill} (${a.why})`).join(", ") || "nothing to do"}`);
|
|
462
|
+
else for (const a of r.actions) console.log(`${opts.dryRun ? "would " : ""}${a.action.padEnd(10)} ${a.skill} \u2014 ${a.why}`);
|
|
463
|
+
if (!opts.auto && !r.actions.length) console.log("no learned skills here");
|
|
464
|
+
const { consolidateOn, proposeMerges } = await import("../skill-merge-GXBQLBZA.js");
|
|
465
|
+
if (!opts.dryRun && (opts.merge || consolidateOn(repo))) {
|
|
466
|
+
const m = await proposeMerges(repo);
|
|
467
|
+
const line = `merge: ${m.drafted.length} draft(s) for review${m.skipped.length ? `; ${m.skipped.join("; ")}` : ""}`;
|
|
468
|
+
if (opts.auto) log(repo, line);
|
|
469
|
+
else console.log(line);
|
|
470
|
+
}
|
|
471
|
+
process.exit(0);
|
|
472
|
+
});
|
|
473
|
+
cmd.command("restore <name>").description("Bring an archived learned skill back, active.").action(async (name) => {
|
|
474
|
+
const { restore } = await import("../core/skill-curator.js");
|
|
475
|
+
try {
|
|
476
|
+
const e = restore(await repoOf(), name);
|
|
477
|
+
console.log(`\u2713 restored ${name} \u2192 ${e.files[0].path} (ledger ${e.id}). Pin it (skill_loop.pinned: true) to keep the curator away.`);
|
|
478
|
+
} catch (err) {
|
|
479
|
+
console.error(`\u2717 ${err.message}`);
|
|
480
|
+
process.exit(1);
|
|
481
|
+
}
|
|
482
|
+
process.exit(0);
|
|
483
|
+
});
|
|
484
|
+
cmd.command("verify").description("Run each landed learned skill's ## Verify command here; failures are flagged, nothing is archived.").action(async () => {
|
|
485
|
+
const { verifySkills } = await import("../core/skill-curator.js");
|
|
486
|
+
const r = verifySkills(await repoOf());
|
|
487
|
+
for (const x of r) console.log(`${x.result === "pass" ? "\u2713" : x.result === "fail" ? "\u2717" : "\xB7"} ${x.skill}: ${x.result} \u2014 ${x.detail}`);
|
|
488
|
+
if (!r.length) console.log("no learned skills here");
|
|
489
|
+
process.exit(r.some((x) => x.result === "fail") ? 1 : 0);
|
|
490
|
+
});
|
|
491
|
+
}
|
|
492
|
+
async function skillLoopStatus(cwd) {
|
|
493
|
+
const { repoRoot } = await import("../core/skill-evidence.js");
|
|
494
|
+
const { listDrafts } = await import("../core/skill-review.js");
|
|
495
|
+
const { runsToday, dailyRunCap, pendingJobs, lockHolder } = await import("../core/skill-queue.js");
|
|
496
|
+
const repo = repoRoot(cwd);
|
|
497
|
+
const lines = [
|
|
498
|
+
`drafts waiting: ${listDrafts(repo).length}${listDrafts(repo).length ? " (/skill-loop review)" : ""}`,
|
|
499
|
+
`queued sessions for this repo: ${pendingJobs(repo).length}${lockHolder(repo) ? ` (worker running, pid ${lockHolder(repo)})` : ""}`,
|
|
500
|
+
`reviews today (all repos): ${runsToday()} of ${dailyRunCap()} (SEMANTIC_MEMORY_SKILL_LOOP_DAILY_RUNS)`
|
|
501
|
+
];
|
|
502
|
+
const { curatorState } = await import("../core/skill-curator.js");
|
|
503
|
+
const st = curatorState(repo);
|
|
504
|
+
lines.push(`curator: ${st.last_run ? `last run ${st.last_run.slice(0, 16)} \u2014 ${st.last_report}` : "not run yet (first run 7 days after the loop first sees a repo)"}`);
|
|
505
|
+
if (st.flagged?.length) lines.push(`verify failures flagged: ${st.flagged.map((f) => f.name).join(", ")}`);
|
|
506
|
+
return lines;
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
// src/cli/skill-eval.ts
|
|
510
|
+
import { readFileSync } from "fs";
|
|
511
|
+
import { resolve as resolve3 } from "path";
|
|
512
|
+
var pct = (n) => n === null ? "n/a" : `${Math.round(n * 100)}%`;
|
|
513
|
+
function load(file, promptsFile) {
|
|
514
|
+
const skillRaw = readFileSync(resolve3(file), "utf8");
|
|
515
|
+
const candidates = readFileSync(resolve3(promptsFile), "utf8").split("\n").filter(Boolean).map((l) => JSON.parse(l));
|
|
516
|
+
return { skillRaw, prompts: buildEvalSet(parseSkill(skillRaw), candidates) };
|
|
517
|
+
}
|
|
518
|
+
var out = (json, data, text) => console.log(json ? JSON.stringify(data, null, 2) : text());
|
|
519
|
+
function registerSkillEvalCommands(program2) {
|
|
520
|
+
const ev = program2.command("skill-eval").description("Evaluate a learned skill: does it trigger, does it help, can its description be better (v1.7)");
|
|
521
|
+
const repoRoot = process.cwd();
|
|
522
|
+
const fail = (e) => {
|
|
523
|
+
console.error(`\u2717 ${e.message}`);
|
|
524
|
+
process.exit(1);
|
|
525
|
+
};
|
|
526
|
+
ev.command("trigger <skill-md>").description("Run the trigger eval; --prompts is JSONL of {session, text}").requiredOption("--prompts <jsonl>", "candidate prompts, one {session, text} per line").option("--json", "machine-readable output").action(async (file, opts) => {
|
|
527
|
+
try {
|
|
528
|
+
const { skillRaw, prompts } = load(file, opts.prompts);
|
|
529
|
+
const r = await runTrigger(prompts, { repoRoot, skillRaw });
|
|
530
|
+
out(
|
|
531
|
+
opts.json,
|
|
532
|
+
r,
|
|
533
|
+
() => [
|
|
534
|
+
`${r.skill}: ${prompts.length} prompts, ${r.runs} claude run(s)`,
|
|
535
|
+
...["train", "heldout"].map((s) => ` ${s}: triggers on ${pct(r[s].positiveRate)} of positives, false-triggers on ${pct(r[s].falseTriggerRate)} of near-misses (n=${r[s].n})`)
|
|
536
|
+
].join("\n")
|
|
537
|
+
);
|
|
538
|
+
} catch (e) {
|
|
539
|
+
fail(e);
|
|
540
|
+
}
|
|
541
|
+
});
|
|
542
|
+
ev.command("task <skill-md>").description("Replay a task with and without the skill and check its ## Verify").requiredOption("--prompt <text>", "the task prompt").option("--json", "machine-readable output").action(async (file, opts) => {
|
|
543
|
+
try {
|
|
544
|
+
const r = await runTaskEval({ repoRoot, skillRaw: readFileSync(resolve3(file), "utf8"), prompt: opts.prompt });
|
|
545
|
+
out(opts.json, r, () => `${r.skill}: with skill ${r.with.passed ? "PASS" : "FAIL"} (${r.with.evidence}); without ${r.without.passed ? "PASS" : "FAIL"} (${r.without.evidence})`);
|
|
546
|
+
} catch (e) {
|
|
547
|
+
fail(e);
|
|
548
|
+
}
|
|
549
|
+
});
|
|
550
|
+
ev.command("tune <skill-md>").description("Propose a better description (never writes the skill file)").requiredOption("--prompts <jsonl>", "candidate prompts, one {session, text} per line").option("--json", "machine-readable output").action(async (file, opts) => {
|
|
551
|
+
try {
|
|
552
|
+
const { skillRaw, prompts } = load(file, opts.prompts);
|
|
553
|
+
const r = await tuneDescription({ repoRoot, skillRaw, prompts });
|
|
554
|
+
out(
|
|
555
|
+
opts.json,
|
|
556
|
+
r,
|
|
557
|
+
() => r.improved ? `proposed (held-out ${pct(r.original.heldout)} -> ${pct(r.best.heldout)}):
|
|
558
|
+
${r.best.description}` : `no better description found (held-out ${pct(r.original.heldout)}); ${r.candidates.length - 1} tried`
|
|
559
|
+
);
|
|
560
|
+
} catch (e) {
|
|
561
|
+
fail(e);
|
|
562
|
+
}
|
|
563
|
+
});
|
|
564
|
+
}
|
|
565
|
+
|
|
308
566
|
// src/cli/index.ts
|
|
309
567
|
var require_ = createRequire(import.meta.url);
|
|
310
568
|
var { version } = require_("../../package.json");
|
|
@@ -314,7 +572,7 @@ function runRelink(cwd) {
|
|
|
314
572
|
const candidates = [
|
|
315
573
|
join3(cwd, "node_modules", "@theglitchking", "semantic-memory", "scripts", "link-skills.js"),
|
|
316
574
|
join3(cwd, "node_modules", "@theglitchking", "semantic-sidekick", "scripts", "link-skills.js"),
|
|
317
|
-
|
|
575
|
+
resolve4(process.cwd(), "scripts", "link-skills.js")
|
|
318
576
|
];
|
|
319
577
|
const script = candidates.find((p) => existsSync3(p));
|
|
320
578
|
if (!script) {
|
|
@@ -400,7 +658,7 @@ function localBinArg(cwd) {
|
|
|
400
658
|
}
|
|
401
659
|
function readJsonSafe(path) {
|
|
402
660
|
try {
|
|
403
|
-
const raw =
|
|
661
|
+
const raw = readFileSync2(path, "utf8");
|
|
404
662
|
return raw.trim() ? JSON.parse(raw) : null;
|
|
405
663
|
} catch {
|
|
406
664
|
return null;
|
|
@@ -636,38 +894,81 @@ program.command("search <query>").description("Run a vault search and print top
|
|
|
636
894
|
process.exit(1);
|
|
637
895
|
}
|
|
638
896
|
});
|
|
639
|
-
program.command("eval-failures").description("Replay failed tool calls through search and measure how often the top hit answers them (v1.6 failure lookup)").requiredOption("--cases <file>", "JSONL of {id, notes, error, command}").requiredOption("--judgments <file>", "JSONL of {case, path, relevant}").option("--limit <n>", "Hits per search", (v) => parseInt(v, 10), 5).option("--query <mode>", "raw (error + command as-is) or normalized (failureQuery)", "normalized").option("--target <p>", "Target precision for the recommended threshold", (v) => parseFloat(v), 0.95).option("--min-judged <n>", "Judged injections needed before a threshold can be recommended", (v) => parseInt(v, 10), 20).option("--unjudged-out <file>", "Write unlabelled top hits here (JSONL) for labelling").option("--hits-out <file>", "Write every case's hits here (JSONL) for analysis").option("--json", "Print the report as JSON").action(async (opts) => {
|
|
897
|
+
program.command("eval-failures").description("Replay failed tool calls through search and measure how often the top hit answers them (v1.6 failure lookup)").requiredOption("--cases <file>", "JSONL of {id, notes, error, command}").requiredOption("--judgments <file>", "JSONL of {case, path, relevant}").option("--limit <n>", "Hits per search", (v) => parseInt(v, 10), 5).option("--query <mode>", "raw (error + command as-is) or normalized (failureQuery)", "normalized").option("--target <p>", "Target precision for the recommended threshold", (v) => parseFloat(v), 0.95).option("--min-judged <n>", "Judged injections needed before a threshold can be recommended", (v) => parseInt(v, 10), 20).option("--unjudged-out <file>", "Write unlabelled top hits here (JSONL) for labelling").option("--hits-out <file>", "Write every case's hits here (JSONL) for analysis").option("--json", "Print the report as JSON").option("--hits-in <file>", "Reuse hits from an earlier --hits-out instead of searching").option("--gate <kind>", "Also replay the failure hook's gate: 'jev' (needs --jev-scores)").option("--jev-scores <files...>", "JSONL of {case, path, p} from scripts/eval-failures/judge-jev.mjs").option("--gate-batch-out <file>", "With --gate jev: write the candidates Jev has not scored as a judge batch (set B format)").action(async (opts) => {
|
|
640
898
|
const { openSearch } = await import("../search-325ULY57.js");
|
|
641
|
-
const { evaluateFailures, formatFailureEval } = await import("../failure-eval-
|
|
899
|
+
const { evaluateFailures, formatFailureEval } = await import("../failure-eval-IUJO47UC.js");
|
|
642
900
|
const { failureQuery } = await import("../core/failure-query.js");
|
|
643
901
|
const toQuery = (c) => opts.query === "raw" ? `${c.error}
|
|
644
902
|
${c.command}` : failureQuery(c.error, c.command);
|
|
645
|
-
const readJsonl = (p) =>
|
|
903
|
+
const readJsonl = (p) => readFileSync2(resolve4(p), "utf-8").split("\n").filter(Boolean).map((l) => JSON.parse(l));
|
|
646
904
|
const cases = readJsonl(opts.cases);
|
|
647
905
|
const judgments = readJsonl(opts.judgments);
|
|
648
906
|
const searchers = /* @__PURE__ */ new Map();
|
|
649
907
|
const results = [];
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
908
|
+
if (opts.hitsIn) {
|
|
909
|
+
const ids = new Set(cases.map((c) => c.id));
|
|
910
|
+
results.push(...readJsonl(opts.hitsIn).filter((r) => ids.has(r.case)));
|
|
911
|
+
} else {
|
|
912
|
+
for (const c of cases) {
|
|
913
|
+
if (!searchers.has(c.notes)) searchers.set(c.notes, await openSearch(c.notes));
|
|
914
|
+
const hits = await searchers.get(c.notes).search(toQuery(c), opts.limit);
|
|
915
|
+
results.push({ case: c.id, hits: hits.map((h) => ({ path: h.path, score: h.score })) });
|
|
916
|
+
}
|
|
654
917
|
}
|
|
655
918
|
const report = evaluateFailures(results, judgments, { targetPrecision: opts.target, minJudged: opts.minJudged });
|
|
919
|
+
if (opts.gate) {
|
|
920
|
+
if (opts.gate !== "jev" || !opts.jevScores) {
|
|
921
|
+
console.error("--gate jev needs --jev-scores <file...>");
|
|
922
|
+
process.exit(2);
|
|
923
|
+
}
|
|
924
|
+
const { evaluateGate, formatGate } = await import("../failure-eval-IUJO47UC.js");
|
|
925
|
+
const jev = opts.jevScores.flatMap((f) => readJsonl(f));
|
|
926
|
+
const gate = evaluateGate(results, judgments, jev);
|
|
927
|
+
const veto = evaluateGate(results, judgments, jev, { mode: "veto", thresholds: [0.3, 0.35, 0.4, 0.45, 0.5] });
|
|
928
|
+
if (opts.gateBatchOut) {
|
|
929
|
+
const { default: matter } = await import("../matter-SNJ2WDL6.js");
|
|
930
|
+
const byId = new Map(cases.map((c) => [c.id, c]));
|
|
931
|
+
const missing = [...new Map([...gate.missing, ...veto.missing].map((m) => [`${m.case}\0${m.path}`, m])).values()];
|
|
932
|
+
const batch = missing.map((m) => {
|
|
933
|
+
const c = byId.get(m.case);
|
|
934
|
+
let title = m.path, excerpt = "";
|
|
935
|
+
try {
|
|
936
|
+
const doc = matter(readFileSync2(join3(c.notes, m.path), "utf-8"));
|
|
937
|
+
title = String(doc.data.title ?? m.path);
|
|
938
|
+
excerpt = doc.content.replace(/^#.*$/m, "").trim().slice(0, 600);
|
|
939
|
+
} catch {
|
|
940
|
+
}
|
|
941
|
+
return { case: m.case, path: m.path, command: c.command, error: c.error, doc_title: title, doc_excerpt: excerpt };
|
|
942
|
+
});
|
|
943
|
+
writeFileSync(resolve4(opts.gateBatchOut), JSON.stringify(batch, null, 1));
|
|
944
|
+
console.error(`${batch.length} unscored candidates \u2192 ${opts.gateBatchOut} (judge them with judge-jev.mjs, then re-run)`);
|
|
945
|
+
}
|
|
946
|
+
if (opts.json) {
|
|
947
|
+
console.log(JSON.stringify({ ...report, gate: gate.rows, veto: veto.rows }, null, 2));
|
|
948
|
+
process.exit(0);
|
|
949
|
+
}
|
|
950
|
+
console.log(formatFailureEval(report, `query: ${opts.query}`));
|
|
951
|
+
console.log("");
|
|
952
|
+
console.log(formatGate(gate.rows));
|
|
953
|
+
console.log("");
|
|
954
|
+
console.log(formatGate(veto.rows, "veto"));
|
|
955
|
+
process.exit(0);
|
|
956
|
+
}
|
|
656
957
|
if (opts.hitsOut) {
|
|
657
|
-
writeFileSync(
|
|
958
|
+
writeFileSync(resolve4(opts.hitsOut), results.map((r) => JSON.stringify(r)).join("\n") + "\n");
|
|
658
959
|
}
|
|
659
960
|
if (opts.unjudgedOut) {
|
|
660
|
-
writeFileSync(
|
|
961
|
+
writeFileSync(resolve4(opts.unjudgedOut), report.unjudged.map((u) => JSON.stringify(u)).join("\n") + "\n");
|
|
661
962
|
}
|
|
662
963
|
console.log(opts.json ? JSON.stringify(report, null, 2) : formatFailureEval(report, `query: ${opts.query}`));
|
|
663
964
|
process.exit(0);
|
|
664
965
|
});
|
|
665
966
|
program.command("lint").description("Lint the vault against vault.schema.yml (schema violations + missing provenance + stale)").requiredOption("--notes <path>", "Path to markdown notes directory").option("--rule <name>", "Limit output to one rule: schema_violations | missing_provenance | stale").option("--json", "Emit JSON instead of the human-readable report").option("--strict", "Exit non-zero on warnings too (default: exit non-zero only on errors)").action(async (opts) => {
|
|
666
|
-
const { lintVault, formatLintReport } = await import("../lint-
|
|
667
|
-
const report = await lintVault(
|
|
967
|
+
const { lintVault, formatLintReport } = await import("../lint-3JNLRM5U.js");
|
|
968
|
+
const report = await lintVault(resolve4(opts.notes));
|
|
668
969
|
if (opts.json) {
|
|
669
|
-
const
|
|
670
|
-
console.log(JSON.stringify(
|
|
970
|
+
const out2 = opts.rule ? report.byRule[opts.rule] : report;
|
|
971
|
+
console.log(JSON.stringify(out2, null, 2));
|
|
671
972
|
} else {
|
|
672
973
|
console.log(formatLintReport(report, { rule: opts.rule }));
|
|
673
974
|
}
|
|
@@ -687,7 +988,7 @@ program.command("log-event").description("Append a structured event to the vault
|
|
|
687
988
|
}
|
|
688
989
|
}
|
|
689
990
|
try {
|
|
690
|
-
const entry = await logEvent(
|
|
991
|
+
const entry = await logEvent(resolve4(opts.notes), {
|
|
691
992
|
kind: opts.kind,
|
|
692
993
|
summary: opts.summary,
|
|
693
994
|
payload
|
|
@@ -701,7 +1002,7 @@ program.command("log-event").description("Append a structured event to the vault
|
|
|
701
1002
|
program.command("log-query").description("Read structured log entries from log.md \u2014 filters by kind, date range, limit").requiredOption("--notes <path>", "Path to markdown notes directory").option("--kind <name>", "Filter by kind (ingest, synthesis, error, mode_change, etc.)").option("--after <iso>", "ISO timestamp \u2014 only entries on or after").option("--before <iso>", "ISO timestamp \u2014 only entries on or before").option("--limit <n>", "Max entries (most recent)", (v) => parseInt(v, 10)).action(async (opts) => {
|
|
702
1003
|
const { logQuery } = await import("../log-RM2NDNPH.js");
|
|
703
1004
|
try {
|
|
704
|
-
const entries = await logQuery(
|
|
1005
|
+
const entries = await logQuery(resolve4(opts.notes), {
|
|
705
1006
|
kind: opts.kind,
|
|
706
1007
|
after: opts.after,
|
|
707
1008
|
before: opts.before,
|
|
@@ -714,8 +1015,8 @@ program.command("log-query").description("Read structured log entries from log.m
|
|
|
714
1015
|
}
|
|
715
1016
|
});
|
|
716
1017
|
program.command("install-schema").description("Bootstrap vault.schema.yml in the vault root with the default schema").requiredOption("--notes <path>", "Path to markdown notes directory").option("--force", "Overwrite existing schema file").action(async (opts) => {
|
|
717
|
-
const { installDefaultSchema } = await import("../schema-
|
|
718
|
-
const r = await installDefaultSchema(
|
|
1018
|
+
const { installDefaultSchema } = await import("../schema-RDYUK6CC.js");
|
|
1019
|
+
const r = await installDefaultSchema(resolve4(opts.notes), opts.force);
|
|
719
1020
|
console.log(r.written ? `Installed default schema: ${r.path}` : `Schema already exists: ${r.path} (use --force to overwrite)`);
|
|
720
1021
|
process.exit(0);
|
|
721
1022
|
});
|
|
@@ -728,7 +1029,7 @@ program.command("tools [name]").description("List all MCP tools, or show details
|
|
|
728
1029
|
process.exit(0);
|
|
729
1030
|
});
|
|
730
1031
|
program.command("serve", { isDefault: true }).description("Start the MCP server (default command)").requiredOption("--notes <path>", "Path to markdown notes directory").option("--reindex", "Force full reindex and exit").option("--stats", "Show vault statistics and exit").option("--wait-for-ready", "Block startup until index is fully built before serving (default: index in background; tools return 'Indexing in progress' until ready)").option("--read-only", "Suppress write tools (create_note, update_note, delete_note, move_note, update_frontmatter, manage_tags, rename_tag, log_event, ...) \u2014 use for shared docs vaults owned by another tool").option("--model <name>", "Embedding model to use (default: all-MiniLM-L6-v2, fast; use nomic-ai/nomic-embed-text-v1.5 for higher quality)").option("--workers <n>", "Number of worker threads for parallel embedding", parseInt).option("--batch-size <n>", "Texts per ONNX forward pass (default: 8)", parseInt).option("--no-quantized", "Use full-precision model instead of quantized (slower, slightly higher quality)").option("--no-watch", "Disable file watcher").action(async (opts) => {
|
|
731
|
-
const notesPath =
|
|
1032
|
+
const notesPath = resolve4(opts.notes);
|
|
732
1033
|
if (!existsSync3(notesPath)) {
|
|
733
1034
|
console.error(`Error: notes directory not found: ${notesPath}`);
|
|
734
1035
|
process.exit(1);
|
|
@@ -824,7 +1125,7 @@ run 'npm install --save @theglitchking/semantic-memory' first, then re-run this
|
|
|
824
1125
|
}
|
|
825
1126
|
const bakPath = join3(dirname(mcpPath), ".mcp.json.bak");
|
|
826
1127
|
try {
|
|
827
|
-
writeFileSync(bakPath,
|
|
1128
|
+
writeFileSync(bakPath, readFileSync2(mcpPath, "utf8"));
|
|
828
1129
|
console.log(`backup written: ${bakPath}`);
|
|
829
1130
|
} catch (err) {
|
|
830
1131
|
console.error(`could not write backup: ${err.message}`);
|
|
@@ -836,7 +1137,7 @@ run 'npm install --save @theglitchking/semantic-memory' first, then re-run this
|
|
|
836
1137
|
console.error(`verification failed (exit ${verify.status}):
|
|
837
1138
|
${verify.stderr?.toString() || ""}`);
|
|
838
1139
|
console.error(`rolling back from ${bakPath}`);
|
|
839
|
-
writeFileSync(mcpPath,
|
|
1140
|
+
writeFileSync(mcpPath, readFileSync2(bakPath, "utf8"));
|
|
840
1141
|
process.exit(1);
|
|
841
1142
|
}
|
|
842
1143
|
console.log(`\u2713 .mcp.json normalized and verified. Toggle the MCPs in /mcp to reconnect.`);
|
|
@@ -895,7 +1196,7 @@ ${r2.stderr?.toString() || ""}`);
|
|
|
895
1196
|
}
|
|
896
1197
|
}
|
|
897
1198
|
if (smokeOk && !opts.json) console.log(`\u2713 local install starts cleanly (v${smokeVersion})`);
|
|
898
|
-
const { runHealthcheck, formatDriftBanner, filterToDrift } = await import("../healthcheck-
|
|
1199
|
+
const { runHealthcheck, formatDriftBanner, filterToDrift } = await import("../healthcheck-S7D6YNTA.js");
|
|
899
1200
|
const vaultCandidate = join3(cwd, ".claude", ".vault");
|
|
900
1201
|
const vaultPath = existsSync3(vaultCandidate) ? vaultCandidate : void 0;
|
|
901
1202
|
let result = await runHealthcheck({ projectRoot: cwd, vaultPath, tier: opts.fast ? "fast" : "all", force: true });
|
|
@@ -909,7 +1210,8 @@ ${r2.stderr?.toString() || ""}`);
|
|
|
909
1210
|
}
|
|
910
1211
|
}
|
|
911
1212
|
if (opts.json) {
|
|
912
|
-
|
|
1213
|
+
const features = { skill_loop: featureState("skill_loop", cwd), jev: featureState("jev", cwd) };
|
|
1214
|
+
console.log(JSON.stringify({ smoke: { ok: smokeOk, version: smokeVersion }, result, features, fix: opts.fix ? { plan, outcomes: fixOutcomes } : void 0 }, null, 2));
|
|
913
1215
|
} else {
|
|
914
1216
|
if (opts.fix) {
|
|
915
1217
|
if (fixOutcomes.length === 0 && plan.skipped.length === 0) {
|
|
@@ -930,6 +1232,9 @@ ${r2.stderr?.toString() || ""}`);
|
|
|
930
1232
|
console.log(`
|
|
931
1233
|
\u2713 no drift detected (${result.findings.length} checks ran in ${result.duration_ms}ms)`);
|
|
932
1234
|
}
|
|
1235
|
+
console.log(`
|
|
1236
|
+
${describeFeature(featureState("skill_loop", cwd))}
|
|
1237
|
+
${describeFeature(featureState("jev", cwd))}`);
|
|
933
1238
|
}
|
|
934
1239
|
process.exit(smokeOk ? 0 : 1);
|
|
935
1240
|
});
|
|
@@ -953,15 +1258,15 @@ function findSourceRoot() {
|
|
|
953
1258
|
const installed = join3(process.cwd(), "node_modules", "@theglitchking", pkg);
|
|
954
1259
|
if (existsSync3(installed)) return installed;
|
|
955
1260
|
}
|
|
956
|
-
const dev =
|
|
1261
|
+
const dev = resolve4(new URL("../../", import.meta.url).pathname);
|
|
957
1262
|
return dev;
|
|
958
1263
|
}
|
|
959
1264
|
var skillsCmd = program.command("skills").description("Install, target, or uninstall skill bundles for non-Claude agents (codex, copilot, pi). Claude installs continue to use the npm postinstall flow.");
|
|
960
1265
|
skillsCmd.command("targets").description("Preview where skill bundles would be installed for one or more agents.").option("--agent <name...>", "One or more agents (default: all). Repeatable.", void 0).option("--scope <scope>", "global or local (default: local)", "local").option("--project <path>", "Project root for local-scope paths (default: cwd)", process.cwd()).action((opts) => {
|
|
961
1266
|
const scope = parseScope(opts.scope);
|
|
962
1267
|
const agents = (opts.agent && opts.agent.length > 0 ? opts.agent : SKILL_BUNDLER_AGENTS).map(parseAgent);
|
|
963
|
-
const
|
|
964
|
-
console.log(JSON.stringify(
|
|
1268
|
+
const out2 = agents.map((a) => resolveTarget(a, scope, opts.project));
|
|
1269
|
+
console.log(JSON.stringify(out2, null, 2));
|
|
965
1270
|
});
|
|
966
1271
|
skillsCmd.command("install").description("Install skill bundles into an agent's skill directory. Non-destructive by default; --force to overwrite.").requiredOption("--agent <name>", "Agent runtime (claude, codex, copilot, pi)").option("--scope <scope>", "global or local (default: local)", "local").option("--project <path>", "Project root for local-scope paths (default: cwd)", process.cwd()).option("--only <names...>", "Restrict to specific skill names (default: all shipped)").option("--force", "Overwrite even if drift is detected against an existing manifest", false).action(async (opts) => {
|
|
967
1272
|
const agent = parseAgent(opts.agent);
|
|
@@ -1023,7 +1328,7 @@ ${result.conflicts.length} conflict${result.conflicts.length === 1 ? "" : "s"} d
|
|
|
1023
1328
|
});
|
|
1024
1329
|
program.command("decay-config").description("Print the active confidence-decay config (shipped defaults + any vault.schema.yml override).").requiredOption("--notes <path>", "Path to markdown notes directory").action(async (opts) => {
|
|
1025
1330
|
const { loadDecayConfig, DEFAULT_DECAY_CONFIG } = await import("../decay-ELJUKVQG.js");
|
|
1026
|
-
const cfg = loadDecayConfig(
|
|
1331
|
+
const cfg = loadDecayConfig(resolve4(opts.notes));
|
|
1027
1332
|
const source = JSON.stringify(cfg) === JSON.stringify(DEFAULT_DECAY_CONFIG) ? "defaults" : "vault.schema.yml (overridden)";
|
|
1028
1333
|
console.log(JSON.stringify({ source, config: cfg }, null, 2));
|
|
1029
1334
|
process.exit(0);
|
|
@@ -1031,18 +1336,18 @@ program.command("decay-config").description("Print the active confidence-decay c
|
|
|
1031
1336
|
program.command("decay-trace <notePath>").description("Show the decay calculation for one note: type, last_verified, age, half-life, evergreen, multiplier.").requiredOption("--notes <path>", "Path to markdown notes directory").action(async (notePath, opts) => {
|
|
1032
1337
|
const { loadDecayConfig, computeDecay, normalizeVerifiedDate } = await import("../decay-ELJUKVQG.js");
|
|
1033
1338
|
const matter = (await import("../matter-SNJ2WDL6.js")).default;
|
|
1034
|
-
const abs =
|
|
1339
|
+
const abs = resolve4(opts.notes, notePath);
|
|
1035
1340
|
if (!existsSync3(abs)) {
|
|
1036
1341
|
console.error(`note not found: ${abs}`);
|
|
1037
1342
|
process.exit(1);
|
|
1038
1343
|
}
|
|
1039
|
-
const { data: fm } = matter(
|
|
1344
|
+
const { data: fm } = matter(readFileSync2(abs, "utf8"));
|
|
1040
1345
|
const lastVerified = normalizeVerifiedDate(fm.last_verified);
|
|
1041
1346
|
const d = computeDecay({
|
|
1042
1347
|
type: typeof fm.type === "string" ? fm.type : void 0,
|
|
1043
1348
|
last_verified: lastVerified,
|
|
1044
1349
|
evergreen: fm.evergreen === true,
|
|
1045
|
-
config: loadDecayConfig(
|
|
1350
|
+
config: loadDecayConfig(resolve4(opts.notes))
|
|
1046
1351
|
});
|
|
1047
1352
|
console.log(
|
|
1048
1353
|
JSON.stringify(
|
|
@@ -1064,55 +1369,55 @@ program.command("decay-trace <notePath>").description("Show the decay calculatio
|
|
|
1064
1369
|
});
|
|
1065
1370
|
var lexiconCmd = program.command("lexicon").description("Manage the learned human\u2192artifact lexicon (aliases).");
|
|
1066
1371
|
lexiconCmd.command("list").description("List all aliases in the lexicon.").requiredOption("--notes <path>", "Path to markdown notes directory").action(async (opts) => {
|
|
1067
|
-
const { loadLexiconCache } = await import("../lexicon-
|
|
1068
|
-
console.log(JSON.stringify(await loadLexiconCache(
|
|
1372
|
+
const { loadLexiconCache } = await import("../lexicon-SEJSLBHO.js");
|
|
1373
|
+
console.log(JSON.stringify(await loadLexiconCache(resolve4(opts.notes)), null, 2));
|
|
1069
1374
|
process.exit(0);
|
|
1070
1375
|
});
|
|
1071
1376
|
lexiconCmd.command("compile").description("Rebuild the derived lexicon cache from the lexicon/ notes.").requiredOption("--notes <path>", "Path to markdown notes directory").action(async (opts) => {
|
|
1072
|
-
const { compileLexicon } = await import("../lexicon-
|
|
1073
|
-
const entries = await compileLexicon(
|
|
1377
|
+
const { compileLexicon } = await import("../lexicon-SEJSLBHO.js");
|
|
1378
|
+
const entries = await compileLexicon(resolve4(opts.notes));
|
|
1074
1379
|
console.log(`compiled ${entries.length} alias(es)`);
|
|
1075
1380
|
process.exit(0);
|
|
1076
1381
|
});
|
|
1077
1382
|
lexiconCmd.command("add <canonical> <phrases...>").description("Upsert an alias: a canonical target and the human phrases that refer to it.").requiredOption("--notes <path>", "Path to markdown notes directory").option("--authored", "Mark as human-authored (default: learned)").action(async (canonical, phrases, opts) => {
|
|
1078
|
-
const { addAlias } = await import("../lexicon-
|
|
1079
|
-
const r = await addAlias(
|
|
1383
|
+
const { addAlias } = await import("../lexicon-SEJSLBHO.js");
|
|
1384
|
+
const r = await addAlias(resolve4(opts.notes), { canonical, phrases, source: opts.authored ? "authored" : "learned" });
|
|
1080
1385
|
console.log(JSON.stringify(r, null, 2));
|
|
1081
1386
|
process.exit(0);
|
|
1082
1387
|
});
|
|
1083
1388
|
var dossierCmd = program.command("dossier").description("Manage entity dossiers \u2014 per-component living notes (purpose / failure modes / knobs / incident log / current state).");
|
|
1084
1389
|
dossierCmd.command("init <entity>").description("Scaffold a dossier for an entity (no-op if one already exists). Optionally seed aliases and provenance.").requiredOption("--notes <path>", "Path to markdown notes directory").option("--alias <phrase...>", "Human phrase(s) for this entity \u2014 fed to the lexicon compiler").option("--seeded-from <ref>", "Provenance of the scaffold (e.g. a babel-fish project-map entry)").action(async (entity, opts) => {
|
|
1085
|
-
const { initDossier } = await import("../dossier-
|
|
1086
|
-
const r = await initDossier(
|
|
1390
|
+
const { initDossier } = await import("../dossier-RXBLQOXC.js");
|
|
1391
|
+
const r = await initDossier(resolve4(opts.notes), entity, { aliases: opts.alias, seeded_from: opts.seededFrom });
|
|
1087
1392
|
console.log(JSON.stringify(r, null, 2));
|
|
1088
1393
|
process.exit(0);
|
|
1089
1394
|
});
|
|
1090
1395
|
dossierCmd.command("list").description("List all dossiers with their entity, aliases, and current state.").requiredOption("--notes <path>", "Path to markdown notes directory").action(async (opts) => {
|
|
1091
|
-
const { listDossiers } = await import("../dossier-
|
|
1092
|
-
console.log(JSON.stringify(await listDossiers(
|
|
1396
|
+
const { listDossiers } = await import("../dossier-RXBLQOXC.js");
|
|
1397
|
+
console.log(JSON.stringify(await listDossiers(resolve4(opts.notes)), null, 2));
|
|
1093
1398
|
process.exit(0);
|
|
1094
1399
|
});
|
|
1095
1400
|
var profileCmd = program.command("profile").description("Manage the speaker profile \u2014 how THIS human communicates (severity calibration, chronic omissions, verbosity, shorthand).");
|
|
1096
1401
|
profileCmd.command("init").description("Scaffold profile/speaker.md with the fixed sections (no-op if it exists).").requiredOption("--notes <path>", "Path to markdown notes directory").action(async (opts) => {
|
|
1097
|
-
const { initSpeakerProfile } = await import("../profile-
|
|
1098
|
-
const r = await initSpeakerProfile(
|
|
1402
|
+
const { initSpeakerProfile } = await import("../profile-SNULTJ27.js");
|
|
1403
|
+
const r = await initSpeakerProfile(resolve4(opts.notes));
|
|
1099
1404
|
console.log(JSON.stringify(r, null, 2));
|
|
1100
1405
|
process.exit(0);
|
|
1101
1406
|
});
|
|
1102
1407
|
profileCmd.command("show").description("Print the speaker profile's injection head (the capped, placeholder-stripped view).").requiredOption("--notes <path>", "Path to markdown notes directory").action(async (opts) => {
|
|
1103
|
-
const { readSpeakerProfile } = await import("../profile-
|
|
1104
|
-
const p = await readSpeakerProfile(
|
|
1408
|
+
const { readSpeakerProfile } = await import("../profile-SNULTJ27.js");
|
|
1409
|
+
const p = await readSpeakerProfile(resolve4(opts.notes));
|
|
1105
1410
|
console.log(p ? p.head || "(profile is empty \u2014 only placeholders)" : "(no speaker profile \u2014 run `profile init`)");
|
|
1106
1411
|
process.exit(0);
|
|
1107
1412
|
});
|
|
1108
1413
|
program.command("selection-stats").description("Summarize the local selection log: searches, selections, most-cited notes, and retrieved-but-never-cited notes.").requiredOption("--notes <path>", "Path to markdown notes directory").option("--json", "Emit raw JSON instead of a human summary").action(async (opts) => {
|
|
1109
1414
|
const { computeSelectionStats, selectionLogPath } = await import("../telemetry-YAG3VA4Y.js");
|
|
1110
|
-
const stats = await computeSelectionStats(
|
|
1415
|
+
const stats = await computeSelectionStats(resolve4(opts.notes));
|
|
1111
1416
|
if (opts.json) {
|
|
1112
1417
|
console.log(JSON.stringify(stats, null, 2));
|
|
1113
1418
|
process.exit(0);
|
|
1114
1419
|
}
|
|
1115
|
-
console.log(`Selection log: ${selectionLogPath(
|
|
1420
|
+
console.log(`Selection log: ${selectionLogPath(resolve4(opts.notes))}`);
|
|
1116
1421
|
console.log(` searches: ${stats.searches}`);
|
|
1117
1422
|
console.log(` selections: ${stats.selections}`);
|
|
1118
1423
|
const topCited = Object.entries(stats.citedPaths).sort((a, b) => b[1] - a[1]).slice(0, 10);
|
|
@@ -1125,5 +1430,29 @@ program.command("selection-stats").description("Summarize the local selection lo
|
|
|
1125
1430
|
}
|
|
1126
1431
|
process.exit(0);
|
|
1127
1432
|
});
|
|
1433
|
+
skillsCmd.command("lint").description("Lint the skill loop's learned skills this repo loads (v1.7): format, description, evidence, sections, cites, applies_to, secrets, name clashes.").option("--root <dir>", "Repo root (default: cwd)").option("--json", "JSON output").action(async (opts) => {
|
|
1434
|
+
const { lintSkills } = await import("../skill-schema-RVB3EHEF.js");
|
|
1435
|
+
const root = resolve4(opts.root ?? process.cwd());
|
|
1436
|
+
const r = lintSkills(root);
|
|
1437
|
+
if (opts.json) console.log(JSON.stringify(r, null, 2));
|
|
1438
|
+
else {
|
|
1439
|
+
for (const f of r.findings) console.log(`${f.severity === "error" ? "\u2717" : "!"} ${relative(root, f.path)}: ${f.rule}: ${f.message}`);
|
|
1440
|
+
const errors = r.findings.filter((f) => f.severity === "error").length;
|
|
1441
|
+
console.log(`${r.files} learned skill(s), ${errors} error(s), ${r.findings.length - errors} warning(s)`);
|
|
1442
|
+
}
|
|
1443
|
+
process.exit(r.findings.some((f) => f.severity === "error") ? 1 : 0);
|
|
1444
|
+
});
|
|
1445
|
+
registerFeatureCommands(program, {
|
|
1446
|
+
skill_loop: skillLoopStatus,
|
|
1447
|
+
jev: async () => {
|
|
1448
|
+
const { jevSpendToday } = await import("../core/jev.js");
|
|
1449
|
+
const { jevLoopDir } = await import("../core/features.js");
|
|
1450
|
+
const spend = jevSpendToday(jevLoopDir());
|
|
1451
|
+
const total = Object.values(spend).reduce((a, b) => a + b, 0);
|
|
1452
|
+
return [`Jev today (semantic-memory callers): ${total.toLocaleString()} tokens${total ? ` \u2014 ${Object.entries(spend).map(([c, n]) => `${c} ${n.toLocaleString()}`).join(", ")}` : ""}`];
|
|
1453
|
+
}
|
|
1454
|
+
});
|
|
1455
|
+
registerSkillLoopCommands(program);
|
|
1456
|
+
registerSkillEvalCommands(program);
|
|
1128
1457
|
program.parse();
|
|
1129
1458
|
//# sourceMappingURL=index.js.map
|