archymedes-cli 2.1.0 → 2.3.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/README.md +25 -0
- package/dist/{archymedes-brkqgrxv.mjs → archymedes-4kx65anz.mjs} +47 -50
- package/dist/{archymedes-jecyxv5c.mjs → archymedes-a10k8yrk.mjs} +218 -3
- package/dist/{archymedes-76z6n643.mjs → archymedes-aj30app4.mjs} +160 -150
- package/dist/{archymedes-0jxzsh4z.mjs → archymedes-d5wqcmdr.mjs} +97 -97
- package/dist/archymedes-dz59nvw7.mjs +315 -0
- package/dist/{archymedes-dwvn4c51.mjs → archymedes-g8y3rt6t.mjs} +220 -200
- package/dist/{archymedes-1g1y6q74.mjs → archymedes-h13ap0za.mjs} +1 -1
- package/dist/archymedes-main.mjs +17634 -14542
- package/dist/{archymedes-k1bx08z9.mjs → archymedes-sf9t1hz9.mjs} +481 -50
- package/dist/{archymedes-44c1mqas.mjs → archymedes-tsne2m7p.mjs} +17 -44
- package/dist/{archymedes-2tp22t9a.mjs → archymedes-twbks7g3.mjs} +9 -7
- package/dist/{editor-screen-51z7z11j.mjs → editor-screen-g08zzt70.mjs} +263 -14
- package/dist/{factory-96yy6mxr.mjs → factory-7vtm2fed.mjs} +326 -326
- package/dist/{file-screen-eg28a3ky.mjs → file-screen-ma9yxq2b.mjs} +9 -9
- package/dist/guide-screen-gd129txd.mjs +271 -0
- package/dist/{index-teq1cta4.mjs → index-qvgcwsvm.mjs} +1 -1
- package/dist/{node-0fh63p8z.mjs → node-mdgzx68h.mjs} +7 -7
- package/dist/{sandbox-templates-8sd1psez.mjs → sandbox-templates-9jcjws23.mjs} +2 -2
- package/dist/{workspace-screen-3zztn4by.mjs → workspace-screen-8cwk6jz9.mjs} +10 -9
- package/package.json +2 -2
- package/dist/guide-screen-mfvwkdhb.mjs +0 -565
|
@@ -1,565 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
applyViewport,
|
|
3
|
-
newViewport,
|
|
4
|
-
visibleLines
|
|
5
|
-
} from "./archymedes-k1bx08z9.mjs";
|
|
6
|
-
import"./archymedes-44c1mqas.mjs";
|
|
7
|
-
import {
|
|
8
|
-
joinHorizontal,
|
|
9
|
-
padToWidth,
|
|
10
|
-
scrollIndicator,
|
|
11
|
-
scrollPercent,
|
|
12
|
-
sliceToWidth,
|
|
13
|
-
visibleWidth
|
|
14
|
-
} from "./archymedes-dwvn4c51.mjs";
|
|
15
|
-
import {
|
|
16
|
-
NO_COLOR_PALETTE
|
|
17
|
-
} from "./archymedes-jecyxv5c.mjs";
|
|
18
|
-
import {
|
|
19
|
-
__require
|
|
20
|
-
} from "./archymedes-x67v3n58.mjs";
|
|
21
|
-
|
|
22
|
-
// packages/archymedes-cli/src/guide-screen.tsx
|
|
23
|
-
import { useInput, useState } from "@termuijs/jsx";
|
|
24
|
-
import { Box, Text } from "@termuijs/widgets";
|
|
25
|
-
|
|
26
|
-
// packages/archymedes-cli/src/guide.ts
|
|
27
|
-
var GUIDE_TOPICS = [
|
|
28
|
-
{
|
|
29
|
-
id: "start",
|
|
30
|
-
title: "Getting started",
|
|
31
|
-
summary: "What Archymedes is, and the first thing to type.",
|
|
32
|
-
body: [
|
|
33
|
-
"Archymedes is a coding agent that works in a scrolling terminal transcript. You type what you want in plain language; it reads files, runs commands and edits code, and shows you what it did as it goes.",
|
|
34
|
-
"Everything it prints stays in your terminal's scrollback, so you can scroll up, select text and pipe a session to a file the way you would with any other command.",
|
|
35
|
-
"Type a request and press Enter. Slash commands do everything else — press / to see them, or open the searchable list."
|
|
36
|
-
],
|
|
37
|
-
examples: [
|
|
38
|
-
{ input: "fix the failing test in cart.ts", effect: "an ordinary request; Archymedes reads, edits and runs the tests" },
|
|
39
|
-
{ input: "/help", effect: "every command, with one line each" },
|
|
40
|
-
{ input: "/palette", effect: "search commands by name or by what they do" },
|
|
41
|
-
{ input: "/guide", effect: "open this guide as a screen — arrows to move, / to search, q to leave" },
|
|
42
|
-
{ input: "/guide tabs", effect: "print one topic into the transcript, to keep or to copy" },
|
|
43
|
-
{ input: "/exit", effect: "leave; the session is saved and can be resumed" }
|
|
44
|
-
],
|
|
45
|
-
covers: ["/help", "/palette", "/exit", "/keys", "/guide"]
|
|
46
|
-
},
|
|
47
|
-
{
|
|
48
|
-
id: "modes",
|
|
49
|
-
title: "Permission modes",
|
|
50
|
-
summary: "How much Archymedes may do without asking.",
|
|
51
|
-
body: [
|
|
52
|
-
"Four modes, and you can change them at any time. Plan reads and reasons but cannot write or run commands. Build is the default: edits are proposed and you approve them. Auto applies ordinary workspace edits without asking, while anything sensitive or reaching outside the workspace still stops for you. Defender turns Archymedes into a security reviewer — full tool access to actually scan and fix, but never auto-approved, the same posture as Build.",
|
|
53
|
-
"Defender also looks outward: when web search is configured it checks current advisories against what the project actually depends on, remembers durable findings in project memory so the next review builds on this one, and closes out with concrete, costed remediation resources fitted to how the project actually hosts and deploys — not a generic checklist.",
|
|
54
|
-
"/scan runs the deterministic half of defender's own secrets playbook directly — no model turn, no mode switch needed. It is the same pattern-matching scan_secrets uses, worst severity first: a private key or a live cloud credential ahead of a merely credential-shaped variable name.",
|
|
55
|
-
"The mode is per tab, so one piece of work can be on a short leash while another runs freely."
|
|
56
|
-
],
|
|
57
|
-
examples: [
|
|
58
|
-
{ input: "/plan", effect: "read-only: no writes, no commands" },
|
|
59
|
-
{ input: "/build", effect: "edits ask first (the default)" },
|
|
60
|
-
{ input: "/auto", effect: "ordinary edits apply; sensitive actions still ask" },
|
|
61
|
-
{ input: "/defender", effect: "security review — find and fix real issues; every change still asks" },
|
|
62
|
-
{ input: "/mode", effect: "show which mode you are in" },
|
|
63
|
-
{ input: "/scan", effect: "pattern-based secret scan, worst severity first, no model call" },
|
|
64
|
-
{ input: "/scan src/**", effect: "same scan, limited to files matching the glob" }
|
|
65
|
-
],
|
|
66
|
-
covers: ["/mode", "/plan", "/build", "/auto", "/defender", "/scan"]
|
|
67
|
-
},
|
|
68
|
-
{
|
|
69
|
-
id: "tabs",
|
|
70
|
-
title: "Tabs: several pieces of work, one at a time",
|
|
71
|
-
summary: "Each tab keeps its own conversation, model, cost and machine — the tab in front is the one that runs.",
|
|
72
|
-
body: [
|
|
73
|
-
"A tab is a separate piece of work with its own conversation, its own running total, its own mode — and its own model and location. One tab can be on a large model against your checkout while another runs a cheaper or open model inside a throwaway remote sandbox.",
|
|
74
|
-
"Only the tab in front runs. Switching away does not leave a turn working in the background — it pauses that piece of work and hands the terminal to another one. That is a deliberate limit rather than a missing feature: a terminal transcript has one bottom, and two agents printing into it at once would interleave into something neither of them said. (Archymedes's desktop window is different — it has a transcript per tab, so its tabs really do run at the same time.)",
|
|
75
|
-
"When you want work running while you do something else, that is what /detach and /jobs are for: a detached job keeps going after you close the terminal, and /watch streams it into the session without taking the prompt.",
|
|
76
|
-
"Tabs keep what they printed. Leave one and come back and the last lines reappear, so you can pick up where you were instead of facing a bare prompt.",
|
|
77
|
-
"The strip above the prompt shows every tab with its model and a mark for where it runs. Closing a tab that started its own sandbox stops that sandbox — and only that one."
|
|
78
|
-
],
|
|
79
|
-
examples: [
|
|
80
|
-
{ input: "/tab new review", effect: "a second tab, same model, same machine" },
|
|
81
|
-
{ input: "/tab new fast --model claude-haiku-4-5-20251001", effect: "a tab on a different model" },
|
|
82
|
-
{ input: "/tab new risky --sandbox e2b", effect: "a tab whose edits land in a remote sandbox, not on your disk" },
|
|
83
|
-
{ input: "/tab 2", effect: "switch to tab 2 and replay where you left off" },
|
|
84
|
-
{ input: "/tab close 2", effect: "close it, stopping its sandbox if it started one" },
|
|
85
|
-
{ input: "/detach run the full test suite", effect: "work that runs while you use another tab" }
|
|
86
|
-
],
|
|
87
|
-
covers: ["/tab"]
|
|
88
|
-
},
|
|
89
|
-
{
|
|
90
|
-
id: "models",
|
|
91
|
-
title: "Models and providers",
|
|
92
|
-
summary: "Choosing what runs, and what it costs.",
|
|
93
|
-
body: [
|
|
94
|
-
"Archymedes talks to several providers. The picker lists what you can use, with prices, and offers to save a key for anything you cannot use yet. Switching model keeps the conversation — you are changing who answers, not starting over.",
|
|
95
|
-
"The list is the union of what this build knows the prices of and what each provider says it has today, so a model released last week is still offerable — it is simply shown without a price rather than hidden. The live half is cached for six hours; /models refresh asks again.",
|
|
96
|
-
"A model switch applies to the tab you are in. Opening a tab with --model is how you compare two models on the same problem."
|
|
97
|
-
],
|
|
98
|
-
examples: [
|
|
99
|
-
{ input: "/models", effect: "the list, with prices" },
|
|
100
|
-
{ input: "/models refresh", effect: "ask every provider what it has right now" },
|
|
101
|
-
{ input: "/model", effect: "the picker" },
|
|
102
|
-
{ input: "/model claude-sonnet-5", effect: "switch straight to one, keeping the transcript" },
|
|
103
|
-
{ input: "/fallback openai:gpt-5.4-mini", effect: "retry transient provider failures on this model, only before any output or tool action" },
|
|
104
|
-
{ input: "/providers", effect: "which providers are configured" }
|
|
105
|
-
],
|
|
106
|
-
covers: ["/models", "/model", "/fallback", "/providers", "/settings"]
|
|
107
|
-
},
|
|
108
|
-
{
|
|
109
|
-
id: "where",
|
|
110
|
-
title: "Where the work happens",
|
|
111
|
-
summary: "This machine, a container, or a remote sandbox.",
|
|
112
|
-
body: [
|
|
113
|
-
"By default Archymedes edits the directory you started it in. Started with --sandbox e2b it works inside a throwaway remote machine instead, and --sandbox docker uses a local container. Files written there never touch your disk unless you ask for them.",
|
|
114
|
-
"Sandboxes are per tab, so 'try this somewhere I don't mind breaking' is one command rather than a second terminal.",
|
|
115
|
-
"One thing worth knowing: background jobs always run on your machine, even when the session that started them is sandboxed. Archymedes says so each time you start one."
|
|
116
|
-
],
|
|
117
|
-
examples: [
|
|
118
|
-
{ input: "archymedes --sandbox e2b", effect: "the whole session runs in a remote sandbox" },
|
|
119
|
-
{ input: "/tab new spike --sandbox docker", effect: "one tab in a local container" },
|
|
120
|
-
{ input: "/where", effect: "which machine the current tab is using" },
|
|
121
|
-
{ input: "/pull", effect: "copy files out of a sandbox and onto your disk" }
|
|
122
|
-
],
|
|
123
|
-
covers: ["/where", "/pull"]
|
|
124
|
-
},
|
|
125
|
-
{
|
|
126
|
-
id: "reading",
|
|
127
|
-
title: "Reading what Archymedes did",
|
|
128
|
-
summary: "Code, diffs, test results, and folded detail.",
|
|
129
|
-
body: [
|
|
130
|
-
"When Archymedes writes or edits a file it shows the code, numbered, under the tool line. When it runs tests it re-lays the output into sections with the failures pulled out. When output is long it is folded rather than truncated, and the whole of it is one command away.",
|
|
131
|
-
"The diff shows the actual patch since the last checkpoint — the lines, not a count of them."
|
|
132
|
-
],
|
|
133
|
-
examples: [
|
|
134
|
-
{ input: "/diff", effect: "the real patch, file by file" },
|
|
135
|
-
{ input: "/diff stat", effect: "just the summary of how much changed" },
|
|
136
|
-
{ input: "/expand", effect: "unfold the last folded block" },
|
|
137
|
-
{ input: "/expand list", effect: "everything that has been folded this session" },
|
|
138
|
-
{ input: "/undo", effect: "revert the last turn — files, conversation, or both" },
|
|
139
|
-
{ input: "/retry", effect: "repeat a failed request when nothing had run or changed" },
|
|
140
|
-
{ input: "/continue", effect: "finish an interrupted task without repeating completed work" },
|
|
141
|
-
{ input: "/todos", effect: "the agent's current plan" },
|
|
142
|
-
{ input: "/task", effect: "the whole task at a glance: request, plan, changes, verification, blockers" },
|
|
143
|
-
{ input: "/route", effect: "the hosted exchange's routing decision for the last turn — chosen model, alternatives, cost vs estimate" }
|
|
144
|
-
],
|
|
145
|
-
covers: ["/diff", "/expand", "/undo", "/retry", "/continue", "/todos", "/task", "/route"]
|
|
146
|
-
},
|
|
147
|
-
{
|
|
148
|
-
id: "memory",
|
|
149
|
-
title: "Memory",
|
|
150
|
-
summary: "Facts worth keeping between sessions.",
|
|
151
|
-
body: [
|
|
152
|
-
"Telling Archymedes the same thing every session costs you tokens and patience. A remembered fact is prepended to the conversation instead.",
|
|
153
|
-
"Memory is a plain markdown file you can open, edit, commit or delete. Project memory belongs to the repository; personal memory follows you across projects. Nothing is remembered unless you say so."
|
|
154
|
-
],
|
|
155
|
-
examples: [
|
|
156
|
-
{ input: "# we use bun, not npm", effect: "remember a fact about this project" },
|
|
157
|
-
{ input: "/memory", effect: "list what is remembered" },
|
|
158
|
-
{ input: "/memory add --user I prefer terse explanations", effect: "remember something about you, everywhere" },
|
|
159
|
-
{ input: "/memory forget 2", effect: "drop one" }
|
|
160
|
-
],
|
|
161
|
-
covers: ["/memory"]
|
|
162
|
-
},
|
|
163
|
-
{
|
|
164
|
-
id: "history",
|
|
165
|
-
title: "History",
|
|
166
|
-
summary: "Past conversations, searchable and resumable.",
|
|
167
|
-
body: [
|
|
168
|
-
"Every session is saved. You can list them, search the ones you half-remember, read one back, or pick one up and keep going with its whole context intact."
|
|
169
|
-
],
|
|
170
|
-
examples: [
|
|
171
|
-
{ input: "/history", effect: "recent sessions, newest first" },
|
|
172
|
-
{ input: "/history search failing tests", effect: "find one by what you typed" },
|
|
173
|
-
{ input: "/history resume", effect: "pick up where a past session left off" },
|
|
174
|
-
{ input: "/export support", effect: "write a compact redacted artifact safe to attach to a support request" },
|
|
175
|
-
{ input: "/clear", effect: "start a fresh thread in this tab" }
|
|
176
|
-
],
|
|
177
|
-
covers: ["/history", "/sessions", "/export", "/clear"]
|
|
178
|
-
},
|
|
179
|
-
{
|
|
180
|
-
id: "cost",
|
|
181
|
-
title: "Cost and pace",
|
|
182
|
-
summary: "Knowing what you are spending, and spending it slower.",
|
|
183
|
-
body: [
|
|
184
|
-
"Every turn is priced in your own currency, and the running total is always on screen. Start with --budget N to approve a cap that is then enforced rather than merely reported.",
|
|
185
|
-
"Slow mode is the other half: it limits how many model rounds and tool calls a turn may use, shortens replies, and pauses between turns. It caps the rate, not the total, and asks before an unusually expensive turn."
|
|
186
|
-
],
|
|
187
|
-
examples: [
|
|
188
|
-
{ input: "/cost", effect: "the breakdown for this session" },
|
|
189
|
-
{ input: "archymedes --budget 20", effect: "approve and enforce a cap" },
|
|
190
|
-
{ input: "/slow", effect: "spend at a gentler pace" },
|
|
191
|
-
{ input: "/slow strict", effect: "slower still, with a pause between turns" },
|
|
192
|
-
{ input: "/slow off", effect: "back to full speed" }
|
|
193
|
-
],
|
|
194
|
-
covers: ["/cost", "/slow"]
|
|
195
|
-
},
|
|
196
|
-
{
|
|
197
|
-
id: "balance",
|
|
198
|
-
title: "Tracking a spend balance",
|
|
199
|
-
summary: "A local budget the prompt draws down as you work.",
|
|
200
|
-
body: [
|
|
201
|
-
"Archymedes has no hosted wallet. /balance sets a figure — the amount you have decided this project can spend — and after every completed turn Archymedes subtracts that turn's measured cost from it. It is an estimate you own, not a provider statement.",
|
|
202
|
-
"The balance is kept in whatever currency your session shows costs in. /balance 5000 uses that currency; /balance 50 usd or /balance 5000 inr sets it explicitly. /balance on its own shows what remains and what this session has used so far. /balance clear stops tracking.",
|
|
203
|
-
"Once a balance is being tracked, Archymedes warns calmly when it gets low, drops below the critical threshold, or falls unusually fast between turns. Before a demanding task it compares its token-based estimate with the balance and stops before contacting the model when even the conservative estimate cannot fit.",
|
|
204
|
-
"The thresholds default to small USD amounts converted to your currency; ARCHYMEDES_LOW_BALANCE and ARCHYMEDES_CRITICAL_BALANCE override them in /settings."
|
|
205
|
-
],
|
|
206
|
-
examples: [
|
|
207
|
-
{ input: "/balance 5000", effect: "track a 5,000 balance in the display currency" },
|
|
208
|
-
{ input: "/balance 50 usd", effect: "track a balance in a specific currency" },
|
|
209
|
-
{ input: "/balance", effect: "what remains, and what this session has used" },
|
|
210
|
-
{ input: "/balance clear", effect: "stop tracking a balance" }
|
|
211
|
-
],
|
|
212
|
-
covers: ["/balance", "/cost"]
|
|
213
|
-
},
|
|
214
|
-
{
|
|
215
|
-
id: "updating",
|
|
216
|
-
title: "Keeping Archymedes current",
|
|
217
|
-
summary: "Automatic updates by default, with explicit check-only and off controls.",
|
|
218
|
-
body: [
|
|
219
|
-
"Archymedes looks for a new version once a day, at the moment a session starts and never in the middle of one. The check is bounded at three seconds and its failure is silent: an unreachable registry costs one attempt a day, not a slow prompt.",
|
|
220
|
-
"By default Archymedes installs a newer release automatically through whichever package manager installed it; it does not wait for another acceptance prompt. /update check changes that to notification-only behavior, and /update off stops it looking at all.",
|
|
221
|
-
"Automatic installs never happen in a piped or headless run, or in CI, because automation may depend on an exact installed version. A version that fails to install is remembered and not retried, because an update mechanism that fails loudly every launch is one people uninstall.",
|
|
222
|
-
"An install replaces the program on disk; the session you are in keeps running the version it started with until you restart it."
|
|
223
|
-
],
|
|
224
|
-
examples: [
|
|
225
|
-
{ input: "/update", effect: "install the newest version now" },
|
|
226
|
-
{ input: "/update check", effect: "tell you about updates without installing" },
|
|
227
|
-
{ input: "/update off", effect: "stop looking for updates" }
|
|
228
|
-
],
|
|
229
|
-
covers: ["/update"]
|
|
230
|
-
},
|
|
231
|
-
{
|
|
232
|
-
id: "background",
|
|
233
|
-
title: "Background work",
|
|
234
|
-
summary: "Jobs that outlive the prompt, and research that runs itself.",
|
|
235
|
-
body: [
|
|
236
|
-
"Work can be sent to the background and keeps running after you close the terminal. A watched job streams its output into the session without taking the prompt, so you can read it when you want to and carry on meanwhile.",
|
|
237
|
-
"This — not tabs — is how two things run at once in the terminal. A tab you switch away from is paused; a detached job keeps working.",
|
|
238
|
-
"Wander runs a bounded research lab on a topic, gathers evidence first, and grades its own claims. It can run once or on a schedule.",
|
|
239
|
-
"A job that needs approval says so and waits — attach to it to answer."
|
|
240
|
-
],
|
|
241
|
-
examples: [
|
|
242
|
-
{ input: "/detach update the changelog", effect: "start it in the background" },
|
|
243
|
-
{ input: "/jobs", effect: "what is running, queued or finished" },
|
|
244
|
-
{ input: "/watch 20260814T0132Z-ab12cd", effect: "follow its output without giving up the prompt" },
|
|
245
|
-
{ input: "/watch show <id>", effect: "read what it has said so far" },
|
|
246
|
-
{ input: "/attach <id>", effect: "take the prompt to answer an approval" },
|
|
247
|
-
{ input: "/wander daily coral reefs", effect: "a research lab, every day" }
|
|
248
|
-
],
|
|
249
|
-
covers: ["/detach", "/jobs", "/attach", "/watch", "/wander"]
|
|
250
|
-
},
|
|
251
|
-
{
|
|
252
|
-
id: "panel",
|
|
253
|
-
title: "The control panel",
|
|
254
|
-
summary: "Every tab and job, live, on one screen.",
|
|
255
|
-
body: [
|
|
256
|
-
"The workspace is a full-screen view of everything at once: each tab as a pane showing its live output, what model it is running and where, and each watched job alongside them.",
|
|
257
|
-
"It is a view — it reads the session and changes nothing — so leaving it puts you back exactly where you were.",
|
|
258
|
-
"Inside it: 1-9 select a pane, arrows or Tab move between them, arrows or j/k scroll, g and G jump to the ends, q leaves."
|
|
259
|
-
],
|
|
260
|
-
examples: [
|
|
261
|
-
{ input: "/workspace", effect: "open the panel" },
|
|
262
|
-
{ input: "q", effect: "leave it and return to the prompt" }
|
|
263
|
-
],
|
|
264
|
-
covers: ["/workspace"]
|
|
265
|
-
},
|
|
266
|
-
{
|
|
267
|
-
id: "look",
|
|
268
|
-
title: "How it looks",
|
|
269
|
-
summary: "Themes, symbols, and terminals that need help.",
|
|
270
|
-
body: [
|
|
271
|
-
"Archymedes ships a blueprint theme and three others, and reads themes you write yourself as .tss files in .archymedes/themes — the same format TermUI apps use, so a palette written once works in both.",
|
|
272
|
-
"If your terminal draws question marks instead of symbols, --ascii switches to characters every terminal has. If you would rather have a status line pinned to the bottom row, --pin does that, at the cost of your terminal's scrollback."
|
|
273
|
-
],
|
|
274
|
-
examples: [
|
|
275
|
-
{ input: "/theme list", effect: "every theme available, including your own" },
|
|
276
|
-
{ input: "/theme chalkboard", effect: "change the colours immediately" },
|
|
277
|
-
{ input: "/theme where", effect: "where to put a theme file of your own" },
|
|
278
|
-
{ input: "archymedes --ascii", effect: "plain characters for terminals that mangle symbols" }
|
|
279
|
-
],
|
|
280
|
-
covers: ["/theme"]
|
|
281
|
-
},
|
|
282
|
-
{
|
|
283
|
-
id: "extend",
|
|
284
|
-
title: "Tools and voice",
|
|
285
|
-
summary: "What the agent can call, and talking to it.",
|
|
286
|
-
body: [
|
|
287
|
-
"Archymedes has a fixed set of built-in tools, and a project can add more through skills, plugins, MCP servers and hooks. The tools command shows exactly what is available in this project.",
|
|
288
|
-
"Voice input records or transcribes a prompt you can edit before sending.",
|
|
289
|
-
"The file picker is the project as a tree rather than a flat completion list: expand a folder, preview a file's contents, and pick one to drop an @mention into the line you're writing.",
|
|
290
|
-
"`/edit <path>` opens a file in the built-in editor. It reads and writes through the workspace like every tool does, so it edits the sandbox's copy in a sandboxed session rather than a same-named file on this machine, and it only writes when you save — quitting really is a discard."
|
|
291
|
-
],
|
|
292
|
-
examples: [
|
|
293
|
-
{ input: "/tools", effect: "every tool, skill, plugin, MCP server and hook" },
|
|
294
|
-
{ input: "/voice", effect: "record a prompt, then edit it before sending" },
|
|
295
|
-
{ input: "/files", effect: "browse the project tree and @mention a file" },
|
|
296
|
-
{ input: "/edit src/app.ts", effect: "open a file in the built-in editor" }
|
|
297
|
-
],
|
|
298
|
-
covers: ["/tools", "/voice", "/files", "/edit"]
|
|
299
|
-
}
|
|
300
|
-
];
|
|
301
|
-
function wrapText(text, width) {
|
|
302
|
-
const measure = Math.max(1, width);
|
|
303
|
-
const lines = [];
|
|
304
|
-
let current = "";
|
|
305
|
-
for (const word of text.split(/\s+/).filter(Boolean)) {
|
|
306
|
-
if (current === "") {
|
|
307
|
-
current = word;
|
|
308
|
-
continue;
|
|
309
|
-
}
|
|
310
|
-
if (visibleWidth(current) + 1 + visibleWidth(word) <= measure)
|
|
311
|
-
current = `${current} ${word}`;
|
|
312
|
-
else {
|
|
313
|
-
lines.push(current);
|
|
314
|
-
current = word;
|
|
315
|
-
}
|
|
316
|
-
}
|
|
317
|
-
if (current !== "")
|
|
318
|
-
lines.push(current);
|
|
319
|
-
return lines;
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
// packages/archymedes-cli/src/guide-browser.ts
|
|
323
|
-
function initialGuideState(columns, rows, palette = NO_COLOR_PALETTE) {
|
|
324
|
-
return { topics: GUIDE_TOPICS, selected: 0, scroll: 0, query: "", searching: false, columns, rows, palette };
|
|
325
|
-
}
|
|
326
|
-
function visibleTopics(state) {
|
|
327
|
-
const needle = state.query.trim().toLowerCase();
|
|
328
|
-
if (!needle)
|
|
329
|
-
return [...state.topics];
|
|
330
|
-
return state.topics.filter((topic) => [topic.title, topic.summary, topic.id, ...topic.body, ...(topic.examples ?? []).flatMap((example) => [example.input, example.effect])].some((text) => text.toLowerCase().includes(needle)));
|
|
331
|
-
}
|
|
332
|
-
function currentTopic(state) {
|
|
333
|
-
const topics = visibleTopics(state);
|
|
334
|
-
if (topics.length === 0)
|
|
335
|
-
return;
|
|
336
|
-
return topics[Math.max(0, Math.min(state.selected, topics.length - 1))];
|
|
337
|
-
}
|
|
338
|
-
function sidebarWidth(columns) {
|
|
339
|
-
const width = Math.max(1, Math.floor(columns));
|
|
340
|
-
return Math.min(width, Math.max(14, Math.min(26, Math.floor(width * 0.28))));
|
|
341
|
-
}
|
|
342
|
-
function bodyHeight(rows) {
|
|
343
|
-
return Math.max(0, Math.floor(rows) - 2);
|
|
344
|
-
}
|
|
345
|
-
function keyToGuideAction(key, character, searching) {
|
|
346
|
-
const name = key.name ?? "";
|
|
347
|
-
if (key.ctrl && name === "c")
|
|
348
|
-
return { kind: "exit" };
|
|
349
|
-
const isEnter = name === "return" || name === "enter" || name === "\r" || name === `
|
|
350
|
-
`;
|
|
351
|
-
const isEscape = name === "escape" || name === "\x1B";
|
|
352
|
-
if (searching) {
|
|
353
|
-
if (isEscape || isEnter)
|
|
354
|
-
return { kind: "commit" };
|
|
355
|
-
if (name === "backspace")
|
|
356
|
-
return { kind: "backspace" };
|
|
357
|
-
if (name === "up")
|
|
358
|
-
return { kind: "move", step: -1 };
|
|
359
|
-
if (name === "down")
|
|
360
|
-
return { kind: "move", step: 1 };
|
|
361
|
-
if (character && character.length === 1 && character >= " ")
|
|
362
|
-
return { kind: "type", character };
|
|
363
|
-
return { kind: "none" };
|
|
364
|
-
}
|
|
365
|
-
if (isEscape || name === "q")
|
|
366
|
-
return { kind: "exit" };
|
|
367
|
-
if (character === "/" || name === "f3")
|
|
368
|
-
return { kind: "search" };
|
|
369
|
-
if (name === "up" || name === "k")
|
|
370
|
-
return { kind: "move", step: -1 };
|
|
371
|
-
if (name === "down" || name === "j")
|
|
372
|
-
return { kind: "move", step: 1 };
|
|
373
|
-
if (name === "tab")
|
|
374
|
-
return { kind: "move", step: key.shift ? -1 : 1 };
|
|
375
|
-
if (name === "pagedown" || character === " ")
|
|
376
|
-
return { kind: "page", step: 1 };
|
|
377
|
-
if (name === "pageup")
|
|
378
|
-
return { kind: "page", step: -1 };
|
|
379
|
-
if (name === "right")
|
|
380
|
-
return { kind: "scroll", rows: 1 };
|
|
381
|
-
if (name === "left")
|
|
382
|
-
return { kind: "scroll", rows: -1 };
|
|
383
|
-
return { kind: "none" };
|
|
384
|
-
}
|
|
385
|
-
function applyGuideAction(state, action) {
|
|
386
|
-
const topics = visibleTopics(state);
|
|
387
|
-
switch (action.kind) {
|
|
388
|
-
case "move": {
|
|
389
|
-
if (topics.length === 0)
|
|
390
|
-
return state;
|
|
391
|
-
const next = ((state.selected + action.step) % topics.length + topics.length) % topics.length;
|
|
392
|
-
return { ...state, selected: next, scroll: 0 };
|
|
393
|
-
}
|
|
394
|
-
case "scroll":
|
|
395
|
-
case "page": {
|
|
396
|
-
const topic = currentTopic(state);
|
|
397
|
-
if (!topic)
|
|
398
|
-
return state;
|
|
399
|
-
const lines = topicLines(topic, bodyWidth(state.columns)).map((line) => line.text);
|
|
400
|
-
const viewport = { ...newViewport(lines, bodyHeight(state.rows)), top: state.scroll };
|
|
401
|
-
const moved = action.kind === "page" ? applyViewport(viewport, { kind: action.step === 1 ? "pageDown" : "pageUp" }) : applyViewport(viewport, action.rows > 0 ? { kind: "down", rows: action.rows } : { kind: "up", rows: -action.rows });
|
|
402
|
-
return { ...state, scroll: moved.top };
|
|
403
|
-
}
|
|
404
|
-
case "search":
|
|
405
|
-
return { ...state, searching: true };
|
|
406
|
-
case "commit":
|
|
407
|
-
return { ...state, searching: false };
|
|
408
|
-
case "backspace":
|
|
409
|
-
return { ...state, query: state.query.slice(0, -1), selected: 0, scroll: 0 };
|
|
410
|
-
case "type":
|
|
411
|
-
return { ...state, query: state.query + action.character, selected: 0, scroll: 0 };
|
|
412
|
-
default:
|
|
413
|
-
return state;
|
|
414
|
-
}
|
|
415
|
-
}
|
|
416
|
-
function bodyWidth(columns) {
|
|
417
|
-
const width = Math.max(1, Math.floor(columns));
|
|
418
|
-
return width < 40 ? width : Math.max(1, width - sidebarWidth(width) - 3);
|
|
419
|
-
}
|
|
420
|
-
function topicLines(topic, width) {
|
|
421
|
-
const measure = Math.max(1, Math.floor(width));
|
|
422
|
-
const wrap = (text, available = measure) => wrapText(text, Math.max(1, available)).flatMap((line) => hardWrap(line, Math.max(1, available)));
|
|
423
|
-
const rows = [
|
|
424
|
-
...wrap(topic.title).map((line) => ({ text: line, bold: true })),
|
|
425
|
-
{ text: "" }
|
|
426
|
-
];
|
|
427
|
-
for (const paragraph of topic.body) {
|
|
428
|
-
for (const line of wrap(paragraph))
|
|
429
|
-
rows.push({ text: line });
|
|
430
|
-
rows.push({ text: "" });
|
|
431
|
-
}
|
|
432
|
-
if (topic.examples && topic.examples.length > 0) {
|
|
433
|
-
rows.push({ text: "try", bold: true });
|
|
434
|
-
for (const example of topic.examples) {
|
|
435
|
-
const commandIndent = " ".repeat(Math.min(2, Math.max(0, measure - 1)));
|
|
436
|
-
const detailIndent = " ".repeat(Math.min(4, Math.max(0, measure - 1)));
|
|
437
|
-
const [head, ...rest] = wrap(example.input, measure - visibleWidth(detailIndent));
|
|
438
|
-
rows.push({ text: `${commandIndent}${head ?? ""}`, bold: true });
|
|
439
|
-
for (const line of rest)
|
|
440
|
-
rows.push({ text: `${detailIndent}${line}`, bold: true });
|
|
441
|
-
for (const line of wrap(example.effect, measure - visibleWidth(detailIndent)))
|
|
442
|
-
rows.push({ text: `${detailIndent}${line}`, dim: true });
|
|
443
|
-
}
|
|
444
|
-
}
|
|
445
|
-
return rows;
|
|
446
|
-
}
|
|
447
|
-
function hardWrap(text, width) {
|
|
448
|
-
if (visibleWidth(text) <= width)
|
|
449
|
-
return [text];
|
|
450
|
-
const lines = [];
|
|
451
|
-
let line = "";
|
|
452
|
-
const segments = typeof Intl.Segmenter === "function" ? [...new Intl.Segmenter(undefined, { granularity: "grapheme" }).segment(text)].map((entry) => entry.segment) : [...text];
|
|
453
|
-
for (const segment of segments) {
|
|
454
|
-
if (line && visibleWidth(line + segment) > width) {
|
|
455
|
-
lines.push(line);
|
|
456
|
-
line = "";
|
|
457
|
-
}
|
|
458
|
-
if (visibleWidth(segment) > width) {
|
|
459
|
-
lines.push("…");
|
|
460
|
-
continue;
|
|
461
|
-
}
|
|
462
|
-
line += segment;
|
|
463
|
-
}
|
|
464
|
-
if (line)
|
|
465
|
-
lines.push(line);
|
|
466
|
-
return lines;
|
|
467
|
-
}
|
|
468
|
-
function composeGuideFrame(state) {
|
|
469
|
-
const columns = Math.max(1, Math.floor(state.columns));
|
|
470
|
-
const rowCount = Math.max(1, Math.floor(state.rows));
|
|
471
|
-
const compact = columns < 40;
|
|
472
|
-
const sidebar = compact ? 0 : sidebarWidth(columns);
|
|
473
|
-
const body = bodyWidth(columns);
|
|
474
|
-
const height = bodyHeight(rowCount);
|
|
475
|
-
const topics = visibleTopics(state);
|
|
476
|
-
const selectedIndex = topics.length === 0 ? -1 : Math.max(0, Math.min(state.selected, topics.length - 1));
|
|
477
|
-
const topic = selectedIndex === -1 ? undefined : topics[selectedIndex];
|
|
478
|
-
const theme = state.palette.tokens;
|
|
479
|
-
const rows = [];
|
|
480
|
-
rows.push({
|
|
481
|
-
text: padToWidth(compact ? ` archymedes guide · ${topic?.title ?? "no matches"}` : " archymedes guide", columns),
|
|
482
|
-
bold: true,
|
|
483
|
-
color: theme.primary
|
|
484
|
-
});
|
|
485
|
-
if (rowCount === 1)
|
|
486
|
-
return rows;
|
|
487
|
-
const lines = topic ? topicLines(topic, body) : [{ text: "Nothing matches that." }];
|
|
488
|
-
const indices = visibleLines({ ...newViewport(lines.map((_, index) => `${index}`), height), top: state.scroll });
|
|
489
|
-
const start = indices.length > 0 ? Number(indices[0]) : 0;
|
|
490
|
-
const window = lines.slice(start, start + height);
|
|
491
|
-
const listStart = Math.max(0, Math.min(selectedIndex - Math.floor(height / 2), Math.max(0, topics.length - height)));
|
|
492
|
-
for (let offset = 0;offset < height; offset += 1) {
|
|
493
|
-
const listed = topics[listStart + offset];
|
|
494
|
-
const chosen = listed !== undefined && listStart + offset === selectedIndex;
|
|
495
|
-
const cell = listed ? `${chosen ? "›" : " "} ${listed.title}` : "";
|
|
496
|
-
const line = window[offset];
|
|
497
|
-
rows.push({
|
|
498
|
-
text: compact ? padToWidth(line?.text ?? "", columns) : joinHorizontal(sliceToWidth(cell, sidebar), line?.text ?? "", { leftWidth: sidebar, rightWidth: body, separator: " │ " }),
|
|
499
|
-
bold: chosen || line?.bold,
|
|
500
|
-
dim: line?.dim && !chosen,
|
|
501
|
-
...chosen ? { color: theme.accent } : line?.bold ? { color: theme.primary } : {}
|
|
502
|
-
});
|
|
503
|
-
}
|
|
504
|
-
const position = lines.length > height ? ` ${scrollIndicator(scrollPercent(start, lines.length, height))}` : "";
|
|
505
|
-
const footer = state.searching ? `search: ${state.query}▏ Enter done · Esc done` : `↑↓ topic · ←→ scroll · space page · / search · q leave${state.query ? ` filter: ${state.query}` : ""}${position}`;
|
|
506
|
-
rows.push({ text: padToWidth(` ${footer}`, columns), dim: true, color: theme.textMuted });
|
|
507
|
-
return rows;
|
|
508
|
-
}
|
|
509
|
-
|
|
510
|
-
// packages/archymedes-cli/src/guide-screen.tsx
|
|
511
|
-
import { jsxDEV } from "@termuijs/jsx/jsx-dev-runtime";
|
|
512
|
-
var Panel = Box;
|
|
513
|
-
var Line = Text;
|
|
514
|
-
function GuideScreen({ columns, rows, onExit, startAt, palette = NO_COLOR_PALETTE }) {
|
|
515
|
-
const [state, setState] = useState(() => {
|
|
516
|
-
const initial = initialGuideState(columns, rows, palette);
|
|
517
|
-
const index = startAt ? initial.topics.findIndex((topic) => topic.id === startAt) : -1;
|
|
518
|
-
return index >= 0 ? { ...initial, selected: index } : initial;
|
|
519
|
-
});
|
|
520
|
-
useInput((input, key) => {
|
|
521
|
-
const action = keyToGuideAction({ name: key?.key, ctrl: key?.ctrl, shift: key?.shift }, input, state.searching);
|
|
522
|
-
if (action.kind === "exit") {
|
|
523
|
-
onExit();
|
|
524
|
-
return;
|
|
525
|
-
}
|
|
526
|
-
setState((current) => applyGuideAction(current, action));
|
|
527
|
-
});
|
|
528
|
-
const frame = composeGuideFrame(state);
|
|
529
|
-
return /* @__PURE__ */ jsxDEV(Panel, {
|
|
530
|
-
flexDirection: "column",
|
|
531
|
-
width: state.columns,
|
|
532
|
-
height: state.rows,
|
|
533
|
-
children: frame.map((row, index) => /* @__PURE__ */ jsxDEV(Line, {
|
|
534
|
-
bold: row.bold,
|
|
535
|
-
dimColor: row.dim,
|
|
536
|
-
inverse: row.inverse,
|
|
537
|
-
color: row.color,
|
|
538
|
-
children: row.text
|
|
539
|
-
}, `row-${index}`, false, undefined, this))
|
|
540
|
-
}, undefined, false, undefined, this);
|
|
541
|
-
}
|
|
542
|
-
async function runGuideScreen(options) {
|
|
543
|
-
const { renderApp } = await import("@termuijs/jsx");
|
|
544
|
-
await new Promise((resolve) => {
|
|
545
|
-
let settled = false;
|
|
546
|
-
const finish = () => {
|
|
547
|
-
if (settled)
|
|
548
|
-
return;
|
|
549
|
-
settled = true;
|
|
550
|
-
resolve();
|
|
551
|
-
};
|
|
552
|
-
renderApp(GuideScreen, {
|
|
553
|
-
columns: options.columns,
|
|
554
|
-
rows: options.rows,
|
|
555
|
-
startAt: options.startAt,
|
|
556
|
-
palette: options.palette,
|
|
557
|
-
onExit: finish,
|
|
558
|
-
fullscreen: true
|
|
559
|
-
}).catch(finish);
|
|
560
|
-
});
|
|
561
|
-
}
|
|
562
|
-
export {
|
|
563
|
-
runGuideScreen,
|
|
564
|
-
GuideScreen
|
|
565
|
-
};
|