@selesai/code 0.8.5 → 0.8.7

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 (231) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/dist/core/agent-session.js +13 -1
  3. package/dist/core/extensions/types.d.ts +2 -0
  4. package/dist/core/provider-composer.js +17 -4
  5. package/dist/core/settings-manager.d.ts +2 -0
  6. package/dist/core/settings-manager.js +3 -0
  7. package/dist/core/system-prompt.js +2 -8
  8. package/dist/core/system-prompt.test.js +6 -0
  9. package/dist/core/tools/index.d.ts +1 -0
  10. package/dist/core/tools/index.js +1 -0
  11. package/dist/core/tools/schema-prune.d.ts +16 -0
  12. package/dist/core/tools/schema-prune.js +39 -0
  13. package/dist/core/tools/schema-prune.test.d.ts +1 -0
  14. package/dist/core/tools/schema-prune.test.js +111 -0
  15. package/dist/defaults/models.json +5 -0
  16. package/dist/defaults/settings.json +1 -0
  17. package/dist/extensions/agent-browser.test.ts +178 -2
  18. package/dist/extensions/agent-browser.ts +2 -0
  19. package/dist/extensions/copy-turn.test.ts +229 -0
  20. package/dist/extensions/copy-turn.ts +11 -0
  21. package/dist/extensions/grep-app/index.test.ts +356 -1
  22. package/dist/extensions/grep-app/index.ts +6 -1
  23. package/dist/extensions/handoff-new.test.ts +351 -161
  24. package/dist/extensions/handoff-new.ts +3 -0
  25. package/dist/extensions/inline-skills.test.ts +93 -0
  26. package/dist/extensions/inline-skills.ts +3 -0
  27. package/dist/extensions/model-prompt-injector/config.json +10 -0
  28. package/dist/extensions/model-prompt-injector/index.test.ts +112 -0
  29. package/dist/extensions/model-prompt-injector/index.ts +151 -0
  30. package/dist/extensions/package.json +1 -0
  31. package/dist/extensions/pi-subagents/CHANGELOG.md +158 -90
  32. package/dist/extensions/pi-subagents/UPSTREAM-V0.50-MAPPING.md +172 -0
  33. package/dist/extensions/pi-subagents/agents/builder.md +5 -2
  34. package/dist/extensions/pi-subagents/agents/commentator.md +1 -1
  35. package/dist/extensions/pi-subagents/agents/oracle.md +7 -5
  36. package/dist/extensions/pi-subagents/agents/reviewer.md +2 -2
  37. package/dist/extensions/pi-subagents/agents/scout.md +1 -1
  38. package/dist/extensions/pi-subagents/agents/worker.md +1 -1
  39. package/dist/extensions/pi-subagents/docs/configuration.md +78 -11
  40. package/dist/extensions/pi-subagents/docs/extension-api.md +36 -0
  41. package/dist/extensions/pi-subagents/docs/missions.md +5 -3
  42. package/dist/extensions/pi-subagents/docs/models.md +1 -1
  43. package/dist/extensions/pi-subagents/docs/observability.md +42 -2
  44. package/dist/extensions/pi-subagents/docs/tool-reference.md +20 -3
  45. package/dist/extensions/pi-subagents/docs/workflows.md +4 -4
  46. package/dist/extensions/pi-subagents/install.mjs +2 -2
  47. package/dist/extensions/pi-subagents/package-lock.json +1470 -1472
  48. package/dist/extensions/pi-subagents/package.json +1 -1
  49. package/dist/extensions/pi-subagents/skills/pi-subagents/SKILL.md +1 -1
  50. package/dist/extensions/pi-subagents/skills/pi-subagents/references/constraints-and-recipes.md +5 -4
  51. package/dist/extensions/pi-subagents/skills/pi-subagents/references/execution-controls.md +16 -15
  52. package/dist/extensions/pi-subagents/skills/pi-subagents/references/management-authoring-rpc.md +5 -5
  53. package/dist/extensions/pi-subagents/skills/pi-subagents/references/prompting-and-roles.md +46 -26
  54. package/dist/extensions/pi-subagents/src/agents/agent-serializer.ts +2 -0
  55. package/dist/extensions/pi-subagents/src/agents/agents.ts +37 -12
  56. package/dist/extensions/pi-subagents/src/api/external-runs.ts +174 -84
  57. package/dist/extensions/pi-subagents/src/api/preflight.ts +13 -7
  58. package/dist/extensions/pi-subagents/src/extension/config.ts +59 -0
  59. package/dist/extensions/pi-subagents/src/extension/index.ts +146 -23
  60. package/dist/extensions/pi-subagents/src/extension/public-execution.ts +34 -6
  61. package/dist/extensions/pi-subagents/src/extension/rpc.ts +5 -1
  62. package/dist/extensions/pi-subagents/src/extension/schemas.ts +31 -30
  63. package/dist/extensions/pi-subagents/src/extension/tool-description.ts +10 -9
  64. package/dist/extensions/pi-subagents/src/inspectors/herdr/actions.ts +13 -8
  65. package/dist/extensions/pi-subagents/src/inspectors/herdr/inspector-runner.ts +16 -3
  66. package/dist/extensions/pi-subagents/src/inspectors/herdr/project-panes.ts +2 -6
  67. package/dist/extensions/pi-subagents/src/inspectors/herdr/shell-command.ts +16 -0
  68. package/dist/extensions/pi-subagents/src/intercom/intercom-bridge.ts +5 -4
  69. package/dist/extensions/pi-subagents/src/intercom/native-supervisor-channel.ts +19 -42
  70. package/dist/extensions/pi-subagents/src/missions/goal-driver.ts +3 -1
  71. package/dist/extensions/pi-subagents/src/missions/store.ts +8 -3
  72. package/dist/extensions/pi-subagents/src/runs/background/active-async-capacity.ts +82 -25
  73. package/dist/extensions/pi-subagents/src/runs/background/active-run-index.ts +71 -1
  74. package/dist/extensions/pi-subagents/src/runs/background/async-execution.ts +73 -43
  75. package/dist/extensions/pi-subagents/src/runs/background/async-job-tracker.ts +17 -6
  76. package/dist/extensions/pi-subagents/src/runs/background/async-resume.ts +14 -6
  77. package/dist/extensions/pi-subagents/src/runs/background/async-status-snapshot.ts +277 -0
  78. package/dist/extensions/pi-subagents/src/runs/background/async-status.ts +11 -3
  79. package/dist/extensions/pi-subagents/src/runs/background/chain-root-attachment.ts +2 -2
  80. package/dist/extensions/pi-subagents/src/runs/background/completion-replay.ts +11 -1
  81. package/dist/extensions/pi-subagents/src/runs/background/fleet-view.ts +21 -6
  82. package/dist/extensions/pi-subagents/src/runs/background/notify.ts +5 -2
  83. package/dist/extensions/pi-subagents/src/runs/background/result-files.ts +437 -0
  84. package/dist/extensions/pi-subagents/src/runs/background/result-watcher.ts +224 -42
  85. package/dist/extensions/pi-subagents/src/runs/background/resume-guidance.ts +27 -7
  86. package/dist/extensions/pi-subagents/src/runs/background/retained-children.ts +76 -19
  87. package/dist/extensions/pi-subagents/src/runs/background/run-id-resolver.ts +35 -26
  88. package/dist/extensions/pi-subagents/src/runs/background/run-status.ts +108 -9
  89. package/dist/extensions/pi-subagents/src/runs/background/scheduled-runs.ts +54 -28
  90. package/dist/extensions/pi-subagents/src/runs/background/stale-run-reconciler.ts +27 -13
  91. package/dist/extensions/pi-subagents/src/runs/background/subagent-runner.ts +298 -33
  92. package/dist/extensions/pi-subagents/src/runs/background/subagent-wait.ts +2 -0
  93. package/dist/extensions/pi-subagents/src/runs/background/wait-completions.ts +5 -2
  94. package/dist/extensions/pi-subagents/src/runs/background/wait-subscriptions.ts +2 -1
  95. package/dist/extensions/pi-subagents/src/runs/foreground/async-dismiss-action.ts +2 -1
  96. package/dist/extensions/pi-subagents/src/runs/foreground/chain-execution.ts +16 -0
  97. package/dist/extensions/pi-subagents/src/runs/foreground/execution.ts +219 -15
  98. package/dist/extensions/pi-subagents/src/runs/foreground/foreground-history.ts +7 -4
  99. package/dist/extensions/pi-subagents/src/runs/foreground/prompt-audit.ts +4 -3
  100. package/dist/extensions/pi-subagents/src/runs/foreground/subagent-executor.ts +368 -50
  101. package/dist/extensions/pi-subagents/src/runs/shared/completion-guard.ts +107 -1
  102. package/dist/extensions/pi-subagents/src/runs/shared/external-cli-runner.ts +4 -0
  103. package/dist/extensions/pi-subagents/src/runs/shared/llm-intent-arbiter.ts +39 -23
  104. package/dist/extensions/pi-subagents/src/runs/shared/model-fallback.ts +16 -2
  105. package/dist/extensions/pi-subagents/src/runs/shared/nested-events.ts +66 -62
  106. package/dist/extensions/pi-subagents/src/runs/shared/orca-progress-tabs.ts +376 -0
  107. package/dist/extensions/pi-subagents/src/runs/shared/parallel-utils.ts +2 -0
  108. package/dist/extensions/pi-subagents/src/runs/shared/subagent-control.ts +15 -0
  109. package/dist/extensions/pi-subagents/src/runs/shared/subagent-prompt-runtime.ts +1 -9
  110. package/dist/extensions/pi-subagents/src/runs/shared/subagent-startup-retry.ts +12 -0
  111. package/dist/extensions/pi-subagents/src/runs/shared/tool-timeout.ts +95 -0
  112. package/dist/extensions/pi-subagents/src/shared/agent-stream-options.ts +5 -0
  113. package/dist/extensions/pi-subagents/src/shared/artifacts.ts +0 -4
  114. package/dist/extensions/pi-subagents/src/shared/display-text.ts +50 -0
  115. package/dist/extensions/pi-subagents/src/shared/node-executable.ts +21 -0
  116. package/dist/extensions/pi-subagents/src/shared/session-lineage.ts +71 -0
  117. package/dist/extensions/pi-subagents/src/shared/types.ts +66 -4
  118. package/dist/extensions/pi-subagents/src/slash/slash-commands.ts +34 -25
  119. package/dist/extensions/pi-subagents/src/slash/slash-live-state.ts +4 -0
  120. package/dist/extensions/pi-subagents/src/tui/fleet-status.ts +160 -45
  121. package/dist/extensions/pi-subagents/src/tui/fleet-transcript.ts +1 -48
  122. package/dist/extensions/pi-subagents/src/tui/fleet.ts +129 -17
  123. package/dist/extensions/pi-subagents/src/tui/render.ts +122 -44
  124. package/dist/extensions/pi-subagents/src/watchdog/permission-arbiter.ts +2 -1
  125. package/dist/extensions/pi-subagents/src/watchdog/review.ts +4 -3
  126. package/dist/extensions/pi-subagents/src/workflows/chat-progress.ts +10 -2
  127. package/dist/extensions/pi-subagents/src/workflows/scripted-workflow.ts +272 -76
  128. package/dist/extensions/pi-subagents/src/workflows/workflow-auto-relaunch.ts +28 -0
  129. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/extensions/types.d.ts +2 -0
  130. package/dist/extensions/pi-subagents/test/integration/async-execution.test.ts +2 -2
  131. package/dist/extensions/pi-subagents/test/integration/async-job-tracker.test.ts +68 -9
  132. package/dist/extensions/pi-subagents/test/integration/async-status.test.ts +27 -1
  133. package/dist/extensions/pi-subagents/test/integration/chain-execution.test.ts +1 -1
  134. package/dist/extensions/pi-subagents/test/integration/error-handling.test.ts +49 -0
  135. package/dist/extensions/pi-subagents/test/integration/external-cli-runner.test.ts +1 -0
  136. package/dist/extensions/pi-subagents/test/integration/intercom-result-delivery.test.ts +26 -1
  137. package/dist/extensions/pi-subagents/test/integration/orca-progress-tabs.test.ts +144 -0
  138. package/dist/extensions/pi-subagents/test/integration/render-widget.test.ts +26 -0
  139. package/dist/extensions/pi-subagents/test/integration/result-watcher.test.ts +369 -98
  140. package/dist/extensions/pi-subagents/test/integration/single-execution.test.ts +111 -78
  141. package/dist/extensions/pi-subagents/test/integration/slash-commands.test.ts +49 -1
  142. package/dist/extensions/pi-subagents/test/integration/slash-live-state.test.ts +12 -0
  143. package/dist/extensions/pi-subagents/test/support/node-command.ts +15 -0
  144. package/dist/extensions/pi-subagents/test/unit/active-async-capacity.test.ts +50 -0
  145. package/dist/extensions/pi-subagents/test/unit/agent-frontmatter.test.ts +28 -0
  146. package/dist/extensions/pi-subagents/test/unit/agent-overrides.test.ts +61 -0
  147. package/dist/extensions/pi-subagents/test/unit/agent-stream-options.test.ts +14 -0
  148. package/dist/extensions/pi-subagents/test/unit/async-interrupt-action.test.ts +76 -0
  149. package/dist/extensions/pi-subagents/test/unit/async-resume.test.ts +2 -0
  150. package/dist/extensions/pi-subagents/test/unit/async-status-snapshot.test.ts +162 -0
  151. package/dist/extensions/pi-subagents/test/unit/completion-guard.test.ts +209 -10
  152. package/dist/extensions/pi-subagents/test/unit/completion-replay.test.ts +41 -1
  153. package/dist/extensions/pi-subagents/test/unit/external-runs.test.ts +135 -42
  154. package/dist/extensions/pi-subagents/test/unit/fleet-status.test.ts +252 -1
  155. package/dist/extensions/pi-subagents/test/unit/fleet.test.ts +192 -6
  156. package/dist/extensions/pi-subagents/test/unit/handoff-adoption.test.ts +103 -0
  157. package/dist/extensions/pi-subagents/test/unit/herdr-inspector-bootstrap.test.ts +32 -0
  158. package/dist/extensions/pi-subagents/test/unit/herdr-shell-command.test.ts +59 -0
  159. package/dist/extensions/pi-subagents/test/unit/index-child-registration.test.ts +91 -0
  160. package/dist/extensions/pi-subagents/test/unit/intercom-bridge.test.ts +23 -4
  161. package/dist/extensions/pi-subagents/test/unit/llm-intent-arbiter.test.ts +56 -1
  162. package/dist/extensions/pi-subagents/test/unit/mission-goal-driver.test.ts +67 -3
  163. package/dist/extensions/pi-subagents/test/unit/mission-lifecycle.test.ts +1 -1
  164. package/dist/extensions/pi-subagents/test/unit/mission-store.test.ts +14 -0
  165. package/dist/extensions/pi-subagents/test/unit/model-fallback.test.ts +18 -0
  166. package/dist/extensions/pi-subagents/test/unit/native-supervisor-channel.test.ts +2 -2
  167. package/dist/extensions/pi-subagents/test/unit/nested-events.test.ts +25 -2
  168. package/dist/extensions/pi-subagents/test/unit/node-executable.test.ts +26 -0
  169. package/dist/extensions/pi-subagents/test/unit/notify.test.ts +8 -0
  170. package/dist/extensions/pi-subagents/test/unit/orca-progress-tabs.test.ts +348 -0
  171. package/dist/extensions/pi-subagents/test/unit/package-manifest.test.ts +5 -2
  172. package/dist/extensions/pi-subagents/test/unit/pi-args.test.ts +1 -1
  173. package/dist/extensions/pi-subagents/test/unit/pi-coding-agent-dir.test.ts +34 -0
  174. package/dist/extensions/pi-subagents/test/unit/public-execution.test.ts +6 -1
  175. package/dist/extensions/pi-subagents/test/unit/render-helpers.test.ts +92 -0
  176. package/dist/extensions/pi-subagents/test/unit/result-files.test.ts +175 -0
  177. package/dist/extensions/pi-subagents/test/unit/retained-children.test.ts +127 -17
  178. package/dist/extensions/pi-subagents/test/unit/run-id-resolver.test.ts +44 -0
  179. package/dist/extensions/pi-subagents/test/unit/run-status.test.ts +247 -0
  180. package/dist/extensions/pi-subagents/test/unit/scheduled-runs.test.ts +107 -2
  181. package/dist/extensions/pi-subagents/test/unit/schemas.test.ts +5 -8
  182. package/dist/extensions/pi-subagents/test/unit/scripted-workflow.test.ts +456 -4
  183. package/dist/extensions/pi-subagents/test/unit/session-lineage.test.ts +73 -0
  184. package/dist/extensions/pi-subagents/test/unit/stale-run-reconciler.test.ts +128 -0
  185. package/dist/extensions/pi-subagents/test/unit/subagent-control.test.ts +28 -0
  186. package/dist/extensions/pi-subagents/test/unit/subagent-prompt-runtime.test.ts +16 -7
  187. package/dist/extensions/pi-subagents/test/unit/subagent-startup-retry.test.ts +15 -0
  188. package/dist/extensions/pi-subagents/test/unit/subagent-wait.test.ts +79 -15
  189. package/dist/extensions/pi-subagents/test/unit/tool-description.test.ts +12 -12
  190. package/dist/extensions/pi-subagents/test/unit/tool-timeout.test.ts +109 -0
  191. package/dist/extensions/pi-subagents/test/unit/wait-subscriptions.test.ts +34 -0
  192. package/dist/extensions/pi-subagents/test/unit/workflow-auto-relaunch.test.ts +45 -0
  193. package/dist/extensions/pi-subagents/test/unit/workflow-chat-progress.test.ts +94 -1
  194. package/dist/extensions/pi-subagents/test/unit/workflow-launch-params.test.ts +42 -2
  195. package/dist/extensions/ponytail/index.js +11 -0
  196. package/dist/extensions/ponytail/ponytail-config.cjs +2 -0
  197. package/dist/extensions/ponytail/ponytail-instructions.cjs +6 -0
  198. package/dist/extensions/ponytail/test/extension.test.js +274 -140
  199. package/dist/extensions/ponytail/test/helpers.test.js +280 -92
  200. package/dist/extensions/question/index.ts +33 -0
  201. package/dist/extensions/question/question-list.ts +28 -0
  202. package/dist/extensions/question/row-layout.ts +3 -0
  203. package/dist/extensions/question/tests/batch.test.ts +103 -65
  204. package/dist/extensions/question/tests/exp.test.ts +70 -0
  205. package/dist/extensions/question/tests/exp2.test.ts +59 -0
  206. package/dist/extensions/question/tests/exp3.test.ts +47 -0
  207. package/dist/extensions/question/tests/helpers.test.ts +75 -35
  208. package/dist/extensions/question/tests/question-list.test.ts +640 -198
  209. package/dist/extensions/question/tests/row-layout.test.ts +193 -114
  210. package/dist/extensions/question/tests/schemas.test.ts +42 -19
  211. package/dist/extensions/question/tests/selection-mode.test.ts +54 -0
  212. package/dist/extensions/question/tests/shortcuts.test.ts +106 -84
  213. package/dist/extensions/question/tests/wizard.test.ts +771 -50
  214. package/dist/extensions/question/tests/zz-ig3.test.ts +20 -0
  215. package/dist/extensions/question/tests/zz-probe.test.ts +29 -0
  216. package/dist/extensions/rtk.test.ts +180 -1
  217. package/dist/extensions/tokenin-onboarding.ts +3 -0
  218. package/dist/extensions/undo.test.ts +720 -0
  219. package/dist/extensions/undo.ts +6 -0
  220. package/dist/extensions/web-agent-onboarding.test.ts +415 -0
  221. package/dist/extensions/workflow/extension.ts +3 -3
  222. package/dist/extensions/workflow/modes.ts +41 -19
  223. package/dist/skills/pi-subagents/SKILL.md +1 -1
  224. package/dist/skills/pi-subagents/references/constraints-and-recipes.md +5 -4
  225. package/dist/skills/pi-subagents/references/execution-controls.md +16 -15
  226. package/dist/skills/pi-subagents/references/management-authoring-rpc.md +5 -5
  227. package/dist/skills/pi-subagents/references/prompting-and-roles.md +46 -26
  228. package/docs/plans/workflow-autoloop-reference.md +1 -2
  229. package/docs/plans/workflow-handoff-carryover.md +108 -0
  230. package/docs/settings.md +6 -0
  231. package/package.json +4 -1
