@selesai/code 0.3.0 → 0.3.2

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 (236) hide show
  1. package/dist/config.d.ts +3 -4
  2. package/dist/config.d.ts.map +1 -1
  3. package/dist/config.js +3 -5
  4. package/dist/config.js.map +1 -1
  5. package/dist/core/diagnostics.d.ts +1 -0
  6. package/dist/core/diagnostics.d.ts.map +1 -1
  7. package/dist/core/diagnostics.js.map +1 -1
  8. package/dist/core/package-manager.d.ts +10 -0
  9. package/dist/core/package-manager.d.ts.map +1 -1
  10. package/dist/core/package-manager.js +93 -8
  11. package/dist/core/package-manager.js.map +1 -1
  12. package/dist/core/package-manager.test.d.ts +2 -0
  13. package/dist/core/package-manager.test.d.ts.map +1 -0
  14. package/dist/core/package-manager.test.js +20 -0
  15. package/dist/core/package-manager.test.js.map +1 -0
  16. package/dist/core/resource-loader.d.ts +3 -0
  17. package/dist/core/resource-loader.d.ts.map +1 -1
  18. package/dist/core/resource-loader.js +10 -0
  19. package/dist/core/resource-loader.js.map +1 -1
  20. package/dist/core/settings-manager.d.ts +1 -0
  21. package/dist/core/settings-manager.d.ts.map +1 -1
  22. package/dist/core/settings-manager.js.map +1 -1
  23. package/dist/defaults/models.json +56 -72
  24. package/dist/extensions/caveman/caveman-instructions.cjs +39 -0
  25. package/dist/extensions/caveman/index.js +116 -0
  26. package/dist/extensions/caveman/package.json +8 -0
  27. package/dist/extensions/caveman/test/extension.test.js +185 -0
  28. package/dist/extensions/caveman/test/helpers.test.js +67 -0
  29. package/dist/extensions/pi-intercom/broker/broker.ts +2 -3
  30. package/dist/extensions/pi-intercom/broker/paths.ts +10 -5
  31. package/dist/extensions/pi-intercom/broker/spawn.ts +2 -3
  32. package/dist/extensions/pi-intercom/config.ts +2 -2
  33. package/dist/extensions/pi-intercom/index.ts +2 -2
  34. package/dist/extensions/pi-intercom/package.json +2 -2
  35. package/dist/extensions/pi-intercom/ui/compose.ts +3 -3
  36. package/dist/extensions/pi-intercom/ui/inline-message.ts +3 -3
  37. package/dist/extensions/pi-intercom/ui/session-list.ts +3 -3
  38. package/dist/extensions/pi-powerline-footer/bash-mode/history.ts +2 -1
  39. package/dist/extensions/pi-powerline-footer/index.ts +9 -16
  40. package/dist/extensions/pi-powerline-footer/welcome.ts +23 -16
  41. package/dist/extensions/pi-powerline-footer/working-vibes.ts +2 -9
  42. package/dist/extensions/pi-rewind-hook/index.ts +1 -1
  43. package/dist/extensions/pi-rewind-hook/package.json +1 -1
  44. package/dist/extensions/pi-subagents/README.md +224 -52
  45. package/dist/extensions/pi-subagents/install.mjs +93 -0
  46. package/dist/extensions/pi-subagents/package.json +4 -5
  47. package/dist/extensions/pi-subagents/prompts/parallel-context-build.md +1 -1
  48. package/dist/extensions/pi-subagents/prompts/parallel-handoff-plan.md +1 -1
  49. package/dist/extensions/pi-subagents/skills/pi-subagents/SKILL.md +76 -13
  50. package/dist/extensions/pi-subagents/src/agents/agent-management.ts +179 -2
  51. package/dist/extensions/pi-subagents/src/agents/agent-memory.ts +254 -0
  52. package/dist/extensions/pi-subagents/src/agents/agent-serializer.ts +11 -0
  53. package/dist/extensions/pi-subagents/src/agents/agents.ts +193 -19
  54. package/dist/extensions/pi-subagents/src/agents/chain-serializer.ts +27 -2
  55. package/dist/extensions/pi-subagents/src/extension/config.ts +27 -4
  56. package/dist/extensions/pi-subagents/src/extension/doctor.ts +1 -7
  57. package/dist/extensions/pi-subagents/src/extension/fanout-child.ts +3 -2
  58. package/dist/extensions/pi-subagents/src/extension/index.ts +73 -71
  59. package/dist/extensions/pi-subagents/src/extension/rpc.ts +369 -0
  60. package/dist/extensions/pi-subagents/src/extension/schemas.ts +54 -10
  61. package/dist/extensions/pi-subagents/src/extension/tool-description.ts +200 -0
  62. package/dist/extensions/pi-subagents/src/intercom/intercom-bridge.ts +21 -253
  63. package/dist/extensions/pi-subagents/src/intercom/native-supervisor-channel.ts +519 -0
  64. package/dist/extensions/pi-subagents/src/profiles/profiles.ts +1 -1
  65. package/dist/extensions/pi-subagents/src/runs/background/async-execution.ts +195 -105
  66. package/dist/extensions/pi-subagents/src/runs/background/async-job-tracker.ts +88 -2
  67. package/dist/extensions/pi-subagents/src/runs/background/async-status.ts +67 -10
  68. package/dist/extensions/pi-subagents/src/runs/background/chain-root-attachment.ts +34 -4
  69. package/dist/extensions/pi-subagents/src/runs/background/completion-batcher.ts +166 -0
  70. package/dist/extensions/pi-subagents/src/runs/background/control-channel.ts +156 -1
  71. package/dist/extensions/pi-subagents/src/runs/background/fleet-view.ts +515 -0
  72. package/dist/extensions/pi-subagents/src/runs/background/notify.ts +161 -44
  73. package/dist/extensions/pi-subagents/src/runs/background/result-watcher.ts +1 -2
  74. package/dist/extensions/pi-subagents/src/runs/background/run-id-resolver.ts +3 -2
  75. package/dist/extensions/pi-subagents/src/runs/background/run-status.ts +167 -6
  76. package/dist/extensions/pi-subagents/src/runs/background/scheduled-runs.ts +514 -0
  77. package/dist/extensions/pi-subagents/src/runs/background/stale-run-reconciler.ts +28 -1
  78. package/dist/extensions/pi-subagents/src/runs/background/subagent-runner.ts +873 -204
  79. package/dist/extensions/pi-subagents/src/runs/background/wait.ts +353 -0
  80. package/dist/extensions/pi-subagents/src/runs/foreground/chain-execution.ts +123 -27
  81. package/dist/extensions/pi-subagents/src/runs/foreground/execution.ts +174 -70
  82. package/dist/extensions/pi-subagents/src/runs/foreground/subagent-executor.ts +569 -81
  83. package/dist/extensions/pi-subagents/src/runs/shared/acceptance.ts +45 -22
  84. package/dist/extensions/pi-subagents/src/runs/shared/completion-guard.ts +3 -1
  85. package/dist/extensions/pi-subagents/src/runs/shared/dynamic-fanout.ts +2 -2
  86. package/dist/extensions/pi-subagents/src/runs/shared/model-fallback.ts +172 -21
  87. package/dist/extensions/pi-subagents/src/runs/shared/model-scope.ts +128 -0
  88. package/dist/extensions/pi-subagents/src/runs/shared/nested-events.ts +89 -0
  89. package/dist/extensions/pi-subagents/src/runs/shared/parallel-utils.ts +50 -1
  90. package/dist/extensions/pi-subagents/src/runs/shared/pi-args.ts +35 -4
  91. package/dist/extensions/pi-subagents/src/runs/shared/pi-spawn.ts +63 -45
  92. package/dist/extensions/pi-subagents/src/runs/shared/single-output.ts +2 -0
  93. package/dist/extensions/pi-subagents/src/runs/shared/subagent-prompt-runtime.ts +125 -5
  94. package/dist/extensions/pi-subagents/src/runs/shared/tool-budget.ts +74 -0
  95. package/dist/extensions/pi-subagents/src/runs/shared/turn-budget.ts +52 -0
  96. package/dist/extensions/pi-subagents/src/runs/shared/worktree.ts +28 -5
  97. package/dist/extensions/pi-subagents/src/shared/artifacts.ts +16 -1
  98. package/dist/extensions/pi-subagents/src/shared/atomic-json.ts +15 -2
  99. package/dist/extensions/pi-subagents/src/shared/child-transcript.ts +212 -0
  100. package/dist/extensions/pi-subagents/src/shared/fork-context.ts +133 -22
  101. package/dist/extensions/pi-subagents/src/shared/settings.ts +3 -1
  102. package/dist/extensions/pi-subagents/src/shared/types.ts +197 -4
  103. package/dist/extensions/pi-subagents/src/shared/utils.ts +100 -15
  104. package/dist/extensions/pi-subagents/src/slash/prompt-workflows.ts +330 -0
  105. package/dist/extensions/pi-subagents/src/slash/slash-commands.ts +135 -4
  106. package/dist/extensions/pi-subagents/src/tui/render.ts +32 -12
  107. package/dist/extensions/pi-subagents/test/e2e/real-session-subagent.test.ts +94 -0
  108. package/dist/extensions/pi-subagents/test/integration/async-execution.test.ts +2772 -0
  109. package/dist/extensions/pi-subagents/test/integration/async-job-tracker.test.ts +1107 -0
  110. package/dist/extensions/pi-subagents/test/integration/async-status.test.ts +463 -0
  111. package/dist/extensions/pi-subagents/test/integration/chain-clarify.test.ts +274 -0
  112. package/dist/extensions/pi-subagents/test/integration/chain-execution.test.ts +1535 -0
  113. package/dist/extensions/pi-subagents/test/integration/detect-error.test.ts +242 -0
  114. package/dist/extensions/pi-subagents/test/integration/doctor-executor.test.ts +128 -0
  115. package/dist/extensions/pi-subagents/test/integration/error-handling.test.ts +252 -0
  116. package/dist/extensions/pi-subagents/test/integration/foreground-result-size.test.ts +188 -0
  117. package/dist/extensions/pi-subagents/test/integration/fork-context-execution.test.ts +1306 -0
  118. package/dist/extensions/pi-subagents/test/integration/intercom-result-delivery.test.ts +1033 -0
  119. package/dist/extensions/pi-subagents/test/integration/parallel-execution.test.ts +397 -0
  120. package/dist/extensions/pi-subagents/test/integration/render-fork-badge.test.ts +583 -0
  121. package/dist/extensions/pi-subagents/test/integration/render-widget.test.ts +691 -0
  122. package/dist/extensions/pi-subagents/test/integration/result-watcher.test.ts +892 -0
  123. package/dist/extensions/pi-subagents/test/integration/session-tokens.test.ts +62 -0
  124. package/dist/extensions/pi-subagents/test/integration/single-execution.test.ts +1824 -0
  125. package/dist/extensions/pi-subagents/test/integration/slash-commands.test.ts +1284 -0
  126. package/dist/extensions/pi-subagents/test/integration/slash-live-state.test.ts +127 -0
  127. package/dist/extensions/pi-subagents/test/integration/template-resolution.test.ts +299 -0
  128. package/dist/extensions/pi-subagents/test/integration/top-level-async.test.ts +37 -0
  129. package/dist/extensions/pi-subagents/test/support/helpers.ts +189 -0
  130. package/dist/extensions/pi-subagents/test/support/mock-pi-script.mjs +335 -0
  131. package/dist/extensions/pi-subagents/test/support/mock-pi.ts +129 -0
  132. package/dist/extensions/pi-subagents/test/support/real-session-child-cli.mjs +184 -0
  133. package/dist/extensions/pi-subagents/test/support/real-session-runner.ts +285 -0
  134. package/dist/extensions/pi-subagents/test/support/register-loader.mjs +15 -0
  135. package/dist/extensions/pi-subagents/test/support/ts-loader.mjs +108 -0
  136. package/dist/extensions/pi-subagents/test/unit/acceptance.test.ts +421 -0
  137. package/dist/extensions/pi-subagents/test/unit/agent-disabled.test.ts +213 -0
  138. package/dist/extensions/pi-subagents/test/unit/agent-eject-disable.test.ts +343 -0
  139. package/dist/extensions/pi-subagents/test/unit/agent-frontmatter.test.ts +1199 -0
  140. package/dist/extensions/pi-subagents/test/unit/agent-management.test.ts +556 -0
  141. package/dist/extensions/pi-subagents/test/unit/agent-memory.test.ts +420 -0
  142. package/dist/extensions/pi-subagents/test/unit/agent-overrides.test.ts +579 -0
  143. package/dist/extensions/pi-subagents/test/unit/agent-scope.test.ts +20 -0
  144. package/dist/extensions/pi-subagents/test/unit/agent-selection.test.ts +88 -0
  145. package/dist/extensions/pi-subagents/test/unit/artifacts.test.ts +24 -0
  146. package/dist/extensions/pi-subagents/test/unit/async-execution.test.ts +86 -0
  147. package/dist/extensions/pi-subagents/test/unit/async-interrupt-action.test.ts +221 -0
  148. package/dist/extensions/pi-subagents/test/unit/async-permission-session.test.ts +98 -0
  149. package/dist/extensions/pi-subagents/test/unit/async-resume.test.ts +285 -0
  150. package/dist/extensions/pi-subagents/test/unit/atomic-json.test.ts +112 -0
  151. package/dist/extensions/pi-subagents/test/unit/chain-append.test.ts +206 -0
  152. package/dist/extensions/pi-subagents/test/unit/chain-root-attachment.test.ts +159 -0
  153. package/dist/extensions/pi-subagents/test/unit/chain-serializer.test.ts +229 -0
  154. package/dist/extensions/pi-subagents/test/unit/child-transcript.test.ts +225 -0
  155. package/dist/extensions/pi-subagents/test/unit/close-grace-timer.test.ts +101 -0
  156. package/dist/extensions/pi-subagents/test/unit/completion-batcher.test.ts +228 -0
  157. package/dist/extensions/pi-subagents/test/unit/completion-dedupe.test.ts +36 -0
  158. package/dist/extensions/pi-subagents/test/unit/completion-guard.test.ts +152 -0
  159. package/dist/extensions/pi-subagents/test/unit/config-dir-runtime.test.ts +72 -0
  160. package/dist/extensions/pi-subagents/test/unit/control-channel.test.ts +327 -0
  161. package/dist/extensions/pi-subagents/test/unit/control-notices.test.ts +161 -0
  162. package/dist/extensions/pi-subagents/test/unit/doctor.test.ts +159 -0
  163. package/dist/extensions/pi-subagents/test/unit/dynamic-fanout.test.ts +217 -0
  164. package/dist/extensions/pi-subagents/test/unit/extra-agent-dirs.test.ts +88 -0
  165. package/dist/extensions/pi-subagents/test/unit/file-coalescer.test.ts +68 -0
  166. package/dist/extensions/pi-subagents/test/unit/foreground-tool-call-compaction.test.ts +78 -0
  167. package/dist/extensions/pi-subagents/test/unit/fork-context.test.ts +415 -0
  168. package/dist/extensions/pi-subagents/test/unit/get-final-output.test.ts +126 -0
  169. package/dist/extensions/pi-subagents/test/unit/index-child-registration.test.ts +271 -0
  170. package/dist/extensions/pi-subagents/test/unit/intercom-bridge.test.ts +173 -0
  171. package/dist/extensions/pi-subagents/test/unit/jsonl-writer.test.ts +119 -0
  172. package/dist/extensions/pi-subagents/test/unit/live-async-resume.test.ts +170 -0
  173. package/dist/extensions/pi-subagents/test/unit/model-fallback.test.ts +346 -0
  174. package/dist/extensions/pi-subagents/test/unit/model-info.test.ts +62 -0
  175. package/dist/extensions/pi-subagents/test/unit/model-scope-settings.test.ts +87 -0
  176. package/dist/extensions/pi-subagents/test/unit/model-scope.test.ts +145 -0
  177. package/dist/extensions/pi-subagents/test/unit/native-supervisor-channel.test.ts +162 -0
  178. package/dist/extensions/pi-subagents/test/unit/nested-control.test.ts +470 -0
  179. package/dist/extensions/pi-subagents/test/unit/nested-events.test.ts +329 -0
  180. package/dist/extensions/pi-subagents/test/unit/notify.test.ts +319 -0
  181. package/dist/extensions/pi-subagents/test/unit/package-manifest.test.ts +70 -0
  182. package/dist/extensions/pi-subagents/test/unit/parallel-utils.test.ts +247 -0
  183. package/dist/extensions/pi-subagents/test/unit/path-handling.test.ts +113 -0
  184. package/dist/extensions/pi-subagents/test/unit/path-resolution.test.ts +94 -0
  185. package/dist/extensions/pi-subagents/test/unit/pi-args.test.ts +803 -0
  186. package/dist/extensions/pi-subagents/test/unit/pi-coding-agent-dir.test.ts +209 -0
  187. package/dist/extensions/pi-subagents/test/unit/pi-spawn.test.ts +238 -0
  188. package/dist/extensions/pi-subagents/test/unit/proactive-skills.test.ts +118 -0
  189. package/dist/extensions/pi-subagents/test/unit/profiles.test.ts +299 -0
  190. package/dist/extensions/pi-subagents/test/unit/prompt-template-bridge.test.ts +408 -0
  191. package/dist/extensions/pi-subagents/test/unit/prompt-workflows.test.ts +134 -0
  192. package/dist/extensions/pi-subagents/test/unit/recursion-guard.test.ts +257 -0
  193. package/dist/extensions/pi-subagents/test/unit/render-helpers.test.ts +209 -0
  194. package/dist/extensions/pi-subagents/test/unit/result-intercom.test.ts +226 -0
  195. package/dist/extensions/pi-subagents/test/unit/rpc.test.ts +294 -0
  196. package/dist/extensions/pi-subagents/test/unit/run-id-resolver.test.ts +147 -0
  197. package/dist/extensions/pi-subagents/test/unit/run-status.test.ts +997 -0
  198. package/dist/extensions/pi-subagents/test/unit/scheduled-runs.test.ts +478 -0
  199. package/dist/extensions/pi-subagents/test/unit/schemas.test.ts +562 -0
  200. package/dist/extensions/pi-subagents/test/unit/single-output.test.ts +213 -0
  201. package/dist/extensions/pi-subagents/test/unit/skills-fallback.test.ts +438 -0
  202. package/dist/extensions/pi-subagents/test/unit/slash-bridge.test.ts +54 -0
  203. package/dist/extensions/pi-subagents/test/unit/slash-chain-groups.test.ts +464 -0
  204. package/dist/extensions/pi-subagents/test/unit/stale-run-reconciler.test.ts +255 -0
  205. package/dist/extensions/pi-subagents/test/unit/status-format.test.ts +19 -0
  206. package/dist/extensions/pi-subagents/test/unit/subagent-control.test.ts +245 -0
  207. package/dist/extensions/pi-subagents/test/unit/subagent-prompt-runtime.test.ts +517 -0
  208. package/dist/extensions/pi-subagents/test/unit/temp-paths.test.ts +72 -0
  209. package/dist/extensions/pi-subagents/test/unit/tool-budget.test.ts +52 -0
  210. package/dist/extensions/pi-subagents/test/unit/tool-description.test.ts +237 -0
  211. package/dist/extensions/pi-subagents/test/unit/total-cost.test.ts +60 -0
  212. package/dist/extensions/pi-subagents/test/unit/ts-loader.test.ts +33 -0
  213. package/dist/extensions/pi-subagents/test/unit/turn-budget.test.ts +202 -0
  214. package/dist/extensions/pi-subagents/test/unit/types-fork-preamble.test.ts +23 -0
  215. package/dist/extensions/pi-subagents/test/unit/wait.test.ts +398 -0
  216. package/dist/extensions/pi-subagents/test/unit/widget-nested-render.test.ts +88 -0
  217. package/dist/extensions/pi-subagents/test/unit/windows-hide-spawn.test.ts +26 -0
  218. package/dist/extensions/pi-subagents/test/unit/workflow-graph.test.ts +134 -0
  219. package/dist/extensions/pi-subagents/test/unit/worktree.test.ts +479 -0
  220. package/dist/extensions/pi-web-agent/src/changelog-notice.ts +5 -2
  221. package/dist/extensions/pi-web-agent/src/presentation/config-store.ts +6 -4
  222. package/dist/extensions/tokenin-onboarding.ts +52 -42
  223. package/dist/main.d.ts.map +1 -1
  224. package/dist/main.js +6 -1
  225. package/dist/main.js.map +1 -1
  226. package/dist/modes/interactive/components/first-time-setup.d.ts +1 -1
  227. package/dist/modes/interactive/components/first-time-setup.d.ts.map +1 -1
  228. package/dist/modes/interactive/components/first-time-setup.js +19 -5
  229. package/dist/modes/interactive/components/first-time-setup.js.map +1 -1
  230. package/dist/skills/caveman/SKILL.md +49 -0
  231. package/dist/skills/handoff-text/SKILL.md +13 -0
  232. package/docs/docs.json +4 -0
  233. package/docs/extensions.md +2 -0
  234. package/docs/settings.md +15 -0
  235. package/docs/shared-host-extensions.md +109 -0
  236. package/package.json +1 -1
