skillwiki 0.2.1-beta.14 → 0.2.1-beta.16
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 +139 -12
- package/package.json +1 -1
- package/skills/.claude-plugin/plugin.json +1 -1
- package/skills/package.json +1 -1
- package/skills/wiki-add-task/SKILL.md +48 -30
- package/templates/project-README.md +4 -0
package/dist/cli.js
CHANGED
|
@@ -73,7 +73,8 @@ var ExitCode = {
|
|
|
73
73
|
RAW_DEDUP_DETECTED: 33,
|
|
74
74
|
MIGRATION_APPLIED: 34,
|
|
75
75
|
UNKNOWN_WIKI_PROFILE: 35,
|
|
76
|
-
DEDUP_APPLIED: 36
|
|
76
|
+
DEDUP_APPLIED: 36,
|
|
77
|
+
PROJECT_NOT_FOUND: 37
|
|
77
78
|
};
|
|
78
79
|
|
|
79
80
|
// ../shared/src/json-output.ts
|
|
@@ -120,12 +121,10 @@ var RawSourceSchema = z.object({
|
|
|
120
121
|
sha256: sha256Hex,
|
|
121
122
|
project: wikilink.optional(),
|
|
122
123
|
work_item: wikilink.optional(),
|
|
123
|
-
kind: z.enum(["postmortem", "session-log", "meeting-notes", "other"]).optional()
|
|
124
|
+
kind: z.enum(["postmortem", "session-log", "meeting-notes", "other", "idea", "bug", "task", "note"]).optional()
|
|
124
125
|
}).superRefine((v, ctx) => {
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
if (present !== 0 && present !== 3) {
|
|
128
|
-
ctx.addIssue({ code: z.ZodIssueCode.custom, message: "project, work_item, kind must all be set together" });
|
|
126
|
+
if (v.work_item !== void 0 && (v.project === void 0 || v.kind === void 0)) {
|
|
127
|
+
ctx.addIssue({ code: z.ZodIssueCode.custom, message: "project and kind are required when work_item is set" });
|
|
129
128
|
}
|
|
130
129
|
});
|
|
131
130
|
var WorkItemSchema = z.object({
|
|
@@ -1341,11 +1340,11 @@ async function runInit(input) {
|
|
|
1341
1340
|
const errTemplate = await writeOrPreserve(`${TEMPLATE_FOLDER}/tpl-ad-hoc-capture.md`, async () => {
|
|
1342
1341
|
return [
|
|
1343
1342
|
"---",
|
|
1344
|
-
"
|
|
1345
|
-
"
|
|
1346
|
-
"
|
|
1347
|
-
"
|
|
1348
|
-
|
|
1343
|
+
"source_url:",
|
|
1344
|
+
"ingested: {{date:YYYY-MM-DD}}",
|
|
1345
|
+
"sha256: # run: skillwiki hash <this-file>",
|
|
1346
|
+
"kind: # idea | bug | task | note | other",
|
|
1347
|
+
'project: # optional: "[[slug]]"',
|
|
1349
1348
|
"---",
|
|
1350
1349
|
"",
|
|
1351
1350
|
""
|
|
@@ -2782,6 +2781,129 @@ async function runTranscripts(input) {
|
|
|
2782
2781
|
return { exitCode: ExitCode.OK, result: ok({ transcripts, humanHint: hint }) };
|
|
2783
2782
|
}
|
|
2784
2783
|
|
|
2784
|
+
// src/commands/project-index.ts
|
|
2785
|
+
import { readdir as readdir5, readFile as readFile16, writeFile as writeFile11, mkdir as mkdir6 } from "fs/promises";
|
|
2786
|
+
import { join as join20, dirname as dirname8 } from "path";
|
|
2787
|
+
var LAYER2_DIRS = ["entities", "concepts", "comparisons", "queries", "meta"];
|
|
2788
|
+
async function runProjectIndex(input) {
|
|
2789
|
+
const slug = input.slug;
|
|
2790
|
+
const projectDir = join20(input.vault, "projects", slug);
|
|
2791
|
+
try {
|
|
2792
|
+
await readdir5(projectDir);
|
|
2793
|
+
} catch {
|
|
2794
|
+
return {
|
|
2795
|
+
exitCode: ExitCode.PROJECT_NOT_FOUND,
|
|
2796
|
+
result: err("PROJECT_NOT_FOUND", { slug, path: projectDir })
|
|
2797
|
+
};
|
|
2798
|
+
}
|
|
2799
|
+
const wikilinkPattern = `[[${slug}]]`;
|
|
2800
|
+
const entries = [];
|
|
2801
|
+
for (const dir of LAYER2_DIRS) {
|
|
2802
|
+
let files;
|
|
2803
|
+
try {
|
|
2804
|
+
files = await readdir5(join20(input.vault, dir), { withFileTypes: true });
|
|
2805
|
+
} catch {
|
|
2806
|
+
continue;
|
|
2807
|
+
}
|
|
2808
|
+
for (const entry of files) {
|
|
2809
|
+
if (!entry.isFile() || !entry.name.endsWith(".md")) continue;
|
|
2810
|
+
const filePath = join20(input.vault, dir, entry.name);
|
|
2811
|
+
let text;
|
|
2812
|
+
try {
|
|
2813
|
+
text = await readFile16(filePath, "utf8");
|
|
2814
|
+
} catch {
|
|
2815
|
+
continue;
|
|
2816
|
+
}
|
|
2817
|
+
const fm = extractFrontmatter(text);
|
|
2818
|
+
if (!fm.ok) continue;
|
|
2819
|
+
const pp = fm.data.provenance_projects;
|
|
2820
|
+
if (!Array.isArray(pp) || !pp.some((p) => String(p) === wikilinkPattern)) continue;
|
|
2821
|
+
entries.push({
|
|
2822
|
+
page: `${dir}/${entry.name}`,
|
|
2823
|
+
type: fm.data.type ?? dir.slice(0, -1),
|
|
2824
|
+
// singularize dir name
|
|
2825
|
+
title: fm.data.title ?? entry.name.replace(/\.md$/, "")
|
|
2826
|
+
});
|
|
2827
|
+
}
|
|
2828
|
+
}
|
|
2829
|
+
const typeOrder = { entity: 0, concept: 1, comparison: 2, query: 3, summary: 4, meta: 5 };
|
|
2830
|
+
entries.sort((a, b) => {
|
|
2831
|
+
const ta = typeOrder[a.type] ?? 99;
|
|
2832
|
+
const tb = typeOrder[b.type] ?? 99;
|
|
2833
|
+
return ta !== tb ? ta - tb : a.title.localeCompare(b.title);
|
|
2834
|
+
});
|
|
2835
|
+
const indexPath = join20(projectDir, "knowledge.md");
|
|
2836
|
+
let existing = false;
|
|
2837
|
+
let stale = false;
|
|
2838
|
+
try {
|
|
2839
|
+
const existingText = await readFile16(indexPath, "utf8");
|
|
2840
|
+
existing = true;
|
|
2841
|
+
const existingEntries = existingText.split("\n").filter((l) => l.startsWith("- [["));
|
|
2842
|
+
const existingPages = new Set(existingEntries.map((l) => {
|
|
2843
|
+
const m = l.match(/\[\[([^\]]+)\]\]/);
|
|
2844
|
+
return m ? m[1] : "";
|
|
2845
|
+
}));
|
|
2846
|
+
const currentPages = new Set(entries.map((e) => e.page.replace(/\.md$/, "")));
|
|
2847
|
+
stale = existingPages.size !== currentPages.size || [...currentPages].some((p) => !existingPages.has(p));
|
|
2848
|
+
} catch {
|
|
2849
|
+
}
|
|
2850
|
+
const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
2851
|
+
const grouped = /* @__PURE__ */ new Map();
|
|
2852
|
+
for (const e of entries) {
|
|
2853
|
+
const group = e.type;
|
|
2854
|
+
if (!grouped.has(group)) grouped.set(group, []);
|
|
2855
|
+
grouped.get(group).push(e);
|
|
2856
|
+
}
|
|
2857
|
+
let body = `# Knowledge Index: ${slug}
|
|
2858
|
+
|
|
2859
|
+
Autogenerated by \`skillwiki project-index\` on ${today}.
|
|
2860
|
+
|
|
2861
|
+
`;
|
|
2862
|
+
for (const [type, items] of grouped) {
|
|
2863
|
+
body += `## ${type}
|
|
2864
|
+
|
|
2865
|
+
`;
|
|
2866
|
+
for (const item of items) {
|
|
2867
|
+
const pageRef = item.page.replace(/\.md$/, "");
|
|
2868
|
+
body += `- [[${pageRef}]] \u2014 ${item.title}
|
|
2869
|
+
`;
|
|
2870
|
+
}
|
|
2871
|
+
body += "\n";
|
|
2872
|
+
}
|
|
2873
|
+
if (entries.length === 0) {
|
|
2874
|
+
body += `No Layer 2 pages reference \`[[${slug}]]\` in provenance_projects.
|
|
2875
|
+
`;
|
|
2876
|
+
}
|
|
2877
|
+
if (input.apply) {
|
|
2878
|
+
try {
|
|
2879
|
+
await mkdir6(dirname8(indexPath), { recursive: true });
|
|
2880
|
+
await writeFile11(indexPath, body, "utf8");
|
|
2881
|
+
} catch (e) {
|
|
2882
|
+
return {
|
|
2883
|
+
exitCode: ExitCode.WRITE_FAILED,
|
|
2884
|
+
result: err("WRITE_FAILED", { file: indexPath, message: String(e) })
|
|
2885
|
+
};
|
|
2886
|
+
}
|
|
2887
|
+
}
|
|
2888
|
+
const action = input.apply ? `written ${entries.length} entries to ${indexPath}` : `${entries.length} entries found (use --apply to write)`;
|
|
2889
|
+
const staleHint = stale ? " (STALE \u2014 existing index outdated)" : existing ? " (up to date)" : "";
|
|
2890
|
+
return {
|
|
2891
|
+
exitCode: ExitCode.OK,
|
|
2892
|
+
result: ok({
|
|
2893
|
+
slug,
|
|
2894
|
+
entries,
|
|
2895
|
+
existing,
|
|
2896
|
+
stale,
|
|
2897
|
+
index_path: `projects/${slug}/knowledge.md`,
|
|
2898
|
+
humanHint: `project: ${slug}
|
|
2899
|
+
entries: ${entries.length}${staleHint}
|
|
2900
|
+
${action}
|
|
2901
|
+
|
|
2902
|
+
${entries.map((e) => ` ${e.type}: [[${e.page.replace(/\.md$/, "")}]] \u2014 ${e.title}`).join("\n")}`
|
|
2903
|
+
})
|
|
2904
|
+
};
|
|
2905
|
+
}
|
|
2906
|
+
|
|
2785
2907
|
// src/cli.ts
|
|
2786
2908
|
var pkg = JSON.parse(readFileSync5(new URL("../package.json", import.meta.url), "utf8"));
|
|
2787
2909
|
var program = new Command();
|
|
@@ -2913,7 +3035,7 @@ program.command("doctor").description("diagnose skillwiki setup issues").action(
|
|
|
2913
3035
|
currentVersion: pkg.version,
|
|
2914
3036
|
cwd: process.cwd()
|
|
2915
3037
|
})));
|
|
2916
|
-
program.command("archive <page> [vault]").description("archive a typed-knowledge page").option("--wiki <name>", "wiki profile name").action(async (page, vault, opts) => {
|
|
3038
|
+
program.command("archive <page> [vault]").description("archive a typed-knowledge or raw page").option("--wiki <name>", "wiki profile name").action(async (page, vault, opts) => {
|
|
2917
3039
|
const v = await resolveVaultArg(vault, opts.wiki);
|
|
2918
3040
|
if (!v.ok) emit({ exitCode: v.exitCode, result: v.payload });
|
|
2919
3041
|
else emit(await runArchive({ vault: v.vault, page }));
|
|
@@ -2947,6 +3069,11 @@ program.command("transcripts [vault]").description("list transcript files in raw
|
|
|
2947
3069
|
if (!v.ok) emit({ exitCode: v.exitCode, result: v.payload });
|
|
2948
3070
|
else emit(await runTranscripts({ vault: v.vault, since: opts.since }));
|
|
2949
3071
|
});
|
|
3072
|
+
program.command("project-index <slug> [vault]").description("generate a knowledge index for a project workspace").option("--apply", "write knowledge.md to the project directory", false).option("--wiki <name>", "wiki profile name").action(async (slug, vault, opts) => {
|
|
3073
|
+
const v = await resolveVaultArg(vault, opts.wiki);
|
|
3074
|
+
if (!v.ok) emit({ exitCode: v.exitCode, result: v.payload });
|
|
3075
|
+
else emit(await runProjectIndex({ vault: v.vault, slug, apply: !!opts.apply }));
|
|
3076
|
+
});
|
|
2950
3077
|
triggerAutoUpdate(process.env.HOME ?? "", pkg.version);
|
|
2951
3078
|
program.parseAsync(process.argv).catch((e) => {
|
|
2952
3079
|
process.stdout.write(JSON.stringify({ ok: false, error: "INTERNAL", detail: { message: String(e) } }) + "\n");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "skillwiki",
|
|
3
|
-
"version": "0.2.1-beta.
|
|
3
|
+
"version": "0.2.1-beta.16",
|
|
4
4
|
"skills": "./",
|
|
5
5
|
"description": "Project-aware Karpathy-style knowledge base for Claude Code: 15 prompt-only skills (wiki-*, proj-*, using-skillwiki) backed by the deterministic `skillwiki` CLI.",
|
|
6
6
|
"author": {
|
package/skills/package.json
CHANGED
|
@@ -9,8 +9,8 @@ Capture ad-hoc ideas, bugs, tasks, and notes into the vault. Three entry points
|
|
|
9
9
|
|
|
10
10
|
| Entry | When | What happens |
|
|
11
11
|
|-------|------|-------------|
|
|
12
|
-
| `/wiki-add-task <text>` | You're in a Claude session |
|
|
13
|
-
| Filesystem drop | You're NOT in a Claude session (Obsidian, editor, sync) | Create
|
|
12
|
+
| `/wiki-add-task <text>` | You're in a Claude session | Creates `raw/transcripts/YYYY-MM-DD-{type}-{slug}.md` with raw-valid frontmatter |
|
|
13
|
+
| Filesystem drop | You're NOT in a Claude session (Obsidian, editor, sync) | Create any `.md` file in `raw/transcripts/` using the vault template — dev-loop discovers it on next cycle |
|
|
14
14
|
| Dev-loop discovery | Automatic, next cycle | Scans `raw/transcripts/` for new files since last cycle, surfaces as claimable work |
|
|
15
15
|
|
|
16
16
|
## When This Skill Activates
|
|
@@ -30,52 +30,76 @@ Run `skillwiki lang` at the start. Entry prose and `--human` summaries use the r
|
|
|
30
30
|
- `text` — the idea/bug/task/note content (required)
|
|
31
31
|
- `type` — one of: `idea`, `bug`, `task`, `note` (default: `idea`)
|
|
32
32
|
- `project` — optional project slug to cross-reference (e.g., `llm-wiki`)
|
|
33
|
-
2. **
|
|
34
|
-
3. **Write
|
|
33
|
+
2. **Build filename.** Derive a short slug from the text (lowercase, hyphenated, max 8 words). The capture file is `raw/transcripts/YYYY-MM-DD-{type}-{slug}.md`. Each capture gets its own file — never append to an existing file.
|
|
34
|
+
3. **Write frontmatter.** Create the file with raw-source frontmatter:
|
|
35
|
+
```yaml
|
|
36
|
+
---
|
|
37
|
+
source_url:
|
|
38
|
+
ingested: YYYY-MM-DD
|
|
39
|
+
ingested_by: manual
|
|
40
|
+
sha256:
|
|
41
|
+
kind: {type}
|
|
42
|
+
project: "[[{slug}]]"
|
|
43
|
+
---
|
|
44
|
+
```
|
|
45
|
+
- Set `kind` to the parsed type (`idea`, `bug`, `task`, `note`).
|
|
46
|
+
- If a `project` slug was provided, set `project: "[[slug]]"`.
|
|
47
|
+
- If no project, omit the `project` field entirely.
|
|
48
|
+
- Leave `sha256` empty for now — step 5 fills it in.
|
|
49
|
+
- `source_url` is null (these are locally originated captures).
|
|
50
|
+
4. **Write body.** Below the frontmatter, write:
|
|
35
51
|
```markdown
|
|
36
|
-
|
|
52
|
+
# {type}: {text}
|
|
37
53
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
<!---meta: {"captured_at": "YYYY-MM-DDTHH:MM:SS", "type": "[type]"}--->
|
|
54
|
+
{text}
|
|
41
55
|
```
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
56
|
+
Use the resolved output language for any prose. The type label and frontmatter stay English.
|
|
57
|
+
5. **Compute and write sha256.** Run `skillwiki hash <file>` to get the SHA-256 of the body. Update the `sha256:` field in the frontmatter with the computed value. This makes the file validate as a raw source.
|
|
58
|
+
6. **Cross-reference (optional).** If a `project` slug was provided:
|
|
45
59
|
- Check that `projects/{slug}/` exists in the vault.
|
|
46
|
-
- Append a one-line reference to the project's
|
|
47
|
-
`- [YYYY-MM-DD] capture: [text] → raw/transcripts/YYYY-MM-DD-
|
|
60
|
+
- Append a one-line reference to the project's compound notes:
|
|
61
|
+
`- [YYYY-MM-DD] capture: [text (first 60 chars)] → raw/transcripts/YYYY-MM-DD-{type}-{slug}.md`
|
|
48
62
|
- Do NOT create a full work item (that's `proj-work`'s job).
|
|
49
|
-
|
|
50
|
-
|
|
63
|
+
7. **Update log.md.** Append: `## [YYYY-MM-DD] capture | [type]: [text (first 60 chars)]`
|
|
64
|
+
8. **Confirm to user.** Report what was captured and where. Suggest next steps:
|
|
51
65
|
- If `type: idea` → "Consider ingesting related sources to develop this idea."
|
|
52
66
|
- If `type: bug` → "Use proj-work to create a bug-fix work item."
|
|
53
67
|
- If `type: task` → "Use proj-work to track this task through the dev loop."
|
|
54
68
|
- If `type: note` → "Will be available for future wiki-query searches."
|
|
55
69
|
|
|
56
|
-
##
|
|
70
|
+
## Capture file format
|
|
57
71
|
|
|
58
|
-
|
|
72
|
+
Each capture is a standalone raw source file with valid frontmatter:
|
|
59
73
|
|
|
60
74
|
```yaml
|
|
61
75
|
---
|
|
62
76
|
source_url:
|
|
63
|
-
ingested:
|
|
64
|
-
|
|
77
|
+
ingested: 2026-05-08
|
|
78
|
+
ingested_by: manual
|
|
79
|
+
sha256: <64-char hex computed over body>
|
|
80
|
+
kind: idea
|
|
81
|
+
project: "[[llm-wiki]]"
|
|
65
82
|
---
|
|
83
|
+
|
|
84
|
+
# idea: Fix the template mismatch
|
|
85
|
+
|
|
86
|
+
Fix the template mismatch between wiki-add-task and the vault template.
|
|
66
87
|
```
|
|
67
88
|
|
|
68
|
-
The `
|
|
89
|
+
The `kind` field uses the capture type and must be one of: `idea`, `bug`, `task`, `note` (plus the existing `postmortem`, `session-log`, `meeting-notes`, `other` for non-capture raw sources).
|
|
90
|
+
|
|
91
|
+
The `project` and `kind` fields can be set independently — they do not require `work_item`. The `work_item` field is only used when the raw source is directly tied to a project work item (set by `proj-work`).
|
|
69
92
|
|
|
70
93
|
## Stop conditions
|
|
71
94
|
|
|
72
95
|
- `skillwiki path` returns NO_VAULT_CONFIGURED.
|
|
73
96
|
- No `text` provided (prompt user once, then stop).
|
|
97
|
+
- Target file already exists (use a different slug or add a suffix).
|
|
74
98
|
|
|
75
99
|
## Forbidden
|
|
76
100
|
|
|
77
101
|
- Creating an `inbox/` directory. All captures go to `raw/transcripts/`.
|
|
78
|
-
-
|
|
102
|
+
- Appending to existing capture files — each capture gets its own file.
|
|
79
103
|
- Creating a work item — this is capture-only. Use `proj-work` for full work items.
|
|
80
104
|
- Writing to any Layer 2 or Layer 3 location. Captures are Layer 1 (raw).
|
|
81
105
|
|
|
@@ -83,16 +107,10 @@ The `sha256` is computed over the body after the closing `---`. On each append,
|
|
|
83
107
|
|
|
84
108
|
When you're not in a Claude session, drop files directly into `raw/transcripts/`:
|
|
85
109
|
|
|
86
|
-
1. Create
|
|
87
|
-
2.
|
|
88
|
-
```yaml
|
|
89
|
-
---
|
|
90
|
-
source_url:
|
|
91
|
-
ingested: YYYY-MM-DD
|
|
92
|
-
sha256:
|
|
93
|
-
---
|
|
94
|
-
```
|
|
110
|
+
1. Create a `.md` file in `raw/transcripts/` — name it descriptively (e.g., `2026-05-08-idea-fix-template.md`)
|
|
111
|
+
2. Use the vault template at `_Templates/tpl-ad-hoc-capture.md` for frontmatter scaffolding
|
|
95
112
|
3. Write your idea/bug/task/note below the frontmatter
|
|
113
|
+
4. Run `skillwiki hash <file>` when you're back in a session to fill in sha256
|
|
96
114
|
|
|
97
115
|
No special format required — the dev-loop QUERY step will discover new files on the next cycle and surface them as claimable work. Mark the type with a heading like `## idea`, `## bug`, `## task`, or just write freeform.
|
|
98
116
|
|
|
@@ -12,3 +12,7 @@
|
|
|
12
12
|
- `architecture/` — how it's designed (incl. ADRs).
|
|
13
13
|
- `work/YYYY-MM-DD-{slug}/` — per-work-item folders containing `spec.md`, `plan.md`, `log.md`.
|
|
14
14
|
- `compound/` — project-local concrete learnings.
|
|
15
|
+
|
|
16
|
+
## Knowledge
|
|
17
|
+
|
|
18
|
+
Run `skillwiki project-index {{slug}} --apply` to generate `knowledge.md` listing all Layer 2 pages that reference `[[{{slug}}]]` in their provenance_projects.
|