@selesai/code 0.13.0 → 0.13.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 (461) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/core/agent-session.d.ts +1 -1
  3. package/dist/core/agent-session.js +1 -1
  4. package/dist/core/sdk.d.ts +2 -2
  5. package/dist/core/sdk.js +1 -1
  6. package/dist/core/system-prompt.d.ts +1 -1
  7. package/dist/core/system-prompt.js +1 -1
  8. package/dist/extensions/ascii.txt +9 -0
  9. package/dist/extensions/package.json +1 -2
  10. package/dist/extensions/pi-hermes-memory/CHANGELOG.md +371 -0
  11. package/dist/extensions/pi-hermes-memory/LICENSE +21 -0
  12. package/dist/extensions/pi-hermes-memory/README.md +643 -0
  13. package/dist/extensions/pi-hermes-memory/docs/0.1/TASKS.md +197 -0
  14. package/dist/extensions/pi-hermes-memory/docs/0.2/PLAN.md +290 -0
  15. package/dist/extensions/pi-hermes-memory/docs/0.2/TASKS.md +134 -0
  16. package/dist/extensions/pi-hermes-memory/docs/0.2/TEST-PLAN.md +216 -0
  17. package/dist/extensions/pi-hermes-memory/docs/0.3/PLAN.md +330 -0
  18. package/dist/extensions/pi-hermes-memory/docs/0.3/TASKS.md +125 -0
  19. package/dist/extensions/pi-hermes-memory/docs/0.4/PLAN.md +160 -0
  20. package/dist/extensions/pi-hermes-memory/docs/0.4/TASKS.md +113 -0
  21. package/dist/extensions/pi-hermes-memory/docs/0.6/CHANGELOG.md +57 -0
  22. package/dist/extensions/pi-hermes-memory/docs/0.6/PLAN.md +202 -0
  23. package/dist/extensions/pi-hermes-memory/docs/0.6/TASKS.md +144 -0
  24. package/dist/extensions/pi-hermes-memory/docs/0.7/PLAN.md +349 -0
  25. package/dist/extensions/pi-hermes-memory/docs/0.7/TASKS.md +110 -0
  26. package/dist/extensions/pi-hermes-memory/docs/PUBLISHING.md +149 -0
  27. package/dist/extensions/pi-hermes-memory/docs/ROADMAP.md +468 -0
  28. package/dist/extensions/pi-hermes-memory/docs/images/memory-architecture.svg +1 -0
  29. package/dist/extensions/pi-hermes-memory/docs/images/pi-logo.svg +22 -0
  30. package/dist/extensions/pi-hermes-memory/docs/images/pi_memory.png +0 -0
  31. package/dist/extensions/pi-hermes-memory/docs/images/pi_memory.svg +19 -0
  32. package/dist/extensions/pi-hermes-memory/docs/images/security-flow.svg +1 -0
  33. package/dist/extensions/pi-hermes-memory/docs/images/session-lifecycle.svg +1 -0
  34. package/dist/extensions/pi-hermes-memory/docs/images/source-architecture.svg +1 -0
  35. package/dist/extensions/pi-hermes-memory/docs/mermaid/memory-architecture.mmd +49 -0
  36. package/dist/extensions/pi-hermes-memory/docs/mermaid/security-flow.mmd +40 -0
  37. package/dist/extensions/pi-hermes-memory/docs/mermaid/session-lifecycle.mmd +56 -0
  38. package/dist/extensions/pi-hermes-memory/docs/mermaid/source-architecture.mmd +54 -0
  39. package/dist/extensions/pi-hermes-memory/package-lock.json +3799 -0
  40. package/dist/extensions/pi-hermes-memory/package.json +66 -0
  41. package/dist/extensions/pi-hermes-memory/scripts/check-min-sdk.mjs +99 -0
  42. package/dist/extensions/pi-hermes-memory/scripts/ensure-dev.mjs +57 -0
  43. package/dist/extensions/pi-hermes-memory/src/auto-consolidation-warning.ts +6 -0
  44. package/dist/extensions/pi-hermes-memory/src/config.ts +178 -0
  45. package/dist/extensions/pi-hermes-memory/src/constants.ts +424 -0
  46. package/dist/extensions/pi-hermes-memory/src/extension-root-migration.ts +639 -0
  47. package/dist/extensions/pi-hermes-memory/src/handlers/auto-consolidate.ts +401 -0
  48. package/dist/extensions/pi-hermes-memory/src/handlers/background-review.ts +297 -0
  49. package/dist/extensions/pi-hermes-memory/src/handlers/child-process-watchdog.mjs +90 -0
  50. package/dist/extensions/pi-hermes-memory/src/handlers/correction-detector.ts +300 -0
  51. package/dist/extensions/pi-hermes-memory/src/handlers/index-sessions.ts +86 -0
  52. package/dist/extensions/pi-hermes-memory/src/handlers/insights.ts +78 -0
  53. package/dist/extensions/pi-hermes-memory/src/handlers/interview.ts +37 -0
  54. package/dist/extensions/pi-hermes-memory/src/handlers/learn-memory.ts +186 -0
  55. package/dist/extensions/pi-hermes-memory/src/handlers/message-parts.ts +27 -0
  56. package/dist/extensions/pi-hermes-memory/src/handlers/pi-child-process.ts +485 -0
  57. package/dist/extensions/pi-hermes-memory/src/handlers/preview-context.ts +105 -0
  58. package/dist/extensions/pi-hermes-memory/src/handlers/review-memory-ops.ts +506 -0
  59. package/dist/extensions/pi-hermes-memory/src/handlers/session-backfill.ts +145 -0
  60. package/dist/extensions/pi-hermes-memory/src/handlers/session-flush.ts +151 -0
  61. package/dist/extensions/pi-hermes-memory/src/handlers/session-live-index.ts +94 -0
  62. package/dist/extensions/pi-hermes-memory/src/handlers/skills-command.ts +1334 -0
  63. package/dist/extensions/pi-hermes-memory/src/handlers/standing-pin.ts +111 -0
  64. package/dist/extensions/pi-hermes-memory/src/handlers/switch-project.ts +75 -0
  65. package/dist/extensions/pi-hermes-memory/src/handlers/sync-markdown-memories.ts +266 -0
  66. package/dist/extensions/pi-hermes-memory/src/index.ts +378 -0
  67. package/dist/extensions/pi-hermes-memory/src/lifecycle-timing.ts +54 -0
  68. package/dist/extensions/pi-hermes-memory/src/paths.ts +61 -0
  69. package/dist/extensions/pi-hermes-memory/src/project-context.ts +13 -0
  70. package/dist/extensions/pi-hermes-memory/src/project-memory-migration.ts +93 -0
  71. package/dist/extensions/pi-hermes-memory/src/project.ts +157 -0
  72. package/dist/extensions/pi-hermes-memory/src/prompt-context.ts +55 -0
  73. package/dist/extensions/pi-hermes-memory/src/store/atomic-lock-coordinator.ts +375 -0
  74. package/dist/extensions/pi-hermes-memory/src/store/canonical-storage-path.ts +54 -0
  75. package/dist/extensions/pi-hermes-memory/src/store/content-scanner.ts +101 -0
  76. package/dist/extensions/pi-hermes-memory/src/store/db.ts +1180 -0
  77. package/dist/extensions/pi-hermes-memory/src/store/fts-query.ts +101 -0
  78. package/dist/extensions/pi-hermes-memory/src/store/markdown-mutation-lock.ts +38 -0
  79. package/dist/extensions/pi-hermes-memory/src/store/memory-lookup.ts +24 -0
  80. package/dist/extensions/pi-hermes-memory/src/store/memory-store.ts +1191 -0
  81. package/dist/extensions/pi-hermes-memory/src/store/recovery-maintenance.ts +66 -0
  82. package/dist/extensions/pi-hermes-memory/src/store/schema.ts +118 -0
  83. package/dist/extensions/pi-hermes-memory/src/store/session-anchor-search.ts +472 -0
  84. package/dist/extensions/pi-hermes-memory/src/store/session-indexer.ts +511 -0
  85. package/dist/extensions/pi-hermes-memory/src/store/session-parser.ts +213 -0
  86. package/dist/extensions/pi-hermes-memory/src/store/session-search.ts +234 -0
  87. package/dist/extensions/pi-hermes-memory/src/store/skill-store.ts +950 -0
  88. package/dist/extensions/pi-hermes-memory/src/store/skill-utils.ts +133 -0
  89. package/dist/extensions/pi-hermes-memory/src/store/sqlite-memory-store.ts +1002 -0
  90. package/dist/extensions/pi-hermes-memory/src/store/sqlite-native.ts +241 -0
  91. package/dist/extensions/pi-hermes-memory/src/store/standing-instructions.ts +247 -0
  92. package/dist/extensions/pi-hermes-memory/src/tools/memory-search-tool.ts +95 -0
  93. package/dist/extensions/pi-hermes-memory/src/tools/memory-tool.ts +469 -0
  94. package/dist/extensions/pi-hermes-memory/src/tools/session-search-tool.ts +246 -0
  95. package/dist/extensions/pi-hermes-memory/src/tools/shared-output-view.ts +170 -0
  96. package/dist/extensions/pi-hermes-memory/src/tools/skill-tool.ts +333 -0
  97. package/dist/extensions/pi-hermes-memory/src/tools/tool-result-views.ts +105 -0
  98. package/dist/extensions/pi-hermes-memory/src/types.ts +214 -0
  99. package/dist/extensions/pi-hermes-memory/tests/config.test.ts +434 -0
  100. package/dist/extensions/pi-hermes-memory/tests/extension-root-migration.test.ts +368 -0
  101. package/dist/extensions/pi-hermes-memory/tests/handlers/auto-consolidate.test.ts +984 -0
  102. package/dist/extensions/pi-hermes-memory/tests/handlers/background-review.test.ts +950 -0
  103. package/dist/extensions/pi-hermes-memory/tests/handlers/correction-detector.test.ts +671 -0
  104. package/dist/extensions/pi-hermes-memory/tests/handlers/insights.test.ts +157 -0
  105. package/dist/extensions/pi-hermes-memory/tests/handlers/interview.test.ts +127 -0
  106. package/dist/extensions/pi-hermes-memory/tests/handlers/pi-child-process.test.ts +905 -0
  107. package/dist/extensions/pi-hermes-memory/tests/handlers/preview-context.test.ts +152 -0
  108. package/dist/extensions/pi-hermes-memory/tests/handlers/prompt-context.test.ts +166 -0
  109. package/dist/extensions/pi-hermes-memory/tests/handlers/resources-discover.test.ts +71 -0
  110. package/dist/extensions/pi-hermes-memory/tests/handlers/review-memory-ops.test.ts +614 -0
  111. package/dist/extensions/pi-hermes-memory/tests/handlers/session-backfill.test.ts +192 -0
  112. package/dist/extensions/pi-hermes-memory/tests/handlers/session-flush.test.ts +677 -0
  113. package/dist/extensions/pi-hermes-memory/tests/handlers/session-live-index.test.ts +225 -0
  114. package/dist/extensions/pi-hermes-memory/tests/handlers/skills-command.test.ts +827 -0
  115. package/dist/extensions/pi-hermes-memory/tests/handlers/standing-pin.test.ts +134 -0
  116. package/dist/extensions/pi-hermes-memory/tests/handlers/sync-markdown-memories.test.ts +491 -0
  117. package/dist/extensions/pi-hermes-memory/tests/handlers/system-prompt.test.ts +199 -0
  118. package/dist/extensions/pi-hermes-memory/tests/handlers/thinking-max.test.ts +64 -0
  119. package/dist/extensions/pi-hermes-memory/tests/index.test.ts +30 -0
  120. package/dist/extensions/pi-hermes-memory/tests/integration/flow.test.ts +126 -0
  121. package/dist/extensions/pi-hermes-memory/tests/lifecycle-timing.test.ts +76 -0
  122. package/dist/extensions/pi-hermes-memory/tests/paths.test.ts +23 -0
  123. package/dist/extensions/pi-hermes-memory/tests/project-memory-migration.test.ts +80 -0
  124. package/dist/extensions/pi-hermes-memory/tests/project-rebinding.test.ts +115 -0
  125. package/dist/extensions/pi-hermes-memory/tests/project.test.ts +120 -0
  126. package/dist/extensions/pi-hermes-memory/tests/run-all-timeout.test.ts +107 -0
  127. package/dist/extensions/pi-hermes-memory/tests/run-all.sh +45 -0
  128. package/dist/extensions/pi-hermes-memory/tests/store/atomic-lock-coordinator.test.ts +381 -0
  129. package/dist/extensions/pi-hermes-memory/tests/store/content-scanner.test.ts +388 -0
  130. package/dist/extensions/pi-hermes-memory/tests/store/db.test.ts +1046 -0
  131. package/dist/extensions/pi-hermes-memory/tests/store/markdown-mutation-lock.test.ts +34 -0
  132. package/dist/extensions/pi-hermes-memory/tests/store/memory-store.test.ts +2150 -0
  133. package/dist/extensions/pi-hermes-memory/tests/store/recovery-maintenance.test.ts +170 -0
  134. package/dist/extensions/pi-hermes-memory/tests/store/session-anchor-search.test.ts +210 -0
  135. package/dist/extensions/pi-hermes-memory/tests/store/session-indexer.test.ts +643 -0
  136. package/dist/extensions/pi-hermes-memory/tests/store/session-parser.test.ts +282 -0
  137. package/dist/extensions/pi-hermes-memory/tests/store/session-search.test.ts +287 -0
  138. package/dist/extensions/pi-hermes-memory/tests/store/skill-store.test.ts +678 -0
  139. package/dist/extensions/pi-hermes-memory/tests/store/skill-utils.test.ts +21 -0
  140. package/dist/extensions/pi-hermes-memory/tests/store/sqlite-lazy-load.test.ts +78 -0
  141. package/dist/extensions/pi-hermes-memory/tests/store/sqlite-memory-store.test.ts +543 -0
  142. package/dist/extensions/pi-hermes-memory/tests/store/sqlite-native.test.ts +164 -0
  143. package/dist/extensions/pi-hermes-memory/tests/store/standing-instructions.test.ts +171 -0
  144. package/dist/extensions/pi-hermes-memory/tests/tools/memory-search-tool.test.ts +84 -0
  145. package/dist/extensions/pi-hermes-memory/tests/tools/memory-tool.test.ts +709 -0
  146. package/dist/extensions/pi-hermes-memory/tests/tools/session-search-tool.test.ts +345 -0
  147. package/dist/extensions/pi-hermes-memory/tests/tools/shared-output-view.test.ts +362 -0
  148. package/dist/extensions/pi-hermes-memory/tests/tools/skill-tool.test.ts +478 -0
  149. package/dist/extensions/pi-hermes-memory/tests/tools/tool-result-renderer-wiring.test.ts +40 -0
  150. package/dist/extensions/pi-hermes-memory/tsconfig.json +19 -0
  151. package/dist/extensions/pi-subagents/.oxlintrc.json +43 -0
  152. package/dist/extensions/pi-subagents/AGENTS.md +5 -0
  153. package/dist/extensions/pi-subagents/CHANGELOG.md +157 -0
  154. package/dist/extensions/pi-subagents/README.md +12 -6
  155. package/dist/extensions/pi-subagents/VISION.md +81 -0
  156. package/dist/extensions/pi-subagents/agents/claude-code-writer.md +15 -0
  157. package/dist/extensions/pi-subagents/agents/claude-code.md +15 -0
  158. package/dist/extensions/pi-subagents/agents/codex-exec-writer.md +15 -0
  159. package/dist/extensions/pi-subagents/agents/codex-exec.md +15 -0
  160. package/dist/extensions/pi-subagents/agents/cursor-agent-writer.md +14 -0
  161. package/dist/extensions/pi-subagents/agents/cursor-agent.md +14 -0
  162. package/dist/extensions/pi-subagents/agents/reviewer.md +1 -3
  163. package/dist/extensions/pi-subagents/docs/agents.md +488 -0
  164. package/dist/extensions/pi-subagents/docs/configuration.md +46 -9
  165. package/dist/extensions/pi-subagents/docs/extension-api.md +42 -3
  166. package/dist/extensions/pi-subagents/docs/models.md +27 -4
  167. package/dist/extensions/pi-subagents/docs/observability.md +10 -9
  168. package/dist/extensions/pi-subagents/docs/tool-reference.md +102 -6
  169. package/dist/extensions/pi-subagents/docs/workflows.md +135 -0
  170. package/dist/extensions/pi-subagents/package-lock.json +389 -2
  171. package/dist/extensions/pi-subagents/package.json +3 -1
  172. package/dist/extensions/pi-subagents/prompts/review-loop.md +2 -2
  173. package/dist/extensions/pi-subagents/skills/council-mode/SKILL.md +1 -1
  174. package/dist/extensions/pi-subagents/skills/pi-subagents/SKILL.md +3 -1
  175. package/dist/extensions/pi-subagents/skills/pi-subagents/references/execution-controls.md +7 -3
  176. package/dist/extensions/pi-subagents/skills/pi-subagents/references/management-authoring-rpc.md +2 -2
  177. package/dist/extensions/pi-subagents/skills/pi-subagents/references/prompting-and-roles.md +26 -5
  178. package/dist/extensions/pi-subagents/src/agents/agent-management.ts +41 -22
  179. package/dist/extensions/pi-subagents/src/agents/agent-refinements.ts +4 -4
  180. package/dist/extensions/pi-subagents/src/agents/agent-serializer.ts +9 -2
  181. package/dist/extensions/pi-subagents/src/agents/agents.ts +322 -75
  182. package/dist/extensions/pi-subagents/src/agents/builtin-names.ts +6 -0
  183. package/dist/extensions/pi-subagents/src/agents/runtime-agent-events.ts +70 -0
  184. package/dist/extensions/pi-subagents/src/agents/runtime-agent-registry.ts +27 -20
  185. package/dist/extensions/pi-subagents/src/api/agents.ts +10 -5
  186. package/dist/extensions/pi-subagents/src/api/background-work.ts +5 -1
  187. package/dist/extensions/pi-subagents/src/api/delegation.ts +0 -7
  188. package/dist/extensions/pi-subagents/src/api/preflight.ts +34 -12
  189. package/dist/extensions/pi-subagents/src/extension/config.ts +70 -0
  190. package/dist/extensions/pi-subagents/src/extension/doctor.ts +3 -3
  191. package/dist/extensions/pi-subagents/src/extension/fanout-child.ts +5 -3
  192. package/dist/extensions/pi-subagents/src/extension/index.ts +83 -30
  193. package/dist/extensions/pi-subagents/src/extension/public-execution.ts +44 -15
  194. package/dist/extensions/pi-subagents/src/extension/rpc.ts +55 -19
  195. package/dist/extensions/pi-subagents/src/extension/schemas.ts +41 -15
  196. package/dist/extensions/pi-subagents/src/extension/tool-description.ts +28 -31
  197. package/dist/extensions/pi-subagents/src/inspectors/herdr/actions.ts +2 -1
  198. package/dist/extensions/pi-subagents/src/inspectors/herdr/inspector-runner.ts +2 -10
  199. package/dist/extensions/pi-subagents/src/inspectors/herdr/session-roots-codec.ts +42 -0
  200. package/dist/extensions/pi-subagents/src/integrations/herdr-status.ts +51 -3
  201. package/dist/extensions/pi-subagents/src/intercom/result-intercom.ts +2 -0
  202. package/dist/extensions/pi-subagents/src/profiles/profiles.ts +5 -6
  203. package/dist/extensions/pi-subagents/src/runs/background/active-async-capacity.ts +77 -10
  204. package/dist/extensions/pi-subagents/src/runs/background/async-execution.ts +176 -53
  205. package/dist/extensions/pi-subagents/src/runs/background/async-job-tracker.ts +19 -12
  206. package/dist/extensions/pi-subagents/src/runs/background/async-resume.ts +29 -27
  207. package/dist/extensions/pi-subagents/src/runs/background/async-retention.ts +20 -3
  208. package/dist/extensions/pi-subagents/src/runs/background/async-status-snapshot.ts +23 -261
  209. package/dist/extensions/pi-subagents/src/runs/background/async-status.ts +73 -7
  210. package/dist/extensions/pi-subagents/src/runs/background/chain-append.ts +8 -3
  211. package/dist/extensions/pi-subagents/src/runs/background/chain-root-attachment.ts +74 -8
  212. package/dist/extensions/pi-subagents/src/runs/background/fleet-view.ts +37 -21
  213. package/dist/extensions/pi-subagents/src/runs/background/inspect-rpc.ts +8 -8
  214. package/dist/extensions/pi-subagents/src/runs/background/notify.ts +184 -9
  215. package/dist/extensions/pi-subagents/src/runs/background/result-delivery-ownership.ts +45 -0
  216. package/dist/extensions/pi-subagents/src/runs/background/result-files.ts +28 -14
  217. package/dist/extensions/pi-subagents/src/runs/background/result-watcher.ts +38 -15
  218. package/dist/extensions/pi-subagents/src/runs/background/resume-guidance.ts +1 -1
  219. package/dist/extensions/pi-subagents/src/runs/background/retained-children.ts +1 -1
  220. package/dist/extensions/pi-subagents/src/runs/background/run-status.ts +50 -13
  221. package/dist/extensions/pi-subagents/src/runs/background/scheduled-runs.ts +93 -8
  222. package/dist/extensions/pi-subagents/src/runs/background/stale-run-reconciler.ts +10 -4
  223. package/dist/extensions/pi-subagents/src/runs/background/steering.ts +4 -14
  224. package/dist/extensions/pi-subagents/src/runs/background/subagent-runner.ts +549 -377
  225. package/dist/extensions/pi-subagents/src/runs/background/subagent-wait.ts +58 -10
  226. package/dist/extensions/pi-subagents/src/runs/background/terminal-run-index.ts +1 -1
  227. package/dist/extensions/pi-subagents/src/runs/background/wait-completions.ts +26 -1
  228. package/dist/extensions/pi-subagents/src/runs/background/wait-config.ts +23 -9
  229. package/dist/extensions/pi-subagents/src/runs/background/wait-tool.ts +9 -2
  230. package/dist/extensions/pi-subagents/src/runs/foreground/async-steering-action.ts +2 -2
  231. package/dist/extensions/pi-subagents/src/runs/foreground/execution.ts +204 -129
  232. package/dist/extensions/pi-subagents/src/runs/foreground/foreground-control.ts +9 -0
  233. package/dist/extensions/pi-subagents/src/runs/foreground/foreground-history.ts +23 -1
  234. package/dist/extensions/pi-subagents/src/runs/foreground/subagent-executor.ts +773 -311
  235. package/dist/extensions/pi-subagents/src/runs/foreground/workflow-detach-reconcile.ts +140 -115
  236. package/dist/extensions/pi-subagents/src/runs/shared/abort-recovery.ts +119 -0
  237. package/dist/extensions/pi-subagents/src/runs/shared/async-status-projection.ts +463 -0
  238. package/dist/extensions/pi-subagents/src/runs/shared/child-identity.ts +19 -4
  239. package/dist/extensions/pi-subagents/src/runs/shared/child-launch-plan.ts +151 -0
  240. package/dist/extensions/pi-subagents/src/runs/shared/child-protocol.ts +21 -7
  241. package/dist/extensions/pi-subagents/src/runs/shared/claude-code-adapter.ts +129 -0
  242. package/dist/extensions/pi-subagents/src/runs/shared/codex-exec-adapter.ts +129 -0
  243. package/dist/extensions/pi-subagents/src/runs/shared/completion-evidence.ts +89 -0
  244. package/dist/extensions/pi-subagents/src/runs/shared/completion-guard.ts +5 -4
  245. package/dist/extensions/pi-subagents/src/runs/shared/cursor-agent-adapter.ts +114 -0
  246. package/dist/extensions/pi-subagents/src/runs/shared/dynamic-fanout.ts +3 -3
  247. package/dist/extensions/pi-subagents/src/runs/shared/external-cli-contract.ts +167 -0
  248. package/dist/extensions/pi-subagents/src/runs/shared/external-cli-preflight.ts +122 -0
  249. package/dist/extensions/pi-subagents/src/runs/shared/external-cli-runner.ts +348 -55
  250. package/dist/extensions/pi-subagents/src/runs/shared/fast-mode-extension.ts +5 -5
  251. package/dist/extensions/pi-subagents/src/runs/shared/host-step-status.ts +230 -0
  252. package/dist/extensions/pi-subagents/src/runs/shared/lane-metadata.ts +105 -0
  253. package/dist/extensions/pi-subagents/src/runs/shared/launch-cwd.ts +16 -0
  254. package/dist/extensions/pi-subagents/src/runs/shared/long-running-guard.ts +2 -1
  255. package/dist/extensions/pi-subagents/src/runs/shared/mcp-config-sources.ts +422 -0
  256. package/dist/extensions/pi-subagents/src/runs/shared/mcp-direct-tool-allowlist.ts +221 -158
  257. package/dist/extensions/pi-subagents/src/runs/shared/mcp-direct-tool-grant.ts +197 -0
  258. package/dist/extensions/pi-subagents/src/runs/shared/model-exclusions.ts +69 -7
  259. package/dist/extensions/pi-subagents/src/runs/shared/model-fallback.ts +47 -6
  260. package/dist/extensions/pi-subagents/src/runs/shared/mutation-evidence.ts +7 -2
  261. package/dist/extensions/pi-subagents/src/runs/shared/nested-events.ts +9 -3
  262. package/dist/extensions/pi-subagents/src/runs/shared/nested-render.ts +9 -5
  263. package/dist/extensions/pi-subagents/src/runs/shared/parallel-handoff.ts +419 -7
  264. package/dist/extensions/pi-subagents/src/runs/shared/parallel-utils.ts +15 -1
  265. package/dist/extensions/pi-subagents/src/runs/shared/pi-args.ts +81 -8
  266. package/dist/extensions/pi-subagents/src/runs/shared/process-signal.ts +13 -0
  267. package/dist/extensions/pi-subagents/src/runs/shared/run-history.ts +21 -1
  268. package/dist/extensions/pi-subagents/src/runs/shared/single-output.ts +44 -4
  269. package/dist/extensions/pi-subagents/src/runs/shared/subagent-prompt-runtime.ts +98 -13
  270. package/dist/extensions/pi-subagents/src/runs/shared/subagent-startup-retry.ts +1 -1
  271. package/dist/extensions/pi-subagents/src/runs/shared/worktree-cleanup-plan.ts +847 -0
  272. package/dist/extensions/pi-subagents/src/runs/shared/worktree.ts +18 -0
  273. package/dist/extensions/pi-subagents/src/shared/child-session-name.ts +46 -0
  274. package/dist/extensions/pi-subagents/src/shared/extension-context.ts +24 -0
  275. package/dist/extensions/pi-subagents/src/shared/fork-context.ts +21 -0
  276. package/dist/extensions/pi-subagents/src/shared/formatters.ts +18 -3
  277. package/dist/extensions/pi-subagents/src/shared/launch-contract.ts +5 -1
  278. package/dist/extensions/pi-subagents/src/shared/pruned-fork.ts +450 -0
  279. package/dist/extensions/pi-subagents/src/shared/session-file-trust.ts +19 -0
  280. package/dist/extensions/pi-subagents/src/shared/session-tokens.ts +14 -3
  281. package/dist/extensions/pi-subagents/src/shared/settings.ts +19 -105
  282. package/dist/extensions/pi-subagents/src/shared/shortcuts.ts +17 -0
  283. package/dist/extensions/pi-subagents/src/shared/types.ts +359 -40
  284. package/dist/extensions/pi-subagents/src/shared/utils.ts +41 -84
  285. package/dist/extensions/pi-subagents/src/shared/workflow-child-permit.ts +116 -0
  286. package/dist/extensions/pi-subagents/src/slash/delegation-adapters.ts +1 -9
  287. package/dist/extensions/pi-subagents/src/slash/delegation-request.ts +0 -4
  288. package/dist/extensions/pi-subagents/src/slash/slash-bridge.ts +1 -2
  289. package/dist/extensions/pi-subagents/src/slash/slash-commands.ts +378 -88
  290. package/dist/extensions/pi-subagents/src/slash/slash-live-state.ts +22 -11
  291. package/dist/extensions/pi-subagents/src/slash/subagents-admin.ts +3 -0
  292. package/dist/extensions/pi-subagents/src/tui/fleet-status.ts +147 -79
  293. package/dist/extensions/pi-subagents/src/tui/fleet-transcript.ts +11 -5
  294. package/dist/extensions/pi-subagents/src/tui/fleet.ts +39 -18
  295. package/dist/extensions/pi-subagents/src/tui/render.ts +369 -46
  296. package/dist/extensions/pi-subagents/src/watchdog/turn-delta.ts +1 -1
  297. package/dist/extensions/pi-subagents/src/workflows/chat-progress.ts +6 -3
  298. package/dist/extensions/pi-subagents/src/workflows/host-command.ts +230 -0
  299. package/dist/extensions/pi-subagents/src/workflows/scripted-workflow.ts +740 -58
  300. package/dist/extensions/pi-subagents/src/workflows/workflow-child-summary.ts +121 -0
  301. package/dist/extensions/pi-subagents/src/workflows/workflow-preflight.ts +270 -0
  302. package/dist/extensions/pi-subagents/src/workflows/workflow-receipt.ts +195 -6
  303. package/dist/extensions/pi-subagents/src/workflows/workflow-settlement.ts +246 -0
  304. package/dist/extensions/pi-subagents/test/e2e/real-session-subagent.test.ts +2 -2
  305. package/dist/extensions/pi-subagents/test/integration/async-execution.test.ts +1114 -260
  306. package/dist/extensions/pi-subagents/test/integration/async-job-tracker.test.ts +34 -0
  307. package/dist/extensions/pi-subagents/test/integration/async-status.test.ts +150 -3
  308. package/dist/extensions/pi-subagents/test/integration/claude-code-smoke.test.ts +52 -0
  309. package/dist/extensions/pi-subagents/test/integration/claude-code-writer-smoke.test.ts +55 -0
  310. package/dist/extensions/pi-subagents/test/integration/codex-exec-smoke.test.ts +53 -0
  311. package/dist/extensions/pi-subagents/test/integration/codex-exec-writer-smoke.test.ts +57 -0
  312. package/dist/extensions/pi-subagents/test/integration/cursor-agent-smoke.test.ts +59 -0
  313. package/dist/extensions/pi-subagents/test/integration/cursor-agent-writer-smoke.test.ts +62 -0
  314. package/dist/extensions/pi-subagents/test/integration/error-handling.test.ts +57 -1
  315. package/dist/extensions/pi-subagents/test/integration/external-cli-runner.test.ts +1 -0
  316. package/dist/extensions/pi-subagents/test/integration/fork-context-execution.test.ts +20 -0
  317. package/dist/extensions/pi-subagents/test/integration/intercom-result-delivery.test.ts +4 -3
  318. package/dist/extensions/pi-subagents/test/integration/orca-progress-tabs.test.ts +2 -2
  319. package/dist/extensions/pi-subagents/test/integration/render-fork-badge.test.ts +54 -4
  320. package/dist/extensions/pi-subagents/test/integration/render-widget.test.ts +482 -4
  321. package/dist/extensions/pi-subagents/test/integration/result-watcher.test.ts +157 -9
  322. package/dist/extensions/pi-subagents/test/integration/session-tokens.test.ts +5 -5
  323. package/dist/extensions/pi-subagents/test/integration/single-execution.test.ts +1508 -291
  324. package/dist/extensions/pi-subagents/test/integration/slash-commands.test.ts +423 -1
  325. package/dist/extensions/pi-subagents/test/integration/template-resolution.test.ts +8 -0
  326. package/dist/extensions/pi-subagents/test/support/cursor-smoke-inputs.ts +51 -0
  327. package/dist/extensions/pi-subagents/test/support/helpers.ts +2 -0
  328. package/dist/extensions/pi-subagents/test/support/mock-pi.ts +5 -5
  329. package/dist/extensions/pi-subagents/test/support/real-session-runner.ts +6 -6
  330. package/dist/extensions/pi-subagents/test/unit/abort-recovery.test.ts +152 -0
  331. package/dist/extensions/pi-subagents/test/unit/active-async-capacity.test.ts +48 -0
  332. package/dist/extensions/pi-subagents/test/unit/agent-frontmatter.test.ts +184 -6
  333. package/dist/extensions/pi-subagents/test/unit/agent-management.test.ts +66 -6
  334. package/dist/extensions/pi-subagents/test/unit/agent-overrides.test.ts +77 -0
  335. package/dist/extensions/pi-subagents/test/unit/agent-refinements.test.ts +18 -0
  336. package/dist/extensions/pi-subagents/test/unit/anti-slop-oxlint.test.ts +86 -0
  337. package/dist/extensions/pi-subagents/test/unit/artifacts.test.ts +1 -1
  338. package/dist/extensions/pi-subagents/test/unit/async-execution.test.ts +101 -1
  339. package/dist/extensions/pi-subagents/test/unit/async-interrupt-action.test.ts +121 -0
  340. package/dist/extensions/pi-subagents/test/unit/async-recovery-descriptor.test.ts +28 -0
  341. package/dist/extensions/pi-subagents/test/unit/async-resume.test.ts +14 -7
  342. package/dist/extensions/pi-subagents/test/unit/async-retention.test.ts +53 -0
  343. package/dist/extensions/pi-subagents/test/unit/async-status-projection.test.ts +190 -0
  344. package/dist/extensions/pi-subagents/test/unit/async-status-snapshot.test.ts +42 -0
  345. package/dist/extensions/pi-subagents/test/unit/auto-drain.test.ts +8 -4
  346. package/dist/extensions/pi-subagents/test/unit/background-work.test.ts +23 -0
  347. package/dist/extensions/pi-subagents/test/unit/chain-root-attachment.test.ts +78 -1
  348. package/dist/extensions/pi-subagents/test/unit/child-launch-plan.test.ts +94 -0
  349. package/dist/extensions/pi-subagents/test/unit/child-protocol.test.ts +21 -0
  350. package/dist/extensions/pi-subagents/test/unit/child-session-name.test.ts +50 -0
  351. package/dist/extensions/pi-subagents/test/unit/claude-code-adapter.test.ts +245 -0
  352. package/dist/extensions/pi-subagents/test/unit/codex-exec-adapter.test.ts +192 -0
  353. package/dist/extensions/pi-subagents/test/unit/compaction-resume.test.ts +27 -0
  354. package/dist/extensions/pi-subagents/test/unit/completion-evidence.test.ts +150 -0
  355. package/dist/extensions/pi-subagents/test/unit/completion-guard.test.ts +21 -0
  356. package/dist/extensions/pi-subagents/test/unit/cursor-agent-adapter.test.ts +259 -0
  357. package/dist/extensions/pi-subagents/test/unit/cursor-smoke-inputs.test.ts +77 -0
  358. package/dist/extensions/pi-subagents/test/unit/delegation-api.test.ts +1 -4
  359. package/dist/extensions/pi-subagents/test/unit/doctor.test.ts +1 -1
  360. package/dist/extensions/pi-subagents/test/unit/extension-context.test.ts +27 -0
  361. package/dist/extensions/pi-subagents/test/unit/external-cli-runner.test.ts +223 -0
  362. package/dist/extensions/pi-subagents/test/unit/fast-mode-extension.test.ts +27 -0
  363. package/dist/extensions/pi-subagents/test/unit/fleet-status.test.ts +208 -2
  364. package/dist/extensions/pi-subagents/test/unit/fleet-transcript.test.ts +29 -1
  365. package/dist/extensions/pi-subagents/test/unit/fleet.test.ts +110 -1
  366. package/dist/extensions/pi-subagents/test/unit/foreground-control.test.ts +10 -0
  367. package/dist/extensions/pi-subagents/test/unit/formatters.test.ts +14 -0
  368. package/dist/extensions/pi-subagents/test/unit/herdr-inspector.test.ts +18 -9
  369. package/dist/extensions/pi-subagents/test/unit/herdr-session-roots-codec.test.ts +63 -0
  370. package/dist/extensions/pi-subagents/test/unit/herdr-shell-command.test.ts +2 -2
  371. package/dist/extensions/pi-subagents/test/unit/herdr-status-bridge.test.ts +132 -0
  372. package/dist/extensions/pi-subagents/test/unit/host-command.test.ts +156 -0
  373. package/dist/extensions/pi-subagents/test/unit/host-step-status.test.ts +98 -0
  374. package/dist/extensions/pi-subagents/test/unit/index-child-registration.test.ts +178 -26
  375. package/dist/extensions/pi-subagents/test/unit/index-segment.test.ts +2 -1
  376. package/dist/extensions/pi-subagents/test/unit/inspect-rpc.test.ts +21 -0
  377. package/dist/extensions/pi-subagents/test/unit/mcp-direct-tool-grant.test.ts +138 -0
  378. package/dist/extensions/pi-subagents/test/unit/mission-lifecycle.test.ts +1 -1
  379. package/dist/extensions/pi-subagents/test/unit/model-exclusions.test.ts +91 -0
  380. package/dist/extensions/pi-subagents/test/unit/model-fallback.test.ts +72 -7
  381. package/dist/extensions/pi-subagents/test/unit/mutation-evidence.test.ts +21 -0
  382. package/dist/extensions/pi-subagents/test/unit/nested-control.test.ts +1 -0
  383. package/dist/extensions/pi-subagents/test/unit/nested-events.test.ts +20 -2
  384. package/dist/extensions/pi-subagents/test/unit/notify.test.ts +129 -1
  385. package/dist/extensions/pi-subagents/test/unit/orca-progress-tabs.test.ts +2 -2
  386. package/dist/extensions/pi-subagents/test/unit/package-manifest.test.ts +4 -0
  387. package/dist/extensions/pi-subagents/test/unit/parallel-handoff.test.ts +358 -0
  388. package/dist/extensions/pi-subagents/test/unit/pi-args.test.ts +574 -21
  389. package/dist/extensions/pi-subagents/test/unit/pi-coding-agent-dir.test.ts +109 -1
  390. package/dist/extensions/pi-subagents/test/unit/preflight.test.ts +71 -1
  391. package/dist/extensions/pi-subagents/test/unit/profiles.test.ts +7 -7
  392. package/dist/extensions/pi-subagents/test/unit/pruned-fork.test.ts +214 -0
  393. package/dist/extensions/pi-subagents/test/unit/public-execution.test.ts +29 -8
  394. package/dist/extensions/pi-subagents/test/unit/render-helpers.test.ts +53 -0
  395. package/dist/extensions/pi-subagents/test/unit/result-delivery-ownership.test.ts +34 -0
  396. package/dist/extensions/pi-subagents/test/unit/result-files.test.ts +101 -3
  397. package/dist/extensions/pi-subagents/test/unit/retained-children.test.ts +1 -0
  398. package/dist/extensions/pi-subagents/test/unit/rpc.test.ts +10 -8
  399. package/dist/extensions/pi-subagents/test/unit/run-fanout-budget.test.ts +71 -1
  400. package/dist/extensions/pi-subagents/test/unit/run-status.test.ts +41 -7
  401. package/dist/extensions/pi-subagents/test/unit/runtime-agent-registration.test.ts +136 -2
  402. package/dist/extensions/pi-subagents/test/unit/scheduled-runs.test.ts +137 -0
  403. package/dist/extensions/pi-subagents/test/unit/schemas.test.ts +33 -12
  404. package/dist/extensions/pi-subagents/test/unit/scripted-workflow.test.ts +504 -1
  405. package/dist/extensions/pi-subagents/test/unit/single-output.test.ts +8 -0
  406. package/dist/extensions/pi-subagents/test/unit/skills-fallback.test.ts +1 -1
  407. package/dist/extensions/pi-subagents/test/unit/stale-run-reconciler.test.ts +12 -1
  408. package/dist/extensions/pi-subagents/test/unit/steering-action.test.ts +3 -5
  409. package/dist/extensions/pi-subagents/test/unit/steering.test.ts +5 -6
  410. package/dist/extensions/pi-subagents/test/unit/subagent-action-recovery.test.ts +0 -8
  411. package/dist/extensions/pi-subagents/test/unit/subagent-guide.test.ts +5 -0
  412. package/dist/extensions/pi-subagents/test/unit/subagent-prompt-runtime.test.ts +157 -2
  413. package/dist/extensions/pi-subagents/test/unit/subagent-startup-retry.test.ts +1 -1
  414. package/dist/extensions/pi-subagents/test/unit/subagent-wait.test.ts +63 -4
  415. package/dist/extensions/pi-subagents/test/unit/tool-description.test.ts +27 -23
  416. package/dist/extensions/pi-subagents/test/unit/wait-completions.test.ts +44 -0
  417. package/dist/extensions/pi-subagents/test/unit/wait-subscriptions.test.ts +1 -0
  418. package/dist/extensions/pi-subagents/test/unit/widget-nested-render.test.ts +7 -5
  419. package/dist/extensions/pi-subagents/test/unit/workflow-chat-progress.test.ts +106 -0
  420. package/dist/extensions/pi-subagents/test/unit/workflow-detach-reconcile.test.ts +484 -9
  421. package/dist/extensions/pi-subagents/test/unit/workflow-launch-params.test.ts +38 -4
  422. package/dist/extensions/pi-subagents/test/unit/workflow-preflight.test.ts +155 -0
  423. package/dist/extensions/pi-subagents/test/unit/workflow-receipt.test.ts +318 -0
  424. package/dist/extensions/pi-subagents/test/unit/workflow-resume-hint.test.ts +365 -0
  425. package/dist/extensions/pi-subagents/test/unit/worktree-cleanup-plan.test.ts +349 -0
  426. package/dist/extensions/pi-subagents/test/unit/worktree.test.ts +21 -0
  427. package/dist/extensions/pi-subagents/test/unit/writer-budget-guidance.test.ts +2 -7
  428. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/effect/index.ts +13 -0
  429. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/effect/rules/no-service-constructor-imports.ts +52 -0
  430. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/index.ts +41 -0
  431. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-chained-type-assertions.ts +77 -0
  432. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-conditional-empty-object-spread.ts +49 -0
  433. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-known-value-widening.ts +247 -0
  434. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-module-mocking.ts +91 -0
  435. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-object-parameters.ts +126 -0
  436. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-reflect-apply.ts +28 -0
  437. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-reflect-get.ts +28 -0
  438. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-runtime-typeof.ts +67 -0
  439. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-shape-in-symbol-names.ts +39 -0
  440. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-unknown-parameters.ts +83 -0
  441. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-unknown-returns.ts +115 -0
  442. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-unknown-type-aliases.ts +72 -0
  443. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-unsafe-dictionary-type.ts +134 -0
  444. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/no-widen-then-assert.ts +366 -0
  445. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/rules/require-safety-comment-for-type-assertion.ts +62 -0
  446. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/shared/dictionary-types.ts +502 -0
  447. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/shared/lexical-type-parameters.ts +60 -0
  448. package/dist/extensions/pi-subagents/tools/oxlint/anti-slop/shared/reflect-method.ts +35 -0
  449. package/dist/extensions/pi-tool-display/tests/capabilities.test.ts +1 -1
  450. package/dist/extensions/pi-tool-display/tests/settings-inspector.test.ts +2 -2
  451. package/dist/extensions/pi-tool-display/tests/tool-overrides-mcp.test.ts +1 -1
  452. package/dist/modes/interactive/interactive-mode.js +7 -7
  453. package/dist/skills/workflow/SKILL.md +65 -50
  454. package/package.json +112 -110
  455. package/dist/extensions/enable-readonly-tools.test.ts +0 -63
  456. package/dist/extensions/enable-readonly-tools.ts +0 -24
  457. package/dist/extensions/model-prompt-injector/config.json +0 -16
  458. package/dist/extensions/model-prompt-injector/index.test.ts +0 -102
  459. package/dist/extensions/model-prompt-injector/index.ts +0 -148
  460. package/dist/extensions/pi-subagents/src/runs/shared/turn-budget.ts +0 -98
  461. package/dist/extensions/pi-subagents/test/unit/turn-budget.test.ts +0 -250
