@tekmidian/pai 0.59.0 → 0.60.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.
Files changed (129) hide show
  1. package/.claude-plugin/plugin.json +8 -33
  2. package/PLUGIN-ARCHITECTURE.md +4 -6
  3. package/dist/{aibroker-client-B8Wtasez.mjs → aibroker-client-BYG-GtAg.mjs} +1 -1
  4. package/dist/{chain-Cltx0NjY.mjs → chain-CGyAIHo4.mjs} +4 -4
  5. package/dist/{chain-Cltx0NjY.mjs.map → chain-CGyAIHo4.mjs.map} +1 -1
  6. package/dist/cli/index.mjs +12 -12
  7. package/dist/cli/program.mjs +12 -12
  8. package/dist/{config-D1omG_UU.mjs → config-CQCmH_gV.mjs} +17 -4
  9. package/dist/{config-D1omG_UU.mjs.map → config-CQCmH_gV.mjs.map} +1 -1
  10. package/dist/{config-DCYWFaEi.mjs → config-DjXq_W42.mjs} +1 -1
  11. package/dist/{context-handover-cache-Dt4HW1Ro.mjs → context-handover-cache-BzM7Qudn.mjs} +4 -4
  12. package/dist/{context-handover-cache-Dt4HW1Ro.mjs.map → context-handover-cache-BzM7Qudn.mjs.map} +1 -1
  13. package/dist/daemon/index.mjs +12 -12
  14. package/dist/{daemon-CmqJfeb9.mjs → daemon-B_TodsIS.mjs} +97 -23
  15. package/dist/daemon-B_TodsIS.mjs.map +1 -0
  16. package/dist/daemon-jviL3VVJ.mjs +19 -0
  17. package/dist/daemon-mcp/index.mjs +9 -9
  18. package/dist/daemon-mcp/index.mjs.map +1 -1
  19. package/dist/{factory-C9wGUe30.mjs → factory-DW7BCJJT.mjs} +1 -1
  20. package/dist/{factory-DiuntvwG.mjs → factory-EUUhSSoq.mjs} +7 -8
  21. package/dist/factory-EUUhSSoq.mjs.map +1 -0
  22. package/dist/{fallback-B_2ozQHw.mjs → fallback-CjdakQVu.mjs} +80 -9
  23. package/dist/fallback-CjdakQVu.mjs.map +1 -0
  24. package/dist/hooks/block-sleep-poll.mjs +7590 -0
  25. package/dist/hooks/block-sleep-poll.mjs.map +7 -0
  26. package/dist/hooks/capture-all-events.mjs +8 -4
  27. package/dist/hooks/capture-all-events.mjs.map +2 -2
  28. package/dist/hooks/capture-session-summary.mjs.map +1 -1
  29. package/dist/hooks/capture-tool-output.mjs.map +1 -1
  30. package/dist/hooks/cleanup-session-files.mjs.map +1 -1
  31. package/dist/hooks/context-compression-hook.mjs +6 -6
  32. package/dist/hooks/context-compression-hook.mjs.map +3 -3
  33. package/dist/hooks/initialize-session.mjs.map +1 -1
  34. package/dist/hooks/load-core-context.mjs.map +1 -1
  35. package/dist/hooks/load-project-context.mjs.map +2 -2
  36. package/dist/hooks/post-compact-inject.mjs +1 -1
  37. package/dist/hooks/post-compact-inject.mjs.map +2 -2
  38. package/dist/hooks/route-agents-to-worker.mjs +5 -4
  39. package/dist/hooks/route-agents-to-worker.mjs.map +3 -3
  40. package/dist/hooks/security-validator.mjs +5 -4
  41. package/dist/hooks/security-validator.mjs.map +3 -3
  42. package/dist/hooks/stop-hook.mjs.map +1 -1
  43. package/dist/hooks/sync-todo-to-md.mjs.map +1 -1
  44. package/dist/hooks/whisper-rules.mjs +5 -4
  45. package/dist/hooks/whisper-rules.mjs.map +3 -3
  46. package/dist/hooks/worker-proxy.mjs +5 -4
  47. package/dist/hooks/worker-proxy.mjs.map +3 -3
  48. package/dist/hooks/worker-status-line.mjs +10 -7
  49. package/dist/hooks/worker-status-line.mjs.map +3 -3
  50. package/dist/hooks/worker-supervision.mjs +5 -4
  51. package/dist/hooks/worker-supervision.mjs.map +3 -3
  52. package/dist/{main-resolver-DYNxxNwJ.mjs → main-resolver-BFQfU5xo.mjs} +100 -21
  53. package/dist/main-resolver-BFQfU5xo.mjs.map +1 -0
  54. package/dist/main-resolver-VMBENifT.mjs +7 -0
  55. package/dist/{planner-DsZZEZCA.mjs → planner-DN967Lvm.mjs} +83 -30
  56. package/dist/planner-DN967Lvm.mjs.map +1 -0
  57. package/dist/{program-B5miXqQa.mjs → program-BnsjB57Q.mjs} +278 -92
  58. package/dist/program-BnsjB57Q.mjs.map +1 -0
  59. package/dist/router-C5hPk9YP.mjs +3 -0
  60. package/dist/{router-BdxdsAvs.mjs → router-DjFZIoDl.mjs} +2 -1
  61. package/dist/router-DjFZIoDl.mjs.map +1 -0
  62. package/dist/{run-XaA-UPdW.mjs → run-FeM1_UVa.mjs} +102 -18
  63. package/dist/run-FeM1_UVa.mjs.map +1 -0
  64. package/dist/{run-env-DyTWgZC9.mjs → run-env-CEHTeIUM.mjs} +2 -2
  65. package/dist/{run-env-DyTWgZC9.mjs.map → run-env-CEHTeIUM.mjs.map} +1 -1
  66. package/dist/{server-CzqHGbKt.mjs → server-CekGOJcp.mjs} +2 -2
  67. package/dist/{server-CzqHGbKt.mjs.map → server-CekGOJcp.mjs.map} +1 -1
  68. package/dist/{session-keepalive-DDsEo81Q.mjs → session-keepalive-Bc9aT678.mjs} +5 -5
  69. package/dist/{session-keepalive-DDsEo81Q.mjs.map → session-keepalive-Bc9aT678.mjs.map} +1 -1
  70. package/dist/{tools-Diro2s8J.mjs → tools-qXyHFwNj.mjs} +2 -2
  71. package/dist/{tools-Diro2s8J.mjs.map → tools-qXyHFwNj.mjs.map} +1 -1
  72. package/dist/{tools-KgL1rkPD.mjs → tools-r-7ZG8B5.mjs} +3 -3
  73. package/dist/{wakeup-D0uvqLsE.mjs → wakeup-C48hcN6O.mjs} +2 -2
  74. package/dist/wakeup-C48hcN6O.mjs.map +1 -0
  75. package/dist/{work-queue-worker-DL9lu8sN.mjs → work-queue-worker-BL4dq5P7.mjs} +2 -2
  76. package/dist/{work-queue-worker-DL9lu8sN.mjs.map → work-queue-worker-BL4dq5P7.mjs.map} +1 -1
  77. package/dist/work-queue-worker-DVOVcEnO.mjs +11 -0
  78. package/docker/init.sql +1 -1
  79. package/docs/commands/README.md +4 -4
  80. package/docs/commands/daemon.md +6 -6
  81. package/docs/commands/session.md +2 -2
  82. package/docs/commands/worker.md +5 -2
  83. package/docs/workers-config.md +5 -1
  84. package/package.json +1 -1
  85. package/plugins/context-preservation/hooks/hooks.json +38 -31
  86. package/plugins/context-preservation/plugin.json +1 -4
  87. package/plugins/core/hooks/hooks.json +51 -46
  88. package/plugins/core/plugin.json +1 -4
  89. package/plugins/creative/plugin.json +1 -5
  90. package/plugins/observability/hooks/hooks.json +87 -72
  91. package/plugins/observability/plugin.json +1 -5
  92. package/plugins/productivity/hooks/hooks.json +22 -19
  93. package/plugins/productivity/plugin.json +2 -5
  94. package/plugins/semantic-search/plugin.json +1 -16
  95. package/plugins/ui/hooks/hooks.json +17 -14
  96. package/plugins/ui/plugin.json +1 -5
  97. package/plugins/zettelkasten/plugin.json +1 -14
  98. package/src/hooks/session-autosave.sh +4 -2
  99. package/src/hooks/session-stop.sh +2 -2
  100. package/src/hooks/ts/capture-all-events.ts +3 -3
  101. package/src/hooks/ts/lib/metadata-extraction.ts +3 -2
  102. package/src/hooks/ts/lib/pai-paths.test.ts +1 -1
  103. package/src/hooks/ts/lib/sleep-poll-gate.test.ts +62 -0
  104. package/src/hooks/ts/lib/sleep-poll-gate.ts +77 -0
  105. package/src/hooks/ts/post-tool-use/capture-tool-output.ts +1 -1
  106. package/src/hooks/ts/post-tool-use/sync-todo-to-md.ts +1 -1
  107. package/src/hooks/ts/pre-compact/context-compression-hook.ts +1 -1
  108. package/src/hooks/ts/pre-tool-use/block-sleep-poll.test.ts +44 -0
  109. package/src/hooks/ts/pre-tool-use/block-sleep-poll.ts +50 -0
  110. package/src/hooks/ts/session-end/capture-session-summary.ts +1 -1
  111. package/src/hooks/ts/session-start/initialize-session.ts +2 -2
  112. package/src/hooks/ts/session-start/load-core-context.ts +2 -2
  113. package/src/hooks/ts/session-start/load-project-context.ts +1 -1
  114. package/src/hooks/ts/stop/stop-hook.ts +1 -1
  115. package/src/hooks/ts/user-prompt/cleanup-session-files.ts +1 -1
  116. package/statusline-command.sh +26 -11
  117. package/dist/daemon-B6hx1Ysz.mjs +0 -19
  118. package/dist/daemon-CmqJfeb9.mjs.map +0 -1
  119. package/dist/factory-DiuntvwG.mjs.map +0 -1
  120. package/dist/fallback-B_2ozQHw.mjs.map +0 -1
  121. package/dist/main-resolver-DYNxxNwJ.mjs.map +0 -1
  122. package/dist/main-resolver-Df65bPAi.mjs +0 -7
  123. package/dist/planner-DsZZEZCA.mjs.map +0 -1
  124. package/dist/program-B5miXqQa.mjs.map +0 -1
  125. package/dist/router-BXOf_e9e.mjs +0 -3
  126. package/dist/router-BdxdsAvs.mjs.map +0 -1
  127. package/dist/run-XaA-UPdW.mjs.map +0 -1
  128. package/dist/wakeup-D0uvqLsE.mjs.map +0 -1
  129. package/dist/work-queue-worker-BY7xn4pw.mjs +0 -11