@@ -1,166 +1,300 @@
1
- import assert from "node:assert/strict";
1
+ import { afterEach, describe, expect, it, vi } from "vitest";
2
2
  import { mkdtempSync, rmSync } from "node:fs";
3
3
  import { tmpdir } from "node:os";
4
4
  import { join } from "node:path";
5
- import test from "node:test";
6
5
 
7
6
  import ponytailExtension from "../index.js";
8
7
 
9
8
  function createPiHarness() {
10
- const events = new Map();
11
- const commands = new Map();
12
- const appendedEntries = [];
13
- const sentUserMessages = [];
14
-
15
- const pi = {
16
- on(eventName, handler) {
17
- events.set(eventName, handler);
18
- },
19
- registerCommand(name, options) {
20
- commands.set(name, options);
21
- },
22
- appendEntry(customType, data) {
23
- appendedEntries.push({ customType, data });
24
- },
25
- sendUserMessage(text, options) {
26
- sentUserMessages.push({ text, options });
27
- },
28
- };
29
-
30
- ponytailExtension(pi);
31
- return { events, commands, appendedEntries, sentUserMessages };
9
+ const events = new Map();
10
+ const commands = new Map();
11
+ const appendedEntries = [];
12
+ const sentUserMessages = [];
13
+
14
+ const pi = {
15
+ on(eventName, handler) {
16
+ events.set(eventName, handler);
17
+ },
18
+ registerCommand(name, options) {
19
+ commands.set(name, options);
20
+ },
21
+ appendEntry(customType, data) {
22
+ appendedEntries.push({ customType, data });
23
+ },
24
+ sendUserMessage(text, options) {
25
+ sentUserMessages.push({ text, options });
26
+ },
27
+ };
28
+
29
+ ponytailExtension(pi);
30
+ return { events, commands, appendedEntries, sentUserMessages };
32
31
  }
