@justestif/pk 0.1.14 → 0.2.1

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/README.md CHANGED
@@ -30,25 +30,24 @@ Non-interactive:
30
30
 
31
31
  ```bash
32
32
  pk init my-project --harness claude
33
- pk init my-project --harness claude,omp,cursor # multiple harnesses
33
+ pk init my-project --harness claude,cursor # multiple harnesses
34
34
  ```
35
35
 
36
- Available harnesses: `claude`, `claude-desktop`, `omp`, `cursor`, `opencode`, `codex`.
36
+ Available harnesses: `claude` (Claude Code & Claude Desktop), `cursor` (Cursor), `gemini` (Gemini CLI), `codex` (Codex), `opencode` (OpenCode).
37
37
 
38
38
  `pk init` does three things:
39
39
 
40
40
  1. Creates `~/.pk/<name>/` as the knowledge home for this project
41
41
  2. Writes an MCP server config so your harness discovers `pk mcp`
42
- 3. Copies the `pk` skill to the harness skill directory (where supported)
42
+ 3. Installs a harness adapter that calls `pk prime` at session start to inject the skill into context
43
43
 
44
- | Harness | Config file written |
44
+ | Harness | Files written |
45
45
  |---|---|
46
- | `claude` | `.mcp.json` (project root) |
47
- | `claude-desktop` | `~/Library/Application Support/Claude/claude_desktop_config.json` |
48
- | `cursor` | `.cursor/mcp.json` |
49
- | `omp` | `.omp/mcp.json` |
50
- | `opencode` | `opencode.json` |
51
- | `codex` | `.codex/config.toml` |
46
+ | `claude` | `.mcp.json`, `CLAUDE.md`, `.claude/hooks/pk-eval.ts`, `.claude/settings.json` | (also works for Claude Desktop) |
47
+ | `cursor` | `.cursor/mcp.json`, `.cursor/rules/pk.mdc`, `.cursor/hooks/pk-eval.sh`, `.cursor/hooks.json` |
48
+ | `gemini` | `.gemini/settings.json`, `GEMINI.md`, `.gemini/hooks/pk-eval.sh` |
49
+ | `codex` | `.codex/config.toml`, `AGENTS.md`, `.codex/hooks/pk-eval.sh`, `.codex/hooks.json` |
50
+ | `opencode` | `opencode.json`, `AGENTS.md`, `CLAUDE.md`, `.opencode/plugins/pk-eval.ts` |
52
51
 
53
52
  ## How it works
54
53
 
