archymedes-cli 2.0.0
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/I18N.md +46 -0
- package/LICENSE +202 -0
- package/NOTICE +12 -0
- package/README.md +309 -0
- package/dist/archymedes-0jxzsh4z.mjs +9004 -0
- package/dist/archymedes-1g1y6q74.mjs +86 -0
- package/dist/archymedes-2tp22t9a.mjs +254 -0
- package/dist/archymedes-44c1mqas.mjs +171 -0
- package/dist/archymedes-brkqgrxv.mjs +212 -0
- package/dist/archymedes-dwvn4c51.mjs +826 -0
- package/dist/archymedes-h2x8ct7s.mjs +8853 -0
- package/dist/archymedes-jecyxv5c.mjs +249 -0
- package/dist/archymedes-k1bx08z9.mjs +589 -0
- package/dist/archymedes-main.mjs +26941 -0
- package/dist/archymedes-x67v3n58.mjs +67 -0
- package/dist/archymedes.mjs +18 -0
- package/dist/defender-knowledge/README.md +15 -0
- package/dist/defender-knowledge/knowledge-v1.jsonl +14 -0
- package/dist/editor-screen-51z7z11j.mjs +400 -0
- package/dist/factory-0z4he0ev.mjs +51462 -0
- package/dist/file-screen-eg28a3ky.mjs +320 -0
- package/dist/guide-screen-mfvwkdhb.mjs +565 -0
- package/dist/index-teq1cta4.mjs +52 -0
- package/dist/node-0fh63p8z.mjs +892 -0
- package/dist/sandbox-templates-8sd1psez.mjs +59 -0
- package/dist/workspace-screen-3zztn4by.mjs +240 -0
- package/package.json +62 -0
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ALLOWED_SANDBOX_PROGRAMS
|
|
3
|
+
} from "./archymedes-1g1y6q74.mjs";
|
|
4
|
+
import"./archymedes-x67v3n58.mjs";
|
|
5
|
+
|
|
6
|
+
// packages/core/src/sandbox-templates.ts
|
|
7
|
+
var WORKSPACE_PRESETS = [
|
|
8
|
+
{
|
|
9
|
+
id: "general",
|
|
10
|
+
label: "General",
|
|
11
|
+
description: "The stock image: Python, Node and git, with no extra tooling. Fastest to start.",
|
|
12
|
+
templateAlias: "base",
|
|
13
|
+
programs: ["npm", "git", "node", "python", "python3", "ls", "pwd", "find"]
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
id: "python-data",
|
|
17
|
+
label: "Python + data",
|
|
18
|
+
description: "Python with pytest, uv and ripgrep for scripting, analysis and test-driven work.",
|
|
19
|
+
templateAlias: "circuit-python-data",
|
|
20
|
+
programs: ["git", "python", "python3", "pytest", "uv", "rg", "ls", "pwd", "find"]
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
id: "node-web",
|
|
24
|
+
label: "Node + web",
|
|
25
|
+
description: "Node with npm, bun and ripgrep for JavaScript and TypeScript projects.",
|
|
26
|
+
templateAlias: "circuit-node-web",
|
|
27
|
+
programs: ["npm", "bun", "node", "git", "rg", "ls", "pwd", "find"]
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
id: "next-app",
|
|
31
|
+
label: "Deployable Next.js app",
|
|
32
|
+
description: "A production Next.js starter with dependencies installed for verified mobile web-app builds.",
|
|
33
|
+
templateAlias: "circuit-next-web",
|
|
34
|
+
programs: ["npm", "bun", "node", "git", "rg", "ls", "pwd", "find"]
|
|
35
|
+
}
|
|
36
|
+
];
|
|
37
|
+
var DEFAULT_WORKSPACE_PRESET_ID = "general";
|
|
38
|
+
function findWorkspacePreset(id) {
|
|
39
|
+
return WORKSPACE_PRESETS.find((preset) => preset.id === id) ?? WORKSPACE_PRESETS[0];
|
|
40
|
+
}
|
|
41
|
+
function presetPrograms(preset) {
|
|
42
|
+
const permitted = new Set(ALLOWED_SANDBOX_PROGRAMS);
|
|
43
|
+
return preset.programs.filter((program) => permitted.has(program));
|
|
44
|
+
}
|
|
45
|
+
function inferWorkspacePresetId(objective) {
|
|
46
|
+
const normalized = objective.toLowerCase();
|
|
47
|
+
const asksToCreate = /\b(build|create|make|develop|design|scaffold|launch)\b/.test(normalized);
|
|
48
|
+
const namesAnApp = /\b(app|application|website|dashboard|portal|platform|web ?app|saas)\b/.test(normalized);
|
|
49
|
+
const namesAWebSurface = /\b(page|site|landing|storefront|front-?end|ui|web interface)\b/.test(normalized);
|
|
50
|
+
const namesANonWebArtifact = /\b(command-?line|cli|library|module|package|script|api|endpoint|microservice|daemon|parser|converter)\b/.test(normalized);
|
|
51
|
+
return asksToCreate && (namesAnApp || namesAWebSurface && !namesANonWebArtifact) ? "next-app" : undefined;
|
|
52
|
+
}
|
|
53
|
+
export {
|
|
54
|
+
presetPrograms,
|
|
55
|
+
inferWorkspacePresetId,
|
|
56
|
+
findWorkspacePreset,
|
|
57
|
+
WORKSPACE_PRESETS,
|
|
58
|
+
DEFAULT_WORKSPACE_PRESET_ID
|
|
59
|
+
};
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
import {
|
|
2
|
+
StreamSeries,
|
|
3
|
+
waveLine
|
|
4
|
+
} from "./archymedes-2tp22t9a.mjs";
|
|
5
|
+
import {
|
|
6
|
+
clip
|
|
7
|
+
} from "./archymedes-44c1mqas.mjs";
|
|
8
|
+
import {
|
|
9
|
+
scrollIndicator,
|
|
10
|
+
scrollPercent,
|
|
11
|
+
visibleWidth
|
|
12
|
+
} from "./archymedes-dwvn4c51.mjs";
|
|
13
|
+
import"./archymedes-jecyxv5c.mjs";
|
|
14
|
+
import {
|
|
15
|
+
__require
|
|
16
|
+
} from "./archymedes-x67v3n58.mjs";
|
|
17
|
+
|
|
18
|
+
// packages/archymedes-cli/src/workspace-screen.tsx
|
|
19
|
+
import { useInput, useInterval, useState } from "@termuijs/jsx";
|
|
20
|
+
import { Box, Text } from "@termuijs/widgets";
|
|
21
|
+
|
|
22
|
+
// packages/archymedes-cli/src/workspace-model.ts
|
|
23
|
+
function paneHeight(rows) {
|
|
24
|
+
return Math.max(1, rows - 4);
|
|
25
|
+
}
|
|
26
|
+
function selectedPane(snapshot) {
|
|
27
|
+
if (snapshot.panes.length === 0)
|
|
28
|
+
return;
|
|
29
|
+
const index = Math.max(0, Math.min(snapshot.selected, snapshot.panes.length - 1));
|
|
30
|
+
return snapshot.panes[index];
|
|
31
|
+
}
|
|
32
|
+
function visibleLines(pane, height, scroll) {
|
|
33
|
+
const clamped = Math.max(0, Math.min(scroll, Math.max(0, pane.lines.length - height)));
|
|
34
|
+
const end = pane.lines.length - clamped;
|
|
35
|
+
return pane.lines.slice(Math.max(0, end - height), end);
|
|
36
|
+
}
|
|
37
|
+
function atLiveEdge(pane, height, scroll) {
|
|
38
|
+
return Math.min(scroll, Math.max(0, pane.lines.length - height)) === 0;
|
|
39
|
+
}
|
|
40
|
+
function keyToAction(key, character) {
|
|
41
|
+
const name = key.name ?? "";
|
|
42
|
+
const isEscape = name === "escape" || name === "\x1B";
|
|
43
|
+
const isEnter = name === "return" || name === "enter" || name === "\r" || name === `
|
|
44
|
+
`;
|
|
45
|
+
if (isEscape || name === "q" && !key.ctrl || key.ctrl && name === "c")
|
|
46
|
+
return { kind: "exit" };
|
|
47
|
+
if (isEnter)
|
|
48
|
+
return { kind: "focus" };
|
|
49
|
+
if (name === "tab")
|
|
50
|
+
return { kind: "cycle", step: key.shift ? -1 : 1 };
|
|
51
|
+
if (name === "left")
|
|
52
|
+
return { kind: "cycle", step: -1 };
|
|
53
|
+
if (name === "right")
|
|
54
|
+
return { kind: "cycle", step: 1 };
|
|
55
|
+
if (name === "up" || name === "k")
|
|
56
|
+
return { kind: "scroll", lines: 1 };
|
|
57
|
+
if (name === "down" || name === "j")
|
|
58
|
+
return { kind: "scroll", lines: -1 };
|
|
59
|
+
if (name === "pageup")
|
|
60
|
+
return { kind: "scroll", lines: 10 };
|
|
61
|
+
if (name === "pagedown")
|
|
62
|
+
return { kind: "scroll", lines: -10 };
|
|
63
|
+
if (name === "home" || character === "g")
|
|
64
|
+
return { kind: "top" };
|
|
65
|
+
if (name === "end" || character === "G")
|
|
66
|
+
return { kind: "bottom" };
|
|
67
|
+
const digit = character && /^[1-9]$/.test(character) ? Number(character) : undefined;
|
|
68
|
+
if (digit !== undefined)
|
|
69
|
+
return { kind: "select", index: digit - 1 };
|
|
70
|
+
return { kind: "none" };
|
|
71
|
+
}
|
|
72
|
+
function applyAction(snapshot, action) {
|
|
73
|
+
const count = snapshot.panes.length;
|
|
74
|
+
if (count === 0 || action.kind === "none" || action.kind === "exit" || action.kind === "focus")
|
|
75
|
+
return snapshot;
|
|
76
|
+
if (action.kind === "select") {
|
|
77
|
+
if (action.index < 0 || action.index >= count)
|
|
78
|
+
return snapshot;
|
|
79
|
+
return { ...snapshot, selected: action.index, scroll: 0 };
|
|
80
|
+
}
|
|
81
|
+
if (action.kind === "cycle") {
|
|
82
|
+
const next = ((snapshot.selected + action.step) % count + count) % count;
|
|
83
|
+
return { ...snapshot, selected: next, scroll: 0 };
|
|
84
|
+
}
|
|
85
|
+
const pane = selectedPane(snapshot);
|
|
86
|
+
const height = paneHeight(snapshot.rows);
|
|
87
|
+
const furthest = Math.max(0, (pane?.lines.length ?? 0) - height);
|
|
88
|
+
if (action.kind === "bottom")
|
|
89
|
+
return { ...snapshot, scroll: 0 };
|
|
90
|
+
if (action.kind === "top")
|
|
91
|
+
return { ...snapshot, scroll: furthest };
|
|
92
|
+
return { ...snapshot, scroll: Math.max(0, Math.min(snapshot.scroll + action.lines, furthest)) };
|
|
93
|
+
}
|
|
94
|
+
function paneTabs(snapshot) {
|
|
95
|
+
const selected = Math.max(0, Math.min(snapshot.selected, snapshot.panes.length - 1));
|
|
96
|
+
return snapshot.panes.map((pane, index) => ({
|
|
97
|
+
label: `${index + 1} ${pane.title}`,
|
|
98
|
+
active: index === selected,
|
|
99
|
+
status: pane.status
|
|
100
|
+
}));
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
class PaneActivity {
|
|
104
|
+
capacity;
|
|
105
|
+
series = new Map;
|
|
106
|
+
seen = new Map;
|
|
107
|
+
constructor(capacity = 48) {
|
|
108
|
+
this.capacity = capacity;
|
|
109
|
+
}
|
|
110
|
+
sample(panes) {
|
|
111
|
+
const live = new Set(panes.map((pane) => pane.key));
|
|
112
|
+
for (const key of [...this.series.keys()]) {
|
|
113
|
+
if (!live.has(key)) {
|
|
114
|
+
this.series.delete(key);
|
|
115
|
+
this.seen.delete(key);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
const result = new Map;
|
|
119
|
+
for (const pane of panes) {
|
|
120
|
+
const produced = pane.lines.length + pane.dropped;
|
|
121
|
+
const previous = this.seen.get(pane.key);
|
|
122
|
+
this.seen.set(pane.key, produced);
|
|
123
|
+
const stream = this.series.get(pane.key) ?? new StreamSeries(this.capacity);
|
|
124
|
+
this.series.set(pane.key, stream);
|
|
125
|
+
if (previous !== undefined)
|
|
126
|
+
stream.push(Math.max(0, produced - previous));
|
|
127
|
+
result.set(pane.key, stream.values());
|
|
128
|
+
}
|
|
129
|
+
return result;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
var STATUS_MARK = {
|
|
133
|
+
running: "●",
|
|
134
|
+
failed: "✕",
|
|
135
|
+
done: "✓",
|
|
136
|
+
idle: " "
|
|
137
|
+
};
|
|
138
|
+
var WORKSPACE_LEGEND = "1-9 pane · ←→ move · ↑↓ scroll · g/G ends · q leave";
|
|
139
|
+
function paneBar(snapshot, columns) {
|
|
140
|
+
const tabs = paneTabs(snapshot);
|
|
141
|
+
const cells = tabs.map((tab) => ` ${STATUS_MARK[tab.status] ?? " "}${tab.label} `);
|
|
142
|
+
const full = cells.join("");
|
|
143
|
+
if (full.length <= columns)
|
|
144
|
+
return full || " no panes ";
|
|
145
|
+
const selected = Math.max(0, Math.min(snapshot.selected, tabs.length - 1));
|
|
146
|
+
return `${selected > 0 ? "‹ " : ""}${cells[selected] ?? " no panes "}${selected < tabs.length - 1 ? " ›" : ""}`;
|
|
147
|
+
}
|
|
148
|
+
function composeFrame(snapshot) {
|
|
149
|
+
const rows = [];
|
|
150
|
+
const columns = Math.max(1, Math.floor(snapshot.columns));
|
|
151
|
+
const rowCount = Math.max(1, Math.floor(snapshot.rows));
|
|
152
|
+
const pane = selectedPane(snapshot);
|
|
153
|
+
const height = paneHeight(rowCount);
|
|
154
|
+
const theme = snapshot.palette.tokens;
|
|
155
|
+
const bar = paneBar(snapshot, columns);
|
|
156
|
+
rows.push({ text: bar || " no panes ", bold: true, color: theme.primary });
|
|
157
|
+
const scrolled = pane && !atLiveEdge(pane, height, snapshot.scroll);
|
|
158
|
+
const position = (() => {
|
|
159
|
+
if (!pane || !scrolled)
|
|
160
|
+
return "";
|
|
161
|
+
const maxOffset = Math.max(0, pane.lines.length - height);
|
|
162
|
+
const offsetFromTop = maxOffset - Math.min(snapshot.scroll, maxOffset);
|
|
163
|
+
return scrollIndicator(scrollPercent(offsetFromTop, pane.lines.length, height));
|
|
164
|
+
})();
|
|
165
|
+
const dropped = pane && pane.dropped > 0 ? ` (${pane.dropped} earlier lines dropped)` : "";
|
|
166
|
+
const header = pane ? `${pane.title} ${pane.subtitle}${dropped}` : "nothing open";
|
|
167
|
+
const activityWidth = Math.min(24, Math.max(0, columns - visibleWidth(header) - 3));
|
|
168
|
+
const activity = pane?.activity && pane.activity.length > 1 && activityWidth >= 8 ? ` ${waveLine(pane.activity, { width: activityWidth })}` : "";
|
|
169
|
+
rows.push({
|
|
170
|
+
text: pane ? `${header}${activity}` : header,
|
|
171
|
+
bold: true,
|
|
172
|
+
color: pane?.status === "failed" ? theme.error : pane?.status === "running" ? theme.success : theme.accent
|
|
173
|
+
});
|
|
174
|
+
const body = pane ? visibleLines(pane, height, snapshot.scroll) : [];
|
|
175
|
+
for (let index = 0;index < height; index += 1)
|
|
176
|
+
rows.push({ text: body[index] ?? "" });
|
|
177
|
+
while (rows.length < rowCount - 1)
|
|
178
|
+
rows.push({ text: "" });
|
|
179
|
+
rows.length = Math.max(0, rowCount - 1);
|
|
180
|
+
rows.push({
|
|
181
|
+
text: `${WORKSPACE_LEGEND}${scrolled ? ` ▲ scrolled back ${position}` : ""}`,
|
|
182
|
+
dim: true,
|
|
183
|
+
color: theme.textMuted
|
|
184
|
+
});
|
|
185
|
+
return rows.map((row) => ({ ...row, text: clip(row.text, columns) }));
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// packages/archymedes-cli/src/workspace-screen.tsx
|
|
189
|
+
import { jsxDEV } from "@termuijs/jsx/jsx-dev-runtime";
|
|
190
|
+
var Panel = Box;
|
|
191
|
+
var Line = Text;
|
|
192
|
+
function Workspace({ read, onExit, refreshMs = 200 }) {
|
|
193
|
+
const [snapshot, setSnapshot] = useState(() => read());
|
|
194
|
+
useInterval(() => {
|
|
195
|
+
setSnapshot((current) => ({ ...read(), selected: current.selected, scroll: current.scroll }));
|
|
196
|
+
}, refreshMs);
|
|
197
|
+
useInput((input, key) => {
|
|
198
|
+
const action = keyToAction({ name: key?.key, ctrl: key?.ctrl, shift: key?.shift }, input);
|
|
199
|
+
if (action.kind === "exit") {
|
|
200
|
+
onExit();
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
setSnapshot((current) => applyAction(current, action));
|
|
204
|
+
});
|
|
205
|
+
const rows = composeFrame(snapshot);
|
|
206
|
+
return /* @__PURE__ */ jsxDEV(Panel, {
|
|
207
|
+
flexDirection: "column",
|
|
208
|
+
width: snapshot.columns,
|
|
209
|
+
height: snapshot.rows,
|
|
210
|
+
children: rows.map((row, index) => /* @__PURE__ */ jsxDEV(Line, {
|
|
211
|
+
bold: row.bold,
|
|
212
|
+
dimColor: row.dim,
|
|
213
|
+
inverse: row.inverse,
|
|
214
|
+
color: row.color,
|
|
215
|
+
children: row.text
|
|
216
|
+
}, `row-${index}`, false, undefined, this))
|
|
217
|
+
}, undefined, false, undefined, this);
|
|
218
|
+
}
|
|
219
|
+
async function runWorkspace(host) {
|
|
220
|
+
const { renderApp } = await import("@termuijs/jsx");
|
|
221
|
+
await new Promise((resolve) => {
|
|
222
|
+
let settled = false;
|
|
223
|
+
const finish = () => {
|
|
224
|
+
if (settled)
|
|
225
|
+
return;
|
|
226
|
+
settled = true;
|
|
227
|
+
host.onExit?.();
|
|
228
|
+
resolve();
|
|
229
|
+
};
|
|
230
|
+
renderApp(Workspace, {
|
|
231
|
+
read: host.read,
|
|
232
|
+
onExit: finish,
|
|
233
|
+
fullscreen: true
|
|
234
|
+
}).catch(finish);
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
export {
|
|
238
|
+
runWorkspace,
|
|
239
|
+
Workspace
|
|
240
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "archymedes-cli",
|
|
3
|
+
"version": "2.0.0",
|
|
4
|
+
"description": "Archymedes CLI — a coding agent that shows its work. Runs against your working tree or an isolated remote sandbox, with guarded approvals, git checkpoints, and exact, measured cost accounting.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"cli",
|
|
7
|
+
"coding-agent",
|
|
8
|
+
"ai",
|
|
9
|
+
"terminal",
|
|
10
|
+
"agent",
|
|
11
|
+
"archymedes",
|
|
12
|
+
"anthropic",
|
|
13
|
+
"openai"
|
|
14
|
+
],
|
|
15
|
+
"license": "Apache-2.0",
|
|
16
|
+
"author": "chrisnkuno",
|
|
17
|
+
"repository": {
|
|
18
|
+
"type": "git",
|
|
19
|
+
"url": "git+https://github.com/chrisnkuno/archymedes-cli.git",
|
|
20
|
+
"directory": "packages/archymedes-cli"
|
|
21
|
+
},
|
|
22
|
+
"bugs": {
|
|
23
|
+
"url": "https://github.com/chrisnkuno/archymedes-cli/issues"
|
|
24
|
+
},
|
|
25
|
+
"homepage": "https://github.com/chrisnkuno/archymedes-cli/tree/main/packages/archymedes-cli#readme",
|
|
26
|
+
"type": "module",
|
|
27
|
+
"bin": {
|
|
28
|
+
"archymedes": "dist/archymedes.mjs"
|
|
29
|
+
},
|
|
30
|
+
"files": [
|
|
31
|
+
"dist",
|
|
32
|
+
"README.md",
|
|
33
|
+
"LICENSE",
|
|
34
|
+
"NOTICE",
|
|
35
|
+
"I18N.md"
|
|
36
|
+
],
|
|
37
|
+
"engines": {
|
|
38
|
+
"node": ">=22.5"
|
|
39
|
+
},
|
|
40
|
+
"publishConfig": {
|
|
41
|
+
"access": "public"
|
|
42
|
+
},
|
|
43
|
+
"scripts": {
|
|
44
|
+
"build": "bun run ../../tooling/build/build-packages.ts",
|
|
45
|
+
"prepublishOnly": "bun run ../../tooling/build/build-packages.ts"
|
|
46
|
+
},
|
|
47
|
+
"dependencies": {
|
|
48
|
+
"@termuijs/core": "0.1.7",
|
|
49
|
+
"@termuijs/jsx": "0.1.7",
|
|
50
|
+
"@termuijs/widgets": "0.1.7"
|
|
51
|
+
},
|
|
52
|
+
"optionalDependencies": {
|
|
53
|
+
"@archymedes/state-darwin-arm64": "0.4.0",
|
|
54
|
+
"@archymedes/state-darwin-x64": "0.4.0",
|
|
55
|
+
"@archymedes/state-linux-arm64-gnu": "0.4.0",
|
|
56
|
+
"@archymedes/state-linux-arm64-musl": "0.4.0",
|
|
57
|
+
"@archymedes/state-linux-x64-gnu": "0.4.0",
|
|
58
|
+
"@archymedes/state-linux-x64-musl": "0.4.0",
|
|
59
|
+
"@archymedes/state-win32-arm64": "0.4.0",
|
|
60
|
+
"@archymedes/state-win32-x64": "0.4.0"
|
|
61
|
+
}
|
|
62
|
+
}
|