33
32
 
34
33
  function createCommandContext(overrides = {}) {
35
- return {
36
- isIdle: () => true,
37
- sessionManager: { getEntries: () => [] },
38
- ui: { notify() {} },
39
- ...overrides,
40
- };
34
+ return {
35
+ isIdle: () => true,
36
+ sessionManager: { getEntries: () => [] },
37
+ ui: { notify() {} },
38
+ ...overrides,
39
+ };
41
40
  }
42
41
 
43
42
  function withTempConfig(fn) {
44
- const tempConfigHome = mkdtempSync(join(tmpdir(), "ponytail-test-"));
45
- const previousXdg = process.env.XDG_CONFIG_HOME;
46
- process.env.XDG_CONFIG_HOME = tempConfigHome;
47
-
48
- return Promise.resolve()
49
- .then(fn)
50
- .finally(() => {
51
- if (previousXdg === undefined) delete process.env.XDG_CONFIG_HOME;
52
- else process.env.XDG_CONFIG_HOME = previousXdg;
53
- rmSync(tempConfigHome, { recursive: true, force: true });
54
- });
43
+ const tempConfigHome = mkdtempSync(join(tmpdir(), "ponytail-test-"));
44
+ const previousXdg = process.env.XDG_CONFIG_HOME;
45
+ process.env.XDG_CONFIG_HOME = tempConfigHome;
46
+
47
+ return Promise.resolve()
48
+ .then(fn)
49
+ .finally(() => {
50
+ if (previousXdg === undefined) delete process.env.XDG_CONFIG_HOME;
51
+ else process.env.XDG_CONFIG_HOME = previousXdg;
52
+ rmSync(tempConfigHome, { recursive: true, force: true });
53
+ });
55
54
  }
