skillwiki 0.9.20 → 0.9.22

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 CHANGED
@@ -8,10 +8,6 @@ import {
8
8
  normalizeDistTag,
9
9
  semverGt
10
10
  } from "./chunk-E6UWZ3S3.js";
11
- import {
12
- git,
13
- gitStrict
14
- } from "./chunk-TPS5XD2J.js";
15
11
 
16
12
  // src/cli.ts
17
13
  import { join as join49 } from "path";
@@ -3527,8 +3523,8 @@ function buildCliSurface() {
3527
3523
  backupCmd2.command("restore").option("--bucket <name>").option("--endpoint <url>").option("--region <region>").option("--target <dir>").option("--wiki <name>");
3528
3524
  const memoryCmd2 = program2.commands.find((c) => c.name() === "memory");
3529
3525
  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>");
3526
+ memoryCmd2.command("index").requiredOption("--project <slug>").option("--check").option("--if-stale").option("--wiki <name>");
3527
+ memoryCmd2.command("recall").requiredOption("--project <slug>").requiredOption("--topic <slug>").option("--scope <scope>").option("--limit <n>").option("--wiki <name>");
3532
3528
  memoryCmd2.command("import").requiredOption("--from <path>").requiredOption("--project <slug>").option("--dry-run").option("--apply").option("--max-bytes <n>").option("--wiki <name>");
3533
3529
  const fleetCmd2 = program2.commands.find((c) => c.name() === "fleet");
3534
3530
  fleetCmd2.command("validate");
@@ -9044,24 +9040,34 @@ async function runMemoryTopics(input) {
9044
9040
  async function runMemoryIndex(input) {
9045
9041
  const scan = await scanVault(input.vault);
9046
9042
  if (!scan.ok) return { exitCode: ExitCode.VAULT_PATH_INVALID, result: scan };
9047
- const warnings = [];
9048
- const sourcePages = dedupePages(scan.data.allMarkdown);
9049
- const sources = [];
9050
- for (const page of sourcePages) {
9051
- const source = await readMemorySource(page, input.project, warnings);
9052
- if (source) sources.push(source);
9043
+ const state = await buildMemoryIndexState(scan.data.allMarkdown, input.project);
9044
+ let status;
9045
+ if (input.check || input.ifStale) {
9046
+ const checked = await checkMemoryIndex(input.vault, input.project, state);
9047
+ if (!checked.ok) return checked.error;
9048
+ status = checked.data;
9049
+ if (input.check || !status.stale) {
9050
+ return {
9051
+ exitCode: ExitCode.OK,
9052
+ result: ok({
9053
+ ...status,
9054
+ files_written: [],
9055
+ warnings: state.warnings,
9056
+ humanHint: renderMemoryIndexStatusHint(status)
9057
+ })
9058
+ };
9059
+ }
9053
9060
  }
9054
9061
  const generatedAt = (/* @__PURE__ */ new Date()).toISOString().replace(/\.\d{3}Z$/, "Z");
9055
- const topics = buildTopics(input.project, sources);
9056
9062
  const relCachePath = memoryCacheRelPath(input.project);
9057
9063
  const absCachePath = join39(input.vault, relCachePath);
9058
9064
  await mkdir14(join39(input.vault, ".skillwiki", "memory", input.project), { recursive: true });
9059
9065
  await writeFile15(absCachePath, `${JSON.stringify({
9060
9066
  generated_at: generatedAt,
9061
9067
  project: input.project,
9062
- topics,
9063
- sources,
9064
- warnings
9068
+ topics: state.topics,
9069
+ sources: state.sources,
9070
+ warnings: state.warnings
9065
9071
  }, null, 2)}
9066
9072
  `, "utf8");
9067
9073
  return {
@@ -9069,18 +9075,31 @@ async function runMemoryIndex(input) {
9069
9075
  result: ok({
9070
9076
  project: input.project,
9071
9077
  generated_at: generatedAt,
9072
- topic_count: topics.length,
9073
- source_count: sources.length,
9074
- topics,
9078
+ cache_present: status?.cache_present ?? true,
9079
+ stale: status?.stale ?? false,
9080
+ drift: status?.drift ?? emptyMemoryIndexDrift(),
9081
+ topic_count: state.topics.length,
9082
+ source_count: state.sources.length,
9083
+ topics: state.topics,
9075
9084
  files_written: [relCachePath],
9076
- warnings,
9077
- humanHint: `indexed ${sources.length} memory sources into ${topics.length} topics for ${input.project}`
9085
+ warnings: state.warnings,
9086
+ humanHint: `indexed ${state.sources.length} memory sources into ${state.topics.length} topics for ${input.project}`
9078
9087
  })
9079
9088
  };
9080
9089
  }
9081
9090
  async function runMemoryRecall(input) {
9082
9091
  const scan = await scanVault(input.vault);
9083
9092
  if (!scan.ok) return { exitCode: ExitCode.VAULT_PATH_INVALID, result: scan };
9093
+ const scope = normalizeRecallScope(input.scope);
9094
+ if (input.scope && !scope) {
9095
+ return {
9096
+ exitCode: ExitCode.WRITE_FAILED,
9097
+ result: err("WRITE_FAILED", {
9098
+ path: "memory recall --scope",
9099
+ message: `invalid memory recall scope: ${String(input.scope)}`
9100
+ })
9101
+ };
9102
+ }
9084
9103
  const cacheText = await readMemoryCache(input.vault, input.project);
9085
9104
  if (!cacheText) {
9086
9105
  return {
@@ -9088,6 +9107,7 @@ async function runMemoryRecall(input) {
9088
9107
  result: ok({
9089
9108
  project: input.project,
9090
9109
  topic: input.topic,
9110
+ ...scope ? { scope } : {},
9091
9111
  sources: [],
9092
9112
  humanHint: `no memory topics cache found for project ${input.project}`
9093
9113
  })
@@ -9106,12 +9126,13 @@ async function runMemoryRecall(input) {
9106
9126
  };
9107
9127
  }
9108
9128
  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(compareSources).slice(0, limit);
9129
+ 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
9130
  return {
9111
9131
  exitCode: ExitCode.OK,
9112
9132
  result: ok({
9113
9133
  project: input.project,
9114
9134
  topic: input.topic,
9135
+ ...scope ? { scope } : {},
9115
9136
  sources,
9116
9137
  humanHint: renderRecallHint(input.topic, sources)
9117
9138
  })
@@ -9172,6 +9193,115 @@ async function runMemoryImport(input) {
9172
9193
  })
9173
9194
  };
9174
9195
  }
9196
+ async function buildMemoryIndexState(pages, project) {
9197
+ const warnings = [];
9198
+ const sourcePages = dedupePages(pages);
9199
+ const sources = [];
9200
+ for (const page of sourcePages) {
9201
+ const source = await readMemorySource(page, project, warnings);
9202
+ if (source) sources.push(source);
9203
+ }
9204
+ return {
9205
+ sources,
9206
+ topics: buildTopics(project, sources),
9207
+ warnings
9208
+ };
9209
+ }
9210
+ async function checkMemoryIndex(vault, project, current) {
9211
+ const relCachePath = memoryCacheRelPath(project);
9212
+ const cacheText = await readIfExists3(join39(vault, relCachePath));
9213
+ if (!cacheText) {
9214
+ return {
9215
+ ok: true,
9216
+ data: {
9217
+ project,
9218
+ cache_present: false,
9219
+ stale: true,
9220
+ topic_count: current.topics.length,
9221
+ source_count: current.sources.length,
9222
+ topics: current.topics,
9223
+ drift: emptyMemoryIndexDrift(),
9224
+ files_written: [],
9225
+ warnings: current.warnings,
9226
+ humanHint: ""
9227
+ }
9228
+ };
9229
+ }
9230
+ let parsed;
9231
+ try {
9232
+ parsed = JSON.parse(cacheText);
9233
+ } catch (e) {
9234
+ return {
9235
+ ok: false,
9236
+ error: {
9237
+ exitCode: ExitCode.WRITE_FAILED,
9238
+ result: err("WRITE_FAILED", {
9239
+ path: relCachePath,
9240
+ message: `invalid memory topics cache: ${String(e)}`
9241
+ })
9242
+ }
9243
+ };
9244
+ }
9245
+ const cachedSources = normalizeSources(parsed.sources);
9246
+ const cachedTopics = normalizeTopics(parsed.topics);
9247
+ const drift = compareMemorySources(current.sources, cachedSources);
9248
+ 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));
9249
+ return {
9250
+ ok: true,
9251
+ data: {
9252
+ project,
9253
+ generated_at: stringField2(parsed.generated_at) || void 0,
9254
+ cache_present: true,
9255
+ stale,
9256
+ topic_count: current.topics.length,
9257
+ source_count: current.sources.length,
9258
+ topics: current.topics,
9259
+ drift,
9260
+ files_written: [],
9261
+ warnings: current.warnings,
9262
+ humanHint: ""
9263
+ }
9264
+ };
9265
+ }
9266
+ function compareMemorySources(current, cached) {
9267
+ const currentByPath = new Map(current.map((source) => [source.path, source]));
9268
+ const cachedByPath = new Map(cached.map((source) => [source.path, source]));
9269
+ const missing_sources = current.filter((source) => !cachedByPath.has(source.path)).map((source) => source.path).sort((a, b) => a.localeCompare(b));
9270
+ const removed_sources = cached.filter((source) => !currentByPath.has(source.path)).map((source) => source.path).sort((a, b) => a.localeCompare(b));
9271
+ const changed_sources = current.filter((source) => {
9272
+ const cachedSource = cachedByPath.get(source.path);
9273
+ return !!cachedSource && !sameMemorySource(source, cachedSource);
9274
+ }).map((source) => source.path).sort((a, b) => a.localeCompare(b));
9275
+ return { missing_sources, removed_sources, changed_sources };
9276
+ }
9277
+ function sameMemorySource(a, b) {
9278
+ 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);
9279
+ }
9280
+ function sameStringSet(a, b) {
9281
+ if (a.length !== b.length) return false;
9282
+ const left = [...a].sort((x, y) => x.localeCompare(y));
9283
+ const right = [...b].sort((x, y) => x.localeCompare(y));
9284
+ return left.every((value, index) => value === right[index]);
9285
+ }
9286
+ function emptyMemoryIndexDrift() {
9287
+ return {
9288
+ missing_sources: [],
9289
+ removed_sources: [],
9290
+ changed_sources: []
9291
+ };
9292
+ }
9293
+ function renderMemoryIndexStatusHint(status) {
9294
+ if (!status.cache_present) {
9295
+ return `memory index cache missing for ${status.project}; rebuild with memory index --project ${status.project}`;
9296
+ }
9297
+ if (!status.stale) {
9298
+ return `memory index cache current for ${status.project}: ${status.source_count} sources, ${status.topic_count} topics`;
9299
+ }
9300
+ const changed = status.drift.changed_sources.length;
9301
+ const missing = status.drift.missing_sources.length;
9302
+ const removed = status.drift.removed_sources.length;
9303
+ return `memory index cache stale for ${status.project}: ${missing} missing, ${removed} removed, ${changed} changed sources`;
9304
+ }
9175
9305
  async function readIfExists3(path) {
9176
9306
  try {
9177
9307
  return await readFile26(path, "utf8");
@@ -9401,6 +9531,26 @@ function compareTopics(a, b) {
9401
9531
  function compareSources(a, b) {
9402
9532
  return b.updated.localeCompare(a.updated) || a.path.localeCompare(b.path);
9403
9533
  }
9534
+ function normalizeRecallScope(value) {
9535
+ if (!value) return void 0;
9536
+ return isRecallScope(value) ? value : void 0;
9537
+ }
9538
+ function isRecallScope(value) {
9539
+ return value === "project" || value === "global" || value === "cross-agent" || value === "all";
9540
+ }
9541
+ function recallScopeMatches(source, project, scope) {
9542
+ if (!scope || scope === "all") return true;
9543
+ if (scope === "project") return isProjectMemorySource(source, project);
9544
+ return source.memory_scope === scope;
9545
+ }
9546
+ function compareRecallSources(a, b, project, scope) {
9547
+ if (scope !== "all") return compareSources(a, b);
9548
+ return b.updated.localeCompare(a.updated) || Number(isProjectMemorySource(b, project)) - Number(isProjectMemorySource(a, project)) || a.path.localeCompare(b.path);
9549
+ }
9550
+ function isProjectMemorySource(source, project) {
9551
+ const scope = source.memory_scope || "project";
9552
+ return scope === "project" && (!source.project || source.project === project);
9553
+ }
9404
9554
  function renderHumanHint(topics) {
9405
9555
  if (topics.length === 0) return "no memory topics found";
9406
9556
  return topics.map((topic) => {
@@ -10000,6 +10150,19 @@ ${body}`;
10000
10150
  import { existsSync as existsSync17 } from "fs";
10001
10151
  import { join as join42 } from "path";
10002
10152
 
10153
+ // src/utils/git.ts
10154
+ import { execFileSync } from "child_process";
10155
+ function git(cwd, args) {
10156
+ try {
10157
+ return execFileSync("git", args, { cwd, encoding: "utf8", stdio: ["pipe", "pipe", "pipe"] }).trim();
10158
+ } catch {
10159
+ return "";
10160
+ }
10161
+ }
10162
+ function gitStrict(cwd, args) {
10163
+ return execFileSync("git", args, { cwd, encoding: "utf8", stdio: ["pipe", "pipe", "pipe"] }).trim();
10164
+ }
10165
+
10003
10166
  // src/utils/sync-lock.ts
10004
10167
  import { existsSync as existsSync16, mkdirSync as mkdirSync6, readFileSync as readFileSync11, renameSync as renameSync2, unlinkSync as unlinkSync5, writeFileSync as writeFileSync7 } from "fs";
10005
10168
  import { join as join41 } from "path";
@@ -10110,6 +10273,15 @@ var VAULT_GENERATED_COMMIT_EXCLUDES = [
10110
10273
  ...VAULT_GENERATED_COMMIT_PATHS.map((path) => `:!${path}`)
10111
10274
  ];
10112
10275
  var VAULT_COMMIT_PATHSPEC = [".", ...VAULT_GENERATED_COMMIT_EXCLUDES];
10276
+ function stageVaultContentChanges(vault) {
10277
+ gitStrict(vault, ["add", "-A", "--", "."]);
10278
+ for (const generatedPath of VAULT_GENERATED_COMMIT_PATHS) {
10279
+ try {
10280
+ gitStrict(vault, ["reset", "HEAD", "--", generatedPath]);
10281
+ } catch (_e) {
10282
+ }
10283
+ }
10284
+ }
10113
10285
 
10114
10286
  // src/commands/sync.ts
10115
10287
  function runSyncStatus(input) {
@@ -10236,13 +10408,7 @@ async function runSyncPush(input) {
10236
10408
  };
10237
10409
  }
10238
10410
  try {
10239
- gitStrict(vault, ["add", "-A", "--", ...VAULT_COMMIT_PATHSPEC]);
10240
- for (const generatedPath of VAULT_GENERATED_COMMIT_PATHS) {
10241
- try {
10242
- gitStrict(vault, ["reset", "HEAD", "--", generatedPath]);
10243
- } catch (_e) {
10244
- }
10245
- }
10411
+ stageVaultContentChanges(vault);
10246
10412
  } catch (e) {
10247
10413
  return {
10248
10414
  exitCode: ExitCode.SYNC_PUSH_FAILED,
@@ -11229,15 +11395,8 @@ async function postCommit(vault, exitCode) {
11229
11395
  if (lastOps.length === 0) return;
11230
11396
  const porcelain = git(vault, ["status", "--porcelain", "--", ...VAULT_COMMIT_PATHSPEC]);
11231
11397
  if (!porcelain || porcelain.trim().length === 0) return;
11232
- const { gitStrict: gitStrict2 } = await import("./git-M4WGJ5G3.js");
11233
11398
  try {
11234
- gitStrict2(vault, ["add", "-A", "--", ...VAULT_COMMIT_PATHSPEC]);
11235
- for (const generatedPath of VAULT_GENERATED_COMMIT_PATHS) {
11236
- try {
11237
- gitStrict2(vault, ["reset", "HEAD", "--", generatedPath]);
11238
- } catch (_e) {
11239
- }
11240
- }
11399
+ stageVaultContentChanges(vault);
11241
11400
  } catch (e) {
11242
11401
  process.stderr.write(`auto-commit: git add failed: ${String(e)}
11243
11402
  `);
@@ -11245,7 +11404,7 @@ async function postCommit(vault, exitCode) {
11245
11404
  }
11246
11405
  const commitMessage = lastOps.map((op) => `${op.operation}: ${op.summary} (${op.files.length} files)`).join("; ");
11247
11406
  try {
11248
- gitStrict2(vault, ["commit", "-m", commitMessage]);
11407
+ gitStrict(vault, ["commit", "-m", commitMessage]);
11249
11408
  } catch (e) {
11250
11409
  process.stderr.write(`auto-commit: git commit failed: ${String(e)}
11251
11410
  `);
@@ -11656,21 +11815,24 @@ memoryCmd.command("topics [vault]").description("list topic-oriented memory from
11656
11815
  limit: opts.limit
11657
11816
  }), v.vault, { postCommit: false });
11658
11817
  });
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) => {
11818
+ 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
11819
  const v = await resolveVaultArg(vault, opts.wiki);
11661
11820
  if (!v.ok) emit({ exitCode: v.exitCode, result: v.payload });
11662
11821
  else emit(await runMemoryIndex({
11663
11822
  vault: v.vault,
11664
- project: opts.project
11665
- }), v.vault);
11823
+ project: opts.project,
11824
+ check: !!opts.check,
11825
+ ifStale: !!opts.ifStale
11826
+ }), v.vault, { postCommit: !opts.check });
11666
11827
  });
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) => {
11828
+ 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
11829
  const v = await resolveVaultArg(vault, opts.wiki);
11669
11830
  if (!v.ok) emit({ exitCode: v.exitCode, result: v.payload });
11670
11831
  else emit(await runMemoryRecall({
11671
11832
  vault: v.vault,
11672
11833
  project: opts.project,
11673
11834
  topic: opts.topic,
11835
+ scope: opts.scope,
11674
11836
  limit: opts.limit
11675
11837
  }), v.vault, { postCommit: false });
11676
11838
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "skillwiki",
3
- "version": "0.9.20",
3
+ "version": "0.9.22",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "skillwiki": "dist/cli.js"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "skillwiki",
3
- "version": "0.9.20",
3
+ "version": "0.9.22",
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": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "skillwiki",
3
- "version": "0.9.20",
3
+ "version": "0.9.22",
4
4
  "description": "Project-aware Karpathy-style knowledge base for Codex with 18 prompt-only skills backed by the deterministic skillwiki CLI.",
5
5
  "author": {
6
6
  "name": "karlorz",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@skillwiki/skills",
3
- "version": "0.9.20",
3
+ "version": "0.9.22",
4
4
  "private": true,
5
5
  "files": [
6
6
  "wiki-*",
@@ -1,19 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- // src/utils/git.ts
4
- import { execFileSync } from "child_process";
5
- function git(cwd, args) {
6
- try {
7
- return execFileSync("git", args, { cwd, encoding: "utf8", stdio: ["pipe", "pipe", "pipe"] }).trim();
8
- } catch {
9
- return "";
10
- }
11
- }
12
- function gitStrict(cwd, args) {
13
- return execFileSync("git", args, { cwd, encoding: "utf8", stdio: ["pipe", "pipe", "pipe"] }).trim();
14
- }
15
-
16
- export {
17
- git,
18
- gitStrict
19
- };
@@ -1,9 +0,0 @@
1
- #!/usr/bin/env node
2
- import {
3
- git,
4
- gitStrict
5
- } from "./chunk-TPS5XD2J.js";
6
- export {
7
- git,
8
- gitStrict
9
- };