@preapexis/piexis 0.2.2 → 0.2.5
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 +85 -131
- package/apps/server/src/index.ts +97 -0
- package/apps/server/src/session-manager.ts +95 -0
- package/apps/server/src/workspace.ts +39 -0
- package/{tsconfig.json → apps/server/tsconfig.json} +3 -3
- package/apps/web/dist/assets/index-D4n3_lAw.js +10 -0
- package/apps/web/dist/assets/index-igiK0FPY.css +1 -0
- package/apps/web/dist/index.html +3 -0
- package/bin/piexis-web.mjs +24 -0
- package/extensions/agents.ts +205 -0
- package/extensions/ask-question.ts +54 -95
- package/extensions/recommended-tools.ts +62 -0
- package/extensions/ui.ts +204 -58
- package/extensions/webui.ts +45 -0
- package/lib/agent-config.ts +55 -0
- package/lib/agents.ts +208 -0
- package/lib/question-model.ts +98 -0
- package/lib/question-ui.ts +214 -0
- package/lib/recommended-tools.ts +119 -0
- package/lib/subagent-runner.ts +146 -0
- package/lib/ui-tips.json +41 -0
- package/package.json +41 -14
- package/packages/pi-integration/src/rpc-client.ts +99 -0
- package/packages/shared/src/index.ts +27 -0
- package/prompts/init.md +10 -2
- package/prompts/summary.md +7 -6
- package/schemas/agent.schema.json +48 -0
- package/skills/{claude-api/LICENSE.txt → caveman/LICENSE} +2 -2
- package/skills/caveman/LICENSE-MIT +21 -0
- package/skills/caveman/NOTICE +10 -0
- package/skills/caveman/SKILL.md +89 -0
- package/skills/find-skills/SKILL.md +141 -0
- package/skills/ponytail/LICENSE +21 -0
- package/skills/ponytail/NOTICE +9 -0
- package/skills/ponytail/SKILL.md +52 -0
- package/themes/midnight.json +81 -0
- package/.github/workflows/publish.yml +0 -35
- package/AGENTS.md +0 -58
- package/eval/README.md +0 -72
- package/eval/permission-cases.json +0 -28
- package/eval/permissions.mjs +0 -103
- package/extensions/background-tasks.ts +0 -433
- package/extensions/background-worker.ts +0 -50
- package/extensions/glob.ts +0 -32
- package/extensions/mode.ts +0 -397
- package/lib/bash-approvals.ts +0 -113
- package/lib/bash-policy.ts +0 -164
- package/lib/mode-policy.ts +0 -174
- package/lib/path-policy.ts +0 -182
- package/lib/permission-classifier.ts +0 -128
- package/lib/permission-config.ts +0 -91
- package/lib/permission-denials.ts +0 -32
- package/lib/permission-intent.ts +0 -128
- package/lib/permission-taxonomy.ts +0 -15
- package/lib/task-broker.ts +0 -65
- package/lib/task-checkout.ts +0 -39
- package/lib/task-git.ts +0 -44
- package/lib/task-patch.ts +0 -106
- package/lib/task-permissions.ts +0 -51
- package/lib/task-report.ts +0 -62
- package/lib/task-worktree-policy.ts +0 -56
- package/scripts/release.mjs +0 -32
- package/skills/claude-api/SKILL.md +0 -572
- package/skills/claude-api/csharp/claude-api/README.md +0 -362
- package/skills/claude-api/csharp/claude-api/batches.md +0 -14
- package/skills/claude-api/csharp/claude-api/files-api.md +0 -25
- package/skills/claude-api/csharp/claude-api/streaming.md +0 -28
- package/skills/claude-api/csharp/claude-api/tool-use.md +0 -164
- package/skills/claude-api/curl/examples.md +0 -256
- package/skills/claude-api/curl/managed-agents.md +0 -371
- package/skills/claude-api/go/claude-api/README.md +0 -188
- package/skills/claude-api/go/claude-api/files-api.md +0 -23
- package/skills/claude-api/go/claude-api/streaming.md +0 -43
- package/skills/claude-api/go/claude-api/tool-use.md +0 -220
- package/skills/claude-api/go/managed-agents/README.md +0 -564
- package/skills/claude-api/java/claude-api/README.md +0 -238
- package/skills/claude-api/java/claude-api/files-api.md +0 -27
- package/skills/claude-api/java/claude-api/streaming.md +0 -24
- package/skills/claude-api/java/claude-api/tool-use.md +0 -228
- package/skills/claude-api/java/managed-agents/README.md +0 -443
- package/skills/claude-api/php/claude-api/README.md +0 -174
- package/skills/claude-api/php/claude-api/batches.md +0 -15
- package/skills/claude-api/php/claude-api/files-api.md +0 -13
- package/skills/claude-api/php/claude-api/streaming.md +0 -27
- package/skills/claude-api/php/claude-api/tool-use.md +0 -253
- package/skills/claude-api/php/managed-agents/README.md +0 -443
- package/skills/claude-api/python/claude-api/README.md +0 -565
- package/skills/claude-api/python/claude-api/batches.md +0 -198
- package/skills/claude-api/python/claude-api/files-api.md +0 -170
- package/skills/claude-api/python/claude-api/sdk-upgrade.md +0 -286
- package/skills/claude-api/python/claude-api/streaming.md +0 -179
- package/skills/claude-api/python/claude-api/tool-use.md +0 -627
- package/skills/claude-api/python/managed-agents/README.md +0 -335
- package/skills/claude-api/ruby/claude-api/README.md +0 -134
- package/skills/claude-api/ruby/claude-api/streaming.md +0 -16
- package/skills/claude-api/ruby/claude-api/tool-use.md +0 -41
- package/skills/claude-api/ruby/managed-agents/README.md +0 -394
- package/skills/claude-api/shared/admin-api.md +0 -179
- package/skills/claude-api/shared/agent-design.md +0 -101
- package/skills/claude-api/shared/anthropic-cli.md +0 -269
- package/skills/claude-api/shared/claude-platform-on-aws.md +0 -59
- package/skills/claude-api/shared/cost-optimization.md +0 -233
- package/skills/claude-api/shared/error-codes.md +0 -262
- package/skills/claude-api/shared/live-sources.md +0 -166
- package/skills/claude-api/shared/managed-agents-api-reference.md +0 -455
- package/skills/claude-api/shared/managed-agents-client-patterns.md +0 -220
- package/skills/claude-api/shared/managed-agents-core.md +0 -359
- package/skills/claude-api/shared/managed-agents-environments.md +0 -226
- package/skills/claude-api/shared/managed-agents-events.md +0 -290
- package/skills/claude-api/shared/managed-agents-memory.md +0 -201
- package/skills/claude-api/shared/managed-agents-multiagent.md +0 -260
- package/skills/claude-api/shared/managed-agents-onboarding.md +0 -82
- package/skills/claude-api/shared/managed-agents-outcomes.md +0 -108
- package/skills/claude-api/shared/managed-agents-overview.md +0 -77
- package/skills/claude-api/shared/managed-agents-scheduled-deployments.md +0 -156
- package/skills/claude-api/shared/managed-agents-self-hosted-sandboxes.md +0 -282
- package/skills/claude-api/shared/managed-agents-tools.md +0 -504
- package/skills/claude-api/shared/managed-agents-webhooks.md +0 -143
- package/skills/claude-api/shared/model-migration.md +0 -1872
- package/skills/claude-api/shared/models.md +0 -142
- package/skills/claude-api/shared/platform-availability.md +0 -53
- package/skills/claude-api/shared/prompt-audit.md +0 -223
- package/skills/claude-api/shared/prompt-caching.md +0 -292
- package/skills/claude-api/shared/token-counting.md +0 -56
- package/skills/claude-api/shared/tool-use-concepts.md +0 -526
- package/skills/claude-api/typescript/claude-api/README.md +0 -405
- package/skills/claude-api/typescript/claude-api/batches.md +0 -106
- package/skills/claude-api/typescript/claude-api/files-api.md +0 -98
- package/skills/claude-api/typescript/claude-api/streaming.md +0 -178
- package/skills/claude-api/typescript/claude-api/tool-use.md +0 -586
- package/skills/claude-api/typescript/managed-agents/README.md +0 -359
- package/skills-lock.json +0 -359
- package/tests/auto-permissions.test.mjs +0 -315
- package/tests/background-tasks.test.mjs +0 -27
- package/tests/background-worker.test.mjs +0 -60
- package/tests/bash-permissions.test.mjs +0 -276
- package/tests/glob.test.mjs +0 -31
- package/tests/mode.test.mjs +0 -62
- package/tests/permission-evaluation.test.mjs +0 -70
- package/tests/permission-foundation.test.mjs +0 -194
- package/tests/permission-policy.test.mjs +0 -158
- package/tests/runtime/README.md +0 -51
- package/tests/runtime/harness.mjs +0 -190
- package/tests/runtime/permissions.test.mjs +0 -192
- package/tests/runtime/tui.test.mjs +0 -60
- package/tests/skills-routing.test.mjs +0 -51
- package/tests/task-integration.test.mjs +0 -324
- package/tests/task-permissions.test.mjs +0 -178
- package/tests/task-worktree-policy.test.mjs +0 -53
package/extensions/ui.ts
CHANGED
|
@@ -1,20 +1,108 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { getAgentDir, type ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
2
2
|
|
|
3
|
-
import { truncateToWidth } from "@earendil-works/pi-tui";
|
|
3
|
+
import { truncateToWidth, visibleWidth, wrapTextWithAnsi } from "@earendil-works/pi-tui";
|
|
4
4
|
|
|
5
|
+
import { readFileSync } from "node:fs";
|
|
5
6
|
import { basename } from "node:path";
|
|
7
|
+
import { agentConfigCapabilities } from "../lib/agent-config.ts";
|
|
8
|
+
import { hasProjectAgentConfig, loadAgentConfig } from "../lib/agents.ts";
|
|
9
|
+
|
|
10
|
+
const SPINNER = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
|
|
11
|
+
|
|
12
|
+
const WORDMARK = [
|
|
13
|
+
"██████╗ ██╗███████╗██╗ ██╗██╗███████╗",
|
|
14
|
+
"██╔══██╗██║██╔════╝╚██╗██╔╝██║██╔════╝",
|
|
15
|
+
"██████╔╝██║█████╗ ╚███╔╝ ██║███████╗",
|
|
16
|
+
"██╔═══╝ ██║██╔══╝ ██╔██╗ ██║╚════██║",
|
|
17
|
+
"██║ ██║███████╗██╔╝ ██╗██║███████║",
|
|
18
|
+
"╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝╚═╝╚══════╝"
|
|
19
|
+
];
|
|
20
|
+
const WORDMARK_WIDTH = Math.max(...WORDMARK.map((line) => visibleWidth(line)));
|
|
21
|
+
|
|
22
|
+
const fmt = (n: number) => {
|
|
23
|
+
if (n < 1_000) return `${n}`;
|
|
24
|
+
if (n < 1_000_000) return `${(n / 1_000).toFixed(1)}k`;
|
|
25
|
+
return `${(n / 1_000_000).toFixed(1)}m`;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
type Seg = { text: string; priority: number };
|
|
29
|
+
|
|
30
|
+
// Drop optional fields before shortening the last (highest-priority) field.
|
|
31
|
+
function justify(left: string, right: Seg[], sep: string, width: number): string {
|
|
32
|
+
const segs = [...right];
|
|
33
|
+
while (segs.length) {
|
|
34
|
+
const r = segs.map((s) => s.text).join(sep);
|
|
35
|
+
const gap = width - visibleWidth(left) - visibleWidth(r);
|
|
36
|
+
if (gap >= 2) return left + " ".repeat(gap) + r;
|
|
37
|
+
let weakest = 0;
|
|
38
|
+
segs.forEach((s, i) => {
|
|
39
|
+
if (s.priority < segs[weakest].priority) weakest = i;
|
|
40
|
+
});
|
|
41
|
+
if (segs.length === 1) {
|
|
42
|
+
const available = width - visibleWidth(left) - 2;
|
|
43
|
+
if (available > 0) {
|
|
44
|
+
const text = truncateToWidth(r, available, "…");
|
|
45
|
+
return left + " ".repeat(width - visibleWidth(left) - visibleWidth(text)) + text;
|
|
46
|
+
}
|
|
47
|
+
break;
|
|
48
|
+
}
|
|
49
|
+
segs.splice(weakest, 1);
|
|
50
|
+
}
|
|
51
|
+
return truncateToWidth(left, width, "…");
|
|
52
|
+
}
|
|
6
53
|
|
|
7
54
|
export default function (pi: ExtensionAPI) {
|
|
55
|
+
// Jiti can retain imported JSON across reloads; read it once per extension runtime.
|
|
56
|
+
const tipList: { category: string; text: string }[] = JSON.parse(
|
|
57
|
+
readFileSync(new URL("../lib/ui-tips.json", import.meta.url), "utf8")
|
|
58
|
+
);
|
|
59
|
+
if (!Array.isArray(tipList) || tipList.some((tip) =>
|
|
60
|
+
typeof tip?.category !== "string" || !tip.category.trim() ||
|
|
61
|
+
typeof tip?.text !== "string" || !tip.text.trim()
|
|
62
|
+
)) {
|
|
63
|
+
throw new Error("Invalid ui-tips.json: expected tips with non-empty category and text strings.");
|
|
64
|
+
}
|
|
65
|
+
|
|
8
66
|
let state = "ready";
|
|
9
67
|
let activeTool = "";
|
|
10
68
|
let redraw = () => {};
|
|
69
|
+
let remainingTips: typeof tipList = [];
|
|
70
|
+
let agentSetupTips: string[] = [];
|
|
11
71
|
|
|
12
72
|
function update() {
|
|
13
73
|
redraw();
|
|
14
74
|
}
|
|
15
75
|
|
|
16
76
|
pi.on("session_start", async (_event, ctx) => {
|
|
77
|
+
state = "ready";
|
|
78
|
+
activeTool = "";
|
|
79
|
+
redraw = () => {};
|
|
80
|
+
if (ctx.mode !== "tui") return;
|
|
81
|
+
|
|
17
82
|
const project = basename(ctx.cwd);
|
|
83
|
+
agentSetupTips = [];
|
|
84
|
+
try {
|
|
85
|
+
if (ctx.isProjectTrusted() && await hasProjectAgentConfig(ctx.cwd)) {
|
|
86
|
+
const config = await loadAgentConfig(ctx.cwd, getAgentDir(), true);
|
|
87
|
+
for (const key of Object.keys(agentConfigCapabilities) as (keyof typeof agentConfigCapabilities)[]) {
|
|
88
|
+
if (config[key] === undefined) agentSetupTips.push(`Agent config: add ${key} to ${agentConfigCapabilities[key]}.`);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
} catch {
|
|
92
|
+
agentSetupTips = ["Agent config: fix agent.json so PieXis can apply your agent settings."];
|
|
93
|
+
}
|
|
94
|
+
const selectedTips: typeof tipList = [];
|
|
95
|
+
for (let i = 0; i < Math.min(3, tipList.length); i++) {
|
|
96
|
+
if (remainingTips.length === 0) {
|
|
97
|
+
remainingTips = tipList.filter((tip) => !selectedTips.includes(tip));
|
|
98
|
+
}
|
|
99
|
+
const differentCategories = remainingTips.filter(
|
|
100
|
+
(tip) => !selectedTips.some((selected) => selected.category === tip.category)
|
|
101
|
+
);
|
|
102
|
+
const candidates = differentCategories.length > 0 ? differentCategories : remainingTips;
|
|
103
|
+
const tip = candidates[Math.floor(Math.random() * candidates.length)];
|
|
104
|
+
selectedTips.push(remainingTips.splice(remainingTips.indexOf(tip), 1)[0]);
|
|
105
|
+
}
|
|
18
106
|
|
|
19
107
|
// Put project info in the terminal tab instead of wasting TUI rows.
|
|
20
108
|
ctx.ui.setTitle(`π ${project}`);
|
|
@@ -23,82 +111,126 @@ export default function (pi: ExtensionAPI) {
|
|
|
23
111
|
// stay attached to the editor forever, making the onboarding copy noisy.
|
|
24
112
|
ctx.ui.setHeader((_tui, theme) => ({
|
|
25
113
|
render(width: number) {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
114
|
+
if (width <= 0) return [];
|
|
115
|
+
|
|
116
|
+
const left = width < WORDMARK_WIDTH
|
|
117
|
+
? [`${theme.fg("accent", "✦")} ${theme.bold("PieXis")}`]
|
|
118
|
+
: WORDMARK.map((line) => theme.fg("accent", line));
|
|
119
|
+
|
|
120
|
+
const tips = [
|
|
121
|
+
theme.fg("accent", theme.bold("Get started with PieXis")),
|
|
122
|
+
theme.fg("muted", "Questions, skills, prompts & stats."),
|
|
123
|
+
...agentSetupTips.map((tip) => theme.fg("warning", tip)),
|
|
124
|
+
...selectedTips.map((tip) =>
|
|
125
|
+
`${theme.fg("accent", `[${tip.category}]`)} ${theme.fg("muted", tip.text)}`
|
|
126
|
+
),
|
|
127
|
+
theme.fg("dim", "/help · /summary · /commit")
|
|
128
|
+
];
|
|
129
|
+
const tipWidth = width - WORDMARK_WIDTH - 3;
|
|
130
|
+
if (tipWidth >= 32) {
|
|
131
|
+
const right = tips.flatMap((line) => wrapTextWithAnsi(line, tipWidth));
|
|
132
|
+
return Array.from({ length: Math.max(left.length, right.length) }, (_, index) => {
|
|
133
|
+
const line = truncateToWidth(left[index] ?? "", WORDMARK_WIDTH, "");
|
|
134
|
+
const padding = " ".repeat(WORDMARK_WIDTH - visibleWidth(line) + 3);
|
|
135
|
+
return line + padding + (right[index] ?? "");
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
return [
|
|
140
|
+
...left.map((line) => truncateToWidth(line, width, "")),
|
|
141
|
+
"",
|
|
142
|
+
...tips.flatMap((line) => wrapTextWithAnsi(line, width))
|
|
143
|
+
];
|
|
44
144
|
},
|
|
45
145
|
invalidate() {}
|
|
46
146
|
}));
|
|
47
147
|
|
|
48
148
|
ctx.ui.setFooter((tui, theme, footerData) => {
|
|
49
149
|
redraw = () => tui.requestRender();
|
|
150
|
+
const unsubscribe = footerData.onBranchChange(() => tui.requestRender());
|
|
50
151
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
152
|
+
// Spinner animation, only while busy.
|
|
153
|
+
const timer = setInterval(() => {
|
|
154
|
+
if (state !== "ready") tui.requestRender();
|
|
155
|
+
}, 100);
|
|
156
|
+
timer.unref?.();
|
|
54
157
|
|
|
55
158
|
return {
|
|
56
159
|
invalidate() {},
|
|
57
160
|
|
|
58
161
|
render(width: number) {
|
|
59
|
-
|
|
162
|
+
if (width <= 0) return [];
|
|
163
|
+
const dim = (s: string) => theme.fg("dim", s);
|
|
164
|
+
const muted = (s: string) => theme.fg("muted", s);
|
|
165
|
+
const sep = dim(" · ");
|
|
166
|
+
const prompts = ctx.sessionManager.getEntries().filter(
|
|
167
|
+
(entry) => entry.type === "message" && entry.message.role === "user"
|
|
168
|
+
).length;
|
|
60
169
|
|
|
61
|
-
|
|
62
|
-
const
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
170
|
+
let input = 0, output = 0, cacheRead = 0, cacheWrite = 0, cost = 0;
|
|
171
|
+
for (const entry of ctx.sessionManager.getBranch()) {
|
|
172
|
+
if (entry.type !== "message" || entry.message.role !== "assistant") continue;
|
|
173
|
+
const u = entry.message.usage;
|
|
174
|
+
if (!u) continue;
|
|
175
|
+
input += u.input;
|
|
176
|
+
output += u.output;
|
|
177
|
+
cacheRead += u.cacheRead;
|
|
178
|
+
cacheWrite += u.cacheWrite;
|
|
179
|
+
cost += u.cost?.total ?? 0;
|
|
180
|
+
}
|
|
70
181
|
|
|
71
|
-
|
|
182
|
+
const frame = SPINNER[Math.floor(Date.now() / 100) % SPINNER.length];
|
|
183
|
+
const activity = state === "tool" ? `${frame} ${activeTool}`
|
|
184
|
+
: state === "thinking" ? `${frame} thinking` : "● ready";
|
|
185
|
+
const agent = footerData.getExtensionStatuses?.().get("piexis-agent");
|
|
186
|
+
const branch = footerData.getGitBranch();
|
|
187
|
+
// Reserve a small model slot; long tool names must not displace the profile.
|
|
188
|
+
const modelReserve = width >= 32 ? 9 : 0;
|
|
189
|
+
const profile = agent && visibleWidth(agent) + 6 + modelReserve <= width
|
|
190
|
+
? theme.fg("accent", agent) + sep : "";
|
|
191
|
+
const activityWidth = Math.max(1, Math.min(24, width - visibleWidth(profile) - modelReserve));
|
|
192
|
+
const left1 = profile + theme.fg(state === "ready" ? "success" : "accent",
|
|
193
|
+
truncateToWidth(activity, activityWidth, "…"));
|
|
194
|
+
const row1 = justify(left1, [
|
|
195
|
+
{ text: ctx.model?.id ?? "—", priority: 5 },
|
|
196
|
+
{ text: muted(`think ${pi.getThinkingLevel()}`), priority: 4 },
|
|
197
|
+
...(branch ? [{ text: dim(`⎇ ${truncateToWidth(branch, Math.max(8, Math.floor(width / 4)), "…")}`), priority: 3 }] : []),
|
|
198
|
+
{ text: dim(`${prompts} prompts`), priority: 2 },
|
|
199
|
+
...(agent ? [{ text: dim("[Alt+A]"), priority: 1 }] : [])
|
|
200
|
+
], sep, width);
|
|
72
201
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
202
|
+
const usage = ctx.getContextUsage();
|
|
203
|
+
const pct = usage?.percent ?? null;
|
|
204
|
+
const tone = pct == null ? "dim" : pct >= 90 ? "error" : pct >= 70 ? "warning" : "success";
|
|
205
|
+
const pctText = pct == null ? "—" : `${Math.round(pct)}%`;
|
|
206
|
+
const costText = `$${cost < 0.01 ? cost.toFixed(4) : cost.toFixed(2)}`;
|
|
207
|
+
const barWidth = Math.min(12, Math.max(0,
|
|
208
|
+
width - visibleWidth(`ctx ${pctText}`) - visibleWidth(costText) - 2));
|
|
209
|
+
const filled = pct == null ? 0 : Math.round((Math.min(100, Math.max(0, pct)) / 100) * barWidth);
|
|
210
|
+
const bar = theme.fg(tone, "━".repeat(filled)) + dim("─".repeat(barWidth - filled));
|
|
211
|
+
const cap = usage
|
|
212
|
+
? `${usage.tokens == null ? "—" : fmt(usage.tokens)}/${fmt(usage.contextWindow)}`
|
|
213
|
+
: "—/—";
|
|
214
|
+
const context = `${muted("ctx")} ${barWidth ? bar + " " : ""}${theme.fg(tone, pctText)}`;
|
|
215
|
+
const tokens = muted(`↑${fmt(input)} ↓${fmt(output)}`);
|
|
216
|
+
const promptTotal = input + cacheRead + cacheWrite;
|
|
217
|
+
const hit = promptTotal > 0 ? Math.round((cacheRead / promptTotal) * 100) : null;
|
|
218
|
+
// Add capacity only when it leaves room for the basic token totals and cost.
|
|
219
|
+
const showCapacity = visibleWidth(context + " " + cap + tokens + costText) + 7 <= width;
|
|
220
|
+
const left2 = context + (showCapacity ? " " + dim(cap) : "");
|
|
221
|
+
const row2 = justify(left2, [
|
|
222
|
+
{ text: tokens, priority: 2 },
|
|
223
|
+
...(hit == null ? [] : [{ text: dim(`cache ${hit}%`), priority: 1 }]),
|
|
224
|
+
{ text: muted(costText), priority: 3 }
|
|
225
|
+
], sep, width);
|
|
80
226
|
|
|
81
|
-
|
|
82
|
-
// Keep this tolerant of theme/icon changes in the permission extension.
|
|
83
|
-
const modeStatus =
|
|
84
|
-
statuses.find((status) =>
|
|
85
|
-
/(?:manual|accept edits|plan|auto|don't ask|bypass permissions).*on/i.test(
|
|
86
|
-
status
|
|
87
|
-
)
|
|
88
|
-
) ?? "";
|
|
89
|
-
const line =
|
|
90
|
-
`${activity}` +
|
|
91
|
-
(modeStatus ? theme.fg("accent", ` │ ${modeStatus}`) : "") +
|
|
92
|
-
theme.fg(
|
|
93
|
-
"dim",
|
|
94
|
-
` │ ${branch}` + ` │ ${model}` + ` │ ${context}`
|
|
95
|
-
);
|
|
96
|
-
|
|
97
|
-
return [truncateToWidth(line, width, "")];
|
|
227
|
+
return [row1, row2];
|
|
98
228
|
},
|
|
99
229
|
|
|
100
230
|
dispose() {
|
|
231
|
+
clearInterval(timer);
|
|
101
232
|
unsubscribe();
|
|
233
|
+
redraw = () => {};
|
|
102
234
|
}
|
|
103
235
|
};
|
|
104
236
|
});
|
|
@@ -112,6 +244,20 @@ export default function (pi: ExtensionAPI) {
|
|
|
112
244
|
update();
|
|
113
245
|
});
|
|
114
246
|
|
|
247
|
+
pi.on("turn_start", async () => {
|
|
248
|
+
state = "thinking";
|
|
249
|
+
activeTool = "";
|
|
250
|
+
update();
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
pi.on("message_end", async () => {
|
|
254
|
+
update();
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
pi.on("session_compact", async () => {
|
|
258
|
+
update();
|
|
259
|
+
});
|
|
260
|
+
|
|
115
261
|
pi.on("tool_call", async (event) => {
|
|
116
262
|
state = "tool";
|
|
117
263
|
activeTool = event.toolName;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { randomBytes } from "node:crypto";
|
|
2
|
+
import { spawn, type ChildProcess } from "node:child_process";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
import { createServer } from "node:net";
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
7
|
+
|
|
8
|
+
async function availablePort(): Promise<number> {
|
|
9
|
+
const server = createServer();
|
|
10
|
+
await new Promise<void>((resolve, reject) => {
|
|
11
|
+
server.once("error", reject);
|
|
12
|
+
server.listen(0, "127.0.0.1", () => resolve());
|
|
13
|
+
});
|
|
14
|
+
const address = server.address();
|
|
15
|
+
await new Promise<void>((resolve, reject) => server.close((error) => error ? reject(error) : resolve()));
|
|
16
|
+
if (!address || typeof address === "string") throw new Error("Could not select a local PIXIS WebUI port.");
|
|
17
|
+
return address.port;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** Starts the separate local WebUI; Pi's TUI remains usable in this process. */
|
|
21
|
+
export default function webui(pi: ExtensionAPI) {
|
|
22
|
+
const servers = new Map<string, ChildProcess>();
|
|
23
|
+
const launcher = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../bin/piexis-web.mjs");
|
|
24
|
+
|
|
25
|
+
pi.registerCommand("webui", {
|
|
26
|
+
description: "Start the local PIXIS WebUI for this workspace",
|
|
27
|
+
handler: async (_args, ctx) => {
|
|
28
|
+
const running = servers.get(ctx.cwd);
|
|
29
|
+
if (running && running.exitCode === null && !running.killed) {
|
|
30
|
+
ctx.ui.notify("PIXIS WebUI is already running for this workspace.", "info");
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
const token = randomBytes(32).toString("base64url");
|
|
34
|
+
const port = await availablePort();
|
|
35
|
+
const child = spawn(process.execPath, [launcher, "--workspace", ctx.cwd, "--port", `${port}`, "--token", token], {
|
|
36
|
+
detached: true,
|
|
37
|
+
stdio: "ignore",
|
|
38
|
+
windowsHide: true
|
|
39
|
+
});
|
|
40
|
+
child.unref();
|
|
41
|
+
servers.set(ctx.cwd, child);
|
|
42
|
+
ctx.ui.notify(`PIXIS WebUI started: http://127.0.0.1:${port}/#token=${token}`, "info");
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
export const agentNames = ["plan", "orchestrator", "debug", "ask", "code"] as const;
|
|
2
|
+
export type AgentName = typeof agentNames[number];
|
|
3
|
+
export const effortLevels = ["off", "minimal", "low", "medium", "high", "xhigh", "max"] as const;
|
|
4
|
+
export type Effort = typeof effortLevels[number];
|
|
5
|
+
|
|
6
|
+
export const agentConfigSchemaUrl = "https://raw.githubusercontent.com/VarunGaikwad/piexis/main/schemas/agent.schema.json";
|
|
7
|
+
|
|
8
|
+
export interface AgentConfig {
|
|
9
|
+
"$schema"?: string;
|
|
10
|
+
model?: string;
|
|
11
|
+
small_model?: string;
|
|
12
|
+
subagent_model?: string;
|
|
13
|
+
delegate_exploration?: boolean;
|
|
14
|
+
subagent_variant_overrides?: Partial<Record<AgentName, { model?: string; effort?: Effort }>>;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Static values written to a newly created .pi/agent.json.
|
|
19
|
+
* Add new static AgentConfig properties here so project setup includes them.
|
|
20
|
+
*/
|
|
21
|
+
export const initialAgentConfigDefaults = {
|
|
22
|
+
"$schema": agentConfigSchemaUrl,
|
|
23
|
+
delegate_exploration: false,
|
|
24
|
+
} satisfies Pick<AgentConfig, "$schema" | "delegate_exploration">;
|
|
25
|
+
|
|
26
|
+
/** Header copy for incomplete project agent configuration. */
|
|
27
|
+
export type AgentConfigSetting = Exclude<keyof AgentConfig, "$schema">;
|
|
28
|
+
|
|
29
|
+
export const agentConfigCapabilities = {
|
|
30
|
+
model: "choose the default main model",
|
|
31
|
+
small_model: "use a cheaper model for eligible small tasks",
|
|
32
|
+
subagent_model: "choose the normal model for delegated work",
|
|
33
|
+
delegate_exploration: "opt in to cheap, read-only codebase exploration",
|
|
34
|
+
subagent_variant_overrides: "customize model and effort for each agent profile",
|
|
35
|
+
} satisfies Record<AgentConfigSetting, string>;
|
|
36
|
+
|
|
37
|
+
export function createInitialAgentConfig(selected: string, inherited: AgentConfig): AgentConfig {
|
|
38
|
+
const efforts: Record<AgentName, Effort> = { plan: "high", orchestrator: "high", debug: "high", ask: "low", code: "medium" };
|
|
39
|
+
const overrides: NonNullable<AgentConfig["subagent_variant_overrides"]> = {};
|
|
40
|
+
for (const name of agentNames) {
|
|
41
|
+
overrides[name] = {
|
|
42
|
+
model: inherited.subagent_model ?? selected,
|
|
43
|
+
effort: efforts[name],
|
|
44
|
+
...inherited.subagent_variant_overrides?.[name],
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
return {
|
|
48
|
+
"$schema": initialAgentConfigDefaults.$schema,
|
|
49
|
+
model: selected,
|
|
50
|
+
small_model: inherited.small_model ?? selected,
|
|
51
|
+
subagent_model: inherited.subagent_model ?? selected,
|
|
52
|
+
delegate_exploration: inherited.delegate_exploration ?? initialAgentConfigDefaults.delegate_exploration,
|
|
53
|
+
subagent_variant_overrides: overrides,
|
|
54
|
+
};
|
|
55
|
+
}
|
package/lib/agents.ts
ADDED
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
import { link, lstat, mkdir, readFile, rename, rm, writeFile } from "node:fs/promises";
|
|
2
|
+
import { basename, join } from "node:path";
|
|
3
|
+
import { randomUUID } from "node:crypto";
|
|
4
|
+
import { withFileMutationQueue } from "@earendil-works/pi-coding-agent";
|
|
5
|
+
import { agentNames, createInitialAgentConfig, effortLevels, type AgentConfig, type AgentName, type Effort } from "./agent-config.ts";
|
|
6
|
+
|
|
7
|
+
export { agentNames, type AgentConfig, type AgentName, effortLevels, type Effort } from "./agent-config.ts";
|
|
8
|
+
|
|
9
|
+
export const profiles: Record<AgentName, string> = {
|
|
10
|
+
plan: "Explore the request and relevant code, clarify important unknowns, and return an actionable plan with acceptance checks. Do not implement or delegate implementation. Stop after the plan.",
|
|
11
|
+
orchestrator: "Break work into focused tasks and delegate to plan, debug, ask, or code using subagent. Include all necessary context in each task; children do not inherit the conversation. Keep overlapping file changes sequential. Inspect results, resolve disagreements, and report verification evidence. Do not delegate the same orchestration task recursively.",
|
|
12
|
+
debug: "Reproduce or establish evidence for the failure, trace its cause before editing, and make the smallest justified fix. Verify with a regression check. Separate confirmed facts from hypotheses and report any unexecuted checks.",
|
|
13
|
+
ask: "Answer the user's question using relevant code or documentation as evidence. Do not change files or delegate changes. When context is missing, ask rather than invent facts. Stop when the question is answered.",
|
|
14
|
+
code: "Implement the requested scope using existing repository patterns. Preserve unrelated changes, add focused coverage for behavior changes, run relevant checks, and report results. Stop when acceptance conditions are met.",
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export function isAgentName(value: unknown): value is AgentName {
|
|
18
|
+
return typeof value === "string" && agentNames.includes(value as AgentName);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function object(value: unknown, label: string): Record<string, unknown> {
|
|
22
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error(`${label} must be an object`);
|
|
23
|
+
return value as Record<string, unknown>;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function model(value: unknown, label: string): string {
|
|
27
|
+
if (typeof value !== "string" || !/^[^\s/]+\/\S+$/.test(value)) {
|
|
28
|
+
throw new Error(`${label} must be an exact provider/model ID`);
|
|
29
|
+
}
|
|
30
|
+
return value;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function parseAgentConfig(value: unknown): AgentConfig {
|
|
34
|
+
const source = object(value, "Agent configuration");
|
|
35
|
+
const result: AgentConfig = {};
|
|
36
|
+
for (const key of Object.keys(source)) {
|
|
37
|
+
if (!["$schema", "model", "small_model", "subagent_model", "delegate_exploration", "subagent_variant_overrides"].includes(key)) {
|
|
38
|
+
throw new Error(`Unknown agent configuration key: ${key}`);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
if (source.$schema !== undefined) {
|
|
42
|
+
if (typeof source.$schema !== "string") throw new Error("$schema must be a string");
|
|
43
|
+
result.$schema = source.$schema;
|
|
44
|
+
}
|
|
45
|
+
for (const key of ["model", "small_model", "subagent_model"] as const) {
|
|
46
|
+
if (source[key] !== undefined) result[key] = model(source[key], key);
|
|
47
|
+
}
|
|
48
|
+
if (source.delegate_exploration !== undefined) {
|
|
49
|
+
if (typeof source.delegate_exploration !== "boolean") throw new Error("delegate_exploration must be a boolean");
|
|
50
|
+
result.delegate_exploration = source.delegate_exploration;
|
|
51
|
+
}
|
|
52
|
+
if (source.subagent_variant_overrides !== undefined) {
|
|
53
|
+
result.subagent_variant_overrides = {};
|
|
54
|
+
for (const [name, value] of Object.entries(object(source.subagent_variant_overrides, "subagent_variant_overrides"))) {
|
|
55
|
+
if (!isAgentName(name)) throw new Error(`Unknown agent variant: ${name}`);
|
|
56
|
+
const override = object(value, `Override ${name}`);
|
|
57
|
+
for (const key of Object.keys(override)) {
|
|
58
|
+
if (key !== "model" && key !== "effort") throw new Error(`Unknown override key: ${name}.${key}`);
|
|
59
|
+
}
|
|
60
|
+
const parsed: { model?: string; effort?: Effort } = {};
|
|
61
|
+
if (override.model !== undefined) parsed.model = model(override.model, `${name}.model`);
|
|
62
|
+
if (override.effort !== undefined) {
|
|
63
|
+
if (!effortLevels.includes(override.effort as Effort)) throw new Error(`Invalid effort for ${name}`);
|
|
64
|
+
parsed.effort = override.effort as Effort;
|
|
65
|
+
}
|
|
66
|
+
result.subagent_variant_overrides[name] = parsed;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return result;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export async function loadAgentConfig(cwd: string, agentDir: string, trusted: boolean): Promise<AgentConfig> {
|
|
73
|
+
async function load(directory: string): Promise<AgentConfig> {
|
|
74
|
+
for (const name of ["agent.json", "agents.json"]) {
|
|
75
|
+
const path = join(directory, name);
|
|
76
|
+
try { return parseAgentConfig(JSON.parse(await readFile(path, "utf8"))); }
|
|
77
|
+
catch (error) {
|
|
78
|
+
if ((error as NodeJS.ErrnoException).code === "ENOENT") continue;
|
|
79
|
+
throw new Error(`${path}: ${error instanceof Error ? error.message : String(error)}`);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return {};
|
|
83
|
+
}
|
|
84
|
+
const global = await load(agentDir);
|
|
85
|
+
const project = trusted ? await load(join(cwd, ".pi")) : {};
|
|
86
|
+
const overrides = { ...global.subagent_variant_overrides };
|
|
87
|
+
for (const name of agentNames) {
|
|
88
|
+
if (project.subagent_variant_overrides?.[name]) {
|
|
89
|
+
overrides[name] = { ...overrides[name], ...project.subagent_variant_overrides[name] };
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return { ...global, ...project, subagent_variant_overrides: overrides };
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export async function hasProjectAgentConfig(cwd: string): Promise<boolean> {
|
|
96
|
+
for (const name of ["agent.json", "agents.json"]) {
|
|
97
|
+
try { await lstat(join(cwd, ".pi", name)); return true; }
|
|
98
|
+
catch (error) { if ((error as NodeJS.ErrnoException).code !== "ENOENT") throw error; }
|
|
99
|
+
}
|
|
100
|
+
return false;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/** Create an opt-in project scaffold atomically, never replace existing or legacy configuration. */
|
|
104
|
+
export async function initializeAgentConfig(cwd: string, agentDir: string, defaultModel: string | undefined, signal?: AbortSignal): Promise<boolean> {
|
|
105
|
+
const directory = join(cwd, ".pi");
|
|
106
|
+
const path = join(directory, "agent.json");
|
|
107
|
+
return withFileMutationQueue(path, async () => {
|
|
108
|
+
signal?.throwIfAborted();
|
|
109
|
+
if (await hasProjectAgentConfig(cwd)) return false;
|
|
110
|
+
if (!defaultModel) throw new Error("No active model; select a model and create .pi/agent.json manually.");
|
|
111
|
+
const inherited = await loadAgentConfig(cwd, agentDir, false);
|
|
112
|
+
const selected = inherited.model ?? model(defaultModel, "Default model");
|
|
113
|
+
const config = createInitialAgentConfig(selected, inherited);
|
|
114
|
+
signal?.throwIfAborted();
|
|
115
|
+
await mkdir(directory, { recursive: true });
|
|
116
|
+
const temporary = join(directory, `.agent.json.piexis-${randomUUID()}.tmp`);
|
|
117
|
+
try {
|
|
118
|
+
await writeFile(temporary, `${JSON.stringify(config, null, 2)}\n`, { flag: "wx", mode: 0o600 });
|
|
119
|
+
signal?.throwIfAborted();
|
|
120
|
+
if (await hasProjectAgentConfig(cwd)) return false;
|
|
121
|
+
// Unlike rename, link fails if another writer has already created the destination.
|
|
122
|
+
try { await link(temporary, path); }
|
|
123
|
+
catch (error) { if ((error as NodeJS.ErrnoException).code === "EEXIST") return false; throw error; }
|
|
124
|
+
return true;
|
|
125
|
+
} finally { await rm(temporary, { force: true }); }
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export async function completeProjectAgentConfig(cwd: string, agentDir: string, defaultModel: string | undefined, signal?: AbortSignal): Promise<"created" | "updated" | "complete"> {
|
|
130
|
+
const directory = join(cwd, ".pi");
|
|
131
|
+
let path: string | undefined;
|
|
132
|
+
for (const name of ["agent.json", "agents.json"]) {
|
|
133
|
+
const candidate = join(directory, name);
|
|
134
|
+
try { await lstat(candidate); path = candidate; break; }
|
|
135
|
+
catch (error) { if ((error as NodeJS.ErrnoException).code !== "ENOENT") throw error; }
|
|
136
|
+
}
|
|
137
|
+
if (!path) {
|
|
138
|
+
if (!await initializeAgentConfig(cwd, agentDir, defaultModel, signal)) return "complete";
|
|
139
|
+
return "created";
|
|
140
|
+
}
|
|
141
|
+
return withFileMutationQueue(path, async () => {
|
|
142
|
+
signal?.throwIfAborted();
|
|
143
|
+
if (!(await lstat(path!)).isFile()) throw new Error(`${path} must be a regular file`);
|
|
144
|
+
const source = parseAgentConfig(JSON.parse(await readFile(path!, "utf8")));
|
|
145
|
+
const inherited = await loadAgentConfig(cwd, agentDir, false);
|
|
146
|
+
const selected = source.model ?? inherited.model ?? (defaultModel ? model(defaultModel, "Default model") : undefined);
|
|
147
|
+
if (!selected) throw new Error("No active model; select a model and configure .pi/agent.json manually.");
|
|
148
|
+
const defaults = createInitialAgentConfig(selected, inherited);
|
|
149
|
+
const overrides: NonNullable<AgentConfig["subagent_variant_overrides"]> = {};
|
|
150
|
+
let changed = Object.keys(defaults).some((key) => source[key as keyof AgentConfig] === undefined);
|
|
151
|
+
for (const name of agentNames) {
|
|
152
|
+
const expected = defaults.subagent_variant_overrides![name]!;
|
|
153
|
+
const actual = source.subagent_variant_overrides?.[name];
|
|
154
|
+
if (!actual?.model || actual.effort === undefined) changed = true;
|
|
155
|
+
overrides[name] = { ...expected, ...actual };
|
|
156
|
+
}
|
|
157
|
+
if (!changed) return "complete";
|
|
158
|
+
const config: AgentConfig = { ...defaults, ...source, subagent_variant_overrides: overrides };
|
|
159
|
+
const temporary = join(directory, `.${basename(path!)}.piexis-${randomUUID()}.tmp`);
|
|
160
|
+
try {
|
|
161
|
+
await writeFile(temporary, `${JSON.stringify(config, null, 2)}\n`, { flag: "wx", mode: 0o600 });
|
|
162
|
+
signal?.throwIfAborted();
|
|
163
|
+
await rename(temporary, path!);
|
|
164
|
+
return "updated";
|
|
165
|
+
} finally { await rm(temporary, { force: true }); }
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
export function isSmallTask(agent: AgentName, task: string): boolean {
|
|
170
|
+
// shortcut: conservative text heuristic, replace with evaluated routing if real tasks outgrow it.
|
|
171
|
+
if (task.length > 400 || task.split(/\s+/).length > 65) return false;
|
|
172
|
+
if (/\b(architect\w*|design|implement|investigate|debug|refactor|migrat\w*|security|race|performance|across|multiple|entire|comprehensive)\b/i.test(task)) return false;
|
|
173
|
+
if (agent === "ask") return /^\s*(find|locate|list|what (is|does|file)|where (is|are)|explain|summari[sz]e)\b/i.test(task);
|
|
174
|
+
if (agent === "code") return /^\s*(fix (a |the )?typo|correct (a |the )?typo|rename (a |the )?(variable|symbol)|update (a |the )?(comment|label))\b/i.test(task);
|
|
175
|
+
return false;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
export function routeSubagent(config: AgentConfig, agent: AgentName, task: string, currentModel: string, currentEffort: Effort, exploration = false) {
|
|
179
|
+
if (exploration) {
|
|
180
|
+
if (!config.small_model) throw new Error("Exploration delegation requires small_model in agent.json");
|
|
181
|
+
return { model: config.small_model, effort: "low" as Effort, reason: "configured exploration small model" };
|
|
182
|
+
}
|
|
183
|
+
const override = config.subagent_variant_overrides?.[agent];
|
|
184
|
+
const small = Boolean(config.small_model && isSmallTask(agent, task) && !override?.model);
|
|
185
|
+
return {
|
|
186
|
+
model: override?.model ?? (small ? config.small_model! : config.subagent_model ?? currentModel),
|
|
187
|
+
effort: override?.effort ?? (small ? "low" : currentEffort),
|
|
188
|
+
reason: override?.model ? "variant override" : small ? "automatic small task" : "default subagent",
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
export function subagentTools(agent: AgentName, active: string[], depth: number, allowExplorationDelegation = false): string[] {
|
|
193
|
+
if (agent === "plan" || agent === "ask") {
|
|
194
|
+
return active.filter((name) => ["read", "grep", "find", "ls"].includes(name) || (allowExplorationDelegation && depth < 2 && name === "subagent"));
|
|
195
|
+
}
|
|
196
|
+
return active.filter((name) => name !== "AskQuestion" && (name !== "subagent" || depth < 2));
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
export function childResourceArgs(argv: string[]): string[] {
|
|
200
|
+
const result: string[] = [];
|
|
201
|
+
const values = ["--extension", "-e", "--skill", "--prompt-template", "--system-prompt", "--append-system-prompt"];
|
|
202
|
+
const flags = ["--no-extensions", "-ne", "--no-skills", "-ns", "--no-prompt-templates", "-np", "--no-context-files", "-nc", "--no-mcp", "--offline"];
|
|
203
|
+
for (let i = 0; i < argv.length && argv[i] !== "--"; i++) {
|
|
204
|
+
if (values.includes(argv[i]) && argv[i + 1] !== undefined) result.push(argv[i], argv[++i]);
|
|
205
|
+
else if (flags.includes(argv[i])) result.push(argv[i]);
|
|
206
|
+
}
|
|
207
|
+
return result;
|
|
208
|
+
}
|