skillwiki 0.9.23 → 0.9.26

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
@@ -3469,7 +3469,7 @@ function buildCliSurface() {
3469
3469
  program2.command("index-check").option("--wiki <name>");
3470
3470
  program2.command("index-link-format").option("--wiki <name>");
3471
3471
  program2.command("topic-map-check").option("--threshold <n>").option("--wiki <name>");
3472
- program2.command("stale").option("--archive").option("--days <n>").option("--force-scan").option("--project <slug>").option("--refresh").option("--wiki <name>");
3472
+ program2.command("stale").option("--archive").option("--days <n>").option("--force-scan").option("--project <slug>").option("--wiki <name>");
3473
3473
  program2.command("claim").option("--project <slug>").option("--slug <slug>").option("--wiki <name>");
3474
3474
  program2.command("pagesize").option("--lines <n>").option("--wiki <name>");
3475
3475
  program2.command("log-rotate").option("--threshold <n>").option("--apply").option("--wiki <name>");
@@ -3512,7 +3512,7 @@ function buildCliSurface() {
3512
3512
  compoundCmd2.command("list").requiredOption("--project <slug>").option("--wiki <name>");
3513
3513
  compoundCmd2.command("delete").requiredOption("--project <slug>").option("--wiki <name>");
3514
3514
  const syncCmd2 = program2.commands.find((c) => c.name() === "sync");
3515
- syncCmd2.command("status").option("--wiki <name>");
3515
+ syncCmd2.command("status").option("--wiki <name>").option("--include-stashes");
3516
3516
  syncCmd2.command("push").option("--wiki <name>");
3517
3517
  syncCmd2.command("pull").option("--wiki <name>");
3518
3518
  syncCmd2.command("lock").option("--summary <text>").option("--ttl-minutes <n>").option("--force").option("--wiki <name>");
@@ -3525,7 +3525,7 @@ function buildCliSurface() {
3525
3525
  memoryCmd2.command("topics").option("--project <slug>").option("--limit <n>").option("--wiki <name>");
3526
3526
  memoryCmd2.command("index").requiredOption("--project <slug>").option("--check").option("--if-stale").option("--wiki <name>");
3527
3527
  memoryCmd2.command("recall").requiredOption("--project <slug>").requiredOption("--topic <slug>").option("--scope <scope>").option("--limit <n>").option("--wiki <name>");
3528
- memoryCmd2.command("review").requiredOption("--project <slug>").option("--dry-run").option("--wiki <name>");
3528
+ memoryCmd2.command("review").requiredOption("--project <slug>").option("--dry-run").option("--pre-action <target>").option("--wiki <name>");
3529
3529
  memoryCmd2.command("import").requiredOption("--from <path>").requiredOption("--project <slug>").option("--dry-run").option("--apply").option("--max-bytes <n>").option("--wiki <name>");
3530
3530
  const fleetCmd2 = program2.commands.find((c) => c.name() === "fleet");
3531
3531
  fleetCmd2.command("validate");
@@ -7004,9 +7004,12 @@ function summarizeChecks(checks) {
7004
7004
  function classifyLog(path, id, label, okPattern) {
7005
7005
  if (!existsSync12(path)) return { id, label, status: "warn", detail: `log file missing: ${path}` };
7006
7006
  const lines = readFileSync8(path, "utf8").split(/\r?\n/).filter(Boolean);
7007
- const last = lines[lines.length - 1] ?? "";
7008
- if (!last) return { id, label, status: "warn", detail: `log file empty: ${path}` };
7009
- if (/fail|error/i.test(last)) return { id, label, status: "error", detail: last.slice(0, 120) };
7007
+ if (lines.length === 0) return { id, label, status: "warn", detail: `log file empty: ${path}` };
7008
+ const statusLine = [...lines].reverse().find(
7009
+ (line) => /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z /.test(line) && (okPattern.test(line) || /\bFAIL\b|\bERROR\b/i.test(line))
7010
+ );
7011
+ const last = statusLine ?? lines[lines.length - 1];
7012
+ if (/\bFAIL\b|\bERROR\b/i.test(last)) return { id, label, status: "error", detail: last.slice(0, 120) };
7010
7013
  if (okPattern.test(last)) return { id, label, status: "pass", detail: last.slice(0, 120) };
7011
7014
  return { id, label, status: "warn", detail: last.slice(0, 120) };
7012
7015
  }
@@ -9156,6 +9159,11 @@ async function runMemoryReview(input) {
9156
9159
  indexState: state,
9157
9160
  reviewPages: state.reviewPages
9158
9161
  });
9162
+ const preAction = input.preAction?.trim();
9163
+ if (preAction) {
9164
+ findings.push(...await buildPastFailureFindings(scan.data.allMarkdown, input.project, preAction));
9165
+ findings.sort(compareMemoryReviewFindings);
9166
+ }
9159
9167
  const generatedAt = (/* @__PURE__ */ new Date()).toISOString().replace(/\.\d{3}Z$/, "Z");
9160
9168
  return {
9161
9169
  exitCode: ExitCode.OK,
@@ -9474,6 +9482,135 @@ function buildMemoryReviewFindings(input) {
9474
9482
  }
9475
9483
  return findings.sort(compareMemoryReviewFindings);
9476
9484
  }
9485
+ var COMMON_FAILURE_MATCH_TOKENS = /* @__PURE__ */ new Set([
9486
+ "memory",
9487
+ "review",
9488
+ "test",
9489
+ "tests",
9490
+ "plugin",
9491
+ "cache",
9492
+ "command",
9493
+ "feature",
9494
+ "project",
9495
+ "status",
9496
+ "error",
9497
+ "warning",
9498
+ "warn",
9499
+ "info",
9500
+ "skillwiki",
9501
+ "dev",
9502
+ "loop"
9503
+ ]);
9504
+ var FAILURE_MARKER_RE = /\b(?:miss|root cause|symptom|problem|friction|regression|bug)\b/i;
9505
+ async function buildPastFailureFindings(pages, project, target) {
9506
+ const targetProfile = buildPastFailureTargetProfile(target);
9507
+ if (!targetProfile) return [];
9508
+ const findings = [];
9509
+ for (const page of dedupePages(pages)) {
9510
+ const body = await readPastFailureCandidateBody(page, project);
9511
+ if (!body) continue;
9512
+ const match = matchPastFailureTarget(targetProfile, body);
9513
+ if (!match) continue;
9514
+ findings.push({
9515
+ id: `past_failure_match:${page.relPath}`,
9516
+ severity: "warn",
9517
+ kind: "past_failure_match",
9518
+ path: page.relPath,
9519
+ message: `${page.relPath} matches pre-action target ${targetProfile.summary}: ${match.reason}`,
9520
+ suggested_action: `Read ${page.relPath} before executing; reuse the prior miss/root-cause notes if relevant.`
9521
+ });
9522
+ }
9523
+ return findings.sort(compareMemoryReviewFindings);
9524
+ }
9525
+ function buildPastFailureTargetProfile(target) {
9526
+ const normalizedTarget = normalizeMatchText(target);
9527
+ if (!normalizedTarget) return null;
9528
+ const distinctiveTokens = extractDistinctiveTargetTokens(target);
9529
+ const includeFullPhrase = normalizedTarget.length >= 12 && distinctiveTokens.length >= 2;
9530
+ const exactNeedles = uniqueStrings([
9531
+ includeFullPhrase ? normalizedTarget : "",
9532
+ ...extractPathLikeNeedles(target),
9533
+ ...extractFlagNeedles(target),
9534
+ ...extractCodeLikeNeedles(target)
9535
+ ].map(normalizeMatchText).filter((needle) => needle.length >= 4).filter(hasDistinctiveNeedle));
9536
+ if (exactNeedles.length === 0 && distinctiveTokens.length < 2) return null;
9537
+ return {
9538
+ summary: summarizeTarget(target),
9539
+ exactNeedles,
9540
+ distinctiveTokens
9541
+ };
9542
+ }
9543
+ async function readPastFailureCandidateBody(page, project) {
9544
+ if (isProjectRetroPath(page.relPath, project)) {
9545
+ return bodyWithoutFrontmatter(await readPage(page));
9546
+ }
9547
+ if (!isRawBugCapturePath(page.relPath)) return null;
9548
+ const text = await readPage(page);
9549
+ const fm = extractFrontmatter(text);
9550
+ if (!fm.ok || !frontmatterProjectMatches(fm.data.project, project)) return null;
9551
+ return bodyWithoutFrontmatter(text);
9552
+ }
9553
+ function isProjectRetroPath(path, project) {
9554
+ return path.startsWith(`projects/${project}/work/`) && path.endsWith("/retro.md") || path.startsWith(`projects/${project}/history/`) && path.endsWith("/retro.md");
9555
+ }
9556
+ function isRawBugCapturePath(path) {
9557
+ return /^raw\/transcripts\/.*-bug-.*\.md$/.test(path);
9558
+ }
9559
+ function frontmatterProjectMatches(value, project) {
9560
+ if (typeof value === "string") return wikilinkSlug2(value) === project;
9561
+ if (Array.isArray(value)) {
9562
+ return value.some((item) => typeof item === "string" && wikilinkSlug2(item) === project);
9563
+ }
9564
+ return false;
9565
+ }
9566
+ function bodyWithoutFrontmatter(text) {
9567
+ const split = splitFrontmatter(text);
9568
+ return split.ok ? split.data.body : text;
9569
+ }
9570
+ function matchPastFailureTarget(target, candidateBody) {
9571
+ const candidate = normalizeMatchText(candidateBody);
9572
+ if (!candidate) return null;
9573
+ const exactMatch = target.exactNeedles.find((needle) => candidate.includes(needle));
9574
+ if (exactMatch) {
9575
+ return { reason: `exact signal "${exactMatch}" appeared in prior failure memory` };
9576
+ }
9577
+ if (!FAILURE_MARKER_RE.test(candidateBody)) return null;
9578
+ const candidateTokens = new Set(tokenizeForMatch(candidate));
9579
+ const overlappingTokens = target.distinctiveTokens.filter((token) => candidateTokens.has(token));
9580
+ if (overlappingTokens.length >= 2) {
9581
+ return { reason: `distinctive target terms overlapped prior failure memory (${overlappingTokens.slice(0, 4).join(", ")})` };
9582
+ }
9583
+ return null;
9584
+ }
9585
+ function extractPathLikeNeedles(value) {
9586
+ return value.match(/[A-Za-z0-9_.-]+(?:\/[A-Za-z0-9_.-]+)+/g) ?? [];
9587
+ }
9588
+ function extractFlagNeedles(value) {
9589
+ return value.match(/--[a-z][a-z0-9-]*/gi) ?? [];
9590
+ }
9591
+ function extractCodeLikeNeedles(value) {
9592
+ return (value.match(/[A-Za-z0-9_.:-]*[_.:-][A-Za-z0-9_.:-]+/g) ?? []).filter((needle) => /[A-Za-z]/.test(needle));
9593
+ }
9594
+ function extractDistinctiveTargetTokens(value) {
9595
+ return uniqueStrings(tokenizeForMatch(value).filter((token) => token.length >= 4).filter((token) => !/^\d+$/.test(token)).filter((token) => !COMMON_FAILURE_MATCH_TOKENS.has(token)));
9596
+ }
9597
+ function hasDistinctiveNeedle(value) {
9598
+ return extractDistinctiveTargetTokens(value).length > 0;
9599
+ }
9600
+ function tokenizeForMatch(value) {
9601
+ return normalizeMatchText(value).match(/[a-z0-9]+/g) ?? [];
9602
+ }
9603
+ function normalizeMatchText(value) {
9604
+ return value.toLowerCase().replace(/[`"'“”‘’]/g, "").replace(/\s+/g, " ").trim();
9605
+ }
9606
+ function summarizeTarget(target) {
9607
+ const summary = target.replace(/\s+/g, " ").trim();
9608
+ if (summary.length <= 96) return JSON.stringify(summary);
9609
+ return JSON.stringify(`${summary.slice(0, 93).trimEnd()}...`);
9610
+ }
9611
+ function uniqueStrings(values) {
9612
+ return [...new Set(values.filter((value) => value.length > 0))];
9613
+ }
9477
9614
  function parseInvalidTopicWarning(warning) {
9478
9615
  const match = warning.match(/^(.*): invalid memory topic slug (.+)$/);
9479
9616
  if (!match) return null;
@@ -12019,13 +12156,14 @@ memoryCmd.command("recall [vault]").description("recall bounded source summaries
12019
12156
  limit: opts.limit
12020
12157
  }), v.vault, { postCommit: false });
12021
12158
  });
12022
- memoryCmd.command("review [vault]").description("review deterministic memory gaps and cache drift without writing").requiredOption("--project <slug>", "project slug").option("--dry-run", "preview only; writes are not supported on this surface", false).option("--wiki <name>", "wiki profile name").action(async (vault, opts) => {
12159
+ memoryCmd.command("review [vault]").description("review deterministic memory gaps and cache drift without writing").requiredOption("--project <slug>", "project slug").option("--dry-run", "preview only; writes are not supported on this surface", false).option("--pre-action <target>", "target slug, file path, command, or error signature to check against past failure memory").option("--wiki <name>", "wiki profile name").action(async (vault, opts) => {
12023
12160
  const v = await resolveVaultArg(vault, opts.wiki);
12024
12161
  if (!v.ok) emit({ exitCode: v.exitCode, result: v.payload });
12025
12162
  else emit(await runMemoryReview({
12026
12163
  vault: v.vault,
12027
12164
  project: opts.project,
12028
- dryRun: !!opts.dryRun
12165
+ dryRun: !!opts.dryRun,
12166
+ preAction: opts.preAction
12029
12167
  }), v.vault, { postCommit: false });
12030
12168
  });
12031
12169
  memoryCmd.command("import [vault]").description("preview or apply local memory imports into raw captures").requiredOption("--from <path>", "source file or directory to scan").requiredOption("--project <slug>", "project slug").option("--dry-run", "preview only; do not write captures", false).option("--apply", "write validated raw captures for ready entries", false).option("--max-bytes <n>", "reject source files above this size", (s) => parseInt(s, 10), 2e5).option("--wiki <name>", "wiki profile name").action(async (vault, opts) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "skillwiki",
3
- "version": "0.9.23",
3
+ "version": "0.9.26",
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.23",
3
+ "version": "0.9.26",
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.23",
3
+ "version": "0.9.26",
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.23",
3
+ "version": "0.9.26",
4
4
  "private": true,
5
5
  "files": [
6
6
  "wiki-*",