@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.
Files changed (46) hide show
  1. package/dist/agent.d.ts +7 -2
  2. package/dist/agent.js +55 -39
  3. package/dist/commands-v2/handlers/project.js +65 -2
  4. package/dist/commands-v2/handlers/settings.js +18 -26
  5. package/dist/compilr-diff-companion.vsix +0 -0
  6. package/dist/db/schema.d.ts +1 -1
  7. package/dist/episodes/index.d.ts +13 -13
  8. package/dist/episodes/index.js +13 -14
  9. package/dist/guide/cli-guide-entries.js +54 -2
  10. package/dist/handlers/delegation-handlers.js +228 -50
  11. package/dist/handlers/interactive-flow-handlers.d.ts +26 -0
  12. package/dist/handlers/interactive-flow-handlers.js +61 -0
  13. package/dist/handlers/propose-alternatives-handlers.d.ts +36 -0
  14. package/dist/handlers/propose-alternatives-handlers.js +65 -0
  15. package/dist/index.js +13 -2
  16. package/dist/repl-v2.d.ts +66 -0
  17. package/dist/repl-v2.js +382 -53
  18. package/dist/shared-handlers.d.ts +57 -5
  19. package/dist/shared-handlers.js +50 -0
  20. package/dist/tools/consult.d.ts +14 -0
  21. package/dist/tools/consult.js +73 -0
  22. package/dist/tools/delegate.d.ts +12 -6
  23. package/dist/tools/delegate.js +35 -19
  24. package/dist/tools/interactive-flow.d.ts +13 -0
  25. package/dist/tools/interactive-flow.js +19 -0
  26. package/dist/tools/platform-adapter.d.ts +14 -2
  27. package/dist/tools/platform-adapter.js +16 -4
  28. package/dist/tools/propose-alternatives.d.ts +13 -0
  29. package/dist/tools/propose-alternatives.js +19 -0
  30. package/dist/tools.d.ts +22 -98
  31. package/dist/tools.js +27 -382
  32. package/dist/ui/markdown-renderer.js +26 -7
  33. package/dist/ui/overlay/data/tutorial-registry.js +2 -0
  34. package/dist/ui/overlay/data/tutorials/projects/interactive-flows.d.ts +9 -0
  35. package/dist/ui/overlay/data/tutorials/projects/interactive-flows.js +94 -0
  36. package/dist/ui/overlay/data/tutorials/projects/new-project.js +56 -20
  37. package/dist/ui/overlay/impl/interactive-flow-overlay-v2.d.ts +79 -0
  38. package/dist/ui/overlay/impl/interactive-flow-overlay-v2.js +580 -0
  39. package/dist/ui/overlay/impl/new-overlay-v2.d.ts +32 -0
  40. package/dist/ui/overlay/impl/new-overlay-v2.js +305 -66
  41. package/dist/ui/overlay/impl/propose-alternatives-overlay-v2.d.ts +53 -0
  42. package/dist/ui/overlay/impl/propose-alternatives-overlay-v2.js +326 -0
  43. package/dist/ui/overlay/index.d.ts +2 -0
  44. package/dist/ui/overlay/index.js +2 -0
  45. package/dist/ui/tool-formatters.js +61 -3
  46. package/package.json +3 -3