@@ -0,0 +1,678 @@
1
+ /**
2
+ * Unit tests for SkillStore — scoped CRUD, migration, and Pi-native file layout.
3
+ */
4
+
5
+ import * as fs from "node:fs/promises";
6
+ import * as path from "node:path";
7
+ import * as os from "node:os";
8
+ import * as assert from "node:assert/strict";
9
+ import { describe, it, before, after, beforeEach, afterEach } from "node:test";
10
+ import { SkillStore } from "../../src/store/skill-store.js";
11
+
12
+ let ROOT_DIR = "";
13
+ let GLOBAL_SKILLS_DIR = "";
14
+ let PROJECT_SKILLS_DIR = "";
15
+ let LEGACY_SKILLS_DIR = "";
16
+ let PI_GLOBAL_SKILLS_DIR = "";
17
+ let MIGRATION_SENTINEL = "";
18
+
19
+ async function makeStore(withProject = true): Promise<SkillStore> {
20
+ return new SkillStore({
21
+ globalSkillsDir: GLOBAL_SKILLS_DIR,
22
+ piGlobalSkillsDir: PI_GLOBAL_SKILLS_DIR,
23
+ projectSkillsDir: withProject ? PROJECT_SKILLS_DIR : null,
24
+ projectName: withProject ? "demo-project" : null,
25
+ legacySkillsDir: LEGACY_SKILLS_DIR,
26
+ migrationSentinelPath: MIGRATION_SENTINEL,
27
+ });
28
+ }
29
+
30
+ async function cleanSlate(): Promise<void> {
31
+ try {
32
+ await fs.rm(ROOT_DIR, { recursive: true, force: true });
33
+ } catch {
34
+ // ignore
35
+ }
36
+ await fs.mkdir(GLOBAL_SKILLS_DIR, { recursive: true });
37
+ await fs.mkdir(PROJECT_SKILLS_DIR, { recursive: true });
38
+ await fs.mkdir(LEGACY_SKILLS_DIR, { recursive: true });
39
+ await fs.mkdir(PI_GLOBAL_SKILLS_DIR, { recursive: true });
40
+ }
41
+
42
+ async function readFile(filePath: string): Promise<string> {
43
+ return fs.readFile(filePath, "utf-8");
44
+ }
45
+
46
+ describe("SkillStore", { concurrency: 1 }, () => {
47
+ before(async () => {
48
+ ROOT_DIR = await fs.mkdtemp(path.join(os.tmpdir(), "pi-skill-test-"));
49
+ GLOBAL_SKILLS_DIR = path.join(ROOT_DIR, "global-skills");
50
+ PROJECT_SKILLS_DIR = path.join(ROOT_DIR, "project-skills");
51
+ LEGACY_SKILLS_DIR = path.join(ROOT_DIR, "legacy-skills");
52
+ PI_GLOBAL_SKILLS_DIR = path.join(ROOT_DIR, "pi-global-skills");
53
+ MIGRATION_SENTINEL = path.join(ROOT_DIR, ".skill-migration");
54
+ });
55
+
56
+ after(async () => {
57
+ try {
58
+ await fs.rm(ROOT_DIR, { recursive: true, force: true });
59
+ } catch {
60
+ // ignore
61
+ }
62
+ });
63
+
64
+ beforeEach(async () => {
65
+ await cleanSlate();
66
+ });
67
+
68
+ afterEach(async () => {
69
+ await cleanSlate();
70
+ });
71
+
72
+ describe("create()", () => {
73
+ it("writes global skills to <slug>/SKILL.md", async () => {
74
+ const store = await makeStore();
75
+ const result = await store.create(
76
+ "Debug TypeScript Errors",
77
+ "Step-by-step approach to debugging TS errors",
78
+ "## Procedure\n1. Read the error\n2. Check types",
79
+ );
80
+
81
+ assert.ok(result.success, `create failed: ${result.error}`);
82
+ assert.strictEqual(result.skillId, "global:debug-typescript-errors");
83
+ const filePath = path.join(GLOBAL_SKILLS_DIR, "debug-typescript-errors", "SKILL.md");
84
+ const raw = await readFile(filePath);
85
+ assert.ok(raw.includes('name: "debug-typescript-errors"'));
86
+ assert.ok(raw.includes('display_name: "Debug TypeScript Errors"'));
87
+ assert.ok(raw.includes('description: "Step-by-step approach to debugging TS errors"'));
88
+ assert.ok(raw.includes("version: 1"));
89
+ assert.ok(raw.includes("## Procedure"));
90
+ });
91
+
92
+ it("writes explicit project skills to projects-memory/<project>/skills/<slug>/SKILL.md", async () => {
93
+ const store = await makeStore();
94
+ const result = await store.create(
95
+ "Release App",
96
+ "Release this repository",
97
+ "## Procedure\n1. Run pnpm build\n2. Run pnpm deploy",
98
+ "project",
99
+ );
100
+
101
+ assert.ok(result.success, `create failed: ${result.error}`);
102
+ assert.strictEqual(result.skillId, "project:demo-project:release-app");
103
+ const filePath = path.join(PROJECT_SKILLS_DIR, "release-app", "SKILL.md");
104
+ const raw = await readFile(filePath);
105
+ assert.ok(raw.includes('name: "release-app"'));
106
+ assert.ok(raw.includes('display_name: "Release App"'));
107
+ assert.ok(raw.includes("Run pnpm deploy"));
108
+ });
109
+
110
+ it("classifies transferable procedures as global by default", async () => {
111
+ const store = await makeStore();
112
+ const result = await store.create(
113
+ "debug-fetch-errors",
114
+ "Reusable workflow for tracing fetch failures",
115
+ "## Procedure\n1. Reproduce the request\n2. Check the network trace\n3. Compare status codes",
116
+ );
117
+
118
+ assert.ok(result.success, `create failed: ${result.error}`);
119
+ assert.strictEqual(result.scope, "global");
120
+ assert.strictEqual(result.skillId, "global:debug-fetch-errors");
121
+ });
122
+
123
+ it("classifies repo-specific procedures as project by default", async () => {
124
+ const store = await makeStore();
125
+ const result = await store.create(
126
+ "release-demo-project",
127
+ "How to release this repo",
128
+ "## Procedure\n1. In this repo, run pnpm build\n2. Check package.json scripts\n3. Deploy from src/server",
129
+ );
130
+
131
+ assert.ok(result.success, `create failed: ${result.error}`);
132
+ assert.strictEqual(result.scope, "project");
133
+ assert.strictEqual(result.skillId, "project:demo-project:release-demo-project");
134
+ });
135
+
136
+ it("returns error for empty name", async () => {
137
+ const store = await makeStore();
138
+ const result = await store.create("", "desc", "body");
139
+ assert.ok(!result.success);
140
+ assert.ok(result.error!.includes("name is required"));
141
+ });
142
+
143
+ it("returns error for duplicate slug in same scope", async () => {
144
+ const store = await makeStore();
145
+ await store.create("my-skill", "desc", "body");
146
+
147
+ const result = await store.create("my skill", "new desc", "new body");
148
+ assert.ok(!result.success);
149
+ assert.ok(result.error!.includes("already exists"));
150
+ assert.strictEqual(result.conflictType, "duplicate");
151
+ assert.deepStrictEqual(result.similarSkillIds, ["global:my-skill"]);
152
+ assert.strictEqual(result.suggestedAction, "patch");
153
+ });
154
+
155
+ it("blocks creating a similar global skill and suggests patching", async () => {
156
+ const store = await makeStore();
157
+ await store.create(
158
+ "debug-typescript-errors",
159
+ "Step-by-step workflow for debugging TypeScript compiler errors, type mismatches, and incorrect type assumptions",
160
+ "## Procedure\n1. Reproduce\n2. Inspect inferred types",
161
+ );
162
+
163
+ const result = await store.create(
164
+ "debug-typescript-errors-fast",
165
+ "Step-by-step workflow for debugging TypeScript compiler errors, type mismatches, and incorrect type assumptions",
166
+ "## Procedure\n1. Reproduce\n2. Narrow the failing type",
167
+ );
168
+
169
+ assert.ok(!result.success);
170
+ assert.strictEqual(result.conflictType, "similar");
171
+ assert.strictEqual(result.suggestedAction, "patch");
172
+ assert.ok(result.similarSkillIds?.includes("global:debug-typescript-errors"));
173
+ assert.ok(result.error?.includes("similar global skill already exists"));
174
+
175
+ const index = await store.loadIndex("global");
176
+ assert.strictEqual(index.length, 1);
177
+ });
178
+
179
+ it("blocks near-name global collisions even when descriptions diverge", async () => {
180
+ const store = await makeStore();
181
+ await store.create(
182
+ "debug-typescript-errors",
183
+ "Step-by-step workflow for debugging TypeScript compiler errors",
184
+ "## Procedure\n1. Reproduce\n2. Inspect types",
185
+ );
186
+
187
+ const result = await store.create(
188
+ "debug-typescript-errors-runtime",
189
+ "Incident-response runbook for on-call paging, service alerts, and runtime triage escalation",
190
+ "## Procedure\n1. Acknowledge page\n2. Escalate and contain",
191
+ );
192
+
193
+ assert.ok(!result.success);
194
+ assert.strictEqual(result.conflictType, "name-collision");
195
+ assert.strictEqual(result.suggestedAction, "rename");
196
+ assert.ok(result.similarSkillIds?.includes("global:debug-typescript-errors"));
197
+ assert.ok(result.error?.includes("near-name global skill already exists"));
198
+
199
+ const index = await store.loadIndex("global");
200
+ assert.strictEqual(index.length, 1);
201
+ });
202
+
203
+ it("allows creating distinct global skills", async () => {
204
+ const store = await makeStore();
205
+ await store.create(
206
+ "debug-typescript-errors",
207
+ "Step-by-step workflow for debugging TypeScript compiler errors",
208
+ "## Procedure\n1. Reproduce\n2. Inspect types",
209
+ );
210
+
211
+ const result = await store.create(
212
+ "optimize-postgres-indexes",
213
+ "How to profile slow PostgreSQL queries and add safe indexes",
214
+ "## Procedure\n1. Capture EXPLAIN ANALYZE\n2. Evaluate index options",
215
+ );
216
+
217
+ assert.ok(result.success, `create failed unexpectedly: ${result.error}`);
218
+ assert.strictEqual(result.skillId, "global:optimize-postgres-indexes");
219
+
220
+ const index = await store.loadIndex("global");
221
+ assert.strictEqual(index.length, 2);
222
+ });
223
+
224
+ it("does not allow project scope without an active project", async () => {
225
+ const store = await makeStore(false);
226
+ const result = await store.create("repo-only", "desc", "body", "project");
227
+ assert.ok(!result.success);
228
+ assert.ok(result.error!.includes("active project"));
229
+ });
230
+ });
231
+
232
+ describe("loadIndex()", () => {
233
+ it("returns both global and project skills", async () => {
234
+ const store = await makeStore();
235
+ await store.create("skill-a", "First skill", "body a");
236
+ await store.create("skill-b", "Second skill", "body b", "project");
237
+
238
+ const index = await store.loadIndex();
239
+ assert.strictEqual(index.length, 2);
240
+ assert.ok(index.some((skill) => skill.skillId === "global:skill-a"));
241
+ assert.ok(index.some((skill) => skill.skillId === "project:demo-project:skill-b"));
242
+ });
243
+
244
+ it("includes existing user-managed global Pi skills", async () => {
245
+ const store = await makeStore();
246
+ const customDir = path.join(GLOBAL_SKILLS_DIR, "manual-skill");
247
+ await fs.mkdir(customDir, { recursive: true });
248
+ await fs.writeFile(path.join(customDir, "SKILL.md"), [
249
+ "---",
250
+ "name: manual-skill",
251
+ "description: A manually created Pi skill",
252
+ "---",
253
+ "# Manual Skill",
254
+ ].join("\n"), "utf-8");
255
+
256
+ const index = await store.loadIndex();
257
+ assert.strictEqual(index.length, 1);
258
+ assert.strictEqual(index[0].skillId, "global:manual-skill");
259
+ assert.strictEqual(index[0].scope, "global");
260
+ });
261
+
262
+ it("returns empty array when no skills exist", async () => {
263
+ const store = await makeStore();
264
+ const index = await store.loadIndex();
265
+ assert.deepStrictEqual(index, []);
266
+ });
267
+
268
+ it("sorts skills by updated date descending, then created date descending", async () => {
269
+ const store = await makeStore();
270
+ const olderDir = path.join(GLOBAL_SKILLS_DIR, "older-skill");
271
+ const newerDir = path.join(GLOBAL_SKILLS_DIR, "newer-skill");
272
+ await fs.mkdir(olderDir, { recursive: true });
273
+ await fs.mkdir(newerDir, { recursive: true });
274
+ await fs.writeFile(path.join(olderDir, "SKILL.md"), [
275
+ "---",
276
+ 'name: "older-skill"',
277
+ 'description: "Older skill"',
278
+ "version: 2",
279
+ 'created: "2026-05-18"',
280
+ 'updated: "2026-05-20"',
281
+ "---",
282
+ "## Procedure",
283
+ "1. Old",
284
+ ].join("\n"), "utf-8");
285
+ await fs.writeFile(path.join(newerDir, "SKILL.md"), [
286
+ "---",
287
+ 'name: "newer-skill"',
288
+ 'description: "Newer skill"',
289
+ "version: 1",
290
+ 'created: "2026-05-19"',
291
+ 'updated: "2026-05-21"',
292
+ "---",
293
+ "## Procedure",
294
+ "1. New",
295
+ ].join("\n"), "utf-8");
296
+
297
+ const index = await store.loadIndex("global");
298
+ assert.strictEqual(index[0]?.skillId, "global:newer-skill");
299
+ assert.strictEqual(index[1]?.skillId, "global:older-skill");
300
+ assert.ok(index[0]!.updated >= index[1]!.updated);
301
+ });
302
+ });
303
+
304
+ describe("loadSkill()", () => {
305
+ it("returns full document with scope and skill id", async () => {
306
+ const store = await makeStore();
307
+ const created = await store.create("My Skill", "A test skill", "## Procedure\n1. Do it");
308
+
309
+ const doc = await store.loadSkill(created.skillId!);
310
+
311
+ assert.ok(doc);
312
+ assert.strictEqual(doc!.skillId, "global:my-skill");
313
+ assert.strictEqual(doc!.scope, "global");
314
+ assert.strictEqual(doc!.name, "my-skill");
315
+ assert.strictEqual(doc!.displayName, "My Skill");
316
+ assert.strictEqual(doc!.description, "A test skill");
317
+ assert.strictEqual(doc!.version, 1);
318
+ assert.ok(doc!.body.includes("## Procedure"));
319
+ });
320
+
321
+ it("returns null for missing skill id", async () => {
322
+ const store = await makeStore();
323
+ const doc = await store.loadSkill("global:missing");
324
+ assert.strictEqual(doc, null);
325
+ });
326
+ });
327
+
328
+ describe("patch()", () => {
329
+ it("replaces an existing section by skill id", async () => {
330
+ const store = await makeStore();
331
+ const created = await store.create("test", "desc", "## Procedure\n1. Old way\n\n## Pitfalls\nWatch out");
332
+
333
+ const result = await store.patch(created.skillId!, "Procedure", "1. New way\n2. Better way");
334
+ assert.ok(result.success, `patch failed: ${result.error}`);
335
+
336
+ const doc = await store.loadSkill(created.skillId!);
337
+ assert.ok(doc!.body.includes("1. New way"));
338
+ assert.ok(!doc!.body.includes("1. Old way"));
339
+ assert.ok(doc!.body.includes("## Pitfalls"));
340
+ });
341
+
342
+ it("appends a missing section", async () => {
343
+ const store = await makeStore();
344
+ const created = await store.create("test", "desc", "## Procedure\n1. Do it");
345
+
346
+ const result = await store.patch(created.skillId!, "Verification", "Run the tests");
347
+ assert.ok(result.success, `patch failed: ${result.error}`);
348
+
349
+ const doc = await store.loadSkill(created.skillId!);
350
+ assert.ok(doc!.body.includes("## Verification"));
351
+ assert.ok(doc!.body.includes("Run the tests"));
352
+ assert.strictEqual(doc!.version, 2);
353
+ });
354
+
355
+ it("coerces JSON string arrays into ordered Procedure steps", async () => {
356
+ const store = await makeStore();
357
+ const created = await store.create("test", "desc", "## Procedure\n1. Old\n\n## Pitfalls\n- Keep me");
358
+
359
+ const result = await store.patch(
360
+ created.skillId!,
361
+ "Procedure",
362
+ JSON.stringify(["First step", "Second step"]),
363
+ );
364
+ assert.ok(result.success, `patch failed: ${result.error}`);
365
+
366
+ const doc = await store.loadSkill(created.skillId!);
367
+ assert.match(doc!.body, /## Procedure\n1\. First step\n2\. Second step/);
368
+ assert.match(doc!.body, /## Pitfalls\n- Keep me/);
369
+ assert.doesNotMatch(doc!.body, /\[\"First step\"/);
370
+ });
371
+
372
+ it("rejects JSON object patch payloads", async () => {
373
+ const store = await makeStore();
374
+ const created = await store.create("test", "desc", "## Procedure\n1. Keep");
375
+
376
+ const result = await store.patch(created.skillId!, "Procedure", '{"steps":["a"]}');
377
+ assert.equal(result.success, false);
378
+ assert.match(result.error ?? "", /JSON object/i);
379
+
380
+ const doc = await store.loadSkill(created.skillId!);
381
+ assert.match(doc!.body, /1\. Keep/);
382
+ assert.equal(doc!.version, 1);
383
+ });
384
+
385
+ it("rejects empty patch content", async () => {
386
+ const store = await makeStore();
387
+ const created = await store.create("test", "desc", "## Procedure\n1. Keep");
388
+
389
+ const result = await store.patch(created.skillId!, "Procedure", " ");
390
+ assert.equal(result.success, false);
391
+ assert.match(result.error ?? "", /required for patch/i);
392
+ assert.equal((await store.loadSkill(created.skillId!))!.version, 1);
393
+ });
394
+
395
+ it("rejects patch content that injects section headers", async () => {
396
+ const store = await makeStore();
397
+ const created = await store.create("test", "desc", "## Procedure\n1. Keep\n\n## Pitfalls\n- Keep");
398
+
399
+ const result = await store.patch(
400
+ created.skillId!,
401
+ "Procedure",
402
+ "1. New\n\n## Pitfalls\n- Injected duplicate",
403
+ );
404
+ assert.equal(result.success, false);
405
+ assert.match(result.error ?? "", /section headers/i);
406
+
407
+ const doc = await store.loadSkill(created.skillId!);
408
+ assert.equal(doc!.version, 1);
409
+ assert.equal(doc!.body.match(/## Pitfalls/g)?.length, 1);
410
+ });
411
+ });
412
+
413
+ describe("edit()", () => {
414
+ it("replaces description and body", async () => {
415
+ const store = await makeStore();
416
+ const created = await store.create("test", "old desc", "## Old Body");
417
+
418
+ const result = await store.edit(created.skillId!, "new desc", "## New Body");
419
+ assert.ok(result.success, `edit failed: ${result.error}`);
420
+
421
+ const doc = await store.loadSkill(created.skillId!);
422
+ assert.strictEqual(doc!.description, "new desc");
423
+ assert.ok(doc!.body.includes("## New Body"));
424
+ assert.ok(!doc!.body.includes("## Old Body"));
425
+ assert.strictEqual(doc!.version, 2);
426
+ });
427
+ });
428
+
429
+ describe("move()", () => {
430
+ it("moves a global skill into the active project scope", async () => {
431
+ const store = await makeStore();
432
+ const created = await store.create("move-me", "Reusable process", "## Procedure\n1. Do it", "global");
433
+
434
+ const result = await store.move(created.skillId!, "project");
435
+ assert.ok(result.success, `move failed: ${result.error}`);
436
+ assert.strictEqual(result.skillId, "project:demo-project:move-me");
437
+ await assert.rejects(fs.access(path.join(GLOBAL_SKILLS_DIR, "move-me", "SKILL.md")));
438
+ await fs.access(path.join(PROJECT_SKILLS_DIR, "move-me", "SKILL.md"));
439
+ });
440
+
441
+ it("moves a project skill into global scope", async () => {
442
+ const store = await makeStore();
443
+ const created = await store.create("repo-runbook", "Project process", "## Procedure\n1. Run it", "project");
444
+
445
+ const result = await store.move(created.skillId!, "global");
446
+ assert.ok(result.success, `move failed: ${result.error}`);
447
+ assert.strictEqual(result.skillId, "global:repo-runbook");
448
+ await assert.rejects(fs.access(path.join(PROJECT_SKILLS_DIR, "repo-runbook", "SKILL.md")));
449
+ await fs.access(path.join(GLOBAL_SKILLS_DIR, "repo-runbook", "SKILL.md"));
450
+ });
451
+
452
+ it("blocks move when destination scope already has the same slug", async () => {
453
+ const store = await makeStore();
454
+ const globalSkill = await store.create("same-name", "global skill", "body", "global");
455
+ const projectSkill = await store.create("same-name", "project skill", "body", "project");
456
+
457
+ const result = await store.move(globalSkill.skillId!, "project");
458
+ assert.ok(!result.success);
459
+ assert.strictEqual(result.conflictType, "scope-conflict");
460
+ assert.ok(result.error?.includes("already exists"));
461
+
462
+ const globalDoc = await store.loadSkill(globalSkill.skillId!);
463
+ const projectDoc = await store.loadSkill(projectSkill.skillId!);
464
+ assert.ok(globalDoc);
465
+ assert.ok(projectDoc);
466
+ });
467
+
468
+ it("returns an error when moving to project scope without an active project", async () => {
469
+ const store = await makeStore(false);
470
+ const created = await store.create("global-skill", "desc", "body", "global");
471
+
472
+ const result = await store.move(created.skillId!, "project");
473
+ assert.ok(!result.success);
474
+ assert.ok(result.error?.includes("active project"));
475
+ });
476
+ });
477
+
478
+ describe("delete()", () => {
479
+ it("removes the skill file from disk", async () => {
480
+ const store = await makeStore();
481
+ const created = await store.create("to-delete", "desc", "body");
482
+
483
+ const result = await store.delete(created.skillId!);
484
+ assert.ok(result.success, `delete failed: ${result.error}`);
485
+
486
+ const index = await store.loadIndex();
487
+ assert.strictEqual(index.length, 0);
488
+ });
489
+
490
+ it("keeps duplicate slugs across scopes safe because ids differ", async () => {
491
+ const store = await makeStore();
492
+ const globalSkill = await store.create("same-name", "global", "body");
493
+ const projectSkill = await store.create("same-name", "project", "body", "project");
494
+
495
+ assert.ok(globalSkill.success);
496
+ assert.ok(projectSkill.success);
497
+ assert.notStrictEqual(globalSkill.skillId, projectSkill.skillId);
498
+
499
+ const index = await store.loadIndex();
500
+ assert.strictEqual(index.length, 2);
501
+ });
502
+ });
503
+
504
+ describe("migration", () => {
505
+ it("migrates legacy memory/skills/*.md files into global Pi skills", async () => {
506
+ const legacyFile = path.join(LEGACY_SKILLS_DIR, "legacy-skill.md");
507
+ await fs.writeFile(legacyFile, [
508
+ "---",
509
+ "name: Legacy Skill",
510
+ "description: Legacy migrated skill",
511
+ "version: 2",
512
+ "created: 2026-01-01",
513
+ "updated: 2026-01-02",
514
+ "---",
515
+ "## Procedure",
516
+ "1. Do the legacy thing",
517
+ ].join("\n"), "utf-8");
518
+
519
+ const store = await makeStore();
520
+ const result = await store.migrateLegacySkills();
521
+
522
+ assert.strictEqual(result.migrated, 1);
523
+ const migratedPath = path.join(GLOBAL_SKILLS_DIR, "legacy-skill", "SKILL.md");
524
+ const raw = await readFile(migratedPath);
525
+ assert.ok(raw.includes('name: "legacy-skill"'));
526
+ assert.ok(raw.includes('display_name: "Legacy Skill"'));
527
+ assert.ok(raw.includes('description: "Legacy migrated skill"'));
528
+ assert.ok(raw.includes("1. Do the legacy thing"));
529
+ });
530
+
531
+ it("does not rerun after the sentinel is created", async () => {
532
+ const legacyFile = path.join(LEGACY_SKILLS_DIR, "legacy-skill.md");
533
+ await fs.writeFile(legacyFile, [
534
+ "---",
535
+ "name: legacy-skill",
536
+ "description: Legacy migrated skill",
537
+ "---",
538
+ "body",
539
+ ].join("\n"), "utf-8");
540
+
541
+ const store = await makeStore();
542
+ const first = await store.migrateLegacySkills();
543
+ const second = await store.migrateLegacySkills();
544
+
545
+ assert.strictEqual(first.migrated, 1);
546
+ assert.strictEqual(second.migrated, 0);
547
+ });
548
+
549
+ it("does not overwrite an existing global skill unexpectedly", async () => {
550
+ const existingDir = path.join(GLOBAL_SKILLS_DIR, "legacy-skill");
551
+ await fs.mkdir(existingDir, { recursive: true });
552
+ await fs.writeFile(path.join(existingDir, "SKILL.md"), [
553
+ "---",
554
+ "name: legacy-skill",
555
+ "description: Existing global skill",
556
+ "---",
557
+ "# Existing",
558
+ ].join("\n"), "utf-8");
559
+ await fs.writeFile(path.join(LEGACY_SKILLS_DIR, "legacy-skill.md"), [
560
+ "---",
561
+ "name: legacy-skill",
562
+ "description: Legacy version",
563
+ "---",
564
+ "# Legacy",
565
+ ].join("\n"), "utf-8");
566
+
567
+ const store = await makeStore();
568
+ const result = await store.migrateLegacySkills();
569
+
570
+ assert.strictEqual(result.migrated, 0);
571
+ assert.strictEqual(result.skipped, 1);
572
+
573
+ const raw = await readFile(path.join(existingDir, "SKILL.md"));
574
+ assert.ok(raw.includes("Existing global skill"));
575
+ assert.ok(!raw.includes("Legacy version"));
576
+ });
577
+
578
+ it("migrates flat markdown files under global skills root into SKILL.md folders", async () => {
579
+ await fs.writeFile(path.join(GLOBAL_SKILLS_DIR, "flat-legacy.md"), [
580
+ "---",
581
+ "name: flat-legacy",
582
+ "description: Flat legacy skill",
583
+ "---",
584
+ "# Flat Body",
585
+ ].join("\n"), "utf-8");
586
+
587
+ const store = await makeStore();
588
+ const result = await store.migrateLegacySkills();
589
+
590
+ assert.strictEqual(result.migrated, 1);
591
+ await assert.rejects(fs.access(path.join(GLOBAL_SKILLS_DIR, "flat-legacy.md")));
592
+ const migrated = await readFile(path.join(GLOBAL_SKILLS_DIR, "flat-legacy", "SKILL.md"));
593
+ assert.ok(migrated.includes('description: "Flat legacy skill"'));
594
+ });
595
+
596
+ it("does not write the sentinel when warnings occur, so migration can retry", async () => {
597
+ await fs.mkdir(path.join(LEGACY_SKILLS_DIR, "broken.md"), { recursive: true });
598
+ const legacyFile = path.join(LEGACY_SKILLS_DIR, "legacy-skill.md");
599
+ await fs.writeFile(legacyFile, [
600
+ "---",
601
+ "name: legacy-skill",
602
+ "description: Legacy migrated skill",
603
+ "---",
604
+ "body",
605
+ ].join("\n"), "utf-8");
606
+
607
+ const store = await makeStore();
608
+ const first = await store.migrateLegacySkills();
609
+
610
+ assert.ok(first.warnings.length >= 1);
611
+ await assert.rejects(fs.access(MIGRATION_SENTINEL));
612
+
613
+ await fs.rm(path.join(LEGACY_SKILLS_DIR, "broken.md"), { recursive: true, force: true });
614
+ const second = await store.migrateLegacySkills();
615
+ assert.strictEqual(second.migrated, 0);
616
+ await fs.access(MIGRATION_SENTINEL);
617
+ });
618
+
619
+ });
620
+
621
+ describe("shadowing guard against Pi's own global skills root", () => {
622
+ it("writes global skills to the extension directory, never Pi's root", async () => {
623
+ const result = await (await makeStore()).create("deploy-service", "Deploy the service", "# Deploy", "global");
624
+
625
+ assert.strictEqual(result.success, true);
626
+ assert.ok((await readFile(path.join(GLOBAL_SKILLS_DIR, "deploy-service", "SKILL.md"))).includes("# Deploy"));
627
+ // Skills the user installed themselves stay untouched by ours (#126).
628
+ await assert.rejects(fs.access(path.join(PI_GLOBAL_SKILLS_DIR, "deploy-service")));
629
+ });
630
+
631
+ it("refuses a global name Pi already loads instead of writing a shadowed copy", async () => {
632
+ const theirs = path.join(PI_GLOBAL_SKILLS_DIR, "swarm-decompose");
633
+ await fs.mkdir(theirs, { recursive: true });
634
+ await fs.writeFile(path.join(theirs, "SKILL.md"), "---\nname: \"swarm-decompose\"\n---\n# Theirs", "utf-8");
635
+
636
+ const result = await (await makeStore()).create("swarm-decompose", "Decompose a swarm", "# Ours", "global");
637
+
638
+ // Pi loads its own root first, so writing here would succeed on disk and
639
+ // change nothing about the agent's behaviour — the #125 write-loss bug.
640
+ assert.strictEqual(result.success, false);
641
+ assert.strictEqual(result.conflictType, "name-collision");
642
+ assert.ok(result.error?.includes(path.join(theirs, "SKILL.md")));
643
+ await assert.rejects(fs.access(path.join(GLOBAL_SKILLS_DIR, "swarm-decompose")));
644
+ assert.ok((await readFile(path.join(theirs, "SKILL.md"))).includes("# Theirs"));
645
+ });
646
+
647
+ it("still allows a project skill to reuse a name taken in Pi's global root", async () => {
648
+ const theirs = path.join(PI_GLOBAL_SKILLS_DIR, "run-tests");
649
+ await fs.mkdir(theirs, { recursive: true });
650
+ await fs.writeFile(path.join(theirs, "SKILL.md"), "---\nname: \"run-tests\"\n---\n# Theirs", "utf-8");
651
+
652
+ const result = await (await makeStore()).create("run-tests", "Run this repo's tests", "# Ours", "project");
653
+
654
+ // Project skills are a different scope; only the global root collides.
655
+ assert.strictEqual(result.success, true);
656
+ assert.ok((await readFile(path.join(PROJECT_SKILLS_DIR, "run-tests", "SKILL.md"))).includes("# Ours"));
657
+ });
658
+ });
659
+
660
+ describe("dynamic project context", () => {
661
+ it("can retarget project skill creation to a new project directory", async () => {
662
+ const store = await makeStore();
663
+ const altProjectDir = path.join(ROOT_DIR, "another-project-skills");
664
+ store.setProjectContext("another-project", altProjectDir);
665
+
666
+ const result = await store.create(
667
+ "Deploy Another Project",
668
+ "Project-specific deploy flow",
669
+ "## Procedure\n1. Run npm run deploy",
670
+ "project",
671
+ );
672
+
673
+ assert.ok(result.success, `create failed: ${result.error}`);
674
+ assert.strictEqual(result.skillId, "project:another-project:deploy-another-project");
675
+ await fs.access(path.join(altProjectDir, "deploy-another-project", "SKILL.md"));
676
+ });
677
+ });
678
+ });