@compilr-dev/cli 0.7.3 → 0.7.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent.d.ts +7 -2
- package/dist/agent.js +55 -39
- package/dist/commands-v2/handlers/project.js +65 -2
- package/dist/commands-v2/handlers/settings.js +18 -26
- package/dist/compilr-diff-companion.vsix +0 -0
- package/dist/db/schema.d.ts +1 -1
- package/dist/episodes/index.d.ts +13 -13
- package/dist/episodes/index.js +13 -14
- package/dist/guide/cli-guide-entries.js +54 -2
- package/dist/handlers/delegation-handlers.js +228 -50
- package/dist/handlers/interactive-flow-handlers.d.ts +26 -0
- package/dist/handlers/interactive-flow-handlers.js +61 -0
- package/dist/handlers/propose-alternatives-handlers.d.ts +36 -0
- package/dist/handlers/propose-alternatives-handlers.js +65 -0
- package/dist/index.js +13 -2
- package/dist/repl-v2.d.ts +66 -0
- package/dist/repl-v2.js +382 -53
- package/dist/shared-handlers.d.ts +57 -5
- package/dist/shared-handlers.js +50 -0
- package/dist/tools/consult.d.ts +14 -0
- package/dist/tools/consult.js +73 -0
- package/dist/tools/delegate.d.ts +12 -6
- package/dist/tools/delegate.js +35 -19
- package/dist/tools/interactive-flow.d.ts +13 -0
- package/dist/tools/interactive-flow.js +19 -0
- package/dist/tools/platform-adapter.d.ts +14 -2
- package/dist/tools/platform-adapter.js +16 -4
- package/dist/tools/propose-alternatives.d.ts +13 -0
- package/dist/tools/propose-alternatives.js +19 -0
- package/dist/tools.d.ts +22 -98
- package/dist/tools.js +27 -382
- package/dist/ui/markdown-renderer.js +26 -7
- package/dist/ui/overlay/data/tutorial-registry.js +2 -0
- package/dist/ui/overlay/data/tutorials/projects/interactive-flows.d.ts +9 -0
- package/dist/ui/overlay/data/tutorials/projects/interactive-flows.js +94 -0
- package/dist/ui/overlay/data/tutorials/projects/new-project.js +56 -20
- package/dist/ui/overlay/impl/interactive-flow-overlay-v2.d.ts +79 -0
- package/dist/ui/overlay/impl/interactive-flow-overlay-v2.js +580 -0
- package/dist/ui/overlay/impl/new-overlay-v2.d.ts +32 -0
- package/dist/ui/overlay/impl/new-overlay-v2.js +305 -66
- package/dist/ui/overlay/impl/propose-alternatives-overlay-v2.d.ts +53 -0
- package/dist/ui/overlay/impl/propose-alternatives-overlay-v2.js +326 -0
- package/dist/ui/overlay/index.d.ts +2 -0
- package/dist/ui/overlay/index.js +2 -0
- package/dist/ui/tool-formatters.js +61 -3
- package/package.json +3 -3
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Propose Alternatives Overlay V2
|
|
3
|
+
*
|
|
4
|
+
* Modal overlay for picking one of 2-3 agent-proposed alternatives.
|
|
5
|
+
* One tab per alternative (vs ask_user's one tab per question) — tabs
|
|
6
|
+
* are for COMPARING different answers to the SAME single question, not
|
|
7
|
+
* for moving between distinct questions.
|
|
8
|
+
*
|
|
9
|
+
* Spec: project-docs/00-requirements/compilr-dev-cli/propose-alternatives-spec.md
|
|
10
|
+
*/
|
|
11
|
+
import chalk from 'chalk';
|
|
12
|
+
import { highlight } from 'cli-highlight';
|
|
13
|
+
import { BaseOverlayV2, renderBorder, wrapText } from '../../base/index.js';
|
|
14
|
+
import { renderMarkdown } from '../../conversation.js';
|
|
15
|
+
// =============================================================================
|
|
16
|
+
// Overlay Implementation
|
|
17
|
+
// =============================================================================
|
|
18
|
+
export class ProposeAlternativesOverlayV2 extends BaseOverlayV2 {
|
|
19
|
+
type = 'inline';
|
|
20
|
+
id = 'propose-alternatives-overlay-v2';
|
|
21
|
+
question;
|
|
22
|
+
context;
|
|
23
|
+
alternatives;
|
|
24
|
+
constructor(options) {
|
|
25
|
+
super({
|
|
26
|
+
currentTab: 0,
|
|
27
|
+
isAddingNotes: false,
|
|
28
|
+
notesBuffer: '',
|
|
29
|
+
});
|
|
30
|
+
this.question = options.question;
|
|
31
|
+
this.context = options.context;
|
|
32
|
+
this.alternatives = options.alternatives;
|
|
33
|
+
this.minHeight = 20;
|
|
34
|
+
}
|
|
35
|
+
// ───────────────────────────────────────────────────────────────────────────
|
|
36
|
+
// Public API (BaseOverlayV2)
|
|
37
|
+
// ───────────────────────────────────────────────────────────────────────────
|
|
38
|
+
renderContent(context) {
|
|
39
|
+
const s = context.styles;
|
|
40
|
+
const cols = context.width;
|
|
41
|
+
const border = renderBorder(cols, s);
|
|
42
|
+
const lines = [];
|
|
43
|
+
lines.push(border);
|
|
44
|
+
// Header — single question, persistent
|
|
45
|
+
const headerText = `Decide: ${this.question}`;
|
|
46
|
+
for (const line of wrapText(headerText, cols - 2)) {
|
|
47
|
+
lines.push(' ' + s.primaryBold(line));
|
|
48
|
+
}
|
|
49
|
+
lines.push('');
|
|
50
|
+
// Context block (optional)
|
|
51
|
+
if (this.context && this.context.trim().length > 0) {
|
|
52
|
+
lines.push(' ' + s.muted('Context'));
|
|
53
|
+
for (const line of wrapText(this.context, cols - 4)) {
|
|
54
|
+
lines.push(' ' + s.muted(line));
|
|
55
|
+
}
|
|
56
|
+
lines.push('');
|
|
57
|
+
}
|
|
58
|
+
// Tab bar (alternatives)
|
|
59
|
+
lines.push(...this.renderTabBar(s, cols));
|
|
60
|
+
lines.push('');
|
|
61
|
+
// Body for the active tab
|
|
62
|
+
lines.push(...this.renderActiveAlternative(s, cols));
|
|
63
|
+
// Notes-input row or footer
|
|
64
|
+
lines.push('');
|
|
65
|
+
if (this.state.isAddingNotes) {
|
|
66
|
+
lines.push(...this.renderNotesInput(s, cols));
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
lines.push(...this.renderInstructions(s));
|
|
70
|
+
}
|
|
71
|
+
lines.push(border);
|
|
72
|
+
return lines;
|
|
73
|
+
}
|
|
74
|
+
handleKey(key) {
|
|
75
|
+
// Ctrl+C → hard cancel (reject, even mid-notes)
|
|
76
|
+
if (key.ctrl && key.name === 'c') {
|
|
77
|
+
return this.close(this.makeRejected());
|
|
78
|
+
}
|
|
79
|
+
if (this.state.isAddingNotes) {
|
|
80
|
+
return this.handleNotesKey(key);
|
|
81
|
+
}
|
|
82
|
+
return this.handleNavigationKey(key);
|
|
83
|
+
}
|
|
84
|
+
getCloseSummary(result) {
|
|
85
|
+
const s = this.getStyles();
|
|
86
|
+
if (result.rejected) {
|
|
87
|
+
return s.muted('Alternative: ') + s.warning('rejected');
|
|
88
|
+
}
|
|
89
|
+
const suffix = result.notes !== undefined && result.notes.trim().length > 0 ? ' (with notes)' : '';
|
|
90
|
+
return s.muted('Alternative: ') + s.success(result.chosenTitle + suffix);
|
|
91
|
+
}
|
|
92
|
+
// ───────────────────────────────────────────────────────────────────────────
|
|
93
|
+
// Key handlers
|
|
94
|
+
// ───────────────────────────────────────────────────────────────────────────
|
|
95
|
+
handleNavigationKey(key) {
|
|
96
|
+
const n = this.alternatives.length;
|
|
97
|
+
// Tab navigation
|
|
98
|
+
if (key.name === 'tab' && !key.shift) {
|
|
99
|
+
this.state.currentTab = (this.state.currentTab + 1) % n;
|
|
100
|
+
return this.rerender();
|
|
101
|
+
}
|
|
102
|
+
if ((key.name === 'tab' && key.shift) || key.name === 'left') {
|
|
103
|
+
this.state.currentTab = (this.state.currentTab - 1 + n) % n;
|
|
104
|
+
return this.rerender();
|
|
105
|
+
}
|
|
106
|
+
if (key.name === 'right') {
|
|
107
|
+
this.state.currentTab = (this.state.currentTab + 1) % n;
|
|
108
|
+
return this.rerender();
|
|
109
|
+
}
|
|
110
|
+
// Number-key jumps (1..N)
|
|
111
|
+
if (key.char && /^[1-9]$/.test(key.char)) {
|
|
112
|
+
const idx = parseInt(key.char, 10) - 1;
|
|
113
|
+
if (idx >= 0 && idx < n) {
|
|
114
|
+
this.state.currentTab = idx;
|
|
115
|
+
return this.rerender();
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
// Enter → pick the current tab
|
|
119
|
+
if (key.name === 'return' || key.name === 'enter') {
|
|
120
|
+
return this.close(this.makePicked(undefined));
|
|
121
|
+
}
|
|
122
|
+
// 'n' → enter notes-input mode
|
|
123
|
+
if (key.char === 'n' && !key.ctrl && !key.meta) {
|
|
124
|
+
this.state.isAddingNotes = true;
|
|
125
|
+
return this.rerender();
|
|
126
|
+
}
|
|
127
|
+
// 'r' / 'q' / Esc → reject all
|
|
128
|
+
if ((key.char === 'r' || key.char === 'q') &&
|
|
129
|
+
!key.ctrl &&
|
|
130
|
+
!key.meta) {
|
|
131
|
+
return this.close(this.makeRejected());
|
|
132
|
+
}
|
|
133
|
+
if (key.name === 'escape') {
|
|
134
|
+
return this.close(this.makeRejected());
|
|
135
|
+
}
|
|
136
|
+
return this.noAction();
|
|
137
|
+
}
|
|
138
|
+
handleNotesKey(key) {
|
|
139
|
+
// Esc → cancel notes, return to navigation (don't commit a pick)
|
|
140
|
+
if (key.name === 'escape') {
|
|
141
|
+
this.state.isAddingNotes = false;
|
|
142
|
+
this.state.notesBuffer = '';
|
|
143
|
+
return this.rerender();
|
|
144
|
+
}
|
|
145
|
+
// Enter → commit pick with notes
|
|
146
|
+
if (key.name === 'return' || key.name === 'enter') {
|
|
147
|
+
return this.close(this.makePicked(this.state.notesBuffer));
|
|
148
|
+
}
|
|
149
|
+
// Backspace → delete last char
|
|
150
|
+
if (key.name === 'backspace') {
|
|
151
|
+
this.state.notesBuffer = this.state.notesBuffer.slice(0, -1);
|
|
152
|
+
return this.rerender();
|
|
153
|
+
}
|
|
154
|
+
// Printable → append to buffer
|
|
155
|
+
if (key.char && key.char.length === 1 && !key.ctrl && !key.meta) {
|
|
156
|
+
this.state.notesBuffer += key.char;
|
|
157
|
+
return this.rerender();
|
|
158
|
+
}
|
|
159
|
+
return this.noAction();
|
|
160
|
+
}
|
|
161
|
+
// ───────────────────────────────────────────────────────────────────────────
|
|
162
|
+
// Rendering helpers
|
|
163
|
+
// ───────────────────────────────────────────────────────────────────────────
|
|
164
|
+
renderTabBar(s, cols) {
|
|
165
|
+
let line = ' ';
|
|
166
|
+
for (let i = 0; i < this.alternatives.length; i++) {
|
|
167
|
+
const alt = this.alternatives[i];
|
|
168
|
+
const isCurrent = i === this.state.currentTab;
|
|
169
|
+
// Truncate per-tab title to keep the row at one line for 2-3 alts
|
|
170
|
+
const maxTitle = 25;
|
|
171
|
+
const title = alt.title.length > maxTitle ? alt.title.slice(0, maxTitle - 1) + '…' : alt.title;
|
|
172
|
+
const tabContent = ` ${String(i + 1)} · ${title} `;
|
|
173
|
+
if (isCurrent) {
|
|
174
|
+
line += s.selected(tabContent) + ' ';
|
|
175
|
+
}
|
|
176
|
+
else {
|
|
177
|
+
line += s.muted(tabContent) + ' ';
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
// If too wide for terminal, just truncate — the user can still see the active tab
|
|
181
|
+
if (visibleLength(line) > cols) {
|
|
182
|
+
// Crude fallback: keep the active tab visible by truncating leading tabs
|
|
183
|
+
// (a fancier renderer would scroll; keep it simple for v1)
|
|
184
|
+
line = line.slice(0, cols - 1) + '…';
|
|
185
|
+
}
|
|
186
|
+
return [line];
|
|
187
|
+
}
|
|
188
|
+
renderActiveAlternative(s, cols) {
|
|
189
|
+
const out = [];
|
|
190
|
+
const alt = this.alternatives[this.state.currentTab];
|
|
191
|
+
// Title (re-stated in body)
|
|
192
|
+
out.push(' ' + s.primaryBold(alt.title));
|
|
193
|
+
out.push('');
|
|
194
|
+
// Description
|
|
195
|
+
for (const line of wrapText(alt.description, cols - 4)) {
|
|
196
|
+
out.push(' ' + line);
|
|
197
|
+
}
|
|
198
|
+
out.push('');
|
|
199
|
+
// Content (markdown / code / mermaid)
|
|
200
|
+
if (alt.content && alt.content.trim().length > 0) {
|
|
201
|
+
out.push(...this.renderContentBlock(alt, s, cols));
|
|
202
|
+
out.push('');
|
|
203
|
+
}
|
|
204
|
+
// Pros
|
|
205
|
+
if (alt.pros && alt.pros.length > 0) {
|
|
206
|
+
out.push(' ' + s.muted('Pros'));
|
|
207
|
+
for (const pro of alt.pros) {
|
|
208
|
+
for (let i = 0; i < wrapText(pro, cols - 6).length; i++) {
|
|
209
|
+
const wrapped = wrapText(pro, cols - 6)[i];
|
|
210
|
+
const prefix = i === 0 ? ' + ' : ' ';
|
|
211
|
+
out.push(prefix + s.success(wrapped));
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
out.push('');
|
|
215
|
+
}
|
|
216
|
+
// Cons
|
|
217
|
+
if (alt.cons && alt.cons.length > 0) {
|
|
218
|
+
out.push(' ' + s.muted('Cons'));
|
|
219
|
+
for (const con of alt.cons) {
|
|
220
|
+
for (let i = 0; i < wrapText(con, cols - 6).length; i++) {
|
|
221
|
+
const wrapped = wrapText(con, cols - 6)[i];
|
|
222
|
+
const prefix = i === 0 ? ' − ' : ' ';
|
|
223
|
+
out.push(prefix + s.warning(wrapped));
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
return out;
|
|
228
|
+
}
|
|
229
|
+
renderContentBlock(alt, s, _cols) {
|
|
230
|
+
const out = [];
|
|
231
|
+
const content = alt.content ?? '';
|
|
232
|
+
const contentType = alt.contentType ?? 'markdown';
|
|
233
|
+
if (contentType === 'mermaid') {
|
|
234
|
+
out.push(' ' + s.muted('Diagram (mermaid)'));
|
|
235
|
+
for (const raw of content.split('\n')) {
|
|
236
|
+
out.push(' ' + s.muted(raw));
|
|
237
|
+
}
|
|
238
|
+
out.push(' ' + s.muted('(diagram preview unavailable in terminal — open Desktop to view rendered)'));
|
|
239
|
+
return out;
|
|
240
|
+
}
|
|
241
|
+
if (contentType === 'code') {
|
|
242
|
+
out.push(' ' + s.muted('Code' + (alt.language ? ` · ${alt.language}` : '')));
|
|
243
|
+
let rendered;
|
|
244
|
+
try {
|
|
245
|
+
rendered = highlight(content, {
|
|
246
|
+
language: alt.language,
|
|
247
|
+
ignoreIllegals: true,
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
catch {
|
|
251
|
+
rendered = content;
|
|
252
|
+
}
|
|
253
|
+
for (const raw of rendered.split('\n')) {
|
|
254
|
+
// Don't re-wrap code — preserve original alignment
|
|
255
|
+
out.push(' ' + raw);
|
|
256
|
+
}
|
|
257
|
+
return out;
|
|
258
|
+
}
|
|
259
|
+
// markdown (default)
|
|
260
|
+
let rendered;
|
|
261
|
+
try {
|
|
262
|
+
rendered = renderMarkdown(content);
|
|
263
|
+
}
|
|
264
|
+
catch {
|
|
265
|
+
rendered = content;
|
|
266
|
+
}
|
|
267
|
+
for (const raw of rendered.split('\n')) {
|
|
268
|
+
// Trim trailing whitespace but preserve the rendered indentation
|
|
269
|
+
out.push(' ' + raw.replace(/\s+$/, ''));
|
|
270
|
+
}
|
|
271
|
+
return out;
|
|
272
|
+
}
|
|
273
|
+
renderNotesInput(s, cols) {
|
|
274
|
+
const prefix = ' Notes: ';
|
|
275
|
+
const available = Math.max(20, cols - prefix.length - 3);
|
|
276
|
+
const buffer = this.state.notesBuffer;
|
|
277
|
+
const visible = buffer.length > available ? '…' + buffer.slice(-(available - 1)) : buffer;
|
|
278
|
+
return [
|
|
279
|
+
prefix + visible + chalk.inverse(' '),
|
|
280
|
+
' ' + s.muted('[Enter] confirm · [Esc] cancel notes (no selection)'),
|
|
281
|
+
];
|
|
282
|
+
}
|
|
283
|
+
renderInstructions(s) {
|
|
284
|
+
const parts = [
|
|
285
|
+
'[Tab/←→] navigate',
|
|
286
|
+
'[1-' + String(this.alternatives.length) + '] jump',
|
|
287
|
+
'[Enter] pick',
|
|
288
|
+
'[n] notes',
|
|
289
|
+
'[r] reject',
|
|
290
|
+
'[q] cancel',
|
|
291
|
+
];
|
|
292
|
+
return [' ' + s.muted(parts.join(' · '))];
|
|
293
|
+
}
|
|
294
|
+
// ───────────────────────────────────────────────────────────────────────────
|
|
295
|
+
// Result builders
|
|
296
|
+
// ───────────────────────────────────────────────────────────────────────────
|
|
297
|
+
makePicked(notes) {
|
|
298
|
+
const idx = this.state.currentTab;
|
|
299
|
+
const alt = this.alternatives[idx];
|
|
300
|
+
const trimmed = notes?.trim();
|
|
301
|
+
return {
|
|
302
|
+
chosenIndex: idx,
|
|
303
|
+
chosenTitle: alt.title,
|
|
304
|
+
notes: trimmed && trimmed.length > 0 ? trimmed : undefined,
|
|
305
|
+
rejected: false,
|
|
306
|
+
};
|
|
307
|
+
}
|
|
308
|
+
makeRejected() {
|
|
309
|
+
// SDK contract: chosenIndex + chosenTitle must be populated even on
|
|
310
|
+
// rejection. The agent's prompt explicitly says these fields are
|
|
311
|
+
// ignored when rejected=true, but the schema requires them.
|
|
312
|
+
return {
|
|
313
|
+
chosenIndex: 0,
|
|
314
|
+
chosenTitle: this.alternatives[0]?.title ?? '',
|
|
315
|
+
rejected: true,
|
|
316
|
+
};
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
// =============================================================================
|
|
320
|
+
// Helpers (module-private)
|
|
321
|
+
// =============================================================================
|
|
322
|
+
/** Approximate visible length of an ANSI-coloured string. */
|
|
323
|
+
function visibleLength(s) {
|
|
324
|
+
// eslint-disable-next-line no-control-regex
|
|
325
|
+
return s.replace(/\x1b\[[0-9;]*m/g, '').length;
|
|
326
|
+
}
|
|
@@ -26,6 +26,8 @@ export { ConfigOverlayV2, type ConfigOverlayV2Options, type ConfigOverlayV2Resul
|
|
|
26
26
|
export { AskUserSimpleOverlayV2, type AskUserSimpleOptionsV2, type AskUserSimpleResultV2 } from './impl/ask-user-simple-overlay-v2.js';
|
|
27
27
|
export { GuardrailOverlayV2, type GuardrailConfirmOptionsV2, type GuardrailConfirmResultV2 } from './impl/guardrail-overlay-v2.js';
|
|
28
28
|
export { AskUserOverlayV2, type AskUserOptionsV2, type AskUserResultV2 } from './impl/ask-user-overlay-v2.js';
|
|
29
|
+
export { ProposeAlternativesOverlayV2, type ProposeAlternativesOptionsV2, type ProposeAlternativesResultV2, } from './impl/propose-alternatives-overlay-v2.js';
|
|
30
|
+
export { InteractiveFlowOverlayV2, type InteractiveFlowOptionsV2, type InteractiveFlowResultV2, } from './impl/interactive-flow-overlay-v2.js';
|
|
29
31
|
export { IterationLimitOverlayV2, type IterationLimitOptionsV2, type IterationLimitResultV2 } from './impl/iteration-limit-overlay-v2.js';
|
|
30
32
|
export { AppModelOverlayV2, type AppModelOverlayV2Options, type AppModelOverlayV2Result } from './impl/app-model-overlay-v2.js';
|
|
31
33
|
export { ModelWarningOverlayV2, type ModelWarningOptionsV2, type ModelWarningChoiceV2 } from './impl/model-warning-overlay-v2.js';
|
package/dist/ui/overlay/index.js
CHANGED
|
@@ -28,6 +28,8 @@ export { ConfigOverlayV2 } from './impl/config-overlay-v2.js';
|
|
|
28
28
|
export { AskUserSimpleOverlayV2 } from './impl/ask-user-simple-overlay-v2.js';
|
|
29
29
|
export { GuardrailOverlayV2 } from './impl/guardrail-overlay-v2.js';
|
|
30
30
|
export { AskUserOverlayV2 } from './impl/ask-user-overlay-v2.js';
|
|
31
|
+
export { ProposeAlternativesOverlayV2, } from './impl/propose-alternatives-overlay-v2.js';
|
|
32
|
+
export { InteractiveFlowOverlayV2, } from './impl/interactive-flow-overlay-v2.js';
|
|
31
33
|
export { IterationLimitOverlayV2 } from './impl/iteration-limit-overlay-v2.js';
|
|
32
34
|
export { AppModelOverlayV2 } from './impl/app-model-overlay-v2.js';
|
|
33
35
|
export { ModelWarningOverlayV2 } from './impl/model-warning-overlay-v2.js';
|
|
@@ -202,6 +202,12 @@ export function formatToolResult(toolName, result) {
|
|
|
202
202
|
else if (toolLower === 'ask_user' || toolLower === 'ask_user_simple') {
|
|
203
203
|
({ content, summary } = formatAskUser(innerResult));
|
|
204
204
|
}
|
|
205
|
+
else if (toolLower === 'propose_alternatives') {
|
|
206
|
+
({ content, summary } = formatProposeAlternatives(innerResult));
|
|
207
|
+
}
|
|
208
|
+
else if (toolLower === 'build_interactive_flow') {
|
|
209
|
+
({ content, summary } = formatInteractiveFlow(innerResult));
|
|
210
|
+
}
|
|
205
211
|
else if (toolLower === 'compilr_guide') {
|
|
206
212
|
({ content, summary } = formatCompilrGuide(innerResult));
|
|
207
213
|
}
|
|
@@ -1268,22 +1274,74 @@ function formatWebFetch(result) {
|
|
|
1268
1274
|
return { content, summary: `${urlShort}${statusInfo}${truncInfo}` };
|
|
1269
1275
|
}
|
|
1270
1276
|
function formatAskUser(result) {
|
|
1271
|
-
//
|
|
1277
|
+
// ask_user_simple returns { answer: string, skipped: boolean }
|
|
1278
|
+
// ask_user returns { answers: Record<string, string|string[]>, skipped: string[] }
|
|
1272
1279
|
const answers = result.answers;
|
|
1273
1280
|
const answer = result.answer;
|
|
1274
|
-
|
|
1281
|
+
const skipped = result.skipped;
|
|
1282
|
+
// ask_user_simple
|
|
1283
|
+
if (answer !== undefined) {
|
|
1284
|
+
if (skipped === true)
|
|
1285
|
+
return { content: '', summary: 'Skipped' };
|
|
1275
1286
|
return { content: '', summary: `Answer: ${answer.slice(0, 60)}` };
|
|
1276
1287
|
}
|
|
1288
|
+
// ask_user
|
|
1277
1289
|
if (answers) {
|
|
1278
1290
|
const entries = Object.entries(answers);
|
|
1291
|
+
// Single-question case — show the value inline as before.
|
|
1279
1292
|
if (entries.length === 1) {
|
|
1280
1293
|
const val = Array.isArray(entries[0][1]) ? entries[0][1].join(', ') : entries[0][1];
|
|
1281
1294
|
return { content: '', summary: `Answer: ${val.slice(0, 60)}` };
|
|
1282
1295
|
}
|
|
1283
|
-
|
|
1296
|
+
// Multi-question case — list each "id: value" pair in the expanded
|
|
1297
|
+
// body so the user can see what they answered without the agent
|
|
1298
|
+
// having to echo it back. The summary keeps the count.
|
|
1299
|
+
const lines = entries.map(([id, value]) => {
|
|
1300
|
+
const val = Array.isArray(value) ? value.join(', ') : value;
|
|
1301
|
+
return `${id}: ${val}`;
|
|
1302
|
+
});
|
|
1303
|
+
// Tack on a skipped line if anything was skipped.
|
|
1304
|
+
if (Array.isArray(skipped) && skipped.length > 0) {
|
|
1305
|
+
lines.push(`(skipped: ${skipped.join(', ')})`);
|
|
1306
|
+
}
|
|
1307
|
+
return {
|
|
1308
|
+
content: lines.join('\n'),
|
|
1309
|
+
summary: `${String(entries.length)} answer${entries.length === 1 ? '' : 's'}`,
|
|
1310
|
+
};
|
|
1284
1311
|
}
|
|
1285
1312
|
return { content: '', summary: 'User responded' };
|
|
1286
1313
|
}
|
|
1314
|
+
function formatProposeAlternatives(result) {
|
|
1315
|
+
// The SDK reshapes the handler's ProposeAlternativesResult before
|
|
1316
|
+
// wrapping it (see compilr-dev-sdk/src/tools/propose-alternatives-tool.ts:175).
|
|
1317
|
+
// The result we see here is:
|
|
1318
|
+
// pick: { chosen: <title>, chosenIndex: N, notes?: string, rejected: false }
|
|
1319
|
+
// reject: { chosen: null, rejected: true, feedback: string }
|
|
1320
|
+
const rejected = result.rejected === true;
|
|
1321
|
+
const chosen = typeof result.chosen === 'string' ? result.chosen : '';
|
|
1322
|
+
const notes = typeof result.notes === 'string' ? result.notes.trim() : '';
|
|
1323
|
+
if (rejected) {
|
|
1324
|
+
return { content: '', summary: 'Rejected' };
|
|
1325
|
+
}
|
|
1326
|
+
if (notes.length > 0) {
|
|
1327
|
+
return {
|
|
1328
|
+
content: `notes: ${notes}`,
|
|
1329
|
+
summary: `Picked: ${chosen.slice(0, 50)} (with notes)`,
|
|
1330
|
+
};
|
|
1331
|
+
}
|
|
1332
|
+
return { content: '', summary: `Picked: ${chosen.slice(0, 60)}` };
|
|
1333
|
+
}
|
|
1334
|
+
function formatInteractiveFlow(result) {
|
|
1335
|
+
// The SDK passes the handler's InteractiveFlowResult through verbatim.
|
|
1336
|
+
// The tool is marked silent:true in the SDK so this only runs if that
|
|
1337
|
+
// flag changes in the future. Keep it in sync with the spec's close-summary.
|
|
1338
|
+
const completed = result.completed === true;
|
|
1339
|
+
const path = Array.isArray(result.path) ? result.path : [];
|
|
1340
|
+
if (!completed) {
|
|
1341
|
+
return { content: '', summary: `Cancelled at step ${String(path.length)}` };
|
|
1342
|
+
}
|
|
1343
|
+
return { content: '', summary: `Completed — ${String(path.length)} steps` };
|
|
1344
|
+
}
|
|
1287
1345
|
function formatCompilrGuide(result) {
|
|
1288
1346
|
const output = result.output;
|
|
1289
1347
|
const content = output || '';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@compilr-dev/cli",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.5",
|
|
4
4
|
"description": "AI-powered coding assistant CLI using @compilr-dev/agents",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -55,12 +55,12 @@
|
|
|
55
55
|
},
|
|
56
56
|
"dependencies": {
|
|
57
57
|
"@anthropic-ai/sdk": "^0.74.0",
|
|
58
|
-
"@compilr-dev/agents": "^0.5.
|
|
58
|
+
"@compilr-dev/agents": "^0.5.9",
|
|
59
59
|
"@compilr-dev/agents-coding": "^1.0.4",
|
|
60
60
|
"@compilr-dev/editor-core": "^0.0.2",
|
|
61
61
|
"@compilr-dev/factory": "^0.1.30",
|
|
62
62
|
"@compilr-dev/logger": "^0.1.0",
|
|
63
|
-
"@compilr-dev/sdk": "^0.10.
|
|
63
|
+
"@compilr-dev/sdk": "^0.10.39",
|
|
64
64
|
"@compilr-dev/ui-core": "^0.0.1",
|
|
65
65
|
"@modelcontextprotocol/sdk": "^1.23.0",
|
|
66
66
|
"ansi-escapes": "^7.3.0",
|