package/dist/tools.js CHANGED
@@ -1,387 +1,32 @@
1
1
  /**
2
- * Tool Registry Setup
2
+ * Tool Registry Helpers (slim).
3
3
  *
4
- * Configures all available tools from @compilr-dev/sdk
5
- * (which re-exports from @compilr-dev/agents and @compilr-dev/agents-coding).
4
+ * Pre-SDK migration this file owned the full HOT_TOOLS / WARM_TOOLS /
5
+ * DIRECT_TOOLS / META_REGISTRY_TOOLS arrays and a token-saving registry
6
+ * (~15K → ~2K per turn). That optimisation moved to the SDK as the
7
+ * capability-manager direct/meta tier split (`TOOL_GROUPS.<id>.tier`),
8
+ * and `createCompilrAgent({ capabilities: ... })` now handles tool
9
+ * registration end-to-end. See `src/agent.ts` and
10
+ * `@compilr-dev/sdk/src/capabilities/packs.ts`.
6
11
  *
7
- * ARCHITECTURE (Meta-Tools):
8
- * - DIRECT TOOLS: Always available, called by name (most common operations)
9
- * - META-REGISTRY TOOLS: Available via use_tool() (specialized operations)
12
+ * What remains here is the small surface CLI code outside agent.ts still
13
+ * touches:
14
+ * - `todoStore` alias for the SDK's `getDefaultTodoStore()` singleton,
15
+ * read by the footer renderer (see `repl-v2.ts`).
16
+ * - `TOOL_NAMES` / `ToolName` / `getAllToolNames` — re-exported from
17
+ * `./tool-names.js` for convenience.
10
18
  *
11
- * This reduces token overhead from ~15K to ~2K per request.
19
+ * Audit refs: TODO-4 (HOT/WARM cleanup), TODO-15 (deleted the parallel
20
+ * dead MetaToolsRegistry singleton + its setMetaToolFilter /
21
+ * getRegisteredMetaTools / isMetaToolSilent surface — the SDK's
22
+ * per-agent CapabilityManager already handles tool filtering at agent
23
+ * factory time, so the CLI re-exports here were always no-ops).
12
24
  *
13
- * SINGLE SOURCE OF TRUTH:
14
- * Tool names are defined in tool-names.ts (a dependency-free module).
15
- * This file re-exports them for convenience. Import from here or tool-names.ts.
16
- */
17
- // Re-export tool names from the dependency-free module
18
- // This breaks circular dependencies while maintaining a single source of truth
19
- export { TOOL_NAMES, DIRECT_TOOL_NAMES, HOT_TOOL_NAMES, WARM_TOOL_NAMES, META_TOOL_NAMES, META_REGISTRY_TOOL_NAMES, getAllToolNames, } from './tool-names.js';
20
- // =============================================================================
21
- // TOOL IMPORTS
22
- // =============================================================================
23
- import { readFileTool, writeFileTool, createBashTool, bashOutputTool, killShellTool, grepTool, globTool, editTool, createTodoTools, defineTool, } from '@compilr-dev/sdk';
24
- // Create todo tools with shared store (includes claim/handoff)
25
- const todoToolsResult = createTodoTools();
26
- const originalTodoWriteTool = todoToolsResult.todoWrite;
27
- const todoReadTool = todoToolsResult.todoRead;
28
- const todoClaimTool = todoToolsResult.todoClaim;
29
- const todoHandoffTool = todoToolsResult.todoHandoff;
30
- // Explicit type annotation avoids TS2742 when importing createTodoTools via @compilr-dev/sdk
31
- export const todoStore = todoToolsResult.store;
32
- const todoWriteTool = defineTool({
33
- name: 'todo_write',
34
- description: 'Update the SESSION todo list (shown in footer). These are ephemeral tasks for the current session only. ' +
35
- 'For persistent project backlog items, use workitem_* tools instead. ' +
36
- 'Provide the complete list of todos to replace the current list.',
37
- inputSchema: {
38
- type: 'object',
39
- properties: {
40
- todos: {
41
- type: 'array',
42
- description: 'The complete list of todos',
43
- items: {
44
- type: 'object',
45
- properties: {
46
- content: {
47
- type: 'string',
48
- description: 'Task description (imperative form, e.g., "Fix bug in login")',
49
- },
50
- status: {
51
- type: 'string',
52
- enum: ['pending', 'in_progress', 'completed'],
53
- description: 'Task status',
54
- },
55
- activeForm: {
56
- type: 'string',
57
- description: 'Active form (present continuous, e.g., "Fixing bug in login")',
58
- },
59
- priority: {
60
- type: 'number',
61
- description: 'Priority (higher = more important)',
62
- },
63
- owner: {
64
- type: 'string',
65
- description: 'Owner agent ID (e.g., "dev", "pm", "arch"). Omit for unassigned.',
66
- },
67
- blockedBy: {
68
- type: 'array',
69
- items: { type: 'number' },
70
- description: 'Array of 1-based task numbers this task depends on (e.g., [3, 4] means blocked by task #3 and #4)',
71
- },
72
- },
73
- required: ['content', 'status'],
74
- },
75
- },
76
- },
77
- required: ['todos'],
78
- },
79
- execute: async (input) => {
80
- // Normalize todos - accept alternative property names
81
- const normalizedTodos = input.todos.map((todo) => ({
82
- content: (todo.content || todo.title || todo.description || todo.task || todo.text || '').trim(),
83
- status: todo.status,
84
- activeForm: todo.activeForm,
85
- priority: todo.priority,
86
- owner: todo.owner,
87
- blockedBy: todo.blockedBy,
88
- }));
89
- // Filter out empty todos and provide fallback
90
- const validTodos = normalizedTodos.map((todo) => ({
91
- ...todo,
92
- content: todo.content || 'Untitled task',
93
- }));
94
- // Call the original tool with normalized input
95
- const result = await originalTodoWriteTool.execute({ todos: validTodos });
96
- return result;
97
- },
98
- silent: true,
99
- });
100
- // Create bash tool with longer timeout (2 minutes instead of 30 seconds)
101
- const bashTool = createBashTool({
102
- timeout: 120000, // 2 minutes
103
- });
104
- import {
105
- // Git tools (basic)
106
- gitStatusTool, gitDiffTool, gitLogTool, gitCommitTool, gitBranchTool,
107
- // Git tools (advanced)
108
- gitStashTool, gitBlameTool, gitFileHistoryTool,
109
- // Project detection
110
- detectProjectTool, findProjectRootTool,
111
- // Smart runners
112
- runTestsTool, runLintTool, runBuildTool, runFormatTool,
113
- // Code search
114
- findDefinitionTool, findReferencesTool, findTodosTool,
115
- // Dependency analysis
116
- checkOutdatedTool, findVulnerabilitiesTool,
117
- // Testing analysis
118
- analyzeTestCoverageTool,
119
- // Unified analysis (auto-detect language)
120
- getFileStructureTool, getComplexityTool, } from '@compilr-dev/sdk';
121
- // Local tools
122
- import { askUserTool } from './tools/ask-user.js';
123
- import { askUserSimpleTool } from './tools/ask-user-simple.js';
124
- import { delegateTool } from './tools/delegate.js';
125
- import { delegateBackgroundTool } from './tools/delegate-background.js';
126
- import { delegationStatusTool } from './tools/delegation-status.js';
127
- import { handoffTool } from './tools/handoff.js';
128
- import { createGuideTool } from '@compilr-dev/sdk';
129
- import { CLI_GUIDE_ENTRIES } from './guide/cli-guide-entries.js';
130
- const guideTool = createGuideTool({
131
- environment: 'cli',
132
- additionalEntries: CLI_GUIDE_ENTRIES,
133
- });
134
- // DB tools for project, work item, document, plan, backlog, anchor, artifact, and episode management
135
- import { allDbTools, allFactoryTools } from './tools/db-tools.js';
136
- // Meta-tools for dynamic tool loading
137
- import { initializeMetaToolRegistry, generateToolIndex, generateFilteredToolIndex, getMetaToolCount, META_TOOLS_SYSTEM_PROMPT_PREFIX, setMetaToolFilter, getRegisteredMetaTools, createMetaToolFallback, getToolInfoTool, } from './tools/meta-tools.js';
138
- // Re-export for use in agent.ts
139
- export { setMetaToolFilter, getRegisteredMetaTools };
140
- /**
141
- * Check if a tool in the meta-registry is marked as silent.
142
- * Used by the repl to suppress output for warm tools like todo_write/todo_read.
143
- */
144
- export function isMetaToolSilent(name) {
145
- const tools = getRegisteredMetaTools();
146
- const tool = tools.find((t) => t.definition.name === name);
147
- return tool?.silent === true;
148
- }
149
- // =============================================================================
150
- // DIRECT TOOLS - Always available, called by name
151
- // =============================================================================
152
- /**
153
- * Hot tools — used in >50% of turns. Always declared as direct schemas.
154
- * Estimated: ~2,100 tokens for definitions
155
- */
156
- const HOT_TOOLS = [
157
- // File operations (most common)
158
- readFileTool,
159
- writeFileTool,
160
- editTool,
161
- // Shell (very common)
162
- bashTool,
163
- // Search (very common)
164
- globTool,
165
- grepTool,
166
- // User interaction (always needed)
167
- askUserTool,
168
- ];
169
- /**
170
- * Warm tools — used occasionally. In hot-tools mode these move to meta-registry.
171
- * Estimated: ~3,300 tokens for definitions
172
- */
173
- const WARM_TOOLS = [
174
- // Shell management
175
- bashOutputTool,
176
- killShellTool,
177
- // Task tracking
178
- todoWriteTool,
179
- todoReadTool,
180
- todoClaimTool,
181
- todoHandoffTool,
182
- // User interaction (simple variant)
183
- askUserSimpleTool,
184
- // Delegation (coordinator only)
185
- delegateTool,
186
- // Background delegation (coordinator mode)
187
- delegateBackgroundTool,
188
- // Handoff (specialist-to-specialist)
189
- handoffTool,
190
- // CLI documentation
191
- guideTool,
192
- ];
193
- /**
194
- * All direct tools combined (hot + warm).
195
- * Used in legacy mode (no meta-tools) and for backwards compatibility.
196
- */
197
- const DIRECT_TOOLS = [...HOT_TOOLS, ...WARM_TOOLS];
198
- // =============================================================================
199
- // META-REGISTRY TOOLS - Available via use_tool()
200
- // =============================================================================
201
- /**
202
- * Meta-registry tools are specialized tools available via use_tool().
203
- * Instead of declaring their full schemas (~12K tokens), we:
204
- * 1. Add them to a registry
205
- * 2. Generate a tool index for the system prompt (~800 tokens)
206
- * 3. Let the agent call them via use_tool("tool_name", {args})
207
- */
208
- const META_REGISTRY_TOOLS = [
209
- // Git operations (basic)
210
- gitStatusTool,
211
- gitDiffTool,
212
- gitLogTool,
213
- gitCommitTool,
214
- gitBranchTool,
215
- // Git operations (advanced)
216
- gitStashTool,
217
- gitBlameTool,
218
- gitFileHistoryTool,
219
- // Project detection
220
- detectProjectTool,
221
- findProjectRootTool,
222
- // Smart runners
223
- runTestsTool,
224
- runLintTool,
225
- runBuildTool,
226
- runFormatTool,
227
- // Code search (language-agnostic)
228
- findDefinitionTool,
229
- findReferencesTool,
230
- findTodosTool,
231
- // Dependency analysis
232
- checkOutdatedTool,
233
- findVulnerabilitiesTool,
234
- // Testing analysis
235
- analyzeTestCoverageTool,
236
- // Unified analysis (auto-detect language: TS/JS/Python/Go)
237
- getFileStructureTool,
238
- getComplexityTool,
239
- // DB tools (project, workitem, document, plan, backlog, anchor, artifact, episode)
240
- ...allDbTools,
241
- // Factory tools (app_model_get, app_model_update, app_model_validate, factory_scaffold, factory_list_toolkits)
242
- ...allFactoryTools,
243
- // Delegation status (coordinator query)
244
- delegationStatusTool,
245
- ];
246
- // =============================================================================
247
- // LEGACY API (for backwards compatibility)
248
- // =============================================================================
249
- /**
250
- * Creates the tool registry with all available tools.
251
- * Returns tools as-is - the Agent.registerTools() method handles typing.
252
- *
253
- * @deprecated Use createDirectToolRegistry() + initializeMetaTools() instead
254
- */
255
- export function createToolRegistry() {
256
- return [
257
- ...DIRECT_TOOLS,
258
- ...META_REGISTRY_TOOLS,
259
- ];
260
- }
261
- /**
262
- * Returns a minimal set of tools for basic testing.
263
- */
264
- export function createMinimalToolRegistry() {
265
- return [
266
- readFileTool,
267
- writeFileTool,
268
- bashTool,
269
- globTool,
270
- ];
271
- }
272
- // =============================================================================
273
- // META-TOOLS API (new architecture)
274
- // =============================================================================
275
- /**
276
- * Get direct tools that are always available.
277
- * When hotOnly is true, returns only hot tools (7 tools, ~2.1K tokens).
278
- * When false/undefined, returns all direct tools (18 tools, ~5.4K tokens).
279
- */
280
- export function getDirectTools(hotOnly) {
281
- if (hotOnly)
282
- return HOT_TOOLS;
283
- return DIRECT_TOOLS;
284
- }
285
- /**
286
- * Get meta-tools for agent registration.
287
- * Only get_tool_info is kept as a direct tool — use_tool and list_tools
288
- * are no longer needed since the fallback handler routes calls transparently.
289
- */
290
- export function getMetaTools() {
291
- return [getToolInfoTool];
292
- }
293
- /**
294
- * Create a fallback handler for the tool registry.
295
- * When the agent calls a tool not in the direct registry, this handler
296
- * checks the meta-registry and executes it transparently.
297
- *
298
- * Auto-loading of capability packs for filtered tools is handled by the
299
- * shared onFilteredTool callback in meta-tools.ts (covers both use_tool
300
- * and direct fallback paths).
301
- */
302
- export function createToolFallback() {
303
- return createMetaToolFallback();
304
- }
305
- /** Whether hot-tools mode is active (warm tools in meta-registry) */
306
- let hotToolsModeActive = false;
307
- /**
308
- * Check if hot-tools mode is active.
309
- * When true, only hot tools are declared as direct schemas; warm tools are in meta-registry.
310
- */
311
- export function isHotToolsMode() {
312
- return hotToolsModeActive;
313
- }
314
- /**
315
- * Initialize the meta-tool registry with specialized tools.
316
- * When includeWarmTools is true, warm tools are also added to the meta-registry
317
- * (enabling hot-tools mode where only 7 core tools are declared directly).
318
- * Call this once at startup.
319
- */
320
- export function initializeMetaTools(includeWarmTools) {
321
- const tools = includeWarmTools
322
- ? [...WARM_TOOLS, ...META_REGISTRY_TOOLS]
323
- : META_REGISTRY_TOOLS;
324
- initializeMetaToolRegistry(tools);
325
- if (includeWarmTools) {
326
- hotToolsModeActive = true;
327
- }
328
- }
329
- /**
330
- * Get the tool index for the system prompt.
331
- * This lists all meta-registry tools with their signatures.
332
- */
333
- export function getToolIndexForSystemPrompt() {
334
- return META_TOOLS_SYSTEM_PROMPT_PREFIX + generateToolIndex();
335
- }
336
- /**
337
- * Get a filtered tool index for the system prompt.
338
- * Only includes tools whose names are in the given set.
339
- * Used by the capability-aware BeforeLLM hook.
340
- */
341
- export function getFilteredToolIndexForSystemPrompt(activeToolNames) {
342
- return META_TOOLS_SYSTEM_PROMPT_PREFIX + generateFilteredToolIndex(activeToolNames);
343
- }
344
- /**
345
- * Get count of meta-registry tools.
346
- */
347
- export function getMetaToolsCount() {
348
- return getMetaToolCount();
349
- }
350
- /**
351
- * Check if meta-tools mode is enabled.
352
- * Returns true if the meta-tool registry has been initialized.
353
- */
354
- export function isMetaToolsEnabled() {
355
- return getMetaToolCount() > 0;
356
- }
357
- // =============================================================================
358
- // STATISTICS
359
- // =============================================================================
360
- /**
361
- * Get tool statistics for debugging/display.
362
- */
363
- export function getToolStats() {
364
- const hotCount = HOT_TOOLS.length;
365
- const warmCount = WARM_TOOLS.length;
366
- const directCount = hotToolsModeActive ? hotCount : hotCount + warmCount;
367
- const metaCount = hotToolsModeActive
368
- ? META_REGISTRY_TOOLS.length + warmCount
369
- : META_REGISTRY_TOOLS.length;
370
- const totalCount = hotCount + warmCount + META_REGISTRY_TOOLS.length;
371
- // Rough estimates: ~300 tokens per tool definition
372
- const tokensPerTool = 300;
373
- const estimatedDirectTokens = directCount * tokensPerTool;
374
- const estimatedMetaTokens = 1000; // Tool index + meta-tool definitions
375
- const estimatedLegacyTokens = totalCount * tokensPerTool;
376
- return {
377
- directTools: directCount,
378
- hotTools: hotCount,
379
- warmTools: warmCount,
380
- metaRegistryTools: metaCount,
381
- totalTools: totalCount,
382
- estimatedDirectTokens,
383
- estimatedMetaTokens,
384
- estimatedLegacyTokens,
385
- tokenSavings: estimatedLegacyTokens - estimatedDirectTokens - estimatedMetaTokens,
386
- };
387
- }
25
+ * See `project-docs/00-requirements/compilr-dev-sdk/architecture-audit-cli-vs-desktop.md`.
26
+ */
27
+ import { getDefaultTodoStore } from '@compilr-dev/sdk';
28
+ // Tool-name constants — re-exported from the dependency-free module.
29
+ export { TOOL_NAMES, getAllToolNames } from './tool-names.js';
30
+ // Footer + UI reads this; the SDK preset's todo tools write through the
31
+ // same singleton (audit TODO-9).
32
+ export const todoStore = getDefaultTodoStore();
@@ -112,22 +112,40 @@ function reflowText(text, width, gfm) {
112
112
  const fragments = sec.split(/(\u001b\[(?:\d{1,3})(?:;\d{1,3})*m)/g);
113
113
  let column = 0;
114
114
  let currentLine = '';
115
- let lastWasEscape = false;
115
+ // Carries through ANSI fragments so the space-before-word decision for
116
+ // the next text fragment knows whether boundary whitespace existed in
117
+ // the source. Fixes the bug where "the `consult` tool" rendered as
118
+ // "theconsulttool" — the trailing space of "the " and leading space of
119
+ // " tool" were both discarded by the split-on-whitespace, and the old
120
+ // `lastWasEscape` flag suppressed re-inserting them.
121
+ let prevTextEndedWithWs = false;
116
122
  for (const fragment of fragments) {
117
- if (fragment === '') {
118
- lastWasEscape = false;
123
+ if (fragment === '')
119
124
  continue;
120
- }
121
125
  if (!textLength(fragment)) {
126
+ // ANSI escape sequence — append untouched. We deliberately do NOT
127
+ // reset prevTextEndedWithWs here, so the next text fragment can
128
+ // still re-introduce a space across the ANSI boundary if needed.
122
129
  currentLine += fragment;
123
- lastWasEscape = true;
124
130
  continue;
125
131
  }
132
+ // Plain text fragment.
133
+ const startsWithWs = /^[ \t\n]/.test(fragment);
134
+ const endsWithWs = /[ \t\n]$/.test(fragment);
126
135
  const words = fragment.split(/[ \t\n]+/);
136
+ let isFirstWordInFragment = true;
127
137
  for (const word of words) {
128
138
  if (!word)
129
139
  continue;
130
- const addSpace = column !== 0 && !lastWasEscape;
140
+ // Insert a space before this word when column > 0 AND one of:
141
+ // - it's NOT the first word in this fragment (intra-fragment
142
+ // whitespace existed), OR
143
+ // - this fragment had leading whitespace (e.g. " tool" after a
144
+ // closing ANSI), OR
145
+ // - the previous text fragment ended with whitespace (e.g.
146
+ // "the " before an opening ANSI).
147
+ const addSpace = column !== 0 &&
148
+ (!isFirstWordInFragment || startsWithWs || prevTextEndedWithWs);
131
149
  const neededWidth = word.length + (addSpace ? 1 : 0);
132
150
  if (column + neededWidth > width && word.length <= width) {
133
151
  reflowed.push(currentLine);
@@ -160,8 +178,9 @@ function reflowText(text, width, gfm) {
160
178
  currentLine += word;
161
179
  column += word.length;
162
180
  }
163
- lastWasEscape = false;
181
+ isFirstWordInFragment = false;
164
182
  }
183
+ prevTextEndedWithWs = endsWithWs;
165
184
  }
166
185
  if (textLength(currentLine))
167
186
  reflowed.push(currentLine);
@@ -16,6 +16,7 @@ import { sessionManagementTutorial } from './tutorials/projects/session-manageme
16
16
  import { anchorsTutorial } from './tutorials/projects/anchors.js';
17
17
  import { importProjectTutorial } from './tutorials/projects/import-project.js';
18
18
  import { appModelTutorial } from './tutorials/projects/app-model.js';
19
+ import { interactiveFlowsTutorial } from './tutorials/projects/interactive-flows.js';
19
20
  import { designTutorial } from './tutorials/planning/design.js';
20
21
  import { backlogTutorial } from './tutorials/planning/backlog.js';
21
22
  import { sketchTutorial } from './tutorials/planning/sketch.js';
@@ -69,6 +70,7 @@ export function buildTutorialRegistry(s) {
69
70
  sessionManagementTutorial(s),
70
71
  anchorsTutorial(s),
71
72
  appModelTutorial(s),
73
+ interactiveFlowsTutorial(s),
72
74
  ],
73
75
  },
74
76
  // =========================================================================
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Interactive Flows Tutorial
3
+ *
4
+ * Explains the `build_interactive_flow` agent tool — when an agent uses it,
5
+ * what to expect when the modal opens, and how it differs from `/ask_user`.
6
+ * Desktop-only feature in Phase 1 (CLI degradation lands in Phase 3).
7
+ */
8
+ import type { TutorialBuilder } from '../../tutorial-types.js';
9
+ export declare const interactiveFlowsTutorial: TutorialBuilder;
@@ -0,0 +1,94 @@
1
+ /**
2
+ * Interactive Flows Tutorial
3
+ *
4
+ * Explains the `build_interactive_flow` agent tool — when an agent uses it,
5
+ * what to expect when the modal opens, and how it differs from `/ask_user`.
6
+ * Desktop-only feature in Phase 1 (CLI degradation lands in Phase 3).
7
+ */
8
+ export const interactiveFlowsTutorial = (s) => ({
9
+ id: 'interactive-flows',
10
+ title: 'Interactive Flows',
11
+ description: 'Agent-driven decision trees',
12
+ pages: [
13
+ // Page 1: What it is
14
+ {
15
+ title: 'What is an Interactive Flow?',
16
+ lines: [
17
+ '',
18
+ ` Sometimes an agent needs to walk you through a ${s.primaryBold('branching decision')}`,
19
+ ` — picking a database, choosing a stack, navigating a tradeoff.`,
20
+ '',
21
+ ` Instead of asking 5 questions in a row, the agent can build a`,
22
+ ` ${s.primary('navigable tree')} you step through with Back/Forward.`,
23
+ '',
24
+ ` ${s.secondary('Each step shows up as a modal on the desktop app:')}`,
25
+ '',
26
+ ` ${s.muted('•')} Single-choice questions ${s.muted('(pick one)')}`,
27
+ ` ${s.muted('•')} Multi-select questions ${s.muted('(pick several)')}`,
28
+ ` ${s.muted('•')} Free-text questions ${s.muted('(type your answer)')}`,
29
+ ` ${s.muted('•')} Info screens ${s.muted('(read context, click Next)')}`,
30
+ ` ${s.muted('•')} A summary at the end recapping your choices`,
31
+ '',
32
+ ` ${s.warning('Desktop-only for now.')} CLI rendering arrives later — when the`,
33
+ ` agent uses this tool from a CLI session, you'll see a degraded`,
34
+ ` text-prompt fallback.`,
35
+ '',
36
+ ],
37
+ },
38
+ // Page 2: When agents use it
39
+ {
40
+ title: 'When the Agent Uses This',
41
+ lines: [
42
+ '',
43
+ ` Agents pick ${s.primaryBold('build_interactive_flow')} over plain ${s.primary('ask_user')} when:`,
44
+ '',
45
+ ` ${s.primary('1.')} The decision has ${s.primaryBold('2+ branching considerations')} —`,
46
+ ` "if you pick A, the next question changes"`,
47
+ '',
48
+ ` ${s.primary('2.')} You'd benefit from ${s.primaryBold('exploring before committing')} —`,
49
+ ` Back/Forward lets you revisit prior steps`,
50
+ '',
51
+ ` ${s.primary('3.')} The reasoning ${s.primaryBold('matters as much as the answer')} —`,
52
+ ` the agent gets back your full path through the tree, not`,
53
+ ` just your final choice`,
54
+ '',
55
+ ` ${s.secondary('Typical patterns:')}`,
56
+ '',
57
+ ` ${s.muted('•')} Tech stack selection ${s.muted('(database type → vendor → details)')}`,
58
+ ` ${s.muted('•')} Architecture trade-offs ${s.muted('(monolith vs microservices)')}`,
59
+ ` ${s.muted('•')} Backlog item refinement ${s.muted('(complexity, dependencies, scope)')}`,
60
+ ` ${s.muted('•')} Project type selection during ${s.primary('/design')}`,
61
+ '',
62
+ ],
63
+ },
64
+ // Page 3: Navigation + result
65
+ {
66
+ title: 'What to Expect',
67
+ lines: [
68
+ '',
69
+ ` ${s.secondary('In the modal:')}`,
70
+ '',
71
+ ` ${s.primaryBold('Single-choice')} Click an option → auto-advance to next step`,
72
+ ` ${s.primaryBold('Multi-select')} Tick boxes → ${s.primary('Next')} button to advance`,
73
+ ` ${s.primaryBold('Free text')} Type → ${s.muted('Enter')} (single-line) or`,
74
+ ` ${s.muted('Cmd/Ctrl+Enter')} (multi-line) to submit`,
75
+ ` ${s.primaryBold('Summary')} Confirm your path, or ${s.primary('Back')} to revise`,
76
+ '',
77
+ ` ${s.secondary('Cancel anytime:')}`,
78
+ '',
79
+ ` ${s.muted('•')} ${s.primary('Esc')} key`,
80
+ ` ${s.muted('•')} ${s.primary('X')} icon in the header`,
81
+ ` ${s.muted('•')} Click outside the modal`,
82
+ '',
83
+ ` Canceling sends the agent ${s.primaryBold('completed: false')} with your`,
84
+ ` partial answers. The agent can resume the same flow or ask`,
85
+ ` again with adjustments.`,
86
+ '',
87
+ ` ${s.warning('TIP:')} The agent gets your full path back — including any`,
88
+ ` nodes you backtracked through. That's intentional: the path is`,
89
+ ` ${s.primaryBold('reasoning data')}, not just answer data.`,
90
+ '',
91
+ ],
92
+ },
93
+ ],
94
+ });