archymedes-cli 2.1.0 → 2.2.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 +15 -0
- package/dist/archymedes-5zn4zp84.mjs +646 -0
- package/dist/{archymedes-44c1mqas.mjs → archymedes-cdgejs3p.mjs} +1 -1
- package/dist/archymedes-main.mjs +1175 -663
- package/dist/{archymedes-k1bx08z9.mjs → archymedes-npcrm9a0.mjs} +3 -1
- package/dist/{archymedes-2tp22t9a.mjs → archymedes-pg3pvh9r.mjs} +6 -4
- package/dist/{archymedes-dwvn4c51.mjs → archymedes-r54qthg5.mjs} +47 -378
- package/dist/archymedes-sc3mwttb.mjs +284 -0
- package/dist/{archymedes-brkqgrxv.mjs → archymedes-wjaqydyg.mjs} +44 -32
- package/dist/{editor-screen-51z7z11j.mjs → editor-screen-1b3z96tn.mjs} +259 -11
- package/dist/{file-screen-eg28a3ky.mjs → file-screen-m7q1xcer.mjs} +5 -5
- package/dist/{guide-screen-mfvwkdhb.mjs → guide-screen-aj9n9v80.mjs} +15 -11
- package/dist/{workspace-screen-3zztn4by.mjs → workspace-screen-w88mcw4w.mjs} +7 -6
- package/package.json +1 -1
- package/dist/archymedes-jecyxv5c.mjs +0 -249
package/dist/archymedes-main.mjs
CHANGED
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
runChooser,
|
|
7
7
|
terminalColumns,
|
|
8
8
|
windowStart
|
|
9
|
-
} from "./archymedes-
|
|
9
|
+
} from "./archymedes-wjaqydyg.mjs";
|
|
10
10
|
import {
|
|
11
11
|
DEFAULT_OUTPUT_CEILING,
|
|
12
12
|
OpenAIAgentTurnProvider,
|
|
@@ -35,6 +35,13 @@ import {
|
|
|
35
35
|
wanderGradeCounts,
|
|
36
36
|
wanderPlannerInstructions
|
|
37
37
|
} from "./archymedes-76z6n643.mjs";
|
|
38
|
+
import {
|
|
39
|
+
describeChange,
|
|
40
|
+
diffLines,
|
|
41
|
+
highlightCode,
|
|
42
|
+
renderCode,
|
|
43
|
+
renderDiff
|
|
44
|
+
} from "./archymedes-sc3mwttb.mjs";
|
|
38
45
|
import {
|
|
39
46
|
BrailleCanvas,
|
|
40
47
|
StreamSeries,
|
|
@@ -46,7 +53,7 @@ import {
|
|
|
46
53
|
scatterChart,
|
|
47
54
|
seriesBounds,
|
|
48
55
|
waveLine
|
|
49
|
-
} from "./archymedes-
|
|
56
|
+
} from "./archymedes-pg3pvh9r.mjs";
|
|
50
57
|
import {
|
|
51
58
|
COMMANDS,
|
|
52
59
|
CONTROL_LANGUAGES,
|
|
@@ -59,7 +66,25 @@ import {
|
|
|
59
66
|
scrollFraction,
|
|
60
67
|
t,
|
|
61
68
|
visibleLines
|
|
62
|
-
} from "./archymedes-
|
|
69
|
+
} from "./archymedes-npcrm9a0.mjs";
|
|
70
|
+
import {
|
|
71
|
+
SpringAnimator,
|
|
72
|
+
archymedesSpinnerFrame,
|
|
73
|
+
box,
|
|
74
|
+
formatStatusLine,
|
|
75
|
+
padToWidth,
|
|
76
|
+
paginator,
|
|
77
|
+
progressBar,
|
|
78
|
+
renderPromptBox,
|
|
79
|
+
rowsOccupied,
|
|
80
|
+
scrollIndicator,
|
|
81
|
+
scrollPercent,
|
|
82
|
+
sliceToWidth,
|
|
83
|
+
sparkline,
|
|
84
|
+
stepProgress,
|
|
85
|
+
table,
|
|
86
|
+
terminalStream
|
|
87
|
+
} from "./archymedes-r54qthg5.mjs";
|
|
63
88
|
import {
|
|
64
89
|
BOLD,
|
|
65
90
|
CYAN,
|
|
@@ -79,37 +104,20 @@ import {
|
|
|
79
104
|
paintAll,
|
|
80
105
|
panel,
|
|
81
106
|
rule
|
|
82
|
-
} from "./archymedes-
|
|
107
|
+
} from "./archymedes-cdgejs3p.mjs";
|
|
83
108
|
import {
|
|
84
109
|
ASCII_GLYPHS,
|
|
85
|
-
|
|
110
|
+
BUILTIN_THEME_SOURCES,
|
|
86
111
|
UNICODE_GLYPHS,
|
|
87
|
-
archymedesSpinnerFrame,
|
|
88
112
|
borderGlyphsFor,
|
|
89
|
-
box,
|
|
90
|
-
formatStatusLine,
|
|
91
113
|
newMarkdownState,
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
progressBar,
|
|
114
|
+
parseThemeSource,
|
|
115
|
+
rainbowText,
|
|
95
116
|
renderMarkdown,
|
|
96
117
|
renderMarkdownLine,
|
|
97
|
-
|
|
98
|
-
rowsOccupied,
|
|
99
|
-
scrollIndicator,
|
|
100
|
-
scrollPercent,
|
|
101
|
-
sliceToWidth,
|
|
102
|
-
sparkline,
|
|
103
|
-
stepProgress,
|
|
104
|
-
table,
|
|
105
|
-
terminalStream,
|
|
118
|
+
rgbTo256,
|
|
106
119
|
visibleWidth
|
|
107
|
-
} from "./archymedes-
|
|
108
|
-
import {
|
|
109
|
-
BUILTIN_THEME_SOURCES,
|
|
110
|
-
parseThemeSource,
|
|
111
|
-
rgbTo256
|
|
112
|
-
} from "./archymedes-jecyxv5c.mjs";
|
|
120
|
+
} from "./archymedes-5zn4zp84.mjs";
|
|
113
121
|
import {
|
|
114
122
|
__require
|
|
115
123
|
} from "./archymedes-x67v3n58.mjs";
|
|
@@ -10012,227 +10020,6 @@ async function runDefenderTriage(keys, findings, paint3, options) {
|
|
|
10012
10020
|
return { toFix, findings: state.findings };
|
|
10013
10021
|
}
|
|
10014
10022
|
|
|
10015
|
-
// packages/archymedes-cli/src/code-view.ts
|
|
10016
|
-
var MAX_DIFF_CELLS = 250000;
|
|
10017
|
-
function diffLines(before, after) {
|
|
10018
|
-
const left = before === "" ? [] : before.split(`
|
|
10019
|
-
`);
|
|
10020
|
-
const right = after === "" ? [] : after.split(`
|
|
10021
|
-
`);
|
|
10022
|
-
if (left.length * right.length > MAX_DIFF_CELLS) {
|
|
10023
|
-
return [
|
|
10024
|
-
...left.map((text) => ({ kind: "remove", text })),
|
|
10025
|
-
...right.map((text) => ({ kind: "add", text }))
|
|
10026
|
-
];
|
|
10027
|
-
}
|
|
10028
|
-
const lengths = Array.from({ length: left.length + 1 }, () => new Array(right.length + 1).fill(0));
|
|
10029
|
-
for (let i2 = left.length - 1;i2 >= 0; i2 -= 1) {
|
|
10030
|
-
for (let j2 = right.length - 1;j2 >= 0; j2 -= 1) {
|
|
10031
|
-
lengths[i2][j2] = left[i2] === right[j2] ? lengths[i2 + 1][j2 + 1] + 1 : Math.max(lengths[i2 + 1][j2], lengths[i2][j2 + 1]);
|
|
10032
|
-
}
|
|
10033
|
-
}
|
|
10034
|
-
const result = [];
|
|
10035
|
-
let i = 0;
|
|
10036
|
-
let j = 0;
|
|
10037
|
-
while (i < left.length && j < right.length) {
|
|
10038
|
-
if (left[i] === right[j]) {
|
|
10039
|
-
result.push({ kind: "context", text: left[i] });
|
|
10040
|
-
i += 1;
|
|
10041
|
-
j += 1;
|
|
10042
|
-
} else if (lengths[i + 1][j] >= lengths[i][j + 1]) {
|
|
10043
|
-
result.push({ kind: "remove", text: left[i] });
|
|
10044
|
-
i += 1;
|
|
10045
|
-
} else {
|
|
10046
|
-
result.push({ kind: "add", text: right[j] });
|
|
10047
|
-
j += 1;
|
|
10048
|
-
}
|
|
10049
|
-
}
|
|
10050
|
-
while (i < left.length) {
|
|
10051
|
-
result.push({ kind: "remove", text: left[i] });
|
|
10052
|
-
i += 1;
|
|
10053
|
-
}
|
|
10054
|
-
while (j < right.length) {
|
|
10055
|
-
result.push({ kind: "add", text: right[j] });
|
|
10056
|
-
j += 1;
|
|
10057
|
-
}
|
|
10058
|
-
return result;
|
|
10059
|
-
}
|
|
10060
|
-
function condenseDiff(diff, context = 2) {
|
|
10061
|
-
const keep = new Array(diff.length).fill(false);
|
|
10062
|
-
diff.forEach((line, index) => {
|
|
10063
|
-
if (line.kind === "context")
|
|
10064
|
-
return;
|
|
10065
|
-
for (let offset = -context;offset <= context; offset += 1) {
|
|
10066
|
-
const target = index + offset;
|
|
10067
|
-
if (target >= 0 && target < diff.length)
|
|
10068
|
-
keep[target] = true;
|
|
10069
|
-
}
|
|
10070
|
-
});
|
|
10071
|
-
const result = [];
|
|
10072
|
-
let skipped = 0;
|
|
10073
|
-
diff.forEach((line, index) => {
|
|
10074
|
-
if (keep[index]) {
|
|
10075
|
-
if (skipped > 0) {
|
|
10076
|
-
result.push({ kind: "gap", text: `${skipped} unchanged line${skipped === 1 ? "" : "s"}` });
|
|
10077
|
-
skipped = 0;
|
|
10078
|
-
}
|
|
10079
|
-
result.push(line);
|
|
10080
|
-
return;
|
|
10081
|
-
}
|
|
10082
|
-
skipped += 1;
|
|
10083
|
-
});
|
|
10084
|
-
if (skipped > 0)
|
|
10085
|
-
result.push({ kind: "gap", text: `${skipped} unchanged line${skipped === 1 ? "" : "s"}` });
|
|
10086
|
-
return result;
|
|
10087
|
-
}
|
|
10088
|
-
var KEYWORDS = new Set([
|
|
10089
|
-
"async",
|
|
10090
|
-
"await",
|
|
10091
|
-
"break",
|
|
10092
|
-
"case",
|
|
10093
|
-
"catch",
|
|
10094
|
-
"class",
|
|
10095
|
-
"const",
|
|
10096
|
-
"continue",
|
|
10097
|
-
"def",
|
|
10098
|
-
"default",
|
|
10099
|
-
"elif",
|
|
10100
|
-
"else",
|
|
10101
|
-
"end",
|
|
10102
|
-
"enum",
|
|
10103
|
-
"export",
|
|
10104
|
-
"extends",
|
|
10105
|
-
"finally",
|
|
10106
|
-
"fn",
|
|
10107
|
-
"for",
|
|
10108
|
-
"from",
|
|
10109
|
-
"func",
|
|
10110
|
-
"function",
|
|
10111
|
-
"if",
|
|
10112
|
-
"impl",
|
|
10113
|
-
"import",
|
|
10114
|
-
"in",
|
|
10115
|
-
"interface",
|
|
10116
|
-
"let",
|
|
10117
|
-
"match",
|
|
10118
|
-
"mod",
|
|
10119
|
-
"new",
|
|
10120
|
-
"package",
|
|
10121
|
-
"private",
|
|
10122
|
-
"public",
|
|
10123
|
-
"pub",
|
|
10124
|
-
"raise",
|
|
10125
|
-
"return",
|
|
10126
|
-
"static",
|
|
10127
|
-
"struct",
|
|
10128
|
-
"switch",
|
|
10129
|
-
"throw",
|
|
10130
|
-
"trait",
|
|
10131
|
-
"try",
|
|
10132
|
-
"type",
|
|
10133
|
-
"use",
|
|
10134
|
-
"var",
|
|
10135
|
-
"void",
|
|
10136
|
-
"while",
|
|
10137
|
-
"with",
|
|
10138
|
-
"yield"
|
|
10139
|
-
]);
|
|
10140
|
-
var LITERALS = new Set(["true", "false", "null", "nil", "None", "True", "False", "undefined", "self", "this"]);
|
|
10141
|
-
function highlightCode(line, depth) {
|
|
10142
|
-
if (depth === "none")
|
|
10143
|
-
return line;
|
|
10144
|
-
const commentAt = findCommentStart(line);
|
|
10145
|
-
const code = commentAt === -1 ? line : line.slice(0, commentAt);
|
|
10146
|
-
const comment = commentAt === -1 ? "" : paint(line.slice(commentAt), DIM, depth);
|
|
10147
|
-
const painted = code.replace(/("(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*'|`(?:[^`\\]|\\.)*`)|\b(\d+(?:\.\d+)?)\b|\b([A-Za-z_$][\w$]*)\b/g, (whole, text, numeric, word) => {
|
|
10148
|
-
if (text !== undefined)
|
|
10149
|
-
return paint(text, GREEN, depth);
|
|
10150
|
-
if (numeric !== undefined)
|
|
10151
|
-
return paint(numeric, MAGENTA, depth);
|
|
10152
|
-
if (word !== undefined && KEYWORDS.has(word))
|
|
10153
|
-
return paint(word, CYAN, depth);
|
|
10154
|
-
if (word !== undefined && LITERALS.has(word))
|
|
10155
|
-
return paint(word, MAGENTA, depth);
|
|
10156
|
-
return whole;
|
|
10157
|
-
});
|
|
10158
|
-
return `${painted}${comment}`;
|
|
10159
|
-
}
|
|
10160
|
-
function findCommentStart(line) {
|
|
10161
|
-
let quote;
|
|
10162
|
-
for (let index = 0;index < line.length; index += 1) {
|
|
10163
|
-
const character = line[index];
|
|
10164
|
-
if (quote) {
|
|
10165
|
-
if (character === "\\") {
|
|
10166
|
-
index += 1;
|
|
10167
|
-
continue;
|
|
10168
|
-
}
|
|
10169
|
-
if (character === quote)
|
|
10170
|
-
quote = undefined;
|
|
10171
|
-
continue;
|
|
10172
|
-
}
|
|
10173
|
-
if (character === '"' || character === "'" || character === "`") {
|
|
10174
|
-
quote = character;
|
|
10175
|
-
continue;
|
|
10176
|
-
}
|
|
10177
|
-
if (character === "#")
|
|
10178
|
-
return index;
|
|
10179
|
-
if (character === "/" && line[index + 1] === "/")
|
|
10180
|
-
return index;
|
|
10181
|
-
if (character === "-" && line[index + 1] === "-")
|
|
10182
|
-
return index;
|
|
10183
|
-
}
|
|
10184
|
-
return -1;
|
|
10185
|
-
}
|
|
10186
|
-
function renderCode(content, style, options = {}) {
|
|
10187
|
-
const glyphs = style.glyphs ?? UNICODE_GLYPHS;
|
|
10188
|
-
const lines = content.replace(/\n$/, "").split(`
|
|
10189
|
-
`);
|
|
10190
|
-
const maxLines = options.maxLines ?? 16;
|
|
10191
|
-
const start = options.startLine ?? 1;
|
|
10192
|
-
const numberWidth = String(start + lines.length - 1).length;
|
|
10193
|
-
const bodyWidth = Math.max(8, style.width - GUTTER.length - numberWidth - 4);
|
|
10194
|
-
const render = (from, to) => lines.slice(from, to).map((line, index) => {
|
|
10195
|
-
const number = paint(String(start + from + index).padStart(numberWidth), DIM, style.depth);
|
|
10196
|
-
const painted = options.highlight === false ? line : highlightCode(line, style.depth);
|
|
10197
|
-
return `${number} ${clip(painted, bodyWidth, glyphs)}`;
|
|
10198
|
-
});
|
|
10199
|
-
const hidden = Math.max(0, lines.length - maxLines);
|
|
10200
|
-
const shown = render(0, hidden > 0 ? maxLines : lines.length);
|
|
10201
|
-
return {
|
|
10202
|
-
text: shown.join(`
|
|
10203
|
-
`),
|
|
10204
|
-
hidden,
|
|
10205
|
-
full: render(0, lines.length).join(`
|
|
10206
|
-
`)
|
|
10207
|
-
};
|
|
10208
|
-
}
|
|
10209
|
-
function renderDiff(diff, style, options = {}) {
|
|
10210
|
-
const glyphs = style.glyphs ?? UNICODE_GLYPHS;
|
|
10211
|
-
const maxLines = options.maxLines ?? 16;
|
|
10212
|
-
const condensed = condenseDiff(diff);
|
|
10213
|
-
const bodyWidth = Math.max(8, style.width - GUTTER.length - 4);
|
|
10214
|
-
const row = (line) => {
|
|
10215
|
-
if (line.kind === "gap")
|
|
10216
|
-
return paint(`${glyphs.middot.repeat(3)} ${line.text}`, DIM, style.depth);
|
|
10217
|
-
const mark = line.kind === "add" ? glyphs.plus : line.kind === "remove" ? glyphs.minus : " ";
|
|
10218
|
-
const code = line.kind === "add" ? GREEN : line.kind === "remove" ? RED : DIM;
|
|
10219
|
-
const body = line.kind === "context" ? paint(line.text, DIM, style.depth) : highlightCode(line.text, style.depth);
|
|
10220
|
-
return `${paint(mark, code, style.depth)} ${clip(body, bodyWidth, glyphs)}`;
|
|
10221
|
-
};
|
|
10222
|
-
const rows = condensed.map(row);
|
|
10223
|
-
const hidden = Math.max(0, rows.length - maxLines);
|
|
10224
|
-
return {
|
|
10225
|
-
text: (hidden > 0 ? rows.slice(0, maxLines) : rows).join(`
|
|
10226
|
-
`),
|
|
10227
|
-
hidden,
|
|
10228
|
-
full: diff.map(row).join(`
|
|
10229
|
-
`)
|
|
10230
|
-
};
|
|
10231
|
-
}
|
|
10232
|
-
function describeChange(stat2, depth) {
|
|
10233
|
-
return `${paint(`+${stat2.added}`, GREEN, depth)} ${paint(`-${stat2.removed}`, RED, depth)}`;
|
|
10234
|
-
}
|
|
10235
|
-
|
|
10236
10023
|
// packages/archymedes-cli/src/dropup.ts
|
|
10237
10024
|
var PLAIN = { dim: (text) => text, cyan: (text) => text, green: (text) => text };
|
|
10238
10025
|
function dropupWindow(count, rows, selected) {
|
|
@@ -10812,7 +10599,8 @@ function renderGallery(options) {
|
|
|
10812
10599
|
panel(["gutter only, no frame"], style, { title: "output", gutterOnly: true }),
|
|
10813
10600
|
box(["a box with a title", "and a second row"], { depth, width, title: "you", titleColor: "green", glyphs }),
|
|
10814
10601
|
box(["a box whose border carries the state"], { depth, width, title: "failed", titleColor: "red", borderColor: "red", glyphs }),
|
|
10815
|
-
box(["double border"], { depth, width, title: "double", glyphs, borderStyle: "double" })
|
|
10602
|
+
box(["double border"], { depth, width, title: "double", glyphs, borderStyle: "double" }),
|
|
10603
|
+
box(["the /theme rainbow border"], { depth, width, title: "rainbow", titleColor: "cyan", borderColor: "rainbow", glyphs })
|
|
10816
10604
|
]));
|
|
10817
10605
|
parts.push(section("key/value and tables", style, [
|
|
10818
10606
|
keyValues([["provider", "anthropic"], ["model", "claude-opus-5"], ["workspace", "/home/you/project"]], style),
|
|
@@ -11404,6 +11192,7 @@ var COMMAND_GROUP = {
|
|
|
11404
11192
|
"/scan": "work",
|
|
11405
11193
|
"/wander": "work",
|
|
11406
11194
|
"/voice": "work",
|
|
11195
|
+
"/cat": "review",
|
|
11407
11196
|
"/files": "work",
|
|
11408
11197
|
"/pull": "work",
|
|
11409
11198
|
"/where": "review",
|
|
@@ -11423,6 +11212,7 @@ var COMMAND_GROUP = {
|
|
|
11423
11212
|
"/settings": "setup",
|
|
11424
11213
|
"/providers": "setup",
|
|
11425
11214
|
"/theme": "setup",
|
|
11215
|
+
"/layout": "setup",
|
|
11426
11216
|
"/update": "setup"
|
|
11427
11217
|
};
|
|
11428
11218
|
function navSignals(context) {
|
|
@@ -13661,6 +13451,18 @@ function detectColorDepth(environment, isTTY) {
|
|
|
13661
13451
|
}
|
|
13662
13452
|
var STAR_GLYPHS2 = UNICODE_GLYPHS.starGlyphs;
|
|
13663
13453
|
|
|
13454
|
+
// packages/archymedes-cli/src/fixed-screen.ts
|
|
13455
|
+
var ESCAPE = "\x1B";
|
|
13456
|
+
var CSI = `${ESCAPE}[`;
|
|
13457
|
+
var ENTER_ALTERNATE_SCREEN = `${CSI}?1049h`;
|
|
13458
|
+
var LEAVE_ALTERNATE_SCREEN = `${CSI}?1049l`;
|
|
13459
|
+
var HIDE_CURSOR = `${CSI}?25l`;
|
|
13460
|
+
var SHOW_CURSOR = `${CSI}?25h`;
|
|
13461
|
+
var ENABLE_MOUSE = `${CSI}?1000h${CSI}?1006h`;
|
|
13462
|
+
var DISABLE_MOUSE = `${CSI}?1006l${CSI}?1000l`;
|
|
13463
|
+
var BEGIN_SYNC = `${CSI}?2026h`;
|
|
13464
|
+
var END_SYNC = `${CSI}?2026l`;
|
|
13465
|
+
|
|
13664
13466
|
// packages/archymedes-cli/src/identity.ts
|
|
13665
13467
|
function renderGeometry(angle = 0, ascii = false) {
|
|
13666
13468
|
const width = 23;
|
|
@@ -13671,8 +13473,11 @@ function renderGeometry(angle = 0, ascii = false) {
|
|
|
13671
13473
|
const yaw = angle + 0.25;
|
|
13672
13474
|
const rx = (x * Math.cos(yaw) + z * Math.sin(yaw)) * scale;
|
|
13673
13475
|
const rz = (-x * Math.sin(yaw) + z * Math.cos(yaw)) * scale;
|
|
13674
|
-
const
|
|
13675
|
-
|
|
13476
|
+
const tilt = 0.3 + Math.sin(angle) * 0.18;
|
|
13477
|
+
const ry = y * scale * Math.cos(tilt) - rz * Math.sin(tilt);
|
|
13478
|
+
const depth = y * scale * Math.sin(tilt) + rz * Math.cos(tilt);
|
|
13479
|
+
const perspective = 3.8 / (3.8 + depth);
|
|
13480
|
+
return [Math.max(0, Math.min(22, Math.round(11 + rx * perspective * 8))), Math.max(0, Math.min(10, Math.round(5 - ry * perspective * 3.8)))];
|
|
13676
13481
|
};
|
|
13677
13482
|
const outer = vertices.map((point) => project(point, 1));
|
|
13678
13483
|
const inner = vertices.map((point) => project(point, 0.43));
|
|
@@ -13704,19 +13509,21 @@ async function writeIdentity(options, output, motion) {
|
|
|
13704
13509
|
`);
|
|
13705
13510
|
if (!motion.enabled || motion.signal?.aborted || options.width < 64 || options.rows < 24)
|
|
13706
13511
|
return;
|
|
13707
|
-
const frames =
|
|
13512
|
+
const frames = 28;
|
|
13708
13513
|
for (let frame = 1;frame <= frames; frame++) {
|
|
13709
|
-
await new Promise((resolve) => setTimeout(resolve,
|
|
13514
|
+
await new Promise((resolve) => setTimeout(resolve, 18));
|
|
13710
13515
|
if (motion.signal?.aborted)
|
|
13711
13516
|
return;
|
|
13712
13517
|
const size = motion.size();
|
|
13713
13518
|
if (size.width !== options.width || size.rows !== options.rows)
|
|
13714
13519
|
return;
|
|
13715
|
-
const
|
|
13716
|
-
|
|
13520
|
+
const progress = frame / frames;
|
|
13521
|
+
const eased = progress < 0.5 ? 4 * progress * progress * progress : 1 - Math.pow(-2 * progress + 2, 3) / 2;
|
|
13522
|
+
const rendered = renderIdentity({ ...options, angle: frame === frames ? 0 : eased * Math.PI * 2 });
|
|
13523
|
+
output.write(`${BEGIN_SYNC}\x1B[11F${rendered.split(`
|
|
13717
13524
|
`).map((line) => `\x1B[2K${line}`).join(`
|
|
13718
13525
|
`)}
|
|
13719
|
-
`);
|
|
13526
|
+
${END_SYNC}`);
|
|
13720
13527
|
}
|
|
13721
13528
|
}
|
|
13722
13529
|
function renderIdentity(options) {
|
|
@@ -13745,17 +13552,774 @@ function renderIdentity(options) {
|
|
|
13745
13552
|
const artWidth = Math.max(...geometry.map(visibleWidth));
|
|
13746
13553
|
const gap = 3;
|
|
13747
13554
|
const margin = 2;
|
|
13555
|
+
const rainbow = palette.theme === "rainbow";
|
|
13556
|
+
const phase = (options.angle ?? 0) / (Math.PI * 2);
|
|
13557
|
+
const paintDrawing = (drawing) => rainbow ? rainbowText(drawing, palette.depth, phase) : paint(drawing, palette.primary, palette.depth);
|
|
13748
13558
|
return geometry.map((line, index) => {
|
|
13749
13559
|
const drawing = line + " ".repeat(artWidth - visibleWidth(line));
|
|
13750
|
-
return " ".repeat(margin) +
|
|
13560
|
+
return " ".repeat(margin) + paintDrawing(drawing) + " ".repeat(gap) + paint(clipTo(clean(text[index] ?? ""), width - margin - artWidth - gap, glyphs), codes[index] ?? "", palette.depth);
|
|
13751
13561
|
}).join(`
|
|
13752
13562
|
`);
|
|
13753
13563
|
}
|
|
13754
13564
|
|
|
13755
|
-
// packages/archymedes-cli/src/
|
|
13756
|
-
var
|
|
13757
|
-
var
|
|
13758
|
-
var
|
|
13565
|
+
// packages/archymedes-cli/src/layout.ts
|
|
13566
|
+
var GOLDEN_RATIO = 1.618033988749895;
|
|
13567
|
+
var MAX_CONTENT_WIDTH = Math.round(80 * GOLDEN_RATIO);
|
|
13568
|
+
var FOOTER_ROWS = 3;
|
|
13569
|
+
var MIN_TRANSCRIPT_ROWS = 3;
|
|
13570
|
+
function computeLayout(rows, columns) {
|
|
13571
|
+
const safeRows = Math.max(1, Math.floor(rows));
|
|
13572
|
+
const safeColumns = Math.max(1, Math.floor(columns));
|
|
13573
|
+
const footerRows = Math.min(FOOTER_ROWS, Math.max(0, safeRows - MIN_TRANSCRIPT_ROWS));
|
|
13574
|
+
const scrollBottom = safeRows - footerRows;
|
|
13575
|
+
return {
|
|
13576
|
+
rows: safeRows,
|
|
13577
|
+
columns: safeColumns,
|
|
13578
|
+
scrollTop: 1,
|
|
13579
|
+
scrollBottom,
|
|
13580
|
+
footerRows,
|
|
13581
|
+
statusRow: footerRows >= 2 ? scrollBottom + 1 : undefined,
|
|
13582
|
+
inputRow: footerRows >= 2 ? scrollBottom + 2 : footerRows === 1 ? scrollBottom + 1 : undefined,
|
|
13583
|
+
promptBottomRow: footerRows >= 3 ? scrollBottom + 3 : undefined,
|
|
13584
|
+
contentWidth: Math.min(safeColumns, MAX_CONTENT_WIDTH)
|
|
13585
|
+
};
|
|
13586
|
+
}
|
|
13587
|
+
|
|
13588
|
+
// packages/archymedes-cli/src/screen.ts
|
|
13589
|
+
class PinnedScreen {
|
|
13590
|
+
stream;
|
|
13591
|
+
layout;
|
|
13592
|
+
suggestionRows = 0;
|
|
13593
|
+
holdRegion;
|
|
13594
|
+
regionActive = false;
|
|
13595
|
+
constructor(stream, options = {}) {
|
|
13596
|
+
this.stream = stream;
|
|
13597
|
+
this.layout = computeLayout(stream.rows ?? 24, stream.columns ?? 80);
|
|
13598
|
+
this.holdRegion = options.holdRegion ?? true;
|
|
13599
|
+
this.headerRows = options.headerRows ?? 0;
|
|
13600
|
+
this.reserveHeader();
|
|
13601
|
+
}
|
|
13602
|
+
headerRows;
|
|
13603
|
+
reserveHeader() {
|
|
13604
|
+
this.layout.scrollTop = 1 + Math.min(this.headerRows, Math.max(0, this.layout.scrollBottom - 3));
|
|
13605
|
+
}
|
|
13606
|
+
get pinned() {
|
|
13607
|
+
return this.holdRegion;
|
|
13608
|
+
}
|
|
13609
|
+
get current() {
|
|
13610
|
+
return this.layout;
|
|
13611
|
+
}
|
|
13612
|
+
enter() {
|
|
13613
|
+
if (!this.holdRegion)
|
|
13614
|
+
return;
|
|
13615
|
+
this.setRegion();
|
|
13616
|
+
this.parkInTranscript();
|
|
13617
|
+
}
|
|
13618
|
+
resize() {
|
|
13619
|
+
this.suggestionRows = 0;
|
|
13620
|
+
this.layout = computeLayout(this.stream.rows ?? 24, this.stream.columns ?? 80);
|
|
13621
|
+
this.reserveHeader();
|
|
13622
|
+
if (this.holdRegion) {
|
|
13623
|
+
this.setRegion();
|
|
13624
|
+
this.parkInTranscript();
|
|
13625
|
+
} else if (this.regionActive) {
|
|
13626
|
+
this.releaseRegion();
|
|
13627
|
+
}
|
|
13628
|
+
return this.layout;
|
|
13629
|
+
}
|
|
13630
|
+
setRegion() {
|
|
13631
|
+
this.stream.write(this.layout.footerRows === 0 ? "\x1B[r" : `\x1B[${this.layout.scrollTop};${this.layout.scrollBottom}r`);
|
|
13632
|
+
this.regionActive = this.layout.footerRows !== 0;
|
|
13633
|
+
}
|
|
13634
|
+
releaseRegion() {
|
|
13635
|
+
if (!this.regionActive)
|
|
13636
|
+
return;
|
|
13637
|
+
this.stream.write("\x1B[r");
|
|
13638
|
+
this.regionActive = false;
|
|
13639
|
+
}
|
|
13640
|
+
parkInTranscript() {
|
|
13641
|
+
if (!this.holdRegion || this.layout.footerRows === 0)
|
|
13642
|
+
return;
|
|
13643
|
+
this.stream.write(`\x1B[${this.layout.scrollBottom};1H`);
|
|
13644
|
+
}
|
|
13645
|
+
renderStatus(text) {
|
|
13646
|
+
if (!this.holdRegion || !this.layout.statusRow)
|
|
13647
|
+
return;
|
|
13648
|
+
this.stream.write(`\x1B7\x1B[?25l\x1B[${this.layout.statusRow};1H\x1B[2K${text}\x1B8\x1B[?25h`);
|
|
13649
|
+
}
|
|
13650
|
+
renderPromptBottom(text) {
|
|
13651
|
+
if (!this.holdRegion || !this.layout.promptBottomRow)
|
|
13652
|
+
return;
|
|
13653
|
+
this.stream.write(`\x1B7\x1B[?25l\x1B[${this.layout.promptBottomRow};1H\x1B[2K${text}\x1B8\x1B[?25h`);
|
|
13654
|
+
}
|
|
13655
|
+
positionInput() {
|
|
13656
|
+
if (!this.holdRegion || !this.layout.inputRow)
|
|
13657
|
+
return;
|
|
13658
|
+
this.stream.write(`\x1B[${this.layout.inputRow};1H\x1B[2K`);
|
|
13659
|
+
}
|
|
13660
|
+
renderSuggestions(lines) {
|
|
13661
|
+
if (this.layout.footerRows === 0)
|
|
13662
|
+
return;
|
|
13663
|
+
const wanted = Math.min(lines.length, this.maxSuggestionRows());
|
|
13664
|
+
if (wanted === 0) {
|
|
13665
|
+
this.clearSuggestions();
|
|
13666
|
+
return;
|
|
13667
|
+
}
|
|
13668
|
+
this.applyRows(wanted, lines);
|
|
13669
|
+
}
|
|
13670
|
+
applyRows(target, lines) {
|
|
13671
|
+
if (target > this.suggestionRows) {
|
|
13672
|
+
const extra = target - this.suggestionRows;
|
|
13673
|
+
this.regionActive = true;
|
|
13674
|
+
this.stream.write(`\x1B7\x1B[${this.layout.scrollTop};${this.layout.scrollBottom}r`);
|
|
13675
|
+
this.stream.write(`\x1B[${extra}S`);
|
|
13676
|
+
this.suggestionRows = target;
|
|
13677
|
+
this.stream.write(`\x1B[${this.layout.scrollTop};${this.transcriptBottom()}r\x1B8`);
|
|
13678
|
+
} else if (target < this.suggestionRows) {
|
|
13679
|
+
const top2 = this.transcriptBottom() + 1;
|
|
13680
|
+
this.stream.write("\x1B7\x1B[?25l");
|
|
13681
|
+
for (let offset = target;offset < this.suggestionRows; offset += 1)
|
|
13682
|
+
this.stream.write(`\x1B[${top2 + offset};1H\x1B[2K`);
|
|
13683
|
+
this.suggestionRows = target;
|
|
13684
|
+
this.stream.write(`\x1B[${this.layout.scrollTop};${this.transcriptBottom()}r\x1B8\x1B[?25h`);
|
|
13685
|
+
}
|
|
13686
|
+
const top = this.transcriptBottom() + 1;
|
|
13687
|
+
this.stream.write("\x1B7\x1B[?25l");
|
|
13688
|
+
for (let offset = 0;offset < this.suggestionRows; offset += 1) {
|
|
13689
|
+
this.stream.write(`\x1B[${top + offset};1H\x1B[2K${lines[offset] ?? ""}`);
|
|
13690
|
+
}
|
|
13691
|
+
this.stream.write("\x1B8\x1B[?25h");
|
|
13692
|
+
}
|
|
13693
|
+
clearSuggestions() {
|
|
13694
|
+
if (this.suggestionRows === 0)
|
|
13695
|
+
return;
|
|
13696
|
+
const top = this.transcriptBottom() + 1;
|
|
13697
|
+
this.stream.write("\x1B7\x1B[?25l");
|
|
13698
|
+
for (let offset = 0;offset < this.suggestionRows; offset += 1)
|
|
13699
|
+
this.stream.write(`\x1B[${top + offset};1H\x1B[2K`);
|
|
13700
|
+
this.suggestionRows = 0;
|
|
13701
|
+
this.stream.write(this.holdRegion ? `\x1B[${this.layout.scrollTop};${this.layout.scrollBottom}r\x1B8\x1B[?25h` : "\x1B[r\x1B8\x1B[?25h");
|
|
13702
|
+
if (!this.holdRegion)
|
|
13703
|
+
this.regionActive = false;
|
|
13704
|
+
}
|
|
13705
|
+
transcriptBottom() {
|
|
13706
|
+
return this.layout.scrollBottom - this.suggestionRows;
|
|
13707
|
+
}
|
|
13708
|
+
maxSuggestionRows() {
|
|
13709
|
+
return Math.max(0, Math.min(8, this.layout.scrollBottom - this.layout.scrollTop - 2));
|
|
13710
|
+
}
|
|
13711
|
+
exit() {
|
|
13712
|
+
this.stream.write("\x1B[r");
|
|
13713
|
+
this.stream.write(`\x1B[${this.layout.rows};1H
|
|
13714
|
+
`);
|
|
13715
|
+
}
|
|
13716
|
+
}
|
|
13717
|
+
|
|
13718
|
+
// packages/archymedes-cli/src/identity.ts
|
|
13719
|
+
function renderGeometry2(angle = 0, ascii = false) {
|
|
13720
|
+
const width = 23;
|
|
13721
|
+
const height = 11;
|
|
13722
|
+
const cells = Array.from({ length: height }, () => Array(width).fill(" "));
|
|
13723
|
+
const vertices = [[0, 1.15, 0], [-1, -0.7, 0.65], [1, -0.7, 0.65], [0, -0.7, -1]];
|
|
13724
|
+
const project = ([x, y, z], scale) => {
|
|
13725
|
+
const yaw = angle + 0.25;
|
|
13726
|
+
const rx = (x * Math.cos(yaw) + z * Math.sin(yaw)) * scale;
|
|
13727
|
+
const rz = (-x * Math.sin(yaw) + z * Math.cos(yaw)) * scale;
|
|
13728
|
+
const tilt = 0.3 + Math.sin(angle) * 0.18;
|
|
13729
|
+
const ry = y * scale * Math.cos(tilt) - rz * Math.sin(tilt);
|
|
13730
|
+
const depth = y * scale * Math.sin(tilt) + rz * Math.cos(tilt);
|
|
13731
|
+
const perspective = 3.8 / (3.8 + depth);
|
|
13732
|
+
return [Math.max(0, Math.min(22, Math.round(11 + rx * perspective * 8))), Math.max(0, Math.min(10, Math.round(5 - ry * perspective * 3.8)))];
|
|
13733
|
+
};
|
|
13734
|
+
const outer = vertices.map((point) => project(point, 1));
|
|
13735
|
+
const inner = vertices.map((point) => project(point, 0.43));
|
|
13736
|
+
const line = (a, b, faint = false) => {
|
|
13737
|
+
const dx = b[0] - a[0];
|
|
13738
|
+
const dy = b[1] - a[1];
|
|
13739
|
+
const steps = Math.max(Math.abs(dx), Math.abs(dy));
|
|
13740
|
+
const glyph = faint ? "." : Math.abs(dx) > Math.abs(dy) * 2 ? "-" : dx === 0 ? "|" : dx * dy > 0 ? "\\" : "/";
|
|
13741
|
+
for (let step = 1;step < steps; step++) {
|
|
13742
|
+
const x = Math.round(a[0] + dx * step / steps);
|
|
13743
|
+
const y = Math.round(a[1] + dy * step / steps);
|
|
13744
|
+
if (cells[y]?.[x] !== undefined && (!faint || cells[y][x] === " "))
|
|
13745
|
+
cells[y][x] = glyph;
|
|
13746
|
+
}
|
|
13747
|
+
};
|
|
13748
|
+
for (let i = 0;i < 4; i++)
|
|
13749
|
+
line(outer[i], inner[i], true);
|
|
13750
|
+
for (const points of [outer, inner]) {
|
|
13751
|
+
for (let i = 0;i < 4; i++)
|
|
13752
|
+
for (let j = i + 1;j < 4; j++)
|
|
13753
|
+
line(points[i], points[j], j === 3);
|
|
13754
|
+
}
|
|
13755
|
+
for (const [x, y] of [...outer, ...inner])
|
|
13756
|
+
cells[y][x] = ascii ? "+" : "◇";
|
|
13757
|
+
return cells.map((row) => row.join(""));
|
|
13758
|
+
}
|
|
13759
|
+
function renderIdentity2(options) {
|
|
13760
|
+
const { palette } = options;
|
|
13761
|
+
const glyphs = options.glyphs ?? UNICODE_GLYPHS;
|
|
13762
|
+
const width = Math.max(1, Math.floor(options.width));
|
|
13763
|
+
const ascii = glyphs.boxHorizontal === ASCII_GLYPHS.boxHorizontal;
|
|
13764
|
+
const mark = ascii ? "/\\" : "△";
|
|
13765
|
+
const text = [
|
|
13766
|
+
`ARCHYMEDES ${options.version}`,
|
|
13767
|
+
"A place to think. The tools to build.",
|
|
13768
|
+
"",
|
|
13769
|
+
`${options.mode} ${glyphs.middot} ${options.workspace}`,
|
|
13770
|
+
options.model,
|
|
13771
|
+
`/help ${glyphs.middot} /guide ${glyphs.middot} /workspace`,
|
|
13772
|
+
""
|
|
13773
|
+
];
|
|
13774
|
+
const codes = [palette.primary + BOLD, palette.text, "", palette.secondary, palette.muted, palette.primary];
|
|
13775
|
+
const clean = (value) => value.replace(/[\x00-\x1f\x7f-\x9f]/g, " ");
|
|
13776
|
+
if (width < 64 || options.rows < 20) {
|
|
13777
|
+
const lines = [`${mark} ${text[0]}`, text[3], text[4]];
|
|
13778
|
+
return lines.map((line, index) => paint(clipTo(clean(line), width, glyphs), index === 0 ? codes[0] : palette.muted, palette.depth)).join(`
|
|
13779
|
+
`);
|
|
13780
|
+
}
|
|
13781
|
+
const geometry = renderGeometry2(options.angle, ascii);
|
|
13782
|
+
const artWidth = Math.max(...geometry.map(visibleWidth));
|
|
13783
|
+
const gap = 3;
|
|
13784
|
+
const margin = 2;
|
|
13785
|
+
const rainbow = palette.theme === "rainbow";
|
|
13786
|
+
const phase = (options.angle ?? 0) / (Math.PI * 2);
|
|
13787
|
+
const paintDrawing = (drawing) => rainbow ? rainbowText(drawing, palette.depth, phase) : paint(drawing, palette.primary, palette.depth);
|
|
13788
|
+
return geometry.map((line, index) => {
|
|
13789
|
+
const drawing = line + " ".repeat(artWidth - visibleWidth(line));
|
|
13790
|
+
return " ".repeat(margin) + paintDrawing(drawing) + " ".repeat(gap) + paint(clipTo(clean(text[index] ?? ""), width - margin - artWidth - gap, glyphs), codes[index] ?? "", palette.depth);
|
|
13791
|
+
}).join(`
|
|
13792
|
+
`);
|
|
13793
|
+
}
|
|
13794
|
+
|
|
13795
|
+
// packages/archymedes-cli/src/workspace-frame.ts
|
|
13796
|
+
function frameText(text, width) {
|
|
13797
|
+
let result = "";
|
|
13798
|
+
let columns = 0;
|
|
13799
|
+
const safe = text.replace(/\x1b\][^\x07]*(?:\x07|\x1b\\)/g, "").replace(/\x1b\[(?![0-9;]*m)[0-?]*[ -/]*[@-~]/g, "").replace(/\x1b[^\[]/g, "").replace(/[\x00-\x1f\x7f-\x9f]/g, (c) => c === "\x1B" ? c : c === "\t" ? " " : "");
|
|
13800
|
+
for (const part of safe.split(/(\x1b\[[0-9;]*m)/)) {
|
|
13801
|
+
if (/^\x1b\[[0-9;]*m$/.test(part)) {
|
|
13802
|
+
result += part;
|
|
13803
|
+
continue;
|
|
13804
|
+
}
|
|
13805
|
+
for (const { segment } of new Intl.Segmenter(undefined, { granularity: "grapheme" }).segment(part.replace(/\x1b/g, ""))) {
|
|
13806
|
+
if (columns + visibleWidth(segment) > width)
|
|
13807
|
+
return result + (result.includes("\x1B") ? "\x1B[0m" : "");
|
|
13808
|
+
result += segment;
|
|
13809
|
+
columns += visibleWidth(segment);
|
|
13810
|
+
}
|
|
13811
|
+
}
|
|
13812
|
+
return result + (result.includes("\x1B") ? "\x1B[0m" : "");
|
|
13813
|
+
}
|
|
13814
|
+
function workspaceHeader(context, width, phase = 0, position = "LIVE") {
|
|
13815
|
+
const { palette: p, glyphs: g } = context;
|
|
13816
|
+
const color = { plan: p.warning, build: p.primary, auto: p.success, defender: p.error }[context.mode] ?? p.primary;
|
|
13817
|
+
const ink = (s, c = p.muted) => paint(s, c, p.depth);
|
|
13818
|
+
const clean = (s) => s.replace(/[\x00-\x1f\x7f-\x9f]/g, " ");
|
|
13819
|
+
const ascii = g === ASCII_GLYPHS || g.boxHorizontal === "-";
|
|
13820
|
+
const pulse = context.busy || position === "MENU" || position.startsWith("HISTORY");
|
|
13821
|
+
const marks = ascii ? ["/\\", "<>", "\\/"] : ["◇", "◈", "◆", "◈"];
|
|
13822
|
+
const mark = pulse ? marks[phase % marks.length] : ascii ? "/\\" : "◇";
|
|
13823
|
+
const title = ` ${mark} ARCHYMEDES`;
|
|
13824
|
+
const tail = width >= 72 ? `v${context.version} ${position} ` : `${position} `;
|
|
13825
|
+
const room = Math.max(0, width - visibleWidth(title) - tail.length - 2);
|
|
13826
|
+
const workspace = clipTo(clean(context.workspace), room, g);
|
|
13827
|
+
const first = ink(title, p.primary + BOLD) + " " + ink(workspace, p.text) + " ".repeat(Math.max(1, width - visibleWidth(title) - 2 - visibleWidth(workspace) - tail.length)) + ink(tail, context.busy || position !== "LIVE" ? color + BOLD : p.muted);
|
|
13828
|
+
const modes = ["plan", "build", "auto", "defender"].map((mode) => mode === context.mode ? ink(` ${mode.toUpperCase()} `, color + BOLD + REVERSE) : ink(` ${mode} `, p.muted)).join(ink("│", p.muted));
|
|
13829
|
+
const second = width >= 48 ? ` ${modes}` : ` ${ink(`[${context.mode.toUpperCase()}]`, color + BOLD)}`;
|
|
13830
|
+
const hint = width >= 90 ? ` /mode choose Ctrl+G menu` : "";
|
|
13831
|
+
const model = clean(context.model);
|
|
13832
|
+
const navigation = width >= 72 ? "PgUp/PgDn history Esc live " : "";
|
|
13833
|
+
const modelWidth = Math.max(0, width - navigation.length - 3);
|
|
13834
|
+
const shownModel = clipTo(model, modelWidth, g);
|
|
13835
|
+
const third = ` ${ink(shownModel, p.secondary)}${" ".repeat(Math.max(1, width - visibleWidth(shownModel) - navigation.length - 1))}${ink(navigation)}`;
|
|
13836
|
+
const rule2 = ink(` ${g.boxHorizontal.repeat(Math.max(0, width - 2))} `, p.muted);
|
|
13837
|
+
return [frameText(first, width), frameText(second + ink(hint), width), frameText(third, width), frameText(rule2, width)];
|
|
13838
|
+
}
|
|
13839
|
+
|
|
13840
|
+
class WorkspaceFrame extends PinnedScreen {
|
|
13841
|
+
output;
|
|
13842
|
+
context;
|
|
13843
|
+
history;
|
|
13844
|
+
motion;
|
|
13845
|
+
active = false;
|
|
13846
|
+
timer;
|
|
13847
|
+
phase = 0;
|
|
13848
|
+
offset = 0;
|
|
13849
|
+
overlay;
|
|
13850
|
+
cachedLog;
|
|
13851
|
+
cachedSize = -1;
|
|
13852
|
+
cachedWidth = -1;
|
|
13853
|
+
cachedPending = "";
|
|
13854
|
+
lines = [];
|
|
13855
|
+
introLog;
|
|
13856
|
+
introSize = -1;
|
|
13857
|
+
introPending = "";
|
|
13858
|
+
suggestions = [];
|
|
13859
|
+
heldText;
|
|
13860
|
+
cachedHeldText;
|
|
13861
|
+
introMotion = true;
|
|
13862
|
+
constructor(output, context, history, motion = true) {
|
|
13863
|
+
super(output, { holdRegion: true, headerRows: 4 });
|
|
13864
|
+
this.output = output;
|
|
13865
|
+
this.context = context;
|
|
13866
|
+
this.history = history;
|
|
13867
|
+
this.motion = motion;
|
|
13868
|
+
}
|
|
13869
|
+
enter() {
|
|
13870
|
+
if (this.active)
|
|
13871
|
+
return;
|
|
13872
|
+
this.active = true;
|
|
13873
|
+
process.once("exit", this.restoreOnExit);
|
|
13874
|
+
process.once("SIGTERM", this.terminate);
|
|
13875
|
+
if (!this.introLog) {
|
|
13876
|
+
this.introLog = this.history();
|
|
13877
|
+
this.introSize = this.history().size;
|
|
13878
|
+
this.introPending = this.history().pending;
|
|
13879
|
+
}
|
|
13880
|
+
this.output.write(`${ENTER_ALTERNATE_SCREEN}\x1B[2J`);
|
|
13881
|
+
super.enter();
|
|
13882
|
+
this.refresh();
|
|
13883
|
+
if (this.motion) {
|
|
13884
|
+
this.timer = setInterval(() => {
|
|
13885
|
+
this.phase++;
|
|
13886
|
+
if (this.introMotion && this.showIntro() && !this.overlay)
|
|
13887
|
+
this.refresh();
|
|
13888
|
+
else if (this.context().busy || this.overlay || this.offset)
|
|
13889
|
+
this.drawHeader();
|
|
13890
|
+
}, 120);
|
|
13891
|
+
this.timer.unref();
|
|
13892
|
+
}
|
|
13893
|
+
}
|
|
13894
|
+
exit() {
|
|
13895
|
+
if (!this.active)
|
|
13896
|
+
return;
|
|
13897
|
+
clearInterval(this.timer);
|
|
13898
|
+
this.timer = undefined;
|
|
13899
|
+
this.active = false;
|
|
13900
|
+
process.off("exit", this.restoreOnExit);
|
|
13901
|
+
process.off("SIGTERM", this.terminate);
|
|
13902
|
+
this.output.write(`\x1B[r\x1B[?25h${LEAVE_ALTERNATE_SCREEN}`);
|
|
13903
|
+
}
|
|
13904
|
+
restoreOnExit = () => this.exit();
|
|
13905
|
+
terminate = () => {
|
|
13906
|
+
this.exit();
|
|
13907
|
+
process.exit(143);
|
|
13908
|
+
};
|
|
13909
|
+
resize() {
|
|
13910
|
+
this.suggestions = [];
|
|
13911
|
+
const layout = super.resize();
|
|
13912
|
+
if (this.active) {
|
|
13913
|
+
this.output.write("\x1B[2J");
|
|
13914
|
+
this.refresh();
|
|
13915
|
+
}
|
|
13916
|
+
return layout;
|
|
13917
|
+
}
|
|
13918
|
+
write(text) {
|
|
13919
|
+
if (!this.active || !this.offset && !this.overlay)
|
|
13920
|
+
this.output.write(text);
|
|
13921
|
+
else
|
|
13922
|
+
this.drawHeader();
|
|
13923
|
+
}
|
|
13924
|
+
renderStatus(text) {
|
|
13925
|
+
super.renderStatus(text);
|
|
13926
|
+
this.drawHeader();
|
|
13927
|
+
}
|
|
13928
|
+
drawHeader() {
|
|
13929
|
+
if (!this.active)
|
|
13930
|
+
return;
|
|
13931
|
+
const label = this.overlay ? "MENU" : this.offset ? `HISTORY -${this.offset}` : this.context().busy ? "WORKING" : "LIVE";
|
|
13932
|
+
const rows = workspaceHeader(this.context(), this.current.columns, this.phase, label).slice(0, this.current.scrollTop - 1);
|
|
13933
|
+
this.output.write(`${BEGIN_SYNC}\x1B7${rows.map((line, i) => `\x1B[${i + 1};1H\x1B[2K${line}`).join("")}\x1B8${END_SYNC}`);
|
|
13934
|
+
}
|
|
13935
|
+
projected() {
|
|
13936
|
+
const log = this.history();
|
|
13937
|
+
const width = Math.max(1, this.current.columns - 1);
|
|
13938
|
+
if (this.cachedLog === log && this.cachedSize === log.size + log.dropped && this.cachedPending === log.pending && this.cachedWidth === width && this.cachedHeldText === this.heldText)
|
|
13939
|
+
return this.lines;
|
|
13940
|
+
this.cachedLog = log;
|
|
13941
|
+
this.cachedSize = log.size + log.dropped;
|
|
13942
|
+
this.cachedPending = log.pending;
|
|
13943
|
+
this.cachedWidth = width;
|
|
13944
|
+
this.cachedHeldText = this.heldText;
|
|
13945
|
+
const plain = (this.heldText ?? [...log.lines, ...log.pending ? [log.pending] : []].join(`
|
|
13946
|
+
`)).replace(/\x1b\][^\x07]*(?:\x07|\x1b\\)/g, "").replace(/\x1b\[[0-?]*[ -/]*[@-~]|\x1b./g, "").split(`
|
|
13947
|
+
`).map((line) => line.replace(/^.*\r(?=.)/, "").replace(/\r/g, "")).join(`
|
|
13948
|
+
`);
|
|
13949
|
+
this.lines = [];
|
|
13950
|
+
for (const line of plain.split(`
|
|
13951
|
+
`)) {
|
|
13952
|
+
let row = "";
|
|
13953
|
+
let used = 0;
|
|
13954
|
+
for (const { segment } of new Intl.Segmenter(undefined, { granularity: "grapheme" }).segment(line)) {
|
|
13955
|
+
const size = visibleWidth(segment);
|
|
13956
|
+
if (used + size > width && row) {
|
|
13957
|
+
this.lines.push(row);
|
|
13958
|
+
row = "";
|
|
13959
|
+
used = 0;
|
|
13960
|
+
}
|
|
13961
|
+
row += size > width ? "?" : segment;
|
|
13962
|
+
used += Math.min(size, width);
|
|
13963
|
+
}
|
|
13964
|
+
this.lines.push(row);
|
|
13965
|
+
}
|
|
13966
|
+
return this.lines;
|
|
13967
|
+
}
|
|
13968
|
+
showIntro() {
|
|
13969
|
+
return this.history() === this.introLog && this.history().size === this.introSize && this.history().pending === this.introPending && !this.context().busy && !this.offset;
|
|
13970
|
+
}
|
|
13971
|
+
refresh() {
|
|
13972
|
+
if (!this.active)
|
|
13973
|
+
return;
|
|
13974
|
+
const height = this.current.scrollBottom - this.current.scrollTop + 1 - this.suggestions.length;
|
|
13975
|
+
let lines = this.projected();
|
|
13976
|
+
this.offset = Math.min(this.offset, Math.max(0, lines.length - height));
|
|
13977
|
+
if (!this.offset && this.heldText !== undefined) {
|
|
13978
|
+
this.heldText = undefined;
|
|
13979
|
+
lines = this.projected();
|
|
13980
|
+
}
|
|
13981
|
+
const end = Math.max(0, lines.length - this.offset);
|
|
13982
|
+
let body = lines.slice(Math.max(0, end - height), end);
|
|
13983
|
+
if (this.showIntro()) {
|
|
13984
|
+
const context = this.context();
|
|
13985
|
+
const warnings = lines.filter((line) => /No session spend cap|No price configured|No current.*rate/.test(line));
|
|
13986
|
+
body = renderIdentity2({
|
|
13987
|
+
...context,
|
|
13988
|
+
width: this.current.columns - 1,
|
|
13989
|
+
rows: height >= 15 ? 24 : 12,
|
|
13990
|
+
angle: this.phase * 0.065
|
|
13991
|
+
}).split(`
|
|
13992
|
+
`);
|
|
13993
|
+
body.push("", ...warnings.map((line) => paint(line, context.palette.warning, context.palette.depth)));
|
|
13994
|
+
body.push(paint(" /mode choose your tools /palette find any action", context.palette.muted, context.palette.depth));
|
|
13995
|
+
body = body.slice(0, height);
|
|
13996
|
+
}
|
|
13997
|
+
if (this.overlay !== undefined) {
|
|
13998
|
+
const { palette: p, glyphs: g } = this.context();
|
|
13999
|
+
const cardWidth = Math.max(1, Math.min(88, this.current.columns - 6));
|
|
14000
|
+
const inset = " ".repeat(Math.max(0, Math.floor((this.current.columns - cardWidth) / 2)));
|
|
14001
|
+
const border = (s) => paint(s, p.primary, p.depth);
|
|
14002
|
+
const muted = (s) => paint(s, p.muted, p.depth);
|
|
14003
|
+
const inner = Math.max(0, cardWidth - 4);
|
|
14004
|
+
const edge = g.boxHorizontal.repeat(Math.max(0, cardWidth - 2));
|
|
14005
|
+
const rows = this.overlay.split(`
|
|
14006
|
+
`).slice(0, Math.max(1, height - 4));
|
|
14007
|
+
body = rows.map((line) => {
|
|
14008
|
+
const text = frameText(line, inner);
|
|
14009
|
+
return `${inset}${border(g.boxVertical)} ${text}${" ".repeat(Math.max(0, inner - visibleWidth(text)))} ${border(g.boxVertical)}`;
|
|
14010
|
+
});
|
|
14011
|
+
if (height >= 4 && cardWidth >= 4) {
|
|
14012
|
+
body = [
|
|
14013
|
+
`${inset}${border(g.boxTopLeft)}${border(edge)}${border(g.boxTopRight)}`,
|
|
14014
|
+
...body,
|
|
14015
|
+
`${inset}${muted(g.boxBottomLeft)}${muted(edge)}${muted(g.boxBottomRight)}`
|
|
14016
|
+
];
|
|
14017
|
+
}
|
|
14018
|
+
const top = Math.max(0, Math.floor((height - body.length) / 3));
|
|
14019
|
+
body = [...Array(top).fill(""), ...body];
|
|
14020
|
+
} else
|
|
14021
|
+
body = [...Array(Math.max(0, Math.floor((height - body.length) * (this.showIntro() ? 0.45 : 1)))).fill(""), ...body];
|
|
14022
|
+
this.output.write(`${BEGIN_SYNC}\x1B7${Array.from({ length: height }, (_, i) => `\x1B[${this.current.scrollTop + i};1H\x1B[2K${frameText(body[i] ?? "", this.current.columns - 1)}`).join("")}\x1B8${END_SYNC}`);
|
|
14023
|
+
this.drawHeader();
|
|
14024
|
+
}
|
|
14025
|
+
renderSuggestions(lines) {
|
|
14026
|
+
this.suggestions = lines.slice(0, Math.max(0, Math.min(8, this.current.scrollBottom - this.current.scrollTop - 2)));
|
|
14027
|
+
super.renderSuggestions(lines);
|
|
14028
|
+
this.refresh();
|
|
14029
|
+
}
|
|
14030
|
+
clearSuggestions() {
|
|
14031
|
+
this.suggestions = [];
|
|
14032
|
+
super.clearSuggestions();
|
|
14033
|
+
this.refresh();
|
|
14034
|
+
}
|
|
14035
|
+
navigate(direction) {
|
|
14036
|
+
if (this.overlay)
|
|
14037
|
+
return;
|
|
14038
|
+
if (direction === "up" && !this.offset) {
|
|
14039
|
+
const log = this.history();
|
|
14040
|
+
this.heldText = [...log.lines, ...log.pending ? [log.pending] : []].join(`
|
|
14041
|
+
`);
|
|
14042
|
+
}
|
|
14043
|
+
const page = Math.max(1, this.current.scrollBottom - this.current.scrollTop - 1);
|
|
14044
|
+
this.offset = direction === "live" ? 0 : Math.max(0, this.offset + (direction === "up" ? page : -page));
|
|
14045
|
+
if (!this.offset)
|
|
14046
|
+
this.heldText = undefined;
|
|
14047
|
+
this.refresh();
|
|
14048
|
+
}
|
|
14049
|
+
get browsing() {
|
|
14050
|
+
return this.offset > 0;
|
|
14051
|
+
}
|
|
14052
|
+
stopIntroMotion() {
|
|
14053
|
+
this.introMotion = false;
|
|
14054
|
+
}
|
|
14055
|
+
menu = {
|
|
14056
|
+
paint: (frame) => {
|
|
14057
|
+
this.overlay = frame;
|
|
14058
|
+
this.refresh();
|
|
14059
|
+
},
|
|
14060
|
+
erase: () => {
|
|
14061
|
+
this.overlay = undefined;
|
|
14062
|
+
this.refresh();
|
|
14063
|
+
this.parkInTranscript();
|
|
14064
|
+
},
|
|
14065
|
+
height: () => Math.max(1, this.current.scrollBottom - this.current.scrollTop - 1),
|
|
14066
|
+
width: () => Math.max(1, Math.min(88, this.current.columns - 6) - 4)
|
|
14067
|
+
};
|
|
14068
|
+
}
|
|
14069
|
+
|
|
14070
|
+
// packages/archymedes-cli/src/model-picker.ts
|
|
14071
|
+
function initialSelection(rows, current) {
|
|
14072
|
+
const index = rows.findIndex((row) => row.kind === "model" && row.choice.provider === current.provider && row.choice.model === current.model);
|
|
14073
|
+
return index >= 0 ? index : 0;
|
|
14074
|
+
}
|
|
14075
|
+
function renderModelPicker(frame, options) {
|
|
14076
|
+
const { paint: paint3 } = options;
|
|
14077
|
+
const glyphs = options.glyphs ?? UNICODE_GLYPHS;
|
|
14078
|
+
const height = options.height ?? 10;
|
|
14079
|
+
const columns = terminalColumns(options.width);
|
|
14080
|
+
const start = windowStart(frame.selected, frame.rows.length, height);
|
|
14081
|
+
const visible = frame.rows.slice(start, start + height);
|
|
14082
|
+
const naturalWidth = Math.max(0, ...visible.map((row) => visibleWidth(row.kind === "model" ? row.choice.model : row.label)));
|
|
14083
|
+
const labelBudget = Math.max(0, Math.min(naturalWidth, Math.floor(Math.max(0, columns - 11) * 0.65)));
|
|
14084
|
+
const lines = [];
|
|
14085
|
+
for (const [offset, row] of visible.entries()) {
|
|
14086
|
+
if (row.header)
|
|
14087
|
+
lines.push(paint3.cyan(clipTo(` ${row.header}`, columns)));
|
|
14088
|
+
const active = start + offset === frame.selected;
|
|
14089
|
+
const cursor = active ? paint3.green(glyphs.prompt) : " ";
|
|
14090
|
+
const number = offset < 9 ? `${offset + 1}.` : " ";
|
|
14091
|
+
if (columns < 9) {
|
|
14092
|
+
const label = row.kind === "model" ? row.choice.model : row.label;
|
|
14093
|
+
lines.push(active ? paint3.green(clipTo(`${glyphs.prompt}${label}`, columns)) : clipTo(label, columns));
|
|
14094
|
+
continue;
|
|
14095
|
+
}
|
|
14096
|
+
if (row.kind === "settings") {
|
|
14097
|
+
lines.push(` ${cursor} ${paint3.dim(number)} ${paint3.yellow(clipTo(row.label, columns - 7))}`);
|
|
14098
|
+
continue;
|
|
14099
|
+
}
|
|
14100
|
+
const isCurrent = row.choice.provider === options.current.provider && row.choice.model === options.current.model;
|
|
14101
|
+
const tags = [row.choice.isProviderDefault ? "default" : "", isCurrent ? "current" : ""].filter(Boolean).join(", ");
|
|
14102
|
+
const shownModel = clipTo(row.choice.model, labelBudget);
|
|
14103
|
+
const padded = shownModel + " ".repeat(Math.max(0, labelBudget - visibleWidth(shownModel)));
|
|
14104
|
+
const tail = `${options.price(row.choice)}${tags ? ` (${tags})` : ""}`;
|
|
14105
|
+
const room = Math.max(0, columns - visibleWidth(` ${active ? glyphs.prompt : " "} ${number} ${isCurrent ? glyphs.circleFull : " "} ${padded} `));
|
|
14106
|
+
lines.push(` ${cursor} ${paint3.dim(number)} ${isCurrent ? paint3.green(glyphs.circleFull) : " "} ${padded} ${paint3.dim(clipTo(tail, room))}`);
|
|
14107
|
+
}
|
|
14108
|
+
lines.push(paint3.dim(clipTo(` ${glyphs.arrowUp}${glyphs.arrowDown} move ${glyphs.middot} Enter choose ${glyphs.middot} t table ${glyphs.middot} Esc cancel`, columns)));
|
|
14109
|
+
return lines.join(`
|
|
14110
|
+
`);
|
|
14111
|
+
}
|
|
14112
|
+
function advanceModelPicker(state, rows, input, options = {}) {
|
|
14113
|
+
const name = input.key.name;
|
|
14114
|
+
const last = Math.max(0, rows.length - 1);
|
|
14115
|
+
if (name === "escape" || input.key.ctrl && (name === "c" || name === "g"))
|
|
14116
|
+
return { state, done: {} };
|
|
14117
|
+
if (name === "return" || name === "enter") {
|
|
14118
|
+
const row = rows.length > 0 ? rows[Math.max(0, Math.min(state.selected, rows.length - 1))] : undefined;
|
|
14119
|
+
if (!row)
|
|
14120
|
+
return { state, done: {} };
|
|
14121
|
+
return { state, done: { result: row.kind === "model" ? { kind: "model", choice: row.choice } : { kind: "settings" } } };
|
|
14122
|
+
}
|
|
14123
|
+
if (name === "up" || input.key.ctrl && name === "p")
|
|
14124
|
+
return { state: { selected: Math.max(0, state.selected - 1) } };
|
|
14125
|
+
if (name === "down" || input.key.ctrl && name === "n")
|
|
14126
|
+
return { state: { selected: Math.min(last, state.selected + 1) } };
|
|
14127
|
+
if (name === "home")
|
|
14128
|
+
return { state: { selected: 0 } };
|
|
14129
|
+
if (name === "end")
|
|
14130
|
+
return { state: { selected: last } };
|
|
14131
|
+
if ((input.str === "t" || input.str === "T") && !input.key.ctrl && !input.key.meta) {
|
|
14132
|
+
return { state, done: { result: { kind: "table" } } };
|
|
14133
|
+
}
|
|
14134
|
+
if (input.str && /^[1-9]$/.test(input.str) && !input.key.ctrl && !input.key.meta) {
|
|
14135
|
+
const height = options.height ?? 10;
|
|
14136
|
+
const start = windowStart(state.selected, rows.length, height);
|
|
14137
|
+
const shown = Math.min(height, rows.length - start);
|
|
14138
|
+
const digit = Number(input.str);
|
|
14139
|
+
if (digit > shown)
|
|
14140
|
+
return { state };
|
|
14141
|
+
return { state: { selected: start + digit - 1 } };
|
|
14142
|
+
}
|
|
14143
|
+
return { state };
|
|
14144
|
+
}
|
|
14145
|
+
async function runModelPicker(keys, paint3, options) {
|
|
14146
|
+
const { rows } = options;
|
|
14147
|
+
let state = { selected: initialSelection(rows, options.current) };
|
|
14148
|
+
const liveOptions = () => {
|
|
14149
|
+
const size = options.getSize?.();
|
|
14150
|
+
const height = Math.max(1, Math.min(options.height ?? 10, size?.height ?? Number.POSITIVE_INFINITY));
|
|
14151
|
+
return { ...options, width: size?.width ?? options.width, height };
|
|
14152
|
+
};
|
|
14153
|
+
paint3(renderModelPicker({ rows, selected: state.selected }, liveOptions()));
|
|
14154
|
+
for await (const input of keys) {
|
|
14155
|
+
const current = liveOptions();
|
|
14156
|
+
const step = advanceModelPicker(state, rows, input, { height: current.height });
|
|
14157
|
+
state = step.state;
|
|
14158
|
+
if (step.done)
|
|
14159
|
+
return step.done.result;
|
|
14160
|
+
paint3(renderModelPicker({ rows, selected: state.selected }, liveOptions()));
|
|
14161
|
+
}
|
|
14162
|
+
return;
|
|
14163
|
+
}
|
|
14164
|
+
|
|
14165
|
+
// packages/archymedes-cli/src/shortcuts.ts
|
|
14166
|
+
function replaceLine(readline, text) {
|
|
14167
|
+
readline.write(null, { ctrl: true, name: "e" });
|
|
14168
|
+
readline.write(null, { ctrl: true, name: "u" });
|
|
14169
|
+
readline.write(text);
|
|
14170
|
+
}
|
|
14171
|
+
function submit(readline, command) {
|
|
14172
|
+
replaceLine(readline, command);
|
|
14173
|
+
if (!command.endsWith(" "))
|
|
14174
|
+
readline.write(null, { name: "return" });
|
|
14175
|
+
}
|
|
14176
|
+
function painter(output) {
|
|
14177
|
+
let rows = 0;
|
|
14178
|
+
const erase = () => {
|
|
14179
|
+
for (let row = 0;row < rows; row += 1)
|
|
14180
|
+
output.write("\x1B[1A\x1B[2K");
|
|
14181
|
+
if (rows > 0)
|
|
14182
|
+
output.write("\r");
|
|
14183
|
+
rows = 0;
|
|
14184
|
+
};
|
|
14185
|
+
return {
|
|
14186
|
+
erase,
|
|
14187
|
+
paint: (frame) => {
|
|
14188
|
+
erase();
|
|
14189
|
+
output.write(`${frame}
|
|
14190
|
+
`);
|
|
14191
|
+
const columns = output.columns ?? 80;
|
|
14192
|
+
rows = frame.split(`
|
|
14193
|
+
`).reduce((total, line) => total + rowsOccupied(line, columns), 0);
|
|
14194
|
+
}
|
|
14195
|
+
};
|
|
14196
|
+
}
|
|
14197
|
+
var workspaceMenu;
|
|
14198
|
+
function menuRows(host) {
|
|
14199
|
+
return host.output === process.stdout && workspaceMenu ? workspaceMenu.height() : host.output.rows ?? 24;
|
|
14200
|
+
}
|
|
14201
|
+
function menuColumns(host) {
|
|
14202
|
+
return host.output === process.stdout && workspaceMenu ? workspaceMenu.width() : host.output.columns ?? 80;
|
|
14203
|
+
}
|
|
14204
|
+
function setWorkspaceMenu(surface) {
|
|
14205
|
+
workspaceMenu = surface;
|
|
14206
|
+
}
|
|
14207
|
+
async function withBorrowedKeyboard(host, self, body, surface = (host.output === process.stdout ? workspaceMenu : undefined) ?? painter(host.output)) {
|
|
14208
|
+
const borrowed = host.input.listeners("keypress").filter((listener) => listener !== self);
|
|
14209
|
+
for (const listener of borrowed)
|
|
14210
|
+
host.input.off("keypress", listener);
|
|
14211
|
+
const pending = [];
|
|
14212
|
+
let wake;
|
|
14213
|
+
const collect = (str, key) => {
|
|
14214
|
+
pending.push({ ...str === undefined ? {} : { str }, key: key ?? {} });
|
|
14215
|
+
wake?.();
|
|
14216
|
+
};
|
|
14217
|
+
host.input.on("keypress", collect);
|
|
14218
|
+
const onResize = () => {
|
|
14219
|
+
pending.push({ key: {} });
|
|
14220
|
+
wake?.();
|
|
14221
|
+
};
|
|
14222
|
+
host.output.on("resize", onResize);
|
|
14223
|
+
async function* keys() {
|
|
14224
|
+
for (;; ) {
|
|
14225
|
+
while (pending.length > 0)
|
|
14226
|
+
yield pending.shift();
|
|
14227
|
+
await new Promise((resolve) => {
|
|
14228
|
+
wake = resolve;
|
|
14229
|
+
});
|
|
14230
|
+
wake = undefined;
|
|
14231
|
+
}
|
|
14232
|
+
}
|
|
14233
|
+
try {
|
|
14234
|
+
return await body(keys(), surface.paint);
|
|
14235
|
+
} finally {
|
|
14236
|
+
surface.erase();
|
|
14237
|
+
host.output.off("resize", onResize);
|
|
14238
|
+
host.input.off("keypress", collect);
|
|
14239
|
+
for (const listener of borrowed)
|
|
14240
|
+
host.input.on("keypress", listener);
|
|
14241
|
+
}
|
|
14242
|
+
}
|
|
14243
|
+
async function openPalette(host, self, options = {}) {
|
|
14244
|
+
const chords = Object.fromEntries(host.registry.bindings.map((binding) => [binding.command, formatBinding(binding.chord)]));
|
|
14245
|
+
const sized = {
|
|
14246
|
+
width: host.output.columns ?? 80,
|
|
14247
|
+
...options,
|
|
14248
|
+
getSize: options.getSize ?? (() => ({ width: menuColumns(host), height: Math.max(1, menuRows(host) - 3) }))
|
|
14249
|
+
};
|
|
14250
|
+
return withBorrowedKeyboard(host, self, (keys, paint3) => runCommandPalette(keys, paletteEntries(chords), paint3, sized));
|
|
14251
|
+
}
|
|
14252
|
+
async function openDefenderTriage(host, findings, options, self) {
|
|
14253
|
+
const sized = {
|
|
14254
|
+
width: options.width ?? host.output.columns ?? 80,
|
|
14255
|
+
rows: options.rows ?? Math.max(12, (host.output.rows ?? 24) - 2),
|
|
14256
|
+
...options,
|
|
14257
|
+
getSize: options.getSize ?? (() => ({ width: host.output.columns ?? 80, height: Math.max(12, (host.output.rows ?? 24) - 2) }))
|
|
14258
|
+
};
|
|
14259
|
+
return withBorrowedKeyboard(host, self, (keys, paint3) => runDefenderTriage(keys, findings, paint3, sized));
|
|
14260
|
+
}
|
|
14261
|
+
async function openChooser(host, items, options, self) {
|
|
14262
|
+
const sized = {
|
|
14263
|
+
width: host.output.columns ?? 80,
|
|
14264
|
+
...options,
|
|
14265
|
+
getSize: options.getSize ?? (() => ({ width: menuColumns(host), height: Math.max(1, menuRows(host) - 4) }))
|
|
14266
|
+
};
|
|
14267
|
+
return withBorrowedKeyboard(host, self, (keys, paint3) => runChooser(keys, items, paint3, sized));
|
|
14268
|
+
}
|
|
14269
|
+
async function openTable(host, options, self) {
|
|
14270
|
+
const sized = {
|
|
14271
|
+
width: host.output.columns ?? 80,
|
|
14272
|
+
...options,
|
|
14273
|
+
getSize: options.getSize ?? (() => ({ width: menuColumns(host), height: Math.max(1, menuRows(host) - 2) }))
|
|
14274
|
+
};
|
|
14275
|
+
return withBorrowedKeyboard(host, self, (keys, paint3) => runTable(keys, paint3, sized));
|
|
14276
|
+
}
|
|
14277
|
+
async function openModelPicker(host, options, self) {
|
|
14278
|
+
const sized = {
|
|
14279
|
+
width: host.output.columns ?? 80,
|
|
14280
|
+
...options,
|
|
14281
|
+
getSize: options.getSize ?? (() => ({ width: menuColumns(host), height: Math.max(1, menuRows(host) - 4) }))
|
|
14282
|
+
};
|
|
14283
|
+
return withBorrowedKeyboard(host, self, (keys, paint3) => runModelPicker(keys, paint3, sized));
|
|
14284
|
+
}
|
|
14285
|
+
function formatBinding(chord) {
|
|
14286
|
+
const key = /^f\d+$/.test(chord.key) ? chord.key.toUpperCase() : chord.key.toUpperCase();
|
|
14287
|
+
return `${chord.ctrl ? "Ctrl+" : ""}${chord.meta ? "Alt+" : ""}${chord.shift ? "Shift+" : ""}${key}`;
|
|
14288
|
+
}
|
|
14289
|
+
function installShortcuts(host) {
|
|
14290
|
+
let busy = false;
|
|
14291
|
+
const run = (options) => {
|
|
14292
|
+
busy = true;
|
|
14293
|
+
openPalette(host, onKeypress, options).then((chosen) => {
|
|
14294
|
+
if (chosen)
|
|
14295
|
+
submit(host.readline, chosen);
|
|
14296
|
+
}).finally(() => {
|
|
14297
|
+
busy = false;
|
|
14298
|
+
host.readline.prompt(true);
|
|
14299
|
+
});
|
|
14300
|
+
};
|
|
14301
|
+
const onKeypress = (str, key) => {
|
|
14302
|
+
if (busy || !key)
|
|
14303
|
+
return;
|
|
14304
|
+
const command = host.registry.match(key);
|
|
14305
|
+
if (!command)
|
|
14306
|
+
return;
|
|
14307
|
+
if (host.onIntercept?.(command))
|
|
14308
|
+
return;
|
|
14309
|
+
if (command !== "/palette") {
|
|
14310
|
+
submit(host.readline, command);
|
|
14311
|
+
return;
|
|
14312
|
+
}
|
|
14313
|
+
run({ direction: "up" });
|
|
14314
|
+
};
|
|
14315
|
+
host.input.on("keypress", onKeypress);
|
|
14316
|
+
return () => host.input.off("keypress", onKeypress);
|
|
14317
|
+
}
|
|
14318
|
+
|
|
14319
|
+
// packages/archymedes-cli/src/tui.ts
|
|
14320
|
+
var RESET3 = "\x1B[0m";
|
|
14321
|
+
var DIM2 = "\x1B[2m";
|
|
14322
|
+
var CYAN2 = "\x1B[36m";
|
|
13759
14323
|
var GREEN2 = "\x1B[32m";
|
|
13760
14324
|
var YELLOW2 = "\x1B[33m";
|
|
13761
14325
|
var RED2 = "\x1B[31m";
|
|
@@ -14299,14 +14863,21 @@ function box2(lines, options) {
|
|
|
14299
14863
|
const border = borderGlyphsFor(options.borderStyle ?? "round", glyphs);
|
|
14300
14864
|
const titleWidth = options.title ? visibleWidth(options.title) : 0;
|
|
14301
14865
|
const titlePaint = TONE_CODES[options.titleColor ?? "cyan"];
|
|
14302
|
-
const
|
|
14866
|
+
const rainbowBorder = options.borderColor === "rainbow";
|
|
14867
|
+
const edge = (text, phase = 0) => {
|
|
14868
|
+
if (options.borderColor === undefined)
|
|
14869
|
+
return text;
|
|
14870
|
+
return rainbowBorder ? rainbowText(text, options.depth, phase) : paint3(text, TONE_CODES[options.borderColor], options.depth);
|
|
14871
|
+
};
|
|
14303
14872
|
const contentWidth = Math.min(Math.max(titleWidth, ...lines.map((line) => visibleWidth(line)), 1), Math.max(1, terminalWidth - 4));
|
|
14304
14873
|
const horizontal = border.horizontal.repeat(contentWidth + 2);
|
|
14305
|
-
const top = options.title ? `${edge(`${border.topLeft}${border.horizontal}
|
|
14306
|
-
const
|
|
14307
|
-
const
|
|
14874
|
+
const top = options.title ? `${edge(`${border.topLeft}${border.horizontal}`, 0)} ${paint3(options.title, titlePaint, options.depth)} ${edge(`${border.horizontal.repeat(Math.max(0, contentWidth - titleWidth - 1))}${border.topRight}`, 0.08)}` : edge(`${border.topLeft}${horizontal}${border.topRight}`, 0);
|
|
14875
|
+
const bottomPhase = (lines.length + 1) * 0.1;
|
|
14876
|
+
const bottom = edge(`${border.bottomLeft}${horizontal}${border.bottomRight}`, bottomPhase);
|
|
14877
|
+
const body = lines.map((line, index) => {
|
|
14308
14878
|
const clipped = visibleWidth(line) > contentWidth ? `${sliceToWidth2(line, contentWidth - visibleWidth(glyphs.ellipsis))}${glyphs.ellipsis}` : line;
|
|
14309
|
-
|
|
14879
|
+
const rowPhase = (index + 1) * 0.1;
|
|
14880
|
+
return `${edge(border.vertical, rowPhase)} ${clipped}${" ".repeat(Math.max(0, contentWidth - visibleWidth(clipped)))} ${edge(border.vertical, rowPhase)}`;
|
|
14310
14881
|
});
|
|
14311
14882
|
return [top, ...body, bottom].join(`
|
|
14312
14883
|
`);
|
|
@@ -14793,31 +15364,8 @@ function renderMarkdown2(text, options) {
|
|
|
14793
15364
|
`);
|
|
14794
15365
|
}
|
|
14795
15366
|
|
|
14796
|
-
// packages/archymedes-cli/src/layout.ts
|
|
14797
|
-
var GOLDEN_RATIO = 1.618033988749895;
|
|
14798
|
-
var MAX_CONTENT_WIDTH = Math.round(80 * GOLDEN_RATIO);
|
|
14799
|
-
var FOOTER_ROWS = 3;
|
|
14800
|
-
var MIN_TRANSCRIPT_ROWS = 3;
|
|
14801
|
-
function computeLayout(rows, columns) {
|
|
14802
|
-
const safeRows = Math.max(1, Math.floor(rows));
|
|
14803
|
-
const safeColumns = Math.max(1, Math.floor(columns));
|
|
14804
|
-
const footerRows = Math.min(FOOTER_ROWS, Math.max(0, safeRows - MIN_TRANSCRIPT_ROWS));
|
|
14805
|
-
const scrollBottom = safeRows - footerRows;
|
|
14806
|
-
return {
|
|
14807
|
-
rows: safeRows,
|
|
14808
|
-
columns: safeColumns,
|
|
14809
|
-
scrollTop: 1,
|
|
14810
|
-
scrollBottom,
|
|
14811
|
-
footerRows,
|
|
14812
|
-
statusRow: footerRows >= 2 ? scrollBottom + 1 : undefined,
|
|
14813
|
-
inputRow: footerRows >= 2 ? scrollBottom + 2 : footerRows === 1 ? scrollBottom + 1 : undefined,
|
|
14814
|
-
promptBottomRow: footerRows >= 3 ? scrollBottom + 3 : undefined,
|
|
14815
|
-
contentWidth: Math.min(safeColumns, MAX_CONTENT_WIDTH)
|
|
14816
|
-
};
|
|
14817
|
-
}
|
|
14818
|
-
|
|
14819
15367
|
// packages/archymedes-cli/src/screen.ts
|
|
14820
|
-
class
|
|
15368
|
+
class PinnedScreen2 {
|
|
14821
15369
|
stream;
|
|
14822
15370
|
layout;
|
|
14823
15371
|
suggestionRows = 0;
|
|
@@ -14827,6 +15375,12 @@ class PinnedScreen {
|
|
|
14827
15375
|
this.stream = stream;
|
|
14828
15376
|
this.layout = computeLayout(stream.rows ?? 24, stream.columns ?? 80);
|
|
14829
15377
|
this.holdRegion = options.holdRegion ?? true;
|
|
15378
|
+
this.headerRows = options.headerRows ?? 0;
|
|
15379
|
+
this.reserveHeader();
|
|
15380
|
+
}
|
|
15381
|
+
headerRows;
|
|
15382
|
+
reserveHeader() {
|
|
15383
|
+
this.layout.scrollTop = 1 + Math.min(this.headerRows, Math.max(0, this.layout.scrollBottom - 3));
|
|
14830
15384
|
}
|
|
14831
15385
|
get pinned() {
|
|
14832
15386
|
return this.holdRegion;
|
|
@@ -14843,6 +15397,7 @@ class PinnedScreen {
|
|
|
14843
15397
|
resize() {
|
|
14844
15398
|
this.suggestionRows = 0;
|
|
14845
15399
|
this.layout = computeLayout(this.stream.rows ?? 24, this.stream.columns ?? 80);
|
|
15400
|
+
this.reserveHeader();
|
|
14846
15401
|
if (this.holdRegion) {
|
|
14847
15402
|
this.setRegion();
|
|
14848
15403
|
this.parkInTranscript();
|
|
@@ -14930,7 +15485,7 @@ class PinnedScreen {
|
|
|
14930
15485
|
return this.layout.scrollBottom - this.suggestionRows;
|
|
14931
15486
|
}
|
|
14932
15487
|
maxSuggestionRows() {
|
|
14933
|
-
return Math.max(0, Math.min(8, this.layout.scrollBottom -
|
|
15488
|
+
return Math.max(0, Math.min(8, this.layout.scrollBottom - this.layout.scrollTop - 2));
|
|
14934
15489
|
}
|
|
14935
15490
|
exit() {
|
|
14936
15491
|
this.stream.write("\x1B[r");
|
|
@@ -15171,6 +15726,7 @@ function defineCommands(table3) {
|
|
|
15171
15726
|
return Object.entries(table3).map(([name, command]) => ({ name, ...command }));
|
|
15172
15727
|
}
|
|
15173
15728
|
var COMMANDS2 = defineCommands({
|
|
15729
|
+
"/layout": { args: "[fixed|scrollback]", description: "Toggle the fixed workspace or terminal scrollback layout" },
|
|
15174
15730
|
"/mode": { args: "[plan|build|auto|defender]", description: "Show or switch the permission mode" },
|
|
15175
15731
|
"/plan": { description: "Switch to plan mode — read and reason, no writes" },
|
|
15176
15732
|
"/build": { description: "Switch to build mode — edits need approval" },
|
|
@@ -15194,7 +15750,7 @@ var COMMANDS2 = defineCommands({
|
|
|
15194
15750
|
"/slow": { args: "[on|strict|off]", description: "Spend at a slower pace: fewer model rounds, smaller replies, a pause between turns" },
|
|
15195
15751
|
"/workspace": { description: "Open the control panel: every tab and watched job, live, on one screen" },
|
|
15196
15752
|
"/watch": { args: "[id|show <id>|stop <id>]", description: "Follow a background job's output without giving up the prompt" },
|
|
15197
|
-
"/theme": { args: "[name|list|where]", description: "Change the colours — blueprint, parchment, chalkboard, high-contrast, or a .tss file of your own" },
|
|
15753
|
+
"/theme": { args: "[name|list|where]", description: "Change the colours — blueprint, parchment, chalkboard, high-contrast, rainbow, or a .tss file of your own" },
|
|
15198
15754
|
"/tab": { args: "[new|next|prev|close|rename|N]", description: "Several pieces of work, one at a time — only the tab in front runs (/detach for parallel)" },
|
|
15199
15755
|
"/pull": { args: "[dir]", description: "Copy sandbox files here" },
|
|
15200
15756
|
"/where": { description: "Show the current workspace" },
|
|
@@ -15216,6 +15772,7 @@ var COMMANDS2 = defineCommands({
|
|
|
15216
15772
|
"/guide": { args: "[topic|search <text>|all]", description: "The user guide, as a screen you can browse — or print one topic with /guide <topic>" },
|
|
15217
15773
|
"/files": { description: "Browse the project tree — expand folders, preview a file, pick one to @mention" },
|
|
15218
15774
|
"/edit": { args: "<path>", description: "Open a file in the built-in editor — through the workspace, so it works in a sandbox too" },
|
|
15775
|
+
"/cat": { args: "<path>", description: "Print a file into the transcript — markdown rendered like a document, code numbered and highlighted" },
|
|
15219
15776
|
"/help": { args: "[all|work|review|steer|parallel|learn|setup]", description: "Commands grouped by purpose" },
|
|
15220
15777
|
"/exit": { description: "Leave" }
|
|
15221
15778
|
});
|
|
@@ -15506,323 +16063,90 @@ function resolveBindings(overrides = {}, options = {}) {
|
|
|
15506
16063
|
const editing = RESERVED_CHORDS[id];
|
|
15507
16064
|
if (editing && !RELOCATED_EDITING[id]) {
|
|
15508
16065
|
conflicts.push({ command, chord: formatChord(chord), reason: `reserved for line editing (${editing})` });
|
|
15509
|
-
continue;
|
|
15510
|
-
}
|
|
15511
|
-
const owner = taken.get(id);
|
|
15512
|
-
if (owner) {
|
|
15513
|
-
conflicts.push({ command, chord: formatChord(chord), reason: `already bound to ${owner}` });
|
|
15514
|
-
continue;
|
|
15515
|
-
}
|
|
15516
|
-
const relocation = RELOCATED_EDITING[id];
|
|
15517
|
-
if (relocation)
|
|
15518
|
-
displaced.push({ chord: formatChord(chord), command, ...relocation });
|
|
15519
|
-
taken.set(id, command);
|
|
15520
|
-
bindings.push({ command, chord, description });
|
|
15521
|
-
}
|
|
15522
|
-
}
|
|
15523
|
-
return { bindings, conflicts, displaced };
|
|
15524
|
-
}
|
|
15525
|
-
function isLikelyDeliverable(chord, environment = {}) {
|
|
15526
|
-
if (environment.TERM === "dumb")
|
|
15527
|
-
return false;
|
|
15528
|
-
if (chord.key === "tab" && (chord.ctrl || chord.shift))
|
|
15529
|
-
return false;
|
|
15530
|
-
if (FUNCTION_KEY.test(chord.key))
|
|
15531
|
-
return !environment.TMUX;
|
|
15532
|
-
if (chord.meta)
|
|
15533
|
-
return !environment.TMUX;
|
|
15534
|
-
return true;
|
|
15535
|
-
}
|
|
15536
|
-
|
|
15537
|
-
class KeyBindingRegistry {
|
|
15538
|
-
environment;
|
|
15539
|
-
byChord = new Map;
|
|
15540
|
-
conflicts;
|
|
15541
|
-
displaced;
|
|
15542
|
-
constructor(overrides = {}, environment = {}) {
|
|
15543
|
-
this.environment = environment;
|
|
15544
|
-
const resolved = resolveBindings(overrides);
|
|
15545
|
-
this.conflicts = resolved.conflicts;
|
|
15546
|
-
this.displaced = resolved.displaced;
|
|
15547
|
-
for (const binding of resolved.bindings)
|
|
15548
|
-
this.byChord.set(chordId(binding.chord), binding);
|
|
15549
|
-
}
|
|
15550
|
-
match(key) {
|
|
15551
|
-
const chord = chordFromKeypress(key);
|
|
15552
|
-
return chord ? this.byChord.get(chordId(chord))?.command : undefined;
|
|
15553
|
-
}
|
|
15554
|
-
get bindings() {
|
|
15555
|
-
return [...this.byChord.values()];
|
|
15556
|
-
}
|
|
15557
|
-
groupedByCommand() {
|
|
15558
|
-
const byCommand = new Map;
|
|
15559
|
-
for (const binding of this.bindings) {
|
|
15560
|
-
const entry = byCommand.get(binding.command) ?? { chords: [], description: binding.description };
|
|
15561
|
-
entry.chords.push(binding.chord);
|
|
15562
|
-
byCommand.set(binding.command, entry);
|
|
15563
|
-
}
|
|
15564
|
-
return byCommand;
|
|
15565
|
-
}
|
|
15566
|
-
shortcutLabels() {
|
|
15567
|
-
return new Map([...this.groupedByCommand()].map(([command, { chords }]) => [command, chords.map(formatChord).join(", ")]));
|
|
15568
|
-
}
|
|
15569
|
-
render() {
|
|
15570
|
-
const rows = [...this.groupedByCommand()].map(([command, { chords, description }]) => {
|
|
15571
|
-
const deliverable = chords.some((chord) => isLikelyDeliverable(chord, this.environment));
|
|
15572
|
-
return [
|
|
15573
|
-
chords.map(formatChord).join(", "),
|
|
15574
|
-
deliverable ? `${description} (${command})` : `${description} — this terminal may not send it; use ${command}`
|
|
15575
|
-
];
|
|
15576
|
-
});
|
|
15577
|
-
const width = Math.max(16, ...rows.map(([key]) => key.length));
|
|
15578
|
-
const lines = ["Feature keys", ...rows.map(([key, label]) => ` ${key.padEnd(width + 2)}${label}`)];
|
|
15579
|
-
if (this.displaced.length > 0) {
|
|
15580
|
-
lines.push("", "Line editing that moved");
|
|
15581
|
-
for (const moved of this.displaced) {
|
|
15582
|
-
lines.push(` ${moved.instead.padEnd(width + 2)}${moved.lost} — ${moved.chord} runs ${moved.command} now`);
|
|
15583
|
-
}
|
|
15584
|
-
}
|
|
15585
|
-
if (this.conflicts.length > 0) {
|
|
15586
|
-
lines.push("", "Not bound");
|
|
15587
|
-
for (const conflict of this.conflicts)
|
|
15588
|
-
lines.push(` ${conflict.chord.padEnd(width + 2)}${conflict.command} — ${conflict.reason}`);
|
|
15589
|
-
}
|
|
15590
|
-
return lines.join(`
|
|
15591
|
-
`);
|
|
15592
|
-
}
|
|
15593
|
-
}
|
|
15594
|
-
|
|
15595
|
-
// packages/archymedes-cli/src/model-picker.ts
|
|
15596
|
-
function initialSelection(rows, current) {
|
|
15597
|
-
const index = rows.findIndex((row) => row.kind === "model" && row.choice.provider === current.provider && row.choice.model === current.model);
|
|
15598
|
-
return index >= 0 ? index : 0;
|
|
15599
|
-
}
|
|
15600
|
-
function renderModelPicker(frame, options) {
|
|
15601
|
-
const { paint: paint5 } = options;
|
|
15602
|
-
const glyphs = options.glyphs ?? UNICODE_GLYPHS;
|
|
15603
|
-
const height = options.height ?? 10;
|
|
15604
|
-
const columns = terminalColumns(options.width);
|
|
15605
|
-
const start = windowStart(frame.selected, frame.rows.length, height);
|
|
15606
|
-
const visible = frame.rows.slice(start, start + height);
|
|
15607
|
-
const naturalWidth = Math.max(0, ...visible.map((row) => visibleWidth(row.kind === "model" ? row.choice.model : row.label)));
|
|
15608
|
-
const labelBudget = Math.max(0, Math.min(naturalWidth, Math.floor(Math.max(0, columns - 11) * 0.65)));
|
|
15609
|
-
const lines = [];
|
|
15610
|
-
for (const [offset, row] of visible.entries()) {
|
|
15611
|
-
if (row.header)
|
|
15612
|
-
lines.push(paint5.cyan(clipTo(` ${row.header}`, columns)));
|
|
15613
|
-
const active = start + offset === frame.selected;
|
|
15614
|
-
const cursor = active ? paint5.green(glyphs.prompt) : " ";
|
|
15615
|
-
const number = offset < 9 ? `${offset + 1}.` : " ";
|
|
15616
|
-
if (columns < 9) {
|
|
15617
|
-
const label = row.kind === "model" ? row.choice.model : row.label;
|
|
15618
|
-
lines.push(active ? paint5.green(clipTo(`${glyphs.prompt}${label}`, columns)) : clipTo(label, columns));
|
|
15619
|
-
continue;
|
|
15620
|
-
}
|
|
15621
|
-
if (row.kind === "settings") {
|
|
15622
|
-
lines.push(` ${cursor} ${paint5.dim(number)} ${paint5.yellow(clipTo(row.label, columns - 7))}`);
|
|
15623
|
-
continue;
|
|
15624
|
-
}
|
|
15625
|
-
const isCurrent = row.choice.provider === options.current.provider && row.choice.model === options.current.model;
|
|
15626
|
-
const tags = [row.choice.isProviderDefault ? "default" : "", isCurrent ? "current" : ""].filter(Boolean).join(", ");
|
|
15627
|
-
const shownModel = clipTo(row.choice.model, labelBudget);
|
|
15628
|
-
const padded = shownModel + " ".repeat(Math.max(0, labelBudget - visibleWidth(shownModel)));
|
|
15629
|
-
const tail = `${options.price(row.choice)}${tags ? ` (${tags})` : ""}`;
|
|
15630
|
-
const room = Math.max(0, columns - visibleWidth(` ${active ? glyphs.prompt : " "} ${number} ${isCurrent ? glyphs.circleFull : " "} ${padded} `));
|
|
15631
|
-
lines.push(` ${cursor} ${paint5.dim(number)} ${isCurrent ? paint5.green(glyphs.circleFull) : " "} ${padded} ${paint5.dim(clipTo(tail, room))}`);
|
|
15632
|
-
}
|
|
15633
|
-
lines.push(paint5.dim(clipTo(` ${glyphs.arrowUp}${glyphs.arrowDown} move ${glyphs.middot} Enter choose ${glyphs.middot} t table ${glyphs.middot} Esc cancel`, columns)));
|
|
15634
|
-
return lines.join(`
|
|
15635
|
-
`);
|
|
15636
|
-
}
|
|
15637
|
-
function advanceModelPicker(state, rows, input, options = {}) {
|
|
15638
|
-
const name = input.key.name;
|
|
15639
|
-
const last = Math.max(0, rows.length - 1);
|
|
15640
|
-
if (name === "escape" || input.key.ctrl && (name === "c" || name === "g"))
|
|
15641
|
-
return { state, done: {} };
|
|
15642
|
-
if (name === "return" || name === "enter") {
|
|
15643
|
-
const row = rows.length > 0 ? rows[Math.max(0, Math.min(state.selected, rows.length - 1))] : undefined;
|
|
15644
|
-
if (!row)
|
|
15645
|
-
return { state, done: {} };
|
|
15646
|
-
return { state, done: { result: row.kind === "model" ? { kind: "model", choice: row.choice } : { kind: "settings" } } };
|
|
15647
|
-
}
|
|
15648
|
-
if (name === "up" || input.key.ctrl && name === "p")
|
|
15649
|
-
return { state: { selected: Math.max(0, state.selected - 1) } };
|
|
15650
|
-
if (name === "down" || input.key.ctrl && name === "n")
|
|
15651
|
-
return { state: { selected: Math.min(last, state.selected + 1) } };
|
|
15652
|
-
if (name === "home")
|
|
15653
|
-
return { state: { selected: 0 } };
|
|
15654
|
-
if (name === "end")
|
|
15655
|
-
return { state: { selected: last } };
|
|
15656
|
-
if ((input.str === "t" || input.str === "T") && !input.key.ctrl && !input.key.meta) {
|
|
15657
|
-
return { state, done: { result: { kind: "table" } } };
|
|
15658
|
-
}
|
|
15659
|
-
if (input.str && /^[1-9]$/.test(input.str) && !input.key.ctrl && !input.key.meta) {
|
|
15660
|
-
const height = options.height ?? 10;
|
|
15661
|
-
const start = windowStart(state.selected, rows.length, height);
|
|
15662
|
-
const shown = Math.min(height, rows.length - start);
|
|
15663
|
-
const digit = Number(input.str);
|
|
15664
|
-
if (digit > shown)
|
|
15665
|
-
return { state };
|
|
15666
|
-
return { state: { selected: start + digit - 1 } };
|
|
15667
|
-
}
|
|
15668
|
-
return { state };
|
|
15669
|
-
}
|
|
15670
|
-
async function runModelPicker(keys, paint5, options) {
|
|
15671
|
-
const { rows } = options;
|
|
15672
|
-
let state = { selected: initialSelection(rows, options.current) };
|
|
15673
|
-
const liveOptions = () => {
|
|
15674
|
-
const size = options.getSize?.();
|
|
15675
|
-
const height = Math.max(1, Math.min(options.height ?? 10, size?.height ?? Number.POSITIVE_INFINITY));
|
|
15676
|
-
return { ...options, width: size?.width ?? options.width, height };
|
|
15677
|
-
};
|
|
15678
|
-
paint5(renderModelPicker({ rows, selected: state.selected }, liveOptions()));
|
|
15679
|
-
for await (const input of keys) {
|
|
15680
|
-
const current = liveOptions();
|
|
15681
|
-
const step = advanceModelPicker(state, rows, input, { height: current.height });
|
|
15682
|
-
state = step.state;
|
|
15683
|
-
if (step.done)
|
|
15684
|
-
return step.done.result;
|
|
15685
|
-
paint5(renderModelPicker({ rows, selected: state.selected }, liveOptions()));
|
|
15686
|
-
}
|
|
15687
|
-
return;
|
|
15688
|
-
}
|
|
15689
|
-
|
|
15690
|
-
// packages/archymedes-cli/src/shortcuts.ts
|
|
15691
|
-
function replaceLine(readline, text) {
|
|
15692
|
-
readline.write(null, { ctrl: true, name: "e" });
|
|
15693
|
-
readline.write(null, { ctrl: true, name: "u" });
|
|
15694
|
-
readline.write(text);
|
|
15695
|
-
}
|
|
15696
|
-
function submit(readline, command) {
|
|
15697
|
-
replaceLine(readline, command);
|
|
15698
|
-
if (!command.endsWith(" "))
|
|
15699
|
-
readline.write(null, { name: "return" });
|
|
15700
|
-
}
|
|
15701
|
-
function painter(output) {
|
|
15702
|
-
let rows = 0;
|
|
15703
|
-
const erase = () => {
|
|
15704
|
-
for (let row = 0;row < rows; row += 1)
|
|
15705
|
-
output.write("\x1B[1A\x1B[2K");
|
|
15706
|
-
if (rows > 0)
|
|
15707
|
-
output.write("\r");
|
|
15708
|
-
rows = 0;
|
|
15709
|
-
};
|
|
15710
|
-
return {
|
|
15711
|
-
erase,
|
|
15712
|
-
paint: (frame) => {
|
|
15713
|
-
erase();
|
|
15714
|
-
output.write(`${frame}
|
|
15715
|
-
`);
|
|
15716
|
-
const columns = output.columns ?? 80;
|
|
15717
|
-
rows = frame.split(`
|
|
15718
|
-
`).reduce((total, line) => total + rowsOccupied(line, columns), 0);
|
|
16066
|
+
continue;
|
|
16067
|
+
}
|
|
16068
|
+
const owner = taken.get(id);
|
|
16069
|
+
if (owner) {
|
|
16070
|
+
conflicts.push({ command, chord: formatChord(chord), reason: `already bound to ${owner}` });
|
|
16071
|
+
continue;
|
|
16072
|
+
}
|
|
16073
|
+
const relocation = RELOCATED_EDITING[id];
|
|
16074
|
+
if (relocation)
|
|
16075
|
+
displaced.push({ chord: formatChord(chord), command, ...relocation });
|
|
16076
|
+
taken.set(id, command);
|
|
16077
|
+
bindings.push({ command, chord, description });
|
|
15719
16078
|
}
|
|
15720
|
-
}
|
|
16079
|
+
}
|
|
16080
|
+
return { bindings, conflicts, displaced };
|
|
15721
16081
|
}
|
|
15722
|
-
|
|
15723
|
-
|
|
15724
|
-
|
|
15725
|
-
|
|
15726
|
-
|
|
15727
|
-
|
|
15728
|
-
|
|
15729
|
-
|
|
15730
|
-
|
|
15731
|
-
|
|
15732
|
-
|
|
15733
|
-
|
|
15734
|
-
|
|
15735
|
-
|
|
15736
|
-
|
|
15737
|
-
|
|
15738
|
-
|
|
15739
|
-
|
|
15740
|
-
|
|
16082
|
+
function isLikelyDeliverable(chord, environment = {}) {
|
|
16083
|
+
if (environment.TERM === "dumb")
|
|
16084
|
+
return false;
|
|
16085
|
+
if (chord.key === "tab" && (chord.ctrl || chord.shift))
|
|
16086
|
+
return false;
|
|
16087
|
+
if (FUNCTION_KEY.test(chord.key))
|
|
16088
|
+
return !environment.TMUX;
|
|
16089
|
+
if (chord.meta)
|
|
16090
|
+
return !environment.TMUX;
|
|
16091
|
+
return true;
|
|
16092
|
+
}
|
|
16093
|
+
|
|
16094
|
+
class KeyBindingRegistry {
|
|
16095
|
+
environment;
|
|
16096
|
+
byChord = new Map;
|
|
16097
|
+
conflicts;
|
|
16098
|
+
displaced;
|
|
16099
|
+
constructor(overrides = {}, environment = {}) {
|
|
16100
|
+
this.environment = environment;
|
|
16101
|
+
const resolved = resolveBindings(overrides);
|
|
16102
|
+
this.conflicts = resolved.conflicts;
|
|
16103
|
+
this.displaced = resolved.displaced;
|
|
16104
|
+
for (const binding of resolved.bindings)
|
|
16105
|
+
this.byChord.set(chordId(binding.chord), binding);
|
|
16106
|
+
}
|
|
16107
|
+
match(key) {
|
|
16108
|
+
const chord = chordFromKeypress(key);
|
|
16109
|
+
return chord ? this.byChord.get(chordId(chord))?.command : undefined;
|
|
16110
|
+
}
|
|
16111
|
+
get bindings() {
|
|
16112
|
+
return [...this.byChord.values()];
|
|
16113
|
+
}
|
|
16114
|
+
groupedByCommand() {
|
|
16115
|
+
const byCommand = new Map;
|
|
16116
|
+
for (const binding of this.bindings) {
|
|
16117
|
+
const entry = byCommand.get(binding.command) ?? { chords: [], description: binding.description };
|
|
16118
|
+
entry.chords.push(binding.chord);
|
|
16119
|
+
byCommand.set(binding.command, entry);
|
|
15741
16120
|
}
|
|
16121
|
+
return byCommand;
|
|
15742
16122
|
}
|
|
15743
|
-
|
|
15744
|
-
return
|
|
15745
|
-
} finally {
|
|
15746
|
-
surface.erase();
|
|
15747
|
-
host.input.off("keypress", collect);
|
|
15748
|
-
for (const listener of borrowed)
|
|
15749
|
-
host.input.on("keypress", listener);
|
|
16123
|
+
shortcutLabels() {
|
|
16124
|
+
return new Map([...this.groupedByCommand()].map(([command, { chords }]) => [command, chords.map(formatChord).join(", ")]));
|
|
15750
16125
|
}
|
|
15751
|
-
|
|
15752
|
-
|
|
15753
|
-
|
|
15754
|
-
|
|
15755
|
-
|
|
15756
|
-
|
|
15757
|
-
|
|
15758
|
-
};
|
|
15759
|
-
return withBorrowedKeyboard(host, self, (keys, paint5) => runCommandPalette(keys, paletteEntries(chords), paint5, sized));
|
|
15760
|
-
}
|
|
15761
|
-
async function openDefenderTriage(host, findings, options, self) {
|
|
15762
|
-
const sized = {
|
|
15763
|
-
width: options.width ?? host.output.columns ?? 80,
|
|
15764
|
-
rows: options.rows ?? Math.max(12, (host.output.rows ?? 24) - 2),
|
|
15765
|
-
...options,
|
|
15766
|
-
getSize: options.getSize ?? (() => ({ width: host.output.columns ?? 80, height: Math.max(12, (host.output.rows ?? 24) - 2) }))
|
|
15767
|
-
};
|
|
15768
|
-
return withBorrowedKeyboard(host, self, (keys, paint5) => runDefenderTriage(keys, findings, paint5, sized));
|
|
15769
|
-
}
|
|
15770
|
-
async function openChooser(host, items, options, self) {
|
|
15771
|
-
const sized = {
|
|
15772
|
-
width: host.output.columns ?? 80,
|
|
15773
|
-
...options,
|
|
15774
|
-
getSize: options.getSize ?? (() => ({ width: host.output.columns ?? 80, height: Math.max(1, (host.output.rows ?? 24) - 4) }))
|
|
15775
|
-
};
|
|
15776
|
-
return withBorrowedKeyboard(host, self, (keys, paint5) => runChooser(keys, items, paint5, sized));
|
|
15777
|
-
}
|
|
15778
|
-
async function openTable(host, options, self) {
|
|
15779
|
-
const sized = {
|
|
15780
|
-
width: host.output.columns ?? 80,
|
|
15781
|
-
...options,
|
|
15782
|
-
getSize: options.getSize ?? (() => ({ width: host.output.columns ?? 80, height: Math.max(1, (host.output.rows ?? 24) - 2) }))
|
|
15783
|
-
};
|
|
15784
|
-
return withBorrowedKeyboard(host, self, (keys, paint5) => runTable(keys, paint5, sized));
|
|
15785
|
-
}
|
|
15786
|
-
async function openModelPicker(host, options, self) {
|
|
15787
|
-
const sized = {
|
|
15788
|
-
width: host.output.columns ?? 80,
|
|
15789
|
-
...options,
|
|
15790
|
-
getSize: options.getSize ?? (() => ({ width: host.output.columns ?? 80, height: Math.max(1, (host.output.rows ?? 24) - 4) }))
|
|
15791
|
-
};
|
|
15792
|
-
return withBorrowedKeyboard(host, self, (keys, paint5) => runModelPicker(keys, paint5, sized));
|
|
15793
|
-
}
|
|
15794
|
-
function formatBinding(chord) {
|
|
15795
|
-
const key = /^f\d+$/.test(chord.key) ? chord.key.toUpperCase() : chord.key.toUpperCase();
|
|
15796
|
-
return `${chord.ctrl ? "Ctrl+" : ""}${chord.meta ? "Alt+" : ""}${chord.shift ? "Shift+" : ""}${key}`;
|
|
15797
|
-
}
|
|
15798
|
-
function installShortcuts(host) {
|
|
15799
|
-
let busy = false;
|
|
15800
|
-
const run = (options) => {
|
|
15801
|
-
busy = true;
|
|
15802
|
-
openPalette(host, onKeypress, options).then((chosen) => {
|
|
15803
|
-
if (chosen)
|
|
15804
|
-
submit(host.readline, chosen);
|
|
15805
|
-
}).finally(() => {
|
|
15806
|
-
busy = false;
|
|
15807
|
-
host.readline.prompt(true);
|
|
16126
|
+
render() {
|
|
16127
|
+
const rows = [...this.groupedByCommand()].map(([command, { chords, description }]) => {
|
|
16128
|
+
const deliverable = chords.some((chord) => isLikelyDeliverable(chord, this.environment));
|
|
16129
|
+
return [
|
|
16130
|
+
chords.map(formatChord).join(", "),
|
|
16131
|
+
deliverable ? `${description} (${command})` : `${description} — this terminal may not send it; use ${command}`
|
|
16132
|
+
];
|
|
15808
16133
|
});
|
|
15809
|
-
|
|
15810
|
-
|
|
15811
|
-
if (
|
|
15812
|
-
|
|
15813
|
-
|
|
15814
|
-
|
|
15815
|
-
|
|
15816
|
-
if (host.onIntercept?.(command))
|
|
15817
|
-
return;
|
|
15818
|
-
if (command !== "/palette") {
|
|
15819
|
-
submit(host.readline, command);
|
|
15820
|
-
return;
|
|
16134
|
+
const width = Math.max(16, ...rows.map(([key]) => key.length));
|
|
16135
|
+
const lines = ["Feature keys", ...rows.map(([key, label]) => ` ${key.padEnd(width + 2)}${label}`)];
|
|
16136
|
+
if (this.displaced.length > 0) {
|
|
16137
|
+
lines.push("", "Line editing that moved");
|
|
16138
|
+
for (const moved of this.displaced) {
|
|
16139
|
+
lines.push(` ${moved.instead.padEnd(width + 2)}${moved.lost} — ${moved.chord} runs ${moved.command} now`);
|
|
16140
|
+
}
|
|
15821
16141
|
}
|
|
15822
|
-
|
|
15823
|
-
|
|
15824
|
-
|
|
15825
|
-
|
|
16142
|
+
if (this.conflicts.length > 0) {
|
|
16143
|
+
lines.push("", "Not bound");
|
|
16144
|
+
for (const conflict of this.conflicts)
|
|
16145
|
+
lines.push(` ${conflict.chord.padEnd(width + 2)}${conflict.command} — ${conflict.reason}`);
|
|
16146
|
+
}
|
|
16147
|
+
return lines.join(`
|
|
16148
|
+
`);
|
|
16149
|
+
}
|
|
15826
16150
|
}
|
|
15827
16151
|
|
|
15828
16152
|
// packages/archymedes-cli/src/chooser.ts
|
|
@@ -15948,7 +16272,11 @@ function renderChooser2(state, items, options) {
|
|
|
15948
16272
|
const shownLabel = clip2(item.label, labelBudget);
|
|
15949
16273
|
const padded = shownLabel + " ".repeat(Math.max(0, labelBudget - visibleWidth(shownLabel)));
|
|
15950
16274
|
const marker = active ? paint5.green(glyphs.prompt) : fadingOut ? paint5.dim(glyphs.prompt) : " ";
|
|
15951
|
-
const
|
|
16275
|
+
const focus = Math.max(0, Math.min(1, options.focusProgress ?? 1));
|
|
16276
|
+
const graphemes = [...new Intl.Segmenter(undefined, { granularity: "grapheme" }).segment(padded)].map((part) => part.segment);
|
|
16277
|
+
const illuminated = Math.round(graphemes.length * focus);
|
|
16278
|
+
const label = active ? paint5.cyan(graphemes.slice(0, illuminated).join("")) + graphemes.slice(illuminated).join("") : padded;
|
|
16279
|
+
const head = `${marker} ${paint5.dim(number)} ${label} `;
|
|
15952
16280
|
const tail = `${item.hint ? ` ${item.hint}` : ""}${item.description ? ` ${item.description}` : ""}`;
|
|
15953
16281
|
lines.push(` ${head}${paint5.dim(clip2(tail, Math.max(0, room - visibleWidth(padded) - 2)))}`);
|
|
15954
16282
|
}
|
|
@@ -15992,39 +16320,45 @@ async function runChooser2(keys, items, paint5, options) {
|
|
|
15992
16320
|
glide?.stop();
|
|
15993
16321
|
glide = undefined;
|
|
15994
16322
|
};
|
|
15995
|
-
|
|
15996
|
-
const
|
|
15997
|
-
|
|
15998
|
-
|
|
15999
|
-
|
|
16000
|
-
|
|
16001
|
-
|
|
16002
|
-
|
|
16003
|
-
|
|
16004
|
-
|
|
16323
|
+
try {
|
|
16324
|
+
for await (const input of keys) {
|
|
16325
|
+
const current = liveOptions();
|
|
16326
|
+
const previousSelected = state.selected;
|
|
16327
|
+
const step = advanceChooser(state, items, input, {
|
|
16328
|
+
filter: options.filter ?? false,
|
|
16329
|
+
page: options.page ?? current.height ?? 8,
|
|
16330
|
+
...current.height === undefined ? {} : { height: current.height }
|
|
16331
|
+
});
|
|
16332
|
+
state = step.state;
|
|
16333
|
+
if (step.done) {
|
|
16334
|
+
settleGlide();
|
|
16335
|
+
if (step.done.index === undefined)
|
|
16336
|
+
return;
|
|
16337
|
+
return filterItems(items, state.query)[step.done.index]?.value;
|
|
16338
|
+
}
|
|
16005
16339
|
settleGlide();
|
|
16006
|
-
|
|
16007
|
-
|
|
16008
|
-
|
|
16340
|
+
const isSingleStep = (input.key.name === "up" || input.key.name === "down") && Math.abs(state.selected - previousSelected) === 1;
|
|
16341
|
+
const motion = options.motion ?? (process.env.ARCHYMEDES_NO_MOTION !== "1" && process.env.NO_COLOR === undefined && process.env.TERM !== "dumb");
|
|
16342
|
+
if (!motion || !isSingleStep || previousSelected === state.selected) {
|
|
16343
|
+
paint5(renderChooser2(state, items, liveOptions()));
|
|
16344
|
+
continue;
|
|
16345
|
+
}
|
|
16346
|
+
paint5(renderChooser2(state, items, { ...liveOptions(), transitionFrom: previousSelected, focusProgress: 0 }));
|
|
16347
|
+
const animator = new SpringAnimator(0, (value) => {
|
|
16348
|
+
if (value < 0.95) {
|
|
16349
|
+
paint5(renderChooser2(state, items, { ...liveOptions(), transitionFrom: value < 0.5 ? previousSelected : undefined, focusProgress: value }));
|
|
16350
|
+
return;
|
|
16351
|
+
}
|
|
16352
|
+
animator.stop();
|
|
16353
|
+
glide = undefined;
|
|
16354
|
+
paint5(renderChooser2(state, items, liveOptions()));
|
|
16355
|
+
}, { intervalMs: 30 });
|
|
16356
|
+
glide = animator;
|
|
16357
|
+
animator.retarget(1);
|
|
16009
16358
|
}
|
|
16359
|
+
} finally {
|
|
16010
16360
|
settleGlide();
|
|
16011
|
-
|
|
16012
|
-
if (!isSingleStep || previousSelected === state.selected) {
|
|
16013
|
-
paint5(renderChooser2(state, items, liveOptions()));
|
|
16014
|
-
continue;
|
|
16015
|
-
}
|
|
16016
|
-
paint5(renderChooser2(state, items, { ...liveOptions(), transitionFrom: previousSelected }));
|
|
16017
|
-
const animator = new SpringAnimator(0, (value) => {
|
|
16018
|
-
if (value < 0.6)
|
|
16019
|
-
return;
|
|
16020
|
-
animator.stop();
|
|
16021
|
-
glide = undefined;
|
|
16022
|
-
paint5(renderChooser2(state, items, liveOptions()));
|
|
16023
|
-
}, { intervalMs: 30 });
|
|
16024
|
-
glide = animator;
|
|
16025
|
-
animator.retarget(1);
|
|
16026
|
-
}
|
|
16027
|
-
settleGlide();
|
|
16361
|
+
}
|
|
16028
16362
|
return;
|
|
16029
16363
|
}
|
|
16030
16364
|
|
|
@@ -17301,7 +17635,7 @@ import { createInterface as createInterface3 } from "node:readline/promises";
|
|
|
17301
17635
|
// packages/archymedes-cli/package.json
|
|
17302
17636
|
var package_default = {
|
|
17303
17637
|
name: "archymedes-cli",
|
|
17304
|
-
version: "2.
|
|
17638
|
+
version: "2.2.0",
|
|
17305
17639
|
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.",
|
|
17306
17640
|
keywords: [
|
|
17307
17641
|
"cli",
|
|
@@ -20127,18 +20461,21 @@ var GUIDE_TOPICS = [
|
|
|
20127
20461
|
{
|
|
20128
20462
|
id: "look",
|
|
20129
20463
|
title: "How it looks",
|
|
20130
|
-
summary: "Themes, symbols, and terminals that need help.",
|
|
20464
|
+
summary: "Themes, symbols, the fixed workspace, and terminals that need help.",
|
|
20131
20465
|
body: [
|
|
20132
20466
|
"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.",
|
|
20133
|
-
"
|
|
20467
|
+
"The fixed workspace keeps a mode rail and composer anchored while the transcript scrolls underneath. Start with --layout fixed or ARCHYMEDES_LAYOUT=fixed, switch mid-session with /layout, and use Page Up / Page Down to read retained history (Escape returns to live output). Menus open inside the transcript area so the composer never jumps.",
|
|
20468
|
+
"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. ARCHYMEDES_NO_MOTION=1 keeps the opening geometry and menu focus still."
|
|
20134
20469
|
],
|
|
20135
20470
|
examples: [
|
|
20136
20471
|
{ input: "/theme list", effect: "every theme available, including your own" },
|
|
20137
20472
|
{ input: "/theme chalkboard", effect: "change the colours immediately" },
|
|
20138
20473
|
{ input: "/theme where", effect: "where to put a theme file of your own" },
|
|
20474
|
+
{ input: "/layout fixed", effect: "mode rail, anchored composer, menus in the transcript" },
|
|
20475
|
+
{ input: "/layout scrollback", effect: "return to the ordinary terminal log" },
|
|
20139
20476
|
{ input: "archymedes --ascii", effect: "plain characters for terminals that mangle symbols" }
|
|
20140
20477
|
],
|
|
20141
|
-
covers: ["/theme"]
|
|
20478
|
+
covers: ["/theme", "/layout"]
|
|
20142
20479
|
},
|
|
20143
20480
|
{
|
|
20144
20481
|
id: "extend",
|
|
@@ -20153,10 +20490,11 @@ var GUIDE_TOPICS = [
|
|
|
20153
20490
|
examples: [
|
|
20154
20491
|
{ input: "/tools", effect: "every tool, skill, plugin, MCP server and hook" },
|
|
20155
20492
|
{ input: "/voice", effect: "record a prompt, then edit it before sending" },
|
|
20493
|
+
{ input: "/cat README.md", effect: "read a file in the transcript, with markdown formatting or numbered code" },
|
|
20156
20494
|
{ input: "/files", effect: "browse the project tree and @mention a file" },
|
|
20157
20495
|
{ input: "/edit src/app.ts", effect: "open a file in the built-in editor" }
|
|
20158
20496
|
],
|
|
20159
|
-
covers: ["/tools", "/voice", "/files", "/edit"]
|
|
20497
|
+
covers: ["/tools", "/voice", "/files", "/edit", "/cat"]
|
|
20160
20498
|
}
|
|
20161
20499
|
];
|
|
20162
20500
|
function findTopic(id) {
|
|
@@ -20349,12 +20687,31 @@ var ARCHYMEDES = `
|
|
|
20349
20687
|
--border-focus: #e7bb78;
|
|
20350
20688
|
}
|
|
20351
20689
|
`;
|
|
20690
|
+
var RAINBOW = `
|
|
20691
|
+
/* Every hue at once — the opening art and the thinking spinner sweep the full spectrum. */
|
|
20692
|
+
@theme rainbow {
|
|
20693
|
+
--primary: #ff5f5f;
|
|
20694
|
+
--secondary: #5fd7ff;
|
|
20695
|
+
--accent: #ffd75f;
|
|
20696
|
+
--bg: #0c0c0c;
|
|
20697
|
+
--surface: #161616;
|
|
20698
|
+
--text: #f2f2f2;
|
|
20699
|
+
--text-muted: #9a9a9a;
|
|
20700
|
+
--success: #5fff87;
|
|
20701
|
+
--warning: #ffaf5f;
|
|
20702
|
+
--error: #ff3b3b;
|
|
20703
|
+
--border: double;
|
|
20704
|
+
--border-color: #8a8a8a;
|
|
20705
|
+
--border-focus: #d75fff;
|
|
20706
|
+
}
|
|
20707
|
+
`;
|
|
20352
20708
|
var BUILTIN_THEME_SOURCES2 = {
|
|
20353
20709
|
archymedes: ARCHYMEDES,
|
|
20354
20710
|
blueprint: BLUEPRINT,
|
|
20355
20711
|
parchment: PARCHMENT,
|
|
20356
20712
|
chalkboard: CHALKBOARD,
|
|
20357
|
-
"high-contrast": HIGH_CONTRAST
|
|
20713
|
+
"high-contrast": HIGH_CONTRAST,
|
|
20714
|
+
rainbow: RAINBOW
|
|
20358
20715
|
};
|
|
20359
20716
|
var DEFAULT_THEME_NAME = "archymedes";
|
|
20360
20717
|
var NAMED_COLORS = {
|
|
@@ -20423,6 +20780,15 @@ function colorCode2(value, depth) {
|
|
|
20423
20780
|
return parsed < 8 ? `\x1B[${30 + parsed}m` : `\x1B[${90 + parsed - 8}m`;
|
|
20424
20781
|
return depth === "truecolor" ? `\x1B[38;2;${parsed.r};${parsed.g};${parsed.b}m` : `\x1B[38;5;${rgbTo2562(parsed)}m`;
|
|
20425
20782
|
}
|
|
20783
|
+
function rainbowHex(t2) {
|
|
20784
|
+
const hue = (t2 % 1 + 1) % 1 * 360;
|
|
20785
|
+
const c = 0.5;
|
|
20786
|
+
const x = c * (1 - Math.abs(hue / 60 % 2 - 1));
|
|
20787
|
+
const m = 0.6 - c / 2;
|
|
20788
|
+
const [r1, g1, b1] = hue < 60 ? [c, x, 0] : hue < 120 ? [x, c, 0] : hue < 180 ? [0, c, x] : hue < 240 ? [0, x, c] : hue < 300 ? [x, 0, c] : [c, 0, x];
|
|
20789
|
+
const channel = (value) => Math.round((value + m) * 255).toString(16).padStart(2, "0");
|
|
20790
|
+
return `#${channel(r1)}${channel(g1)}${channel(b1)}`;
|
|
20791
|
+
}
|
|
20426
20792
|
var DEFAULT_TOKENS = {
|
|
20427
20793
|
primary: "cyan",
|
|
20428
20794
|
secondary: "blue",
|
|
@@ -22451,13 +22817,13 @@ function clip2(text, width, glyphs = UNICODE_GLYPHS) {
|
|
|
22451
22817
|
}
|
|
22452
22818
|
|
|
22453
22819
|
// packages/archymedes-cli/src/code-view.ts
|
|
22454
|
-
var
|
|
22820
|
+
var MAX_DIFF_CELLS = 250000;
|
|
22455
22821
|
function diffLines2(before, after) {
|
|
22456
22822
|
const left = before === "" ? [] : before.split(`
|
|
22457
22823
|
`);
|
|
22458
22824
|
const right = after === "" ? [] : after.split(`
|
|
22459
22825
|
`);
|
|
22460
|
-
if (left.length * right.length >
|
|
22826
|
+
if (left.length * right.length > MAX_DIFF_CELLS) {
|
|
22461
22827
|
return [
|
|
22462
22828
|
...left.map((text) => ({ kind: "remove", text })),
|
|
22463
22829
|
...right.map((text) => ({ kind: "add", text }))
|
|
@@ -22501,7 +22867,7 @@ function diffStat(diff) {
|
|
|
22501
22867
|
removed: diff.filter((line) => line.kind === "remove").length
|
|
22502
22868
|
};
|
|
22503
22869
|
}
|
|
22504
|
-
function
|
|
22870
|
+
function condenseDiff(diff, context = 2) {
|
|
22505
22871
|
const keep = new Array(diff.length).fill(false);
|
|
22506
22872
|
diff.forEach((line, index) => {
|
|
22507
22873
|
if (line.kind === "context")
|
|
@@ -22577,7 +22943,7 @@ function languageOf(filePath) {
|
|
|
22577
22943
|
return name.toLowerCase();
|
|
22578
22944
|
return extension || "text";
|
|
22579
22945
|
}
|
|
22580
|
-
var
|
|
22946
|
+
var KEYWORDS = new Set([
|
|
22581
22947
|
"async",
|
|
22582
22948
|
"await",
|
|
22583
22949
|
"break",
|
|
@@ -22629,11 +22995,11 @@ var KEYWORDS2 = new Set([
|
|
|
22629
22995
|
"with",
|
|
22630
22996
|
"yield"
|
|
22631
22997
|
]);
|
|
22632
|
-
var
|
|
22998
|
+
var LITERALS = new Set(["true", "false", "null", "nil", "None", "True", "False", "undefined", "self", "this"]);
|
|
22633
22999
|
function highlightCode2(line, depth) {
|
|
22634
23000
|
if (depth === "none")
|
|
22635
23001
|
return line;
|
|
22636
|
-
const commentAt =
|
|
23002
|
+
const commentAt = findCommentStart(line);
|
|
22637
23003
|
const code = commentAt === -1 ? line : line.slice(0, commentAt);
|
|
22638
23004
|
const comment = commentAt === -1 ? "" : paint(line.slice(commentAt), DIM, depth);
|
|
22639
23005
|
const painted = code.replace(/("(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*'|`(?:[^`\\]|\\.)*`)|\b(\d+(?:\.\d+)?)\b|\b([A-Za-z_$][\w$]*)\b/g, (whole, text, numeric, word) => {
|
|
@@ -22641,15 +23007,15 @@ function highlightCode2(line, depth) {
|
|
|
22641
23007
|
return paint(text, GREEN, depth);
|
|
22642
23008
|
if (numeric !== undefined)
|
|
22643
23009
|
return paint(numeric, MAGENTA, depth);
|
|
22644
|
-
if (word !== undefined &&
|
|
23010
|
+
if (word !== undefined && KEYWORDS.has(word))
|
|
22645
23011
|
return paint(word, CYAN, depth);
|
|
22646
|
-
if (word !== undefined &&
|
|
23012
|
+
if (word !== undefined && LITERALS.has(word))
|
|
22647
23013
|
return paint(word, MAGENTA, depth);
|
|
22648
23014
|
return whole;
|
|
22649
23015
|
});
|
|
22650
23016
|
return `${painted}${comment}`;
|
|
22651
23017
|
}
|
|
22652
|
-
function
|
|
23018
|
+
function findCommentStart(line) {
|
|
22653
23019
|
let quote;
|
|
22654
23020
|
for (let index = 0;index < line.length; index += 1) {
|
|
22655
23021
|
const character = line[index];
|
|
@@ -22701,7 +23067,7 @@ function renderCode2(content, style, options = {}) {
|
|
|
22701
23067
|
function renderDiff2(diff, style, options = {}) {
|
|
22702
23068
|
const glyphs = style.glyphs ?? UNICODE_GLYPHS;
|
|
22703
23069
|
const maxLines = options.maxLines ?? 16;
|
|
22704
|
-
const condensed =
|
|
23070
|
+
const condensed = condenseDiff(diff);
|
|
22705
23071
|
const bodyWidth = Math.max(8, style.width - GUTTER.length - 4);
|
|
22706
23072
|
const row = (line) => {
|
|
22707
23073
|
if (line.kind === "gap")
|
|
@@ -22739,6 +23105,12 @@ function renderFileChange(change, style, options = {}) {
|
|
|
22739
23105
|
`), style, { title: `${change.kind === "edit" ? "diff" : "new file"} · ${change.path}`, badge, tone: change.kind === "edit" ? "accent" : "good" })
|
|
22740
23106
|
};
|
|
22741
23107
|
}
|
|
23108
|
+
function fenceHeader(language, style) {
|
|
23109
|
+
const glyphs = style.glyphs ?? UNICODE_GLYPHS;
|
|
23110
|
+
const label = language ? ` ${language} ` : glyphs.boxHorizontal.repeat(4);
|
|
23111
|
+
const width = Math.max(0, style.width - GUTTER.length - visibleWidth(label) - 3);
|
|
23112
|
+
return `${GUTTER}${paint(glyphs.boxTopLeft + glyphs.boxHorizontal, DIM, style.depth)}${paintAll(label, [CYAN, BOLD], style.depth)}${paint(glyphs.boxHorizontal.repeat(Math.max(0, width)), DIM, style.depth)}`;
|
|
23113
|
+
}
|
|
22742
23114
|
|
|
22743
23115
|
// packages/archymedes-cli/src/test-report.ts
|
|
22744
23116
|
function toMs(value) {
|
|
@@ -23642,7 +24014,12 @@ function parseArgs(argv) {
|
|
|
23642
24014
|
parsed.pin = true;
|
|
23643
24015
|
else if (argument === "--no-pin")
|
|
23644
24016
|
parsed.pin = false;
|
|
23645
|
-
else if (argument === "--
|
|
24017
|
+
else if (argument === "--layout") {
|
|
24018
|
+
const value = argv[++index];
|
|
24019
|
+
if (value !== "fixed" && value !== "scrollback")
|
|
24020
|
+
throw new Error("--layout expects fixed or scrollback");
|
|
24021
|
+
parsed.layout = value;
|
|
24022
|
+
} else if (argument === "--theme") {
|
|
23646
24023
|
const next = argv[index + 1];
|
|
23647
24024
|
if (next && !next.startsWith("-") && /^[A-Za-z0-9_-]+$/.test(next)) {
|
|
23648
24025
|
parsed.theme = next;
|
|
@@ -23811,6 +24188,8 @@ ${style.bold(t2(language, "help.transcript"))}
|
|
|
23811
24188
|
/expand [N|all|list] Unfold written code, a test run, or a long result
|
|
23812
24189
|
archymedes --ascii Draw with plain ASCII when the terminal mangles symbols
|
|
23813
24190
|
archymedes --theme chalkboard Start in a named theme (/theme list shows them all)
|
|
24191
|
+
archymedes --layout fixed Fixed workspace, mode rail and anchored composer
|
|
24192
|
+
/layout [fixed|scrollback] Switch layouts mid-session; PgUp/PgDn read history
|
|
23814
24193
|
archymedes --pin Pin the status line to the bottom row. Costs the terminal's
|
|
23815
24194
|
scrollback: a reserved footer means scrolled-off lines are
|
|
23816
24195
|
never saved, so this is off unless you ask for it.
|
|
@@ -23832,6 +24211,15 @@ var toolLines = new ReplaceableBlock(out);
|
|
|
23832
24211
|
var markdown = new MarkdownStream(out, "none");
|
|
23833
24212
|
var spinner;
|
|
23834
24213
|
var screen;
|
|
24214
|
+
var sessionStream = {
|
|
24215
|
+
write: (text) => screen instanceof WorkspaceFrame ? screen.write(text) : terminalStream2.write(text),
|
|
24216
|
+
get columns() {
|
|
24217
|
+
return process.stdout.columns;
|
|
24218
|
+
},
|
|
24219
|
+
get rows() {
|
|
24220
|
+
return process.stdout.rows;
|
|
24221
|
+
}
|
|
24222
|
+
};
|
|
23835
24223
|
var glyphs = UNICODE_GLYPHS2;
|
|
23836
24224
|
var renderDepth = "none";
|
|
23837
24225
|
var expandables = new ExpandableStore;
|
|
@@ -24685,10 +25073,14 @@ Setup cancelled.
|
|
|
24685
25073
|
};
|
|
24686
25074
|
configureRendering(depth, !args.json && Boolean(process.stdout.isTTY), sessionGlyphs, activeTheme ? buildPalette(activeTheme, depth) : NO_COLOR_PALETTE2);
|
|
24687
25075
|
let mode = args.mode;
|
|
25076
|
+
let bindFixedNavigation = () => {};
|
|
25077
|
+
let unbindFixedNavigation = () => {};
|
|
24688
25078
|
let pace = args.pace;
|
|
24689
25079
|
let memories = await loadMemories(args.root, environment);
|
|
24690
25080
|
let exitCleanly = () => {
|
|
24691
25081
|
screen?.exit();
|
|
25082
|
+
setWorkspaceMenu(undefined);
|
|
25083
|
+
unbindFixedNavigation();
|
|
24692
25084
|
uninstallShortcuts();
|
|
24693
25085
|
readline.close();
|
|
24694
25086
|
abandonPrompt();
|
|
@@ -24938,7 +25330,9 @@ Setup cancelled.
|
|
|
24938
25330
|
resolvedModelId = tab.payload.modelId;
|
|
24939
25331
|
tab.payload.sink.setLive(true);
|
|
24940
25332
|
out.route(tab.payload.sink);
|
|
24941
|
-
if (
|
|
25333
|
+
if (screen instanceof WorkspaceFrame)
|
|
25334
|
+
screen.navigate("live");
|
|
25335
|
+
else if (options.replay)
|
|
24942
25336
|
replayTab(tab);
|
|
24943
25337
|
};
|
|
24944
25338
|
function replayTab(tab) {
|
|
@@ -24956,7 +25350,7 @@ Setup cancelled.
|
|
|
24956
25350
|
out.write(`${line}
|
|
24957
25351
|
`);
|
|
24958
25352
|
}
|
|
24959
|
-
const firstSink = new TabSink(
|
|
25353
|
+
const firstSink = new TabSink(sessionStream, { live: true });
|
|
24960
25354
|
tabs.adopt(path27.basename(args.root) || "archymedes", {
|
|
24961
25355
|
agent,
|
|
24962
25356
|
ledger,
|
|
@@ -25007,13 +25401,19 @@ Setup cancelled.
|
|
|
25007
25401
|
};
|
|
25008
25402
|
const terminalControls = () => ({
|
|
25009
25403
|
clearStatus: () => statusBar.clear(),
|
|
25010
|
-
releaseScreen: () =>
|
|
25404
|
+
releaseScreen: () => {
|
|
25405
|
+
screen?.exit();
|
|
25406
|
+
setWorkspaceMenu(undefined);
|
|
25407
|
+
},
|
|
25011
25408
|
uninstallShortcuts: () => uninstallShortcuts(),
|
|
25012
25409
|
installShortcuts: () => installShortcutsAgain(),
|
|
25013
25410
|
pauseInput: () => readline.pause(),
|
|
25014
25411
|
resumeInput: () => readline.resume(),
|
|
25015
25412
|
restoreScreen: () => {
|
|
25016
25413
|
screen?.enter();
|
|
25414
|
+
if (screen instanceof WorkspaceFrame)
|
|
25415
|
+
setWorkspaceMenu(screen.menu);
|
|
25416
|
+
bindFixedNavigation();
|
|
25017
25417
|
showIdleStatus();
|
|
25018
25418
|
}
|
|
25019
25419
|
});
|
|
@@ -25022,6 +25422,22 @@ Setup cancelled.
|
|
|
25022
25422
|
columns: process.stdout.columns ?? 80,
|
|
25023
25423
|
rows: process.stdout.rows ?? 24
|
|
25024
25424
|
});
|
|
25425
|
+
const explainCode = async (content, target) => {
|
|
25426
|
+
const started = Date.now();
|
|
25427
|
+
const turn = await model.complete({
|
|
25428
|
+
messages: [
|
|
25429
|
+
{ role: "system", content: "Explain the given source file to a developer reading it for the first time. Cover what it does, its key functions or exports, and any non-obvious design decisions. Plain prose, no code fences, under 200 words." },
|
|
25430
|
+
{ role: "user", content: `File: ${target}
|
|
25431
|
+
|
|
25432
|
+
${content}` }
|
|
25433
|
+
],
|
|
25434
|
+
tools: [],
|
|
25435
|
+
maxOutputTokens: 500,
|
|
25436
|
+
safetyIdentifier: agent.sessionId
|
|
25437
|
+
});
|
|
25438
|
+
ledger.record({ usage: turn.usage, iterations: 1, toolCalls: 0, elapsedMs: Date.now() - started });
|
|
25439
|
+
return turn.content.trim() || "The model returned no explanation.";
|
|
25440
|
+
};
|
|
25025
25441
|
const editFile = async (target) => {
|
|
25026
25442
|
let existing = "";
|
|
25027
25443
|
try {
|
|
@@ -25036,13 +25452,14 @@ Setup cancelled.
|
|
|
25036
25452
|
}
|
|
25037
25453
|
let saved;
|
|
25038
25454
|
const outcome = await withFullScreen(screenCapabilities(), terminalControls(), async () => {
|
|
25039
|
-
const { runEditorScreen } = await import("./editor-screen-
|
|
25455
|
+
const { runEditorScreen } = await import("./editor-screen-1b3z96tn.mjs");
|
|
25040
25456
|
saved = await runEditorScreen({
|
|
25041
25457
|
columns: process.stdout.columns ?? 80,
|
|
25042
25458
|
rows: process.stdout.rows ?? 24,
|
|
25043
25459
|
path: target,
|
|
25044
25460
|
content: existing,
|
|
25045
|
-
palette
|
|
25461
|
+
palette,
|
|
25462
|
+
explain: explainCode
|
|
25046
25463
|
});
|
|
25047
25464
|
});
|
|
25048
25465
|
if (!outcome.ok) {
|
|
@@ -25109,7 +25526,7 @@ Setup cancelled.
|
|
|
25109
25526
|
`));
|
|
25110
25527
|
return;
|
|
25111
25528
|
}
|
|
25112
|
-
const sink = new TabSink(
|
|
25529
|
+
const sink = new TabSink(sessionStream);
|
|
25113
25530
|
const stream = new JobStream({
|
|
25114
25531
|
root: args.root,
|
|
25115
25532
|
id,
|
|
@@ -25228,6 +25645,8 @@ Setup cancelled.
|
|
|
25228
25645
|
unbindSigint();
|
|
25229
25646
|
watched.stopAll();
|
|
25230
25647
|
screen?.exit();
|
|
25648
|
+
setWorkspaceMenu(undefined);
|
|
25649
|
+
unbindFixedNavigation();
|
|
25231
25650
|
uninstallShortcuts();
|
|
25232
25651
|
readline.close();
|
|
25233
25652
|
abandonPrompt();
|
|
@@ -25418,7 +25837,8 @@ Setup cancelled.
|
|
|
25418
25837
|
steps: activity.steps,
|
|
25419
25838
|
badge: paceBadge(pace, glyphs)
|
|
25420
25839
|
});
|
|
25421
|
-
|
|
25840
|
+
const spinnerAccent = () => activeTheme?.name === "rainbow" ? colorCode2(rainbowHex(Date.now() / 1500), depth) : palette.primary;
|
|
25841
|
+
spinner = new Spinner(() => screen?.pinned ? showStatus(formatStatusLine2(fields(), statusRoomFor(screen.current.columns), depth, glyphs, spinnerAccent())) : statusBar.render(fields(), depth, glyphs, spinnerAccent()), 120, glyphs, SPINNER_START_DELAY_MS);
|
|
25422
25842
|
spinner.start();
|
|
25423
25843
|
}
|
|
25424
25844
|
turnActive = true;
|
|
@@ -25666,7 +26086,7 @@ ${style.yellow(`Stopped at the ${formatMoney2(fromUnits2(args.budget, display))}
|
|
|
25666
26086
|
palette,
|
|
25667
26087
|
glyphs
|
|
25668
26088
|
}, out, {
|
|
25669
|
-
enabled: ttyMode && !readline.line && environment.TERM !== "dumb" && environment.NO_COLOR === undefined && environment.ARCHYMEDES_NO_MOTION !== "1",
|
|
26089
|
+
enabled: ttyMode && (args.layout ?? environment.ARCHYMEDES_LAYOUT) !== "fixed" && !readline.line && environment.TERM !== "dumb" && environment.NO_COLOR === undefined && environment.ARCHYMEDES_NO_MOTION !== "1",
|
|
25670
26090
|
signal: identityMotion.signal,
|
|
25671
26091
|
size: () => ({ width: process.stdout.columns ?? 80, rows: process.stdout.rows ?? 24 })
|
|
25672
26092
|
});
|
|
@@ -25712,13 +26132,44 @@ ${starters}
|
|
|
25712
26132
|
`);
|
|
25713
26133
|
}
|
|
25714
26134
|
const pinFooter = args.pin || (environment.ARCHYMEDES_PIN ?? "") !== "" && environment.ARCHYMEDES_PIN !== "0";
|
|
25715
|
-
|
|
25716
|
-
screen
|
|
26135
|
+
const setLayout = (fixed) => {
|
|
26136
|
+
screen?.exit();
|
|
26137
|
+
setWorkspaceMenu(undefined);
|
|
26138
|
+
screen = fixed ? new WorkspaceFrame(process.stdout, () => ({ version: ARCHYMEDES_CLI_VERSION, workspace: path27.basename(args.root), model: `${spec.label} / ${resolvedModelId}`, mode, palette, glyphs, busy: turnActive }), () => tabs.active.payload.sink.log, environment.ARCHYMEDES_NO_MOTION !== "1" && environment.NO_COLOR === undefined && environment.TERM !== "dumb") : new PinnedScreen2(process.stdout, { holdRegion: pinFooter });
|
|
25717
26139
|
screen.enter();
|
|
26140
|
+
if (screen instanceof WorkspaceFrame)
|
|
26141
|
+
setWorkspaceMenu(screen.menu);
|
|
25718
26142
|
showIdleStatus();
|
|
26143
|
+
};
|
|
26144
|
+
if (ttyMode) {
|
|
26145
|
+
setLayout((args.layout ?? environment.ARCHYMEDES_LAYOUT) === "fixed");
|
|
26146
|
+
const fixedNavigation = (_str, key) => {
|
|
26147
|
+
if (!(screen instanceof WorkspaceFrame))
|
|
26148
|
+
return;
|
|
26149
|
+
screen.stopIntroMotion();
|
|
26150
|
+
if (key?.name === "pageup")
|
|
26151
|
+
screen.navigate("up");
|
|
26152
|
+
else if (key?.name === "pagedown")
|
|
26153
|
+
screen.navigate("down");
|
|
26154
|
+
else if (screen.browsing && key?.name === "escape")
|
|
26155
|
+
screen.navigate("live");
|
|
26156
|
+
};
|
|
26157
|
+
unbindFixedNavigation = () => {
|
|
26158
|
+
process.stdin.off("keypress", fixedNavigation);
|
|
26159
|
+
};
|
|
26160
|
+
bindFixedNavigation = () => {
|
|
26161
|
+
unbindFixedNavigation();
|
|
26162
|
+
process.stdin.on("keypress", fixedNavigation);
|
|
26163
|
+
};
|
|
26164
|
+
bindFixedNavigation();
|
|
25719
26165
|
process.stdout.on("resize", () => {
|
|
25720
26166
|
screen?.resize();
|
|
25721
26167
|
showIdleStatus();
|
|
26168
|
+
if (screen instanceof WorkspaceFrame && !turnActive) {
|
|
26169
|
+
screen.positionInput();
|
|
26170
|
+
readline.prompt(true);
|
|
26171
|
+
showIdleStatus();
|
|
26172
|
+
}
|
|
25722
26173
|
});
|
|
25723
26174
|
const browseSuggestions = async (line, suggestions) => {
|
|
25724
26175
|
if (!screen)
|
|
@@ -25971,6 +26422,8 @@ ${starters}
|
|
|
25971
26422
|
const label = `${style.cyan(modeLabel)}${style.dim(` ${glyphs.caret} `)}`;
|
|
25972
26423
|
const promptLabel = screen?.pinned ? promptFrame(idleStatusLine()).prefix : inlineBar() ? promptBox.draw(mode, where, idleStatusLine()) : screen ? label : `
|
|
25973
26424
|
${label}`;
|
|
26425
|
+
if (queued === undefined && screen instanceof WorkspaceFrame)
|
|
26426
|
+
queueMicrotask(() => showIdleStatus());
|
|
25974
26427
|
rawInput = queued ?? await askForInput(promptLabel);
|
|
25975
26428
|
} catch (error) {
|
|
25976
26429
|
if (isReadlineExit(error) || exitRequested)
|
|
@@ -25979,10 +26432,31 @@ ${label}`;
|
|
|
25979
26432
|
}
|
|
25980
26433
|
promptBox.erase(rawInput);
|
|
25981
26434
|
screen?.clearSuggestions();
|
|
26435
|
+
if (screen instanceof WorkspaceFrame && screen.browsing)
|
|
26436
|
+
screen.navigate("live");
|
|
25982
26437
|
screen?.parkInTranscript();
|
|
25983
26438
|
let input = rawInput.trim();
|
|
25984
26439
|
if (!input)
|
|
25985
26440
|
continue;
|
|
26441
|
+
if (input === "/layout" || input.startsWith("/layout ")) {
|
|
26442
|
+
const choice = input.slice(7).trim();
|
|
26443
|
+
if (!ttyMode) {
|
|
26444
|
+
out.write(` Fixed layout requires an interactive terminal.
|
|
26445
|
+
`);
|
|
26446
|
+
continue;
|
|
26447
|
+
}
|
|
26448
|
+
if (choice && choice !== "fixed" && choice !== "scrollback") {
|
|
26449
|
+
out.write(` Use /layout fixed or /layout scrollback.
|
|
26450
|
+
`);
|
|
26451
|
+
continue;
|
|
26452
|
+
}
|
|
26453
|
+
const fixed = choice ? choice === "fixed" : !(screen instanceof WorkspaceFrame);
|
|
26454
|
+
setLayout(fixed);
|
|
26455
|
+
out.write(style.dim(fixed ? ` Fixed workspace \u2014 mode rail and composer stay put. /layout scrollback returns to the log.
|
|
26456
|
+
` : ` Scrollback layout \u2014 the terminal keeps a normal log. /layout fixed restores the workspace.
|
|
26457
|
+
`));
|
|
26458
|
+
continue;
|
|
26459
|
+
}
|
|
25986
26460
|
usage.record(input);
|
|
25987
26461
|
if (input === "/palette") {
|
|
25988
26462
|
const chosen = interactive ? await openPalette({ readline, input: process.stdin, output: process.stdout, registry: keys }, undefined, {
|
|
@@ -26106,6 +26580,14 @@ ${label}`;
|
|
|
26106
26580
|
}
|
|
26107
26581
|
continue;
|
|
26108
26582
|
}
|
|
26583
|
+
if (input === "/mode" && screen instanceof WorkspaceFrame) {
|
|
26584
|
+
const modes = ["plan", "build", "auto", "defender"];
|
|
26585
|
+
const descriptions = ["Read and reason; no changes", "Edit with your approval", "Apply ordinary changes automatically", "Security review; fixes require approval"];
|
|
26586
|
+
const chosen = await openChooser({ readline, input: process.stdin, output: process.stdout }, modes.map((value, i) => ({ value, label: value, description: descriptions[i], hint: value === mode ? "current" : undefined })), { title: "Choose how Archymedes works", initialIndex: modes.indexOf(mode), paint: surfacePaint, glyphs });
|
|
26587
|
+
if (!chosen)
|
|
26588
|
+
continue;
|
|
26589
|
+
input = `/mode ${chosen}`;
|
|
26590
|
+
}
|
|
26109
26591
|
const modeCommand = parseModeCommand(input);
|
|
26110
26592
|
if (modeCommand?.type === "show") {
|
|
26111
26593
|
const posture2 = mode === "plan" ? "read-only; write and command tools are unavailable" : mode === "build" ? "workspace changes ask for approval" : mode === "defender" ? "security review; every change still asks for approval" : "ordinary workspace changes are pre-approved; sensitive and external actions still ask";
|
|
@@ -26444,7 +26926,7 @@ ${note4(`you ${glyphs.middot} ${files.user}`, style_)}
|
|
|
26444
26926
|
};
|
|
26445
26927
|
};
|
|
26446
26928
|
const outcome = await withFullScreen(screenCapabilities(), terminalControls(), async () => {
|
|
26447
|
-
const { runWorkspace } = await import("./workspace-screen-
|
|
26929
|
+
const { runWorkspace } = await import("./workspace-screen-w88mcw4w.mjs");
|
|
26448
26930
|
await runWorkspace({ read: readSnapshot });
|
|
26449
26931
|
});
|
|
26450
26932
|
if (!outcome.ok)
|
|
@@ -26457,7 +26939,7 @@ ${note4(`you ${glyphs.middot} ${files.user}`, style_)}
|
|
|
26457
26939
|
const style_ = sectionStyle();
|
|
26458
26940
|
const openGuideScreen = async (startAt) => {
|
|
26459
26941
|
const outcome = await withFullScreen(screenCapabilities(), terminalControls(), async () => {
|
|
26460
|
-
const { runGuideScreen } = await import("./guide-screen-
|
|
26942
|
+
const { runGuideScreen } = await import("./guide-screen-aj9n9v80.mjs");
|
|
26461
26943
|
await runGuideScreen({
|
|
26462
26944
|
columns: process.stdout.columns ?? 80,
|
|
26463
26945
|
rows: process.stdout.rows ?? 24,
|
|
@@ -26519,7 +27001,7 @@ ${note4(`you ${glyphs.middot} ${files.user}`, style_)}
|
|
|
26519
27001
|
if (input === "/files") {
|
|
26520
27002
|
let picked;
|
|
26521
27003
|
const outcome = await withFullScreen(screenCapabilities(), terminalControls(), async () => {
|
|
26522
|
-
const { runFileScreen } = await import("./file-screen-
|
|
27004
|
+
const { runFileScreen } = await import("./file-screen-m7q1xcer.mjs");
|
|
26523
27005
|
picked = await runFileScreen({
|
|
26524
27006
|
columns: process.stdout.columns ?? 80,
|
|
26525
27007
|
rows: process.stdout.rows ?? 24,
|
|
@@ -26554,6 +27036,36 @@ ${note4(`you ${glyphs.middot} ${files.user}`, style_)}
|
|
|
26554
27036
|
await editFile(target);
|
|
26555
27037
|
continue;
|
|
26556
27038
|
}
|
|
27039
|
+
if (input === "/cat" || input.startsWith("/cat ")) {
|
|
27040
|
+
const target = input.slice("/cat".length).trim();
|
|
27041
|
+
if (!target) {
|
|
27042
|
+
out.write(style.yellow(` Usage: /cat <path>
|
|
27043
|
+
`));
|
|
27044
|
+
continue;
|
|
27045
|
+
}
|
|
27046
|
+
const style_ = sectionStyle();
|
|
27047
|
+
try {
|
|
27048
|
+
const file = await workspace.readFile(target, {});
|
|
27049
|
+
out.write(`${rule2(style_, { label: target, tone: "accent" })}
|
|
27050
|
+
`);
|
|
27051
|
+
if (/\.(md|markdown)$/i.test(target)) {
|
|
27052
|
+
out.write(`${renderMarkdown2(file.content, { width: contentWidth(), depth: renderDepth, glyphs })}
|
|
27053
|
+
`);
|
|
27054
|
+
} else {
|
|
27055
|
+
const language2 = languageOf(target);
|
|
27056
|
+
out.write(`${fenceHeader(language2, style_)}
|
|
27057
|
+
`);
|
|
27058
|
+
writeFoldable(target, renderCode2(file.content, style_, { maxLines: FOLD_AFTER_LINES, language: language2 }));
|
|
27059
|
+
}
|
|
27060
|
+
if (file.truncated)
|
|
27061
|
+
out.write(`${note4(`\u2026 ${file.totalLines} lines total, longer than a session cares to hold at once`, style_)}
|
|
27062
|
+
`);
|
|
27063
|
+
} catch (error) {
|
|
27064
|
+
out.write(style.yellow(` Could not read "${target}": ${error instanceof Error ? error.message : String(error)}
|
|
27065
|
+
`));
|
|
27066
|
+
}
|
|
27067
|
+
continue;
|
|
27068
|
+
}
|
|
26557
27069
|
const themeCommand = parseThemeCommand(input);
|
|
26558
27070
|
if (themeCommand) {
|
|
26559
27071
|
const style_ = sectionStyle();
|
|
@@ -26884,7 +27396,7 @@ ${note4(`you ${glyphs.middot} ${files.user}`, style_)}
|
|
|
26884
27396
|
agent: newTabClient,
|
|
26885
27397
|
ledger: new CostLedger2({ prices: wanted.prices, display, rates, catalog: PRICE_CATALOG2, ...approvedBudget ? { budget: approvedBudget } : {} }),
|
|
26886
27398
|
mode,
|
|
26887
|
-
sink: new TabSink(
|
|
27399
|
+
sink: new TabSink(sessionStream),
|
|
26888
27400
|
provider: wanted.provider,
|
|
26889
27401
|
spec: wanted.spec,
|
|
26890
27402
|
prices: wanted.prices,
|