@compilr-dev/cli 0.4.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 +110 -0
- package/dist/agent.d.ts +62 -0
- package/dist/agent.js +317 -0
- package/dist/agents/registry.d.ts +66 -0
- package/dist/agents/registry.js +238 -0
- package/dist/agents/types.d.ts +40 -0
- package/dist/agents/types.js +94 -0
- package/dist/commands/custom-registry.d.ts +69 -0
- package/dist/commands/custom-registry.js +246 -0
- package/dist/commands/index.d.ts +7 -0
- package/dist/commands/index.js +7 -0
- package/dist/commands/types.d.ts +31 -0
- package/dist/commands/types.js +26 -0
- package/dist/commands.d.ts +63 -0
- package/dist/commands.js +324 -0
- package/dist/db/index.d.ts +42 -0
- package/dist/db/index.js +146 -0
- package/dist/db/repositories/document-repository.d.ts +63 -0
- package/dist/db/repositories/document-repository.js +184 -0
- package/dist/db/repositories/index.d.ts +9 -0
- package/dist/db/repositories/index.js +6 -0
- package/dist/db/repositories/project-repository.d.ts +132 -0
- package/dist/db/repositories/project-repository.js +337 -0
- package/dist/db/repositories/work-item-repository.d.ts +115 -0
- package/dist/db/repositories/work-item-repository.js +389 -0
- package/dist/db/schema.d.ts +83 -0
- package/dist/db/schema.js +143 -0
- package/dist/debug.d.ts +8 -0
- package/dist/debug.js +48 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +348 -0
- package/dist/index.old.d.ts +7 -0
- package/dist/index.old.js +1014 -0
- package/dist/repl.d.ts +121 -0
- package/dist/repl.js +1878 -0
- package/dist/settings/index.d.ts +80 -0
- package/dist/settings/index.js +195 -0
- package/dist/shared-handlers.d.ts +63 -0
- package/dist/shared-handlers.js +57 -0
- package/dist/slash-autocomplete.d.ts +41 -0
- package/dist/slash-autocomplete.js +638 -0
- package/dist/state.d.ts +75 -0
- package/dist/state.js +130 -0
- package/dist/tabbed-menu.d.ts +11 -0
- package/dist/tabbed-menu.js +328 -0
- package/dist/templates/backlog-md.d.ts +7 -0
- package/dist/templates/backlog-md.js +94 -0
- package/dist/templates/claude-md.d.ts +7 -0
- package/dist/templates/claude-md.js +189 -0
- package/dist/templates/coding-standards.d.ts +7 -0
- package/dist/templates/coding-standards.js +299 -0
- package/dist/templates/compilr-md.d.ts +7 -0
- package/dist/templates/compilr-md.js +189 -0
- package/dist/templates/config-json.d.ts +38 -0
- package/dist/templates/config-json.js +39 -0
- package/dist/templates/gitignore.d.ts +7 -0
- package/dist/templates/gitignore.js +85 -0
- package/dist/templates/index.d.ts +19 -0
- package/dist/templates/index.js +302 -0
- package/dist/templates/package-json.d.ts +7 -0
- package/dist/templates/package-json.js +111 -0
- package/dist/templates/readme-md.d.ts +7 -0
- package/dist/templates/readme-md.js +161 -0
- package/dist/templates/tsconfig.d.ts +7 -0
- package/dist/templates/tsconfig.js +61 -0
- package/dist/templates/types.d.ts +33 -0
- package/dist/templates/types.js +24 -0
- package/dist/test-autocomplete.d.ts +7 -0
- package/dist/test-autocomplete.js +85 -0
- package/dist/test-tabbed-menu.d.ts +7 -0
- package/dist/test-tabbed-menu.js +25 -0
- package/dist/themes/colors.d.ts +49 -0
- package/dist/themes/colors.js +135 -0
- package/dist/themes/index.d.ts +23 -0
- package/dist/themes/index.js +24 -0
- package/dist/themes/registry.d.ts +60 -0
- package/dist/themes/registry.js +195 -0
- package/dist/themes/types.d.ts +82 -0
- package/dist/themes/types.js +7 -0
- package/dist/tool-selector.d.ts +71 -0
- package/dist/tool-selector.js +184 -0
- package/dist/tools/ask-user-simple.d.ts +19 -0
- package/dist/tools/ask-user-simple.js +86 -0
- package/dist/tools/ask-user.d.ts +32 -0
- package/dist/tools/ask-user.js +113 -0
- package/dist/tools/backlog.d.ts +53 -0
- package/dist/tools/backlog.js +709 -0
- package/dist/tools.d.ts +15 -0
- package/dist/tools.js +121 -0
- package/dist/ui/agents-overlay.d.ts +12 -0
- package/dist/ui/agents-overlay.js +501 -0
- package/dist/ui/arch-type-overlay.d.ts +20 -0
- package/dist/ui/arch-type-overlay.js +229 -0
- package/dist/ui/ask-user-overlay.d.ts +26 -0
- package/dist/ui/ask-user-overlay.js +647 -0
- package/dist/ui/ask-user-simple-overlay.d.ts +25 -0
- package/dist/ui/ask-user-simple-overlay.js +242 -0
- package/dist/ui/backlog-overlay.d.ts +17 -0
- package/dist/ui/backlog-overlay.js +786 -0
- package/dist/ui/commands-overlay.d.ts +11 -0
- package/dist/ui/commands-overlay.js +410 -0
- package/dist/ui/config-overlay.d.ts +34 -0
- package/dist/ui/config-overlay.js +977 -0
- package/dist/ui/conversation.d.ts +82 -0
- package/dist/ui/conversation.js +508 -0
- package/dist/ui/diff.d.ts +38 -0
- package/dist/ui/diff.js +182 -0
- package/dist/ui/ephemeral.d.ts +111 -0
- package/dist/ui/ephemeral.js +413 -0
- package/dist/ui/file-autocomplete.d.ts +45 -0
- package/dist/ui/file-autocomplete.js +237 -0
- package/dist/ui/footer.d.ts +153 -0
- package/dist/ui/footer.js +422 -0
- package/dist/ui/index.d.ts +12 -0
- package/dist/ui/index.js +15 -0
- package/dist/ui/init-overlay.d.ts +24 -0
- package/dist/ui/init-overlay.js +525 -0
- package/dist/ui/input-prompt-v2.d.ts +179 -0
- package/dist/ui/input-prompt-v2.js +991 -0
- package/dist/ui/input-prompt.d.ts +97 -0
- package/dist/ui/input-prompt.js +800 -0
- package/dist/ui/iteration-limit-overlay.d.ts +21 -0
- package/dist/ui/iteration-limit-overlay.js +150 -0
- package/dist/ui/keys-overlay.d.ts +14 -0
- package/dist/ui/keys-overlay.js +181 -0
- package/dist/ui/model-warning-overlay.d.ts +30 -0
- package/dist/ui/model-warning-overlay.js +171 -0
- package/dist/ui/overlay-controller.d.ts +25 -0
- package/dist/ui/overlay-controller.js +35 -0
- package/dist/ui/overlays.d.ts +47 -0
- package/dist/ui/overlays.js +627 -0
- package/dist/ui/permission-overlay.d.ts +16 -0
- package/dist/ui/permission-overlay.js +494 -0
- package/dist/ui/terminal.d.ts +117 -0
- package/dist/ui/terminal.js +237 -0
- package/dist/ui/todo-zone.d.ts +112 -0
- package/dist/ui/todo-zone.js +353 -0
- package/dist/ui/tools-overlay.d.ts +26 -0
- package/dist/ui/tools-overlay.js +278 -0
- package/dist/ui/tutorial-overlay.d.ts +10 -0
- package/dist/ui/tutorial-overlay.js +936 -0
- package/dist/ui/types.d.ts +103 -0
- package/dist/ui/types.js +33 -0
- package/dist/utils/credentials.d.ts +55 -0
- package/dist/utils/credentials.js +268 -0
- package/dist/utils/model-tiers.d.ts +37 -0
- package/dist/utils/model-tiers.js +118 -0
- package/dist/utils/project-memory.d.ts +47 -0
- package/dist/utils/project-memory.js +117 -0
- package/dist/utils/project-status.d.ts +56 -0
- package/dist/utils/project-status.js +237 -0
- package/package.json +66 -0
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Conversation Renderer
|
|
3
|
+
*
|
|
4
|
+
* Handles the scrolling zone - prints conversation messages.
|
|
5
|
+
* Uses console.log() which naturally scrolls content up.
|
|
6
|
+
*/
|
|
7
|
+
import type { ToolMessage } from './types.js';
|
|
8
|
+
/**
|
|
9
|
+
* Render markdown text for terminal display
|
|
10
|
+
*/
|
|
11
|
+
export declare function renderMarkdown(text: string): string;
|
|
12
|
+
/**
|
|
13
|
+
* Render inline markdown for streaming output
|
|
14
|
+
* Handles bold, italic, inline code, and fenced code blocks with syntax highlighting
|
|
15
|
+
* Used during streaming when we can't wait for full markdown parsing
|
|
16
|
+
*/
|
|
17
|
+
export declare function renderInlineMarkdown(text: string): string;
|
|
18
|
+
/**
|
|
19
|
+
* Format tool result for display - handles strings, objects, arrays
|
|
20
|
+
* When verbose mode is enabled, shows more detailed output
|
|
21
|
+
*/
|
|
22
|
+
export declare function formatToolResult(result: unknown): string;
|
|
23
|
+
/**
|
|
24
|
+
* Print user message to conversation
|
|
25
|
+
* Format: primary "❯ " + message content
|
|
26
|
+
*/
|
|
27
|
+
export declare function printUserMessage(content: string): void;
|
|
28
|
+
/**
|
|
29
|
+
* Print assistant message to conversation
|
|
30
|
+
* Renders markdown (code blocks, bold, etc.)
|
|
31
|
+
*/
|
|
32
|
+
export declare function printAssistantMessage(content: string): void;
|
|
33
|
+
/**
|
|
34
|
+
* Print assistant response (event-based rendering)
|
|
35
|
+
* Used when text is accumulated between tool calls.
|
|
36
|
+
* Format: "● " prefix + rendered markdown with syntax highlighting
|
|
37
|
+
*/
|
|
38
|
+
export declare function printAssistantResponse(content: string): void;
|
|
39
|
+
/**
|
|
40
|
+
* Print tool execution log
|
|
41
|
+
* Format: warning "● toolName" + muted "(args)" + newline + muted " ⎿ " + result
|
|
42
|
+
* When verbose mode is enabled, shows full args
|
|
43
|
+
*/
|
|
44
|
+
export declare function printToolLog(tool: ToolMessage): void;
|
|
45
|
+
/**
|
|
46
|
+
* Print tool execution inline (without creating ToolMessage)
|
|
47
|
+
* When verbose mode is enabled, shows full args instead of truncated
|
|
48
|
+
*/
|
|
49
|
+
export declare function printToolExecution(name: string, args?: string, result?: string): void;
|
|
50
|
+
/**
|
|
51
|
+
* Print error message
|
|
52
|
+
* Format: error "✖ " + message
|
|
53
|
+
*/
|
|
54
|
+
export declare function printError(message: string): void;
|
|
55
|
+
/**
|
|
56
|
+
* Print info message
|
|
57
|
+
* Format: muted text
|
|
58
|
+
*/
|
|
59
|
+
export declare function printInfo(message: string): void;
|
|
60
|
+
/**
|
|
61
|
+
* Print success message
|
|
62
|
+
* Format: success "✔ " + message
|
|
63
|
+
*/
|
|
64
|
+
export declare function printSuccess(message: string): void;
|
|
65
|
+
/**
|
|
66
|
+
* Print warning message
|
|
67
|
+
* Format: warning "⚠ " + message
|
|
68
|
+
*/
|
|
69
|
+
export declare function printWarning(message: string): void;
|
|
70
|
+
/**
|
|
71
|
+
* Print just the ASCII logo (for startup before full welcome)
|
|
72
|
+
*/
|
|
73
|
+
export declare function printLogo(version: string): void;
|
|
74
|
+
/**
|
|
75
|
+
* Print welcome banner with ASCII logo and contextual guidance
|
|
76
|
+
*/
|
|
77
|
+
export declare function printWelcome(model: string, version: string): void;
|
|
78
|
+
/**
|
|
79
|
+
* Format a pending message for display in ephemeral zone
|
|
80
|
+
* (called by EphemeralZone, not printed directly)
|
|
81
|
+
*/
|
|
82
|
+
export declare function formatPendingMessage(content: string): string;
|
|
@@ -0,0 +1,508 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Conversation Renderer
|
|
3
|
+
*
|
|
4
|
+
* Handles the scrolling zone - prints conversation messages.
|
|
5
|
+
* Uses console.log() which naturally scrolls content up.
|
|
6
|
+
*/
|
|
7
|
+
import chalk, { Chalk } from 'chalk';
|
|
8
|
+
import { marked } from 'marked';
|
|
9
|
+
import { markedTerminal } from 'marked-terminal';
|
|
10
|
+
import { highlight } from 'cli-highlight';
|
|
11
|
+
import { getStyles } from '../themes/index.js';
|
|
12
|
+
import * as terminal from './terminal.js';
|
|
13
|
+
import { isVerboseEnabled } from '../settings/index.js';
|
|
14
|
+
import { detectProjectStatus, getWorkflowStage } from '../utils/project-status.js';
|
|
15
|
+
// Create chalk instance with colors forced on for our direct usage
|
|
16
|
+
// Note: FORCE_COLOR=3 is set in index.ts for global chalk-based libraries
|
|
17
|
+
const chalkWithColors = new Chalk({ level: 3 });
|
|
18
|
+
// Configure marked for terminal output
|
|
19
|
+
// Type assertion is needed because markedTerminal returns a type incompatible with marked's extension type
|
|
20
|
+
// but it works correctly at runtime. This is a known issue with the marked-terminal types.
|
|
21
|
+
marked.use(markedTerminal({
|
|
22
|
+
reflowText: true,
|
|
23
|
+
width: process.stdout.columns || 80,
|
|
24
|
+
// Enable styling options
|
|
25
|
+
showSectionPrefix: false,
|
|
26
|
+
tab: 2,
|
|
27
|
+
// Use our chalk instance with colors forced on
|
|
28
|
+
// marked-terminal uses these for inline formatting
|
|
29
|
+
strong: chalkWithColors.bold,
|
|
30
|
+
em: chalkWithColors.italic,
|
|
31
|
+
codespan: chalkWithColors.cyan,
|
|
32
|
+
}));
|
|
33
|
+
// =============================================================================
|
|
34
|
+
// Markdown Rendering
|
|
35
|
+
// =============================================================================
|
|
36
|
+
/**
|
|
37
|
+
* Render markdown text for terminal display
|
|
38
|
+
*/
|
|
39
|
+
export function renderMarkdown(text) {
|
|
40
|
+
try {
|
|
41
|
+
// Preprocess: normalize list markers and indentation
|
|
42
|
+
const lines = text.split('\n');
|
|
43
|
+
const processedLines = lines.map((line) => {
|
|
44
|
+
// Normalize bullet lists: "* " or "- " -> "- "
|
|
45
|
+
// This prevents the asterisk from being confused with italic
|
|
46
|
+
if (/^\*\s{2,}/.test(line)) {
|
|
47
|
+
return line.replace(/^\*\s{2,}/, '- ');
|
|
48
|
+
}
|
|
49
|
+
// Remove leading 4 spaces that would make it a code block
|
|
50
|
+
// But preserve intentional code blocks (``` fenced)
|
|
51
|
+
if (line.startsWith(' ') && !line.startsWith(' ```')) {
|
|
52
|
+
return line.slice(4);
|
|
53
|
+
}
|
|
54
|
+
return line;
|
|
55
|
+
});
|
|
56
|
+
const processed = processedLines.join('\n');
|
|
57
|
+
// marked.parse returns string synchronously with these options
|
|
58
|
+
let rendered = marked.parse(processed, { async: false });
|
|
59
|
+
// Post-process: marked-terminal doesn't always render bold/italic inside list items
|
|
60
|
+
// Convert remaining **bold** and *italic* to ANSI codes
|
|
61
|
+
// Use chalkWithColors to ensure ANSI codes are always generated
|
|
62
|
+
const s = getStyles();
|
|
63
|
+
// Handle **`code`** (bold code) - common pattern
|
|
64
|
+
rendered = rendered.replace(/\*\*`([^`]+)`\*\*/g, (_match, code) => chalkWithColors.bold(s.primary(code)));
|
|
65
|
+
// Handle `code` inside **bold** - e.g., **some `code` here**
|
|
66
|
+
rendered = rendered.replace(/\*\*([^*]*)`([^`]+)`([^*]*)\*\*/g, (_match, before, code, after) => chalkWithColors.bold(`${before}${s.primary(code)}${after}`));
|
|
67
|
+
// Handle **bold** (not already processed)
|
|
68
|
+
rendered = rendered.replace(/\*\*([^*]+)\*\*/g, (_match, boldText) => chalkWithColors.bold(boldText));
|
|
69
|
+
// Handle *italic* - must not be at start of line (bullet) and not part of **
|
|
70
|
+
// Match *text* but not * (bullet with spaces) and not **
|
|
71
|
+
rendered = rendered.replace(/(?<![*\n])\*([^*\s][^*]*[^*\s])\*(?!\*)/g, (_match, italicText) => chalkWithColors.italic(italicText));
|
|
72
|
+
// Also handle single-word italic: *word*
|
|
73
|
+
rendered = rendered.replace(/(?<![*\n])\*(\S+)\*(?!\*)/g, (_match, word) => chalkWithColors.italic(word));
|
|
74
|
+
// Handle `code` (inline code) - use theme primary color
|
|
75
|
+
rendered = rendered.replace(/`([^`]+)`/g, (_match, code) => s.primary(code));
|
|
76
|
+
// Handle numbered lists that weren't processed (e.g., "1. text")
|
|
77
|
+
rendered = rendered.replace(/^(\d+)\.\s{2,}/gm, (_match, num) => chalkWithColors.bold(`${num}. `));
|
|
78
|
+
// Remove trailing newlines that marked adds
|
|
79
|
+
return rendered.replace(/\n+$/, '');
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
return text; // Fallback to raw text
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Render inline markdown for streaming output
|
|
87
|
+
* Handles bold, italic, inline code, and fenced code blocks with syntax highlighting
|
|
88
|
+
* Used during streaming when we can't wait for full markdown parsing
|
|
89
|
+
*/
|
|
90
|
+
export function renderInlineMarkdown(text) {
|
|
91
|
+
const s = getStyles();
|
|
92
|
+
// Handle fenced code blocks with syntax highlighting
|
|
93
|
+
// Match ```language\ncode\n``` pattern
|
|
94
|
+
let result = text.replace(/```(\w*)\n([\s\S]*?)```/g, (_match, lang, code) => {
|
|
95
|
+
try {
|
|
96
|
+
// Apply syntax highlighting
|
|
97
|
+
const highlighted = highlight(code.trimEnd(), {
|
|
98
|
+
language: lang || undefined,
|
|
99
|
+
ignoreIllegals: true,
|
|
100
|
+
});
|
|
101
|
+
return '\n' + highlighted + '\n';
|
|
102
|
+
}
|
|
103
|
+
catch {
|
|
104
|
+
// Fallback to muted code display
|
|
105
|
+
return '\n' + s.muted(code.trimEnd()) + '\n';
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
// Handle **`code`** (bold code) - common pattern
|
|
109
|
+
result = result.replace(/\*\*`([^`]+)`\*\*/g, (_match, code) => chalkWithColors.bold(s.primary(code)));
|
|
110
|
+
// Handle `code` inside **bold** - e.g., **some `code` here**
|
|
111
|
+
result = result.replace(/\*\*([^*]*)`([^`]+)`([^*]*)\*\*/g, (_match, before, code, after) => chalkWithColors.bold(`${before}${s.primary(code)}${after}`));
|
|
112
|
+
// Handle **bold**
|
|
113
|
+
result = result.replace(/\*\*([^*]+)\*\*/g, (_match, boldText) => chalkWithColors.bold(boldText));
|
|
114
|
+
// Handle *italic* - careful not to match bullet points (* ) or **
|
|
115
|
+
result = result.replace(/(?<![*\s])\*([^*\s][^*]*[^*\s])\*(?!\*)/g, (_match, italicText) => chalkWithColors.italic(italicText));
|
|
116
|
+
// Single-word italic
|
|
117
|
+
result = result.replace(/(?<![*\s])\*(\S+)\*(?!\*)/g, (_match, word) => chalkWithColors.italic(word));
|
|
118
|
+
// Handle `code` - use theme primary color
|
|
119
|
+
result = result.replace(/`([^`]+)`/g, (_match, code) => s.primary(code));
|
|
120
|
+
return result;
|
|
121
|
+
}
|
|
122
|
+
// =============================================================================
|
|
123
|
+
// Tool Result Formatting
|
|
124
|
+
// =============================================================================
|
|
125
|
+
/**
|
|
126
|
+
* Format tool result for display - handles strings, objects, arrays
|
|
127
|
+
* When verbose mode is enabled, shows more detailed output
|
|
128
|
+
*/
|
|
129
|
+
export function formatToolResult(result) {
|
|
130
|
+
const s = getStyles();
|
|
131
|
+
const verbose = isVerboseEnabled();
|
|
132
|
+
if (result === null || result === undefined) {
|
|
133
|
+
return '';
|
|
134
|
+
}
|
|
135
|
+
// String result - count lines or truncate
|
|
136
|
+
if (typeof result === 'string') {
|
|
137
|
+
const allLines = result.split('\n');
|
|
138
|
+
const nonEmptyLines = allLines.filter((l) => l.trim()).length;
|
|
139
|
+
if (verbose) {
|
|
140
|
+
// Verbose mode: show first few lines as preview, then count
|
|
141
|
+
const previewLines = 5;
|
|
142
|
+
if (allLines.length > previewLines) {
|
|
143
|
+
const preview = allLines.slice(0, previewLines).join('\n');
|
|
144
|
+
return s.muted(`${preview}\n ... (${String(nonEmptyLines)} lines total)`);
|
|
145
|
+
}
|
|
146
|
+
// Short enough to show fully
|
|
147
|
+
return s.muted(result);
|
|
148
|
+
}
|
|
149
|
+
else {
|
|
150
|
+
// Normal mode: just show line count or truncated single line
|
|
151
|
+
if (nonEmptyLines > 3) {
|
|
152
|
+
return s.muted(`${String(nonEmptyLines)} lines`);
|
|
153
|
+
}
|
|
154
|
+
const truncated = result.slice(0, 80).replace(/\n/g, ' ');
|
|
155
|
+
return s.muted(truncated + (result.length > 80 ? '...' : ''));
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
// Array result - show count and type hint
|
|
159
|
+
if (Array.isArray(result)) {
|
|
160
|
+
const len = result.length;
|
|
161
|
+
if (len === 0)
|
|
162
|
+
return s.muted('empty array');
|
|
163
|
+
// Try to describe contents
|
|
164
|
+
const first = result[0];
|
|
165
|
+
if (typeof first === 'string') {
|
|
166
|
+
return s.muted(`${String(len)} items`);
|
|
167
|
+
}
|
|
168
|
+
else if (typeof first === 'object' && first !== null) {
|
|
169
|
+
// Check for common patterns like todos, files, branches
|
|
170
|
+
if ('content' in first && 'status' in first) {
|
|
171
|
+
return s.muted(`${String(len)} todo items`);
|
|
172
|
+
}
|
|
173
|
+
if ('name' in first || 'path' in first) {
|
|
174
|
+
return s.muted(`${String(len)} items`);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
return s.muted(`${String(len)} items`);
|
|
178
|
+
}
|
|
179
|
+
// Object result - try to extract meaningful summary
|
|
180
|
+
if (typeof result === 'object') {
|
|
181
|
+
const obj = result;
|
|
182
|
+
// Git status pattern
|
|
183
|
+
if ('branch' in obj && 'clean' in obj) {
|
|
184
|
+
const branch = obj.branch;
|
|
185
|
+
const clean = obj.clean;
|
|
186
|
+
return s.muted(`${branch}${clean ? ' (clean)' : ' (changes)'}`);
|
|
187
|
+
}
|
|
188
|
+
// Git diff pattern
|
|
189
|
+
if ('files' in obj && Array.isArray(obj.files)) {
|
|
190
|
+
const files = obj.files;
|
|
191
|
+
if (files.length === 0)
|
|
192
|
+
return s.muted('no changes');
|
|
193
|
+
if (verbose) {
|
|
194
|
+
// Show file names in verbose mode
|
|
195
|
+
const fileNames = files.map((f) => {
|
|
196
|
+
if (typeof f === 'string')
|
|
197
|
+
return f;
|
|
198
|
+
if (typeof f === 'object' && f !== null && 'path' in f)
|
|
199
|
+
return f.path;
|
|
200
|
+
if (typeof f === 'object' && f !== null && 'file' in f)
|
|
201
|
+
return f.file;
|
|
202
|
+
return String(f);
|
|
203
|
+
});
|
|
204
|
+
const preview = fileNames.slice(0, 10).join('\n ');
|
|
205
|
+
if (fileNames.length > 10) {
|
|
206
|
+
return s.muted(`${preview}\n ... (${String(fileNames.length)} files total)`);
|
|
207
|
+
}
|
|
208
|
+
return s.muted(preview);
|
|
209
|
+
}
|
|
210
|
+
return s.muted(`${String(files.length)} file(s) changed`);
|
|
211
|
+
}
|
|
212
|
+
// Git branch pattern
|
|
213
|
+
if ('current' in obj && 'branches' in obj) {
|
|
214
|
+
const branches = obj.branches;
|
|
215
|
+
return s.muted(`${String(branches?.length ?? 0)} branches`);
|
|
216
|
+
}
|
|
217
|
+
// File read pattern
|
|
218
|
+
if ('content' in obj) {
|
|
219
|
+
const content = String(obj.content);
|
|
220
|
+
const allLines = content.split('\n');
|
|
221
|
+
const lineCount = allLines.length;
|
|
222
|
+
if (verbose) {
|
|
223
|
+
// Show preview in verbose mode
|
|
224
|
+
const previewLines = 5;
|
|
225
|
+
if (allLines.length > previewLines) {
|
|
226
|
+
const preview = allLines.slice(0, previewLines).join('\n');
|
|
227
|
+
return s.muted(`${preview}\n ... (${String(lineCount)} lines total)`);
|
|
228
|
+
}
|
|
229
|
+
return s.muted(content);
|
|
230
|
+
}
|
|
231
|
+
return s.muted(`${String(lineCount)} lines`);
|
|
232
|
+
}
|
|
233
|
+
// Bash/command pattern
|
|
234
|
+
if ('stdout' in obj || 'output' in obj) {
|
|
235
|
+
const outValue = (obj.stdout ?? obj.output) ?? '';
|
|
236
|
+
const out = typeof outValue === 'string' ? outValue : JSON.stringify(outValue);
|
|
237
|
+
const allLines = out.split('\n');
|
|
238
|
+
const nonEmptyLines = allLines.filter((l) => l.trim()).length;
|
|
239
|
+
if (verbose && nonEmptyLines > 0) {
|
|
240
|
+
const previewLines = 5;
|
|
241
|
+
if (allLines.length > previewLines) {
|
|
242
|
+
const preview = allLines.slice(0, previewLines).join('\n');
|
|
243
|
+
return s.muted(`${preview}\n ... (${String(nonEmptyLines)} lines total)`);
|
|
244
|
+
}
|
|
245
|
+
return s.muted(out);
|
|
246
|
+
}
|
|
247
|
+
if (nonEmptyLines > 0)
|
|
248
|
+
return s.muted(`${String(nonEmptyLines)} lines`);
|
|
249
|
+
return s.muted('done');
|
|
250
|
+
}
|
|
251
|
+
// Success message pattern
|
|
252
|
+
if ('success' in obj && typeof obj.message === 'string') {
|
|
253
|
+
return s.muted(obj.message.slice(0, 60));
|
|
254
|
+
}
|
|
255
|
+
// Fallback - show key count or full object in verbose mode
|
|
256
|
+
const keys = Object.keys(obj);
|
|
257
|
+
if (verbose) {
|
|
258
|
+
// Verbose mode: show full object (limited depth)
|
|
259
|
+
try {
|
|
260
|
+
const json = JSON.stringify(obj, null, 2);
|
|
261
|
+
const lines = json.split('\n');
|
|
262
|
+
if (lines.length > 15) {
|
|
263
|
+
return s.muted(lines.slice(0, 15).join('\n') + '\n ... (truncated)');
|
|
264
|
+
}
|
|
265
|
+
return s.muted(json);
|
|
266
|
+
}
|
|
267
|
+
catch {
|
|
268
|
+
return s.muted(`${String(keys.length)} fields`);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
if (keys.length <= 3) {
|
|
272
|
+
return s.muted(keys.join(', '));
|
|
273
|
+
}
|
|
274
|
+
return s.muted(`${String(keys.length)} fields`);
|
|
275
|
+
}
|
|
276
|
+
// Primitive - convert to string safely
|
|
277
|
+
const resultStr = typeof result === 'string' ? result :
|
|
278
|
+
typeof result === 'number' ? String(result) :
|
|
279
|
+
typeof result === 'boolean' ? String(result) :
|
|
280
|
+
JSON.stringify(result);
|
|
281
|
+
return s.muted(resultStr.slice(0, 60));
|
|
282
|
+
}
|
|
283
|
+
// =============================================================================
|
|
284
|
+
// Message Printing
|
|
285
|
+
// =============================================================================
|
|
286
|
+
/**
|
|
287
|
+
* Print user message to conversation
|
|
288
|
+
* Format: primary "❯ " + message content
|
|
289
|
+
*/
|
|
290
|
+
export function printUserMessage(content) {
|
|
291
|
+
const s = getStyles();
|
|
292
|
+
console.log(s.primary('❯ ') + content);
|
|
293
|
+
console.log(''); // Blank line after
|
|
294
|
+
}
|
|
295
|
+
/**
|
|
296
|
+
* Print assistant message to conversation
|
|
297
|
+
* Renders markdown (code blocks, bold, etc.)
|
|
298
|
+
*/
|
|
299
|
+
export function printAssistantMessage(content) {
|
|
300
|
+
if (!content.trim())
|
|
301
|
+
return;
|
|
302
|
+
const s = getStyles();
|
|
303
|
+
console.log(s.success('Agent: '));
|
|
304
|
+
console.log(renderMarkdown(content));
|
|
305
|
+
console.log(''); // Blank line after
|
|
306
|
+
}
|
|
307
|
+
/**
|
|
308
|
+
* Print assistant response (event-based rendering)
|
|
309
|
+
* Used when text is accumulated between tool calls.
|
|
310
|
+
* Format: "● " prefix + rendered markdown with syntax highlighting
|
|
311
|
+
*/
|
|
312
|
+
export function printAssistantResponse(content) {
|
|
313
|
+
if (!content.trim())
|
|
314
|
+
return;
|
|
315
|
+
const s = getStyles();
|
|
316
|
+
// Use bullet prefix (consistent with tool output style)
|
|
317
|
+
// Apply renderInlineMarkdown which handles code blocks with syntax highlighting
|
|
318
|
+
const rendered = renderInlineMarkdown(content);
|
|
319
|
+
console.log(s.primary('● ') + rendered);
|
|
320
|
+
}
|
|
321
|
+
/**
|
|
322
|
+
* Print tool execution log
|
|
323
|
+
* Format: warning "● toolName" + muted "(args)" + newline + muted " ⎿ " + result
|
|
324
|
+
* When verbose mode is enabled, shows full args
|
|
325
|
+
*/
|
|
326
|
+
export function printToolLog(tool) {
|
|
327
|
+
const s = getStyles();
|
|
328
|
+
const verbose = isVerboseEnabled();
|
|
329
|
+
// In verbose mode, show full args; otherwise truncate
|
|
330
|
+
let displayArgs = tool.args;
|
|
331
|
+
if (tool.args && !verbose && tool.args.length > 60) {
|
|
332
|
+
displayArgs = tool.args.slice(0, 57) + '...';
|
|
333
|
+
}
|
|
334
|
+
const toolHeader = displayArgs
|
|
335
|
+
? s.warning(`● ${tool.name}`) + s.secondary(`(${displayArgs})`)
|
|
336
|
+
: s.warning(`● ${tool.name}`);
|
|
337
|
+
console.log(toolHeader);
|
|
338
|
+
if (tool.result) {
|
|
339
|
+
console.log(s.muted(' ⎿ ') + tool.result);
|
|
340
|
+
}
|
|
341
|
+
console.log(''); // Blank line after
|
|
342
|
+
}
|
|
343
|
+
/**
|
|
344
|
+
* Print tool execution inline (without creating ToolMessage)
|
|
345
|
+
* When verbose mode is enabled, shows full args instead of truncated
|
|
346
|
+
*/
|
|
347
|
+
export function printToolExecution(name, args, result) {
|
|
348
|
+
const s = getStyles();
|
|
349
|
+
const verbose = isVerboseEnabled();
|
|
350
|
+
// In verbose mode, show full args; otherwise truncate
|
|
351
|
+
let displayArgs = args;
|
|
352
|
+
if (args && !verbose && args.length > 60) {
|
|
353
|
+
displayArgs = args.slice(0, 57) + '...';
|
|
354
|
+
}
|
|
355
|
+
const toolHeader = displayArgs
|
|
356
|
+
? s.warning(`● ${name}`) + s.secondary(`(${displayArgs})`)
|
|
357
|
+
: s.warning(`● ${name}`);
|
|
358
|
+
console.log(toolHeader);
|
|
359
|
+
if (result) {
|
|
360
|
+
console.log(s.muted(' ⎿ ') + result);
|
|
361
|
+
}
|
|
362
|
+
console.log(''); // Blank line after
|
|
363
|
+
}
|
|
364
|
+
/**
|
|
365
|
+
* Print error message
|
|
366
|
+
* Format: error "✖ " + message
|
|
367
|
+
*/
|
|
368
|
+
export function printError(message) {
|
|
369
|
+
const s = getStyles();
|
|
370
|
+
console.log(s.error('✖ ') + message);
|
|
371
|
+
console.log('');
|
|
372
|
+
}
|
|
373
|
+
/**
|
|
374
|
+
* Print info message
|
|
375
|
+
* Format: muted text
|
|
376
|
+
*/
|
|
377
|
+
export function printInfo(message) {
|
|
378
|
+
const s = getStyles();
|
|
379
|
+
console.log(s.muted(message));
|
|
380
|
+
}
|
|
381
|
+
/**
|
|
382
|
+
* Print success message
|
|
383
|
+
* Format: success "✔ " + message
|
|
384
|
+
*/
|
|
385
|
+
export function printSuccess(message) {
|
|
386
|
+
const s = getStyles();
|
|
387
|
+
console.log(s.success('✔ ') + message);
|
|
388
|
+
}
|
|
389
|
+
/**
|
|
390
|
+
* Print warning message
|
|
391
|
+
* Format: warning "⚠ " + message
|
|
392
|
+
*/
|
|
393
|
+
export function printWarning(message) {
|
|
394
|
+
const s = getStyles();
|
|
395
|
+
console.log(s.warning('⚠ ') + message);
|
|
396
|
+
}
|
|
397
|
+
// =============================================================================
|
|
398
|
+
// Welcome Banner
|
|
399
|
+
// =============================================================================
|
|
400
|
+
/**
|
|
401
|
+
* Print just the ASCII logo (for startup before full welcome)
|
|
402
|
+
*/
|
|
403
|
+
export function printLogo(version) {
|
|
404
|
+
const s = getStyles();
|
|
405
|
+
terminal.clearScreen();
|
|
406
|
+
console.log('');
|
|
407
|
+
console.log(s.primary(" ___ ___ _ __ ___ _ __ (_) |_ __"));
|
|
408
|
+
console.log(s.primary(" / __|/ _ \\| '_ ` _ \\| '_ \\| | | '__|"));
|
|
409
|
+
console.log(s.primary(" | (__| (_) | | | | | | |_) | | | |"));
|
|
410
|
+
console.log(s.primary(" \\___|\\___/|_| |_| |_| .__/|_|_|_|"));
|
|
411
|
+
console.log(s.primary(' | |') + s.secondary(' .dev'));
|
|
412
|
+
console.log(s.primary(' |_|') + s.muted(` v${version}`));
|
|
413
|
+
console.log('');
|
|
414
|
+
console.log(chalk.bold('@compilr-dev/cli') + s.muted(' - AI-powered coding assistant'));
|
|
415
|
+
console.log('');
|
|
416
|
+
}
|
|
417
|
+
/**
|
|
418
|
+
* Print welcome banner with ASCII logo and contextual guidance
|
|
419
|
+
*/
|
|
420
|
+
export function printWelcome(model, version) {
|
|
421
|
+
const s = getStyles();
|
|
422
|
+
console.log('');
|
|
423
|
+
console.log(s.primary(" ___ ___ _ __ ___ _ __ (_) |_ __"));
|
|
424
|
+
console.log(s.primary(" / __|/ _ \\| '_ ` _ \\| '_ \\| | | '__|"));
|
|
425
|
+
console.log(s.primary(" | (__| (_) | | | | | | |_) | | | |"));
|
|
426
|
+
console.log(s.primary(" \\___|\\___/|_| |_| |_| .__/|_|_|_|"));
|
|
427
|
+
console.log(s.primary(' | |') + s.secondary(' .dev'));
|
|
428
|
+
console.log(s.primary(' |_|') + s.muted(` v${version}`));
|
|
429
|
+
console.log('');
|
|
430
|
+
console.log(chalk.bold('@compilr-dev/cli') + s.muted(' - AI-powered coding assistant'));
|
|
431
|
+
console.log('');
|
|
432
|
+
console.log(s.muted(`Model: ${model}`));
|
|
433
|
+
console.log('');
|
|
434
|
+
// Detect project status and show contextual guidance
|
|
435
|
+
const status = detectProjectStatus();
|
|
436
|
+
const stage = getWorkflowStage(status);
|
|
437
|
+
printWorkflowGuidance(status, stage);
|
|
438
|
+
console.log(s.muted('Type /help for commands, /exit to quit'));
|
|
439
|
+
console.log('');
|
|
440
|
+
}
|
|
441
|
+
/**
|
|
442
|
+
* Print workflow guidance based on project status
|
|
443
|
+
*/
|
|
444
|
+
function printWorkflowGuidance(status, stage) {
|
|
445
|
+
const s = getStyles();
|
|
446
|
+
switch (stage) {
|
|
447
|
+
case 'new':
|
|
448
|
+
// Brand new directory, nothing exists
|
|
449
|
+
console.log(s.muted('Get started:'));
|
|
450
|
+
console.log(s.muted(' /init ') + s.secondary('Create project structure'));
|
|
451
|
+
console.log(s.muted(' /sketch ') + s.secondary('Quick 6-question outline'));
|
|
452
|
+
console.log(s.muted(' /tutorial ') + s.secondary('Learn the workflow'));
|
|
453
|
+
console.log('');
|
|
454
|
+
break;
|
|
455
|
+
case 'initialized':
|
|
456
|
+
// Existing project, but no compilr setup
|
|
457
|
+
if (status.isExistingProject && !status.hasMemory) {
|
|
458
|
+
console.log(s.muted(`Detected ${status.projectType || 'existing'} project. Define requirements:`));
|
|
459
|
+
console.log(s.muted(' /design ') + s.secondary('Agent-driven requirements'));
|
|
460
|
+
console.log(s.muted(' /sketch ') + s.secondary('Quick project outline'));
|
|
461
|
+
console.log(s.muted(' /tutorial ') + s.secondary('Learn the workflow'));
|
|
462
|
+
console.log('');
|
|
463
|
+
}
|
|
464
|
+
break;
|
|
465
|
+
case 'designing':
|
|
466
|
+
// Has PRD or initialized, but no backlog yet
|
|
467
|
+
console.log(s.muted('Project initialized. Create your backlog:'));
|
|
468
|
+
console.log(s.muted(' /design ') + s.secondary('Refine requirements'));
|
|
469
|
+
console.log(s.muted(' /backlog ') + s.secondary('View/manage tasks'));
|
|
470
|
+
console.log('');
|
|
471
|
+
break;
|
|
472
|
+
case 'building':
|
|
473
|
+
// Has backlog with pending items
|
|
474
|
+
if (status.backlogStats) {
|
|
475
|
+
const { pending, inProgress } = status.backlogStats;
|
|
476
|
+
const taskInfo = inProgress > 0
|
|
477
|
+
? `${String(inProgress)} in progress, ${String(pending)} pending`
|
|
478
|
+
: `${String(pending)} tasks pending`;
|
|
479
|
+
console.log(s.muted(`Backlog: ${taskInfo}`));
|
|
480
|
+
console.log(s.muted(' /backlog ') + s.secondary('View tasks'));
|
|
481
|
+
console.log(s.muted(' "build next item" ') + s.secondary('Start working'));
|
|
482
|
+
console.log('');
|
|
483
|
+
}
|
|
484
|
+
break;
|
|
485
|
+
case 'maintaining':
|
|
486
|
+
// Most items completed
|
|
487
|
+
if (status.backlogStats) {
|
|
488
|
+
const { total, completed } = status.backlogStats;
|
|
489
|
+
console.log(s.muted(`Backlog: ${String(completed)}/${String(total)} completed`));
|
|
490
|
+
console.log(s.muted(' /backlog ') + s.secondary('View tasks'));
|
|
491
|
+
console.log(s.muted(' /refine ') + s.secondary('Add new requirements'));
|
|
492
|
+
console.log('');
|
|
493
|
+
}
|
|
494
|
+
break;
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
// =============================================================================
|
|
498
|
+
// Pending Message Display
|
|
499
|
+
// =============================================================================
|
|
500
|
+
/**
|
|
501
|
+
* Format a pending message for display in ephemeral zone
|
|
502
|
+
* (called by EphemeralZone, not printed directly)
|
|
503
|
+
*/
|
|
504
|
+
export function formatPendingMessage(content) {
|
|
505
|
+
const s = getStyles();
|
|
506
|
+
const truncated = content.length > 60 ? content.slice(0, 57) + '...' : content;
|
|
507
|
+
return s.muted('⏳ ') + s.muted(truncated);
|
|
508
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Diff Formatting Utility
|
|
3
|
+
*
|
|
4
|
+
* Generates colorized diff output for file edits, similar to Claude Code's display.
|
|
5
|
+
* Shows removed lines with red background and added lines with green background.
|
|
6
|
+
*/
|
|
7
|
+
export interface DiffResult {
|
|
8
|
+
/** Formatted lines to display */
|
|
9
|
+
lines: string[];
|
|
10
|
+
/** Number of additions */
|
|
11
|
+
additions: number;
|
|
12
|
+
/** Number of removals */
|
|
13
|
+
removals: number;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Generate a formatted diff for an edit operation.
|
|
17
|
+
* Supports both single replacement and replaceAll.
|
|
18
|
+
* Shows full lines being modified with proper addition/removal counts.
|
|
19
|
+
*
|
|
20
|
+
* @param filePath - Path to the file being edited
|
|
21
|
+
* @param oldText - Text being replaced
|
|
22
|
+
* @param newText - Replacement text
|
|
23
|
+
* @param replaceAll - Whether all occurrences are being replaced
|
|
24
|
+
* @returns Formatted diff result, or null if file can't be read
|
|
25
|
+
*/
|
|
26
|
+
export declare function generateEditDiff(filePath: string, oldText: string, newText: string, replaceAll?: boolean): DiffResult | null;
|
|
27
|
+
/**
|
|
28
|
+
* Format a diff header showing what changed
|
|
29
|
+
*/
|
|
30
|
+
export declare function formatDiffHeader(filePath: string, additions: number, removals: number): string;
|
|
31
|
+
/**
|
|
32
|
+
* Generate a simple diff for write_file operations (new content only)
|
|
33
|
+
*/
|
|
34
|
+
export declare function generateWriteDiff(filePath: string, content: string, _isNew: boolean): DiffResult;
|
|
35
|
+
/**
|
|
36
|
+
* Format header for write_file operations
|
|
37
|
+
*/
|
|
38
|
+
export declare function formatWriteHeader(filePath: string, lineCount: number, isNew: boolean): string;
|