@@ -0,0 +1,116 @@
1
+ import { createRequire } from "node:module";
2
+
3
+ const require = createRequire(import.meta.url);
4
+ const { getCavemanInstructions } = require("./caveman-instructions.cjs");
5
+
6
+ export { getCavemanInstructions };
7
+
8
+ export function resolveSessionActive(entries, fallback = true) {
9
+ if (!Array.isArray(entries)) return fallback;
10
+
11
+ for (let i = entries.length - 1; i >= 0; i -= 1) {
12
+ const entry = entries[i];
13
+ if (entry?.type !== "custom" || entry?.customType !== "caveman-mode") continue;
14
+
15
+ const active = entry?.data?.active;
16
+ if (typeof active === "boolean") return active;
17
+ }
18
+
19
+ return fallback;
20
+ }
21
+
22
+ export function parseCavemanCommand(text) {
23
+ const normalizedText = String(text || "").trim().toLowerCase();
24
+ if (!normalizedText) return { type: "toggle" };
25
+
26
+ const [primary] = normalizedText.split(/\s+/);
27
+ if (primary === "status") return { type: "status" };
28
+ if (primary === "on" || primary === "enable") return { type: "set", active: true };
29
+ if (primary === "off" || primary === "disable") return { type: "set", active: false };
30
+ return { type: "invalid", reason: "unknown-subcommand", arg: primary };
31
+ }
32
+
33
+ export function isDeactivationCommand(text) {
34
+ const t = String(text || "").trim().toLowerCase().replace(/[.!?\s]+$/, "");
35
+ return t === "stop caveman" || t === "normal mode";
36
+ }
37
+
38
+ export default function cavemanExtension(pi) {
39
+ let active = true;
40
+ let isActive = false;
41
+ let lastCtx = null;
42
+
43
+ function syncStatus(ctx) {
44
+ if (ctx) lastCtx = ctx;
45
+ const c = ctx || lastCtx;
46
+ if (!c?.ui?.setStatus || !c.ui.theme?.fg) return;
47
+ const theme = c.ui.theme;
48
+ if (!active) {
49
+ c.ui.setStatus("caveman", "");
50
+ return;
51
+ }
52
+ const indicator = isActive ? theme.fg("accent", "●") : theme.fg("dim", "○");
53
+ c.ui.setStatus("caveman", indicator + " 🦴 " + theme.fg("muted", "caveman: ") + theme.fg("text", active ? "ON" : "OFF"));
54
+ }
55
+
56
+ const setActive = (value, ctx) => {
57
+ active = Boolean(value);
58
+ pi.appendEntry("caveman-mode", { active });
59
+ syncStatus(ctx);
60
+ ctx?.ui?.notify?.(`Caveman mode ${active ? "enabled" : "disabled"}.`, "info");
61
+ };
62
+
63
+ pi.registerCommand("caveman", {
64
+ description: "Toggle or report Caveman mode",
65
+ handler: async (args, ctx) => {
66
+ const parsed = parseCavemanCommand(args);
67
+
68
+ if (parsed.type === "status") {
69
+ ctx?.ui?.notify?.(`Caveman: ${active ? "ON" : "OFF"}`, "info");
70
+ return;
71
+ }
72
+
73
+ if (parsed.type === "toggle") {
74
+ setActive(!active, ctx);
75
+ return;
76
+ }
77
+
78
+ if (parsed.type === "set") {
79
+ setActive(parsed.active, ctx);
80
+ return;
81
+ }
82
+
83
+ ctx?.ui?.notify?.("Unknown /caveman subcommand. Use: on, off, status.", "warning");
84
+ },
85
+ });
86
+
87
+ pi.on("input", async (event) => {
88
+ if (event?.source === "extension") return;
89
+ const text = String(event?.text || "");
90
+ if (active && isDeactivationCommand(text)) {
91
+ setActive(false);
92
+ }
93
+ });
94
+
95
+ pi.on("session_start", async (_event, ctx) => {
96
+ const entries = ctx?.sessionManager?.getBranch?.() || ctx?.sessionManager?.getEntries?.() || [];
97
+ active = resolveSessionActive(entries, true);
98
+ syncStatus(ctx);
99
+ ctx?.ui?.notify?.(`Caveman loaded: ${active ? "ON" : "OFF"}`, "info");
100
+ });
101
+
102
+ pi.on("agent_start", async (_event, ctx) => {
103
+ isActive = true;
104
+ syncStatus(ctx);
105
+ });
106
+
107
+ pi.on("agent_end", async (_event, ctx) => {
108
+ isActive = false;
109
+ syncStatus(ctx);
110
+ });
111
+
112
+ pi.on("before_agent_start", async (event) => {
113
+ if (!active) return;
114
+ return { systemPrompt: `${event.systemPrompt}\n\n${getCavemanInstructions()}` };
115
+ });
116
+ }
@@ -0,0 +1,8 @@
1
+ {
2
+ "name": "caveman",
3
+ "private": true,
4
+ "type": "module",
5
+ "scripts": {
6
+ "test": "node --test ./test/*.test.js"
7
+ }
8
+ }
@@ -0,0 +1,185 @@
1
+ import assert from "node:assert/strict";
2
+ import test from "node:test";
3
+
4
+ import cavemanExtension from "../index.js";
5
+
6
+ function createPiHarness() {
7
+ const events = new Map();
8
+ const commands = new Map();
9
+ const appendedEntries = [];
10
+ const sentUserMessages = [];
11
+
12
+ const pi = {
13
+ on(eventName, handler) { events.set(eventName, handler); },
14
+ registerCommand(name, options) { commands.set(name, options); },
15
+ appendEntry(customType, data) { appendedEntries.push({ customType, data }); },
16
+ sendUserMessage(text, options) { sentUserMessages.push({ text, options }); },
17
+ };
18
+
19
+ cavemanExtension(pi);
20
+ return { events, commands, appendedEntries, sentUserMessages };
21
+ }
22
+
23
+ function createCommandContext(overrides = {}) {
24
+ return {
25
+ isIdle: () => true,
26
+ sessionManager: { getEntries: () => [] },
27
+ ui: { notify() {} },
28
+ ...overrides,
29
+ };
30
+ }
31
+
32
+ test("extension registers the /caveman command", () => {
33
+ const { commands } = createPiHarness();
34
+ assert.deepEqual([...commands.keys()], ["caveman"]);
35
+ });
36
+
37
+ test("/caveman on/off appends a caveman-mode entry", async () => {
38
+ const { commands, events, appendedEntries } = createPiHarness();
39
+ const ctx = createCommandContext();
40
+
41
+ await events.get("session_start")({ reason: "startup" }, ctx);
42
+ await commands.get("caveman").handler("off", ctx);
43
+
44
+ assert.deepEqual(appendedEntries.at(-1), { customType: "caveman-mode", data: { active: false } });
45
+
46
+ await commands.get("caveman").handler("on", ctx);
47
+ assert.deepEqual(appendedEntries.at(-1), { customType: "caveman-mode", data: { active: true } });
48
+ });
49
+
50
+ test("bare /caveman toggles off then back on", async () => {
51
+ const { commands, events, appendedEntries } = createPiHarness();
52
+ const ctx = createCommandContext();
53
+
54
+ await events.get("session_start")({ reason: "startup" }, ctx); // starts on
55
+ await commands.get("caveman").handler("", ctx);
56
+ assert.equal(appendedEntries.at(-1).data.active, false);
57
+ await commands.get("caveman").handler("", ctx);
58
+ assert.equal(appendedEntries.at(-1).data.active, true);
59
+ });
60
+
61
+ test("before_agent_start injects instructions while active", async () => {
62
+ const { events } = createPiHarness();
63
+ const ctx = createCommandContext();
64
+
65
+ await events.get("session_start")({ reason: "startup" }, ctx);
66
+ const result = await events.get("before_agent_start")({ systemPrompt: "BASE" }, ctx);
67
+
68
+ assert.ok(result.systemPrompt.startsWith("BASE"));
69
+ assert.ok(result.systemPrompt.includes("CAVEMAN MODE ACTIVE"));
70
+ });
71
+
72
+ test("disabled caveman skips instruction injection", async () => {
73
+ const { commands, events } = createPiHarness();
74
+ const ctx = createCommandContext();
75
+
76
+ await events.get("session_start")({ reason: "startup" }, ctx);
77
+ await commands.get("caveman").handler("off", ctx);
78
+ const result = await events.get("before_agent_start")({ systemPrompt: "BASE" }, ctx);
79
+
80
+ assert.equal(result, undefined);
81
+ });
82
+
83
+ test("session_start restores latest persisted active flag", async () => {
84
+ const { events } = createPiHarness();
85
+ const ctx = createCommandContext({
86
+ sessionManager: {
87
+ getEntries: () => [
88
+ { type: "custom", customType: "caveman-mode", data: { active: false } },
89
+ ],
90
+ },
91
+ });
92
+
93
+ await events.get("session_start")({ reason: "resume" }, ctx);
94
+ const result = await events.get("before_agent_start")({ systemPrompt: "BASE" }, ctx);
95
+
96
+ assert.equal(result, undefined); // persisted false -> no injection
97
+ });
98
+
99
+ test("'stop caveman' as a whole message deactivates mid-session", async () => {
100
+ const { events } = createPiHarness();
101
+ const ctx = createCommandContext();
102
+
103
+ await events.get("session_start")({ reason: "startup" }, ctx);
104
+ await events.get("input")({ text: "stop caveman", source: "interactive" }, ctx);
105
+
106
+ const result = await events.get("before_agent_start")({ systemPrompt: "BASE" }, ctx);
107
+ assert.equal(result, undefined);
108
+ });
109
+
110
+ test("a request merely mentioning 'stop caveman' stays active", async () => {
111
+ const { events } = createPiHarness();
112
+ const ctx = createCommandContext();
113
+
114
+ await events.get("session_start")({ reason: "startup" }, ctx);
115
+ await events.get("input")({ text: "how do I stop caveman re-rendering?", source: "interactive" }, ctx);
116
+
117
+ const result = await events.get("before_agent_start")({ systemPrompt: "BASE" }, ctx);
118
+ assert.ok(result.systemPrompt.includes("CAVEMAN MODE ACTIVE"));
119
+ });
120
+
121
+ test("status bar renders ON and flips active on agent_start", async () => {
122
+ const { events } = createPiHarness();
123
+ const statusWrites = [];
124
+ const ctx = createCommandContext({
125
+ sessionManager: { getEntries: () => [] },
126
+ ui: { notify() {}, setStatus: (key, text) => statusWrites.push({ key, text }), theme: { fg: (_c, t) => t } },
127
+ });
128
+
129
+ await events.get("session_start")({ reason: "startup" }, ctx);
130
+ await events.get("agent_start")({}, ctx);
131
+
132
+ assert.equal(statusWrites.at(-2).key, "caveman");
133
+ assert.match(statusWrites.at(-2).text, /○.*ON/);
134
+ assert.match(statusWrites.at(-1).text, /●.*ON/);
135
+ });
136
+
137
+ test("status bar clears when disabled", async () => {
138
+ const { events, commands } = createPiHarness();
139
+ const statusWrites = [];
140
+ const ctx = createCommandContext({
141
+ ui: { notify() {}, setStatus: (key, text) => statusWrites.push({ key, text }), theme: { fg: (_c, t) => t } },
142
+ });
143
+
144
+ await events.get("session_start")({ reason: "startup" }, ctx);
145
+ await commands.get("caveman").handler("off", ctx);
146
+
147
+ assert.deepEqual(statusWrites.at(-1), { key: "caveman", text: "" });
148
+ });
149
+
150
+ test("status bar stays silent when ui lacks a theme", async () => {
151
+ const { events } = createPiHarness();
152
+ const calls = [];
153
+ const ctx = createCommandContext({
154
+ sessionManager: { getEntries: () => [] },
155
+ ui: { notify() {}, setStatus: (_key, text) => calls.push(text) }, // no theme
156
+ });
157
+
158
+ await events.get("session_start")({ reason: "startup" }, ctx);
159
+ await events.get("agent_start")({}, ctx);
160
+
161
+ assert.deepEqual(calls, []);
162
+ });
163
+
164
+ test("invalid subcommand notifies warning and does not append", async () => {
165
+ const { commands, appendedEntries } = createPiHarness();
166
+ let notified = null;
167
+ const ctx = createCommandContext({ ui: { notify: (_m, kind) => { notified = kind; } } });
168
+
169
+ await commands.get("caveman").handler("banana", ctx);
170
+
171
+ assert.equal(notified, "warning");
172
+ assert.equal(appendedEntries.length, 0);
173
+ });
174
+
175
+ test("status subcommand reports without changing state", async () => {
176
+ const { commands, events, appendedEntries } = createPiHarness();
177
+ let reported = null;
178
+ const ctx = createCommandContext({ ui: { notify: (m) => { reported = m; } } });
179
+
180
+ await events.get("session_start")({ reason: "startup" }, ctx); // starts on
181
+ await commands.get("caveman").handler("status", ctx);
182
+
183
+ assert.match(reported, /ON/);
184
+ assert.equal(appendedEntries.length, 0);
185
+ });
@@ -0,0 +1,67 @@
1
+ import assert from "node:assert/strict";
2
+ import { readFileSync } from "node:fs";
3
+ import { fileURLToPath } from "node:url";
4
+ import test from "node:test";
5
+
6
+ import {
7
+ getCavemanInstructions,
8
+ isDeactivationCommand,
9
+ parseCavemanCommand,
10
+ resolveSessionActive,
11
+ } from "../index.js";
12
+
13
+ test("parseCavemanCommand: bare toggles", () => {
14
+ assert.deepEqual(parseCavemanCommand(""), { type: "toggle" });
15
+ assert.deepEqual(parseCavemanCommand(" "), { type: "toggle" });
16
+ });
17
+
18
+ test("parseCavemanCommand: on/off/status", () => {
19
+ assert.deepEqual(parseCavemanCommand("on"), { type: "set", active: true });
20
+ assert.deepEqual(parseCavemanCommand("ENABLE"), { type: "set", active: true });
21
+ assert.deepEqual(parseCavemanCommand("off"), { type: "set", active: false });
22
+ assert.deepEqual(parseCavemanCommand("disable"), { type: "set", active: false });
23
+ assert.deepEqual(parseCavemanCommand("status"), { type: "status" });
24
+ });
25
+
26
+ test("parseCavemanCommand: unknown arg is invalid", () => {
27
+ assert.deepEqual(parseCavemanCommand("banana"), { type: "invalid", reason: "unknown-subcommand", arg: "banana" });
28
+ });
29
+
30
+ test("resolveSessionActive: latest persisted entry wins", () => {
31
+ const entries = [
32
+ { type: "custom", customType: "caveman-mode", data: { active: false } },
33
+ { type: "custom", customType: "caveman-mode", data: { active: true } },
34
+ ];
35
+ assert.equal(resolveSessionActive(entries, true), true);
36
+ });
37
+
38
+ test("resolveSessionActive: non-array entries fall back", () => {
39
+ assert.equal(resolveSessionActive(null, true), true);
40
+ assert.equal(resolveSessionActive(undefined, false), false);
41
+ assert.equal(resolveSessionActive({}, true), true);
42
+ assert.equal(resolveSessionActive("nope"), true); // DEFAULT fallback
43
+ });
44
+
45
+ test("isDeactivationCommand: only the whole message, case-insensitive, trailing punct ok", () => {
46
+ assert.equal(isDeactivationCommand("stop caveman"), true);
47
+ assert.equal(isDeactivationCommand("STOP CAVEMAN!"), true);
48
+ assert.equal(isDeactivationCommand("normal mode"), true);
49
+ assert.equal(isDeactivationCommand("Normal Mode."), true);
50
+ assert.equal(isDeactivationCommand("add a normal mode toggle"), false);
51
+ assert.equal(isDeactivationCommand("how do I stop caveman re-rendering"), false);
52
+ });
53
+
54
+ test("getCavemanInstructions reads SKILL.md and strips frontmatter", () => {
55
+ const instructions = getCavemanInstructions();
56
+ assert.ok(instructions.startsWith("CAVEMAN MODE ACTIVE"));
57
+ assert.ok(!instructions.includes("name: caveman")); // frontmatter gone
58
+ assert.ok(instructions.includes("Respond terse like smart caveman"));
59
+ });
60
+
61
+ test("getCavemanInstructions matches src/skills/caveman/SKILL.md body", () => {
62
+ const skillPath = fileURLToPath(new URL("../../../skills/caveman/SKILL.md", import.meta.url));
63
+ const raw = readFileSync(skillPath, "utf8");
64
+ const body = raw.replace(/^---[\s\S]*?---\s*/, "");
65
+
66
+ assert.ok(getCavemanInstructions().endsWith(body));
67
+ });
@@ -1,13 +1,12 @@
1
1
  import net from "net";
