@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 +9 -10
- package/dist/index.js +260 -66
- package/package.json +1 -1
- package/skill/SKILL.md +0 -2
- package/skill/references/knowledge-model.md +9 -9
- package/skill/references/git-workflow.md +0 -77
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,
|
|
33
|
+
pk init my-project --harness claude,cursor # multiple harnesses
|
|
34
34
|
```
|
|
35
35
|
|
|
36
|
-
Available harnesses: `claude
|
|
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.
|
|
42
|
+
3. Installs a harness adapter that calls `pk prime` at session start to inject the skill into context
|
|
43
43
|
|
|
44
|
-
| Harness |
|
|
44
|
+
| Harness | Files written |
|
|
45
45
|
|---|---|
|
|
46
|
-
| `claude` | `.mcp.json` (
|
|
47
|
-
| `
|
|
48
|
-
| `
|
|
49
|
-
| `
|
|
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
|
|
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
|
|
34116
|
-
{ hint: "
|
|
34117
|
-
{ hint: ".
|
|
34118
|
-
{ hint: ".
|
|
34119
|
-
{ hint: "opencode.json
|
|
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
|
-
|
|
34126
|
-
|
|
34127
|
-
|
|
34128
|
-
|
|
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}:
|
|
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
|
-
|
|
34183
|
-
|
|
34184
|
-
|
|
34185
|
-
|
|
34186
|
-
|
|
34187
|
-
|
|
34188
|
-
|
|
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
|
|
34199
|
-
const
|
|
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
|
-
|
|
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
|
-
|
|
34215
|
-
|
|
34216
|
-
|
|
34217
|
-
|
|
34218
|
-
|
|
34219
|
-
|
|
34220
|
-
|
|
34221
|
-
|
|
34222
|
-
|
|
34223
|
-
|
|
34224
|
-
|
|
34225
|
-
|
|
34226
|
-
|
|
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 "
|
|
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
|
|
34463
|
+
const { name, knowledgeDir, projectRoot } = ctx;
|
|
34290
34464
|
switch (harness) {
|
|
34291
34465
|
case "claude": {
|
|
34292
34466
|
await writeClaudeConfig(projectRoot, name, knowledgeDir);
|
|
34293
|
-
|
|
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 "
|
|
34308
|
-
await
|
|
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
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
|
|
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 |
|
|
11
|
+
| Type | Subfolder | Purpose | Status values |
|
|
12
12
|
| --- | --- | --- | --- |
|
|
13
|
-
| `source` | `
|
|
14
|
-
| `note` | `
|
|
15
|
-
| `decision` | `
|
|
16
|
-
| `question` | `
|
|
17
|
-
| `index` | `
|
|
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
|
|
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 →
|
|
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.
|