@justestif/pk 0.4.0 → 0.5.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 +8 -8
- package/dist/index.js +105 -95
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -42,22 +42,22 @@ Non-interactive:
|
|
|
42
42
|
|
|
43
43
|
```bash
|
|
44
44
|
pk init my-project --harness claude
|
|
45
|
-
pk init my-project --harness claude,
|
|
45
|
+
pk init my-project --harness claude,opencode # multiple harnesses
|
|
46
46
|
```
|
|
47
47
|
|
|
48
|
-
Available harnesses: `claude` (Claude Code), `
|
|
48
|
+
Available harnesses: `claude` (Claude Code), `opencode` (OpenCode), `pi` (Pi).
|
|
49
49
|
|
|
50
50
|
`pk init` does three things:
|
|
51
51
|
|
|
52
52
|
1. Creates `~/.pk/<name>/` as the knowledge home for this project
|
|
53
|
-
2. Installs a hook or plugin that
|
|
53
|
+
2. Installs a hook or plugin that injects `PK_KNOWLEDGE_DIR` into the shell and calls `pk prime` for context
|
|
54
54
|
3. Installs the pk skill so your agent knows how to use the CLI
|
|
55
55
|
|
|
56
|
-
| Harness | Files written
|
|
57
|
-
| ---------- |
|
|
58
|
-
| `claude` | `.claude/hooks/pk-eval.ts`, `.claude/settings.json
|
|
59
|
-
| `
|
|
60
|
-
| `
|
|
56
|
+
| Harness | Files written | Env injection |
|
|
57
|
+
| ---------- | -------------------------------------------------------------------------- | ------------------------------------ |
|
|
58
|
+
| `claude` | `.claude/hooks/pk-session-start.sh`, `pk-eval.ts`, `.claude/settings.json`| `SessionStart` → `$CLAUDE_ENV_FILE` |
|
|
59
|
+
| `opencode` | `.opencode/plugins/pk-eval.ts` | `shell.env` plugin hook |
|
|
60
|
+
| `pi` | `.pi/extensions/pk-eval.ts` | `tool_call` mutation |
|
|
61
61
|
|
|
62
62
|
## Commands
|
|
63
63
|
|
package/dist/index.js
CHANGED
|
@@ -36019,12 +36019,19 @@ async function writeJson2(filePath, data) {
|
|
|
36019
36019
|
await Bun.write(filePath, JSON.stringify(data, null, 2) + `
|
|
36020
36020
|
`);
|
|
36021
36021
|
}
|
|
36022
|
-
function
|
|
36022
|
+
function sessionStartScript(knowledgeDir) {
|
|
36023
|
+
return `#!/bin/bash
|
|
36024
|
+
# pk hook \u2014 auto-generated by pk init
|
|
36025
|
+
if [ -n "$CLAUDE_ENV_FILE" ]; then
|
|
36026
|
+
echo 'export PK_KNOWLEDGE_DIR=${knowledgeDir}' >> "$CLAUDE_ENV_FILE"
|
|
36027
|
+
fi
|
|
36028
|
+
exit 0
|
|
36029
|
+
`;
|
|
36030
|
+
}
|
|
36031
|
+
function userPromptSubmitScript() {
|
|
36023
36032
|
return `// pk hook \u2014 auto-generated by pk init
|
|
36024
36033
|
async function handleUserPromptSubmit() {
|
|
36025
|
-
const proc = Bun.spawnSync(['pk', 'prime']
|
|
36026
|
-
env: { ...process.env, PK_KNOWLEDGE_DIR: '${knowledgeDir}' }
|
|
36027
|
-
});
|
|
36034
|
+
const proc = Bun.spawnSync(['pk', 'prime']);
|
|
36028
36035
|
const output = proc.stdout?.toString().trim() ?? '';
|
|
36029
36036
|
console.log(JSON.stringify({
|
|
36030
36037
|
hookSpecificOutput: {
|
|
@@ -36038,116 +36045,107 @@ async function handleUserPromptSubmit() {
|
|
|
36038
36045
|
handleUserPromptSubmit().catch(() => process.exit(0));
|
|
36039
36046
|
`;
|
|
36040
36047
|
}
|
|
36048
|
+
function addHookEntry(hooks, event, command) {
|
|
36049
|
+
const entries = hooks[event] ?? [];
|
|
36050
|
+
const wrappedHook = { matcher: "", hooks: [{ type: "command", command }] };
|
|
36051
|
+
const alreadyPresent = (entry) => Array.isArray(entry.hooks) && entry.hooks.some((h2) => typeof h2 === "object" && h2 !== null && h2.command === command);
|
|
36052
|
+
if (!entries.some(alreadyPresent)) {
|
|
36053
|
+
entries.push(wrappedHook);
|
|
36054
|
+
}
|
|
36055
|
+
hooks[event] = entries;
|
|
36056
|
+
}
|
|
36041
36057
|
async function writeClaudeHook(projectRoot, knowledgeDir) {
|
|
36042
36058
|
const hookDir = path9.join(projectRoot, ".claude", "hooks");
|
|
36043
|
-
const hookPath = path9.join(hookDir, "pk-eval.ts");
|
|
36044
36059
|
mkdirSync4(hookDir, { recursive: true });
|
|
36045
|
-
|
|
36060
|
+
const sessionStartPath = path9.join(hookDir, "pk-session-start.sh");
|
|
36061
|
+
await Bun.write(sessionStartPath, sessionStartScript(knowledgeDir));
|
|
36062
|
+
const { chmodSync } = await import("fs");
|
|
36063
|
+
chmodSync(sessionStartPath, 493);
|
|
36064
|
+
const evalPath = path9.join(hookDir, "pk-eval.ts");
|
|
36065
|
+
await Bun.write(evalPath, userPromptSubmitScript());
|
|
36046
36066
|
const settingsPath = path9.join(projectRoot, ".claude", "settings.json");
|
|
36047
36067
|
const settings = await readJson(settingsPath);
|
|
36048
36068
|
const hooks = settings.hooks ?? {};
|
|
36049
|
-
|
|
36050
|
-
|
|
36051
|
-
const wrappedHook = {
|
|
36052
|
-
matcher: "",
|
|
36053
|
-
hooks: [
|
|
36054
|
-
{
|
|
36055
|
-
type: "command",
|
|
36056
|
-
command: hookCmd
|
|
36057
|
-
}
|
|
36058
|
-
]
|
|
36059
|
-
};
|
|
36060
|
-
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);
|
|
36061
|
-
if (!ups.some(hasPkEval)) {
|
|
36062
|
-
ups.push(wrappedHook);
|
|
36063
|
-
}
|
|
36064
|
-
hooks.UserPromptSubmit = ups;
|
|
36069
|
+
addHookEntry(hooks, "SessionStart", sessionStartPath);
|
|
36070
|
+
addHookEntry(hooks, "UserPromptSubmit", `bun run ${evalPath}`);
|
|
36065
36071
|
settings.hooks = hooks;
|
|
36066
36072
|
await writeJson2(settingsPath, settings);
|
|
36067
36073
|
}
|
|
36068
36074
|
|
|
36069
|
-
// src/commands/harnesses/
|
|
36075
|
+
// src/commands/harnesses/opencode.ts
|
|
36070
36076
|
import { mkdirSync as mkdirSync5 } from "fs";
|
|
36071
36077
|
import path10 from "path";
|
|
36072
|
-
|
|
36073
|
-
|
|
36074
|
-
|
|
36075
|
-
|
|
36076
|
-
|
|
36077
|
-
|
|
36078
|
-
|
|
36079
|
-
|
|
36080
|
-
|
|
36081
|
-
|
|
36082
|
-
|
|
36083
|
-
|
|
36084
|
-
|
|
36085
|
-
|
|
36086
|
-
|
|
36087
|
-
|
|
36088
|
-
|
|
36089
|
-
|
|
36090
|
-
async function writeInstructionSection(filePath, content) {
|
|
36091
|
-
const section = `${PK_SECTION_START}
|
|
36092
|
-
${content}
|
|
36093
|
-
${PK_SECTION_END}
|
|
36078
|
+
function openCodePluginScript(knowledgeDir) {
|
|
36079
|
+
return `// pk plugin \u2014 auto-generated by pk init
|
|
36080
|
+
export const pkPlugin = async (_input: unknown) => ({
|
|
36081
|
+
'shell.env': async (
|
|
36082
|
+
_input: unknown,
|
|
36083
|
+
output: { env: Record<string, string> },
|
|
36084
|
+
): Promise<void> => {
|
|
36085
|
+
output.env.PK_KNOWLEDGE_DIR = '${knowledgeDir}';
|
|
36086
|
+
},
|
|
36087
|
+
'experimental.chat.system.transform': async (
|
|
36088
|
+
_input: unknown,
|
|
36089
|
+
output: { system: string[] },
|
|
36090
|
+
): Promise<void> => {
|
|
36091
|
+
const proc = Bun.spawnSync(['pk', 'prime']);
|
|
36092
|
+
const text = proc.stdout?.toString().trim() ?? '';
|
|
36093
|
+
if (text) output.system.unshift(text);
|
|
36094
|
+
},
|
|
36095
|
+
});
|
|
36094
36096
|
`;
|
|
36095
|
-
let existing = "";
|
|
36096
|
-
try {
|
|
36097
|
-
existing = await Bun.file(filePath).text();
|
|
36098
|
-
} catch {}
|
|
36099
|
-
const startIdx = existing.indexOf(PK_SECTION_START);
|
|
36100
|
-
const endIdx = existing.indexOf(PK_SECTION_END);
|
|
36101
|
-
let updated;
|
|
36102
|
-
if (startIdx !== -1 && endIdx !== -1) {
|
|
36103
|
-
updated = existing.slice(0, startIdx) + section + existing.slice(endIdx + PK_SECTION_END.length + 1);
|
|
36104
|
-
} else {
|
|
36105
|
-
updated = existing ? existing.trimEnd() + `
|
|
36106
|
-
|
|
36107
|
-
` + section : section;
|
|
36108
|
-
}
|
|
36109
|
-
mkdirSync5(path10.dirname(filePath), { recursive: true });
|
|
36110
|
-
await Bun.write(filePath, updated);
|
|
36111
36097
|
}
|
|
36112
|
-
async function
|
|
36113
|
-
|
|
36098
|
+
async function writeOpenCodePlugin(projectRoot, knowledgeDir) {
|
|
36099
|
+
const pluginPath = path10.join(projectRoot, ".opencode", "plugins", "pk-eval.ts");
|
|
36100
|
+
mkdirSync5(path10.dirname(pluginPath), { recursive: true });
|
|
36101
|
+
await Bun.write(pluginPath, openCodePluginScript(knowledgeDir));
|
|
36114
36102
|
}
|
|
36115
36103
|
|
|
36116
|
-
// src/commands/harnesses/
|
|
36104
|
+
// src/commands/harnesses/pi.ts
|
|
36117
36105
|
import { mkdirSync as mkdirSync6 } from "fs";
|
|
36118
36106
|
import path11 from "path";
|
|
36119
|
-
function
|
|
36120
|
-
return `// pk
|
|
36121
|
-
|
|
36122
|
-
|
|
36123
|
-
|
|
36124
|
-
|
|
36125
|
-
|
|
36126
|
-
|
|
36127
|
-
|
|
36128
|
-
|
|
36129
|
-
|
|
36130
|
-
|
|
36131
|
-
|
|
36107
|
+
function piPluginScript(knowledgeDir) {
|
|
36108
|
+
return `// pk extension \u2014 auto-generated by pk init
|
|
36109
|
+
import { spawnSync } from 'node:child_process';
|
|
36110
|
+
import type { ExtensionAPI } from '@earendil-works/pi-coding-agent';
|
|
36111
|
+
|
|
36112
|
+
export default function (pi: ExtensionAPI) {
|
|
36113
|
+
pi.on('before_agent_start', async (event, _ctx) => {
|
|
36114
|
+
const proc = spawnSync('pk', ['prime'], {
|
|
36115
|
+
env: { ...process.env, PK_KNOWLEDGE_DIR: '${knowledgeDir}' },
|
|
36116
|
+
encoding: 'utf-8',
|
|
36117
|
+
});
|
|
36118
|
+
const output = proc.stdout?.trim() ?? '';
|
|
36119
|
+
return output
|
|
36120
|
+
? { systemPrompt: output + '\\n\\n' + event.systemPrompt }
|
|
36121
|
+
: undefined;
|
|
36122
|
+
});
|
|
36123
|
+
|
|
36124
|
+
pi.on('tool_call', async (event, _ctx) => {
|
|
36125
|
+
if (event.toolName === 'bash' && event.input?.command) {
|
|
36126
|
+
event.input.command = 'export PK_KNOWLEDGE_DIR=${knowledgeDir}\\n' + event.input.command;
|
|
36127
|
+
}
|
|
36128
|
+
});
|
|
36129
|
+
}
|
|
36132
36130
|
`;
|
|
36133
36131
|
}
|
|
36134
|
-
async function
|
|
36135
|
-
const pluginPath = path11.join(projectRoot, ".
|
|
36132
|
+
async function writePiPlugin(projectRoot, knowledgeDir) {
|
|
36133
|
+
const pluginPath = path11.join(projectRoot, ".pi", "extensions", "pk-eval.ts");
|
|
36136
36134
|
mkdirSync6(path11.dirname(pluginPath), { recursive: true });
|
|
36137
|
-
await Bun.write(pluginPath,
|
|
36135
|
+
await Bun.write(pluginPath, piPluginScript(knowledgeDir));
|
|
36138
36136
|
}
|
|
36139
36137
|
|
|
36140
36138
|
// src/lib/project.ts
|
|
36141
36139
|
var HARNESSES = [
|
|
36142
|
-
{ hint: "
|
|
36143
|
-
{ hint: "
|
|
36144
|
-
{ hint: "
|
|
36140
|
+
{ hint: "SessionStart env + UserPromptSubmit context", label: "Claude Code", value: "claude" },
|
|
36141
|
+
{ hint: "shell.env + chat.system.transform plugin", label: "OpenCode", value: "opencode" },
|
|
36142
|
+
{ hint: "tool_call env + before_agent_start context", label: "Pi", value: "pi" }
|
|
36145
36143
|
];
|
|
36146
36144
|
var HARNESS_VALUES = new Set(HARNESSES.map((h2) => h2.value));
|
|
36147
36145
|
var HARNESS_ACTIVATION = {
|
|
36148
36146
|
claude: "start a new Claude Code session in this project",
|
|
36149
|
-
|
|
36150
|
-
|
|
36147
|
+
opencode: "reload OpenCode or restart the app",
|
|
36148
|
+
pi: "start a new Pi session in this project"
|
|
36151
36149
|
};
|
|
36152
36150
|
async function ensureProject(name21) {
|
|
36153
36151
|
const kDir = projectDir(name21);
|
|
@@ -36206,14 +36204,12 @@ async function initializeProject(options2) {
|
|
|
36206
36204
|
function skillTargetDir(harness, projectRoot) {
|
|
36207
36205
|
switch (harness) {
|
|
36208
36206
|
case "claude": {
|
|
36209
|
-
return path12.join(
|
|
36207
|
+
return path12.join(projectRoot, ".claude", "skills", "pk");
|
|
36210
36208
|
}
|
|
36211
|
-
case "opencode":
|
|
36209
|
+
case "opencode":
|
|
36210
|
+
case "pi": {
|
|
36212
36211
|
return path12.join(projectRoot, ".agents", "skills", "pk");
|
|
36213
36212
|
}
|
|
36214
|
-
case "codex": {
|
|
36215
|
-
return path12.join(os3.homedir(), ".codex", "skills", "pk");
|
|
36216
|
-
}
|
|
36217
36213
|
}
|
|
36218
36214
|
}
|
|
36219
36215
|
function skillSourceDir() {
|
|
@@ -36241,14 +36237,14 @@ async function applyHarness(harness, ctx) {
|
|
|
36241
36237
|
await writeClaudeHook(projectRoot, knowledgeDir);
|
|
36242
36238
|
break;
|
|
36243
36239
|
}
|
|
36244
|
-
case "codex": {
|
|
36245
|
-
await writeAgentsMd(projectRoot, knowledgeDir);
|
|
36246
|
-
break;
|
|
36247
|
-
}
|
|
36248
36240
|
case "opencode": {
|
|
36249
36241
|
await writeOpenCodePlugin(projectRoot, knowledgeDir);
|
|
36250
36242
|
break;
|
|
36251
36243
|
}
|
|
36244
|
+
case "pi": {
|
|
36245
|
+
await writePiPlugin(projectRoot, knowledgeDir);
|
|
36246
|
+
break;
|
|
36247
|
+
}
|
|
36252
36248
|
}
|
|
36253
36249
|
}
|
|
36254
36250
|
async function applyHarnesses(harnesses, ctx) {
|
|
@@ -36408,7 +36404,7 @@ Validates frontmatter, required sections, file location, ID uniqueness, tag form
|
|
|
36408
36404
|
init: `pk init [<name>] [--harness <harness>]
|
|
36409
36405
|
Interactive when args omitted. Creates ~/.pk/<name>/ knowledge base, configures hooks, installs skill.
|
|
36410
36406
|
Detects existing projects and connects without recreating.
|
|
36411
|
-
Harnesses: claude,
|
|
36407
|
+
Harnesses: claude, opencode, pi.`,
|
|
36412
36408
|
config: `pk config [--embedding model] [--no-embedding]
|
|
36413
36409
|
Shows or updates pk configuration. Embedding config shape: {embedding:{enabled,provider,model}}.`
|
|
36414
36410
|
};
|
|
@@ -36442,6 +36438,20 @@ function registerVocab(program2) {
|
|
|
36442
36438
|
}));
|
|
36443
36439
|
}
|
|
36444
36440
|
|
|
36441
|
+
// src/lib/instruction.ts
|
|
36442
|
+
function pkInstruction(knowledgeDir) {
|
|
36443
|
+
return `## pk \u2014 project knowledge
|
|
36444
|
+
|
|
36445
|
+
Use the pk skill and its CLI commands to manage project knowledge for this project. Run \`pk synthesize --session-start\` at the start of every session.
|
|
36446
|
+
|
|
36447
|
+
Knowledge directory: \`${knowledgeDir}\`
|
|
36448
|
+
|
|
36449
|
+
\`\`\`bash
|
|
36450
|
+
export PK_KNOWLEDGE_DIR="${knowledgeDir}"
|
|
36451
|
+
\`\`\``;
|
|
36452
|
+
}
|
|
36453
|
+
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.";
|
|
36454
|
+
|
|
36445
36455
|
// src/commands/prime.ts
|
|
36446
36456
|
init_git();
|
|
36447
36457
|
function registerPrime(program2) {
|