package/dist/index.js CHANGED
@@ -17713,7 +17713,7 @@ var {
17713
17713
  var package_default = {
17714
17714
  name: "@justestif/pk",
17715
17715
  type: "module",
17716
- version: "0.1.14",
17716
+ version: "0.2.1",
17717
17717
  description: "Project knowledge \u2014 structured intake, search, and recall",
17718
17718
  bin: {
17719
17719
  pk: "dist/index.js"
@@ -34112,28 +34112,26 @@ ${c2}
34112
34112
 
34113
34113
  // src/commands/init.ts
34114
34114
  var HARNESSES = [
34115
- { hint: ".mcp.json in project root", label: "Claude Code", value: "claude" },
34116
- { hint: "~/Library/\u2026/claude_desktop_config.json", label: "Claude Desktop", value: "claude-desktop" },
34117
- { hint: ".cursor/mcp.json in project root", label: "Cursor", value: "cursor" },
34118
- { hint: ".omp/mcp.json in project root", label: "Oh My Pi", value: "omp" },
34119
- { hint: "opencode.json in project root", label: "OpenCode", value: "opencode" },
34120
- { hint: ".codex/config.toml in project root", label: "Codex CLI", value: "codex" }
34115
+ { hint: ".mcp.json + CLAUDE.md + forced-eval hook", label: "Claude Code", value: "claude" },
34116
+ { hint: ".cursor/mcp.json + .cursor/rules/pk.mdc + hook", label: "Cursor", value: "cursor" },
34117
+ { hint: ".gemini/settings.json + GEMINI.md + hook", label: "Gemini CLI", value: "gemini" },
34118
+ { hint: ".codex/config.toml + AGENTS.md + hook", label: "Codex", value: "codex" },
34119
+ { hint: "opencode.json + plugin (reads AGENTS.md/CLAUDE.md)", label: "OpenCode", value: "opencode" }
34121
34120
  ];
34122
34121
  var HARNESS_VALUES = new Set(HARNESSES.map((h2) => h2.value));
34123
34122
  var HARNESS_ACTIVATION = {
34124
34123
  claude: "start a new Claude Code session in this project",
34125
- "claude-desktop": "quit and restart Claude Desktop",
34126
- codex: "restart the Codex CLI",
34127
- cursor: "reload the Cursor window (Cmd+Shift+P \u2192 Reload Window)",
34128
- omp: "restart your Oh My Pi session",
34129
- opencode: "restart opencode"
34124
+ cursor: "restart Cursor for changes to take effect",
34125
+ gemini: "restart your Gemini CLI session",
34126
+ codex: "restart Codex for MCP to connect",
34127
+ opencode: "reload OpenCode or restart the app"
34130
34128
  };
34131
34129
  function buildOutro(created, knowledgeDir, harnesses, skillPaths) {
34132
34130
  const lines = [
34133
34131
  created ? `Created project: ${knowledgeDir}` : `Connected to existing project: ${knowledgeDir}`
34134
34132
  ];
34135
34133
  for (const h2 of harnesses) {
34136
- lines.push(` ${h2}: MCP config written \u2192 ${HARNESS_ACTIVATION[h2]}`);
34134
+ lines.push(` ${h2}: configured \u2192 ${HARNESS_ACTIVATION[h2]}`);
34137
34135
  }
34138
34136
  for (const sp of skillPaths) {
34139
34137
  lines.push(` skill installed to ${sp}`);
@@ -34179,13 +34177,81 @@ async function writeClaudeConfig(projectRoot, _name, knowledgeDir) {
34179
34177
  cfg.mcpServers = servers;
34180
34178
  await writeJson(cfgPath, cfg);
34181
34179
  }
34182
- async function writeClaudeDesktopConfig(homeDir, name, knowledgeDir) {
34183
- const cfgPath = path7.join(homeDir, "Library", "Application Support", "Claude", "claude_desktop_config.json");
34184
- const cfg = await readJson(cfgPath);
34185
- const servers = cfg.mcpServers ?? {};
34186
- servers[`pk-${name}`] = pkMcpEntry(knowledgeDir);
34187
- cfg.mcpServers = servers;
34188
- await writeJson(cfgPath, cfg);
34180
+ var PK_SECTION_START = "<!-- pk:start -->";
34181
+ var PK_SECTION_END = "<!-- pk:end -->";
34182
+ var PK_INSTRUCTION = `## pk \u2014 project knowledge
34183
+
34184
+ Use the pk skill and its MCP tools (pk_synthesize, pk_search, pk_read, pk_new, pk_lint) to manage project knowledge for this project. Run pk_synthesize({ sessionStart: true }) at the start of every session.`;
34185
+ async function writeInstructionSection(filePath, content) {
34186
+ const section = `${PK_SECTION_START}
34187
+ ${content}
34188
+ ${PK_SECTION_END}
34189
+ `;
34190
+ let existing = "";
34191
+ try {
34192
+ existing = await Bun.file(filePath).text();
34193
+ } catch {}
34194
+ const startIdx = existing.indexOf(PK_SECTION_START);
34195
+ const endIdx = existing.indexOf(PK_SECTION_END);
34196
+ let updated;
34197
+ if (startIdx !== -1 && endIdx !== -1) {
34198
+ updated = existing.slice(0, startIdx) + section + existing.slice(endIdx + PK_SECTION_END.length + 1);
34199
+ } else {
34200
+ updated = existing ? existing.trimEnd() + `
34201
+
34202
+ ` + section : section;
34203
+ }
34204
+ mkdirSync4(path7.dirname(filePath), { recursive: true });
34205
+ await Bun.write(filePath, updated);
34206
+ }
34207
+ async function writeClaudeMd(projectRoot) {
34208
+ await writeInstructionSection(path7.join(projectRoot, "CLAUDE.md"), PK_INSTRUCTION);
34209
+ }
34210
+ async function writeAgentsMd(projectRoot) {
34211
+ await writeInstructionSection(path7.join(projectRoot, "AGENTS.md"), PK_INSTRUCTION);
34212
+ }
34213
+ var FORCED_EVAL_PROMPT = "SKILL EVALUATION REQUIRED: Before responding, explicitly decide YES or NO" + " \u2014 does this request involve project work, investigation, decisions, or knowledge capture" + " where the pk skill applies? If YES, activate the pk skill, then proceed.";
34214
+ function claudeHookScript() {
34215
+ return `// pk forced-eval hook \u2014 auto-generated by pk init
34216
+ async function handleUserPromptSubmit() {
34217
+ console.log(JSON.stringify({
34218
+ hookSpecificOutput: {
34219
+ hookEventName: 'UserPromptSubmit',
34220
+ additionalContext: ${JSON.stringify(FORCED_EVAL_PROMPT)},
34221
+ },
34222
+ suppressOutput: true,
34223
+ }));
34224
+ }
34225
+
34226
+ handleUserPromptSubmit().catch(() => process.exit(0));
34227
+ `;
34228
+ }
34229
+ async function writeClaudeHook(projectRoot) {
34230
+ const hookDir = path7.join(projectRoot, ".claude", "hooks");
34231
+ const hookPath = path7.join(hookDir, "pk-eval.ts");
34232
+ mkdirSync4(hookDir, { recursive: true });
34233
+ await Bun.write(hookPath, claudeHookScript());
34234
+ const settingsPath = path7.join(projectRoot, ".claude", "settings.json");
34235
+ const settings = await readJson(settingsPath);
34236
+ const hooks = settings.hooks ?? {};
34237
+ const ups = hooks.UserPromptSubmit ?? [];
34238
+ const hookCmd = `bun run ${hookPath}`;
34239
+ const wrappedHook = {
34240
+ matcher: "",
34241
+ hooks: [
34242
+ {
34243
+ type: "command",
34244
+ command: hookCmd
34245
+ }
34246
+ ]
34247
+ };
34248
+ const hasPkEval = (entry) => typeof entry === "object" && entry !== null && ("hooks" in entry) && Array.isArray(entry.hooks) && entry.hooks.some((hook) => typeof hook === "object" && hook !== null && ("type" in hook) && hook.type === "command" && ("command" in hook) && hook.command === hookCmd);
34249
+ if (!ups.some(hasPkEval)) {
34250
+ ups.push(wrappedHook);
34251
+ }
34252
+ hooks.UserPromptSubmit = ups;
34253
+ settings.hooks = hooks;
34254
+ await writeJson(settingsPath, settings);
34189
34255
  }
34190
34256
  async function writeCursorConfig(projectRoot, _name, knowledgeDir) {
34191
34257
  const cfgPath = path7.join(projectRoot, ".cursor", "mcp.json");
@@ -34195,62 +34261,170 @@ async function writeCursorConfig(projectRoot, _name, knowledgeDir) {
34195
34261
  cfg.mcpServers = servers;
34196
34262
  await writeJson(cfgPath, cfg);
34197
34263
  }
34198
- async function writeOmpConfig(projectRoot, _name, knowledgeDir) {
34199
- const cfgPath = path7.join(projectRoot, ".omp", "mcp.json");
34264
+ async function writeCursorRules(projectRoot) {
34265
+ const rulesPath = path7.join(projectRoot, ".cursor", "rules", "pk.mdc");
34266
+ const content = `---
34267
+ description: "pk knowledge base integration"
34268
+ alwaysApply: true
34269
+ ---
34270
+
34271
+ ${PK_INSTRUCTION}
34272
+ `;
34273
+ mkdirSync4(path7.dirname(rulesPath), { recursive: true });
34274
+ await Bun.write(rulesPath, content);
34275
+ }
34276
+ function cursorHookScript() {
34277
+ return `#!/bin/bash
34278
+ # pk forced-eval hook \u2014 auto-generated by pk init
34279
+ # Exit 0 to allow, exit 2 to block
34280
+
34281
+ # Return the forced-eval prompt as additional context
34282
+ cat <<EOF
34283
+ {
34284
+ "continue": true,
34285
+ "additionalContext": ${JSON.stringify(FORCED_EVAL_PROMPT)}
34286
+ }
34287
+ EOF
34288
+ `;
34289
+ }
34290
+ async function writeCursorHook(projectRoot) {
34291
+ const hookPath = path7.join(projectRoot, ".cursor", "hooks", "pk-eval.sh");
34292
+ mkdirSync4(path7.dirname(hookPath), { recursive: true });
34293
+ await Bun.write(hookPath, cursorHookScript());
34294
+ const hooksPath = path7.join(projectRoot, ".cursor", "hooks.json");
34295
+ const hooks = await readJson(hooksPath);
34296
+ const hooksObj = hooks.hooks ?? {};
34297
+ const bsp = hooksObj.beforeSubmitPrompt ?? [];
34298
+ if (!bsp.some((h2) => h2.command === hookPath)) {
34299
+ bsp.push({ command: hookPath });
34300
+ }
34301
+ hooksObj.beforeSubmitPrompt = bsp;
34302
+ hooks.hooks = hooksObj;
34303
+ await writeJson(hooksPath, hooks);
34304
+ }
34305
+ async function writeGeminiConfig(projectRoot, _name, knowledgeDir) {
34306
+ const cfgPath = path7.join(projectRoot, ".gemini", "settings.json");
34200
34307
  const cfg = await readJson(cfgPath);
34201
34308
  const servers = cfg.mcpServers ?? {};
34202
34309
  servers.pk = pkMcpEntry(knowledgeDir);
34203
34310
  cfg.mcpServers = servers;
34204
34311
  await writeJson(cfgPath, cfg);
34205
34312
  }
34313
+ async function writeGeminiMd(projectRoot) {
34314
+ await writeInstructionSection(path7.join(projectRoot, "GEMINI.md"), PK_INSTRUCTION);
34315
+ }
34316
+ function geminiHookScript() {
34317
+ return `#!/bin/bash
34318
+ # pk forced-eval hook \u2014 auto-generated by pk init
34319
+ # Output JSON to stdout with the forced-eval prompt
34320
+
34321
+ cat <<EOF
34322
+ {
34323
+ "hookSpecificOutput": {
34324
+ "hookEventName": "BeforeAgent",
34325
+ "additionalContext": ${JSON.stringify(FORCED_EVAL_PROMPT)}
34326
+ }
34327
+ }
34328
+ EOF
34329
+ `;
34330
+ }
34331
+ async function writeGeminiHook(projectRoot) {
34332
+ const hookPath = path7.join(projectRoot, ".gemini", "hooks", "pk-eval.sh");
34333
+ mkdirSync4(path7.dirname(hookPath), { recursive: true });
34334
+ await Bun.write(hookPath, geminiHookScript());
34335
+ const cfgPath = path7.join(projectRoot, ".gemini", "settings.json");
34336
+ const cfg = await readJson(cfgPath);
34337
+ const hooks = cfg.hooks ?? {};
34338
+ const beforeAgent = hooks.BeforeAgent ?? [];
34339
+ if (!beforeAgent.some((h2) => h2.command === hookPath)) {
34340
+ beforeAgent.push({ command: hookPath });
34341
+ }
34342
+ hooks.BeforeAgent = beforeAgent;
34343
+ cfg.hooks = hooks;
34344
+ await writeJson(cfgPath, cfg);
34345
+ }
34346
+ async function writeToml(filePath, content) {
34347
+ mkdirSync4(path7.dirname(filePath), { recursive: true });
34348
+ await Bun.write(filePath, content);
34349
+ }
34350
+ async function writeCodexConfig(projectRoot, _name, knowledgeDir) {
34351
+ const cfgPath = path7.join(projectRoot, ".codex", "config.toml");
34352
+ const pkCmd = resolvePkCommand();
34353
+ const toml = `[mcp_servers.pk]
34354
+ command = "${pkCmd}"
34355
+ args = ["mcp"]
34356
+ env = { PK_KNOWLEDGE_DIR = "${knowledgeDir}" }
34357
+ `;
34358
+ await writeToml(cfgPath, toml);
34359
+ }
34360
+ function codexHookScript() {
34361
+ return `#!/bin/bash
34362
+ # pk forced-eval hook \u2014 auto-generated by pk init
34363
+ # Output JSON to stdout
34364
+
34365
+ cat <<EOF
34366
+ {
34367
+ "continue": true,
34368
+ "suppressOutput": false
34369
+ }
34370
+ EOF
34371
+ `;
34372
+ }
34373
+ async function writeCodexHook(projectRoot) {
34374
+ const hookPath = path7.join(projectRoot, ".codex", "hooks", "pk-eval.sh");
34375
+ mkdirSync4(path7.dirname(hookPath), { recursive: true });
34376
+ await Bun.write(hookPath, codexHookScript());
34377
+ const hooksPath = path7.join(projectRoot, ".codex", "hooks.json");
34378
+ const hooks = await readJson(hooksPath);
34379
+ const hooksObj = hooks.hooks ?? {};
34380
+ const ups = hooksObj.UserPromptSubmit ?? [];
34381
+ if (!ups.some((h2) => typeof h2 === "object" && h2 !== null && ("command" in h2) && typeof h2.command === "string" && h2.command.includes("pk-eval"))) {
34382
+ ups.push({ command: hookPath });
34383
+ }
34384
+ hooksObj.UserPromptSubmit = ups;
34385
+ hooks.hooks = hooksObj;
34386
+ await writeJson(hooksPath, hooks);
34387
+ }
34206
34388
  async function writeOpenCodeConfig(projectRoot, _name, knowledgeDir) {
34207
34389
  const cfgPath = path7.join(projectRoot, "opencode.json");
34208
34390
  const cfg = await readJson(cfgPath);
34209
34391
  const mcp = cfg.mcp ?? {};
34210
- mcp.pk = pkMcpEntry(knowledgeDir);
34392
+ const pkCmd = resolvePkCommand();
34393
+ mcp.pk = {
34394
+ type: "local",
34395
+ enabled: true,
34396
+ command: [pkCmd, "mcp"],
34397
+ environment: { PK_KNOWLEDGE_DIR: knowledgeDir }
34398
+ };
34211
34399
  cfg.mcp = mcp;
34212
34400
  await writeJson(cfgPath, cfg);
34213
34401
  }
34214
- async function writeCodexConfig(projectRoot, _name, knowledgeDir) {
34215
- const cfgPath = path7.join(projectRoot, ".codex", "config.toml");
34216
- const toml = [
34217
- "[mcp_servers.pk]",
34218
- `command = "${resolvePkCommand()}"`,
34219
- 'args = ["mcp"]',
34220
- "",
34221
- "[mcp_servers.pk.env]",
34222
- `PK_KNOWLEDGE_DIR = "${knowledgeDir}"`,
34223
- ""
34224
- ].join(`
34225
- `);
34226
- mkdirSync4(path7.dirname(cfgPath), { recursive: true });
34227
- if (existsSync3(cfgPath)) {
34228
- const existing = await Bun.file(cfgPath).text();
34229
- if (existing.includes("[mcp_servers.pk]")) {
34230
- return;
34231
- }
34232
- await Bun.write(cfgPath, existing.trimEnd() + `
34233
-
34234
- ` + toml);
34235
- } else {
34236
- await Bun.write(cfgPath, toml);
34237
- }
34402
+ function openCodePluginScript() {
34403
+ return `// pk forced-eval plugin \u2014 auto-generated by pk init
34404
+ export const experimental = {
34405
+ async 'chat.system.transform'({ system }: { system: string[] }): Promise<void> {
34406
+ system.unshift(${JSON.stringify(FORCED_EVAL_PROMPT)});
34407
+ },
34408
+ };
34409
+ `;
34410
+ }
34411
+ async function writeOpenCodePlugin(projectRoot) {
34412
+ const pluginPath = path7.join(projectRoot, ".opencode", "plugins", "pk-eval.ts");
34413
+ mkdirSync4(path7.dirname(pluginPath), { recursive: true });
34414
+ await Bun.write(pluginPath, openCodePluginScript());
34238
34415
  }
34239
34416
  function skillTargetDir(harness, projectRoot) {
34240
34417
  switch (harness) {
34241
- case "claude":
34242
- case "claude-desktop": {
34418
+ case "claude": {
34243
34419
  return path7.join(os3.homedir(), ".claude", "skills", "pk");
34244
34420
  }
34245
- case "omp": {
34421
+ case "cursor":
34422
+ case "gemini":
34423
+ case "opencode": {
34246
34424
  return path7.join(projectRoot, ".agents", "skills", "pk");
34247
34425
  }
34248
- case "cursor": {
34249
- return path7.join(projectRoot, ".cursor", "skills", "pk");
34250
- }
34251
- case "opencode":
34252
34426
  case "codex": {
34253
- return "";
34427
+ return path7.join(os3.homedir(), ".codex", "skills", "pk");
34254
34428
  }
34255
34429
  }
34256
34430
  }
@@ -34286,30 +34460,37 @@ async function ensureProject(name) {
34286
34460
  return { created: !alreadyExists, knowledgeDir: kDir };
34287
34461
  }
34288
34462
  async function applyHarness(harness, ctx) {
34289
- const { name, knowledgeDir, projectRoot, home } = ctx;
34463
+ const { name, knowledgeDir, projectRoot } = ctx;
34290
34464
  switch (harness) {
34291
34465
  case "claude": {
34292
34466
  await writeClaudeConfig(projectRoot, name, knowledgeDir);
34293
- break;
34294
- }
34295
- case "claude-desktop": {
34296
- await writeClaudeDesktopConfig(home, name, knowledgeDir);
34297
- break;
34298
- }
34299
- case "codex": {
34300
- await writeCodexConfig(projectRoot, name, knowledgeDir);
34467
+ await writeClaudeMd(projectRoot);
34468
+ await writeClaudeHook(projectRoot);
34301
34469
  break;
34302
34470
  }
34303
34471
  case "cursor": {
34304
34472
  await writeCursorConfig(projectRoot, name, knowledgeDir);
34473
+ await writeCursorRules(projectRoot);
34474
+ await writeCursorHook(projectRoot);
34305
34475
  break;
34306
34476
  }
34307
- case "omp": {
34308
- await writeOmpConfig(projectRoot, name, knowledgeDir);
34477
+ case "gemini": {
34478
+ await writeGeminiConfig(projectRoot, name, knowledgeDir);
34479
+ await writeGeminiMd(projectRoot);
34480
+ await writeGeminiHook(projectRoot);
34481
+ break;
34482
+ }
34483
+ case "codex": {
34484
+ await writeCodexConfig(projectRoot, name, knowledgeDir);
34485
+ await writeAgentsMd(projectRoot);
34486
+ await writeCodexHook(projectRoot);
34309
34487
  break;
34310
34488
  }
34311
34489
  case "opencode": {
34312
34490
  await writeOpenCodeConfig(projectRoot, name, knowledgeDir);
34491
+ await writeAgentsMd(projectRoot);
34492
+ await writeClaudeMd(projectRoot);
34493
+ await writeOpenCodePlugin(projectRoot);
34313
34494
  break;
34314
34495
  }
34315
34496
  }
@@ -42963,6 +43144,18 @@ function registerMcp(program2) {
42963
43144
  });
42964
43145
  }
42965
43146
 
43147
+ // src/commands/prime.ts
43148
+ import path8 from "path";
43149
+ function skillPath() {
43150
+ return path8.resolve(import.meta.dir, "..", "skill", "SKILL.md");
43151
+ }
43152
+ function registerPrime(program2) {
43153
+ program2.command("prime").description("Print the pk skill to stdout \u2014 used by harness adapters to inject into system prompt at session start").action(async () => {
43154
+ const text = await Bun.file(skillPath()).text();
43155
+ process.stdout.write(text.replace(/^---[\s\S]*?---\n/v, ""));
43156
+ });
43157
+ }
43158
+
42966
43159
  // src/index.ts
42967
43160
  var program2 = new Command().name("pk").description("Project knowledge \u2014 structured intake, search, and recall").version(package_default.version);
42968
43161
  registerNew(program2);
@@ -42975,4 +43168,5 @@ registerInit(program2);
42975
43168
  registerInstructions(program2);
42976
43169
  registerVocab(program2);
42977
43170
  registerMcp(program2);
43171
+ registerPrime(program2);
42978
43172
  program2.parse();
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@justestif/pk",
3
3
  "type": "module",
4
- "version": "0.1.14",
4
+ "version": "0.2.1",
5
5
  "description": "Project knowledge — structured intake, search, and recall",
6
6
  "bin": {
7
7
  "pk": "dist/index.js"
package/skill/SKILL.md CHANGED
@@ -72,8 +72,6 @@ No MCP tool for status changes. Use your file Edit tool directly on the frontmat
72
72
 
73
73
  **MANDATORY READ `references/knowledge-model.md`** when: creating a note type you haven't used before, unsure which folder a type belongs in, validating frontmatter fields, or unsure which status values are valid for a given type. (Read with your standard file Read tool — these are local skill files, not MCP-accessible.)
74
74
 
75
- **MANDATORY READ `references/git-workflow.md`** when: committing knowledge changes or unsure whether to auto-commit. (Same — standard file Read tool.)
76
-
77
75
  ## NEVER
78
76
 
79
77
  - **NEVER skip `pk_search` before `pk_new`**
@@ -4,17 +4,17 @@
4
4
 
5
5
  This system is project-specific. It is not a personal second brain, a full wiki platform, or a semantic memory database.
6
6
 
7
- The canonical store is plain markdown under `knowledge/`. Beans/issues remain for trackable work; the knowledge base is for durable context and future answers.
7
+ The canonical store is plain markdown files managed via `pk` MCP tools. The root directory is set by `PK_KNOWLEDGE_DIR`. The knowledge base is for durable context and future answers — not for task tracking.
8
8
 
9
9
  ## Folder and Type Rules
10
10
 
11
- | Type | Folder | Purpose | Status values |
11
+ | Type | Subfolder | Purpose | Status values |
12
12
  | --- | --- | --- | --- |
13
- | `source` | `knowledge/sources/` | Provenance and raw/lightly cleaned input | `unprocessed`, `processed`, `archived` |
14
- | `note` | `knowledge/notes/` | Durable project knowledge | `active`, `superseded`, `archived` |
15
- | `decision` | `knowledge/decisions/` | Chosen direction and rationale | `proposed`, `accepted`, `superseded` |
16
- | `question` | `knowledge/questions/` | Unresolved or resolved uncertainty | `open`, `answered`, `obsolete` |
17
- | `index` | `knowledge/indexes/` | Navigation/MOC pages | `active`, `archived` |
13
+ | `source` | `sources/` | Provenance and raw/lightly cleaned input | `unprocessed`, `processed`, `archived` |
14
+ | `note` | `notes/` | Durable project knowledge | `active`, `superseded`, `archived` |
15
+ | `decision` | `decisions/` | Chosen direction and rationale | `proposed`, `accepted`, `superseded` |
16
+ | `question` | `questions/` | Unresolved or resolved uncertainty | `open`, `answered`, `obsolete` |
17
+ | `index` | `indexes/` | Navigation/MOC pages | `active`, `archived` |
18
18
 
19
19
  ## Frontmatter
20
20
 
@@ -34,7 +34,7 @@ tags: [tag-one, tag-two]
34
34
 
35
35
  Rules:
36
36
 
37
- - `id` must be unique across `knowledge/**/*.md`.
37
+ - `id` must be unique across all notes in the knowledge directory.
38
38
  - `type` must match both status set and folder.
39
39
  - `tags` must be a flat list of lowercase slugs.
40
40
  - Do not use nested YAML, multiline YAML, or relationship arrays.
@@ -85,7 +85,7 @@ Classify extracted material this way:
85
85
  - Chosen path with rationale/consequences → `decision`
86
86
  - Unknown that blocks or informs work → `question`
87
87
  - Navigation over a topic/type/tag → `index`
88
- - Action item/task → issue tracker, not a knowledge note
88
+ - Action item/task → not a knowledge note; track elsewhere
89
89
  - Low-signal commentary → ignore or keep only in `source`
90
90
 
91
91
  ## Update Policy
@@ -1,77 +0,0 @@
1
- # Git Workflow for Project Knowledge
2
-
3
- Git is the audit, safety, and review layer for `knowledge/`.
4
-
5
- ## Before Modifying Knowledge
6
-
7
- Check repository state:
8
-
9
- ```bash
10
- git status --short
11
- ```
12
-
13
- Stop before editing if unrelated user changes would be mixed into the same commit. If changes are clearly knowledge-system work in scope, continue.
14
-
15
- ## After Modifying Knowledge
16
-
17
- Run mechanical maintenance:
18
-
19
- ```bash
20
- scripts/knowledge-index
21
- scripts/knowledge-lint
22
- ```
23
-
24
- Review what changed:
25
-
26
- ```bash
27
- git diff -- knowledge scripts/knowledge-* assets/templates hk.pkl
28
- ```
29
-
30
- Summarize:
31
-
32
- - files created
33
- - files updated
34
- - generated indexes
35
- - lint result
36
- - any ignored/deferred material
37
-
38
- ## Auto-Commit Policy
39
-
40
- Auto-commit coherent completed knowledge operations unless a safety stop applies.
41
-
42
- Normal intake commits stage only:
43
-
44
- ```bash
45
- git add knowledge/
46
- ```
47
-
48
- Knowledge-system implementation commits may stage:
49
-
50
- ```bash
51
- git add knowledge/ scripts/knowledge-* assets/templates/ hk.pkl
52
- ```
53
-
54
- Use concise commit messages:
55
-
56
- ```txt
57
- knowledge: intake <topic>
58
- knowledge: update <topic>
59
- knowledge: answer <topic>
60
- knowledge-system: update <topic>
61
- ```
62
-
63
- ## Safety Stops
64
-
65
- Do not auto-commit when:
66
-
67
- - unrelated project files are modified
68
- - lint fails
69
- - the edit deletes or rewrites existing knowledge ambiguously
70
- - the working tree contains user changes outside the allowed commit boundary
71
- - the user says not to commit
72
-
73
- When stopped, report the exact reason and show the staged/unstaged state.
74
-
75
- ## Hooks
76
-
77
- If hk is used, run `knowledge-lint` on pre-commit only when `knowledge/**/*.md` changes. Avoid every-N-commit scheduling; it is arbitrary and less transparent than change-triggered checks.