skillwiki 0.9.30 → 0.9.31

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.
@@ -1170,8 +1170,9 @@ var FRONTMATTER = /^---\n[\s\S]*?\n---\n?/;
1170
1170
  function stripFences(body) {
1171
1171
  return body.replace(FENCE2, "").replace(INLINE_CODE, "");
1172
1172
  }
1173
+ var TILDE_FENCE = /~~~[\s\S]*?~~~/g;
1173
1174
  function stripFencedBlocks(body) {
1174
- return body.replace(FENCE2, "");
1175
+ return body.replace(FENCE2, "").replace(TILDE_FENCE, "");
1175
1176
  }
1176
1177
  function extractCitationMarkers(body) {
1177
1178
  const stripped = stripFences(body);
@@ -2922,6 +2923,23 @@ function hasDuplicateFrontmatter(body) {
2922
2923
  }
2923
2924
  return false;
2924
2925
  }
2926
+ var CANONICAL_LOCAL_SOURCE_LABEL = /^\s*(?:>\s*)?(?:[-*+]\s*)?Source (?:file|inspected):/i;
2927
+ var LOCAL_ABSOLUTE_SOURCE_REF = /(?:file:\/\/(?:\/)?(?:Users|home)\/|\/(?:Users|home)\/)/;
2928
+ function hasCanonicalLocalSourceAssertion(body) {
2929
+ const visibleBody = stripFencedBlocks(body);
2930
+ return visibleBody.split(/\r?\n/).some(
2931
+ (line) => CANONICAL_LOCAL_SOURCE_LABEL.test(line) && LOCAL_ABSOLUTE_SOURCE_REF.test(line)
2932
+ );
2933
+ }
2934
+ function shouldCheckCanonicalLocalSourceAssertion(page) {
2935
+ if (page.relPath.startsWith("raw/transcripts/")) return false;
2936
+ if (/^projects\/[^/]+\/work\/[^/]+\/log\.md$/.test(page.relPath)) return false;
2937
+ if (page.relPath.startsWith("raw/")) return true;
2938
+ if (/^(entities|concepts|comparisons|queries|meta)\//.test(page.relPath)) return true;
2939
+ if (/^projects\/[^/]+\/compound\//.test(page.relPath)) return true;
2940
+ if (/^projects\/[^/]+\/work\/[^/]+\/(spec|plan)\.md$/.test(page.relPath)) return true;
2941
+ return false;
2942
+ }
2925
2943
  function extractSourceEntries(rawFm) {
2926
2944
  const lines = rawFm.split(/\r?\n/);
2927
2945
  const sourcesLineIdx = lines.findIndex((l) => /^sources:/.test(l));
@@ -3196,14 +3214,18 @@ async function runLint(input) {
3196
3214
  if (subDirDupes.length > 0) {
3197
3215
  buckets.raw_subdirectory_duplicate = subDirDupes;
3198
3216
  }
3199
- const fileSourceUrlFlags = [];
3217
+ const fileSourceUrlFlags = /* @__PURE__ */ new Set();
3218
+ const fileSourceUrlFrontmatterFlags = /* @__PURE__ */ new Set();
3200
3219
  const rawIdentityConflicts = [];
3220
+ const rawPageBodyByPath = /* @__PURE__ */ new Map();
3201
3221
  for (const raw of scan.data.raw) {
3202
3222
  const text = await readPage(raw);
3203
3223
  const split = splitFrontmatter(text);
3204
3224
  if (!split.ok) continue;
3225
+ rawPageBodyByPath.set(raw.relPath, split.data.body);
3205
3226
  if (/^source_url:\s*file:\/\//m.test(split.data.rawFrontmatter)) {
3206
- fileSourceUrlFlags.push(raw.relPath);
3227
+ fileSourceUrlFlags.add(raw.relPath);
3228
+ fileSourceUrlFrontmatterFlags.add(raw.relPath);
3207
3229
  }
3208
3230
  const sourceUrl = split.data.rawFrontmatter.match(/^source_url:\s*(.+)$/m)?.[1]?.trim().replace(/^["']|["']$/g, "") ?? "";
3209
3231
  const assessment = assessSourceIdentity({
@@ -3222,7 +3244,26 @@ async function runLint(input) {
3222
3244
  });
3223
3245
  }
3224
3246
  }
3225
- if (fileSourceUrlFlags.length > 0) buckets.file_source_url = fileSourceUrlFlags;
3247
+ const canonicalSourcePages = [
3248
+ ...scan.data.raw,
3249
+ ...scan.data.typedKnowledge,
3250
+ ...scan.data.compound,
3251
+ ...scan.data.workItems
3252
+ ];
3253
+ for (const page of canonicalSourcePages) {
3254
+ if (!shouldCheckCanonicalLocalSourceAssertion(page)) continue;
3255
+ let body = rawPageBodyByPath.get(page.relPath);
3256
+ if (body === void 0) {
3257
+ const text = await readPage(page);
3258
+ const split = splitFrontmatter(text);
3259
+ if (!split.ok) continue;
3260
+ body = split.data.body;
3261
+ }
3262
+ if (hasCanonicalLocalSourceAssertion(body)) {
3263
+ fileSourceUrlFlags.add(page.relPath);
3264
+ }
3265
+ }
3266
+ if (fileSourceUrlFlags.size > 0) buckets.file_source_url = [...fileSourceUrlFlags];
3226
3267
  if (rawIdentityConflicts.length > 0) buckets.raw_source_identity_conflict = rawIdentityConflicts;
3227
3268
  const legacyPages = [];
3228
3269
  const orphanedPages = [];
@@ -3697,9 +3738,9 @@ ${newBody}`;
3697
3738
  else delete buckets.wikilink_citation;
3698
3739
  }
3699
3740
  }
3700
- if (shouldFix("file_source_url") && fileSourceUrlFlags.length > 0) {
3741
+ if (shouldFix("file_source_url") && fileSourceUrlFrontmatterFlags.size > 0) {
3701
3742
  const FILE_FIXED = [];
3702
- for (const relPath of fileSourceUrlFlags) {
3743
+ for (const relPath of fileSourceUrlFrontmatterFlags) {
3703
3744
  try {
3704
3745
  const absPath = `${input.vault}/${relPath}`;
3705
3746
  const raw = await readFile12(absPath, "utf8");
@@ -3730,9 +3771,26 @@ ${newBody}`;
3730
3771
  }
3731
3772
  fixed.push(...FILE_FIXED);
3732
3773
  if (FILE_FIXED.length > 0) {
3733
- const fixedSet = new Set(FILE_FIXED);
3734
- const remaining = fileSourceUrlFlags.filter((p) => !fixedSet.has(p));
3735
- if (remaining.length > 0) buckets.file_source_url = remaining;
3774
+ const remaining = new Set(fileSourceUrlFlags);
3775
+ for (const relPath of FILE_FIXED) {
3776
+ try {
3777
+ const page = scan.data.allMarkdown.find((p) => p.relPath === relPath);
3778
+ if (!page) {
3779
+ remaining.delete(relPath);
3780
+ continue;
3781
+ }
3782
+ const text = await readPage(page);
3783
+ const split = splitFrontmatter(text);
3784
+ if (!split.ok) continue;
3785
+ const stillHasFileSourceUrl = /^source_url:\s*file:\/\//m.test(split.data.rawFrontmatter);
3786
+ const stillHasCanonicalBodyAssertion = shouldCheckCanonicalLocalSourceAssertion(page) && hasCanonicalLocalSourceAssertion(split.data.body);
3787
+ if (!stillHasFileSourceUrl && !stillHasCanonicalBodyAssertion) {
3788
+ remaining.delete(relPath);
3789
+ }
3790
+ } catch {
3791
+ }
3792
+ }
3793
+ if (remaining.size > 0) buckets.file_source_url = [...remaining];
3736
3794
  else delete buckets.file_source_url;
3737
3795
  }
3738
3796
  }
package/dist/cli.js CHANGED
@@ -69,7 +69,7 @@ import {
69
69
  triggerAutoUpdate,
70
70
  writeCache,
71
71
  writeDotenv
72
- } from "./chunk-JENSKJP6.js";
72
+ } from "./chunk-6G7RAAOS.js";
73
73
  import {
74
74
  normalizeDistTag
75
75
  } from "./chunk-E6UWZ3S3.js";
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  runSkillwikiMcpStdio
4
- } from "./chunk-JENSKJP6.js";
4
+ } from "./chunk-6G7RAAOS.js";
5
5
  import "./chunk-E6UWZ3S3.js";
6
6
 
7
7
  // src/mcp-entry.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "skillwiki",
3
- "version": "0.9.30",
3
+ "version": "0.9.31",
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.30",
3
+ "version": "0.9.31",
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.30",
3
+ "version": "0.9.31",
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.30",
3
+ "version": "0.9.31",
4
4
  "private": true,
5
5
  "files": [
6
6
  "wiki-*",
@@ -32,6 +32,7 @@ When reading retros as source material:
32
32
  `provenance: project` and
33
33
  `provenance_projects: ["[[slug]]"]`. Validate with
34
34
  `skillwiki validate`.
35
+ - **Portable source references:** follow `using-skillwiki` → Portable Source References (commit-pinned GitHub URLs or repo-relative paths; not canonical `Source file:` / `Source inspected:` host paths).
35
36
  - **Tag hygiene:** `tags:` must only contain entries from
36
37
  `{vault}/SCHEMA.md` taxonomy. Never derive tags from prose text
37
38
  (lesson/evidence sections) — use only established taxonomy tags
@@ -53,6 +53,7 @@ Rules:
53
53
  - Resolve `<vault-root>` via `skillwiki path` (never hardcode).
54
54
  - proj-work does NOT invoke any PRD skill — it provides paths only.
55
55
  - If the PRD skill cannot accept custom save paths, fall back to manual `wiki-ingest`.
56
+ - When `spec.md` or `plan.md` mentions repo files, follow `using-skillwiki` → Portable Source References.
56
57
 
57
58
  ## Pitfalls
58
59
  - **Wiki-as-truth fallacy**: tasks.md status markers are aspirational claims by previous sessions. They are often wrong. Always audit the actual file system before accepting a "DONE" label.
@@ -32,6 +32,7 @@ When reading retros as source material:
32
32
  `provenance: project` and
33
33
  `provenance_projects: ["[[slug]]"]`. Validate with
34
34
  `skillwiki validate`.
35
+ - **Portable source references:** follow `using-skillwiki` → Portable Source References (commit-pinned GitHub URLs or repo-relative paths; not canonical `Source file:` / `Source inspected:` host paths).
35
36
  - **Tag hygiene:** `tags:` must only contain entries from
36
37
  `{vault}/SCHEMA.md` taxonomy. Never derive tags from prose text
37
38
  (lesson/evidence sections) — use only established taxonomy tags
@@ -53,6 +53,7 @@ Rules:
53
53
  - Resolve `<vault-root>` via `skillwiki path` (never hardcode).
54
54
  - proj-work does NOT invoke any PRD skill — it provides paths only.
55
55
  - If the PRD skill cannot accept custom save paths, fall back to manual `wiki-ingest`.
56
+ - When `spec.md` or `plan.md` mentions repo files, follow `using-skillwiki` → Portable Source References.
56
57
 
57
58
  ## Pitfalls
58
59
  - **Wiki-as-truth fallacy**: tasks.md status markers are aspirational claims by previous sessions. They are often wrong. Always audit the actual file system before accepting a "DONE" label.
@@ -59,6 +59,15 @@ sha256: # computed by skillwiki hash over body bytes after closing ---
59
59
  ## Sensitive Content Policy
60
60
  Vault content must not contain live credentials, access keys, tokens, passwords, cookies, bearer headers, private keys, or other authenticating secrets. This includes development-only and local-only credentials. Redact values before filing using `[REDACTED:<kind>]` or `[REDACTED:<kind>:<fingerprint>]`. If a source contains live secrets, stop and ask for a redacted source or explicit rotation/remediation direction; do not preserve the secret in `raw/`.
61
61
 
62
+ ## Portable Source References
63
+ The vault is shared across hosts, so host-local absolute paths are not durable source identity.
64
+
65
+ - Prefer commit-pinned GitHub URLs when the source file is in a pushed repository and the commit is known.
66
+ - Otherwise prefer repo-relative identity in prose, such as repo slug + relative path.
67
+ - Use vault-relative references or `[[wikilinks]]` for pages already inside the wiki.
68
+ - Keep host-local absolute paths (`/Users/...`, `/home/...`, `file:///...`) only as clearly labeled observations such as `Observed on host: ...`, not as canonical `Source file:` or `Source inspected:` lines.
69
+ - Do not use markdown links to local vault files when a `[[wikilink]]` should be used instead.
70
+
62
71
  ### Ad-hoc capture: three entry points
63
72
  | Entry | When | What happens |
64
73
  |-------|------|-------------|
@@ -17,6 +17,7 @@ Standard four reads. If cwd is inside `projects/{slug}/`, also read project READ
17
17
  3. Compose the page with citations pre-attached. Reuse existing `raw/` sources where possible. Every page MUST include:
18
18
  - `> **TL;DR:**` blockquote as the first content after the title heading — a one-sentence summary of the page's key takeaway (under 200 chars). See SCHEMA.md `## TL;DR Convention`.
19
19
  - For pages tagged `architecture` or explaining workflows/systems: include a Mermaid diagram (`graph TB` or `sequenceDiagram`) in the body. Follow Obsidian-compatible Mermaid rules (see SCHEMA.md `## Mermaid Diagrams`).
20
+ - When referring to local repo files in narrative prose, follow `using-skillwiki` → Portable Source References.
20
21
  For `comparison`, evaluation-style `query`, or research-summary pages, end the body with:
21
22
  ```markdown
22
23
  ## Decision Closeout
@@ -36,5 +37,6 @@ Use exactly one disposition. Keep this as a prompt/template convention; do not a
36
37
  ## Forbidden
37
38
  - Filing without explicit `provenance:`.
38
39
  - Updating `index.md` before `validate` passes.
40
+ - Writing host-local absolute paths as canonical durable source references when a portable reference is available (see `using-skillwiki` → Portable Source References).
39
41
  - Writing `[[wikilinks]]` to pages that don't exist in the vault. Before linking, verify the target exists: check `index.md` or `ls` the target directory. If the target doesn't exist yet, use plain text instead of a wikilink.
40
42
  - Writing live credentials, access keys, tokens, passwords, cookies, bearer headers, private keys, or other authenticating secrets to the vault.
@@ -18,6 +18,7 @@ Run `skillwiki lang` at the start. Generate page-body prose, narrative sections,
18
18
  0. **Resolve vault and language.** Run `skillwiki path` (fail if NO_VAULT_CONFIGURED) and `skillwiki lang`. Use the resolved vault path for all writes; use the canonical language for all generated prose.
19
19
  1. **Guard.** For each URL: run `skillwiki fetch-guard <url>`. If exit ≠ 0, STOP and surface the error. Do not retry.
20
20
  2. **Fetch.** Use `web_fetch` (or read local file) under Layer 2 controls (the CLI Layer 2 fetcher applies in tests; in skill runtime use `web_fetch` directly and treat any error as STOP).
21
+ - **Portable local-source rule:** follow `using-skillwiki` → Portable Source References. Do not use `source_url: file:///...` as the canonical durable reference; prefer commit-pinned GitHub `blob/<commit>/<path>` when resolvable, else empty `source_url` plus portable repo-relative prose.
21
22
  3. **Identity guard.** Before writing raw files, ensure the target raw filename/title, `source_url`, fetched H1/title, and early body subject agree. If `skillwiki ingest` reports `INGEST_VALIDATION_FAILED` with `source identity conflict`, STOP. Do not fix by renaming after the fact; choose the correct title/source pair or ask the user.
22
23
  4. **Sensitive content guard.** Before writing or filing any vault page, scan the source and generated body for live credentials, access keys, tokens, passwords, cookies, bearer headers, or private keys. Redact generated prose before writing. If the source itself must remain raw and contains a live secret, STOP instead of preserving it.
23
24
  5. **Hash.** Write the raw file (frontmatter + body). Run `skillwiki hash <raw-file>` and embed the result in raw frontmatter `sha256:`.
@@ -56,6 +57,7 @@ Raw ephemeral data (market feeds, logs, transient JSON) must be written to the *
56
57
  - Updating `index.md` or `log.md` before all pages validate.
57
58
  - Modifying any existing file in `raw/`.
58
59
  - Writing raw ephemeral data directly to cloud-mounted wiki paths (`~/wiki/`).
60
+ - Writing host-local absolute paths as canonical durable source references (see `using-skillwiki` → Portable Source References).
59
61
  - Writing `[[wikilinks]]` to pages that don't exist in the vault. Before linking, verify the target exists: check `index.md` or `ls` the target directory. If the target doesn't exist yet, use plain text instead of a wikilink.
60
62
  ## Batch Mode
61
63
  When the user provides multiple sources (a directory of files, a list of URLs, or a multi-document input):
@@ -59,6 +59,15 @@ sha256: # computed by skillwiki hash over body bytes after closing ---
59
59
  ## Sensitive Content Policy
60
60
  Vault content must not contain live credentials, access keys, tokens, passwords, cookies, bearer headers, private keys, or other authenticating secrets. This includes development-only and local-only credentials. Redact values before filing using `[REDACTED:<kind>]` or `[REDACTED:<kind>:<fingerprint>]`. If a source contains live secrets, stop and ask for a redacted source or explicit rotation/remediation direction; do not preserve the secret in `raw/`.
61
61
 
62
+ ## Portable Source References
63
+ The vault is shared across hosts, so host-local absolute paths are not durable source identity.
64
+
65
+ - Prefer commit-pinned GitHub URLs when the source file is in a pushed repository and the commit is known.
66
+ - Otherwise prefer repo-relative identity in prose, such as repo slug + relative path.
67
+ - Use vault-relative references or `[[wikilinks]]` for pages already inside the wiki.
68
+ - Keep host-local absolute paths (`/Users/...`, `/home/...`, `file:///...`) only as clearly labeled observations such as `Observed on host: ...`, not as canonical `Source file:` or `Source inspected:` lines.
69
+ - Do not use markdown links to local vault files when a `[[wikilink]]` should be used instead.
70
+
62
71
  ### Ad-hoc capture: three entry points
63
72
  | Entry | When | What happens |
64
73
  |-------|------|-------------|
@@ -17,6 +17,7 @@ Standard four reads. If cwd is inside `projects/{slug}/`, also read project READ
17
17
  3. Compose the page with citations pre-attached. Reuse existing `raw/` sources where possible. Every page MUST include:
18
18
  - `> **TL;DR:**` blockquote as the first content after the title heading — a one-sentence summary of the page's key takeaway (under 200 chars). See SCHEMA.md `## TL;DR Convention`.
19
19
  - For pages tagged `architecture` or explaining workflows/systems: include a Mermaid diagram (`graph TB` or `sequenceDiagram`) in the body. Follow Obsidian-compatible Mermaid rules (see SCHEMA.md `## Mermaid Diagrams`).
20
+ - When referring to local repo files in narrative prose, follow `using-skillwiki` → Portable Source References.
20
21
  For `comparison`, evaluation-style `query`, or research-summary pages, end the body with:
21
22
  ```markdown
22
23
  ## Decision Closeout
@@ -36,5 +37,6 @@ Use exactly one disposition. Keep this as a prompt/template convention; do not a
36
37
  ## Forbidden
37
38
  - Filing without explicit `provenance:`.
38
39
  - Updating `index.md` before `validate` passes.
40
+ - Writing host-local absolute paths as canonical durable source references when a portable reference is available (see `using-skillwiki` → Portable Source References).
39
41
  - Writing `[[wikilinks]]` to pages that don't exist in the vault. Before linking, verify the target exists: check `index.md` or `ls` the target directory. If the target doesn't exist yet, use plain text instead of a wikilink.
40
42
  - Writing live credentials, access keys, tokens, passwords, cookies, bearer headers, private keys, or other authenticating secrets to the vault.
@@ -18,6 +18,7 @@ Run `skillwiki lang` at the start. Generate page-body prose, narrative sections,
18
18
  0. **Resolve vault and language.** Run `skillwiki path` (fail if NO_VAULT_CONFIGURED) and `skillwiki lang`. Use the resolved vault path for all writes; use the canonical language for all generated prose.
19
19
  1. **Guard.** For each URL: run `skillwiki fetch-guard <url>`. If exit ≠ 0, STOP and surface the error. Do not retry.
20
20
  2. **Fetch.** Use `web_fetch` (or read local file) under Layer 2 controls (the CLI Layer 2 fetcher applies in tests; in skill runtime use `web_fetch` directly and treat any error as STOP).
21
+ - **Portable local-source rule:** follow `using-skillwiki` → Portable Source References. Do not use `source_url: file:///...` as the canonical durable reference; prefer commit-pinned GitHub `blob/<commit>/<path>` when resolvable, else empty `source_url` plus portable repo-relative prose.
21
22
  3. **Identity guard.** Before writing raw files, ensure the target raw filename/title, `source_url`, fetched H1/title, and early body subject agree. If `skillwiki ingest` reports `INGEST_VALIDATION_FAILED` with `source identity conflict`, STOP. Do not fix by renaming after the fact; choose the correct title/source pair or ask the user.
22
23
  4. **Sensitive content guard.** Before writing or filing any vault page, scan the source and generated body for live credentials, access keys, tokens, passwords, cookies, bearer headers, or private keys. Redact generated prose before writing. If the source itself must remain raw and contains a live secret, STOP instead of preserving it.
23
24
  5. **Hash.** Write the raw file (frontmatter + body). Run `skillwiki hash <raw-file>` and embed the result in raw frontmatter `sha256:`.
@@ -56,6 +57,7 @@ Raw ephemeral data (market feeds, logs, transient JSON) must be written to the *
56
57
  - Updating `index.md` or `log.md` before all pages validate.
57
58
  - Modifying any existing file in `raw/`.
58
59
  - Writing raw ephemeral data directly to cloud-mounted wiki paths (`~/wiki/`).
60
+ - Writing host-local absolute paths as canonical durable source references (see `using-skillwiki` → Portable Source References).
59
61
  - Writing `[[wikilinks]]` to pages that don't exist in the vault. Before linking, verify the target exists: check `index.md` or `ls` the target directory. If the target doesn't exist yet, use plain text instead of a wikilink.
60
62
  ## Batch Mode
61
63
  When the user provides multiple sources (a directory of files, a list of URLs, or a multi-document input):