skillwiki 0.9.20 → 0.9.21
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/cli.js +180 -23
- package/package.json +1 -1
- package/skills/.claude-plugin/plugin.json +1 -1
- package/skills/.codex-plugin/plugin.json +1 -1
- package/skills/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -3527,8 +3527,8 @@ function buildCliSurface() {
|
|
|
3527
3527
|
backupCmd2.command("restore").option("--bucket <name>").option("--endpoint <url>").option("--region <region>").option("--target <dir>").option("--wiki <name>");
|
|
3528
3528
|
const memoryCmd2 = program2.commands.find((c) => c.name() === "memory");
|
|
3529
3529
|
memoryCmd2.command("topics").option("--project <slug>").option("--limit <n>").option("--wiki <name>");
|
|
3530
|
-
memoryCmd2.command("index").requiredOption("--project <slug>").option("--wiki <name>");
|
|
3531
|
-
memoryCmd2.command("recall").requiredOption("--project <slug>").requiredOption("--topic <slug>").option("--limit <n>").option("--wiki <name>");
|
|
3530
|
+
memoryCmd2.command("index").requiredOption("--project <slug>").option("--check").option("--if-stale").option("--wiki <name>");
|
|
3531
|
+
memoryCmd2.command("recall").requiredOption("--project <slug>").requiredOption("--topic <slug>").option("--scope <scope>").option("--limit <n>").option("--wiki <name>");
|
|
3532
3532
|
memoryCmd2.command("import").requiredOption("--from <path>").requiredOption("--project <slug>").option("--dry-run").option("--apply").option("--max-bytes <n>").option("--wiki <name>");
|
|
3533
3533
|
const fleetCmd2 = program2.commands.find((c) => c.name() === "fleet");
|
|
3534
3534
|
fleetCmd2.command("validate");
|
|
@@ -9044,24 +9044,34 @@ async function runMemoryTopics(input) {
|
|
|
9044
9044
|
async function runMemoryIndex(input) {
|
|
9045
9045
|
const scan = await scanVault(input.vault);
|
|
9046
9046
|
if (!scan.ok) return { exitCode: ExitCode.VAULT_PATH_INVALID, result: scan };
|
|
9047
|
-
const
|
|
9048
|
-
|
|
9049
|
-
|
|
9050
|
-
|
|
9051
|
-
|
|
9052
|
-
|
|
9047
|
+
const state = await buildMemoryIndexState(scan.data.allMarkdown, input.project);
|
|
9048
|
+
let status;
|
|
9049
|
+
if (input.check || input.ifStale) {
|
|
9050
|
+
const checked = await checkMemoryIndex(input.vault, input.project, state);
|
|
9051
|
+
if (!checked.ok) return checked.error;
|
|
9052
|
+
status = checked.data;
|
|
9053
|
+
if (input.check || !status.stale) {
|
|
9054
|
+
return {
|
|
9055
|
+
exitCode: ExitCode.OK,
|
|
9056
|
+
result: ok({
|
|
9057
|
+
...status,
|
|
9058
|
+
files_written: [],
|
|
9059
|
+
warnings: state.warnings,
|
|
9060
|
+
humanHint: renderMemoryIndexStatusHint(status)
|
|
9061
|
+
})
|
|
9062
|
+
};
|
|
9063
|
+
}
|
|
9053
9064
|
}
|
|
9054
9065
|
const generatedAt = (/* @__PURE__ */ new Date()).toISOString().replace(/\.\d{3}Z$/, "Z");
|
|
9055
|
-
const topics = buildTopics(input.project, sources);
|
|
9056
9066
|
const relCachePath = memoryCacheRelPath(input.project);
|
|
9057
9067
|
const absCachePath = join39(input.vault, relCachePath);
|
|
9058
9068
|
await mkdir14(join39(input.vault, ".skillwiki", "memory", input.project), { recursive: true });
|
|
9059
9069
|
await writeFile15(absCachePath, `${JSON.stringify({
|
|
9060
9070
|
generated_at: generatedAt,
|
|
9061
9071
|
project: input.project,
|
|
9062
|
-
topics,
|
|
9063
|
-
sources,
|
|
9064
|
-
warnings
|
|
9072
|
+
topics: state.topics,
|
|
9073
|
+
sources: state.sources,
|
|
9074
|
+
warnings: state.warnings
|
|
9065
9075
|
}, null, 2)}
|
|
9066
9076
|
`, "utf8");
|
|
9067
9077
|
return {
|
|
@@ -9069,18 +9079,31 @@ async function runMemoryIndex(input) {
|
|
|
9069
9079
|
result: ok({
|
|
9070
9080
|
project: input.project,
|
|
9071
9081
|
generated_at: generatedAt,
|
|
9072
|
-
|
|
9073
|
-
|
|
9074
|
-
|
|
9082
|
+
cache_present: status?.cache_present ?? true,
|
|
9083
|
+
stale: status?.stale ?? false,
|
|
9084
|
+
drift: status?.drift ?? emptyMemoryIndexDrift(),
|
|
9085
|
+
topic_count: state.topics.length,
|
|
9086
|
+
source_count: state.sources.length,
|
|
9087
|
+
topics: state.topics,
|
|
9075
9088
|
files_written: [relCachePath],
|
|
9076
|
-
warnings,
|
|
9077
|
-
humanHint: `indexed ${sources.length} memory sources into ${topics.length} topics for ${input.project}`
|
|
9089
|
+
warnings: state.warnings,
|
|
9090
|
+
humanHint: `indexed ${state.sources.length} memory sources into ${state.topics.length} topics for ${input.project}`
|
|
9078
9091
|
})
|
|
9079
9092
|
};
|
|
9080
9093
|
}
|
|
9081
9094
|
async function runMemoryRecall(input) {
|
|
9082
9095
|
const scan = await scanVault(input.vault);
|
|
9083
9096
|
if (!scan.ok) return { exitCode: ExitCode.VAULT_PATH_INVALID, result: scan };
|
|
9097
|
+
const scope = normalizeRecallScope(input.scope);
|
|
9098
|
+
if (input.scope && !scope) {
|
|
9099
|
+
return {
|
|
9100
|
+
exitCode: ExitCode.WRITE_FAILED,
|
|
9101
|
+
result: err("WRITE_FAILED", {
|
|
9102
|
+
path: "memory recall --scope",
|
|
9103
|
+
message: `invalid memory recall scope: ${String(input.scope)}`
|
|
9104
|
+
})
|
|
9105
|
+
};
|
|
9106
|
+
}
|
|
9084
9107
|
const cacheText = await readMemoryCache(input.vault, input.project);
|
|
9085
9108
|
if (!cacheText) {
|
|
9086
9109
|
return {
|
|
@@ -9088,6 +9111,7 @@ async function runMemoryRecall(input) {
|
|
|
9088
9111
|
result: ok({
|
|
9089
9112
|
project: input.project,
|
|
9090
9113
|
topic: input.topic,
|
|
9114
|
+
...scope ? { scope } : {},
|
|
9091
9115
|
sources: [],
|
|
9092
9116
|
humanHint: `no memory topics cache found for project ${input.project}`
|
|
9093
9117
|
})
|
|
@@ -9106,12 +9130,13 @@ async function runMemoryRecall(input) {
|
|
|
9106
9130
|
};
|
|
9107
9131
|
}
|
|
9108
9132
|
const limit = normalizeLimit(input.limit);
|
|
9109
|
-
const sources = normalizeSources(parsed.sources).filter((source) => source.topics.includes(input.topic)).filter((source) => source.memory_privacy !== "secret-blocked").sort(
|
|
9133
|
+
const sources = normalizeSources(parsed.sources).filter((source) => source.topics.includes(input.topic)).filter((source) => source.memory_privacy !== "secret-blocked").filter((source) => source.memory_status !== "archived" && source.memory_status !== "rejected").filter((source) => recallScopeMatches(source, input.project, scope)).sort((a, b) => compareRecallSources(a, b, input.project, scope)).slice(0, limit);
|
|
9110
9134
|
return {
|
|
9111
9135
|
exitCode: ExitCode.OK,
|
|
9112
9136
|
result: ok({
|
|
9113
9137
|
project: input.project,
|
|
9114
9138
|
topic: input.topic,
|
|
9139
|
+
...scope ? { scope } : {},
|
|
9115
9140
|
sources,
|
|
9116
9141
|
humanHint: renderRecallHint(input.topic, sources)
|
|
9117
9142
|
})
|
|
@@ -9172,6 +9197,115 @@ async function runMemoryImport(input) {
|
|
|
9172
9197
|
})
|
|
9173
9198
|
};
|
|
9174
9199
|
}
|
|
9200
|
+
async function buildMemoryIndexState(pages, project) {
|
|
9201
|
+
const warnings = [];
|
|
9202
|
+
const sourcePages = dedupePages(pages);
|
|
9203
|
+
const sources = [];
|
|
9204
|
+
for (const page of sourcePages) {
|
|
9205
|
+
const source = await readMemorySource(page, project, warnings);
|
|
9206
|
+
if (source) sources.push(source);
|
|
9207
|
+
}
|
|
9208
|
+
return {
|
|
9209
|
+
sources,
|
|
9210
|
+
topics: buildTopics(project, sources),
|
|
9211
|
+
warnings
|
|
9212
|
+
};
|
|
9213
|
+
}
|
|
9214
|
+
async function checkMemoryIndex(vault, project, current) {
|
|
9215
|
+
const relCachePath = memoryCacheRelPath(project);
|
|
9216
|
+
const cacheText = await readIfExists3(join39(vault, relCachePath));
|
|
9217
|
+
if (!cacheText) {
|
|
9218
|
+
return {
|
|
9219
|
+
ok: true,
|
|
9220
|
+
data: {
|
|
9221
|
+
project,
|
|
9222
|
+
cache_present: false,
|
|
9223
|
+
stale: true,
|
|
9224
|
+
topic_count: current.topics.length,
|
|
9225
|
+
source_count: current.sources.length,
|
|
9226
|
+
topics: current.topics,
|
|
9227
|
+
drift: emptyMemoryIndexDrift(),
|
|
9228
|
+
files_written: [],
|
|
9229
|
+
warnings: current.warnings,
|
|
9230
|
+
humanHint: ""
|
|
9231
|
+
}
|
|
9232
|
+
};
|
|
9233
|
+
}
|
|
9234
|
+
let parsed;
|
|
9235
|
+
try {
|
|
9236
|
+
parsed = JSON.parse(cacheText);
|
|
9237
|
+
} catch (e) {
|
|
9238
|
+
return {
|
|
9239
|
+
ok: false,
|
|
9240
|
+
error: {
|
|
9241
|
+
exitCode: ExitCode.WRITE_FAILED,
|
|
9242
|
+
result: err("WRITE_FAILED", {
|
|
9243
|
+
path: relCachePath,
|
|
9244
|
+
message: `invalid memory topics cache: ${String(e)}`
|
|
9245
|
+
})
|
|
9246
|
+
}
|
|
9247
|
+
};
|
|
9248
|
+
}
|
|
9249
|
+
const cachedSources = normalizeSources(parsed.sources);
|
|
9250
|
+
const cachedTopics = normalizeTopics(parsed.topics);
|
|
9251
|
+
const drift = compareMemorySources(current.sources, cachedSources);
|
|
9252
|
+
const stale = drift.missing_sources.length > 0 || drift.removed_sources.length > 0 || drift.changed_sources.length > 0 || !sameStringSet(current.topics.map((topic) => topic.name), cachedTopics.map((topic) => topic.name));
|
|
9253
|
+
return {
|
|
9254
|
+
ok: true,
|
|
9255
|
+
data: {
|
|
9256
|
+
project,
|
|
9257
|
+
generated_at: stringField2(parsed.generated_at) || void 0,
|
|
9258
|
+
cache_present: true,
|
|
9259
|
+
stale,
|
|
9260
|
+
topic_count: current.topics.length,
|
|
9261
|
+
source_count: current.sources.length,
|
|
9262
|
+
topics: current.topics,
|
|
9263
|
+
drift,
|
|
9264
|
+
files_written: [],
|
|
9265
|
+
warnings: current.warnings,
|
|
9266
|
+
humanHint: ""
|
|
9267
|
+
}
|
|
9268
|
+
};
|
|
9269
|
+
}
|
|
9270
|
+
function compareMemorySources(current, cached) {
|
|
9271
|
+
const currentByPath = new Map(current.map((source) => [source.path, source]));
|
|
9272
|
+
const cachedByPath = new Map(cached.map((source) => [source.path, source]));
|
|
9273
|
+
const missing_sources = current.filter((source) => !cachedByPath.has(source.path)).map((source) => source.path).sort((a, b) => a.localeCompare(b));
|
|
9274
|
+
const removed_sources = cached.filter((source) => !currentByPath.has(source.path)).map((source) => source.path).sort((a, b) => a.localeCompare(b));
|
|
9275
|
+
const changed_sources = current.filter((source) => {
|
|
9276
|
+
const cachedSource = cachedByPath.get(source.path);
|
|
9277
|
+
return !!cachedSource && !sameMemorySource(source, cachedSource);
|
|
9278
|
+
}).map((source) => source.path).sort((a, b) => a.localeCompare(b));
|
|
9279
|
+
return { missing_sources, removed_sources, changed_sources };
|
|
9280
|
+
}
|
|
9281
|
+
function sameMemorySource(a, b) {
|
|
9282
|
+
return a.hash === b.hash && a.updated === b.updated && (a.project ?? "") === (b.project ?? "") && (a.memory_kind ?? "") === (b.memory_kind ?? "") && (a.memory_scope ?? "") === (b.memory_scope ?? "") && (a.memory_policy ?? "") === (b.memory_policy ?? "") && a.memory_privacy === b.memory_privacy && a.memory_status === b.memory_status && sameStringSet(a.topics, b.topics);
|
|
9283
|
+
}
|
|
9284
|
+
function sameStringSet(a, b) {
|
|
9285
|
+
if (a.length !== b.length) return false;
|
|
9286
|
+
const left = [...a].sort((x, y) => x.localeCompare(y));
|
|
9287
|
+
const right = [...b].sort((x, y) => x.localeCompare(y));
|
|
9288
|
+
return left.every((value, index) => value === right[index]);
|
|
9289
|
+
}
|
|
9290
|
+
function emptyMemoryIndexDrift() {
|
|
9291
|
+
return {
|
|
9292
|
+
missing_sources: [],
|
|
9293
|
+
removed_sources: [],
|
|
9294
|
+
changed_sources: []
|
|
9295
|
+
};
|
|
9296
|
+
}
|
|
9297
|
+
function renderMemoryIndexStatusHint(status) {
|
|
9298
|
+
if (!status.cache_present) {
|
|
9299
|
+
return `memory index cache missing for ${status.project}; rebuild with memory index --project ${status.project}`;
|
|
9300
|
+
}
|
|
9301
|
+
if (!status.stale) {
|
|
9302
|
+
return `memory index cache current for ${status.project}: ${status.source_count} sources, ${status.topic_count} topics`;
|
|
9303
|
+
}
|
|
9304
|
+
const changed = status.drift.changed_sources.length;
|
|
9305
|
+
const missing = status.drift.missing_sources.length;
|
|
9306
|
+
const removed = status.drift.removed_sources.length;
|
|
9307
|
+
return `memory index cache stale for ${status.project}: ${missing} missing, ${removed} removed, ${changed} changed sources`;
|
|
9308
|
+
}
|
|
9175
9309
|
async function readIfExists3(path) {
|
|
9176
9310
|
try {
|
|
9177
9311
|
return await readFile26(path, "utf8");
|
|
@@ -9401,6 +9535,26 @@ function compareTopics(a, b) {
|
|
|
9401
9535
|
function compareSources(a, b) {
|
|
9402
9536
|
return b.updated.localeCompare(a.updated) || a.path.localeCompare(b.path);
|
|
9403
9537
|
}
|
|
9538
|
+
function normalizeRecallScope(value) {
|
|
9539
|
+
if (!value) return void 0;
|
|
9540
|
+
return isRecallScope(value) ? value : void 0;
|
|
9541
|
+
}
|
|
9542
|
+
function isRecallScope(value) {
|
|
9543
|
+
return value === "project" || value === "global" || value === "cross-agent" || value === "all";
|
|
9544
|
+
}
|
|
9545
|
+
function recallScopeMatches(source, project, scope) {
|
|
9546
|
+
if (!scope || scope === "all") return true;
|
|
9547
|
+
if (scope === "project") return isProjectMemorySource(source, project);
|
|
9548
|
+
return source.memory_scope === scope;
|
|
9549
|
+
}
|
|
9550
|
+
function compareRecallSources(a, b, project, scope) {
|
|
9551
|
+
if (scope !== "all") return compareSources(a, b);
|
|
9552
|
+
return b.updated.localeCompare(a.updated) || Number(isProjectMemorySource(b, project)) - Number(isProjectMemorySource(a, project)) || a.path.localeCompare(b.path);
|
|
9553
|
+
}
|
|
9554
|
+
function isProjectMemorySource(source, project) {
|
|
9555
|
+
const scope = source.memory_scope || "project";
|
|
9556
|
+
return scope === "project" && (!source.project || source.project === project);
|
|
9557
|
+
}
|
|
9404
9558
|
function renderHumanHint(topics) {
|
|
9405
9559
|
if (topics.length === 0) return "no memory topics found";
|
|
9406
9560
|
return topics.map((topic) => {
|
|
@@ -11231,7 +11385,7 @@ async function postCommit(vault, exitCode) {
|
|
|
11231
11385
|
if (!porcelain || porcelain.trim().length === 0) return;
|
|
11232
11386
|
const { gitStrict: gitStrict2 } = await import("./git-M4WGJ5G3.js");
|
|
11233
11387
|
try {
|
|
11234
|
-
gitStrict2(vault, ["add", "-A", "--",
|
|
11388
|
+
gitStrict2(vault, ["add", "-A", "--", "."]);
|
|
11235
11389
|
for (const generatedPath of VAULT_GENERATED_COMMIT_PATHS) {
|
|
11236
11390
|
try {
|
|
11237
11391
|
gitStrict2(vault, ["reset", "HEAD", "--", generatedPath]);
|
|
@@ -11656,21 +11810,24 @@ memoryCmd.command("topics [vault]").description("list topic-oriented memory from
|
|
|
11656
11810
|
limit: opts.limit
|
|
11657
11811
|
}), v.vault, { postCommit: false });
|
|
11658
11812
|
});
|
|
11659
|
-
memoryCmd.command("index [vault]").description("build the derived project memory topic cache").requiredOption("--project <slug>", "project slug").option("--wiki <name>", "wiki profile name").action(async (vault, opts) => {
|
|
11813
|
+
memoryCmd.command("index [vault]").description("build the derived project memory topic cache").requiredOption("--project <slug>", "project slug").option("--check", "report whether the local project memory cache is missing or stale without writing", false).option("--if-stale", "rebuild the local project memory cache only when it is missing or stale", false).option("--wiki <name>", "wiki profile name").action(async (vault, opts) => {
|
|
11660
11814
|
const v = await resolveVaultArg(vault, opts.wiki);
|
|
11661
11815
|
if (!v.ok) emit({ exitCode: v.exitCode, result: v.payload });
|
|
11662
11816
|
else emit(await runMemoryIndex({
|
|
11663
11817
|
vault: v.vault,
|
|
11664
|
-
project: opts.project
|
|
11665
|
-
|
|
11818
|
+
project: opts.project,
|
|
11819
|
+
check: !!opts.check,
|
|
11820
|
+
ifStale: !!opts.ifStale
|
|
11821
|
+
}), v.vault, { postCommit: !opts.check });
|
|
11666
11822
|
});
|
|
11667
|
-
memoryCmd.command("recall [vault]").description("recall bounded source summaries for a memory topic").requiredOption("--project <slug>", "project slug").requiredOption("--topic <slug>", "memory topic slug").option("--limit <n>", "maximum sources to return", (s) => parseInt(s, 10), 10).option("--wiki <name>", "wiki profile name").action(async (vault, opts) => {
|
|
11823
|
+
memoryCmd.command("recall [vault]").description("recall bounded source summaries for a memory topic").requiredOption("--project <slug>", "project slug").requiredOption("--topic <slug>", "memory topic slug").option("--scope <scope>", "memory scope: project, global, cross-agent, or all").option("--limit <n>", "maximum sources to return", (s) => parseInt(s, 10), 10).option("--wiki <name>", "wiki profile name").action(async (vault, opts) => {
|
|
11668
11824
|
const v = await resolveVaultArg(vault, opts.wiki);
|
|
11669
11825
|
if (!v.ok) emit({ exitCode: v.exitCode, result: v.payload });
|
|
11670
11826
|
else emit(await runMemoryRecall({
|
|
11671
11827
|
vault: v.vault,
|
|
11672
11828
|
project: opts.project,
|
|
11673
11829
|
topic: opts.topic,
|
|
11830
|
+
scope: opts.scope,
|
|
11674
11831
|
limit: opts.limit
|
|
11675
11832
|
}), v.vault, { postCommit: false });
|
|
11676
11833
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "skillwiki",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.21",
|
|
4
4
|
"skills": "./",
|
|
5
5
|
"description": "Project-aware Karpathy-style knowledge base for Claude Code: 18 prompt-only skills (wiki-*, proj-*, using-skillwiki) backed by the deterministic `skillwiki` CLI.",
|
|
6
6
|
"author": {
|