2
2
  import { writeFileSync, unlinkSync, mkdirSync } from "fs";
3
3
  import { join } from "path";
4
- import { homedir } from "os";
5
4
  import { randomUUID } from "crypto";
6
5
  import { writeMessage, createMessageReader } from "./framing.js";
7
- import { getBrokerSocketPath } from "./paths.js";
6
+ import { getBrokerSocketPath, getIntercomDir } from "./paths.js";
8
7
  import type { SessionInfo, Message, Attachment, BrokerMessage } from "../types.js";
9
8
 
10
- const INTERCOM_DIR = join(homedir(), ".pi/agent/intercom");
9
+ const INTERCOM_DIR = getIntercomDir();
11
10
  const SOCKET_PATH = getBrokerSocketPath();
12
11
  const PID_PATH = join(INTERCOM_DIR, "broker.pid");
13
12
 
@@ -1,5 +1,5 @@
1
1
  import { join } from "path";
2
- import { homedir } from "os";
2
+ import { getAgentDir } from "@selesai/code";
3
3
 
4
4
  function sanitizePipeSegment(value: string): string {
5
5
  return value
@@ -8,13 +8,18 @@ function sanitizePipeSegment(value: string): string {
8
8
  .toLowerCase() || "default";
9
9
  }
10
10
 
11
+ /** selesai config dir (~/.selesai/agent on the selesai fork, ~/.pi/agent upstream). */
12
+ export function getIntercomDir(): string {
13
+ return join(getAgentDir(), "intercom");
14
+ }
15
+
11
16
  export function getBrokerSocketPath(
12
17
  platform: NodeJS.Platform = process.platform,
13
- homeDir: string = homedir(),
18
+ intercomDir: string = getIntercomDir(),
14
19
  ): string {
15
20
  if (platform === "win32") {
16
- return `\\\\.\\pipe\\pi-intercom-${sanitizePipeSegment(homeDir)}`;
21
+ return `\\\\.\\pipe\\pi-intercom-${sanitizePipeSegment(intercomDir)}`;
17
22
  }
18
23
 
19
- return join(homeDir, ".pi/agent/intercom/broker.sock");
20
- }
24
+ return join(intercomDir, "broker.sock");
25
+ }
@@ -2,11 +2,10 @@ import { spawn } from "child_process";
2
2
  import { existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from "fs";
3
3
  import { join, dirname } from "path";
4
4
  import { fileURLToPath } from "url";
5
- import { homedir } from "os";
6
5
  import net from "net";
7
- import { getBrokerSocketPath } from "./paths.js";
6
+ import { getBrokerSocketPath, getIntercomDir } from "./paths.js";
8
7
 
9
- const INTERCOM_DIR = join(homedir(), ".pi/agent/intercom");
8
+ const INTERCOM_DIR = getIntercomDir();
10
9
  const EXTENSION_DIR = join(dirname(fileURLToPath(import.meta.url)), "..");
11
10
  const BROKER_SOCKET = getBrokerSocketPath();
12
11
  const BROKER_PID = join(INTERCOM_DIR, "broker.pid");
@@ -1,6 +1,6 @@
1
1
  import { existsSync, readFileSync } from "fs";
2
2
  import { join } from "path";
3
- import { homedir } from "os";
3
+ import { getIntercomDir } from "./broker/paths.js";
4
4
 
5
5
  export interface IntercomConfig {
6
6
  /** Broker command used to spawn the broker process (e.g. "npx" or "bun") */
@@ -22,7 +22,7 @@ export interface IntercomConfig {
22
22
  replyHint: boolean;
23
23
  }
24
24
 
25
- const CONFIG_PATH = join(homedir(), ".pi/agent/intercom/config.json");
25
+ const CONFIG_PATH = join(getIntercomDir(), "config.json");
26
26
 
27
27
  const defaults: IntercomConfig = {
28
28
  brokerCommand: "npx",
@@ -1,7 +1,7 @@
1
- import type { ExtensionAPI, ExtensionContext } from "@mariozechner/pi-coding-agent";
1
+ import type { ExtensionAPI, ExtensionContext } from "@selesai/code";
2
2
  import { randomUUID } from "crypto";
3
3
  import { Type } from "typebox";
4
- import { Text } from "@mariozechner/pi-tui";
4
+ import { Text } from "@earendil-works/pi-tui";
5
5
  import { IntercomClient } from "./broker/client.ts";
6
6
  import { spawnBrokerIfNeeded } from "./broker/spawn.ts";
7
7
  import { SessionListOverlay } from "./ui/session-list.ts";
@@ -28,8 +28,8 @@
28
28
  ]
29
29
  },
30
30
  "peerDependencies": {
31
- "@mariozechner/pi-coding-agent": "*",
32
- "@mariozechner/pi-tui": "*"
31
+ "@selesai/code": "*",
32
+ "@earendil-works/pi-tui": "*"
33
33
  },
34
34
  "dependencies": {
35
35
  "tsx": "^4.20.0",
@@ -1,6 +1,6 @@
1
- import type { Component, TUI } from "@mariozechner/pi-tui";
2
- import { truncateToWidth, visibleWidth } from "@mariozechner/pi-tui";
3
- import type { KeybindingsManager, Theme } from "@mariozechner/pi-coding-agent";
1
+ import type { Component, TUI } from "@earendil-works/pi-tui";
2
+ import { truncateToWidth, visibleWidth } from "@earendil-works/pi-tui";
3
+ import type { KeybindingsManager, Theme } from "@selesai/code";
4
4
  import type { IntercomClient } from "../broker/client.js";
5
5
  import type { SessionInfo } from "../types.js";
6
6
 
@@ -1,6 +1,6 @@
1
- import type { Component } from "@mariozechner/pi-tui";
2
- import { truncateToWidth, visibleWidth, wrapTextWithAnsi } from "@mariozechner/pi-tui";
3
- import type { Theme } from "@mariozechner/pi-coding-agent";
1
+ import type { Component } from "@earendil-works/pi-tui";
2
+ import { truncateToWidth, visibleWidth, wrapTextWithAnsi } from "@earendil-works/pi-tui";
3
+ import type { Theme } from "@selesai/code";
4
4
  import type { SessionInfo, Message } from "../types.js";
5
5
 
6
6
  export class InlineMessageComponent implements Component {
@@ -1,6 +1,6 @@
1
- import type { Component } from "@mariozechner/pi-tui";
2
- import { truncateToWidth, visibleWidth } from "@mariozechner/pi-tui";
3
- import type { KeybindingsManager, Theme } from "@mariozechner/pi-coding-agent";
1
+ import type { Component } from "@earendil-works/pi-tui";
2
+ import { truncateToWidth, visibleWidth } from "@earendil-works/pi-tui";
3
+ import type { KeybindingsManager, Theme } from "@selesai/code";
4
4
  import type { SessionInfo } from "../types.js";
5
5
 
6
6
  function middleTruncate(text: string, maxWidth: number): string {
@@ -1,6 +1,7 @@
1
1
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
2
2
  import { dirname, join } from "node:path";
3
3
  import { homedir } from "node:os";
4
+ import { getAgentDir } from "@selesai/code";
4
5
 
5
6
  interface PersistedHistoryEntry {
6
7
  command: string;
@@ -13,7 +14,7 @@ function getHomeDir(): string {
13
14
  }
14
15
 
15
16
  function getHistoryDir(): string {
16
- return join(getHomeDir(), ".pi", "agent", "powerline-footer", "bash-history");
17
+ return join(getAgentDir(), "powerline-footer", "bash-history");
17
18
  }
18
19
 
19
20
  function projectKey(cwd: string): string {
@@ -1,5 +1,8 @@
1
1
  import {
2
2
  copyToClipboard,
3
+ CONFIG_DIR_NAME,
4
+ getAgentDir,
5
+ getSettingsPath,
3
6
  type ExtensionAPI,
4
7
  type ReadonlyFooterDataProvider,
5
8
  type Theme,
@@ -8,7 +11,6 @@ import type { AssistantMessage } from "@earendil-works/pi-ai";
8
11
  import { isKeyRelease, matchesKey, type AutocompleteProvider, type SelectItem, SelectList, truncateToWidth, TUI_KEYBINDINGS, visibleWidth } from "@earendil-works/pi-tui";
9
12
  import { readFileSync, writeFileSync, existsSync, mkdirSync, readdirSync } from "node:fs";
10
13
  import { join, dirname } from "node:path";
11
- import { homedir } from "node:os";
12
14
 
13
15
  import type { ColorScheme, SegmentContext, StatusLinePreset, StatusLineSegmentId } from "./types.ts";
14
16
  import type { PowerlineConfig } from "./powerline-config.ts";
@@ -324,23 +326,16 @@ function trackPromptHistory(editor: any): void {
324
326
  snapshotPromptHistory(editor);
325
327
  }
326
328
 
327
- function getSettingsPath(): string {
328
- const homeDir = process.env.HOME || process.env.USERPROFILE || homedir();
329
- return join(homeDir, ".pi", "agent", "settings.json");
330
- }
331
-
332
329
  function getProjectSettingsPath(cwd: string): string {
333
- return join(cwd, ".pi", "settings.json");
330
+ return join(cwd, CONFIG_DIR_NAME, "settings.json");
334
331
  }
335
332
 
336
333
  function getGlobalCompactionPolicyPath(): string {
337
- const homeDir = process.env.HOME || process.env.USERPROFILE || homedir();
338
- return join(homeDir, ".pi", "agent", "compaction-policy.json");
334
+ return join(getAgentDir(), "compaction-policy.json");
339
335
  }
340
336
 
341
337
  function getCustomCompactionExtensionPath(): string {
342
- const homeDir = process.env.HOME || process.env.USERPROFILE || homedir();
343
- return join(homeDir, ".pi", "agent", "extensions", "pi-custom-compaction");
338
+ return join(getAgentDir(), "extensions", "pi-custom-compaction");
344
339
  }
345
340
 
346
341
  function mergeSettings(base: Record<string, unknown>, override: Record<string, unknown>): Record<string, unknown> {
@@ -413,7 +408,7 @@ function readCompactionPolicyEnabled(configPath: string): boolean | undefined {
413
408
  function detectCustomCompactionEnabled(cwd: string): boolean {
414
409
  if (!existsSync(getCustomCompactionExtensionPath())) return false;
415
410
 
416
- const projectSetting = readCompactionPolicyEnabled(join(cwd, ".pi", "compaction-policy.json"));
411
+ const projectSetting = readCompactionPolicyEnabled(join(cwd, CONFIG_DIR_NAME, "compaction-policy.json"));
417
412
  if (projectSetting !== undefined) return projectSetting;
418
413
 
419
414
  return readCompactionPolicyEnabled(getGlobalCompactionPolicyPath()) ?? false;
@@ -424,13 +419,11 @@ function isRecord(value: unknown): value is Record<string, unknown> {
424
419
  }
425
420
 
426
421
  function getStashHistoryPath(): string {
427
- const homeDir = process.env.HOME || process.env.USERPROFILE || homedir();
428
- return join(homeDir, ".pi", "agent", "powerline-footer", "stash-history.json");
422
+ return join(getAgentDir(), "powerline-footer", "stash-history.json");
429
423
  }
430
424
 
431
425
  function getSessionsPath(): string {
432
- const homeDir = process.env.HOME || process.env.USERPROFILE || homedir();
433
- return join(homeDir, ".pi", "agent", "sessions");
426
+ return join(getAgentDir(), "sessions");
434
427
  }
435
428
 
436
429
  function getProjectSessionsPath(cwd: string): string {