@theglitchking/semantic-memory 1.0.1 → 1.1.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/.claude-plugin/marketplace.json +4 -4
- package/CHANGELOG.md +94 -0
- package/commands/contract.md +14 -0
- package/commands/healthcheck.md +21 -3
- package/commands/mode.md +3 -3
- package/commands/normalize-config.md +1 -1
- package/commands/policy.md +4 -4
- package/commands/relink.md +1 -1
- package/commands/status.md +1 -1
- package/commands/update.md +2 -2
- package/dist/chunk-CCSX3BHF.js +273 -0
- package/dist/chunk-CCSX3BHF.js.map +1 -0
- package/dist/{chunk-CXTRTCIK.js → chunk-XBAN7GRT.js} +380 -6
- package/dist/chunk-XBAN7GRT.js.map +1 -0
- package/dist/cli/index.js +324 -34
- package/dist/cli/index.js.map +1 -1
- package/dist/core/index.d.ts +276 -3
- package/dist/core/index.js +22 -2
- package/dist/healthcheck-DCFGPI7M.js +14 -0
- package/dist/healthcheck-DCFGPI7M.js.map +1 -0
- package/dist/mcp/server.d.ts +1 -0
- package/dist/mcp/server.js +1008 -568
- package/dist/mcp/server.js.map +1 -1
- package/hooks/reconcile.js +39 -10
- package/hooks/session-start.js +7 -7
- package/hooks/vault-context.js +141 -6
- package/package.json +1 -1
- package/scripts/link-skills.js +5 -5
- package/dist/chunk-CXTRTCIK.js.map +0 -1
package/dist/cli/index.js
CHANGED
|
@@ -1,19 +1,196 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import "../chunk-PZ5AY32C.js";
|
|
2
3
|
|
|
3
4
|
// src/cli/index.ts
|
|
4
5
|
import { program } from "commander";
|
|
5
|
-
import { resolve, join, dirname, relative } from "path";
|
|
6
|
-
import { existsSync, readFileSync, writeFileSync, rmSync } from "fs";
|
|
6
|
+
import { resolve as resolve2, join as join2, dirname, relative } from "path";
|
|
7
|
+
import { existsSync as existsSync2, readFileSync, writeFileSync, rmSync } from "fs";
|
|
7
8
|
import { createRequire } from "module";
|
|
8
9
|
import { spawnSync } from "child_process";
|
|
9
10
|
import { registerUpdateCommands } from "@theglitchking/claude-plugin-runtime";
|
|
11
|
+
|
|
12
|
+
// src/cli/skills.ts
|
|
13
|
+
import { mkdir, readFile, writeFile, copyFile, readdir, rm } from "fs/promises";
|
|
14
|
+
import { existsSync } from "fs";
|
|
15
|
+
import { join, resolve } from "path";
|
|
16
|
+
import { homedir } from "os";
|
|
17
|
+
import { createHash } from "crypto";
|
|
18
|
+
var MANIFEST_FILE = ".semantic-memory-skill-manifest.json";
|
|
19
|
+
function resolveTarget(agent, scope, projectRoot) {
|
|
20
|
+
const root = resolve(projectRoot);
|
|
21
|
+
const home = homedir();
|
|
22
|
+
const map = {
|
|
23
|
+
claude: { global: join(home, ".claude", "skills"), local: join(root, ".claude", "skills") },
|
|
24
|
+
codex: { global: join(home, ".codex", "skills"), local: join(root, ".codex", "skills") },
|
|
25
|
+
copilot: { global: join(home, ".copilot", "skills"), local: join(root, ".github", "skills") },
|
|
26
|
+
pi: { global: join(home, ".pi", "agent", "skills"), local: join(root, ".pi", "skills") }
|
|
27
|
+
};
|
|
28
|
+
return { agent, scope, path: map[agent][scope] };
|
|
29
|
+
}
|
|
30
|
+
async function listShippedSkills(sourceRoot) {
|
|
31
|
+
const skillsDir = join(sourceRoot, "skills");
|
|
32
|
+
if (!existsSync(skillsDir)) return [];
|
|
33
|
+
const entries = await readdir(skillsDir, { withFileTypes: true });
|
|
34
|
+
const out = [];
|
|
35
|
+
for (const e of entries) {
|
|
36
|
+
if (!e.isDirectory()) continue;
|
|
37
|
+
const dir = join(skillsDir, e.name);
|
|
38
|
+
const skillFile = join(dir, "SKILL.md");
|
|
39
|
+
if (!existsSync(skillFile)) continue;
|
|
40
|
+
out.push({ name: e.name, dir });
|
|
41
|
+
}
|
|
42
|
+
return out;
|
|
43
|
+
}
|
|
44
|
+
async function sha256OfFile(path) {
|
|
45
|
+
const buf = await readFile(path);
|
|
46
|
+
return createHash("sha256").update(buf).digest("hex");
|
|
47
|
+
}
|
|
48
|
+
async function copyBundle(srcDir, destDir) {
|
|
49
|
+
await mkdir(destDir, { recursive: true });
|
|
50
|
+
const entries = await readdir(srcDir, { withFileTypes: true });
|
|
51
|
+
const written = [];
|
|
52
|
+
for (const e of entries) {
|
|
53
|
+
const src = join(srcDir, e.name);
|
|
54
|
+
const dest = join(destDir, e.name);
|
|
55
|
+
if (e.isDirectory()) {
|
|
56
|
+
const childWritten = await copyBundle(src, dest);
|
|
57
|
+
written.push(...childWritten);
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
if (!e.isFile()) continue;
|
|
61
|
+
await copyFile(src, dest);
|
|
62
|
+
written.push(dest);
|
|
63
|
+
}
|
|
64
|
+
return written;
|
|
65
|
+
}
|
|
66
|
+
async function installSkills(opts) {
|
|
67
|
+
const target = resolveTarget(opts.agent, opts.scope, opts.projectRoot);
|
|
68
|
+
const shipped = await listShippedSkills(opts.sourceRoot);
|
|
69
|
+
const filter = opts.only && opts.only.length > 0 ? new Set(opts.only) : null;
|
|
70
|
+
const toInstall = filter ? shipped.filter((s) => filter.has(s.name)) : shipped;
|
|
71
|
+
if (toInstall.length === 0) {
|
|
72
|
+
return {
|
|
73
|
+
agent: opts.agent,
|
|
74
|
+
scope: opts.scope,
|
|
75
|
+
target_path: target.path,
|
|
76
|
+
installed: [],
|
|
77
|
+
skipped: [],
|
|
78
|
+
reason: filter ? `No matching skills found (only=${[...filter].join(",")}).` : "No shipped skills found at sourceRoot/skills/."
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
const manifestPath = join(target.path, MANIFEST_FILE);
|
|
82
|
+
const existing = await readManifest(manifestPath);
|
|
83
|
+
if (existing && !opts.force) {
|
|
84
|
+
const drift = await detectDrift(existing, toInstall);
|
|
85
|
+
if (drift.length > 0) {
|
|
86
|
+
return {
|
|
87
|
+
agent: opts.agent,
|
|
88
|
+
scope: opts.scope,
|
|
89
|
+
target_path: target.path,
|
|
90
|
+
installed: [],
|
|
91
|
+
skipped: toInstall.map((s) => s.name),
|
|
92
|
+
reason: `Drift detected on ${drift.length} skill(s): ${drift.join(", ")}. Pass --force to overwrite.`,
|
|
93
|
+
manifest_path: manifestPath
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
await mkdir(target.path, { recursive: true });
|
|
98
|
+
const installed = [];
|
|
99
|
+
const skillEntries = [];
|
|
100
|
+
for (const skill of toInstall) {
|
|
101
|
+
const destDir = join(target.path, skill.name);
|
|
102
|
+
await copyBundle(skill.dir, destDir);
|
|
103
|
+
const skillFile = join(skill.dir, "SKILL.md");
|
|
104
|
+
skillEntries.push({ name: skill.name, sha256: await sha256OfFile(skillFile) });
|
|
105
|
+
installed.push(skill.name);
|
|
106
|
+
}
|
|
107
|
+
const manifest = {
|
|
108
|
+
package: "@theglitchking/semantic-memory",
|
|
109
|
+
version: opts.version,
|
|
110
|
+
agent: opts.agent,
|
|
111
|
+
scope: opts.scope,
|
|
112
|
+
installed_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
113
|
+
skills: skillEntries
|
|
114
|
+
};
|
|
115
|
+
await writeFile(manifestPath, JSON.stringify(manifest, null, 2), "utf-8");
|
|
116
|
+
return {
|
|
117
|
+
agent: opts.agent,
|
|
118
|
+
scope: opts.scope,
|
|
119
|
+
target_path: target.path,
|
|
120
|
+
installed,
|
|
121
|
+
skipped: [],
|
|
122
|
+
manifest_path: manifestPath
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
async function uninstallSkills(opts) {
|
|
126
|
+
const target = resolveTarget(opts.agent, opts.scope, opts.projectRoot);
|
|
127
|
+
const manifestPath = join(target.path, MANIFEST_FILE);
|
|
128
|
+
const manifest = await readManifest(manifestPath);
|
|
129
|
+
if (!manifest) {
|
|
130
|
+
return {
|
|
131
|
+
agent: opts.agent,
|
|
132
|
+
scope: opts.scope,
|
|
133
|
+
target_path: target.path,
|
|
134
|
+
removed: [],
|
|
135
|
+
manifest_present: false,
|
|
136
|
+
reason: "No semantic-memory manifest found \u2014 nothing to uninstall."
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
const removed = [];
|
|
140
|
+
for (const skill of manifest.skills) {
|
|
141
|
+
const dir = join(target.path, skill.name);
|
|
142
|
+
if (existsSync(dir)) {
|
|
143
|
+
await rm(dir, { recursive: true, force: true });
|
|
144
|
+
removed.push(skill.name);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
await rm(manifestPath, { force: true });
|
|
148
|
+
return {
|
|
149
|
+
agent: opts.agent,
|
|
150
|
+
scope: opts.scope,
|
|
151
|
+
target_path: target.path,
|
|
152
|
+
removed,
|
|
153
|
+
manifest_present: true
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
async function readManifest(path) {
|
|
157
|
+
if (!existsSync(path)) return null;
|
|
158
|
+
try {
|
|
159
|
+
const raw = await readFile(path, "utf-8");
|
|
160
|
+
const parsed = JSON.parse(raw);
|
|
161
|
+
if (typeof parsed?.package !== "string") return null;
|
|
162
|
+
return parsed;
|
|
163
|
+
} catch {
|
|
164
|
+
return null;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
async function detectDrift(existing, toInstall) {
|
|
168
|
+
const driftNames = [];
|
|
169
|
+
const existingByName = new Map(existing.skills.map((s) => [s.name, s.sha256]));
|
|
170
|
+
for (const skill of toInstall) {
|
|
171
|
+
const sourceSha = await sha256OfFile(join(skill.dir, "SKILL.md"));
|
|
172
|
+
const existingSha = existingByName.get(skill.name);
|
|
173
|
+
if (existingSha !== void 0 && existingSha !== sourceSha) {
|
|
174
|
+
driftNames.push(skill.name);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
return driftNames;
|
|
178
|
+
}
|
|
179
|
+
var SKILL_BUNDLER_AGENTS = ["claude", "codex", "copilot", "pi"];
|
|
180
|
+
|
|
181
|
+
// src/cli/index.ts
|
|
10
182
|
var require_ = createRequire(import.meta.url);
|
|
11
183
|
var { version } = require_("../../package.json");
|
|
12
|
-
var PKG_NAME = "@theglitchking/semantic-
|
|
184
|
+
var PKG_NAME = "@theglitchking/semantic-memory";
|
|
185
|
+
var LEGACY_PKG_NAME = "@theglitchking/semantic-sidekick";
|
|
13
186
|
function runRelink(cwd) {
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
|
|
187
|
+
const candidates = [
|
|
188
|
+
join2(cwd, "node_modules", "@theglitchking", "semantic-memory", "scripts", "link-skills.js"),
|
|
189
|
+
join2(cwd, "node_modules", "@theglitchking", "semantic-sidekick", "scripts", "link-skills.js"),
|
|
190
|
+
resolve2(process.cwd(), "scripts", "link-skills.js")
|
|
191
|
+
];
|
|
192
|
+
const script = candidates.find((p) => existsSync2(p));
|
|
193
|
+
if (!script) {
|
|
17
194
|
console.error("link-skills.js not found \u2014 is the package installed?");
|
|
18
195
|
return;
|
|
19
196
|
}
|
|
@@ -24,8 +201,14 @@ function runRelink(cwd) {
|
|
|
24
201
|
});
|
|
25
202
|
}
|
|
26
203
|
function findLocalBin(cwd) {
|
|
27
|
-
const
|
|
28
|
-
|
|
204
|
+
const candidates = [
|
|
205
|
+
join2(cwd, "node_modules", "@theglitchking", "semantic-memory", "bin", "semantic-memory"),
|
|
206
|
+
join2(cwd, "node_modules", "@theglitchking", "semantic-sidekick", "bin", "semantic-sidekick")
|
|
207
|
+
];
|
|
208
|
+
for (const p of candidates) {
|
|
209
|
+
if (existsSync2(p)) return p;
|
|
210
|
+
}
|
|
211
|
+
return null;
|
|
29
212
|
}
|
|
30
213
|
function localBinArg(cwd) {
|
|
31
214
|
const abs = findLocalBin(cwd);
|
|
@@ -45,7 +228,9 @@ function isNpxForm(entry) {
|
|
|
45
228
|
if (!entry || typeof entry !== "object") return false;
|
|
46
229
|
const cmd = entry.command;
|
|
47
230
|
const args = Array.isArray(entry.args) ? entry.args : [];
|
|
48
|
-
return cmd === "npx" && args.some(
|
|
231
|
+
return cmd === "npx" && args.some(
|
|
232
|
+
(a) => typeof a === "string" && (a.includes(PKG_NAME) || a.includes(LEGACY_PKG_NAME))
|
|
233
|
+
);
|
|
49
234
|
}
|
|
50
235
|
function extractNotesPath(entry) {
|
|
51
236
|
const args = Array.isArray(entry?.args) ? entry.args : [];
|
|
@@ -218,7 +403,7 @@ var TOOL_CATEGORIES = {
|
|
|
218
403
|
function printToolList() {
|
|
219
404
|
console.log("\nSemantic Pages \u2014 21 MCP Tools\n");
|
|
220
405
|
console.log("Usage: These tools are available via MCP when the server is running.");
|
|
221
|
-
console.log(" Run `semantic-
|
|
406
|
+
console.log(" Run `semantic-memory tools <name>` for details on a specific tool.\n");
|
|
222
407
|
for (const [category, tools] of Object.entries(TOOL_CATEGORIES)) {
|
|
223
408
|
console.log(` ${category}:`);
|
|
224
409
|
for (const name of tools) {
|
|
@@ -227,13 +412,13 @@ function printToolList() {
|
|
|
227
412
|
}
|
|
228
413
|
console.log();
|
|
229
414
|
}
|
|
230
|
-
console.log("Run `semantic-
|
|
415
|
+
console.log("Run `semantic-memory tools <tool-name>` for arguments and examples.");
|
|
231
416
|
}
|
|
232
417
|
function printToolDetail(name) {
|
|
233
418
|
const tool = TOOL_HELP[name];
|
|
234
419
|
if (!tool) {
|
|
235
420
|
console.error(`Unknown tool: ${name}`);
|
|
236
|
-
console.error(`Run \`semantic-
|
|
421
|
+
console.error(`Run \`semantic-memory tools\` to see all available tools.`);
|
|
237
422
|
process.exit(1);
|
|
238
423
|
}
|
|
239
424
|
console.log(`
|
|
@@ -271,7 +456,7 @@ program.command("search <query>").description("Run a vault search and print top
|
|
|
271
456
|
});
|
|
272
457
|
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) => {
|
|
273
458
|
const { lintVault, formatLintReport } = await import("../lint-GMXQF4RW.js");
|
|
274
|
-
const report = await lintVault(
|
|
459
|
+
const report = await lintVault(resolve2(opts.notes));
|
|
275
460
|
if (opts.json) {
|
|
276
461
|
const out = opts.rule ? report.byRule[opts.rule] : report;
|
|
277
462
|
console.log(JSON.stringify(out, null, 2));
|
|
@@ -294,7 +479,7 @@ program.command("log-event").description("Append a structured event to the vault
|
|
|
294
479
|
}
|
|
295
480
|
}
|
|
296
481
|
try {
|
|
297
|
-
const entry = await logEvent(
|
|
482
|
+
const entry = await logEvent(resolve2(opts.notes), {
|
|
298
483
|
kind: opts.kind,
|
|
299
484
|
summary: opts.summary,
|
|
300
485
|
payload
|
|
@@ -308,7 +493,7 @@ program.command("log-event").description("Append a structured event to the vault
|
|
|
308
493
|
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) => {
|
|
309
494
|
const { logQuery } = await import("../log-LQXIJXIA.js");
|
|
310
495
|
try {
|
|
311
|
-
const entries = await logQuery(
|
|
496
|
+
const entries = await logQuery(resolve2(opts.notes), {
|
|
312
497
|
kind: opts.kind,
|
|
313
498
|
after: opts.after,
|
|
314
499
|
before: opts.before,
|
|
@@ -322,7 +507,7 @@ program.command("log-query").description("Read structured log entries from log.m
|
|
|
322
507
|
});
|
|
323
508
|
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) => {
|
|
324
509
|
const { installDefaultSchema } = await import("../schema-F3RTY6SQ.js");
|
|
325
|
-
const r = await installDefaultSchema(
|
|
510
|
+
const r = await installDefaultSchema(resolve2(opts.notes), opts.force);
|
|
326
511
|
console.log(r.written ? `Installed default schema: ${r.path}` : `Schema already exists: ${r.path} (use --force to overwrite)`);
|
|
327
512
|
process.exit(0);
|
|
328
513
|
});
|
|
@@ -335,8 +520,8 @@ program.command("tools [name]").description("List all MCP tools, or show details
|
|
|
335
520
|
process.exit(0);
|
|
336
521
|
});
|
|
337
522
|
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) \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) => {
|
|
338
|
-
const notesPath =
|
|
339
|
-
if (!
|
|
523
|
+
const notesPath = resolve2(opts.notes);
|
|
524
|
+
if (!existsSync2(notesPath)) {
|
|
340
525
|
console.error(`Error: notes directory not found: ${notesPath}`);
|
|
341
526
|
process.exit(1);
|
|
342
527
|
}
|
|
@@ -380,16 +565,16 @@ program.command("serve", { isDefault: true }).description("Start the MCP server
|
|
|
380
565
|
});
|
|
381
566
|
registerUpdateCommands(program, {
|
|
382
567
|
packageName: PKG_NAME,
|
|
383
|
-
pluginName: "semantic-
|
|
384
|
-
configFile: "semantic-
|
|
568
|
+
pluginName: "semantic-memory",
|
|
569
|
+
configFile: "semantic-memory.json",
|
|
385
570
|
onAfterUpdate: (cwd) => runRelink(cwd)
|
|
386
571
|
});
|
|
387
572
|
program.command("normalize-config").description(
|
|
388
573
|
"Rewrite fragile `npx @latest` entries in .mcp.json to the stable node-against-node_modules form (with backup and validation)"
|
|
389
574
|
).option("--dry-run", "print the proposed changes but don't write").action((opts) => {
|
|
390
575
|
const cwd = process.cwd();
|
|
391
|
-
const mcpPath =
|
|
392
|
-
if (!
|
|
576
|
+
const mcpPath = join2(cwd, ".mcp.json");
|
|
577
|
+
if (!existsSync2(mcpPath)) {
|
|
393
578
|
console.log("no .mcp.json in current directory \u2014 nothing to do");
|
|
394
579
|
process.exit(0);
|
|
395
580
|
}
|
|
@@ -401,8 +586,8 @@ program.command("normalize-config").description(
|
|
|
401
586
|
const bin = localBinArg(cwd);
|
|
402
587
|
if (!bin) {
|
|
403
588
|
console.error(
|
|
404
|
-
`no local install found at ./node_modules/@theglitchking/semantic-
|
|
405
|
-
run 'npm install --save @theglitchking/semantic-
|
|
589
|
+
`no local install found at ./node_modules/@theglitchking/semantic-memory/bin/semantic-memory (or the legacy semantic-sidekick path).
|
|
590
|
+
run 'npm install --save @theglitchking/semantic-memory' first, then re-run this command.`
|
|
406
591
|
);
|
|
407
592
|
process.exit(1);
|
|
408
593
|
}
|
|
@@ -429,7 +614,7 @@ run 'npm install --save @theglitchking/semantic-sidekick' first, then re-run thi
|
|
|
429
614
|
console.log("--- (dry-run; no changes written)");
|
|
430
615
|
process.exit(0);
|
|
431
616
|
}
|
|
432
|
-
const bakPath =
|
|
617
|
+
const bakPath = join2(dirname(mcpPath), ".mcp.json.bak");
|
|
433
618
|
try {
|
|
434
619
|
writeFileSync(bakPath, readFileSync(mcpPath, "utf8"));
|
|
435
620
|
console.log(`backup written: ${bakPath}`);
|
|
@@ -450,17 +635,17 @@ ${verify.stderr?.toString() || ""}`);
|
|
|
450
635
|
process.exit(0);
|
|
451
636
|
});
|
|
452
637
|
program.command("healthcheck").description(
|
|
453
|
-
"Verify the local install starts cleanly
|
|
454
|
-
).action(() => {
|
|
638
|
+
"Verify the local install starts cleanly + run drift detection. Self-heals common npx-cache corruption (ERR_MODULE_NOT_FOUND). With --fast, skips the slow tier (full vault lint)."
|
|
639
|
+
).option("--fast", "Run only the fast-tier drift checks (file-system probes). Skips the slow tier (full vault lint).", false).option("--json", "Emit structured JSON output instead of human-readable text.", false).action(async (opts) => {
|
|
455
640
|
const cwd = process.cwd();
|
|
456
641
|
const bin = findLocalBin(cwd);
|
|
457
642
|
if (!bin) {
|
|
458
|
-
console.error(`no local install at ./node_modules/@theglitchking/semantic-
|
|
459
|
-
console.error(`run 'npm install --save @theglitchking/semantic-
|
|
643
|
+
console.error(`no local install at ./node_modules/@theglitchking/semantic-memory/bin/semantic-memory (or legacy semantic-sidekick path)`);
|
|
644
|
+
console.error(`run 'npm install --save @theglitchking/semantic-memory' to install.`);
|
|
460
645
|
process.exit(1);
|
|
461
646
|
}
|
|
462
|
-
const mcpPath =
|
|
463
|
-
if (
|
|
647
|
+
const mcpPath = join2(cwd, ".mcp.json");
|
|
648
|
+
if (existsSync2(mcpPath)) {
|
|
464
649
|
const data = readJsonSafe(mcpPath);
|
|
465
650
|
const entries = data?.mcpServers && typeof data.mcpServers === "object" ? Object.entries(data.mcpServers) : [];
|
|
466
651
|
const fragile = entries.filter(([, e]) => isNpxForm(e)).map(([k]) => k);
|
|
@@ -469,7 +654,7 @@ program.command("healthcheck").description(
|
|
|
469
654
|
`\u26A0\uFE0F .mcp.json uses the fragile npx-@latest form for: ${fragile.join(", ")}`
|
|
470
655
|
);
|
|
471
656
|
console.warn(` Rewrite to the stable form:`);
|
|
472
|
-
console.warn(` npx --no @theglitchking/semantic-
|
|
657
|
+
console.warn(` npx --no @theglitchking/semantic-memory normalize-config`);
|
|
473
658
|
}
|
|
474
659
|
}
|
|
475
660
|
const r = spawnSync("node", [bin, "--version"], { cwd, stdio: "pipe", timeout: 15e3 });
|
|
@@ -498,8 +683,113 @@ ${r2.stderr?.toString() || ""}`);
|
|
|
498
683
|
console.error(`ERR_MODULE_NOT_FOUND but could not locate offending cache path in the error.`);
|
|
499
684
|
}
|
|
500
685
|
}
|
|
501
|
-
console.error(stderr
|
|
502
|
-
|
|
686
|
+
if (stderr) console.error(stderr);
|
|
687
|
+
if (r.status !== 0) {
|
|
688
|
+
console.error(`local install failed (exit ${r.status})`);
|
|
689
|
+
process.exit(1);
|
|
690
|
+
}
|
|
691
|
+
});
|
|
692
|
+
var driftHealthcheckActionPostprocess = async (opts) => {
|
|
693
|
+
try {
|
|
694
|
+
const { runHealthcheck, formatDriftBanner } = await import("../healthcheck-DCFGPI7M.js");
|
|
695
|
+
const projectRoot = process.cwd();
|
|
696
|
+
const vaultCandidate = join2(projectRoot, ".claude", ".vault");
|
|
697
|
+
const vaultPath = existsSync2(vaultCandidate) ? vaultCandidate : void 0;
|
|
698
|
+
const result = await runHealthcheck({
|
|
699
|
+
projectRoot,
|
|
700
|
+
vaultPath,
|
|
701
|
+
tier: opts.fast ? "fast" : "all",
|
|
702
|
+
force: true
|
|
703
|
+
});
|
|
704
|
+
if (opts.json) {
|
|
705
|
+
console.log(JSON.stringify(result, null, 2));
|
|
706
|
+
return;
|
|
707
|
+
}
|
|
708
|
+
const banner = formatDriftBanner(result);
|
|
709
|
+
if (banner) {
|
|
710
|
+
console.log("");
|
|
711
|
+
console.log(banner);
|
|
712
|
+
} else {
|
|
713
|
+
console.log("");
|
|
714
|
+
console.log(`\u2713 no drift detected (${result.findings.length} checks ran in ${result.duration_ms}ms)`);
|
|
715
|
+
}
|
|
716
|
+
} catch (err) {
|
|
717
|
+
console.error(`drift check threw: ${err?.message ?? err}`);
|
|
718
|
+
}
|
|
719
|
+
};
|
|
720
|
+
var healthcheckCmd = program.commands.find((c) => c.name() === "healthcheck");
|
|
721
|
+
if (healthcheckCmd) {
|
|
722
|
+
healthcheckCmd.hook("postAction", async (thisCmd) => {
|
|
723
|
+
const opts = thisCmd.opts();
|
|
724
|
+
await driftHealthcheckActionPostprocess({ fast: !!opts.fast, json: !!opts.json });
|
|
725
|
+
});
|
|
726
|
+
}
|
|
727
|
+
function parseAgent(input) {
|
|
728
|
+
const a = input.toLowerCase();
|
|
729
|
+
if (!SKILL_BUNDLER_AGENTS.includes(a)) {
|
|
730
|
+
console.error(`Unknown agent: ${input}. Supported: ${SKILL_BUNDLER_AGENTS.join(", ")}.`);
|
|
731
|
+
process.exit(2);
|
|
732
|
+
}
|
|
733
|
+
return a;
|
|
734
|
+
}
|
|
735
|
+
function parseScope(input) {
|
|
736
|
+
if (input !== "global" && input !== "local") {
|
|
737
|
+
console.error(`Unknown scope: ${input}. Use 'global' or 'local'.`);
|
|
738
|
+
process.exit(2);
|
|
739
|
+
}
|
|
740
|
+
return input;
|
|
741
|
+
}
|
|
742
|
+
function findSourceRoot() {
|
|
743
|
+
for (const pkg of ["semantic-memory", "semantic-sidekick"]) {
|
|
744
|
+
const installed = join2(process.cwd(), "node_modules", "@theglitchking", pkg);
|
|
745
|
+
if (existsSync2(installed)) return installed;
|
|
746
|
+
}
|
|
747
|
+
const dev = resolve2(new URL("../../", import.meta.url).pathname);
|
|
748
|
+
return dev;
|
|
749
|
+
}
|
|
750
|
+
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.");
|
|
751
|
+
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) => {
|
|
752
|
+
const scope = parseScope(opts.scope);
|
|
753
|
+
const agents = (opts.agent && opts.agent.length > 0 ? opts.agent : SKILL_BUNDLER_AGENTS).map(parseAgent);
|
|
754
|
+
const out = agents.map((a) => resolveTarget(a, scope, opts.project));
|
|
755
|
+
console.log(JSON.stringify(out, null, 2));
|
|
756
|
+
});
|
|
757
|
+
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) => {
|
|
758
|
+
const agent = parseAgent(opts.agent);
|
|
759
|
+
const scope = parseScope(opts.scope);
|
|
760
|
+
if (agent === "claude") {
|
|
761
|
+
console.error(
|
|
762
|
+
"Claude skill installs are handled by the npm postinstall (claude-plugin-runtime). Run `npm install @theglitchking/semantic-memory` instead \u2014 that flow symlinks skills into ~/.claude/skills/ or .claude/skills/ automatically. The skills CLI is intended for non-Claude agents (codex, copilot, pi) which do not have that postinstall integration."
|
|
763
|
+
);
|
|
764
|
+
process.exit(2);
|
|
765
|
+
}
|
|
766
|
+
const sourceRoot = findSourceRoot();
|
|
767
|
+
const result = await installSkills({
|
|
768
|
+
agent,
|
|
769
|
+
scope,
|
|
770
|
+
sourceRoot,
|
|
771
|
+
projectRoot: opts.project,
|
|
772
|
+
version,
|
|
773
|
+
only: opts.only,
|
|
774
|
+
force: opts.force === true
|
|
775
|
+
});
|
|
776
|
+
console.log(JSON.stringify(result, null, 2));
|
|
777
|
+
if (result.installed.length === 0 && result.reason) process.exit(1);
|
|
778
|
+
});
|
|
779
|
+
skillsCmd.command("uninstall").description("Remove skill bundles tracked by the semantic-memory manifest. User-written skills outside the manifest are left untouched.").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()).action(async (opts) => {
|
|
780
|
+
const agent = parseAgent(opts.agent);
|
|
781
|
+
const scope = parseScope(opts.scope);
|
|
782
|
+
if (agent === "claude") {
|
|
783
|
+
console.error("Claude skill uninstalls go through the npm uninstall flow. The skills CLI is for non-Claude agents.");
|
|
784
|
+
process.exit(2);
|
|
785
|
+
}
|
|
786
|
+
const result = await uninstallSkills({ agent, scope, projectRoot: opts.project });
|
|
787
|
+
console.log(JSON.stringify(result, null, 2));
|
|
788
|
+
});
|
|
789
|
+
skillsCmd.command("list").description("List the skill bundles shipped with this plugin version.").action(async () => {
|
|
790
|
+
const sourceRoot = findSourceRoot();
|
|
791
|
+
const shipped = await listShippedSkills(sourceRoot);
|
|
792
|
+
console.log(JSON.stringify(shipped.map((s) => s.name), null, 2));
|
|
503
793
|
});
|
|
504
794
|
program.parse();
|
|
505
795
|
//# sourceMappingURL=index.js.map
|