@selesai/code 0.5.12 → 0.5.14
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/README.md +186 -0
- package/dist/core/system-prompt.d.ts.map +1 -1
- package/dist/core/system-prompt.js +1 -1
- package/dist/core/system-prompt.js.map +1 -1
- package/dist/extensions/caveman/caveman-instructions.cjs +6 -34
- package/dist/extensions/caveman/test/helpers.test.js +7 -13
- package/dist/extensions/package.json +4 -3
- package/dist/extensions/pi-powerline-footer/README.md +1 -1
- package/dist/extensions/pi-powerline-footer/guide.ts +227 -0
- package/dist/extensions/pi-powerline-footer/index.ts +88 -55
- package/dist/extensions/pi-powerline-footer/tests/editor-responsiveness.test.ts +0 -78
- package/dist/extensions/pi-powerline-footer/tests/guide.test.ts +102 -0
- package/dist/extensions/pi-powerline-footer/welcome.ts +65 -30
- package/dist/extensions/ponytail/index.js +1 -1
- package/dist/extensions/ponytail/ponytail-instructions.cjs +15 -1
- package/dist/extensions/ponytail/test/extension.test.js +20 -4
- package/dist/extensions/ponytail/test/helpers.test.js +32 -0
- package/dist/extensions/workflow/adapter.ts +0 -19
- package/dist/skills/caveman/SKILL.md +1 -0
- package/dist/skills/handoff/SKILL.md +1 -0
- package/dist/skills/handoff-text/SKILL.md +1 -0
- package/dist/skills/implanger/SKILL.md +1 -0
- package/dist/skills/improve-codebase/SKILL.md +1 -0
- package/dist/skills/planger/SKILL.md +1 -0
- package/dist/skills/ponytail/SKILL.md +1 -0
- package/dist/skills/ponytail-audit/SKILL.md +1 -0
- package/dist/skills/ponytail-debt/SKILL.md +1 -0
- package/dist/skills/ponytail-gain/SKILL.md +1 -0
- package/dist/skills/ponytail-help/SKILL.md +1 -0
- package/dist/skills/ponytail-review/SKILL.md +1 -0
- package/dist/skills/workflow-creation/SKILL.md +1 -0
- package/package.json +23 -2
- package/dist/agents/architect.md +0 -217
- package/dist/agents/builder.md +0 -120
- package/dist/agents/commentator.md +0 -130
- package/dist/agents/explorer.md +0 -51
- package/dist/agents/recapper.md +0 -26
- package/dist/extensions/gitignore-guard.ts +0 -132
- package/dist/extensions/hooks/claude-codex-hooks.json +0 -44
- package/dist/extensions/hooks/copilot-hooks.json +0 -21
- package/dist/extensions/hooks/ponytail-activate.js +0 -91
- package/dist/extensions/hooks/ponytail-config.js +0 -122
- package/dist/extensions/hooks/ponytail-instructions.js +0 -94
- package/dist/extensions/hooks/ponytail-mode-tracker.js +0 -55
- package/dist/extensions/hooks/ponytail-runtime.js +0 -68
- package/dist/extensions/hooks/ponytail-statusline.ps1 +0 -21
- package/dist/extensions/hooks/ponytail-statusline.sh +0 -12
- package/dist/extensions/hooks/ponytail-subagent.js +0 -22
- package/dist/extensions/pi-extension/index.js +0 -189
- package/dist/extensions/pi-extension/package.json +0 -8
- package/dist/extensions/pi-extension/test/extension.test.js +0 -167
- package/dist/extensions/pi-extension/test/helpers.test.js +0 -92
- package/dist/extensions/pi-powerline-footer/welcome-dismiss.ts +0 -34
- package/dist/extensions/pi-subagents/agents/context-builder.md +0 -46
- package/dist/extensions/pi-subagents/agents/delegate.md +0 -12
- package/dist/extensions/pi-subagents/agents/oracle.md +0 -73
- package/dist/extensions/pi-subagents/agents/planner.md +0 -55
- package/dist/extensions/pi-subagents/agents/reviewer.md +0 -79
- package/dist/extensions/pi-subagents/agents/scout.md +0 -50
- package/dist/extensions/pi-subagents/agents/worker.md +0 -55
- package/dist/extensions/pi-subagents/src/runs/shared/spawn-timing.ts +0 -46
- package/dist/extensions/pi-subagents/test/unit/spawn-timing.test.ts +0 -45
- package/dist/extensions/pi-subagents/tsconfig.runner.json +0 -17
- package/dist/extensions/ponytail-pi-extension/index.js +0 -189
- package/dist/extensions/ponytail-pi-extension/package.json +0 -8
- package/dist/extensions/ponytail-pi-extension/test/extension.test.js +0 -167
- package/dist/extensions/ponytail-pi-extension/test/helpers.test.js +0 -92
- package/dist/extensions/prototype.ts +0 -713
- package/dist/extensions/question.ts +0 -350
- package/dist/extensions/quick.ts +0 -630
- package/dist/extensions/tps-tracker.ts +0 -280
- package/dist/extensions/workflow/task-validators.ts +0 -69
- package/dist/skills/selesai-default/SKILL.md +0 -16
|
@@ -3,8 +3,9 @@ import { join, basename } from "node:path";
|
|
|
3
3
|
import { homedir as osHomedir } from "node:os";
|
|
4
4
|
import { CONFIG_DIR_NAME, getAgentDir, getSettingsPath } from "@selesai/code";
|
|
5
5
|
import type { Component } from "@earendil-works/pi-tui";
|
|
6
|
-
import { truncateToWidth as tuiTruncateToWidth, visibleWidth } from "@earendil-works/pi-tui";
|
|
6
|
+
import { truncateToWidth as tuiTruncateToWidth, visibleWidth, wrapTextWithAnsi } from "@earendil-works/pi-tui";
|
|
7
7
|
import { ansi, fgOnly, getFgAnsiCode } from "./colors.ts";
|
|
8
|
+
import { GUIDE_COMPACT_LINES, GUIDE_DISMISS_HINT, getGuideFeatureLines, type GuideFeature, type GuideMode } from "./guide.ts";
|
|
8
9
|
|
|
9
10
|
export interface RecentSession {
|
|
10
11
|
name: string;
|
|
@@ -82,11 +83,20 @@ function fitToWidth(str: string, width: number): string {
|
|
|
82
83
|
return str + " ".repeat(width - visLen);
|
|
83
84
|
}
|
|
84
85
|
|
|
86
|
+
function wrapLines(lines: string[], width: number): string[] {
|
|
87
|
+
const continuation = " ";
|
|
88
|
+
const contentWidth = Math.max(1, width - continuation.length);
|
|
89
|
+
return lines.flatMap((line) => wrapTextWithAnsi(line, contentWidth)
|
|
90
|
+
.map((wrapped, index) => index === 0 ? wrapped : continuation + wrapped));
|
|
91
|
+
}
|
|
92
|
+
|
|
85
93
|
interface WelcomeData {
|
|
86
94
|
modelName: string;
|
|
87
95
|
providerName: string;
|
|
88
96
|
recentSessions: RecentSession[];
|
|
89
97
|
loadedCounts: LoadedCounts;
|
|
98
|
+
guideMode: GuideMode;
|
|
99
|
+
newFeatures: GuideFeature[];
|
|
90
100
|
}
|
|
91
101
|
|
|
92
102
|
function buildLeftColumn(data: WelcomeData, colWidth: number): string[] {
|
|
@@ -107,6 +117,27 @@ function buildRightColumn(data: WelcomeData, colWidth: number): string[] {
|
|
|
107
117
|
const hChar = "─";
|
|
108
118
|
const separator = ` ${dim(hChar.repeat(colWidth - 2))}`;
|
|
109
119
|
|
|
120
|
+
const guideLines = data.guideMode === "compact"
|
|
121
|
+
? [
|
|
122
|
+
` ${bold(fgOnly("accent", "Guide"))}`,
|
|
123
|
+
...(data.newFeatures.length > 0
|
|
124
|
+
? [` ${bold(fgOnly("model", `New: ${data.newFeatures.slice(0, 2).map((feature) => feature.title).join(" · ")}`))}`]
|
|
125
|
+
: []),
|
|
126
|
+
...GUIDE_COMPACT_LINES.map((line) => ` ${dim(line)}`),
|
|
127
|
+
]
|
|
128
|
+
: [
|
|
129
|
+
` ${bold(fgOnly("accent", "Selesai guide"))}`,
|
|
130
|
+
...(data.newFeatures.length > 0
|
|
131
|
+
? [
|
|
132
|
+
` ${bold(fgOnly("model", "New since your last guide"))}`,
|
|
133
|
+
...data.newFeatures.slice(0, 3).map((feature) => ` ${dim("• ")}${feature.title}`),
|
|
134
|
+
]
|
|
135
|
+
: []),
|
|
136
|
+
...getGuideFeatureLines().map((line) => line.kind === "section"
|
|
137
|
+
? ` ${bold(fgOnly("accent", line.text))}`
|
|
138
|
+
: ` ${dim("• ")}${line.feature.title}: ${dim(line.feature.example)}`),
|
|
139
|
+
];
|
|
140
|
+
|
|
110
141
|
// Session lines
|
|
111
142
|
const sessionLines: string[] = [];
|
|
112
143
|
if (data.recentSessions.length === 0) {
|
|
@@ -141,17 +172,23 @@ function buildRightColumn(data: WelcomeData, colWidth: number): string[] {
|
|
|
141
172
|
countLines.push(` ${dim("No extensions loaded")}`);
|
|
142
173
|
}
|
|
143
174
|
|
|
175
|
+
const dashboardLines = data.guideMode === "full"
|
|
176
|
+
? []
|
|
177
|
+
: [
|
|
178
|
+
separator,
|
|
179
|
+
` ${bold(fgOnly("accent", "Loaded"))}`,
|
|
180
|
+
...countLines,
|
|
181
|
+
separator,
|
|
182
|
+
` ${bold(fgOnly("accent", "Recent sessions"))}`,
|
|
183
|
+
...sessionLines,
|
|
184
|
+
];
|
|
185
|
+
|
|
144
186
|
return [
|
|
145
|
-
` ${bold(fgOnly("accent", "Tips"))}`,
|
|
146
|
-
` ${dim("/")} for commands`,
|
|
147
|
-
` ${dim("!")} to run bash`,
|
|
187
|
+
` ${bold(fgOnly("accent", "Tips"))}: ${dim("/")} commands · ${dim("/settings")} configure`,
|
|
148
188
|
` ${dim("Shift+Tab")} cycle thinking`,
|
|
149
189
|
separator,
|
|
150
|
-
|
|
151
|
-
...
|
|
152
|
-
separator,
|
|
153
|
-
` ${bold(fgOnly("accent", "Recent sessions"))}`,
|
|
154
|
-
...sessionLines,
|
|
190
|
+
...guideLines,
|
|
191
|
+
...dashboardLines,
|
|
155
192
|
"",
|
|
156
193
|
];
|
|
157
194
|
}
|
|
@@ -170,8 +207,8 @@ function renderWelcomeBox(
|
|
|
170
207
|
}
|
|
171
208
|
|
|
172
209
|
const minWidth = 76;
|
|
173
|
-
const maxWidth = 96;
|
|
174
|
-
// Clamp to termWidth to prevent crash on narrow terminals
|
|
210
|
+
const maxWidth = data.guideMode === "full" ? 132 : 96;
|
|
211
|
+
// Clamp to termWidth to prevent crash on narrow terminals.
|
|
175
212
|
const boxWidth = Math.min(termWidth, Math.max(minWidth, Math.min(termWidth - 2, maxWidth)));
|
|
176
213
|
const leftCol = 26;
|
|
177
214
|
const rightCol = Math.max(1, boxWidth - leftCol - 3); // Ensure rightCol is at least 1
|
|
@@ -184,7 +221,7 @@ function renderWelcomeBox(
|
|
|
184
221
|
const br = dim("╯");
|
|
185
222
|
|
|
186
223
|
const leftLines = buildLeftColumn(data, leftCol);
|
|
187
|
-
const rightLines = buildRightColumn(data, rightCol);
|
|
224
|
+
const rightLines = wrapLines(buildRightColumn(data, rightCol), rightCol);
|
|
188
225
|
|
|
189
226
|
const lines: string[] = [];
|
|
190
227
|
|
|
@@ -221,19 +258,16 @@ function renderWelcomeBox(
|
|
|
221
258
|
*/
|
|
222
259
|
export class WelcomeComponent implements Component {
|
|
223
260
|
private data: WelcomeData;
|
|
224
|
-
private countdown: number = 30;
|
|
225
261
|
|
|
226
262
|
constructor(
|
|
227
263
|
modelName: string,
|
|
228
264
|
providerName: string,
|
|
229
265
|
recentSessions: RecentSession[] = [],
|
|
230
266
|
loadedCounts: LoadedCounts = { contextFiles: 0, extensions: 0, skills: 0, promptTemplates: 0 },
|
|
267
|
+
guideMode: GuideMode = "full",
|
|
268
|
+
newFeatures: GuideFeature[] = [],
|
|
231
269
|
) {
|
|
232
|
-
this.data = { modelName, providerName, recentSessions, loadedCounts };
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
setCountdown(seconds: number): void {
|
|
236
|
-
this.countdown = seconds;
|
|
270
|
+
this.data = { modelName, providerName, recentSessions, loadedCounts, guideMode, newFeatures };
|
|
237
271
|
}
|
|
238
272
|
|
|
239
273
|
invalidate(): void {}
|
|
@@ -246,20 +280,20 @@ export class WelcomeComponent implements Component {
|
|
|
246
280
|
}
|
|
247
281
|
|
|
248
282
|
const minWidth = 76;
|
|
249
|
-
const maxWidth =
|
|
250
|
-
// Clamp to termWidth to prevent crash on narrow terminals
|
|
283
|
+
const maxWidth = 132;
|
|
284
|
+
// Clamp to termWidth to prevent crash on narrow terminals.
|
|
251
285
|
const boxWidth = Math.min(termWidth, Math.max(minWidth, Math.min(termWidth - 2, maxWidth)));
|
|
252
286
|
|
|
253
|
-
//
|
|
254
|
-
const
|
|
255
|
-
const
|
|
287
|
+
// The guide is intentionally blocking: no timer or implicit dismissal.
|
|
288
|
+
const promptText = ` ${GUIDE_DISMISS_HINT} `;
|
|
289
|
+
const promptStyled = dim(promptText);
|
|
256
290
|
const bottomContentWidth = boxWidth - 2;
|
|
257
|
-
const
|
|
258
|
-
const leftPad = Math.floor((bottomContentWidth -
|
|
259
|
-
const rightPad = bottomContentWidth -
|
|
291
|
+
const promptVisLen = visibleWidth(promptText);
|
|
292
|
+
const leftPad = Math.floor((bottomContentWidth - promptVisLen) / 2);
|
|
293
|
+
const rightPad = bottomContentWidth - promptVisLen - leftPad;
|
|
260
294
|
const hChar = "─";
|
|
261
295
|
const bottomLine = dim(hChar.repeat(Math.max(0, leftPad))) +
|
|
262
|
-
|
|
296
|
+
promptStyled +
|
|
263
297
|
dim(hChar.repeat(Math.max(0, rightPad)));
|
|
264
298
|
|
|
265
299
|
return renderWelcomeBox(this.data, termWidth, bottomLine);
|
|
@@ -267,8 +301,7 @@ export class WelcomeComponent implements Component {
|
|
|
267
301
|
}
|
|
268
302
|
|
|
269
303
|
/**
|
|
270
|
-
*
|
|
271
|
-
* Used when quietStartup: true.
|
|
304
|
+
* Compact guide header used when the user selects `/guide compact`.
|
|
272
305
|
*/
|
|
273
306
|
export class WelcomeHeader implements Component {
|
|
274
307
|
private data: WelcomeData;
|
|
@@ -278,8 +311,10 @@ export class WelcomeHeader implements Component {
|
|
|
278
311
|
providerName: string,
|
|
279
312
|
recentSessions: RecentSession[] = [],
|
|
280
313
|
loadedCounts: LoadedCounts = { contextFiles: 0, extensions: 0, skills: 0, promptTemplates: 0 },
|
|
314
|
+
guideMode: GuideMode = "compact",
|
|
315
|
+
newFeatures: GuideFeature[] = [],
|
|
281
316
|
) {
|
|
282
|
-
this.data = { modelName, providerName, recentSessions, loadedCounts };
|
|
317
|
+
this.data = { modelName, providerName, recentSessions, loadedCounts, guideMode, newFeatures };
|
|
283
318
|
}
|
|
284
319
|
|
|
285
320
|
invalidate(): void {}
|
|
@@ -184,6 +184,6 @@ export default function ponytailExtension(pi) {
|
|
|
184
184
|
|
|
185
185
|
pi.on("before_agent_start", async (event) => {
|
|
186
186
|
if (!currentMode || currentMode === "off") return;
|
|
187
|
-
return { systemPrompt: `${event.systemPrompt}\n\n${getPonytailInstructions(currentMode)}` };
|
|
187
|
+
return { systemPrompt: `${event.systemPrompt}\n\n${getPonytailInstructions(currentMode, { compact: true })}` };
|
|
188
188
|
});
|
|
189
189
|
}
|
|
@@ -7,6 +7,11 @@ const { DEFAULT_MODE, normalizeMode, normalizePersistedMode } = require('./ponyt
|
|
|
7
7
|
|
|
8
8
|
const INDEPENDENT_MODES = new Set(['review']);
|
|
9
9
|
const SKILL_PATH = path.join(__dirname, '..', '..', 'skills', 'ponytail', 'SKILL.md');
|
|
10
|
+
const COMPACT_MODE_RULES = {
|
|
11
|
+
lite: 'Build the ask; name a lazier alternative.',
|
|
12
|
+
full: 'Enforce ladder; shortest diff.',
|
|
13
|
+
ultra: 'YAGNI first; delete; challenge the rest.',
|
|
14
|
+
};
|
|
10
15
|
|
|
11
16
|
function filterSkillBodyForMode(body, mode) {
|
|
12
17
|
const effectiveMode = normalizeMode(mode) || DEFAULT_MODE;
|
|
@@ -70,7 +75,15 @@ function getFallbackInstructions(mode) {
|
|
|
70
75
|
'Ponytail governs what you build, not how you talk. "stop ponytail" or "normal mode": revert. Level persists until changed or session end.';
|
|
71
76
|
}
|
|
72
77
|
|
|
73
|
-
function
|
|
78
|
+
function getCompactInstructions(mode) {
|
|
79
|
+
return 'PONYTAIL ' + mode.toUpperCase() + ': persistent every response; off only "stop ponytail"/"normal mode". ' +
|
|
80
|
+
'Trace first. Ladder: YAGNI -> reuse -> stdlib/native -> installed deps -> minimum diff. ' +
|
|
81
|
+
'Fix root cause, not caller symptom. No unrequested abstractions/deps/boilerplate; delete first. ' +
|
|
82
|
+
'Preserve validation, data-loss handling, security, accessibility, explicit asks. ' +
|
|
83
|
+
'Non-trivial logic: one runnable check. Code first; <=3 lines. ' + COMPACT_MODE_RULES[mode];
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function getPonytailInstructions(mode, options) {
|
|
74
87
|
const configuredMode = normalizePersistedMode(mode) || DEFAULT_MODE;
|
|
75
88
|
|
|
76
89
|
if (INDEPENDENT_MODES.has(configuredMode)) {
|
|
@@ -78,6 +91,7 @@ function getPonytailInstructions(mode) {
|
|
|
78
91
|
}
|
|
79
92
|
|
|
80
93
|
const effectiveMode = normalizeMode(configuredMode) || DEFAULT_MODE;
|
|
94
|
+
if (options?.compact === true) return getCompactInstructions(effectiveMode);
|
|
81
95
|
|
|
82
96
|
try {
|
|
83
97
|
return 'PONYTAIL MODE ACTIVE — level: ' + effectiveMode + '\n\n' +
|
|
@@ -73,8 +73,8 @@ test("/ponytail updates session mode and injects instructions", async () => with
|
|
|
73
73
|
});
|
|
74
74
|
|
|
75
75
|
const result = await events.get("before_agent_start")({ systemPrompt: "BASE" }, ctx);
|
|
76
|
-
assert.
|
|
77
|
-
assert.ok(result.systemPrompt.
|
|
76
|
+
assert.match(result.systemPrompt, /^BASE\n\nPONYTAIL ULTRA:/);
|
|
77
|
+
assert.ok(result.systemPrompt.length - "BASE\n\n".length <= 450);
|
|
78
78
|
}));
|
|
79
79
|
|
|
80
80
|
test("session_start restores latest persisted mode", async () => withTempConfig(async () => {
|
|
@@ -90,7 +90,23 @@ test("session_start restores latest persisted mode", async () => withTempConfig(
|
|
|
90
90
|
await events.get("session_start")({ reason: "resume" }, ctx);
|
|
91
91
|
const result = await events.get("before_agent_start")({ systemPrompt: "BASE" }, ctx);
|
|
92
92
|
|
|
93
|
-
assert.
|
|
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.");
|
|
94
110
|
}));
|
|
95
111
|
|
|
96
112
|
test("skill alias commands delegate to Pi skill commands", async () => {
|
|
@@ -133,7 +149,7 @@ test("a request mentioning normal mode stays active", async () => withTempConfig
|
|
|
133
149
|
await events.get("input")({ text: "add a normal mode toggle next to dark mode", source: "interactive" }, ctx);
|
|
134
150
|
|
|
135
151
|
const result = await events.get("before_agent_start")({ systemPrompt: "BASE" }, ctx);
|
|
136
|
-
assert.match(result.systemPrompt, /PONYTAIL
|
|
152
|
+
assert.match(result.systemPrompt, /PONYTAIL ULTRA:/);
|
|
137
153
|
}));
|
|
138
154
|
|
|
139
155
|
test("status bar renders the mode and flips active on agent_start", async () => withTempConfig(async () => {
|
|
@@ -2,8 +2,12 @@ import assert from "node:assert/strict";
|
|
|
2
2
|
import { existsSync, mkdtempSync, readFileSync, rmSync } from "node:fs";
|
|
3
3
|
import { tmpdir } from "node:os";
|
|
4
4
|
import { join } from "node:path";
|
|
5
|
+
import { createRequire } from "node:module";
|
|
5
6
|
import test from "node:test";
|
|
6
7
|
|
|
8
|
+
const require = createRequire(import.meta.url);
|
|
9
|
+
const { getPonytailInstructions } = require("../ponytail-instructions.cjs");
|
|
10
|
+
|
|
7
11
|
import {
|
|
8
12
|
filterSkillBodyForMode,
|
|
9
13
|
parsePonytailCommand,
|
|
@@ -74,6 +78,34 @@ test("filterSkillBodyForMode keeps only requested intensity examples and rows",
|
|
|
74
78
|
assert.ok(filtered.includes("Other line"));
|
|
75
79
|
});
|
|
76
80
|
|
|
81
|
+
test("compact Ponytail instructions retain core rules and mode distinctions", () => {
|
|
82
|
+
const compact = Object.fromEntries(
|
|
83
|
+
["lite", "full", "ultra"].map((mode) => [mode, getPonytailInstructions(mode, { compact: true })]),
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
for (const [mode, instructions] of Object.entries(compact)) {
|
|
87
|
+
assert.match(instructions, new RegExp(`^PONYTAIL ${mode.toUpperCase()}:`));
|
|
88
|
+
assert.ok(instructions.length >= 300 && instructions.length <= 450, `${mode}: ${instructions.length} characters`);
|
|
89
|
+
for (const phrase of ["stop ponytail", "Trace first", "root cause", "validation", "data-loss", "security", "accessibility", "runnable check"]) {
|
|
90
|
+
assert.ok(instructions.includes(phrase), `${mode} lacks ${phrase}`);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
assert.match(compact.lite, /Build the ask; name a lazier alternative/);
|
|
95
|
+
assert.match(compact.full, /Enforce ladder; shortest diff/);
|
|
96
|
+
assert.match(compact.ultra, /YAGNI first; delete; challenge the rest/);
|
|
97
|
+
|
|
98
|
+
const fullSkill = getPonytailInstructions("full");
|
|
99
|
+
assert.match(fullSkill, /## The ladder/);
|
|
100
|
+
assert.ok(fullSkill.length > compact.full.length);
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
test("review instructions remain independent in compact mode", () => {
|
|
104
|
+
const expected = "PONYTAIL MODE ACTIVE — level: review. Behavior defined by /ponytail-review skill.";
|
|
105
|
+
assert.equal(getPonytailInstructions("review"), expected);
|
|
106
|
+
assert.equal(getPonytailInstructions("review", { compact: true }), expected);
|
|
107
|
+
});
|
|
108
|
+
|
|
77
109
|
test("filterSkillBodyForMode keeps rule bullets that contain a colon", () => {
|
|
78
110
|
// Regression: rule bullets outside the Intensity section (e.g. the
|
|
79
111
|
// "No unrequested abstractions:" rule or the `ponytail:` comment convention)
|
|
@@ -472,11 +472,6 @@ function registerSharedArtifactWriter(pi: ExtensionAPI): void {
|
|
|
472
472
|
name: WORKFLOW_ARTIFACT_TOOL,
|
|
473
473
|
label: "Write Workflow Artifact",
|
|
474
474
|
description: "Write the current workflow phase artifact. The workflow enforces the destination path; provide content only.",
|
|
475
|
-
promptSnippet: `${WORKFLOW_ARTIFACT_TOOL}(content) - write the active workflow artifact; path is chosen by the workflow, not the agent.`,
|
|
476
|
-
promptGuidelines: [
|
|
477
|
-
`During any workflow, use ${WORKFLOW_ARTIFACT_TOOL} instead of write/edit for workflow artifacts.`,
|
|
478
|
-
"Provide artifact content only; do not provide a path.",
|
|
479
|
-
],
|
|
480
475
|
parameters: Type.Object({
|
|
481
476
|
content: Type.String({ description: "Artifact markdown/text content to save." }),
|
|
482
477
|
}),
|
|
@@ -571,11 +566,6 @@ export function createWorkflowExtension(
|
|
|
571
566
|
name: start,
|
|
572
567
|
label: options.toolLabels.start,
|
|
573
568
|
description: `Start the ${mode} workflow for the given goal. Sets up ${config.phases[0]} as the first phase and returns its prompt. Do not call if a workflow is already active.`,
|
|
574
|
-
promptSnippet: `${start}(goal) - start the ${mode} workflow; goal is what the user wants to build.`,
|
|
575
|
-
promptGuidelines: [
|
|
576
|
-
`Call ${start} only when the user explicitly asks to start a ${mode} workflow and none is active.`,
|
|
577
|
-
"Pass the user's full goal as the goal parameter.",
|
|
578
|
-
],
|
|
579
569
|
parameters: Type.Object({
|
|
580
570
|
goal: Type.String({
|
|
581
571
|
description: `What the user wants the ${mode} workflow to build or accomplish.`,
|
|
@@ -650,11 +640,6 @@ export function createWorkflowExtension(
|
|
|
650
640
|
name: resume,
|
|
651
641
|
label: options.toolLabels.resume,
|
|
652
642
|
description: `Resume an explicitly selected ${mode} workflow run. Pass a run id, artifact directory, or workflow.json path.`,
|
|
653
|
-
promptSnippet: `${resume}(run) - resume an explicit ${mode} workflow run; run is its id, artifact directory, or workflow.json path.`,
|
|
654
|
-
promptGuidelines: [
|
|
655
|
-
`Call ${resume} only when the user explicitly selects a ${mode} workflow run.`,
|
|
656
|
-
"Workflow runs never resume automatically after reload.",
|
|
657
|
-
],
|
|
658
643
|
parameters: Type.Object({ run: Type.String({ description: "Run id, artifact directory, or workflow.json path." }) }),
|
|
659
644
|
async execute(_id, params, _signal, _onUpdate, ctx) {
|
|
660
645
|
return resumeController(controller, ctx, params.run, end);
|
|
@@ -671,10 +656,6 @@ export function createWorkflowExtension(
|
|
|
671
656
|
name: end,
|
|
672
657
|
label: options.toolLabels.end,
|
|
673
658
|
description: `Close the ${mode} workflow. Must be called when the final phase is complete. Marks the workflow finished and stops the agent loop.`,
|
|
674
|
-
promptSnippet: `${end}() - close the finished ${mode} workflow; no further phase transitions allowed.`,
|
|
675
|
-
promptGuidelines: [
|
|
676
|
-
`Call ${end} exactly once, from the terminal phase, when all close artifacts are complete.`,
|
|
677
|
-
],
|
|
678
659
|
parameters: Type.Object({}),
|
|
679
660
|
async execute(_id, _params, _signal, _onUpdate, ctx) {
|
|
680
661
|
const before = checkpoint(controller);
|
|
@@ -5,6 +5,7 @@ description: >
|
|
|
5
5
|
filler, articles, and pleasantries while keeping full technical accuracy.
|
|
6
6
|
Use when user says "caveman mode", "talk like caveman", "use caveman",
|
|
7
7
|
"less tokens", "be brief", or invokes /caveman.
|
|
8
|
+
disable-model-invocation: true
|
|
8
9
|
---
|
|
9
10
|
|
|
10
11
|
Respond terse like smart caveman. All technical substance stay. Only fluff die.
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
name: handoff
|
|
3
3
|
description: Compact the current conversation into a handoff document for another agent to pick up.
|
|
4
4
|
argument-hint: "What will the next session be used for?"
|
|
5
|
+
disable-model-invocation: true
|
|
5
6
|
---
|
|
6
7
|
|
|
7
8
|
Write a handoff document summarising the current conversation so a fresh agent can continue the work. Save to the temporary directory of the user's OS - not the current workspace.
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
name: handoff-text
|
|
3
3
|
description: Compact the current conversation into a handoff document for another agent to pick up.
|
|
4
4
|
argument-hint: "What will the next session be used for?"
|
|
5
|
+
disable-model-invocation: true
|
|
5
6
|
---
|
|
6
7
|
|
|
7
8
|
Write a handoff document summarising the current conversation so a fresh agent can continue the work. Dont save the handoff, output only.
|
|
@@ -7,6 +7,7 @@ description: >
|
|
|
7
7
|
codebase", "audit for over-engineering", "what can I delete from this repo",
|
|
8
8
|
"find bloat", "ponytail-audit", or "/ponytail-audit". One-shot report, does
|
|
9
9
|
not apply fixes.
|
|
10
|
+
disable-model-invocation: true
|
|
10
11
|
---
|
|
11
12
|
|
|
12
13
|
ponytail-review, repo-wide. Scan the whole tree instead of a diff. Rank
|
|
@@ -6,6 +6,7 @@ description: >
|
|
|
6
6
|
of rotting into "later means never". Use when the user says "ponytail debt",
|
|
7
7
|
"/ponytail-debt", "what did ponytail defer", "list the shortcuts", "ponytail
|
|
8
8
|
ledger", or "what did we mark to do later". One-shot report, changes nothing.
|
|
9
|
+
disable-model-invocation: true
|
|
9
10
|
---
|
|
10
11
|
|
|
11
12
|
Every deliberate ponytail shortcut is marked with a `ponytail:` comment naming
|
|
@@ -4,6 +4,7 @@ description: >
|
|
|
4
4
|
Quick-reference card for all ponytail modes, skills, and commands.
|
|
5
5
|
One-shot display, not a persistent mode. Trigger: /ponytail-help,
|
|
6
6
|
"ponytail help", "what ponytail commands", "how do I use ponytail".
|
|
7
|
+
disable-model-invocation: true
|
|
7
8
|
---
|
|
8
9
|
|
|
9
10
|
# Ponytail Help
|
|
@@ -8,6 +8,7 @@ description: >
|
|
|
8
8
|
delete", "is this over-engineered", "simplify review", or invokes
|
|
9
9
|
/ponytail-review. Complements correctness-focused review, this one only
|
|
10
10
|
hunts complexity.
|
|
11
|
+
disable-model-invocation: true
|
|
11
12
|
---
|
|
12
13
|
|
|
13
14
|
Review diffs for unnecessary complexity. One line per finding: location, what
|
package/package.json
CHANGED
|
@@ -1,8 +1,28 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@selesai/code",
|
|
3
|
-
"version": "0.5.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.5.14",
|
|
4
|
+
"description": "Maintained, extension-first Pi coding agent with built-in workflows, subagents, web research, questions, skills, and an enhanced terminal UI.",
|
|
5
5
|
"type": "module",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/SelesaiInTech/selesai-code.git"
|
|
9
|
+
},
|
|
10
|
+
"homepage": "https://github.com/SelesaiInTech/selesai-code#readme",
|
|
11
|
+
"bugs": {
|
|
12
|
+
"url": "https://github.com/SelesaiInTech/selesai-code/issues"
|
|
13
|
+
},
|
|
14
|
+
"keywords": [
|
|
15
|
+
"pi-package",
|
|
16
|
+
"coding-agent",
|
|
17
|
+
"ai-agent",
|
|
18
|
+
"terminal",
|
|
19
|
+
"typescript",
|
|
20
|
+
"llm",
|
|
21
|
+
"subagents",
|
|
22
|
+
"workflow",
|
|
23
|
+
"web-research",
|
|
24
|
+
"tui"
|
|
25
|
+
],
|
|
6
26
|
"piConfig": {
|
|
7
27
|
"name": "selesai",
|
|
8
28
|
"configDir": ".selesai"
|
|
@@ -22,6 +42,7 @@
|
|
|
22
42
|
}
|
|
23
43
|
},
|
|
24
44
|
"files": [
|
|
45
|
+
"README.md",
|
|
25
46
|
"dist",
|
|
26
47
|
"docs",
|
|
27
48
|
"examples",
|