56
55
 
57
- test("extension registers Ponytail commands", () => {
58
- const { commands } = createPiHarness();
56
+ describe("ponytail extension", () => {
57
+ it("registers Ponytail commands", () => {
58
+ const { commands } = createPiHarness();
59
59
 
60
- assert.deepEqual([...commands.keys()].sort(), ["ponytail", "ponytail-audit", "ponytail-debt", "ponytail-gain", "ponytail-help", "ponytail-review"]);
61
- });
60
+ expect([...commands.keys()].sort()).toEqual(["ponytail", "ponytail-audit", "ponytail-debt", "ponytail-gain", "ponytail-help", "ponytail-review"]);
61
+ });
62
62
 
63
- test("/ponytail updates session mode and injects instructions", async () => withTempConfig(async () => {
64
- const { commands, events, appendedEntries } = createPiHarness();
65
- const ctx = createCommandContext();
66
-
67
- await events.get("session_start")({ reason: "startup" }, ctx);
68
- await commands.get("ponytail").handler("ultra", ctx);
69
-
70
- assert.deepEqual(appendedEntries.at(-1), {
71
- customType: "ponytail-mode",
72
- data: { mode: "ultra" },
73
- });
74
-
75
- const result = await events.get("before_agent_start")({ systemPrompt: "BASE" }, ctx);
76
- assert.match(result.systemPrompt, /^BASE\n\nPONYTAIL ULTRA:/);
77
- assert.ok(result.systemPrompt.length - "BASE\n\n".length <= 450);
78
- }));
79
-
80
- test("session_start restores latest persisted mode", async () => withTempConfig(async () => {
81
- const { events } = createPiHarness();
82
- const ctx = createCommandContext({
83
- sessionManager: {
84
- getEntries: () => [
85
- { type: "custom", customType: "ponytail-mode", data: { mode: "lite" } },
86
- ],
87
- },
88
- });
89
-
90
- await events.get("session_start")({ reason: "resume" }, ctx);
91
- const result = await events.get("before_agent_start")({ systemPrompt: "BASE" }, ctx);
92
-
93
- assert.match(result.systemPrompt, /PONYTAIL LITE:/);
94
- }));
95
-
96
- test("review mode retains its independent skill prompt", async () => withTempConfig(async () => {
97
- const { events } = createPiHarness();
98
- const ctx = createCommandContext({
99
- sessionManager: {
100
- getEntries: () => [
101
- { type: "custom", customType: "ponytail-mode", data: { mode: "review" } },
102
- ],
103
- },
104
- });
105
-
106
- await events.get("session_start")({ reason: "resume" }, ctx);
107
- const result = await events.get("before_agent_start")({ systemPrompt: "BASE" }, ctx);
108
-
109
- assert.equal(result.systemPrompt, "BASE\n\nPONYTAIL MODE ACTIVE — level: review. Behavior defined by /ponytail-review skill.");
110
- }));
111
-
112
- test("skill alias commands delegate to Pi skill commands", async () => {
113
- const { commands, sentUserMessages } = createPiHarness();
114
- const ctx = createCommandContext();
115
-
116
- await commands.get("ponytail-review").handler("", ctx);
117
- await commands.get("ponytail-audit").handler("", ctx);
118
- await commands.get("ponytail-debt").handler("", ctx);
119
- await commands.get("ponytail-gain").handler("", ctx);
120
- await commands.get("ponytail-help").handler("", ctx);
121
-
122
- assert.deepEqual(sentUserMessages.map((entry) => entry.text), [
123
- "/skill:ponytail-review",
124
- "/skill:ponytail-audit",
125
- "/skill:ponytail-debt",
126
- "/skill:ponytail-gain",
127
- "/skill:ponytail-help",
128
- ]);
129
- });
63
+ it("/ponytail updates session mode and injects instructions", async () => withTempConfig(async () => {
64
+ const { commands, events, appendedEntries } = createPiHarness();
65
+ const ctx = createCommandContext();
66
+
67
+ await events.get("session_start")({ reason: "startup" }, ctx);
68
+ await commands.get("ponytail").handler("ultra", ctx);
69
+
70
+ expect(appendedEntries.at(-1)).toEqual({
71
+ customType: "ponytail-mode",
72
+ data: { mode: "ultra" },
73
+ });
74
+
75
+ const result = await events.get("before_agent_start")({ systemPrompt: "BASE" }, ctx);
76
+ expect(result.systemPrompt).toMatch(/^BASE\n\nPONYTAIL ULTRA:/);
77
+ expect(result.systemPrompt.length - "BASE\n\n".length).toBeLessThanOrEqual(450);
78
+ }));
79
+
80
+ it("session_start restores latest persisted mode", async () => withTempConfig(async () => {
81
+ const { events } = createPiHarness();
82
+ const ctx = createCommandContext({
83
+ sessionManager: {
84
+ getEntries: () => [
85
+ { type: "custom", customType: "ponytail-mode", data: { mode: "lite" } },
86
+ ],
87
+ },
88
+ });
89
+
90
+ await events.get("session_start")({ reason: "resume" }, ctx);
91
+ const result = await events.get("before_agent_start")({ systemPrompt: "BASE" }, ctx);
92
+
93
+ expect(result.systemPrompt).toMatch(/PONYTAIL LITE:/);
94
+ }));
95
+
96
+ it("review mode retains its independent skill prompt", async () => withTempConfig(async () => {
97
+ const { events } = createPiHarness();
98
+ const ctx = createCommandContext({
99
+ sessionManager: {
100
+ getEntries: () => [
101
+ { type: "custom", customType: "ponytail-mode", data: { mode: "review" } },
102
+ ],
103
+ },
104
+ });
105
+
106
+ await events.get("session_start")({ reason: "resume" }, ctx);
107
+ const result = await events.get("before_agent_start")({ systemPrompt: "BASE" }, ctx);
108
+
109
+ expect(result.systemPrompt).toBe("BASE\n\nPONYTAIL MODE ACTIVE — level: review. Behavior defined by /ponytail-review skill.");
110
+ }));
111
+
112
+ it("skill alias commands delegate to Pi skill commands", async () => {
113
+ const { commands, sentUserMessages } = createPiHarness();
114
+ const ctx = createCommandContext();
115
+
116
+ await commands.get("ponytail-review").handler("", ctx);
117
+ await commands.get("ponytail-audit").handler("", ctx);
118
+ await commands.get("ponytail-debt").handler("", ctx);
119
+ await commands.get("ponytail-gain").handler("", ctx);
120
+ await commands.get("ponytail-help").handler("", ctx);
121
+
122
+ expect(sentUserMessages.map((entry) => entry.text)).toEqual([
123
+ "/skill:ponytail-review",
124
+ "/skill:ponytail-audit",
125
+ "/skill:ponytail-debt",
126
+ "/skill:ponytail-gain",
127
+ "/skill:ponytail-help",
128
+ ]);
129
+ });
130
+
131
+ it("skill alias queues as follow-up when not idle", async () => {
132
+ const { commands, sentUserMessages } = createPiHarness();
133
+ const ctx = createCommandContext({ isIdle: () => false });
134
+
135
+ await commands.get("ponytail-review").handler("", ctx);
136
+ expect(sentUserMessages[0]).toEqual({ text: "/skill:ponytail-review", options: { deliverAs: "followUp" } });
137
+ });
138
+
139
+ it("normal mode disables persistent instructions", async () => withTempConfig(async () => {
140
+ const { commands, events } = createPiHarness();
141
+ const ctx = createCommandContext();
142
+
143
+ await events.get("session_start")({ reason: "startup" }, ctx);
144
+ await commands.get("ponytail").handler("ultra", ctx);
145
+ await events.get("input")({ text: "normal mode", source: "interactive" }, ctx);
146
+
147
+ const disabled = await events.get("before_agent_start")({ systemPrompt: "BASE" }, ctx);
148
+ expect(disabled).toBeUndefined();
149
+ }));
130
150
 
131
- test("normal mode disables persistent instructions", async () => withTempConfig(async () => {
132
- const { commands, events } = createPiHarness();
133
- const ctx = createCommandContext();
151
+ it("a request mentioning normal mode stays active", async () => withTempConfig(async () => {
152
+ const { commands, events } = createPiHarness();
153
+ const ctx = createCommandContext();
134
154
 
135
- await events.get("session_start")({ reason: "startup" }, ctx);
136
- await commands.get("ponytail").handler("ultra", ctx);
137
- await events.get("input")({ text: "normal mode", source: "interactive" }, ctx);
155
+ await events.get("session_start")({ reason: "startup" }, ctx);
156
+ await commands.get("ponytail").handler("ultra", ctx);
157
+ await events.get("input")({ text: "add a normal mode toggle next to dark mode", source: "interactive" }, ctx);
138
158
 
139
- const disabled = await events.get("before_agent_start")({ systemPrompt: "BASE" }, ctx);
140
- assert.equal(disabled, undefined);
141
- }));
159
+ const result = await events.get("before_agent_start")({ systemPrompt: "BASE" }, ctx);
160
+ expect(result.systemPrompt).toMatch(/PONYTAIL ULTRA:/);
161
+ }));
142
162
 
143
- test("a request mentioning normal mode stays active", async () => withTempConfig(async () => {
144
- const { commands, events } = createPiHarness();
145
- const ctx = createCommandContext();
163
+ it("session_start clears the legacy status without rendering a replacement", async () => withTempConfig(async () => {
164
+ const { commands, events } = createPiHarness();
165
+ const statusWrites = [];
166
+ const ctx = createCommandContext({
167
+ ui: { notify() {}, setStatus: (key, text) => statusWrites.push({ key, text }) },
168
+ });
146
169
 
147
- await events.get("session_start")({ reason: "startup" }, ctx);
148
- await commands.get("ponytail").handler("ultra", ctx);
149
- await events.get("input")({ text: "add a normal mode toggle next to dark mode", source: "interactive" }, ctx);
170
+ await events.get("session_start")({ reason: "resume" }, ctx);
171
+ await commands.get("ponytail").handler("ultra", ctx);
150
172
 
151
- const result = await events.get("before_agent_start")({ systemPrompt: "BASE" }, ctx);
152
- assert.match(result.systemPrompt, /PONYTAIL ULTRA:/);
153
- }));
173
+ expect(statusWrites).toEqual([{ key: "ponytail", text: undefined }]);
174
+ }));
154
175
 
155
- test("session_start clears the legacy status without rendering a replacement", async () => withTempConfig(async () => {
156
- const { commands, events } = createPiHarness();
157
- const statusWrites = [];
158
- const ctx = createCommandContext({
159
- ui: { notify() {}, setStatus: (key, text) => statusWrites.push({ key, text }) },
160
- });
176
+ it("/ponytail status reports current and default modes", async () => withTempConfig(async () => {
177
+ const { commands, events } = createPiHarness();
178
+ const notifications = [];
179
+ const ctx = createCommandContext({ ui: { notify: (msg, kind) => notifications.push({ msg, kind }) } });
161
180
 
162
- await events.get("session_start")({ reason: "resume" }, ctx);
163
- await commands.get("ponytail").handler("ultra", ctx);
181
+ await events.get("session_start")({ reason: "startup" }, ctx);
182
+ await commands.get("ponytail").handler("status", ctx);
183
+ expect(notifications.at(-1).msg).toContain("Ponytail: current full • default full");
184
+ }));
164
185
 
165
- assert.deepEqual(statusWrites, [{ key: "ponytail", text: undefined }]);
166
- }));
186
+ it("/ponytail set-default persists the default mode", async () => withTempConfig(async () => {
187
+ const { commands, events } = createPiHarness();
188
+ const notifications = [];
189
+ const ctx = createCommandContext({ ui: { notify: (msg, kind) => notifications.push({ msg, kind }) } });
190
+
191
+ await events.get("session_start")({ reason: "startup" }, ctx);
192
+ await commands.get("ponytail").handler("default lite", ctx);
193
+ expect(notifications.at(-1).msg).toContain("Default Ponytail mode set to lite.");
194
+ }));
195
+
196
+ it("/ponytail set-default with env override reports the override", async () => withTempConfig(async () => {
197
+ const previous = process.env.PONYTAIL_DEFAULT_MODE;
198
+ process.env.PONYTAIL_DEFAULT_MODE = "ultra";
199
+ try {
200
+ const { commands, events } = createPiHarness();
201
+ const notifications = [];
202
+ const ctx = createCommandContext({ ui: { notify: (msg, kind) => notifications.push({ msg, kind }) } });
203
+
204
+ await events.get("session_start")({ reason: "startup" }, ctx);
205
+ await commands.get("ponytail").handler("default lite", ctx);
206
+ expect(notifications.at(-1).msg).toContain("env override keeps default at ultra");
207
+ } finally {
208
+ if (previous === undefined) delete process.env.PONYTAIL_DEFAULT_MODE;
209
+ else process.env.PONYTAIL_DEFAULT_MODE = previous;
210
+ }
211
+ }));
212
+
213
+ it("/ponytail invalid mode warns", async () => withTempConfig(async () => {
214
+ const { commands, events } = createPiHarness();
215
+ const notifications = [];
216
+ const ctx = createCommandContext({ ui: { notify: (msg, kind) => notifications.push({ msg, kind }) } });
217
+
218
+ await events.get("session_start")({ reason: "startup" }, ctx);
219
+ await commands.get("ponytail").handler("bogus", ctx);
220
+ expect(notifications.at(-1).msg).toContain("Unknown or unsupported /ponytail mode.");
221
+ }));
222
+
223
+ it("input from extension source is ignored", async () => withTempConfig(async () => {
224
+ const { commands, events } = createPiHarness();
225
+ const ctx = createCommandContext();
226
+
227
+ await events.get("session_start")({ reason: "startup" }, ctx);
228
+ await commands.get("ponytail").handler("ultra", ctx);
229
+ await events.get("input")({ text: "normal mode", source: "extension" }, ctx);
230
+
231
+ const result = await events.get("before_agent_start")({ systemPrompt: "BASE" }, ctx);
232
+ expect(result.systemPrompt).toMatch(/PONYTAIL ULTRA:/);
233
+ }));
234
+
235
+ it("setMode ignores invalid modes", async () => withTempConfig(async () => {
236
+ const { commands, events, appendedEntries } = createPiHarness();
237
+ const ctx = createCommandContext();
238
+
239
+ await events.get("session_start")({ reason: "startup" }, ctx);
240
+ await commands.get("ponytail").handler("bogus", ctx);
241
+ expect(appendedEntries).toHaveLength(0);
242
+ }));
243
+
244
+ it("session_start uses getBranch when getEntries is missing", async () => withTempConfig(async () => {
245
+ const { events } = createPiHarness();
246
+ const ctx = createCommandContext({
247
+ sessionManager: {
248
+ getBranch: () => [
249
+ { type: "custom", customType: "ponytail-mode", data: { mode: "lite" } },
250
+ ],
251
+ },
252
+ });
253
+
254
+ await events.get("session_start")({ reason: "resume" }, ctx);
255
+ const result = await events.get("before_agent_start")({ systemPrompt: "BASE" }, ctx);
256
+ expect(result.systemPrompt).toMatch(/PONYTAIL LITE:/);
257
+ }));
258
+
259
+ it("session_start tolerates a missing sessionManager", async () => withTempConfig(async () => {
260
+ const { events } = createPiHarness();
261
+ const ctx = createCommandContext({ sessionManager: undefined });
262
+
263
+ await events.get("session_start")({ reason: "resume" }, ctx);
264
+ const result = await events.get("before_agent_start")({ systemPrompt: "BASE" }, ctx);
265
+ expect(result.systemPrompt).toMatch(/PONYTAIL FULL:/);
266
+ }));
267
+
268
+ it("input handler tolerates missing text", async () => withTempConfig(async () => {
269
+ const { commands, events } = createPiHarness();
270
+ const ctx = createCommandContext();
271
+
272
+ await events.get("session_start")({ reason: "startup" }, ctx);
273
+ await commands.get("ponytail").handler("ultra", ctx);
274
+ await events.get("input")({ source: "interactive" }, ctx);
275
+
276
+ const result = await events.get("before_agent_start")({ systemPrompt: "BASE" }, ctx);
277
+ expect(result.systemPrompt).toMatch(/PONYTAIL ULTRA:/);
278
+ }));
279
+
280
+ it("setMode notifies when ctx has a UI", async () => withTempConfig(async () => {
281
+ const { commands, events } = createPiHarness();
282
+ const notifications = [];
283
+ const ctx = createCommandContext({ ui: { notify: (msg, kind) => notifications.push({ msg, kind }) } });
284
+
285
+ await events.get("session_start")({ reason: "startup" }, ctx);
286
+ await commands.get("ponytail").handler("lite", ctx);
287
+ expect(notifications.some((n) => n.msg.includes("Ponytail mode set to lite."))).toBe(true);
288
+ }));
289
+
290
+ it("sendAlias with args sends the skill name plus args", async () => withTempConfig(async () => {
291
+ const { commands, sentUserMessages } = createPiHarness();
292
+ const ctx = createCommandContext();
293
+
294
+ // The alias handlers hardcode empty args; exercise the follow-up path
295
+ // through a non-idle context.
296
+ const busyCtx = createCommandContext({ isIdle: () => false });
297
+ await commands.get("ponytail-review").handler("", busyCtx);
298
+ expect(sentUserMessages[0]).toEqual({ text: "/skill:ponytail-review", options: { deliverAs: "followUp" } });
299
+ }));
300
+ });