@@ -1,15 +1,14 @@
1
1
  {
2
- "name": "@tekmidian/pai",
2
+ "name": "pai",
3
3
  "displayName": "PAI Knowledge OS",
4
4
  "description": "Personal AI Infrastructure — persistent memory, session continuity, and knowledge graph for Claude Code",
5
- "version": "0.7.0",
6
- "author": "Matthias Nott",
5
+ "version": "0.60.0",
6
+ "author": {
7
+ "name": "Matthias Nott"
8
+ },
7
9
  "license": "MIT",
8
10
  "homepage": "https://github.com/mnott/PAI",
9
- "repository": {
10
- "type": "git",
11
- "url": "https://github.com/mnott/PAI"
12
- },
11
+ "repository": "https://github.com/mnott/PAI",
13
12
  "keywords": [
14
13
  "memory",
15
14
  "knowledge-os",
@@ -20,31 +19,7 @@
20
19
  "skills",
21
20
  "zettelkasten"
22
21
  ],
23
- "engines": {
24
- "claude-code": ">=1.0.0",
25
- "node": ">=20.0.0"
26
- },
27
- "setup": "pai setup",
28
- "mcp": {
29
- "server": "dist/daemon-mcp/index.mjs",
30
- "daemon": "dist/daemon/index.mjs"
31
- },
32
- "hooks": "plugins/core/hooks/hooks.json",
33
22
  "skills": [
34
- "plugins/core/skills/",
35
- "plugins/productivity/skills/",
36
- "plugins/observability/skills/",
37
- "plugins/zettelkasten/skills/",
38
- "plugins/creative/skills/"
39
- ],
40
- "templates": [
41
- "templates/claude-md.template.md",
42
- "templates/pai-skill.template.md",
43
- "templates/agent-prefs.example.md"
44
- ],
45
- "userExtensions": {
46
- "hooks": "user-extensions/hooks/",
47
- "skills": "user-extensions/skills/",
48
- "prompts": "src/daemon-mcp/prompts/custom/"
49
- }
23
+ "./plugins/productivity/skills/"
24
+ ]
50
25
  }
