@selesai/code 0.5.12 → 0.5.13
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/dist/extensions/caveman/caveman-instructions.cjs +6 -34
- package/dist/extensions/caveman/test/helpers.test.js +7 -13
- 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/package.json +1 -1
|
@@ -1,39 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
const path = require('path');
|
|
7
|
-
|
|
8
|
-
const SKILL_PATH = path.join(__dirname, '..', '..', 'skills', 'caveman', 'SKILL.md');
|
|
9
|
-
|
|
10
|
-
const FALLBACK_INSTRUCTIONS =
|
|
11
|
-
'CAVEMAN MODE ACTIVE.\n\n' +
|
|
12
|
-
'Respond terse like smart caveman. All technical substance stay. Only fluff die.\n\n' +
|
|
13
|
-
'## Persistence\n\n' +
|
|
14
|
-
'ACTIVE EVERY RESPONSE once triggered. No revert after many turns. No filler drift. ' +
|
|
15
|
-
'Still active if unsure. Off only when user says "stop caveman" or "normal mode".\n\n' +
|
|
16
|
-
'## Rules\n\n' +
|
|
17
|
-
'Drop: articles (a/an/the), filler (just/really/basically/actually/simply), ' +
|
|
18
|
-
'pleasantries (sure/certainly/of course/happy to), hedging. Fragments OK. ' +
|
|
19
|
-
'Short synonyms. Abbreviate common terms (DB/auth/config/req/res/fn/impl). ' +
|
|
20
|
-
'Strip conjunctions. Use arrows for causality (X -> Y). One word when one word enough.\n\n' +
|
|
21
|
-
'Technical terms stay exact. Code blocks unchanged. Errors quoted exact.\n\n' +
|
|
22
|
-
'## Auto-Clarity Exception\n\n' +
|
|
23
|
-
'Drop caveman temporarily for: security warnings, irreversible action confirmations, ' +
|
|
24
|
-
'multi-step sequences where fragment order risks misread, user asks to clarify or ' +
|
|
25
|
-
'repeats question. Resume caveman after clear part done.';
|
|
26
|
-
|
|
27
|
-
function stripFrontmatter(body) {
|
|
28
|
-
return String(body || '').replace(/^---[\s\S]*?---\s*/, '');
|
|
29
|
-
}
|
|
2
|
+
// The skill is user-facing documentation. This short runtime prompt is injected
|
|
3
|
+
// on every turn, while extension state owns activation and deactivation.
|
|
4
|
+
const CAVEMAN_INSTRUCTIONS =
|
|
5
|
+
'CAVEMAN MODE ACTIVE. Terse, technically complete: omit articles, filler, pleasantries, and hedging; fragments, short terms, standard abbreviations, and -> are OK. Preserve technical terms, code blocks, and quoted errors exactly. Use clear normal prose for security warnings, irreversible confirmations, risky multi-step instructions, or requested/repeated clarification; then resume terse mode.';
|
|
30
6
|
|
|
31
7
|
function getCavemanInstructions() {
|
|
32
|
-
|
|
33
|
-
return 'CAVEMAN MODE ACTIVE.\n\n' + stripFrontmatter(fs.readFileSync(SKILL_PATH, 'utf8'));
|
|
34
|
-
} catch (e) {
|
|
35
|
-
return FALLBACK_INSTRUCTIONS;
|
|
36
|
-
}
|
|
8
|
+
return CAVEMAN_INSTRUCTIONS;
|
|
37
9
|
}
|
|
38
10
|
|
|
39
|
-
module.exports = { getCavemanInstructions
|
|
11
|
+
module.exports = { getCavemanInstructions };
|
|
@@ -1,6 +1,4 @@
|
|
|
1
1
|
import assert from "node:assert/strict";
|
|
2
|
-
import { readFileSync } from "node:fs";
|
|
3
|
-
import { fileURLToPath } from "node:url";
|
|
4
2
|
import test from "node:test";
|
|
5
3
|
|
|
6
4
|
import {
|
|
@@ -48,17 +46,13 @@ test("isDeactivationCommand: only the whole message, case-insensitive, trailing
|
|
|
48
46
|
assert.equal(isDeactivationCommand("how do I stop caveman re-rendering"), false);
|
|
49
47
|
});
|
|
50
48
|
|
|
51
|
-
test("getCavemanInstructions
|
|
49
|
+
test("getCavemanInstructions returns the compact runtime prompt", () => {
|
|
52
50
|
const instructions = getCavemanInstructions();
|
|
53
|
-
assert.ok(instructions.startsWith("CAVEMAN MODE ACTIVE"));
|
|
54
|
-
assert.ok(!instructions.includes("name: caveman")); // frontmatter gone
|
|
55
|
-
assert.ok(instructions.includes("Respond terse like smart caveman"));
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
test("getCavemanInstructions matches src/skills/caveman/SKILL.md body", () => {
|
|
59
|
-
const skillPath = fileURLToPath(new URL("../../../skills/caveman/SKILL.md", import.meta.url));
|
|
60
|
-
const raw = readFileSync(skillPath, "utf8");
|
|
61
|
-
const body = raw.replace(/^---[\s\S]*?---\s*/, "");
|
|
62
51
|
|
|
63
|
-
assert.
|
|
52
|
+
assert.equal(instructions,
|
|
53
|
+
"CAVEMAN MODE ACTIVE. Terse, technically complete: omit articles, filler, pleasantries, and hedging; fragments, short terms, standard abbreviations, and -> are OK. Preserve technical terms, code blocks, and quoted errors exactly. Use clear normal prose for security warnings, irreversible confirmations, risky multi-step instructions, or requested/repeated clarification; then resume terse mode.");
|
|
54
|
+
assert.ok(!instructions.includes("Persistence"));
|
|
55
|
+
assert.ok(!instructions.includes("stop caveman"));
|
|
56
|
+
assert.ok(!instructions.includes("normal mode"));
|
|
57
|
+
assert.ok(instructions.length < 400);
|
|
64
58
|
});
|
|
@@ -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);
|