skillwiki 0.2.0-beta.4 → 0.2.0-beta.6

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
@@ -70,8 +70,8 @@ var ExitCode = {
70
70
  function ok(data) {
71
71
  return { ok: true, data };
72
72
  }
73
- function err(error2, detail) {
74
- return detail === void 0 ? { ok: false, error: error2 } : { ok: false, error: error2, detail };
73
+ function err(error, detail) {
74
+ return detail === void 0 ? { ok: false, error } : { ok: false, error, detail };
75
75
  }
76
76
 
77
77
  // ../shared/src/schemas.ts
@@ -491,14 +491,9 @@ import { join as join2 } from "path";
491
491
  // src/utils/dotenv.ts
492
492
  import { readFile as readFile4, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
493
493
  import { dirname as dirname2 } from "path";
494
- var WHITELIST = /* @__PURE__ */ new Set(["WIKI_PATH", "WIKI_LANG"]);
495
- async function parseDotenvFile(path) {
496
- let text;
497
- try {
498
- text = await readFile4(path, "utf8");
499
- } catch {
500
- return {};
501
- }
494
+ var CONFIG_KEYS = ["WIKI_PATH", "WIKI_LANG"];
495
+ var _whitelist = new Set(CONFIG_KEYS);
496
+ function parseDotenvText(text) {
502
497
  const out = {};
503
498
  for (const rawLine of text.split(/\r?\n/)) {
504
499
  const line = rawLine.trim();
@@ -507,12 +502,21 @@ async function parseDotenvFile(path) {
507
502
  if (eq <= 0) continue;
508
503
  const key = line.slice(0, eq).trim();
509
504
  const value = line.slice(eq + 1).trim();
510
- if (!WHITELIST.has(key)) continue;
505
+ if (!_whitelist.has(key)) continue;
511
506
  if (value.length === 0) continue;
512
507
  out[key] = value;
513
508
  }
514
509
  return out;
515
510
  }
511
+ async function parseDotenvFile(path) {
512
+ let text;
513
+ try {
514
+ text = await readFile4(path, "utf8");
515
+ } catch {
516
+ return {};
517
+ }
518
+ return parseDotenvText(text);
519
+ }
516
520
  async function writeDotenv(filePath, entries, originalContent) {
517
521
  const lines = originalContent !== void 0 ? updateLines(originalContent, entries) : freshLines(entries);
518
522
  await mkdir2(dirname2(filePath), { recursive: true });
@@ -1296,7 +1300,7 @@ import { readFile as readFile11 } from "fs/promises";
1296
1300
  import { existsSync } from "fs";
1297
1301
  import { join as join12 } from "path";
1298
1302
  function validateKey(key) {
1299
- return key === "WIKI_PATH" || key === "WIKI_LANG";
1303
+ return CONFIG_KEYS.includes(key);
1300
1304
  }
1301
1305
  function configPath(home) {
1302
1306
  return join12(home, ".skillwiki", ".env");
@@ -1320,7 +1324,7 @@ async function runConfigSet(input) {
1320
1324
  originalContent = await readFile11(filePath, "utf8");
1321
1325
  } catch {
1322
1326
  }
1323
- const existing = originalContent !== void 0 ? await parseDotenvFile(filePath) : {};
1327
+ const existing = originalContent !== void 0 ? parseDotenvText(originalContent) : {};
1324
1328
  const merged = { ...existing, [input.key]: input.value };
1325
1329
  await writeDotenv(filePath, merged, originalContent);
1326
1330
  return { exitCode: ExitCode.OK, result: ok({ key: input.key, value: input.value, written: true, humanHint: `${input.key}=${input.value}` }) };
@@ -1342,71 +1346,58 @@ async function runConfigPath(input) {
1342
1346
  import { existsSync as existsSync2, readdirSync, statSync } from "fs";
1343
1347
  import { join as join13 } from "path";
1344
1348
  import { execSync } from "child_process";
1345
- function pass(id, label, detail) {
1346
- return { id, label, status: "pass", detail };
1347
- }
1348
- function warn(id, label, detail) {
1349
- return { id, label, status: "warn", detail };
1350
- }
1351
- function error(id, label, detail) {
1352
- return { id, label, status: "error", detail };
1349
+ function check(status, id, label, detail) {
1350
+ return { id, label, status, detail };
1353
1351
  }
1354
1352
  function checkNodeVersion() {
1355
1353
  const major = parseInt(process.version.slice(1).split(".")[0], 10);
1356
1354
  if (major >= 20) {
1357
- return pass("node_version", "Node.js version", `v${major} >= 20`);
1355
+ return check("pass", "node_version", "Node.js version", `v${major} >= 20`);
1358
1356
  }
1359
- return error("node_version", "Node.js version", `Node.js v${major} is below minimum v20`);
1357
+ return check("error", "node_version", "Node.js version", `Node.js v${major} is below minimum v20`);
1360
1358
  }
1361
1359
  function checkCliOnPath(argv) {
1362
1360
  if (argv.length >= 2 && argv[1].endsWith("cli.js")) {
1363
- return warn("cli_on_path", "skillwiki on PATH", "Running via node cli.js (dev mode) \u2014 PATH check skipped");
1361
+ return check("warn", "cli_on_path", "skillwiki on PATH", "Running via node cli.js (dev mode) \u2014 PATH check skipped");
1364
1362
  }
1365
1363
  if (argv.length >= 2 && argv[1] === "skillwiki") {
1366
- return pass("cli_on_path", "skillwiki on PATH", "Running as skillwiki \u2014 already on PATH");
1364
+ return check("pass", "cli_on_path", "skillwiki on PATH", "Running as skillwiki \u2014 already on PATH");
1367
1365
  }
1368
1366
  try {
1369
1367
  execSync("which skillwiki 2>/dev/null", { encoding: "utf8" }).trim();
1370
- return pass("cli_on_path", "skillwiki on PATH", "skillwiki found on PATH");
1368
+ return check("pass", "cli_on_path", "skillwiki on PATH", "skillwiki found on PATH");
1371
1369
  } catch {
1372
- return warn("cli_on_path", "skillwiki on PATH", "skillwiki not found on PATH");
1370
+ return check("warn", "cli_on_path", "skillwiki on PATH", "skillwiki not found on PATH");
1373
1371
  }
1374
1372
  }
1375
1373
  async function checkConfigFile(home) {
1376
- const cfgPath = join13(home, ".skillwiki", ".env");
1374
+ const cfgPath = configPath(home);
1377
1375
  if (!existsSync2(cfgPath)) {
1378
- return warn("config_file", "Config file exists", `${cfgPath} not found`);
1376
+ return check("warn", "config_file", "Config file exists", `${cfgPath} not found`);
1379
1377
  }
1380
1378
  try {
1381
1379
  const map = await parseDotenvFile(cfgPath);
1382
1380
  const keys = Object.keys(map);
1383
- return pass("config_file", "Config file exists", `Found with keys: ${keys.length > 0 ? keys.join(", ") : "(none set)"}`);
1381
+ return check("pass", "config_file", "Config file exists", `Found with keys: ${keys.length > 0 ? keys.join(", ") : "(none set)"}`);
1384
1382
  } catch (e) {
1385
- return warn("config_file", "Config file exists", `Failed to parse ${cfgPath}: ${String(e)}`);
1383
+ return check("warn", "config_file", "Config file exists", `Failed to parse ${cfgPath}: ${String(e)}`);
1386
1384
  }
1387
1385
  }
1388
- async function checkWikiPathSet(input) {
1389
- const r = await resolveRuntimePath({ flag: void 0, envValue: input.envValue, home: input.home });
1390
- if (r.ok) {
1391
- return pass("wiki_path_set", "WIKI_PATH configured", `Resolved via ${r.data.source}: ${r.data.path}`);
1392
- }
1393
- return error("wiki_path_set", "WIKI_PATH configured", "No vault configured. Run `skillwiki init` or pass --vault.");
1394
- }
1395
1386
  function checkWikiPathExists(resolvedPath) {
1396
1387
  if (resolvedPath === void 0) {
1397
- return error("wiki_path_exists", "Vault directory exists", "Cannot check \u2014 WIKI_PATH not resolved");
1388
+ return check("error", "wiki_path_exists", "Vault directory exists", "Cannot check \u2014 WIKI_PATH not resolved");
1398
1389
  }
1399
1390
  if (existsSync2(resolvedPath) && statSync(resolvedPath).isDirectory()) {
1400
- return pass("wiki_path_exists", "Vault directory exists", resolvedPath);
1391
+ return check("pass", "wiki_path_exists", "Vault directory exists", resolvedPath);
1401
1392
  }
1402
- return error("wiki_path_exists", "Vault directory exists", `${resolvedPath} does not exist or is not a directory`);
1393
+ return check("error", "wiki_path_exists", "Vault directory exists", `${resolvedPath} does not exist or is not a directory`);
1403
1394
  }
1404
1395
  function checkVaultStructure(resolvedPath) {
1405
1396
  if (resolvedPath === void 0) {
1406
- return error("vault_structure", "Vault structure valid", "Cannot check \u2014 WIKI_PATH not resolved");
1397
+ return check("error", "vault_structure", "Vault structure valid", "Cannot check \u2014 WIKI_PATH not resolved");
1407
1398
  }
1408
1399
  if (!existsSync2(resolvedPath)) {
1409
- return error("vault_structure", "Vault structure valid", "Cannot check \u2014 vault directory does not exist");
1400
+ return check("error", "vault_structure", "Vault structure valid", "Cannot check \u2014 vault directory does not exist");
1410
1401
  }
1411
1402
  const missing = [];
1412
1403
  if (!existsSync2(join13(resolvedPath, "SCHEMA.md"))) missing.push("SCHEMA.md");
@@ -1414,20 +1405,20 @@ function checkVaultStructure(resolvedPath) {
1414
1405
  if (!existsSync2(join13(resolvedPath, dir))) missing.push(dir + "/");
1415
1406
  }
1416
1407
  if (missing.length === 0) {
1417
- return pass("vault_structure", "Vault structure valid", "All required files and directories present");
1408
+ return check("pass", "vault_structure", "Vault structure valid", "All required files and directories present");
1418
1409
  }
1419
- return error("vault_structure", "Vault structure valid", `Missing: ${missing.join(", ")}`);
1410
+ return check("error", "vault_structure", "Vault structure valid", `Missing: ${missing.join(", ")}`);
1420
1411
  }
1421
1412
  function checkSkillsInstalled(home) {
1422
1413
  const skillsDir = join13(home, ".claude", "skills");
1423
1414
  if (!existsSync2(skillsDir)) {
1424
- return warn("skills_installed", "Skills installed", `${skillsDir} not found`);
1415
+ return check("warn", "skills_installed", "Skills installed", `${skillsDir} not found`);
1425
1416
  }
1426
1417
  const found = findSkillMd(skillsDir);
1427
1418
  if (found.length > 0) {
1428
- return pass("skills_installed", "Skills installed", `${found.length} SKILL.md file(s) found`);
1419
+ return check("pass", "skills_installed", "Skills installed", `${found.length} SKILL.md file(s) found`);
1429
1420
  }
1430
- return warn("skills_installed", "Skills installed", "No SKILL.md files found in ~/.claude/skills/");
1421
+ return check("warn", "skills_installed", "Skills installed", "No SKILL.md files found in ~/.claude/skills/");
1431
1422
  }
1432
1423
  function findSkillMd(dir) {
1433
1424
  const results = [];
@@ -1451,9 +1442,12 @@ async function runDoctor(input) {
1451
1442
  checks.push(checkNodeVersion());
1452
1443
  checks.push(checkCliOnPath(input.argv));
1453
1444
  checks.push(await checkConfigFile(input.home));
1454
- const wikiPathCheck = await checkWikiPathSet(input);
1455
- checks.push(wikiPathCheck);
1456
1445
  const resolved = await resolveRuntimePath({ flag: void 0, envValue: input.envValue, home: input.home });
1446
+ if (resolved.ok) {
1447
+ checks.push(check("pass", "wiki_path_set", "WIKI_PATH configured", `Resolved via ${resolved.data.source}: ${resolved.data.path}`));
1448
+ } else {
1449
+ checks.push(check("error", "wiki_path_set", "WIKI_PATH configured", "No vault configured. Run `skillwiki init` or pass --vault."));
1450
+ }
1457
1451
  const resolvedPath = resolved.ok ? resolved.data.path : void 0;
1458
1452
  checks.push(checkWikiPathExists(resolvedPath));
1459
1453
  checks.push(checkVaultStructure(resolvedPath));
@@ -1593,7 +1587,6 @@ configCmd.command("path").description("print the config file path").action(async
1593
1587
  program.command("doctor").description("diagnose skillwiki setup issues").action(async () => emit(await runDoctor({
1594
1588
  home: process.env.HOME ?? "",
1595
1589
  envValue: process.env.WIKI_PATH,
1596
- envLang: process.env.WIKI_LANG,
1597
1590
  argv: process.argv
1598
1591
  })));
1599
1592
  program.parseAsync(process.argv).catch((e) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "skillwiki",
3
- "version": "0.2.0-beta.4",
3
+ "version": "0.2.0-beta.6",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "skillwiki": "dist/cli.js"
@@ -1,7 +1,8 @@
1
1
  {
2
2
  "name": "skillwiki",
3
- "version": "0.1.0",
4
- "description": "Project-aware Karpathy-style knowledge base for Claude Code: 10 prompt-only skills (wiki-* + proj-*) backed by the deterministic `skillwiki` CLI (8 subcommands, JSON-by-default).",
3
+ "version": "0.2.0-beta.6",
4
+ "skills": "./",
5
+ "description": "Project-aware Karpathy-style knowledge base for Claude Code: 11 prompt-only skills (wiki-*, proj-*, using-skillwiki) backed by the deterministic `skillwiki` CLI (8 subcommands, JSON-by-default).",
5
6
  "author": {
6
7
  "name": "karlorz",
7
8
  "url": "https://github.com/karlorz"
@@ -0,0 +1,16 @@
1
+ {
2
+ "hooks": {
3
+ "SessionStart": [
4
+ {
5
+ "matcher": "startup|clear|compact",
6
+ "hooks": [
7
+ {
8
+ "type": "command",
9
+ "command": "\"${CLAUDE_PLUGIN_ROOT}/hooks/run-hook.cmd\" session-start",
10
+ "async": false
11
+ }
12
+ ]
13
+ }
14
+ ]
15
+ }
16
+ }
@@ -0,0 +1,43 @@
1
+ : << 'CMDBLOCK'
2
+ @echo off
3
+ REM Cross-platform polyglot wrapper for hook scripts.
4
+ REM On Windows: cmd.exe runs the batch portion, which finds and calls bash.
5
+ REM On Unix: the shell interprets this as a script (: is a no-op in bash).
6
+ REM
7
+ REM Hook scripts use extensionless filenames (e.g. "session-start" not
8
+ REM "session-start.sh") so Claude Code's Windows auto-detection -- which
9
+ REM prepends "bash" to any command containing .sh -- doesn't interfere.
10
+
11
+ if "%~1"=="" (
12
+ echo run-hook.cmd: missing script name >&2
13
+ exit /b 1
14
+ )
15
+
16
+ set "HOOK_DIR=%~dp0"
17
+
18
+ REM Try Git for Windows bash in standard locations
19
+ if exist "C:\Program Files\Git\bin\bash.exe" (
20
+ "C:\Program Files\Git\bin\bash.exe" "%HOOK_DIR%%~1" %2 %3 %4 %5 %6 %7 %8 %9
21
+ exit /b %ERRORLEVEL%
22
+ )
23
+ if exist "C:\Program Files (x86)\Git\bin\bash.exe" (
24
+ "C:\Program Files (x86)\Git\bin\bash.exe" "%HOOK_DIR%%~1" %2 %3 %4 %5 %6 %7 %8 %9
25
+ exit /b %ERRORLEVEL%
26
+ )
27
+
28
+ REM Try bash on PATH
29
+ where bash >nul 2>nul
30
+ if %ERRORLEVEL% equ 0 (
31
+ bash "%HOOK_DIR%%~1" %2 %3 %4 %5 %6 %7 %8 %9
32
+ exit /b %ERRORLEVEL%
33
+ )
34
+
35
+ REM No bash found - exit silently
36
+ exit /b 0
37
+ CMDBLOCK
38
+
39
+ # Unix: run the named script directly
40
+ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
41
+ SCRIPT_NAME="$1"
42
+ shift
43
+ exec bash "${SCRIPT_DIR}/${SCRIPT_NAME}" "$@"
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env bash
2
+ # SessionStart hook for skillwiki plugin
3
+ # Injects using-skillwiki SKILL.md content into every conversation.
4
+
5
+ set -euo pipefail
6
+
7
+ PLUGIN_ROOT="${CLAUDE_PLUGIN_ROOT:-$(cd "$(dirname "$0")/.." && pwd)}"
8
+
9
+ skill_content=$(cat "${PLUGIN_ROOT}/using-skillwiki/SKILL.md" 2>/dev/null || echo "Error reading using-skillwiki skill")
10
+
11
+ # Escape string for JSON embedding using bash parameter substitution.
12
+ # Each ${s//old/new} is a single C-level pass.
13
+ escape_for_json() {
14
+ local s="$1"
15
+ s="${s//\\/\\\\}"
16
+ s="${s//\"/\\\"}"
17
+ s="${s//$'\n'/\\n}"
18
+ s="${s//$'\r'/\\r}"
19
+ s="${s//$'\t'/\\t}"
20
+ printf '%s' "$s"
21
+ }
22
+
23
+ skill_escaped=$(escape_for_json "$skill_content")
24
+ session_context="<EXTREMELY_IMPORTANT>\nYou have skillwiki.\n\n**Below is the full content of your 'skillwiki:using-skillwiki' skill - your introduction to the skillwiki skills. For all other skills, use the 'Skill' tool:**\n\n${skill_escaped}\n</EXTREMELY_IMPORTANT>"
25
+
26
+ # Uses printf instead of heredoc to work around bash 5.3+ heredoc hang.
27
+ printf '{\n "hookSpecificOutput": {\n "hookEventName": "SessionStart",\n "additionalContext": "%s"\n }\n}\n' "$session_context"
28
+
29
+ exit 0
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@skillwiki/skills",
3
- "version": "0.1.0",
3
+ "version": "0.2.0-beta.6",
4
4
  "private": true,
5
- "files": ["wiki-*", "proj-*", ".claude-plugin", "README.md"]
5
+ "files": ["wiki-*", "proj-*", "using-skillwiki", ".claude-plugin", "hooks", "README.md"]
6
6
  }
@@ -0,0 +1,57 @@
1
+ ---
2
+ name: using-skillwiki
3
+ description: Invoke at session start or when knowledge-base tasks arise — maps all wiki-*/proj-* skills and teaches the skillwiki CLI workflow
4
+ ---
5
+
6
+ <SUBAGENT-STOP>
7
+ If you were dispatched as a subagent to execute a specific task, skip this skill.
8
+ </SUBAGENT-STOP>
9
+
10
+ # using-skillwiki
11
+
12
+ You have skillwiki — a project-aware Karpathy-style knowledge base for Claude Code.
13
+
14
+ ## When to Use These Skills
15
+
16
+ Invoke a skillwiki skill when the user:
17
+ - Wants to create, build, or start a vault/wiki/knowledge base
18
+ - Mentions ingesting sources, reading URLs into notes, converting content
19
+ - Asks to search, query, or find information in their vault
20
+ - Wants a health check or lint on their vault
21
+ - Mentions crystallizing a session into a note
22
+ - Talks about project workspaces, ADRs, or distillation
23
+ - Asks about their skillwiki configuration or setup health
24
+
25
+ ## Skill Map
26
+
27
+ | Skill | When to Invoke |
28
+ |-------|----------------|
29
+ | `wiki-init` | Bootstrap a new vault — SCHEMA.md, index.md, log.md, ~/.skillwiki/.env |
30
+ | `wiki-ingest` | Convert URLs, files, or pasted text into typed-knowledge pages |
31
+ | `wiki-query` | Search the vault and synthesize an answer with ranked results |
32
+ | `wiki-lint` | Vault health check (stale pages, oversized pages, log rotation) |
33
+ | `wiki-crystallize` | Distill the current working session into a typed-knowledge page |
34
+ | `wiki-audit` | Verify raw provenance references and source frontmatter integrity |
35
+ | `proj-init` | Bootstrap a project workspace (README, requirements, architecture) |
36
+ | `proj-work` | Open or run a work item under a project's work/ directory |
37
+ | `proj-distill` | Distill project compound entries into vault concept pages |
38
+ | `proj-decide` | Write an Architectural Decision Record (ADR) |
39
+
40
+ ## CLI Backbone
41
+
42
+ All skills are backed by the `skillwiki` CLI — a deterministic tool with no LLM calls. It handles path resolution, config management, validation, and linting. Skills invoke it via Bash for the mechanical parts and use Claude for the creative parts.
43
+
44
+ Key CLI subcommands: `init`, `lint`, `config`, `doctor`, `path`, `lang`, `install`, `graph build`.
45
+
46
+ Run `skillwiki doctor` to diagnose setup issues. Run `skillwiki config list` to see current configuration.
47
+
48
+ ## Typical Workflow
49
+
50
+ 1. **Init** (`wiki-init`) — create vault, set domain and taxonomy
51
+ 2. **Ingest** (`wiki-ingest`) — add sources, build pages
52
+ 3. **Query** (`wiki-query`) — search and synthesize answers
53
+ 4. **Lint** (`wiki-lint`) — periodic health checks
54
+ 5. **Crystallize** (`wiki-crystallize`) — save session insights as pages
55
+ 6. **Audit** (`wiki-audit`) — verify source integrity
56
+
57
+ For longer-running project work, use `proj-init` → `proj-work` → `proj-distill` / `proj-decide`.