@@ -42,14 +42,12 @@ Each module has a `plugins/<module>/plugin.json` that declares:
42
42
  "displayName": "PAI Core",
43
43
  "description": "Core memory engine, session management, and project registry",
44
44
  "version": "0.7.0",
45
- "tier": "free",
46
- "required": true,
47
- "depends": [],
48
- "hooks": "hooks/hooks.json",
49
- "skills": ["Sessions", "Route", "Name"]
45
+ "hooks": "./hooks/hooks.json"
50
46
  }
51
47
  ```
52
48
 
49
+ `tier`, `required`, and `depends` are PAI-internal concepts (see `pai-plugin.json`), not part of Claude Code's plugin schema — the Claude Code loader ignores them, so they live only in `pai-plugin.json`, not in each module's `plugin.json`. `hooks/hooks.json` itself uses Claude Code's standard hooks shape (`{"hooks": {"<Event>": [{"matcher": "...", "hooks": [{"type": "command", "command": "..."}]}]}}`), not a PAI-private shape.
50
+
53
51
  ### Module Inventory
54
52
 
55
53
  | Module | Tier | Hooks | Skills | Description |
@@ -102,7 +100,7 @@ PAI/
102
100
  ├── .cursor/
103
101
  │ └── plugin.json # Cursor plugin manifest
104
102
  ├── gemini-extension.json # Gemini CLI extension manifest
105
- ├── pai-plugin.json # Canonical module manifest (build reads this)
103
+ ├── pai-plugin.json # Documentation-only module/tier overview; not read by build or runtime
106
104
  │
107
105
  ├── plugins/ # Module definitions
108
106
  │ ├── core/
@@ -1,3 +1,3 @@
1
1
  import { a as switchToSession, i as sendToSession, n as fetchLiveSessions, r as revealItermSession, t as callAiBroker } from "./aibroker-client-BK1h--W_.mjs";
2
2
 
3
- export { sendToSession };
3
+ export { callAiBroker, sendToSession };
@@ -1,6 +1,6 @@
1
- import { T as readWorkersSection } from "./run-env-DyTWgZC9.mjs";
2
- import { G as shortText, L as newWorkerId, T as appendLedger, n as runWorker } from "./run-XaA-UPdW.mjs";
3
- import { h as ledgerPath, v as workersLogDir } from "./server-CzqHGbKt.mjs";
1
+ import { T as readWorkersSection } from "./run-env-CEHTeIUM.mjs";
2
+ import { B as newWorkerId, J as shortText, O as appendLedger, n as runWorker } from "./run-FeM1_UVa.mjs";
3
+ import { h as ledgerPath, v as workersLogDir } from "./server-CekGOJcp.mjs";
4
4
  import { existsSync, mkdirSync, readFileSync } from "node:fs";
5
5
  import { join } from "node:path";
6
6
 
@@ -162,4 +162,4 @@ async function runChain(opts, deps = {}) {
162
162
 
163
163
  //#endregion
164
164
  export { swapPromptArg as n, runChain as t };
165
- //# sourceMappingURL=chain-Cltx0NjY.mjs.map
165
+ //# sourceMappingURL=chain-CGyAIHo4.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"chain-Cltx0NjY.mjs","names":[],"sources":["../src/workers/chain.ts"],"sourcesContent":["/**\n * chain.ts — draft-then-implement chains: `--chain draft,implement[,review]`.\n *\n * The chain runs each stage as its own worker (own id, own pane, `parent` set\n * to the chain id), so `ps` shows the chain as a tree and every stage can be\n * followed, replayed and said to like any other worker:\n *\n * - draft turns the operator's brief into a full spec file under\n * <logDir>/specs/<chain id>.md (goal, constraints, files likely\n * touched, acceptance checks, verification commands);\n * - any other stage (implement, plan, …) runs with that spec as its prompt\n * and the original brief attached;\n * - review reads the spec and the working-tree diff and produces the\n * structured report.\n *\n * A stage that fails (or a draft that produces no spec file) stops the chain;\n * the caller then writes the spec itself and re-runs without the draft stage.\n */\n\nimport { existsSync, mkdirSync, readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { readWorkersSection } from \"./config.js\";\nimport { appendLedger } from \"./ledger.js\";\nimport { ledgerPath, workersLogDir } from \"./paths.js\";\nimport { newWorkerId } from \"./status.js\";\nimport { shortText } from \"./args.js\";\nimport { runWorker, type RunOptions } from \"./run.js\";\n\n/** Where a chain's spec file lives: <logDir>/specs/<chain id>.md. */\nexport function specPathFor(logDir: string, chainId: string): string {\n return join(logDir, \"specs\", `${chainId}.md`);\n}\n\n/**\n * Replace the caller's -p value with `prompt`, dropping every existing\n * -p/--print pair first (two -p flags on one claude command line are an error,\n * so the chain must never leave the brief in place when swapping prompts).\n */\nexport function swapPromptArg(claudeArgs: string[], prompt: string): string[] {\n const out: string[] = [];\n for (let i = 0; i < claudeArgs.length; i++) {\n const a = claudeArgs[i];\n if (a === \"-p\" || a === \"--print\") {\n if (i + 1 < claudeArgs.length && !claudeArgs[i + 1].startsWith(\"-\")) i += 1;\n continue;\n }\n out.push(a);\n }\n out.push(\"-p\", prompt);\n return out;\n}\n\nexport function draftPrompt(brief: string, specPath: string): string {\n return [\n \"You are the DRAFT stage of a worker chain. Turn the operator's brief below into a full implementation spec and write it to\",\n specPath,\n \"with the Write tool.\",\n \"\",\n \"Use exactly these five sections as Markdown headings:\",\n \"# Goal\",\n \"# Constraints\",\n \"# Files likely touched\",\n \"# Acceptance checks\",\n \"# Verification commands\",\n \"\",\n \"Read the repository first (Glob/Grep/Read) so the spec names real files and real commands. Do not implement anything.\",\n \"\",\n \"## Operator brief\",\n \"\",\n brief,\n ].join(\"\\n\");\n}\n\nexport function implementPrompt(brief: string, specPath: string | null, spec: string | null): string {\n const head = spec\n ? [\n \"You are the IMPLEMENT stage of a worker chain. The draft stage wrote the spec below (also at \" +\n specPath +\n \"). Implement it exactly, then run the spec's verification commands before finishing.\",\n \"\",\n \"## Spec\",\n \"\",\n spec,\n ]\n : [\"Implement the operator's brief below.\"];\n return [...head, \"\", \"## Operator brief\", \"\", brief].join(\"\\n\");\n}\n\nexport function reviewPrompt(brief: string, specPath: string | null, spec: string | null): string {\n const specPart = spec\n ? [\n \"\",\n \"## Spec (also at \" + specPath + \")\",\n \"\",\n spec,\n ]\n : [];\n return [\n \"You are the REVIEW stage of a worker chain. The implement stage just ran. Read the repository's diff (run `git diff` and `git status`; use `git diff --stat` for the overview) and check it against the spec's acceptance checks and verification commands — run the checks when they are cheap. Do not fix anything you find; report it.\",\n ...specPart,\n \"\",\n \"## Operator brief\",\n \"\",\n brief,\n ].join(\"\\n\");\n}\n\nexport interface ChainOptions {\n /** Class names, run in order: e.g. [\"draft\", \"implement\", \"review\"]. */\n stages: string[];\n /** Overrides the class of every stage when given (--class with --chain). */\n className?: string;\n providerFlag?: string;\n modelFlag?: string;\n label?: string;\n noPane?: boolean;\n mcpFlag?: string;\n /** --spec value, resolved for display/recording (\"-\" for stdin). */\n specPath?: string;\n /** The operator's brief — the -p value of the run. */\n brief: string;\n /** The caller's claude args (allowedTools etc.); the -p value is swapped. */\n claudeArgs: string[];\n cwd?: string;\n /** Internal: notified with the chain id once it exists (worker_run uses it). */\n onChainStart?: (chainId: string) => void;\n /** Internal: suppress result printing (the MCP shim's stdout is the RPC channel). */\n quiet?: boolean;\n}\n\nexport interface ChainDeps {\n /** Stage runner; tests inject a mock, production uses runWorker. */\n runStage?: (opts: RunOptions) => Promise<number>;\n /** logDir override for tests; default: the configured workers logDir. */\n logDir?: string;\n}\n\n/** Run a chain of stages; returns the exit code of the first failed stage, 0 when all pass. */\nexport async function runChain(opts: ChainOptions, deps: ChainDeps = {}): Promise<number> {\n const stages = opts.stages.map((s) => s.trim()).filter(Boolean);\n if (!stages.length) throw new Error(\"--chain needs at least one class, e.g. --chain draft,implement\");\n const runStage = deps.runStage ?? runWorker;\n const logDir = deps.logDir ?? workersLogDir(readWorkersSection().workers);\n const chainId = newWorkerId();\n const specPath = specPathFor(logDir, chainId);\n mkdirSync(join(logDir, \"specs\"), { recursive: true }); // the draft stage writes into it\n const baseLabel = opts.label ?? shortText(opts.brief, 40);\n\n appendLedger(ledgerPath(logDir), \"WORKER-CHAIN\", {\n chain: chainId,\n stages: stages.join(\",\"),\n label: baseLabel,\n });\n opts.onChainStart?.(chainId);\n\n let spec: string | null = null;\n for (let i = 0; i < stages.length; i++) {\n const stage = stages[i];\n if (stage === \"draft\") {\n spec = null; // a chain may legally start over; the draft rewrites it\n } else if (spec === null && existsSync(specPath)) {\n spec = readFileSync(specPath, \"utf8\");\n }\n const prompt =\n stage === \"draft\"\n ? draftPrompt(opts.brief, specPath)\n : stage === \"review\"\n ? reviewPrompt(opts.brief, spec ? specPath : null, spec)\n : implementPrompt(opts.brief, spec ? specPath : null, spec);\n process.stderr.write(\n `chain ${chainId}: stage ${i + 1}/${stages.length} ${stage} (spec: ${specPath})\\n`\n );\n const rc = await runStage({\n className: opts.className ?? stage,\n providerFlag: opts.providerFlag,\n modelFlag: opts.modelFlag,\n label: `${baseLabel} · ${stage}`,\n noPane: opts.noPane,\n mcpFlag: opts.mcpFlag,\n specPath: opts.specPath,\n claudeArgs: swapPromptArg(opts.claudeArgs, prompt),\n cwd: opts.cwd,\n parent: chainId,\n stage,\n quiet: opts.quiet,\n });\n if (stage === \"draft\") {\n if (!existsSync(specPath)) {\n process.stderr.write(\n `chain ${chainId}: draft stage produced no spec at ${specPath} — stopping. ` +\n `Write the spec yourself and re-run without the draft stage.\\n`\n );\n appendLedger(ledgerPath(logDir), \"WORKER-CHAIN-END\", {\n chain: chainId,\n rc: rc !== 0 ? rc : 1,\n failed: \"draft\",\n });\n return rc !== 0 ? rc : 1;\n }\n spec = readFileSync(specPath, \"utf8\");\n }\n if (rc !== 0) {\n appendLedger(ledgerPath(logDir), \"WORKER-CHAIN-END\", {\n chain: chainId,\n rc,\n failed: stage,\n });\n return rc;\n }\n }\n appendLedger(ledgerPath(logDir), \"WORKER-CHAIN-END\", { chain: chainId, rc: 0 });\n return 0;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"chain-CGyAIHo4.mjs","names":[],"sources":["../src/workers/chain.ts"],"sourcesContent":["/**\n * chain.ts — draft-then-implement chains: `--chain draft,implement[,review]`.\n *\n * The chain runs each stage as its own worker (own id, own pane, `parent` set\n * to the chain id), so `ps` shows the chain as a tree and every stage can be\n * followed, replayed and said to like any other worker:\n *\n * - draft turns the operator's brief into a full spec file under\n * <logDir>/specs/<chain id>.md (goal, constraints, files likely\n * touched, acceptance checks, verification commands);\n * - any other stage (implement, plan, …) runs with that spec as its prompt\n * and the original brief attached;\n * - review reads the spec and the working-tree diff and produces the\n * structured report.\n *\n * A stage that fails (or a draft that produces no spec file) stops the chain;\n * the caller then writes the spec itself and re-runs without the draft stage.\n */\n\nimport { existsSync, mkdirSync, readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { readWorkersSection } from \"./config.js\";\nimport { appendLedger } from \"./ledger.js\";\nimport { ledgerPath, workersLogDir } from \"./paths.js\";\nimport { newWorkerId } from \"./status.js\";\nimport { shortText } from \"./args.js\";\nimport { runWorker, type RunOptions } from \"./run.js\";\n\n/** Where a chain's spec file lives: <logDir>/specs/<chain id>.md. */\nexport function specPathFor(logDir: string, chainId: string): string {\n return join(logDir, \"specs\", `${chainId}.md`);\n}\n\n/**\n * Replace the caller's -p value with `prompt`, dropping every existing\n * -p/--print pair first (two -p flags on one claude command line are an error,\n * so the chain must never leave the brief in place when swapping prompts).\n */\nexport function swapPromptArg(claudeArgs: string[], prompt: string): string[] {\n const out: string[] = [];\n for (let i = 0; i < claudeArgs.length; i++) {\n const a = claudeArgs[i];\n if (a === \"-p\" || a === \"--print\") {\n if (i + 1 < claudeArgs.length && !claudeArgs[i + 1].startsWith(\"-\")) i += 1;\n continue;\n }\n out.push(a);\n }\n out.push(\"-p\", prompt);\n return out;\n}\n\nexport function draftPrompt(brief: string, specPath: string): string {\n return [\n \"You are the DRAFT stage of a worker chain. Turn the operator's brief below into a full implementation spec and write it to\",\n specPath,\n \"with the Write tool.\",\n \"\",\n \"Use exactly these five sections as Markdown headings:\",\n \"# Goal\",\n \"# Constraints\",\n \"# Files likely touched\",\n \"# Acceptance checks\",\n \"# Verification commands\",\n \"\",\n \"Read the repository first (Glob/Grep/Read) so the spec names real files and real commands. Do not implement anything.\",\n \"\",\n \"## Operator brief\",\n \"\",\n brief,\n ].join(\"\\n\");\n}\n\nexport function implementPrompt(brief: string, specPath: string | null, spec: string | null): string {\n const head = spec\n ? [\n \"You are the IMPLEMENT stage of a worker chain. The draft stage wrote the spec below (also at \" +\n specPath +\n \"). Implement it exactly, then run the spec's verification commands before finishing.\",\n \"\",\n \"## Spec\",\n \"\",\n spec,\n ]\n : [\"Implement the operator's brief below.\"];\n return [...head, \"\", \"## Operator brief\", \"\", brief].join(\"\\n\");\n}\n\nexport function reviewPrompt(brief: string, specPath: string | null, spec: string | null): string {\n const specPart = spec\n ? [\n \"\",\n \"## Spec (also at \" + specPath + \")\",\n \"\",\n spec,\n ]\n : [];\n return [\n \"You are the REVIEW stage of a worker chain. The implement stage just ran. Read the repository's diff (run `git diff` and `git status`; use `git diff --stat` for the overview) and check it against the spec's acceptance checks and verification commands — run the checks when they are cheap. Do not fix anything you find; report it.\",\n ...specPart,\n \"\",\n \"## Operator brief\",\n \"\",\n brief,\n ].join(\"\\n\");\n}\n\nexport interface ChainOptions {\n /** Class names, run in order: e.g. [\"draft\", \"implement\", \"review\"]. */\n stages: string[];\n /** Overrides the class of every stage when given (--class with --chain). */\n className?: string;\n providerFlag?: string;\n modelFlag?: string;\n label?: string;\n noPane?: boolean;\n mcpFlag?: string;\n /** --spec value, resolved for display/recording (\"-\" for stdin). */\n specPath?: string;\n /** The operator's brief — the -p value of the run. */\n brief: string;\n /** The caller's claude args (allowedTools etc.); the -p value is swapped. */\n claudeArgs: string[];\n cwd?: string;\n /** Internal: notified with the chain id once it exists (worker_run uses it). */\n onChainStart?: (chainId: string) => void;\n /** Internal: suppress result printing (the MCP shim's stdout is the RPC channel). */\n quiet?: boolean;\n}\n\nexport interface ChainDeps {\n /** Stage runner; tests inject a mock, production uses runWorker. */\n runStage?: (opts: RunOptions) => Promise<number>;\n /** logDir override for tests; default: the configured workers logDir. */\n logDir?: string;\n}\n\n/** Run a chain of stages; returns the exit code of the first failed stage, 0 when all pass. */\nexport async function runChain(opts: ChainOptions, deps: ChainDeps = {}): Promise<number> {\n const stages = opts.stages.map((s) => s.trim()).filter(Boolean);\n if (!stages.length) throw new Error(\"--chain needs at least one class, e.g. --chain draft,implement\");\n const runStage = deps.runStage ?? runWorker;\n const logDir = deps.logDir ?? workersLogDir(readWorkersSection().workers);\n const chainId = newWorkerId();\n const specPath = specPathFor(logDir, chainId);\n mkdirSync(join(logDir, \"specs\"), { recursive: true }); // the draft stage writes into it\n const baseLabel = opts.label ?? shortText(opts.brief, 40);\n\n appendLedger(ledgerPath(logDir), \"WORKER-CHAIN\", {\n chain: chainId,\n stages: stages.join(\",\"),\n label: baseLabel,\n });\n opts.onChainStart?.(chainId);\n\n let spec: string | null = null;\n for (let i = 0; i < stages.length; i++) {\n const stage = stages[i];\n if (stage === \"draft\") {\n spec = null; // a chain may legally start over; the draft rewrites it\n } else if (spec === null && existsSync(specPath)) {\n spec = readFileSync(specPath, \"utf8\");\n }\n const prompt =\n stage === \"draft\"\n ? draftPrompt(opts.brief, specPath)\n : stage === \"review\"\n ? reviewPrompt(opts.brief, spec ? specPath : null, spec)\n : implementPrompt(opts.brief, spec ? specPath : null, spec);\n process.stderr.write(\n `chain ${chainId}: stage ${i + 1}/${stages.length} ${stage} (spec: ${specPath})\\n`\n );\n const rc = await runStage({\n className: opts.className ?? stage,\n providerFlag: opts.providerFlag,\n modelFlag: opts.modelFlag,\n label: `${baseLabel} · ${stage}`,\n noPane: opts.noPane,\n mcpFlag: opts.mcpFlag,\n specPath: opts.specPath,\n claudeArgs: swapPromptArg(opts.claudeArgs, prompt),\n cwd: opts.cwd,\n parent: chainId,\n stage,\n quiet: opts.quiet,\n });\n if (stage === \"draft\") {\n if (!existsSync(specPath)) {\n process.stderr.write(\n `chain ${chainId}: draft stage produced no spec at ${specPath} — stopping. ` +\n `Write the spec yourself and re-run without the draft stage.\\n`\n );\n appendLedger(ledgerPath(logDir), \"WORKER-CHAIN-END\", {\n chain: chainId,\n rc: rc !== 0 ? rc : 1,\n failed: \"draft\",\n });\n return rc !== 0 ? rc : 1;\n }\n spec = readFileSync(specPath, \"utf8\");\n }\n if (rc !== 0) {\n appendLedger(ledgerPath(logDir), \"WORKER-CHAIN-END\", {\n chain: chainId,\n rc,\n failed: stage,\n });\n return rc;\n }\n }\n appendLedger(ledgerPath(logDir), \"WORKER-CHAIN-END\", { chain: chainId, rc: 0 });\n return 0;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AA6BA,SAAgB,YAAY,QAAgB,SAAyB;AACnE,QAAO,KAAK,QAAQ,SAAS,GAAG,QAAQ,KAAK;;;;;;;AAQ/C,SAAgB,cAAc,YAAsB,QAA0B;CAC5E,MAAM,MAAgB,EAAE;AACxB,MAAK,IAAI,IAAI,GAAG,IAAI,WAAW,QAAQ,KAAK;EAC1C,MAAM,IAAI,WAAW;AACrB,MAAI,MAAM,QAAQ,MAAM,WAAW;AACjC,OAAI,IAAI,IAAI,WAAW,UAAU,CAAC,WAAW,IAAI,GAAG,WAAW,IAAI,CAAE,MAAK;AAC1E;;AAEF,MAAI,KAAK,EAAE;;AAEb,KAAI,KAAK,MAAM,OAAO;AACtB,QAAO;;AAGT,SAAgB,YAAY,OAAe,UAA0B;AACnE,QAAO;EACL;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACD,CAAC,KAAK,KAAK;;AAGd,SAAgB,gBAAgB,OAAe,UAAyB,MAA6B;AAYnG,QAAO;EAAC,GAXK,OACT;GACE,kGACE,WACA;GACF;GACA;GACA;GACA;GACD,GACD,CAAC,wCAAwC;EAC5B;EAAI;EAAqB;EAAI;EAAM,CAAC,KAAK,KAAK;;AAGjE,SAAgB,aAAa,OAAe,UAAyB,MAA6B;AAShG,QAAO;EACL;EACA,GAVe,OACb;GACE;GACA,sBAAsB,WAAW;GACjC;GACA;GACD,GACD,EAAE;EAIJ;EACA;EACA;EACA;EACD,CAAC,KAAK,KAAK;;;AAkCd,eAAsB,SAAS,MAAoB,OAAkB,EAAE,EAAmB;CACxF,MAAM,SAAS,KAAK,OAAO,KAAK,MAAM,EAAE,MAAM,CAAC,CAAC,OAAO,QAAQ;AAC/D,KAAI,CAAC,OAAO,OAAQ,OAAM,IAAI,MAAM,iEAAiE;CACrG,MAAM,WAAW,KAAK,YAAY;CAClC,MAAM,SAAS,KAAK,UAAU,cAAc,oBAAoB,CAAC,QAAQ;CACzE,MAAM,UAAU,aAAa;CAC7B,MAAM,WAAW,YAAY,QAAQ,QAAQ;AAC7C,WAAU,KAAK,QAAQ,QAAQ,EAAE,EAAE,WAAW,MAAM,CAAC;CACrD,MAAM,YAAY,KAAK,SAAS,UAAU,KAAK,OAAO,GAAG;AAEzD,cAAa,WAAW,OAAO,EAAE,gBAAgB;EAC/C,OAAO;EACP,QAAQ,OAAO,KAAK,IAAI;EACxB,OAAO;EACR,CAAC;AACF,MAAK,eAAe,QAAQ;CAE5B,IAAI,OAAsB;AAC1B,MAAK,IAAI,IAAI,GAAG,IAAI,OAAO,QAAQ,KAAK;EACtC,MAAM,QAAQ,OAAO;AACrB,MAAI,UAAU,QACZ,QAAO;WACE,SAAS,QAAQ,WAAW,SAAS,CAC9C,QAAO,aAAa,UAAU,OAAO;EAEvC,MAAM,SACJ,UAAU,UACN,YAAY,KAAK,OAAO,SAAS,GACjC,UAAU,WACR,aAAa,KAAK,OAAO,OAAO,WAAW,MAAM,KAAK,GACtD,gBAAgB,KAAK,OAAO,OAAO,WAAW,MAAM,KAAK;AACjE,UAAQ,OAAO,MACb,SAAS,QAAQ,UAAU,IAAI,EAAE,GAAG,OAAO,OAAO,GAAG,MAAM,UAAU,SAAS,KAC/E;EACD,MAAM,KAAK,MAAM,SAAS;GACxB,WAAW,KAAK,aAAa;GAC7B,cAAc,KAAK;GACnB,WAAW,KAAK;GAChB,OAAO,GAAG,UAAU,KAAK;GACzB,QAAQ,KAAK;GACb,SAAS,KAAK;GACd,UAAU,KAAK;GACf,YAAY,cAAc,KAAK,YAAY,OAAO;GAClD,KAAK,KAAK;GACV,QAAQ;GACR;GACA,OAAO,KAAK;GACb,CAAC;AACF,MAAI,UAAU,SAAS;AACrB,OAAI,CAAC,WAAW,SAAS,EAAE;AACzB,YAAQ,OAAO,MACb,SAAS,QAAQ,oCAAoC,SAAS,4EAE/D;AACD,iBAAa,WAAW,OAAO,EAAE,oBAAoB;KACnD,OAAO;KACP,IAAI,OAAO,IAAI,KAAK;KACpB,QAAQ;KACT,CAAC;AACF,WAAO,OAAO,IAAI,KAAK;;AAEzB,UAAO,aAAa,UAAU,OAAO;;AAEvC,MAAI,OAAO,GAAG;AACZ,gBAAa,WAAW,OAAO,EAAE,oBAAoB;IACnD,OAAO;IACP;IACA,QAAQ;IACT,CAAC;AACF,UAAO;;;AAGX,cAAa,WAAW,OAAO,EAAE,oBAAoB;EAAE,OAAO;EAAS,IAAI;EAAG,CAAC;AAC/E,QAAO"}
@@ -2,25 +2,25 @@
2
2
  import { i as err } from "../utils-C9HsYDpO.mjs";
3
3
  import "../migrate-BD7D8EEh.mjs";
4
4
  import "../helpers-D9WkIf9R.mjs";
5
- import "../factory-DiuntvwG.mjs";
6
- import { n as drainStdio, t as buildProgram } from "../program-B5miXqQa.mjs";
7
- import "../wakeup-D0uvqLsE.mjs";
8
- import "../context-handover-cache-Dt4HW1Ro.mjs";
9
- import "../config-D1omG_UU.mjs";
10
- import "../run-env-DyTWgZC9.mjs";
11
- import "../run-XaA-UPdW.mjs";
5
+ import "../factory-EUUhSSoq.mjs";
6
+ import { n as drainStdio, t as buildProgram } from "../program-BnsjB57Q.mjs";
7
+ import "../wakeup-C48hcN6O.mjs";
8
+ import "../context-handover-cache-BzM7Qudn.mjs";
9
+ import "../config-CQCmH_gV.mjs";
10
+ import "../run-env-CEHTeIUM.mjs";
11
+ import "../run-FeM1_UVa.mjs";
12
12
  import "../federation-db-w8e-sKjp.mjs";
13
13
  import "../registry-db-Bkpbp7Gf.mjs";
14
14
  import "../async-CLvuY7b-.mjs";
15
15
  import "../indexer-backend-Cc-_NK3q.mjs";
16
16
  import "../ipc-client-Dasm515c.mjs";
17
- import "../fallback-B_2ozQHw.mjs";
18
- import "../main-resolver-DYNxxNwJ.mjs";
19
- import "../server-CzqHGbKt.mjs";
17
+ import "../fallback-CjdakQVu.mjs";
18
+ import "../main-resolver-BFQfU5xo.mjs";
19
+ import "../server-CekGOJcp.mjs";
20
20
  import "../state-n13uPK5B.mjs";
21
21
  import "../aibroker-client-BK1h--W_.mjs";
22
- import "../session-keepalive-DDsEo81Q.mjs";
23
- import "../chain-Cltx0NjY.mjs";
22
+ import "../session-keepalive-Bc9aT678.mjs";
23
+ import "../chain-CGyAIHo4.mjs";
24
24
  import { CommanderError } from "commander";
25
25
 
26
26
  //#region src/cli/index.ts
@@ -1,24 +1,24 @@
1
1
  import "../utils-C9HsYDpO.mjs";
2
2
  import "../migrate-BD7D8EEh.mjs";
3
3
  import "../helpers-D9WkIf9R.mjs";
4
- import "../factory-DiuntvwG.mjs";
5
- import { t as buildProgram } from "../program-B5miXqQa.mjs";
6
- import "../wakeup-D0uvqLsE.mjs";
7
- import "../context-handover-cache-Dt4HW1Ro.mjs";
8
- import "../config-D1omG_UU.mjs";
9
- import "../run-env-DyTWgZC9.mjs";
10
- import "../run-XaA-UPdW.mjs";
4
+ import "../factory-EUUhSSoq.mjs";
5
+ import { t as buildProgram } from "../program-BnsjB57Q.mjs";
6
+ import "../wakeup-C48hcN6O.mjs";
7
+ import "../context-handover-cache-BzM7Qudn.mjs";
8
+ import "../config-CQCmH_gV.mjs";
9
+ import "../run-env-CEHTeIUM.mjs";
10
+ import "../run-FeM1_UVa.mjs";
11
11
  import "../federation-db-w8e-sKjp.mjs";
12
12
  import "../registry-db-Bkpbp7Gf.mjs";
13
13
  import "../async-CLvuY7b-.mjs";
14
14
  import "../indexer-backend-Cc-_NK3q.mjs";
15
15
  import "../ipc-client-Dasm515c.mjs";
16
- import "../fallback-B_2ozQHw.mjs";
17
- import "../main-resolver-DYNxxNwJ.mjs";
18
- import "../server-CzqHGbKt.mjs";
16
+ import "../fallback-CjdakQVu.mjs";
17
+ import "../main-resolver-BFQfU5xo.mjs";
18
+ import "../server-CekGOJcp.mjs";
19
19
  import "../state-n13uPK5B.mjs";
20
20
  import "../aibroker-client-BK1h--W_.mjs";
21
- import "../session-keepalive-DDsEo81Q.mjs";
22
- import "../chain-Cltx0NjY.mjs";
21
+ import "../session-keepalive-Bc9aT678.mjs";
22
+ import "../chain-CGyAIHo4.mjs";
23
23
 
24
24
  export { buildProgram };
@@ -1,6 +1,6 @@
1
1
  import { a as paiHomePath, o as resolvePaiFile, r as migratePaiFile } from "./pai-home-CeOIYltV.mjs";
2
2
  import { i as paiSocketPath } from "./runtime-paths-BRwDeBEg.mjs";
3
- import { chmodSync, copyFileSync, existsSync, mkdirSync, readFileSync, renameSync, unlinkSync, writeFileSync } from "node:fs";
3
+ import { chmodSync, copyFileSync, existsSync, mkdirSync, readFileSync, readdirSync, renameSync, unlinkSync, writeFileSync } from "node:fs";
4
4
  import { homedir, userInfo } from "node:os";
5
5
  import { basename, dirname, join } from "node:path";
6
6
  import { Document, YAMLMap, parseDocument } from "yaml";
@@ -159,13 +159,18 @@ function writeJsonAtomic(path, data, opts = {}) {
159
159
  if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
160
160
  if (backup && existsSync(path)) try {
161
161
  copyFileSync(path, `${path}.bak-pai`);
162
+ chmodSync(`${path}.bak-pai`, 384);
162
163
  } catch (e) {
163
164
  throw new Error(`Could not back up ${label}: ${e instanceof Error ? e.message : String(e)}\nRefusing to write without a backup.`);
164
165
  }
165
166
  const tmp = `${path}.tmp-pai-${process.pid}`;
166
167
  try {
167
- writeFileSync(tmp, serialized, "utf8");
168
+ writeFileSync(tmp, serialized, {
169
+ encoding: "utf8",
170
+ mode: 384
171
+ });
168
172
  renameSync(tmp, path);
173
+ chmodSync(path, 384);
169
174
  } catch (e) {
170
175
  try {
171
176
  if (existsSync(tmp)) unlinkSync(tmp);
@@ -437,6 +442,7 @@ function migrateMainConfigToYaml(jsonPath, opts = {}) {
437
442
  const stamp = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
438
443
  const backupPath = join(dirname(jsonPath), `${basename(jsonPath)}.migrated-${stamp}`);
439
444
  renameSync(jsonPath, backupPath);
445
+ chmodSync(backupPath, 384);
440
446
  return {
441
447
  yamlPath,
442
448
  yamlText,
@@ -598,7 +604,14 @@ function writeMainConfigRaw(raw, path = CONFIG_FILE) {
598
604
  * config.json.migrated-<YYYYMMDD> (never deleted).
599
605
  */
600
606
  function migrateConfigFile(opts = {}) {
601
- return migratePaiFile(NEW_CONFIG_FILE, [OLD_CONFIG_FILE, LEGACY_CONFIG_FILE], opts);
607
+ const result = migratePaiFile(NEW_CONFIG_FILE, [OLD_CONFIG_FILE, LEGACY_CONFIG_FILE], opts);
608
+ if (!result.dryRun && result.fromPath) {
609
+ if (existsSync(result.toPath)) chmodSync(result.toPath, 384);
610
+ const oldDir = dirname(result.fromPath);
611
+ const oldName = basename(result.fromPath);
612
+ for (const entry of readdirSync(oldDir)) if (entry.startsWith(`${oldName}.migrated-`)) chmodSync(join(oldDir, entry), 384);
613
+ }
614
+ return result;
602
615
  }
603
616
  function deepMerge(target, source) {
604
617
  const result = { ...target };
@@ -650,4 +663,4 @@ function ensureConfigDir() {
650
663
 
651
664
  //#endregion
652
665
  export { DEFAULT_NOTIFICATION_CONFIG as S, writeYamlFileAtomic as _, ensureConfigDir as a, OWNER_LABEL_PREFIX as b, migrateConfigFile as c, readMainConfigRaw as d, writeMainConfigRaw as f, YamlStoreError as g, resolvedMainConfigPath as h, DEFAULT_SESSIONS_CACHE_KEEPALIVE as i, paiConfigFilePath as l, migrateMainConfigToYaml as m, CONFIG_FILE as n, expandHome as o, MainConfigError as p, DEFAULTS as r, loadConfig as s, CONFIG_DIR as t, paiConfigYamlFilePath as u, readJsonStrict as v, UNROUTED as x, writeJsonAtomic as y };
653
- //# sourceMappingURL=config-D1omG_UU.mjs.map
666
+ //# sourceMappingURL=config-CQCmH_gV.mjs.map