@justestif/pk 0.1.14 → 0.2.0

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,25 @@ 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,omp # multiple harnesses
34
34
  ```
35
35
 
36
- Available harnesses: `claude`, `claude-desktop`, `omp`, `cursor`, `opencode`, `codex`.
36
+ Available harnesses: `claude` (Claude Code), `omp` (Oh My Pi), `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` |
47
+ | `omp` | `.omp/mcp.json`, `AGENTS.md`, `.omp/extensions/pk-eval.ts` |
48
+ | `cursor` | `.cursor/mcp.json`, `.cursor/rules/pk.mdc`, `.cursor/hooks/pk-eval.sh`, `.cursor/hooks.json` |
49
+ | `gemini` | `.gemini/settings.json`, `GEMINI.md`, `.gemini/hooks/pk-eval.sh` |
50
+ | `codex` | `.codex/config.toml`, `AGENTS.md`, `.codex/hooks/pk-eval.sh`, `.codex/hooks.json` |
51
+ | `opencode` | `opencode.json`, `AGENTS.md`, `CLAUDE.md`, `.opencode/plugins/pk-eval.ts` |
52
52
 
53
53
  ## How it works
54
54
 
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.0",
17717
17717
  description: "Project knowledge \u2014 structured intake, search, and recall",
17718
17718
  bin: {
17719
17719
  pk: "dist/index.js"
@@ -34112,28 +34112,28 @@ ${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: ".omp/mcp.json + AGENTS.md + forced-eval hook", label: "Oh My Pi", value: "omp" },
34117
+ { hint: ".cursor/mcp.json + .cursor/rules/pk.mdc + hook", label: "Cursor", value: "cursor" },
34118
+ { hint: ".gemini/settings.json + GEMINI.md + hook", label: "Gemini CLI", value: "gemini" },
34119
+ { hint: ".codex/config.toml + AGENTS.md + hook", label: "Codex", value: "codex" },
34120
+ { hint: "opencode.json + plugin (reads AGENTS.md/CLAUDE.md)", label: "OpenCode", value: "opencode" }
34121
34121
  ];
34122
34122
  var HARNESS_VALUES = new Set(HARNESSES.map((h2) => h2.value));
34123
34123
  var HARNESS_ACTIVATION = {
34124
34124
  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
34125
  omp: "restart your Oh My Pi session",
34129
- opencode: "restart opencode"
34126
+ cursor: "restart Cursor for changes to take effect",
34127
+ gemini: "restart your Gemini CLI session",
34128
+ codex: "restart Codex for MCP to connect",
34129
+ opencode: "reload OpenCode or restart the app"
34130
34130
  };
34131
34131
  function buildOutro(created, knowledgeDir, harnesses, skillPaths) {
34132
34132
  const lines = [
34133
34133
  created ? `Created project: ${knowledgeDir}` : `Connected to existing project: ${knowledgeDir}`
34134
34134
  ];
34135
34135
  for (const h2 of harnesses) {
34136
- lines.push(` ${h2}: MCP config written \u2192 ${HARNESS_ACTIVATION[h2]}`);
34136
+ lines.push(` ${h2}: configured \u2192 ${HARNESS_ACTIVATION[h2]}`);
34137
34137
  }
34138
34138
  for (const sp of skillPaths) {
34139
34139
  lines.push(` skill installed to ${sp}`);
@@ -34179,14 +34179,96 @@ async function writeClaudeConfig(projectRoot, _name, knowledgeDir) {
34179
34179
  cfg.mcpServers = servers;
34180
34180
  await writeJson(cfgPath, cfg);
34181
34181
  }
34182
- async function writeClaudeDesktopConfig(homeDir, name, knowledgeDir) {
34183
- const cfgPath = path7.join(homeDir, "Library", "Application Support", "Claude", "claude_desktop_config.json");
34182
+ async function writeOmpConfig(projectRoot, _name, knowledgeDir) {
34183
+ const cfgPath = path7.join(projectRoot, ".omp", "mcp.json");
34184
34184
  const cfg = await readJson(cfgPath);
34185
34185
  const servers = cfg.mcpServers ?? {};
34186
- servers[`pk-${name}`] = pkMcpEntry(knowledgeDir);
34186
+ servers.pk = pkMcpEntry(knowledgeDir);
34187
34187
  cfg.mcpServers = servers;
34188
34188
  await writeJson(cfgPath, cfg);
34189
34189
  }
34190
+ var PK_SECTION_START = "<!-- pk:start -->";
34191
+ var PK_SECTION_END = "<!-- pk:end -->";
34192
+ var PK_INSTRUCTION = `## pk \u2014 project knowledge
34193
+
34194
+ 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.`;
34195
+ async function writeInstructionSection(filePath, content) {
34196
+ const section = `${PK_SECTION_START}
34197
+ ${content}
34198
+ ${PK_SECTION_END}
34199
+ `;
34200
+ let existing = "";
34201
+ try {
34202
+ existing = await Bun.file(filePath).text();
34203
+ } catch {}
34204
+ const startIdx = existing.indexOf(PK_SECTION_START);
34205
+ const endIdx = existing.indexOf(PK_SECTION_END);
34206
+ let updated;
34207
+ if (startIdx !== -1 && endIdx !== -1) {
34208
+ updated = existing.slice(0, startIdx) + section + existing.slice(endIdx + PK_SECTION_END.length + 1);
34209
+ } else {
34210
+ updated = existing ? existing.trimEnd() + `
34211
+
34212
+ ` + section : section;
34213
+ }
34214
+ mkdirSync4(path7.dirname(filePath), { recursive: true });
34215
+ await Bun.write(filePath, updated);
34216
+ }
34217
+ async function writeClaudeMd(projectRoot) {
34218
+ await writeInstructionSection(path7.join(projectRoot, "CLAUDE.md"), PK_INSTRUCTION);
34219
+ }
34220
+ async function writeAgentsMd(projectRoot) {
34221
+ await writeInstructionSection(path7.join(projectRoot, "AGENTS.md"), PK_INSTRUCTION);
34222
+ }
34223
+ 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.";
34224
+ function claudeHookScript() {
34225
+ return `// pk forced-eval hook \u2014 auto-generated by pk init
34226
+ async function handleUserPromptSubmit() {
34227
+ console.log(JSON.stringify({
34228
+ hookSpecificOutput: {
34229
+ hookEventName: 'UserPromptSubmit',
34230
+ additionalContext: ${JSON.stringify(FORCED_EVAL_PROMPT)},
34231
+ },
34232
+ suppressOutput: true,
34233
+ }));
34234
+ }
34235
+
34236
+ handleUserPromptSubmit().catch(() => process.exit(0));
34237
+ `;
34238
+ }
34239
+ async function writeClaudeHook(projectRoot) {
34240
+ const hookDir = path7.join(projectRoot, ".claude", "hooks");
34241
+ const hookPath = path7.join(hookDir, "pk-eval.ts");
34242
+ mkdirSync4(hookDir, { recursive: true });
34243
+ await Bun.write(hookPath, claudeHookScript());
34244
+ const settingsPath = path7.join(projectRoot, ".claude", "settings.json");
34245
+ const settings = await readJson(settingsPath);
34246
+ const hooks = settings.hooks ?? {};
34247
+ const ups = hooks.UserPromptSubmit ?? [];
34248
+ const hookCmd = `bun run ${hookPath}`;
34249
+ if (!ups.some((h2) => h2.command === hookCmd)) {
34250
+ ups.push({ command: hookCmd });
34251
+ }
34252
+ hooks.UserPromptSubmit = ups;
34253
+ settings.hooks = hooks;
34254
+ await writeJson(settingsPath, settings);
34255
+ }
34256
+ function ompHookScript() {
34257
+ return `// pk forced-eval hook \u2014 auto-generated by pk init
34258
+ import type {HookAPI} from '@oh-my-pi/pi-coding-agent/extensibility/hooks';
34259
+
34260
+ export default function (pi: HookAPI) {
34261
+ pi.on('before_agent_start', (event: {systemPrompt?: string}) => ({
34262
+ systemPrompt: ${JSON.stringify(FORCED_EVAL_PROMPT)} + '\\n\\n' + (event.systemPrompt ?? ''),
34263
+ }));
34264
+ }
34265
+ `;
34266
+ }
34267
+ async function writeOmpHook(projectRoot) {
34268
+ const hookPath = path7.join(projectRoot, ".omp", "extensions", "pk-eval.ts");
34269
+ mkdirSync4(path7.dirname(hookPath), { recursive: true });
34270
+ await Bun.write(hookPath, ompHookScript());
34271
+ }
34190
34272
  async function writeCursorConfig(projectRoot, _name, knowledgeDir) {
34191
34273
  const cfgPath = path7.join(projectRoot, ".cursor", "mcp.json");
34192
34274
  const cfg = await readJson(cfgPath);
@@ -34195,62 +34277,171 @@ async function writeCursorConfig(projectRoot, _name, knowledgeDir) {
34195
34277
  cfg.mcpServers = servers;
34196
34278
  await writeJson(cfgPath, cfg);
34197
34279
  }
34198
- async function writeOmpConfig(projectRoot, _name, knowledgeDir) {
34199
- const cfgPath = path7.join(projectRoot, ".omp", "mcp.json");
34280
+ async function writeCursorRules(projectRoot) {
34281
+ const rulesPath = path7.join(projectRoot, ".cursor", "rules", "pk.mdc");
34282
+ const content = `---
34283
+ description: "pk knowledge base integration"
34284
+ alwaysApply: true
34285
+ ---
34286
+
34287
+ ${PK_INSTRUCTION}
34288
+ `;
34289
+ mkdirSync4(path7.dirname(rulesPath), { recursive: true });
34290
+ await Bun.write(rulesPath, content);
34291
+ }
34292
+ function cursorHookScript() {
34293
+ return `#!/bin/bash
34294
+ # pk forced-eval hook \u2014 auto-generated by pk init
34295
+ # Exit 0 to allow, exit 2 to block
34296
+
34297
+ # Return the forced-eval prompt as additional context
34298
+ cat <<EOF
34299
+ {
34300
+ "continue": true,
34301
+ "additionalContext": ${JSON.stringify(FORCED_EVAL_PROMPT)}
34302
+ }
34303
+ EOF
34304
+ `;
34305
+ }
34306
+ async function writeCursorHook(projectRoot) {
34307
+ const hookPath = path7.join(projectRoot, ".cursor", "hooks", "pk-eval.sh");
34308
+ mkdirSync4(path7.dirname(hookPath), { recursive: true });
34309
+ await Bun.write(hookPath, cursorHookScript());
34310
+ const hooksPath = path7.join(projectRoot, ".cursor", "hooks.json");
34311
+ const hooks = await readJson(hooksPath);
34312
+ const hooksObj = hooks.hooks ?? {};
34313
+ const bsp = hooksObj.beforeSubmitPrompt ?? [];
34314
+ if (!bsp.some((h2) => h2.command === hookPath)) {
34315
+ bsp.push({ command: hookPath });
34316
+ }
34317
+ hooksObj.beforeSubmitPrompt = bsp;
34318
+ hooks.hooks = hooksObj;
34319
+ await writeJson(hooksPath, hooks);
34320
+ }
34321
+ async function writeGeminiConfig(projectRoot, _name, knowledgeDir) {
34322
+ const cfgPath = path7.join(projectRoot, ".gemini", "settings.json");
34200
34323
  const cfg = await readJson(cfgPath);
34201
34324
  const servers = cfg.mcpServers ?? {};
34202
34325
  servers.pk = pkMcpEntry(knowledgeDir);
34203
34326
  cfg.mcpServers = servers;
34204
34327
  await writeJson(cfgPath, cfg);
34205
34328
  }
34329
+ async function writeGeminiMd(projectRoot) {
34330
+ await writeInstructionSection(path7.join(projectRoot, "GEMINI.md"), PK_INSTRUCTION);
34331
+ }
34332
+ function geminiHookScript() {
34333
+ return `#!/bin/bash
34334
+ # pk forced-eval hook \u2014 auto-generated by pk init
34335
+ # Output JSON to stdout with the forced-eval prompt
34336
+
34337
+ cat <<EOF
34338
+ {
34339
+ "hookSpecificOutput": {
34340
+ "hookEventName": "BeforeAgent",
34341
+ "additionalContext": ${JSON.stringify(FORCED_EVAL_PROMPT)}
34342
+ }
34343
+ }
34344
+ EOF
34345
+ `;
34346
+ }
34347
+ async function writeGeminiHook(projectRoot) {
34348
+ const hookPath = path7.join(projectRoot, ".gemini", "hooks", "pk-eval.sh");
34349
+ mkdirSync4(path7.dirname(hookPath), { recursive: true });
34350
+ await Bun.write(hookPath, geminiHookScript());
34351
+ const cfgPath = path7.join(projectRoot, ".gemini", "settings.json");
34352
+ const cfg = await readJson(cfgPath);
34353
+ const hooks = cfg.hooks ?? {};
34354
+ const beforeAgent = hooks.BeforeAgent ?? [];
34355
+ if (!beforeAgent.some((h2) => h2.command === hookPath)) {
34356
+ beforeAgent.push({ command: hookPath });
34357
+ }
34358
+ hooks.BeforeAgent = beforeAgent;
34359
+ cfg.hooks = hooks;
34360
+ await writeJson(cfgPath, cfg);
34361
+ }
34362
+ async function writeToml(filePath, content) {
34363
+ mkdirSync4(path7.dirname(filePath), { recursive: true });
34364
+ await Bun.write(filePath, content);
34365
+ }
34366
+ async function writeCodexConfig(projectRoot, _name, knowledgeDir) {
34367
+ const cfgPath = path7.join(projectRoot, ".codex", "config.toml");
34368
+ const pkCmd = resolvePkCommand();
34369
+ const toml = `[mcp_servers.pk]
34370
+ command = "${pkCmd}"
34371
+ args = ["mcp"]
34372
+ env = { PK_KNOWLEDGE_DIR = "${knowledgeDir}" }
34373
+ `;
34374
+ await writeToml(cfgPath, toml);
34375
+ }
34376
+ function codexHookScript() {
34377
+ return `#!/bin/bash
34378
+ # pk forced-eval hook \u2014 auto-generated by pk init
34379
+ # Output JSON to stdout
34380
+
34381
+ cat <<EOF
34382
+ {
34383
+ "continue": true,
34384
+ "suppressOutput": false
34385
+ }
34386
+ EOF
34387
+ `;
34388
+ }
34389
+ async function writeCodexHook(projectRoot) {
34390
+ const hookPath = path7.join(projectRoot, ".codex", "hooks", "pk-eval.sh");
34391
+ mkdirSync4(path7.dirname(hookPath), { recursive: true });
34392
+ await Bun.write(hookPath, codexHookScript());
34393
+ const hooksPath = path7.join(projectRoot, ".codex", "hooks.json");
34394
+ const hooks = await readJson(hooksPath);
34395
+ const hooksObj = hooks.hooks ?? {};
34396
+ const ups = hooksObj.UserPromptSubmit ?? [];
34397
+ if (!ups.some((h2) => typeof h2 === "object" && h2 !== null && ("command" in h2) && typeof h2.command === "string" && h2.command.includes("pk-eval"))) {
34398
+ ups.push({ command: hookPath });
34399
+ }
34400
+ hooksObj.UserPromptSubmit = ups;
34401
+ hooks.hooks = hooksObj;
34402
+ await writeJson(hooksPath, hooks);
34403
+ }
34206
34404
  async function writeOpenCodeConfig(projectRoot, _name, knowledgeDir) {
34207
34405
  const cfgPath = path7.join(projectRoot, "opencode.json");
34208
34406
  const cfg = await readJson(cfgPath);
34209
34407
  const mcp = cfg.mcp ?? {};
34210
- mcp.pk = pkMcpEntry(knowledgeDir);
34408
+ const pkCmd = resolvePkCommand();
34409
+ mcp.pk = {
34410
+ type: "local",
34411
+ enabled: true,
34412
+ command: [pkCmd, "mcp"],
34413
+ environment: { PK_KNOWLEDGE_DIR: knowledgeDir }
34414
+ };
34211
34415
  cfg.mcp = mcp;
34212
34416
  await writeJson(cfgPath, cfg);
34213
34417
  }
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
- }
34418
+ function openCodePluginScript() {
34419
+ return `// pk forced-eval plugin \u2014 auto-generated by pk init
34420
+ export const experimental = {
34421
+ async 'chat.system.transform'({ system }: { system: string[] }): Promise<void> {
34422
+ system.unshift(${JSON.stringify(FORCED_EVAL_PROMPT)});
34423
+ },
34424
+ };
34425
+ `;
34426
+ }
34427
+ async function writeOpenCodePlugin(projectRoot) {
34428
+ const pluginPath = path7.join(projectRoot, ".opencode", "plugins", "pk-eval.ts");
34429
+ mkdirSync4(path7.dirname(pluginPath), { recursive: true });
34430
+ await Bun.write(pluginPath, openCodePluginScript());
34238
34431
  }
34239
34432
  function skillTargetDir(harness, projectRoot) {
34240
34433
  switch (harness) {
34241
- case "claude":
34242
- case "claude-desktop": {
34434
+ case "claude": {
34243
34435
  return path7.join(os3.homedir(), ".claude", "skills", "pk");
34244
34436
  }
34245
- case "omp": {
34437
+ case "omp":
34438
+ case "cursor":
34439
+ case "gemini":
34440
+ case "opencode": {
34246
34441
  return path7.join(projectRoot, ".agents", "skills", "pk");
34247
34442
  }
34248
- case "cursor": {
34249
- return path7.join(projectRoot, ".cursor", "skills", "pk");
34250
- }
34251
- case "opencode":
34252
34443
  case "codex": {
34253
- return "";
34444
+ return path7.join(os3.homedir(), ".codex", "skills", "pk");
34254
34445
  }
34255
34446
  }
34256
34447
  }
@@ -34286,30 +34477,43 @@ async function ensureProject(name) {
34286
34477
  return { created: !alreadyExists, knowledgeDir: kDir };
34287
34478
  }
34288
34479
  async function applyHarness(harness, ctx) {
34289
- const { name, knowledgeDir, projectRoot, home } = ctx;
34480
+ const { name, knowledgeDir, projectRoot } = ctx;
34290
34481
  switch (harness) {
34291
34482
  case "claude": {
34292
34483
  await writeClaudeConfig(projectRoot, name, knowledgeDir);
34484
+ await writeClaudeMd(projectRoot);
34485
+ await writeClaudeHook(projectRoot);
34293
34486
  break;
34294
34487
  }
34295
- case "claude-desktop": {
34296
- await writeClaudeDesktopConfig(home, name, knowledgeDir);
34297
- break;
34298
- }
34299
- case "codex": {
34300
- await writeCodexConfig(projectRoot, name, knowledgeDir);
34488
+ case "omp": {
34489
+ await writeOmpConfig(projectRoot, name, knowledgeDir);
34490
+ await writeAgentsMd(projectRoot);
34491
+ await writeOmpHook(projectRoot);
34301
34492
  break;
34302
34493
  }
34303
34494
  case "cursor": {
34304
34495
  await writeCursorConfig(projectRoot, name, knowledgeDir);
34496
+ await writeCursorRules(projectRoot);
34497
+ await writeCursorHook(projectRoot);
34305
34498
  break;
34306
34499
  }
34307
- case "omp": {
34308
- await writeOmpConfig(projectRoot, name, knowledgeDir);
34500
+ case "gemini": {
34501
+ await writeGeminiConfig(projectRoot, name, knowledgeDir);
34502
+ await writeGeminiMd(projectRoot);
34503
+ await writeGeminiHook(projectRoot);
34504
+ break;
34505
+ }
34506
+ case "codex": {
34507
+ await writeCodexConfig(projectRoot, name, knowledgeDir);
34508
+ await writeAgentsMd(projectRoot);
34509
+ await writeCodexHook(projectRoot);
34309
34510
  break;
34310
34511
  }
34311
34512
  case "opencode": {
34312
34513
  await writeOpenCodeConfig(projectRoot, name, knowledgeDir);
34514
+ await writeAgentsMd(projectRoot);
34515
+ await writeClaudeMd(projectRoot);
34516
+ await writeOpenCodePlugin(projectRoot);
34313
34517
  break;
34314
34518
  }
34315
34519
  }
@@ -42963,6 +43167,18 @@ function registerMcp(program2) {
42963
43167
  });
42964
43168
  }
42965
43169
 
43170
+ // src/commands/prime.ts
43171
+ import path8 from "path";
43172
+ function skillPath() {
43173
+ return path8.resolve(import.meta.dir, "..", "skill", "SKILL.md");
43174
+ }
43175
+ function registerPrime(program2) {
43176
+ 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 () => {
43177
+ const text = await Bun.file(skillPath()).text();
43178
+ process.stdout.write(text.replace(/^---[\s\S]*?---\n/v, ""));
43179
+ });
43180
+ }
43181
+
42966
43182
  // src/index.ts
42967
43183
  var program2 = new Command().name("pk").description("Project knowledge \u2014 structured intake, search, and recall").version(package_default.version);
42968
43184
  registerNew(program2);
@@ -42975,4 +43191,5 @@ registerInit(program2);
42975
43191
  registerInstructions(program2);
42976
43192
  registerVocab(program2);
42977
43193
  registerMcp(program2);
43194
+ registerPrime(program2);
42978
43195
  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.0",
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.