codebase-onboarder 0.7.0 → 1.0.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 +115 -12
- package/bin/onboarder.js +0 -0
- package/cli/agent/command.d.ts +71 -0
- package/cli/agent/command.js +177 -0
- package/cli/agent/command.js.map +1 -0
- package/cli/agent/config.d.ts +31 -0
- package/cli/agent/config.js +98 -0
- package/cli/agent/config.js.map +1 -0
- package/cli/agent/contracts.d.ts +77 -0
- package/cli/agent/contracts.js +2 -0
- package/cli/agent/contracts.js.map +1 -0
- package/cli/agent/files.d.ts +19 -0
- package/cli/agent/files.js +109 -0
- package/cli/agent/files.js.map +1 -0
- package/cli/agent/gitSource.d.ts +18 -0
- package/cli/agent/gitSource.js +69 -0
- package/cli/agent/gitSource.js.map +1 -0
- package/cli/agent/github.d.ts +21 -0
- package/cli/agent/github.js +94 -0
- package/cli/agent/github.js.map +1 -0
- package/cli/agent/mcp.d.ts +15 -0
- package/cli/agent/mcp.js +78 -0
- package/cli/agent/mcp.js.map +1 -0
- package/cli/agent/process.d.ts +18 -0
- package/cli/agent/process.js +118 -0
- package/cli/agent/process.js.map +1 -0
- package/cli/agent/protocol.d.ts +3 -0
- package/cli/agent/protocol.js +26 -0
- package/cli/agent/protocol.js.map +1 -0
- package/cli/agent/runner.d.ts +34 -0
- package/cli/agent/runner.js +152 -0
- package/cli/agent/runner.js.map +1 -0
- package/cli/agent/stdio.d.ts +1 -0
- package/cli/agent/stdio.js +62 -0
- package/cli/agent/stdio.js.map +1 -0
- package/cli/agent/tools.d.ts +7 -0
- package/cli/agent/tools.js +201 -0
- package/cli/agent/tools.js.map +1 -0
- package/cli/chat/app.d.ts +16 -0
- package/cli/chat/app.js +345 -0
- package/cli/chat/app.js.map +1 -0
- package/cli/chat/commands.d.ts +11 -0
- package/cli/chat/commands.js +120 -0
- package/cli/chat/commands.js.map +1 -0
- package/cli/chat/composer.d.ts +75 -0
- package/cli/chat/composer.js +467 -0
- package/cli/chat/composer.js.map +1 -0
- package/cli/chat/controller.d.ts +60 -0
- package/cli/chat/controller.js +393 -0
- package/cli/chat/controller.js.map +1 -0
- package/cli/chat/logo.d.ts +3 -0
- package/cli/chat/logo.js +14 -0
- package/cli/chat/logo.js.map +1 -0
- package/cli/chat/models.d.ts +18 -0
- package/cli/chat/models.js +88 -0
- package/cli/chat/models.js.map +1 -0
- package/cli/chat/profile.d.ts +5 -0
- package/cli/chat/profile.js +98 -0
- package/cli/chat/profile.js.map +1 -0
- package/cli/chat/render.d.ts +40 -0
- package/cli/chat/render.js +259 -0
- package/cli/chat/render.js.map +1 -0
- package/cli/chat/screen.d.ts +26 -0
- package/cli/chat/screen.js +117 -0
- package/cli/chat/screen.js.map +1 -0
- package/cli/chat/source.d.ts +29 -0
- package/cli/chat/source.js +116 -0
- package/cli/chat/source.js.map +1 -0
- package/cli/chat/store.d.ts +43 -0
- package/cli/chat/store.js +94 -0
- package/cli/chat/store.js.map +1 -0
- package/cli/chat/text.d.ts +4 -0
- package/cli/chat/text.js +27 -0
- package/cli/chat/text.js.map +1 -0
- package/cli/explorer/app.d.ts +1 -0
- package/cli/explorer/app.js +20 -1
- package/cli/explorer/app.js.map +1 -1
- package/cli/explorer/commands.js +5 -0
- package/cli/explorer/commands.js.map +1 -1
- package/cli/main.js +47 -13
- package/cli/main.js.map +1 -1
- package/docs/HERMES_RESEARCH.md +51 -0
- package/package.json +2 -1
- package/public/app.js +98 -24
- package/public/app.js.map +1 -1
- package/public/assets/onboarder-mark.svg +11 -0
- package/public/browser.d.ts +3 -0
- package/public/browser.js.map +1 -1
- package/public/index.html +24 -7
- package/public/js/about.js +2 -2
- package/public/js/about.js.map +1 -1
- package/public/js/aiDraft.d.ts +1 -0
- package/public/js/aiDraft.js +66 -17
- package/public/js/aiDraft.js.map +1 -1
- package/public/js/api.d.ts +8 -5
- package/public/js/api.js +45 -22
- package/public/js/api.js.map +1 -1
- package/public/js/deepAnalysisView.js +2 -2
- package/public/js/deepAnalysisView.js.map +1 -1
- package/public/js/diagramPane.d.ts +1 -0
- package/public/js/diagramPane.js +5 -0
- package/public/js/diagramPane.js.map +1 -1
- package/public/js/docsView.js +6 -6
- package/public/js/docsView.js.map +1 -1
- package/public/js/dom.d.ts +8 -0
- package/public/js/dom.js.map +1 -1
- package/public/js/llm.d.ts +3 -1
- package/public/js/llm.js +15 -8
- package/public/js/llm.js.map +1 -1
- package/public/js/repoFiles.js +1 -1
- package/public/js/repoFiles.js.map +1 -1
- package/public/js/reviewView.js +1 -1
- package/public/js/reviewView.js.map +1 -1
- package/public/styles.css +25 -0
- package/server/gitDiff.d.ts +1 -1
- package/server/hermesChat.d.ts +13 -0
- package/server/hermesChat.js +145 -0
- package/server/hermesChat.js.map +1 -0
- package/server/http.d.ts +5 -4
- package/server/http.js +0 -4
- package/server/http.js.map +1 -1
- package/server/mcp/server.d.ts +1 -1
- package/server/mcp/server.js +2 -2
- package/server/mcp/server.js.map +1 -1
- package/server/router.js +7 -3
- package/server/router.js.map +1 -1
- package/shared/diagram/aiMermaid.js +16 -13
- package/shared/diagram/aiMermaid.js.map +1 -1
package/cli/chat/app.js
ADDED
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { randomUUID } from 'node:crypto';
|
|
3
|
+
import { ChatController } from './controller.js';
|
|
4
|
+
import { TerminalComposer } from './composer.js';
|
|
5
|
+
import { profileDisplay } from './profile.js';
|
|
6
|
+
import { HermesModels } from './models.js';
|
|
7
|
+
import { ChatSources } from './source.js';
|
|
8
|
+
import { chatBanner, ChatRenderer } from './render.js';
|
|
9
|
+
import { AGENT_MODES } from '../agent/contracts.js';
|
|
10
|
+
import { agentCommand } from '../agent/command.js';
|
|
11
|
+
import { terminalText, redact } from '../agent/process.js';
|
|
12
|
+
import { repositoryFromRemote } from '../agent/github.js';
|
|
13
|
+
import { scanOptionsFromFlags } from '../scanOptions.js';
|
|
14
|
+
import { dim, bold, ok, bad, paint, colorEnabled } from '../ui.js';
|
|
15
|
+
export const CHAT_HELP = `
|
|
16
|
+
Onboarder chat — a repository agent in your terminal
|
|
17
|
+
|
|
18
|
+
onboarder Chat about the current repository
|
|
19
|
+
onboarder chat [folder|GitHub URL] Open the chat harness (alias: tui)
|
|
20
|
+
onboarder chat https://github.com/owner/repo Clone a repository, then ask questions
|
|
21
|
+
onboarder chat --resume <chat-id> Continue a saved conversation
|
|
22
|
+
onboarder chat --mode review Start in a specific workflow
|
|
23
|
+
onboarder explore [folder|url] Open the original offline explorer
|
|
24
|
+
|
|
25
|
+
Type / to see live commands. ↑/↓ select; Tab or Enter inserts; Esc dismisses.
|
|
26
|
+
Chat fills the terminal and adjusts on resize. PgUp/PgDn scroll the conversation.
|
|
27
|
+
Alt-Enter adds a line. Pasted multiline messages stay in the composer.
|
|
28
|
+
/review 42 · /triage 12 · /implement <task> · /pr <task> · /github <task>
|
|
29
|
+
/model opens a searchable model picker. /model configure opens full Hermes setup.
|
|
30
|
+
/permissions checks on permits repository test execution.
|
|
31
|
+
/permissions github on permits GitHub writes explicitly requested in a task.
|
|
32
|
+
/new starts fresh; /history and /resume restore conversations.
|
|
33
|
+
/repo <GitHub URL> or a pasted URL clones and opens a repository.
|
|
34
|
+
/clone <GitHub URL> also works; /pull refreshes a clean saved checkout.
|
|
35
|
+
/paste enters multiline input; /send submits it; /discard cancels it.
|
|
36
|
+
Ctrl-C cancels an active task. Ctrl-D saves and exits.
|
|
37
|
+
|
|
38
|
+
Flags: --mode, --model, --provider, --allow-checks, --allow-github-writes,
|
|
39
|
+
--timeout, --max-turns, --resume, --base, --no-color
|
|
40
|
+
For scripts: onboarder agent <workflow> --task "..." --json
|
|
41
|
+
`;
|
|
42
|
+
/** Analysis modules are loaded only when a browsing command actually needs them. */
|
|
43
|
+
export function repositoryBrowser(flags, progress, version = '') {
|
|
44
|
+
let cached = null;
|
|
45
|
+
return {
|
|
46
|
+
get files() { return cached?.scan?.files?.map((f) => f.path) || []; },
|
|
47
|
+
invalidate() { cached = null; },
|
|
48
|
+
async run(name, args, root, signal) {
|
|
49
|
+
const [{ openRepo }, { lookup }, { getGitDiff }, { formatDiff }] = await Promise.all([
|
|
50
|
+
import('../explorer/session.js'), import('../explorer/commands.js'), import('../../server/gitDiff.js'), import('../explorer/featureViews.js'),
|
|
51
|
+
]);
|
|
52
|
+
signal.throwIfAborted();
|
|
53
|
+
if (!cached || cached.root !== root || name === 'rescan') {
|
|
54
|
+
progress('Indexing repository…');
|
|
55
|
+
const repo = await openRepo(root, { ...scanOptionsFromFlags(flags), signal });
|
|
56
|
+
signal.throwIfAborted();
|
|
57
|
+
cached = repo;
|
|
58
|
+
}
|
|
59
|
+
if (name === 'rescan')
|
|
60
|
+
return 'Repository analysis refreshed.';
|
|
61
|
+
const ctx = { repo: cached, flags, version,
|
|
62
|
+
diff: async (base, head, file) => formatDiff(await getGitDiff(root, { base, head, file, signal }), cached.facts.importers || {}, cached.scan.files.length, file),
|
|
63
|
+
engines: async () => { const [{ toolsStatus }, { formatEngines }] = await Promise.all([import('../../server/tools/scan.js'), import('../explorer/featureViews.js')]); return formatEngines(toolsStatus()); },
|
|
64
|
+
deep: async (tool) => {
|
|
65
|
+
const [{ toolsStatus, runExternalAnalysis }, { formatDeepAnalysis }] = await Promise.all([import('../../server/tools/scan.js'), import('../explorer/featureViews.js')]);
|
|
66
|
+
if (tool !== 'all' && !Object.hasOwn(toolsStatus(), tool))
|
|
67
|
+
throw new Error('Unknown analyzer. Use /engines.');
|
|
68
|
+
return formatDeepAnalysis(await runExternalAnalysis(root, tool === 'all' ? { signal } : { signal, tools: [tool] }));
|
|
69
|
+
},
|
|
70
|
+
web: async () => { const { startWeb } = await import('../explorer/app.js'); return (await startWeb({ flags, repo: cached })) + '\nRepository path: ' + root; }, };
|
|
71
|
+
const command = lookup(name);
|
|
72
|
+
if (!command)
|
|
73
|
+
throw new Error('Unknown repository command.');
|
|
74
|
+
return String(await command.run(ctx, args));
|
|
75
|
+
},
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
export async function runChat({ target = null, flags = {}, out = console.log, err = console.error, version = '' } = {}) {
|
|
79
|
+
if (flags.help) {
|
|
80
|
+
out(CHAT_HELP);
|
|
81
|
+
return 0;
|
|
82
|
+
}
|
|
83
|
+
if (!process.stdin.isTTY || !process.stdout.isTTY) {
|
|
84
|
+
out('The chat harness needs an interactive terminal. For scripts use onboarder agent <workflow> --task "..." --json.');
|
|
85
|
+
return 0;
|
|
86
|
+
}
|
|
87
|
+
if (flags.task || flags.json || flags.dryRun)
|
|
88
|
+
throw new Error('Use onboarder agent <workflow> --task "..." for one-shot or JSON requests.');
|
|
89
|
+
const mode = flags.mode || 'ask';
|
|
90
|
+
if (!AGENT_MODES.includes(mode))
|
|
91
|
+
throw new Error('Choose --mode ' + AGENT_MODES.join('|'));
|
|
92
|
+
const timeout = Number(flags.timeout ?? 300), maxTurns = Number(flags.maxTurns ?? 24);
|
|
93
|
+
if (!Number.isInteger(timeout) || timeout < 15 || timeout > 3600 || !Number.isInteger(maxTurns) || maxTurns < 1 || maxTurns > 100)
|
|
94
|
+
throw new Error('Use --timeout 15–3600 and --max-turns 1–100.');
|
|
95
|
+
const sources = new ChatSources(), startup = new AbortController(), cancelStartup = () => startup.abort();
|
|
96
|
+
process.on('SIGINT', cancelStartup);
|
|
97
|
+
process.on('SIGTERM', cancelStartup);
|
|
98
|
+
let source;
|
|
99
|
+
try {
|
|
100
|
+
source = await sources.open(target || '.', process.cwd(), startup.signal, text => out(dim(terminalText(redact(text)))));
|
|
101
|
+
}
|
|
102
|
+
catch (e) {
|
|
103
|
+
if (startup.signal.aborted) {
|
|
104
|
+
out('Repository loading cancelled.');
|
|
105
|
+
return 130;
|
|
106
|
+
}
|
|
107
|
+
throw e;
|
|
108
|
+
}
|
|
109
|
+
finally {
|
|
110
|
+
process.off('SIGINT', cancelStartup);
|
|
111
|
+
process.off('SIGTERM', cancelStartup);
|
|
112
|
+
}
|
|
113
|
+
const root = source.root;
|
|
114
|
+
const theme = { accent: text => paint(text, 'yellow', 'bold'), muted: dim, strong: bold, success: ok, error: bad,
|
|
115
|
+
status: text => colorEnabled() ? '\x1b[48;5;234m\x1b[38;5;220m' + text + '\x1b[0m' : text };
|
|
116
|
+
let composer = null, handingOff = false, exiting = false, active = null;
|
|
117
|
+
let display = await profileDisplay(), startedAt = 0, toolCalls = 0, lastTool = '';
|
|
118
|
+
let resolveDone;
|
|
119
|
+
const done = new Promise(resolve => { resolveDone = resolve; });
|
|
120
|
+
const safe = (text) => terminalText(redact(text));
|
|
121
|
+
const print = (text) => {
|
|
122
|
+
if (composer && !handingOff && !exiting)
|
|
123
|
+
composer.print(text);
|
|
124
|
+
else
|
|
125
|
+
out(text);
|
|
126
|
+
};
|
|
127
|
+
const browser = repositoryBrowser(flags, text => print(dim(text)), version);
|
|
128
|
+
const renderer = new ChatRenderer(print, theme);
|
|
129
|
+
const models = new HermesModels();
|
|
130
|
+
const configureModel = async (signal) => {
|
|
131
|
+
// Give the official wizard exclusive terminal ownership, then restore chat.
|
|
132
|
+
const before = await profileDisplay();
|
|
133
|
+
handingOff = true;
|
|
134
|
+
composer?.suspend();
|
|
135
|
+
try {
|
|
136
|
+
const code = await agentCommand('model', [], {}, { root: controller.settings.root, signal });
|
|
137
|
+
if (code !== 0)
|
|
138
|
+
throw new Error('Model setup did not complete. Existing settings remain available.');
|
|
139
|
+
display = await profileDisplay();
|
|
140
|
+
if (!display.model) {
|
|
141
|
+
print(dim('Choose a provider and model to enable AI chat. Offline commands are ready.'));
|
|
142
|
+
return false;
|
|
143
|
+
}
|
|
144
|
+
return display.model !== before.model || display.provider !== before.provider;
|
|
145
|
+
}
|
|
146
|
+
finally {
|
|
147
|
+
display = await profileDisplay();
|
|
148
|
+
handingOff = false;
|
|
149
|
+
if (!exiting)
|
|
150
|
+
composer?.start();
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
const chooseModel = async (configure, signal) => {
|
|
154
|
+
if (configure)
|
|
155
|
+
return configureModel(signal);
|
|
156
|
+
print(dim('Loading Hermes model catalog…'));
|
|
157
|
+
let providers = [];
|
|
158
|
+
try {
|
|
159
|
+
providers = await models.catalog(signal);
|
|
160
|
+
}
|
|
161
|
+
catch (e) {
|
|
162
|
+
signal.throwIfAborted();
|
|
163
|
+
print(dim(safe(e instanceof Error ? e.message : String(e))));
|
|
164
|
+
}
|
|
165
|
+
signal.throwIfAborted();
|
|
166
|
+
while (!exiting) {
|
|
167
|
+
const provider = await composer.choose({ title: 'Model Picker — Select Provider',
|
|
168
|
+
hint: `Current: ${controller.settings.model || display.model || 'not configured'} · ${controller.settings.provider || display.provider || 'choose a provider'}`,
|
|
169
|
+
selected: controller.settings.provider || display.provider,
|
|
170
|
+
choices: [...providers.map(p => ({ value: p.id, label: p.name, detail: `${p.models.length} models` })),
|
|
171
|
+
{ value: ':configure', label: 'Configure provider / authentication', detail: 'Full Hermes setup · all supported providers' },
|
|
172
|
+
{ value: ':cancel', label: 'Keep current settings' }], signal });
|
|
173
|
+
if (provider === null || provider === ':cancel')
|
|
174
|
+
return false;
|
|
175
|
+
if (provider === ':configure')
|
|
176
|
+
return configureModel(signal);
|
|
177
|
+
const selectedProvider = providers.find(p => p.id === provider);
|
|
178
|
+
const model = await composer.choose({ title: 'Model Picker — ' + selectedProvider.name,
|
|
179
|
+
hint: 'Type to search models · changes are saved to your Onboarder Hermes profile',
|
|
180
|
+
selected: controller.settings.model || display.model,
|
|
181
|
+
choices: [...selectedProvider.models.map(m => ({ value: m, label: m })),
|
|
182
|
+
{ value: ':back', label: '← Back to providers' }, { value: ':configure', label: 'Configure another model / endpoint' }], signal });
|
|
183
|
+
if (model === null)
|
|
184
|
+
return false;
|
|
185
|
+
if (model === ':back')
|
|
186
|
+
continue;
|
|
187
|
+
if (model === ':configure')
|
|
188
|
+
return configureModel(signal);
|
|
189
|
+
print(dim('Activating ' + model + '…'));
|
|
190
|
+
try {
|
|
191
|
+
const selected = await models.select(model, provider, signal);
|
|
192
|
+
display = await profileDisplay();
|
|
193
|
+
print(ok('Model: ' + selected.model + ' · Provider: ' + selected.provider));
|
|
194
|
+
return true;
|
|
195
|
+
}
|
|
196
|
+
catch (e) {
|
|
197
|
+
signal.throwIfAborted();
|
|
198
|
+
print(bad(safe(e instanceof Error ? e.message : String(e))));
|
|
199
|
+
const next = await composer.choose({ title: 'Model needs configuration',
|
|
200
|
+
hint: 'Your current model and conversation are kept until a selection succeeds.', signal,
|
|
201
|
+
choices: [{ value: 'configure', label: 'Configure provider / authentication', detail: 'Continue in the official Hermes wizard' },
|
|
202
|
+
{ value: 'back', label: 'Choose a different provider or model' }, { value: 'cancel', label: 'Keep current settings' }] });
|
|
203
|
+
if (next === 'configure')
|
|
204
|
+
return configureModel(signal);
|
|
205
|
+
if (next !== 'back')
|
|
206
|
+
return false;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
return false;
|
|
210
|
+
};
|
|
211
|
+
const onOutput = (event) => {
|
|
212
|
+
if (event.type === 'start') {
|
|
213
|
+
startedAt = Date.now();
|
|
214
|
+
toolCalls = 0;
|
|
215
|
+
lastTool = '';
|
|
216
|
+
}
|
|
217
|
+
if (event.type === 'event' && event.event.type === 'tool_use') {
|
|
218
|
+
toolCalls++;
|
|
219
|
+
lastTool = safe(event.event.name || '').replace(/^mcp__onboarder__/, '');
|
|
220
|
+
}
|
|
221
|
+
if (event.type === 'end')
|
|
222
|
+
browser.invalidate();
|
|
223
|
+
if (event.type === 'clear')
|
|
224
|
+
composer?.clear();
|
|
225
|
+
else if (event.type === 'exit') {
|
|
226
|
+
exiting = true;
|
|
227
|
+
composer?.close();
|
|
228
|
+
resolveDone();
|
|
229
|
+
}
|
|
230
|
+
else
|
|
231
|
+
renderer.handle(event);
|
|
232
|
+
composer?.refresh();
|
|
233
|
+
};
|
|
234
|
+
const controller = new ChatController({ root, sourceUrl: source.sourceUrl, mode: mode, model: flags.model, provider: flags.provider,
|
|
235
|
+
checks: flags.allowChecks === true, github: flags.allowGithubWrites === true, timeout, maxTurns, base: flags.base }, {
|
|
236
|
+
output: onOutput,
|
|
237
|
+
sources,
|
|
238
|
+
chooseModel,
|
|
239
|
+
browse: (name, args, folder, signal) => browser.run(name, args, folder, signal),
|
|
240
|
+
utility: async (name, signal) => {
|
|
241
|
+
if (name === 'model') {
|
|
242
|
+
await configureModel(signal);
|
|
243
|
+
return 'Hermes model setup closed.';
|
|
244
|
+
}
|
|
245
|
+
if (name === 'tools')
|
|
246
|
+
return `Hermes tools (${catalog.length})\n\n` + catalog.map(tool => `${tool.name}\n ${tool.description}`).join('\n\n');
|
|
247
|
+
const lines = [];
|
|
248
|
+
const code = await agentCommand(name, [], {}, { root: controller.settings.root, signal, out: t => lines.push(t), err: t => lines.push(t) });
|
|
249
|
+
return lines.join('\n') + (code ? '\nCheck the details above before running a task.' : '');
|
|
250
|
+
},
|
|
251
|
+
});
|
|
252
|
+
const { scopedTools } = await import('../agent/mcp.js');
|
|
253
|
+
const catalog = scopedTools({ schemaVersion: 1, id: randomUUID(), mode: controller.settings.mode, root, sourceRoot: root,
|
|
254
|
+
repository: source.sourceUrl ? repositoryFromRemote(source.sourceUrl) : null, remote: source.sourceUrl || null, branch: null, baseBranch: null,
|
|
255
|
+
task: '', permissions: { files: false, checks: false, github: false }, startedAt: '', timeoutSeconds: timeout, maxTurns, auditFile: '',
|
|
256
|
+
}).map(({ name, description }) => ({ name, description }));
|
|
257
|
+
let bannerKey = '', bannerText = '';
|
|
258
|
+
const banner = (columns, rows) => {
|
|
259
|
+
const key = JSON.stringify([controller.settings, display, columns, rows]);
|
|
260
|
+
if (key !== bannerKey) {
|
|
261
|
+
bannerKey = key;
|
|
262
|
+
bannerText = chatBanner(controller.settings, columns, theme, version, { ...display, tools: catalog.length, catalog, session: controller.record.id, rows, fullscreen: true });
|
|
263
|
+
}
|
|
264
|
+
return bannerText;
|
|
265
|
+
};
|
|
266
|
+
composer = new TerminalComposer({ input: process.stdin, output: process.stdout, theme, fullscreen: true, welcome: banner,
|
|
267
|
+
header: () => theme.accent('◈ ONBOARDER') + dim(' · ' + controller.settings.mode + ' · ' + (controller.settings.sourceUrl || controller.settings.root)),
|
|
268
|
+
state: () => {
|
|
269
|
+
const s = controller.settings, model = s.model || display.model || 'model not configured';
|
|
270
|
+
const repo = s.sourceUrl?.split('/').at(-1) || path.basename(s.root);
|
|
271
|
+
const tokens = controller.lastResult?.tokens?.total;
|
|
272
|
+
const status = [model, repo, s.mode, `${catalog.length} tools`, tokens === undefined ? 'tokens —' : `${tokens} tokens`,
|
|
273
|
+
controller.busy && startedAt ? `${Math.floor((Date.now() - startedAt) / 1000)}s · ${toolCalls} calls${lastTool ? ' · ' + lastTool : ''}` : `${controller.record.turns.length} turns`].join(' │ ');
|
|
274
|
+
return { mode: s.mode, status: safe(status), busy: controller.busy, pasting: controller.pasting, files: browser.files };
|
|
275
|
+
},
|
|
276
|
+
submit: line => {
|
|
277
|
+
if (exiting)
|
|
278
|
+
return;
|
|
279
|
+
if (controller.busy) {
|
|
280
|
+
void controller.accept(line);
|
|
281
|
+
composer?.refresh();
|
|
282
|
+
return;
|
|
283
|
+
}
|
|
284
|
+
// Only one task owns the controller; cancellation/exit remain available while it runs.
|
|
285
|
+
const task = controller.accept(line);
|
|
286
|
+
active = task;
|
|
287
|
+
composer?.refresh();
|
|
288
|
+
void task.catch(e => print(bad(safe(e instanceof Error ? e.message : String(e))))).finally(() => {
|
|
289
|
+
renderer.finish();
|
|
290
|
+
if (active === task)
|
|
291
|
+
active = null;
|
|
292
|
+
composer?.refresh();
|
|
293
|
+
});
|
|
294
|
+
},
|
|
295
|
+
cancel: () => {
|
|
296
|
+
if (controller.busy) {
|
|
297
|
+
controller.cancel();
|
|
298
|
+
print(dim('Cancelling…'));
|
|
299
|
+
}
|
|
300
|
+
else if (controller.pasting) {
|
|
301
|
+
void controller.accept('/discard');
|
|
302
|
+
composer?.refresh();
|
|
303
|
+
}
|
|
304
|
+
else
|
|
305
|
+
print(dim('Use /exit or Ctrl-D to leave.'));
|
|
306
|
+
}, exit: () => controller.close(),
|
|
307
|
+
});
|
|
308
|
+
const terminate = () => controller.close();
|
|
309
|
+
const interrupt = () => { if (controller.busy)
|
|
310
|
+
controller.cancel();
|
|
311
|
+
else
|
|
312
|
+
controller.close(); };
|
|
313
|
+
const ticker = setInterval(() => { if (controller.busy && !composer?.choosing)
|
|
314
|
+
composer?.refresh(); }, 1000);
|
|
315
|
+
ticker.unref();
|
|
316
|
+
process.on('SIGTERM', terminate);
|
|
317
|
+
process.on('SIGINT', interrupt);
|
|
318
|
+
try {
|
|
319
|
+
if (flags.resume)
|
|
320
|
+
await controller.resume(flags.resume);
|
|
321
|
+
composer.start();
|
|
322
|
+
if (!controller.settings.model && !display.model) {
|
|
323
|
+
const answer = await composer.choose({ title: 'Welcome — Model & Provider', hint: 'Set up AI chat now, or use repository commands offline.',
|
|
324
|
+
choices: [{ value: 'configure', label: 'Choose a provider and model', detail: 'Open full Hermes configuration' },
|
|
325
|
+
{ value: 'offline', label: 'Continue offline', detail: '/map · /tree · /find · set up later with /model' }], signal: startup.signal });
|
|
326
|
+
if (answer === 'configure' && !exiting)
|
|
327
|
+
await controller.accept('/model configure');
|
|
328
|
+
}
|
|
329
|
+
await done;
|
|
330
|
+
if (active)
|
|
331
|
+
await active;
|
|
332
|
+
if (await controller.save())
|
|
333
|
+
out(dim('Conversation saved. Resume it with /resume ' + controller.record.id));
|
|
334
|
+
return 0;
|
|
335
|
+
}
|
|
336
|
+
finally {
|
|
337
|
+
controller.cancel();
|
|
338
|
+
process.off('SIGTERM', terminate);
|
|
339
|
+
process.off('SIGINT', interrupt);
|
|
340
|
+
clearInterval(ticker);
|
|
341
|
+
exiting = true;
|
|
342
|
+
composer.close();
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
//# sourceMappingURL=app.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"app.js","sourceRoot":"","sources":["../../src/cli/chat/app.ts"],"names":[],"mappings":"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","sourcesContent":["import path from 'node:path';\nimport { randomUUID } from 'node:crypto';\nimport { ChatController, type ChatOutput } from './controller.js';\nimport { TerminalComposer } from './composer.js';\nimport { profileDisplay } from './profile.js';\nimport { HermesModels } from './models.js';\nimport { ChatSources } from './source.js';\nimport { chatBanner, ChatRenderer, type ChatTheme } from './render.js';\nimport { AGENT_MODES, type AgentMode } from '../agent/contracts.js';\nimport { agentCommand } from '../agent/command.js';\nimport { terminalText, redact } from '../agent/process.js';\nimport { repositoryFromRemote } from '../agent/github.js';\nimport type { RunManifest } from '../agent/contracts.js';\nimport { scanOptionsFromFlags } from '../scanOptions.js';\nimport { dim, bold, ok, bad, paint, colorEnabled } from '../ui.js';\n\nexport const CHAT_HELP = `\n Onboarder chat — a repository agent in your terminal\n\n onboarder Chat about the current repository\n onboarder chat [folder|GitHub URL] Open the chat harness (alias: tui)\n onboarder chat https://github.com/owner/repo Clone a repository, then ask questions\n onboarder chat --resume <chat-id> Continue a saved conversation\n onboarder chat --mode review Start in a specific workflow\n onboarder explore [folder|url] Open the original offline explorer\n\n Type / to see live commands. ↑/↓ select; Tab or Enter inserts; Esc dismisses.\n Chat fills the terminal and adjusts on resize. PgUp/PgDn scroll the conversation.\n Alt-Enter adds a line. Pasted multiline messages stay in the composer.\n /review 42 · /triage 12 · /implement <task> · /pr <task> · /github <task>\n /model opens a searchable model picker. /model configure opens full Hermes setup.\n /permissions checks on permits repository test execution.\n /permissions github on permits GitHub writes explicitly requested in a task.\n /new starts fresh; /history and /resume restore conversations.\n /repo <GitHub URL> or a pasted URL clones and opens a repository.\n /clone <GitHub URL> also works; /pull refreshes a clean saved checkout.\n /paste enters multiline input; /send submits it; /discard cancels it.\n Ctrl-C cancels an active task. Ctrl-D saves and exits.\n\n Flags: --mode, --model, --provider, --allow-checks, --allow-github-writes,\n --timeout, --max-turns, --resume, --base, --no-color\n For scripts: onboarder agent <workflow> --task \"...\" --json\n`;\ninterface ChatOptions { target?: string | null; flags?: Record<string, any>; out?: (text: string) => void; err?: (text: string) => void; version?: string }\n\n/** Analysis modules are loaded only when a browsing command actually needs them. */\nexport function repositoryBrowser(flags: Record<string, any>, progress: (text: string) => void, version = '') {\n let cached: any = null;\n return {\n get files(): string[] { return cached?.scan?.files?.map((f: any) => f.path) || []; },\n invalidate() { cached = null; },\n async run(name: string, args: string[], root: string, signal: AbortSignal): Promise<string> {\n const [{ openRepo }, { lookup }, { getGitDiff }, { formatDiff }] = await Promise.all([\n import('../explorer/session.js'), import('../explorer/commands.js'), import('../../server/gitDiff.js'), import('../explorer/featureViews.js'),\n ]);\n signal.throwIfAborted();\n if (!cached || cached.root !== root || name === 'rescan') {\n progress('Indexing repository…');\n const repo = await openRepo(root, { ...scanOptionsFromFlags(flags), signal });\n signal.throwIfAborted(); cached = repo;\n }\n if (name === 'rescan') return 'Repository analysis refreshed.';\n const ctx = { repo: cached, flags, version,\n diff: async (base: string, head: string, file: string) => formatDiff(await getGitDiff(root, { base, head, file, signal }), cached.facts.importers || {}, cached.scan.files.length, file),\n engines: async () => { const [{ toolsStatus }, { formatEngines }] = await Promise.all([import('../../server/tools/scan.js'), import('../explorer/featureViews.js')]); return formatEngines(toolsStatus()); },\n deep: async (tool: string) => {\n const [{ toolsStatus, runExternalAnalysis }, { formatDeepAnalysis }] = await Promise.all([import('../../server/tools/scan.js'), import('../explorer/featureViews.js')]);\n if (tool !== 'all' && !Object.hasOwn(toolsStatus(), tool)) throw new Error('Unknown analyzer. Use /engines.');\n return formatDeepAnalysis(await runExternalAnalysis(root, tool === 'all' ? { signal } : { signal, tools: [tool] }));\n },\n web: async () => { const { startWeb } = await import('../explorer/app.js'); return (await startWeb({ flags, repo: cached })) + '\\nRepository path: ' + root; },\n };\n const command = lookup(name);\n if (!command) throw new Error('Unknown repository command.');\n return String(await command.run(ctx, args));\n },\n };\n}\nexport async function runChat({ target = null, flags = {}, out = console.log, err = console.error, version = '' }: ChatOptions = {}): Promise<number> {\n if (flags.help) { out(CHAT_HELP); return 0; }\n if (!process.stdin.isTTY || !process.stdout.isTTY) { out('The chat harness needs an interactive terminal. For scripts use onboarder agent <workflow> --task \"...\" --json.'); return 0; }\n if (flags.task || flags.json || flags.dryRun) throw new Error('Use onboarder agent <workflow> --task \"...\" for one-shot or JSON requests.');\n const mode = flags.mode || 'ask';\n if (!AGENT_MODES.includes(mode)) throw new Error('Choose --mode ' + AGENT_MODES.join('|'));\n const timeout = Number(flags.timeout ?? 300), maxTurns = Number(flags.maxTurns ?? 24);\n if (!Number.isInteger(timeout) || timeout < 15 || timeout > 3600 || !Number.isInteger(maxTurns) || maxTurns < 1 || maxTurns > 100) throw new Error('Use --timeout 15–3600 and --max-turns 1–100.');\n const sources = new ChatSources(), startup = new AbortController(), cancelStartup = () => startup.abort();\n process.on('SIGINT', cancelStartup); process.on('SIGTERM', cancelStartup);\n let source: Awaited<ReturnType<ChatSources['open']>>;\n try { source = await sources.open(target || '.', process.cwd(), startup.signal, text => out(dim(terminalText(redact(text))))); }\n catch (e) { if (startup.signal.aborted) { out('Repository loading cancelled.'); return 130; } throw e; }\n finally { process.off('SIGINT', cancelStartup); process.off('SIGTERM', cancelStartup); }\n const root = source.root;\n const theme: ChatTheme = { accent: text => paint(text, 'yellow', 'bold'), muted: dim, strong: bold, success: ok, error: bad,\n status: text => colorEnabled() ? '\\x1b[48;5;234m\\x1b[38;5;220m' + text + '\\x1b[0m' : text };\n let composer: TerminalComposer | null = null, handingOff = false, exiting = false, active: Promise<void> | null = null;\n let display = await profileDisplay(), startedAt = 0, toolCalls = 0, lastTool = '';\n let resolveDone: () => void;\n const done = new Promise<void>(resolve => { resolveDone = resolve; });\n const safe = (text: string) => terminalText(redact(text));\n const print = (text: string) => {\n if (composer && !handingOff && !exiting) composer.print(text); else out(text);\n };\n const browser = repositoryBrowser(flags, text => print(dim(text)), version);\n const renderer = new ChatRenderer(print, theme);\n const models = new HermesModels();\n const configureModel = async (signal: AbortSignal): Promise<boolean> => {\n // Give the official wizard exclusive terminal ownership, then restore chat.\n const before = await profileDisplay();\n handingOff = true; composer?.suspend();\n try {\n const code = await agentCommand('model', [], {}, { root: controller.settings.root, signal });\n if (code !== 0) throw new Error('Model setup did not complete. Existing settings remain available.');\n display = await profileDisplay();\n if (!display.model) { print(dim('Choose a provider and model to enable AI chat. Offline commands are ready.')); return false; }\n return display.model !== before.model || display.provider !== before.provider;\n } finally { display = await profileDisplay(); handingOff = false; if (!exiting) composer?.start(); }\n };\n const chooseModel = async (configure: boolean, signal: AbortSignal): Promise<boolean> => {\n if (configure) return configureModel(signal);\n print(dim('Loading Hermes model catalog…'));\n let providers: Awaited<ReturnType<HermesModels['catalog']>> = [];\n try { providers = await models.catalog(signal); }\n catch (e) { signal.throwIfAborted(); print(dim(safe(e instanceof Error ? e.message : String(e)))); }\n signal.throwIfAborted();\n while (!exiting) {\n const provider = await composer!.choose({ title: 'Model Picker — Select Provider',\n hint: `Current: ${controller.settings.model || display.model || 'not configured'} · ${controller.settings.provider || display.provider || 'choose a provider'}`,\n selected: controller.settings.provider || display.provider,\n choices: [...providers.map(p => ({ value: p.id, label: p.name, detail: `${p.models.length} models` })),\n { value: ':configure', label: 'Configure provider / authentication', detail: 'Full Hermes setup · all supported providers' },\n { value: ':cancel', label: 'Keep current settings' }], signal });\n if (provider === null || provider === ':cancel') return false;\n if (provider === ':configure') return configureModel(signal);\n const selectedProvider = providers.find(p => p.id === provider)!;\n const model = await composer!.choose({ title: 'Model Picker — ' + selectedProvider.name,\n hint: 'Type to search models · changes are saved to your Onboarder Hermes profile',\n selected: controller.settings.model || display.model,\n choices: [...selectedProvider.models.map(m => ({ value: m, label: m })),\n { value: ':back', label: '← Back to providers' }, { value: ':configure', label: 'Configure another model / endpoint' }], signal });\n if (model === null) return false;\n if (model === ':back') continue;\n if (model === ':configure') return configureModel(signal);\n print(dim('Activating ' + model + '…'));\n try {\n const selected = await models.select(model, provider, signal);\n display = await profileDisplay();\n print(ok('Model: ' + selected.model + ' · Provider: ' + selected.provider));\n return true;\n } catch (e) {\n signal.throwIfAborted();\n print(bad(safe(e instanceof Error ? e.message : String(e))));\n const next = await composer!.choose({ title: 'Model needs configuration',\n hint: 'Your current model and conversation are kept until a selection succeeds.', signal,\n choices: [{ value: 'configure', label: 'Configure provider / authentication', detail: 'Continue in the official Hermes wizard' },\n { value: 'back', label: 'Choose a different provider or model' }, { value: 'cancel', label: 'Keep current settings' }] });\n if (next === 'configure') return configureModel(signal);\n if (next !== 'back') return false;\n }\n }\n return false;\n };\n const onOutput = (event: ChatOutput) => {\n if (event.type === 'start') { startedAt = Date.now(); toolCalls = 0; lastTool = ''; }\n if (event.type === 'event' && event.event.type === 'tool_use') { toolCalls++; lastTool = safe(event.event.name || '').replace(/^mcp__onboarder__/, ''); }\n if (event.type === 'end') browser.invalidate();\n if (event.type === 'clear') composer?.clear();\n else if (event.type === 'exit') { exiting = true; composer?.close(); resolveDone!(); }\n else renderer.handle(event);\n composer?.refresh();\n };\n const controller = new ChatController({ root, sourceUrl: source.sourceUrl, mode: mode as AgentMode, model: flags.model, provider: flags.provider,\n checks: flags.allowChecks === true, github: flags.allowGithubWrites === true, timeout, maxTurns, base: flags.base }, {\n output: onOutput,\n sources,\n chooseModel,\n browse: (name, args, folder, signal) => browser.run(name, args, folder, signal),\n utility: async (name, signal) => {\n if (name === 'model') {\n await configureModel(signal); return 'Hermes model setup closed.';\n }\n if (name === 'tools') return `Hermes tools (${catalog.length})\\n\\n` + catalog.map(tool => `${tool.name}\\n ${tool.description}`).join('\\n\\n');\n const lines: string[] = [];\n const code = await agentCommand(name, [], {}, { root: controller.settings.root, signal, out: t => lines.push(t), err: t => lines.push(t) });\n return lines.join('\\n') + (code ? '\\nCheck the details above before running a task.' : '');\n },\n });\n const { scopedTools } = await import('../agent/mcp.js');\n const catalog = scopedTools({ schemaVersion: 1, id: randomUUID(), mode: controller.settings.mode, root, sourceRoot: root,\n repository: source.sourceUrl ? repositoryFromRemote(source.sourceUrl) : null, remote: source.sourceUrl || null, branch: null, baseBranch: null,\n task: '', permissions: { files: false, checks: false, github: false }, startedAt: '', timeoutSeconds: timeout, maxTurns, auditFile: '',\n } satisfies RunManifest).map(({ name, description }) => ({ name, description }));\n let bannerKey = '', bannerText = '';\n const banner = (columns: number, rows: number) => {\n const key = JSON.stringify([controller.settings, display, columns, rows]);\n if (key !== bannerKey) { bannerKey = key; bannerText = chatBanner(controller.settings, columns, theme, version, { ...display, tools: catalog.length, catalog, session: controller.record.id, rows, fullscreen: true }); }\n return bannerText;\n };\n composer = new TerminalComposer({ input: process.stdin, output: process.stdout, theme, fullscreen: true, welcome: banner,\n header: () => theme.accent('◈ ONBOARDER') + dim(' · ' + controller.settings.mode + ' · ' + (controller.settings.sourceUrl || controller.settings.root)),\n state: () => {\n const s = controller.settings, model = s.model || display.model || 'model not configured';\n const repo = s.sourceUrl?.split('/').at(-1) || path.basename(s.root);\n const tokens = controller.lastResult?.tokens?.total;\n const status = [model, repo, s.mode, `${catalog.length} tools`, tokens === undefined ? 'tokens —' : `${tokens} tokens`,\n controller.busy && startedAt ? `${Math.floor((Date.now() - startedAt) / 1000)}s · ${toolCalls} calls${lastTool ? ' · ' + lastTool : ''}` : `${controller.record.turns.length} turns`].join(' │ ');\n return { mode: s.mode, status: safe(status), busy: controller.busy, pasting: controller.pasting, files: browser.files };\n },\n submit: line => {\n if (exiting) return;\n if (controller.busy) { void controller.accept(line); composer?.refresh(); return; }\n // Only one task owns the controller; cancellation/exit remain available while it runs.\n const task = controller.accept(line); active = task;\n composer?.refresh();\n void task.catch(e => print(bad(safe(e instanceof Error ? e.message : String(e))))).finally(() => {\n renderer.finish(); if (active === task) active = null;\n composer?.refresh();\n });\n },\n cancel: () => {\n if (controller.busy) { controller.cancel(); print(dim('Cancelling…')); }\n else if (controller.pasting) { void controller.accept('/discard'); composer?.refresh(); }\n else print(dim('Use /exit or Ctrl-D to leave.'));\n }, exit: () => controller.close(),\n });\n const terminate = () => controller.close();\n const interrupt = () => { if (controller.busy) controller.cancel(); else controller.close(); };\n const ticker = setInterval(() => { if (controller.busy && !composer?.choosing) composer?.refresh(); }, 1000); ticker.unref();\n process.on('SIGTERM', terminate); process.on('SIGINT', interrupt);\n try {\n if (flags.resume) await controller.resume(flags.resume);\n composer.start();\n if (!controller.settings.model && !display.model) {\n const answer = await composer.choose({ title: 'Welcome — Model & Provider', hint: 'Set up AI chat now, or use repository commands offline.',\n choices: [{ value: 'configure', label: 'Choose a provider and model', detail: 'Open full Hermes configuration' },\n { value: 'offline', label: 'Continue offline', detail: '/map · /tree · /find · set up later with /model' }], signal: startup.signal });\n if (answer === 'configure' && !exiting) await controller.accept('/model configure');\n }\n await done; if (active) await active;\n if (await controller.save()) out(dim('Conversation saved. Resume it with /resume ' + controller.record.id));\n return 0;\n } finally {\n controller.cancel(); process.off('SIGTERM', terminate); process.off('SIGINT', interrupt); clearInterval(ticker);\n exiting = true; composer.close();\n }\n}\n"]}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export declare const CHAT_COMMANDS: readonly [readonly ['help', '[command]', 'Show commands and examples'], readonly ['ask', '<question>', 'Ask about this repository'], readonly ['review', '[PR number] [task]', 'Review local changes or a GitHub PR'], readonly ['triage', '[issue number] [task]', 'Investigate issues and suggest fixes'], readonly ['implement', '<task>', 'Make changes in an isolated Git workspace'], readonly ['pr', '<task>', 'Prepare a draft PR; publish when GitHub writes are enabled'], readonly ['github', '<task>', 'Work with GitHub issues, reviews, labels, and CI'], readonly ['mode', '[ask|review|triage|implement|pr|github]', 'Choose the workflow for following messages'], readonly ['permissions', '[checks|github] [on|off]', 'Control test execution and GitHub writes'], readonly ['model', '[name] [provider]', 'Open the provider/model picker; configure opens full Hermes setup'], readonly ['limits', '[seconds] [turns]', 'Set the run timeout and tool-turn budget'], readonly ['repo', '[folder|GitHub URL]', 'Show or switch the repository; clone GitHub URLs'], readonly ['clone', '<GitHub URL>', 'Clone a repository into a saved local checkout'], readonly ['pull', '', 'Update the saved checkout without overwriting local changes'], readonly ['new', '', 'Start a fresh conversation'], readonly ['history', '', 'List saved conversations for this repository'], readonly ['resume', '<conversation ID>', 'Continue a saved conversation'], readonly ['status', '', 'Show model, permissions, workspace, and last run'], readonly ['paste', '', 'Enter a multiline message; finish with /send or /discard'], readonly ['cancel', '', 'Stop the active task (Ctrl-C also works)'], readonly ['map', '', 'Repository overview, available offline'], readonly ['tree', '[folder] [depth]', 'Browse repository files'], readonly ['find', '<query>', 'Search files, symbols, and contents'], readonly ['show', '<file> [from] [count]', 'Read a file'], readonly ['diff', '[base] [head] [file]', 'Inspect changes in the current workspace'], readonly ['health', '', 'Inspect repository health'], readonly ['rescan', '', 'Refresh the cached repository analysis'], readonly ['tour', '', 'Find the best reading order for this codebase'], readonly ['explain', '[file|folder]', 'Explain a module using local analysis'], readonly ['deps', '<file>', 'Trace imports and reverse dependencies'], readonly ['inspect', '<file>', 'Inspect role, complexity, exports, and findings'], readonly ['graph', '<file> [depth]', 'Explore the dependency graph'], readonly ['blast', '<file>', 'Show the impact of changing a file'], readonly ['symbols', '<file>', 'List functions, classes, and exports'], readonly ['hubs', '', 'Find the most depended-on modules'], readonly ['layers', '', 'Inspect architecture layers'], readonly ['patterns', '', 'Discover architecture patterns'], readonly ['stats', '', 'Inspect language and complexity statistics'], readonly ['security', '', 'Show local security findings'], readonly ['stack', '', 'Inspect frameworks and dependencies'], readonly ['entry', '', 'Locate application entry points'], readonly ['externals', '', 'Inspect external dependencies and drift'], readonly ['coupling', '', 'Explore traffic between folders'], readonly ['clusters', '', 'Find groups of related modules'], readonly ['risks', '', 'Inspect code and architecture risks'], readonly ['log', '[count]', 'Read recent Git commits'], readonly ['hotspots', '[count]', 'Find frequently changed complex files'], readonly ['blame', '<file>', 'Read line authorship'], readonly ['diagram', '[file|folder]', 'Generate Mermaid diagram source'], readonly ['layers-diagram', '', 'Generate an architecture layer diagram'], readonly ['atlas', '', 'Browse available diagrams'], readonly ['docs', '[file|folder] [--write]', 'Read or explicitly generate repository documentation'], readonly ['workflows', '', 'Inspect GitHub Actions workflows'], readonly ['sbom', '[package]', 'Inspect dependencies and licenses'], readonly ['engines', '', 'Show available optional analyzers'], readonly ['deep', '<engine|all>', 'Run an optional analyzer when checks are enabled'], readonly ['web', '', 'Open the repository in the web interface'], readonly ['about', '', 'Show repository and application information'], readonly ['tools', '', 'List the actual tools available to Hermes'], readonly ['skills', '', 'Discover the six built-in agent workflows'], readonly ['runs', '', 'List saved agent runs'], readonly ['run', '<run ID>', 'Read a saved result from this repository'], readonly ['export', '', 'Save this conversation as Markdown'], readonly ['send', '', 'Submit the message composed with /paste'], readonly ['discard', '', 'Discard the message composed with /paste'], readonly ['doctor', '', 'Check the Hermes runtime and profile'], readonly ['setup', '', 'Prepare the dedicated Hermes profile'], readonly ['clear', '', 'Clear the screen; keep the conversation'], readonly ['exit', '', 'Save and exit (Ctrl-D also works)']];
|
|
2
|
+
export type ChatCommand = typeof CHAT_COMMANDS[number][0];
|
|
3
|
+
export declare const BROWSE_COMMANDS: readonly ['map', 'tree', 'find', 'show', 'diff', 'health', 'rescan', 'tour', 'explain', 'deps', 'inspect', 'graph', 'blast', 'symbols', 'hubs', 'layers', 'patterns', 'stats', 'security', 'stack', 'entry', 'externals', 'coupling', 'clusters', 'risks', 'log', 'hotspots', 'blame', 'diagram', 'layers-diagram', 'atlas', 'docs', 'workflows', 'sbom', 'engines', 'deep', 'web', 'about'];
|
|
4
|
+
export declare function suggestCommands(line: string): typeof CHAT_COMMANDS[number][];
|
|
5
|
+
export declare function slashInput(line: string): {
|
|
6
|
+
name: string;
|
|
7
|
+
args: string[];
|
|
8
|
+
body: string;
|
|
9
|
+
} | null;
|
|
10
|
+
export declare function chatHelp(topic?: string): string;
|
|
11
|
+
export declare function completeChat(line: string, files?: string[]): [string[], string];
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { AGENT_MODES } from '../agent/contracts.js';
|
|
2
|
+
export const CHAT_COMMANDS = [
|
|
3
|
+
['help', '[command]', 'Show commands and examples'],
|
|
4
|
+
['ask', '<question>', 'Ask about this repository'],
|
|
5
|
+
['review', '[PR number] [task]', 'Review local changes or a GitHub PR'],
|
|
6
|
+
['triage', '[issue number] [task]', 'Investigate issues and suggest fixes'],
|
|
7
|
+
['implement', '<task>', 'Make changes in an isolated Git workspace'],
|
|
8
|
+
['pr', '<task>', 'Prepare a draft PR; publish when GitHub writes are enabled'],
|
|
9
|
+
['github', '<task>', 'Work with GitHub issues, reviews, labels, and CI'],
|
|
10
|
+
['mode', '[ask|review|triage|implement|pr|github]', 'Choose the workflow for following messages'],
|
|
11
|
+
['permissions', '[checks|github] [on|off]', 'Control test execution and GitHub writes'],
|
|
12
|
+
['model', '[name] [provider]', 'Open the provider/model picker; configure opens full Hermes setup'],
|
|
13
|
+
['limits', '[seconds] [turns]', 'Set the run timeout and tool-turn budget'],
|
|
14
|
+
['repo', '[folder|GitHub URL]', 'Show or switch the repository; clone GitHub URLs'],
|
|
15
|
+
['clone', '<GitHub URL>', 'Clone a repository into a saved local checkout'],
|
|
16
|
+
['pull', '', 'Update the saved checkout without overwriting local changes'],
|
|
17
|
+
['new', '', 'Start a fresh conversation'],
|
|
18
|
+
['history', '', 'List saved conversations for this repository'],
|
|
19
|
+
['resume', '<conversation ID>', 'Continue a saved conversation'],
|
|
20
|
+
['status', '', 'Show model, permissions, workspace, and last run'],
|
|
21
|
+
['paste', '', 'Enter a multiline message; finish with /send or /discard'],
|
|
22
|
+
['cancel', '', 'Stop the active task (Ctrl-C also works)'],
|
|
23
|
+
['map', '', 'Repository overview, available offline'],
|
|
24
|
+
['tree', '[folder] [depth]', 'Browse repository files'],
|
|
25
|
+
['find', '<query>', 'Search files, symbols, and contents'],
|
|
26
|
+
['show', '<file> [from] [count]', 'Read a file'],
|
|
27
|
+
['diff', '[base] [head] [file]', 'Inspect changes in the current workspace'],
|
|
28
|
+
['health', '', 'Inspect repository health'],
|
|
29
|
+
['rescan', '', 'Refresh the cached repository analysis'],
|
|
30
|
+
['tour', '', 'Find the best reading order for this codebase'],
|
|
31
|
+
['explain', '[file|folder]', 'Explain a module using local analysis'],
|
|
32
|
+
['deps', '<file>', 'Trace imports and reverse dependencies'],
|
|
33
|
+
['inspect', '<file>', 'Inspect role, complexity, exports, and findings'],
|
|
34
|
+
['graph', '<file> [depth]', 'Explore the dependency graph'],
|
|
35
|
+
['blast', '<file>', 'Show the impact of changing a file'],
|
|
36
|
+
['symbols', '<file>', 'List functions, classes, and exports'],
|
|
37
|
+
['hubs', '', 'Find the most depended-on modules'],
|
|
38
|
+
['layers', '', 'Inspect architecture layers'],
|
|
39
|
+
['patterns', '', 'Discover architecture patterns'],
|
|
40
|
+
['stats', '', 'Inspect language and complexity statistics'],
|
|
41
|
+
['security', '', 'Show local security findings'],
|
|
42
|
+
['stack', '', 'Inspect frameworks and dependencies'],
|
|
43
|
+
['entry', '', 'Locate application entry points'],
|
|
44
|
+
['externals', '', 'Inspect external dependencies and drift'],
|
|
45
|
+
['coupling', '', 'Explore traffic between folders'],
|
|
46
|
+
['clusters', '', 'Find groups of related modules'],
|
|
47
|
+
['risks', '', 'Inspect code and architecture risks'],
|
|
48
|
+
['log', '[count]', 'Read recent Git commits'],
|
|
49
|
+
['hotspots', '[count]', 'Find frequently changed complex files'],
|
|
50
|
+
['blame', '<file>', 'Read line authorship'],
|
|
51
|
+
['diagram', '[file|folder]', 'Generate Mermaid diagram source'],
|
|
52
|
+
['layers-diagram', '', 'Generate an architecture layer diagram'],
|
|
53
|
+
['atlas', '', 'Browse available diagrams'],
|
|
54
|
+
['docs', '[file|folder] [--write]', 'Read or explicitly generate repository documentation'],
|
|
55
|
+
['workflows', '', 'Inspect GitHub Actions workflows'],
|
|
56
|
+
['sbom', '[package]', 'Inspect dependencies and licenses'],
|
|
57
|
+
['engines', '', 'Show available optional analyzers'],
|
|
58
|
+
['deep', '<engine|all>', 'Run an optional analyzer when checks are enabled'],
|
|
59
|
+
['web', '', 'Open the repository in the web interface'],
|
|
60
|
+
['about', '', 'Show repository and application information'],
|
|
61
|
+
['tools', '', 'List the actual tools available to Hermes'],
|
|
62
|
+
['skills', '', 'Discover the six built-in agent workflows'],
|
|
63
|
+
['runs', '', 'List saved agent runs'],
|
|
64
|
+
['run', '<run ID>', 'Read a saved result from this repository'],
|
|
65
|
+
['export', '', 'Save this conversation as Markdown'],
|
|
66
|
+
['send', '', 'Submit the message composed with /paste'],
|
|
67
|
+
['discard', '', 'Discard the message composed with /paste'],
|
|
68
|
+
['doctor', '', 'Check the Hermes runtime and profile'],
|
|
69
|
+
['setup', '', 'Prepare the dedicated Hermes profile'],
|
|
70
|
+
['clear', '', 'Clear the screen; keep the conversation'],
|
|
71
|
+
['exit', '', 'Save and exit (Ctrl-D also works)'],
|
|
72
|
+
];
|
|
73
|
+
export const BROWSE_COMMANDS = ['map', 'tree', 'find', 'show', 'diff', 'health', 'rescan', 'tour', 'explain', 'deps', 'inspect', 'graph', 'blast', 'symbols', 'hubs', 'layers', 'patterns', 'stats', 'security', 'stack', 'entry', 'externals', 'coupling', 'clusters', 'risks', 'log', 'hotspots', 'blame', 'diagram', 'layers-diagram', 'atlas', 'docs', 'workflows', 'sbom', 'engines', 'deep', 'web', 'about'];
|
|
74
|
+
export function suggestCommands(line) {
|
|
75
|
+
if (!/^\/[\w-]*$/.test(line))
|
|
76
|
+
return [];
|
|
77
|
+
const query = line.slice(1).toLowerCase();
|
|
78
|
+
return CHAT_COMMANDS.filter(([name, , summary]) => name.startsWith(query) || query.length > 1 && summary.toLowerCase().includes(query))
|
|
79
|
+
.sort((a, b) => Number(b[0].startsWith(query)) - Number(a[0].startsWith(query)));
|
|
80
|
+
}
|
|
81
|
+
export function slashInput(line) {
|
|
82
|
+
const match = /^\/([\w-]*)(?:\s+([\s\S]*))?$/.exec(line.trim());
|
|
83
|
+
if (!match)
|
|
84
|
+
return null;
|
|
85
|
+
const body = match[2] || '';
|
|
86
|
+
// Quotes are useful for file paths. Workflow tasks retain the original body.
|
|
87
|
+
const args = [...body.matchAll(/"([^"]*)"|'([^']*)'|(\S+)/g)].map(m => m[1] ?? m[2] ?? m[3]);
|
|
88
|
+
return { name: match[1], args, body };
|
|
89
|
+
}
|
|
90
|
+
export function chatHelp(topic = '') {
|
|
91
|
+
const commands = topic ? CHAT_COMMANDS.filter(c => c[0] === topic.replace(/^\//, '')) : CHAT_COMMANDS;
|
|
92
|
+
if (!commands.length)
|
|
93
|
+
return `Unknown command /${topic}. Use /help.`;
|
|
94
|
+
return ['Slash commands', '', ...commands.map(([name, args, detail]) => ` /${name}${args ? ' ' + args : ''}\n ${detail}`), '',
|
|
95
|
+
'Type / for live suggestions. ↑/↓ select; Tab or Enter fills the command. Esc dismisses.',
|
|
96
|
+
'Alt-Enter adds a line. Bracketed paste preserves multiline text. Ctrl-C cancels a task.',
|
|
97
|
+
'/review 42 /triage 12 /implement Fix the failing test',
|
|
98
|
+
'/permissions checks on /permissions github on',
|
|
99
|
+
'Paste a GitHub URL to clone and open it, then type your question.',
|
|
100
|
+
'Follow-ups reuse the Hermes session. Mode, target, model, or permission changes start a new session.',
|
|
101
|
+
'Use /paste for multiline text. /new resets context; /clear only clears the screen.'].join('\n');
|
|
102
|
+
}
|
|
103
|
+
export function completeChat(line, files = []) {
|
|
104
|
+
if (!line.startsWith('/'))
|
|
105
|
+
return [[], line];
|
|
106
|
+
if (!line.includes(' '))
|
|
107
|
+
return [CHAT_COMMANDS.map(c => '/' + c[0]).filter(c => c.startsWith(line)), line];
|
|
108
|
+
const [command, ...args] = line.split(/\s+/), fragment = args.at(-1) || '';
|
|
109
|
+
let candidates = [];
|
|
110
|
+
if (command === '/mode')
|
|
111
|
+
candidates = AGENT_MODES;
|
|
112
|
+
if (command === '/permissions')
|
|
113
|
+
candidates = args.length === 1 ? ['checks', 'github'] : ['on', 'off'];
|
|
114
|
+
if (command === '/model' && args.length === 1)
|
|
115
|
+
candidates = ['configure', 'default'];
|
|
116
|
+
if (['/show', '/tree', '/repo', '/deps', '/inspect', '/graph', '/blast', '/symbols', '/explain', '/blame', '/docs', '/diagram'].includes(command))
|
|
117
|
+
candidates = files;
|
|
118
|
+
return [candidates.filter(c => c.startsWith(fragment)).slice(0, 100), fragment];
|
|
119
|
+
}
|
|
120
|
+
//# sourceMappingURL=commands.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"commands.js","sourceRoot":"","sources":["../../src/cli/chat/commands.ts"],"names":[],"mappings":"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","sourcesContent":["import { AGENT_MODES } from '../agent/contracts.js';\n\nexport const CHAT_COMMANDS = [\n ['help', '[command]', 'Show commands and examples'],\n ['ask', '<question>', 'Ask about this repository'],\n ['review', '[PR number] [task]', 'Review local changes or a GitHub PR'],\n ['triage', '[issue number] [task]', 'Investigate issues and suggest fixes'],\n ['implement', '<task>', 'Make changes in an isolated Git workspace'],\n ['pr', '<task>', 'Prepare a draft PR; publish when GitHub writes are enabled'],\n ['github', '<task>', 'Work with GitHub issues, reviews, labels, and CI'],\n ['mode', '[ask|review|triage|implement|pr|github]', 'Choose the workflow for following messages'],\n ['permissions', '[checks|github] [on|off]', 'Control test execution and GitHub writes'],\n ['model', '[name] [provider]', 'Open the provider/model picker; configure opens full Hermes setup'],\n ['limits', '[seconds] [turns]', 'Set the run timeout and tool-turn budget'],\n ['repo', '[folder|GitHub URL]', 'Show or switch the repository; clone GitHub URLs'],\n ['clone', '<GitHub URL>', 'Clone a repository into a saved local checkout'],\n ['pull', '', 'Update the saved checkout without overwriting local changes'],\n ['new', '', 'Start a fresh conversation'],\n ['history', '', 'List saved conversations for this repository'],\n ['resume', '<conversation ID>', 'Continue a saved conversation'],\n ['status', '', 'Show model, permissions, workspace, and last run'],\n ['paste', '', 'Enter a multiline message; finish with /send or /discard'],\n ['cancel', '', 'Stop the active task (Ctrl-C also works)'],\n ['map', '', 'Repository overview, available offline'],\n ['tree', '[folder] [depth]', 'Browse repository files'],\n ['find', '<query>', 'Search files, symbols, and contents'],\n ['show', '<file> [from] [count]', 'Read a file'],\n ['diff', '[base] [head] [file]', 'Inspect changes in the current workspace'],\n ['health', '', 'Inspect repository health'],\n ['rescan', '', 'Refresh the cached repository analysis'],\n ['tour', '', 'Find the best reading order for this codebase'],\n ['explain', '[file|folder]', 'Explain a module using local analysis'],\n ['deps', '<file>', 'Trace imports and reverse dependencies'],\n ['inspect', '<file>', 'Inspect role, complexity, exports, and findings'],\n ['graph', '<file> [depth]', 'Explore the dependency graph'],\n ['blast', '<file>', 'Show the impact of changing a file'],\n ['symbols', '<file>', 'List functions, classes, and exports'],\n ['hubs', '', 'Find the most depended-on modules'],\n ['layers', '', 'Inspect architecture layers'],\n ['patterns', '', 'Discover architecture patterns'],\n ['stats', '', 'Inspect language and complexity statistics'],\n ['security', '', 'Show local security findings'],\n ['stack', '', 'Inspect frameworks and dependencies'],\n ['entry', '', 'Locate application entry points'],\n ['externals', '', 'Inspect external dependencies and drift'],\n ['coupling', '', 'Explore traffic between folders'],\n ['clusters', '', 'Find groups of related modules'],\n ['risks', '', 'Inspect code and architecture risks'],\n ['log', '[count]', 'Read recent Git commits'],\n ['hotspots', '[count]', 'Find frequently changed complex files'],\n ['blame', '<file>', 'Read line authorship'],\n ['diagram', '[file|folder]', 'Generate Mermaid diagram source'],\n ['layers-diagram', '', 'Generate an architecture layer diagram'],\n ['atlas', '', 'Browse available diagrams'],\n ['docs', '[file|folder] [--write]', 'Read or explicitly generate repository documentation'],\n ['workflows', '', 'Inspect GitHub Actions workflows'],\n ['sbom', '[package]', 'Inspect dependencies and licenses'],\n ['engines', '', 'Show available optional analyzers'],\n ['deep', '<engine|all>', 'Run an optional analyzer when checks are enabled'],\n ['web', '', 'Open the repository in the web interface'],\n ['about', '', 'Show repository and application information'],\n ['tools', '', 'List the actual tools available to Hermes'],\n ['skills', '', 'Discover the six built-in agent workflows'],\n ['runs', '', 'List saved agent runs'],\n ['run', '<run ID>', 'Read a saved result from this repository'],\n ['export', '', 'Save this conversation as Markdown'],\n ['send', '', 'Submit the message composed with /paste'],\n ['discard', '', 'Discard the message composed with /paste'],\n ['doctor', '', 'Check the Hermes runtime and profile'],\n ['setup', '', 'Prepare the dedicated Hermes profile'],\n ['clear', '', 'Clear the screen; keep the conversation'],\n ['exit', '', 'Save and exit (Ctrl-D also works)'],\n] as const;\nexport type ChatCommand = typeof CHAT_COMMANDS[number][0];\nexport const BROWSE_COMMANDS = ['map', 'tree', 'find', 'show', 'diff', 'health', 'rescan', 'tour', 'explain', 'deps', 'inspect', 'graph', 'blast', 'symbols', 'hubs', 'layers', 'patterns', 'stats', 'security', 'stack', 'entry', 'externals', 'coupling', 'clusters', 'risks', 'log', 'hotspots', 'blame', 'diagram', 'layers-diagram', 'atlas', 'docs', 'workflows', 'sbom', 'engines', 'deep', 'web', 'about'] as const;\n\nexport function suggestCommands(line: string): typeof CHAT_COMMANDS[number][] {\n if (!/^\\/[\\w-]*$/.test(line)) return [];\n const query = line.slice(1).toLowerCase();\n return CHAT_COMMANDS.filter(([name, , summary]) => name.startsWith(query) || query.length > 1 && summary.toLowerCase().includes(query))\n .sort((a, b) => Number(b[0].startsWith(query)) - Number(a[0].startsWith(query)));\n}\n\nexport function slashInput(line: string): { name: string; args: string[]; body: string } | null {\n const match = /^\\/([\\w-]*)(?:\\s+([\\s\\S]*))?$/.exec(line.trim());\n if (!match) return null;\n const body = match[2] || '';\n // Quotes are useful for file paths. Workflow tasks retain the original body.\n const args = [...body.matchAll(/\"([^\"]*)\"|'([^']*)'|(\\S+)/g)].map(m => m[1] ?? m[2] ?? m[3]!);\n return { name: match[1]!, args, body };\n}\nexport function chatHelp(topic = ''): string {\n const commands = topic ? CHAT_COMMANDS.filter(c => c[0] === topic.replace(/^\\//, '')) : CHAT_COMMANDS;\n if (!commands.length) return `Unknown command /${topic}. Use /help.`;\n return ['Slash commands', '', ...commands.map(([name, args, detail]) => ` /${name}${args ? ' ' + args : ''}\\n ${detail}`), '',\n 'Type / for live suggestions. ↑/↓ select; Tab or Enter fills the command. Esc dismisses.',\n 'Alt-Enter adds a line. Bracketed paste preserves multiline text. Ctrl-C cancels a task.',\n '/review 42 /triage 12 /implement Fix the failing test',\n '/permissions checks on /permissions github on',\n 'Paste a GitHub URL to clone and open it, then type your question.',\n 'Follow-ups reuse the Hermes session. Mode, target, model, or permission changes start a new session.',\n 'Use /paste for multiline text. /new resets context; /clear only clears the screen.'].join('\\n');\n}\nexport function completeChat(line: string, files: string[] = []): [string[], string] {\n if (!line.startsWith('/')) return [[], line];\n if (!line.includes(' ')) return [CHAT_COMMANDS.map(c => '/' + c[0]).filter(c => c.startsWith(line)), line];\n const [command, ...args] = line.split(/\\s+/), fragment = args.at(-1) || '';\n let candidates: readonly string[] = [];\n if (command === '/mode') candidates = AGENT_MODES;\n if (command === '/permissions') candidates = args.length === 1 ? ['checks', 'github'] : ['on', 'off'];\n if (command === '/model' && args.length === 1) candidates = ['configure', 'default'];\n if (['/show', '/tree', '/repo', '/deps', '/inspect', '/graph', '/blast', '/symbols', '/explain', '/blame', '/docs', '/diagram'].includes(command!)) candidates = files;\n return [candidates.filter(c => c.startsWith(fragment)).slice(0, 100), fragment];\n}\n"]}
|