@selesai/code 0.8.4 → 0.8.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +22 -0
- package/README.md +7 -1
- package/dist/core/slash-commands.js +1 -0
- package/dist/defaults/models.json +46 -55
- package/dist/defaults/settings.json +7 -8
- package/dist/extensions/agent-browser.test.ts +178 -2
- package/dist/extensions/agent-browser.ts +2 -0
- package/dist/extensions/copy-turn.test.ts +229 -0
- package/dist/extensions/copy-turn.ts +11 -0
- package/dist/extensions/grep-app/index.test.ts +356 -1
- package/dist/extensions/grep-app/index.ts +6 -1
- package/dist/extensions/handoff-new.test.ts +351 -161
- package/dist/extensions/handoff-new.ts +3 -0
- package/dist/extensions/inline-skills.test.ts +93 -0
- package/dist/extensions/inline-skills.ts +3 -0
- package/dist/extensions/pi-subagents/CHANGELOG.md +26 -3
- package/dist/extensions/pi-subagents/README.md +2 -0
- package/dist/extensions/pi-subagents/agents/builder.md +5 -2
- package/dist/extensions/pi-subagents/agents/commentator.md +1 -1
- package/dist/extensions/pi-subagents/docs/configuration.md +54 -2
- package/dist/extensions/pi-subagents/docs/observability.md +1 -1
- package/dist/extensions/pi-subagents/docs/tool-reference.md +1 -1
- package/dist/extensions/pi-subagents/docs/workflows.md +1 -1
- package/dist/extensions/pi-subagents/package-lock.json +2 -2
- package/dist/extensions/pi-subagents/package.json +1 -1
- package/dist/extensions/pi-subagents/skills/pi-subagents/references/execution-controls.md +1 -1
- package/dist/extensions/pi-subagents/src/agents/agents.ts +9 -3
- package/dist/extensions/pi-subagents/src/extension/config.ts +12 -0
- package/dist/extensions/pi-subagents/src/extension/doctor.ts +40 -0
- package/dist/extensions/pi-subagents/src/extension/index.ts +104 -14
- package/dist/extensions/pi-subagents/src/extension/public-execution.ts +5 -0
- package/dist/extensions/pi-subagents/src/extension/rpc.ts +3 -9
- package/dist/extensions/pi-subagents/src/extension/schemas.ts +2 -2
- package/dist/extensions/pi-subagents/src/intercom/intercom-bridge.ts +4 -1
- package/dist/extensions/pi-subagents/src/missions/lifecycle.ts +4 -7
- package/dist/extensions/pi-subagents/src/missions/store.ts +4 -4
- package/dist/extensions/pi-subagents/src/missions/workflow-state.ts +6 -2
- package/dist/extensions/pi-subagents/src/runs/background/active-async-capacity.ts +374 -0
- package/dist/extensions/pi-subagents/src/runs/background/active-run-index.ts +9 -5
- package/dist/extensions/pi-subagents/src/runs/background/async-execution.ts +117 -29
- package/dist/extensions/pi-subagents/src/runs/background/async-job-tracker.ts +12 -6
- package/dist/extensions/pi-subagents/src/runs/background/async-resume.ts +7 -1
- package/dist/extensions/pi-subagents/src/runs/background/async-status.ts +14 -5
- package/dist/extensions/pi-subagents/src/runs/background/chain-append.ts +33 -15
- package/dist/extensions/pi-subagents/src/runs/background/notify.ts +5 -2
- package/dist/extensions/pi-subagents/src/runs/background/owned-process-tree.ts +104 -0
- package/dist/extensions/pi-subagents/src/runs/background/process-terminal.ts +17 -3
- package/dist/extensions/pi-subagents/src/runs/background/result-watcher.ts +19 -1
- package/dist/extensions/pi-subagents/src/runs/background/retained-children.ts +2 -2
- package/dist/extensions/pi-subagents/src/runs/background/run-id-resolver.ts +5 -2
- package/dist/extensions/pi-subagents/src/runs/background/run-status.ts +12 -6
- package/dist/extensions/pi-subagents/src/runs/background/stale-run-reconciler.ts +3 -3
- package/dist/extensions/pi-subagents/src/runs/background/subagent-runner.ts +84 -36
- package/dist/extensions/pi-subagents/src/runs/background/wait-subscriptions.ts +2 -1
- package/dist/extensions/pi-subagents/src/runs/foreground/chain-execution.ts +37 -2
- package/dist/extensions/pi-subagents/src/runs/foreground/execution.ts +135 -22
- package/dist/extensions/pi-subagents/src/runs/foreground/foreground-control.ts +12 -0
- package/dist/extensions/pi-subagents/src/runs/foreground/foreground-history.ts +7 -4
- package/dist/extensions/pi-subagents/src/runs/foreground/prompt-audit.ts +171 -0
- package/dist/extensions/pi-subagents/src/runs/foreground/subagent-executor.ts +653 -196
- package/dist/extensions/pi-subagents/src/runs/shared/acceptance.ts +13 -4
- package/dist/extensions/pi-subagents/src/runs/shared/llm-intent-arbiter.ts +286 -0
- package/dist/extensions/pi-subagents/src/runs/shared/model-fallback.ts +23 -0
- package/dist/extensions/pi-subagents/src/runs/shared/parallel-utils.ts +2 -0
- package/dist/extensions/pi-subagents/src/runs/shared/pi-args.ts +44 -1
- package/dist/extensions/pi-subagents/src/runs/shared/run-fanout-budget.ts +280 -0
- package/dist/extensions/pi-subagents/src/runs/shared/single-output.ts +4 -2
- package/dist/extensions/pi-subagents/src/runs/shared/task-intent.ts +19 -3
- package/dist/extensions/pi-subagents/src/runs/shared/worktree.ts +17 -5
- package/dist/extensions/pi-subagents/src/shared/session-lineage.ts +71 -0
- package/dist/extensions/pi-subagents/src/shared/types.ts +108 -0
- package/dist/extensions/pi-subagents/src/shared/utils.ts +3 -1
- package/dist/extensions/pi-subagents/src/tui/fleet-status.ts +7 -5
- package/dist/extensions/pi-subagents/src/tui/fleet.ts +226 -13
- package/dist/extensions/pi-subagents/src/workflows/scripted-workflow.ts +22 -5
- package/dist/extensions/pi-subagents/src/workflows/workflow-auto-relaunch.ts +28 -0
- package/dist/extensions/pi-subagents/test/integration/acceptance-file-report.test.ts +87 -0
- package/dist/extensions/pi-subagents/test/integration/async-execution.test.ts +96 -6
- package/dist/extensions/pi-subagents/test/integration/async-status.test.ts +28 -5
- package/dist/extensions/pi-subagents/test/integration/chain-execution.test.ts +8 -5
- package/dist/extensions/pi-subagents/test/integration/fork-context-execution.test.ts +83 -2
- package/dist/extensions/pi-subagents/test/integration/intercom-result-delivery.test.ts +211 -15
- package/dist/extensions/pi-subagents/test/integration/parallel-execution.test.ts +41 -3
- package/dist/extensions/pi-subagents/test/integration/single-execution.test.ts +314 -10
- package/dist/extensions/pi-subagents/test/unit/acceptance.test.ts +31 -1
- package/dist/extensions/pi-subagents/test/unit/active-async-capacity.test.ts +317 -0
- package/dist/extensions/pi-subagents/test/unit/agent-overrides.test.ts +21 -2
- package/dist/extensions/pi-subagents/test/unit/async-recovery-descriptor.test.ts +16 -1
- package/dist/extensions/pi-subagents/test/unit/async-resume.test.ts +6 -1
- package/dist/extensions/pi-subagents/test/unit/chain-append.test.ts +75 -4
- package/dist/extensions/pi-subagents/test/unit/completion-guard.test.ts +18 -0
- package/dist/extensions/pi-subagents/test/unit/doctor.test.ts +29 -1
- package/dist/extensions/pi-subagents/test/unit/fleet-status.test.ts +31 -5
- package/dist/extensions/pi-subagents/test/unit/fleet.test.ts +139 -1
- package/dist/extensions/pi-subagents/test/unit/foreground-control.test.ts +10 -0
- package/dist/extensions/pi-subagents/test/unit/handoff-adoption.test.ts +103 -0
- package/dist/extensions/pi-subagents/test/unit/index-child-registration.test.ts +22 -0
- package/dist/extensions/pi-subagents/test/unit/intercom-bridge.test.ts +2 -1
- package/dist/extensions/pi-subagents/test/unit/llm-intent-arbiter.test.ts +171 -0
- package/dist/extensions/pi-subagents/test/unit/mission-lifecycle.test.ts +30 -4
- package/dist/extensions/pi-subagents/test/unit/model-fallback.test.ts +14 -0
- package/dist/extensions/pi-subagents/test/unit/owned-process-tree.test.ts +69 -0
- package/dist/extensions/pi-subagents/test/unit/pi-args.test.ts +83 -0
- package/dist/extensions/pi-subagents/test/unit/pi-coding-agent-dir.test.ts +2 -2
- package/dist/extensions/pi-subagents/test/unit/process-terminal.test.ts +55 -2
- package/dist/extensions/pi-subagents/test/unit/public-execution.test.ts +12 -1
- package/dist/extensions/pi-subagents/test/unit/rpc.test.ts +7 -7
- package/dist/extensions/pi-subagents/test/unit/run-fanout-budget.test.ts +121 -0
- package/dist/extensions/pi-subagents/test/unit/run-status.test.ts +9 -0
- package/dist/extensions/pi-subagents/test/unit/schemas.test.ts +4 -3
- package/dist/extensions/pi-subagents/test/unit/session-lineage.test.ts +73 -0
- package/dist/extensions/pi-subagents/test/unit/single-output.test.ts +28 -0
- package/dist/extensions/pi-subagents/test/unit/steering-action.test.ts +10 -1
- package/dist/extensions/pi-subagents/test/unit/task-intent.test.ts +39 -0
- package/dist/extensions/pi-subagents/test/unit/timeout-defaults.test.ts +65 -0
- package/dist/extensions/pi-subagents/test/unit/workflow-auto-relaunch.test.ts +45 -0
- package/dist/extensions/pi-subagents/test/unit/worktree.test.ts +61 -0
- package/dist/extensions/ponytail/index.js +11 -0
- package/dist/extensions/ponytail/ponytail-config.cjs +2 -0
- package/dist/extensions/ponytail/ponytail-instructions.cjs +6 -0
- package/dist/extensions/ponytail/test/extension.test.js +274 -140
- package/dist/extensions/ponytail/test/helpers.test.js +280 -92
- package/dist/extensions/question/index.ts +18 -0
- package/dist/extensions/question/question-list.ts +22 -0
- package/dist/extensions/question/tests/batch.test.ts +103 -65
- package/dist/extensions/question/tests/helpers.test.ts +68 -35
- package/dist/extensions/question/tests/question-list.test.ts +506 -204
- package/dist/extensions/question/tests/row-layout.test.ts +180 -114
- package/dist/extensions/question/tests/schemas.test.ts +42 -19
- package/dist/extensions/question/tests/shortcuts.test.ts +106 -84
- package/dist/extensions/question/tests/wizard.test.ts +771 -50
- package/dist/extensions/rtk.test.ts +180 -1
- package/dist/extensions/tokenin-onboarding.ts +3 -0
- package/dist/extensions/undo.test.ts +720 -0
- package/dist/extensions/undo.ts +6 -0
- package/dist/extensions/web-agent-onboarding.test.ts +415 -0
- package/dist/extensions/workflow/extension.ts +3 -3
- package/dist/extensions/workflow/modes.ts +41 -19
- package/dist/modes/interactive/interactive-mode.d.ts +1 -0
- package/dist/modes/interactive/interactive-mode.js +48 -1
- package/dist/skills/pi-subagents/references/execution-controls.md +1 -1
- package/docs/plans/workflow-autoloop-reference.md +1 -2
- package/docs/plans/workflow-handoff-carryover.md +108 -0
- package/docs/quickstart.md +27 -43
- package/docs/settings.md +4 -0
- package/docs/usage.md +1 -0
- package/package.json +5 -1
|
@@ -1,166 +1,300 @@
|
|
|
1
|
-
import
|
|
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
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
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
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
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
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
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
|
-
|
|
58
|
-
|
|
56
|
+
describe("ponytail extension", () => {
|
|
57
|
+
it("registers Ponytail commands", () => {
|
|
58
|
+
const { commands } = createPiHarness();
|
|
59
59
|
|
|
60
|
-
|
|
61
|
-
});
|
|
60
|
+
expect([...commands.keys()].sort()).toEqual(["ponytail", "ponytail-audit", "ponytail-debt", "ponytail-gain", "ponytail-help", "ponytail-review"]);
|
|
61
|
+
});
|
|
62
62
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
}));
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
}));
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
}));
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
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
|
-
|
|
132
|
-
|
|
133
|
-
|
|
151
|
+
it("a request mentioning normal mode stays active", async () => withTempConfig(async () => {
|
|
152
|
+
const { commands, events } = createPiHarness();
|
|
153
|
+
const ctx = createCommandContext();
|
|
134
154
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
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
|
-
|
|
140
|
-
|
|
141
|
-
}));
|
|
159
|
+
const result = await events.get("before_agent_start")({ systemPrompt: "BASE" }, ctx);
|
|
160
|
+
expect(result.systemPrompt).toMatch(/PONYTAIL ULTRA:/);
|
|
161
|
+
}));
|
|
142
162
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
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
|
-
|
|
148
|
-
|
|
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
|
-
|
|
152
|
-
|
|
153
|
-
}));
|
|
173
|
+
expect(statusWrites).toEqual([{ key: "ponytail", text: undefined }]);
|
|
174
|
+
}));
|
|
154
175
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
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
|
-
|
|
163
|
-
|
|
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
|
-
|
|
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
|
+
});
|