overarch-linux-arm64 0.8.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/LICENSE +21 -0
- package/README.md +214 -0
- package/dist/bin/overarch.d.ts +2 -0
- package/dist/bin/overarch.js +178 -0
- package/dist/src/agent/context.d.ts +14 -0
- package/dist/src/agent/context.js +200 -0
- package/dist/src/agent/context_compressor.d.ts +21 -0
- package/dist/src/agent/context_compressor.js +90 -0
- package/dist/src/agent/manifest.d.ts +22 -0
- package/dist/src/agent/manifest.js +131 -0
- package/dist/src/agent/planner.d.ts +38 -0
- package/dist/src/agent/planner.js +118 -0
- package/dist/src/agent/runtime.d.ts +59 -0
- package/dist/src/agent/runtime.js +400 -0
- package/dist/src/cli/approval.d.ts +13 -0
- package/dist/src/cli/approval.js +71 -0
- package/dist/src/cli/args.d.ts +15 -0
- package/dist/src/cli/args.js +47 -0
- package/dist/src/cli/commands/audit.d.ts +10 -0
- package/dist/src/cli/commands/audit.js +79 -0
- package/dist/src/cli/commands/companion.d.ts +1 -0
- package/dist/src/cli/commands/companion.js +143 -0
- package/dist/src/cli/commands/config.d.ts +1 -0
- package/dist/src/cli/commands/config.js +135 -0
- package/dist/src/cli/commands/doctor.d.ts +1 -0
- package/dist/src/cli/commands/doctor.js +217 -0
- package/dist/src/cli/commands/init.d.ts +1 -0
- package/dist/src/cli/commands/init.js +134 -0
- package/dist/src/cli/commands/memory.d.ts +2 -0
- package/dist/src/cli/commands/memory.js +98 -0
- package/dist/src/cli/commands/onboarding.d.ts +3 -0
- package/dist/src/cli/commands/onboarding.js +223 -0
- package/dist/src/cli/commands/sessions.d.ts +1 -0
- package/dist/src/cli/commands/sessions.js +22 -0
- package/dist/src/cli/commands/status.d.ts +1 -0
- package/dist/src/cli/commands/status.js +37 -0
- package/dist/src/cli/commands/update.d.ts +9 -0
- package/dist/src/cli/commands/update.js +105 -0
- package/dist/src/cli/repl.d.ts +3 -0
- package/dist/src/cli/repl.js +821 -0
- package/dist/src/cli/ui.d.ts +53 -0
- package/dist/src/cli/ui.js +215 -0
- package/dist/src/companion/petdex_adapter.d.ts +31 -0
- package/dist/src/companion/petdex_adapter.js +171 -0
- package/dist/src/companion/petdex_bridge.d.ts +65 -0
- package/dist/src/companion/petdex_bridge.js +356 -0
- package/dist/src/companion/types.d.ts +57 -0
- package/dist/src/companion/types.js +11 -0
- package/dist/src/config/index.d.ts +24 -0
- package/dist/src/config/index.js +237 -0
- package/dist/src/config/types.d.ts +58 -0
- package/dist/src/config/types.js +7 -0
- package/dist/src/core/diagnostics.d.ts +48 -0
- package/dist/src/core/diagnostics.js +115 -0
- package/dist/src/core/index.d.ts +23 -0
- package/dist/src/core/index.js +23 -0
- package/dist/src/core/mission.d.ts +55 -0
- package/dist/src/core/mission.js +173 -0
- package/dist/src/index.d.ts +1 -0
- package/dist/src/index.js +1 -0
- package/dist/src/learning/memory_manager.d.ts +26 -0
- package/dist/src/learning/memory_manager.js +211 -0
- package/dist/src/learning/types.d.ts +54 -0
- package/dist/src/learning/types.js +1 -0
- package/dist/src/mcp/registry.d.ts +17 -0
- package/dist/src/mcp/registry.js +97 -0
- package/dist/src/mcp/types.d.ts +13 -0
- package/dist/src/mcp/types.js +1 -0
- package/dist/src/providers/anthropic.d.ts +10 -0
- package/dist/src/providers/anthropic.js +95 -0
- package/dist/src/providers/base.d.ts +15 -0
- package/dist/src/providers/base.js +108 -0
- package/dist/src/providers/factory.d.ts +28 -0
- package/dist/src/providers/factory.js +142 -0
- package/dist/src/providers/gemini.d.ts +10 -0
- package/dist/src/providers/gemini.js +97 -0
- package/dist/src/providers/openai.d.ts +9 -0
- package/dist/src/providers/openai.js +85 -0
- package/dist/src/providers/openrouter.d.ts +5 -0
- package/dist/src/providers/openrouter.js +14 -0
- package/dist/src/providers/registry.d.ts +19 -0
- package/dist/src/providers/registry.js +209 -0
- package/dist/src/providers/types.d.ts +59 -0
- package/dist/src/providers/types.js +1 -0
- package/dist/src/providers/validator.d.ts +24 -0
- package/dist/src/providers/validator.js +161 -0
- package/dist/src/security/audit.d.ts +56 -0
- package/dist/src/security/audit.js +154 -0
- package/dist/src/security/guardrails.d.ts +9 -0
- package/dist/src/security/guardrails.js +32 -0
- package/dist/src/security/permissions.d.ts +12 -0
- package/dist/src/security/permissions.js +68 -0
- package/dist/src/security/policy.d.ts +220 -0
- package/dist/src/security/policy.js +720 -0
- package/dist/src/session/manager.d.ts +22 -0
- package/dist/src/session/manager.js +172 -0
- package/dist/src/session/types.d.ts +36 -0
- package/dist/src/session/types.js +1 -0
- package/dist/src/tools/checkpoint.d.ts +26 -0
- package/dist/src/tools/checkpoint.js +160 -0
- package/dist/src/tools/diagnostics.d.ts +2 -0
- package/dist/src/tools/diagnostics.js +125 -0
- package/dist/src/tools/diff.d.ts +14 -0
- package/dist/src/tools/diff.js +124 -0
- package/dist/src/tools/filesystem.d.ts +2 -0
- package/dist/src/tools/filesystem.js +224 -0
- package/dist/src/tools/git.d.ts +2 -0
- package/dist/src/tools/git.js +138 -0
- package/dist/src/tools/markdown.d.ts +33 -0
- package/dist/src/tools/markdown.js +190 -0
- package/dist/src/tools/project.d.ts +2 -0
- package/dist/src/tools/project.js +63 -0
- package/dist/src/tools/registry.d.ts +14 -0
- package/dist/src/tools/registry.js +230 -0
- package/dist/src/tools/search.d.ts +2 -0
- package/dist/src/tools/search.js +136 -0
- package/dist/src/tools/shell.d.ts +2 -0
- package/dist/src/tools/shell.js +74 -0
- package/dist/src/tools/symbols.d.ts +30 -0
- package/dist/src/tools/symbols.js +155 -0
- package/dist/src/tools/types.d.ts +28 -0
- package/dist/src/tools/types.js +1 -0
- package/dist/src/tools/web_search.d.ts +14 -0
- package/dist/src/tools/web_search.js +154 -0
- package/install.sh +29 -0
- package/overarch +15 -0
- package/package.json +28 -0
|
@@ -0,0 +1,821 @@
|
|
|
1
|
+
import * as readline from 'node:readline/promises';
|
|
2
|
+
import { stdin, stdout } from 'node:process';
|
|
3
|
+
import * as path from 'node:path';
|
|
4
|
+
import * as fs from 'node:fs/promises';
|
|
5
|
+
import { styleText, promisify } from 'node:util';
|
|
6
|
+
import { exec } from 'node:child_process';
|
|
7
|
+
import { SessionManager } from '../session/manager.js';
|
|
8
|
+
import { AgentRuntime } from '../agent/runtime.js';
|
|
9
|
+
import { buildContext, inspectProjectContext } from '../agent/context.js';
|
|
10
|
+
import { ToolRegistry } from '../tools/registry.js';
|
|
11
|
+
import { resetAbort } from '../security/guardrails.js';
|
|
12
|
+
import { createProvider, resolveModel, listModels, OfflineProvider } from '../providers/factory.js';
|
|
13
|
+
import { runSetup } from './commands/onboarding.js';
|
|
14
|
+
import { doctor } from './commands/doctor.js';
|
|
15
|
+
import { init } from './commands/init.js';
|
|
16
|
+
import { status } from './commands/status.js';
|
|
17
|
+
import { handleConfigCommand } from './commands/config.js';
|
|
18
|
+
import { handleMemoryCommand, handleLearnCommand } from './commands/memory.js';
|
|
19
|
+
import { handleCompanionCommand } from './commands/companion.js';
|
|
20
|
+
import { loadConfig, saveGlobalConfig, autoEnsureOverarch, getApiKey } from '../config/index.js';
|
|
21
|
+
import { petdexBridge } from '../companion/petdex_bridge.js';
|
|
22
|
+
import { parseCcssCommand, CCSS_MODE_CONFIGS } from '../agent/planner.js';
|
|
23
|
+
import { CheckpointManager } from '../tools/checkpoint.js';
|
|
24
|
+
import * as ui from './ui.js';
|
|
25
|
+
const execAsync = promisify(exec);
|
|
26
|
+
function resolveRuntimeProvider(config, modelOverride) {
|
|
27
|
+
if (config.mode === 'offline') {
|
|
28
|
+
return {
|
|
29
|
+
provider: new OfflineProvider(),
|
|
30
|
+
model: 'offline',
|
|
31
|
+
displayName: 'Offline Mode (AI disabled)',
|
|
32
|
+
isOffline: true
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
const modelId = modelOverride || config.defaultModel;
|
|
36
|
+
let resolved = modelId ? resolveModel(modelId) : undefined;
|
|
37
|
+
// 1. If an explicit model is requested or configured, try initializing it
|
|
38
|
+
if (resolved && resolved.provider !== 'none') {
|
|
39
|
+
const hasKey = config.providers[resolved.provider]?.apiKey || getApiKey(resolved.provider);
|
|
40
|
+
if (hasKey) {
|
|
41
|
+
try {
|
|
42
|
+
const providerConfig = config.providers[resolved.provider] || {};
|
|
43
|
+
const provider = createProvider(resolved.provider, providerConfig);
|
|
44
|
+
return { provider, model: resolved.model, displayName: resolved.displayName, isOffline: false };
|
|
45
|
+
}
|
|
46
|
+
catch (err) {
|
|
47
|
+
ui.warn(`Failed to initialize provider '${resolved.provider}': ${err.message}`);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
// 2. Fallback: Automatically check environment for any available provider key!
|
|
52
|
+
// Priority: OpenRouter (multi-provider), Anthropic, OpenAI, Gemini
|
|
53
|
+
const providerCandidates = [
|
|
54
|
+
{ id: 'openrouter', defaultModel: 'openrouter/auto', name: 'OpenRouter' },
|
|
55
|
+
{ id: 'anthropic', defaultModel: 'claude-3-5-sonnet-20241022', name: 'Anthropic' },
|
|
56
|
+
{ id: 'openai', defaultModel: 'gpt-4o', name: 'OpenAI' },
|
|
57
|
+
{ id: 'gemini', defaultModel: 'gemini-2.5-flash', name: 'Google Gemini' }
|
|
58
|
+
];
|
|
59
|
+
for (const candidate of providerCandidates) {
|
|
60
|
+
const key = config.providers[candidate.id]?.apiKey || getApiKey(candidate.id);
|
|
61
|
+
if (key) {
|
|
62
|
+
try {
|
|
63
|
+
const provider = createProvider(candidate.id, config.providers[candidate.id] || {});
|
|
64
|
+
const autoResolved = resolveModel(candidate.defaultModel, candidate.id) || {
|
|
65
|
+
provider: candidate.id,
|
|
66
|
+
model: candidate.defaultModel,
|
|
67
|
+
displayName: `${candidate.name} (${candidate.defaultModel})`
|
|
68
|
+
};
|
|
69
|
+
config.defaultModel = autoResolved.model;
|
|
70
|
+
if (modelId && resolved && resolved.provider !== candidate.id) {
|
|
71
|
+
ui.info(`Provider '${resolved.provider}' key not detected. Automatically using active ${candidate.name} key.`);
|
|
72
|
+
}
|
|
73
|
+
return {
|
|
74
|
+
provider,
|
|
75
|
+
model: autoResolved.model,
|
|
76
|
+
displayName: autoResolved.displayName,
|
|
77
|
+
isOffline: false
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
catch { }
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
// 3. Clean offline fallback when no provider keys exist
|
|
84
|
+
return {
|
|
85
|
+
provider: new OfflineProvider(),
|
|
86
|
+
model: 'offline',
|
|
87
|
+
displayName: 'Offline Mode (No API key found — run /setup)',
|
|
88
|
+
isOffline: true
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
export async function startRepl(initialConfig, args) {
|
|
92
|
+
let config = initialConfig;
|
|
93
|
+
// Apply UI accent and avatar from config
|
|
94
|
+
if (config.ui?.accentColor) {
|
|
95
|
+
ui.setAccent(config.ui.accentColor);
|
|
96
|
+
}
|
|
97
|
+
if (config.ui?.avatar) {
|
|
98
|
+
ui.setAvatar(config.ui.avatar);
|
|
99
|
+
}
|
|
100
|
+
let cwd = process.cwd();
|
|
101
|
+
await autoEnsureOverarch(cwd);
|
|
102
|
+
// Initialize optional Petdex companion bridge (non-blocking)
|
|
103
|
+
await petdexBridge.start().catch(() => { });
|
|
104
|
+
let checkpointManager = new CheckpointManager(cwd);
|
|
105
|
+
let sessionManager = new SessionManager(undefined, cwd);
|
|
106
|
+
let providerResult = resolveRuntimeProvider(config, args.model);
|
|
107
|
+
// Session resolution: resume by ID prefix or latest
|
|
108
|
+
let session;
|
|
109
|
+
if (args.resume) {
|
|
110
|
+
if (args.prompt && /^[0-9a-f-]{4,36}$/i.test(args.prompt.trim())) {
|
|
111
|
+
session = await sessionManager.findSession(args.prompt.trim());
|
|
112
|
+
if (session) {
|
|
113
|
+
args.prompt = undefined;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
if (!session) {
|
|
117
|
+
session = await sessionManager.latest();
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
if (!session) {
|
|
121
|
+
session = await sessionManager.create(cwd, providerResult.model);
|
|
122
|
+
}
|
|
123
|
+
else {
|
|
124
|
+
ui.info(`Resumed session ${ui.bold(session.id.slice(0, 8))} (${session.messages.length} messages)`);
|
|
125
|
+
}
|
|
126
|
+
const rl = readline.createInterface({ input: stdin, output: stdout });
|
|
127
|
+
const registry = ToolRegistry.createDefault();
|
|
128
|
+
let agentRuntime = new AgentRuntime(providerResult.provider, registry, {
|
|
129
|
+
...config,
|
|
130
|
+
defaultModel: providerResult.model,
|
|
131
|
+
}, { autoApprove: args.yes || config.agentMode === 'autonomous', rl, cwd });
|
|
132
|
+
if (session && session.messages.length > 0) {
|
|
133
|
+
const pastMessages = session.messages.map(m => ({
|
|
134
|
+
role: m.role,
|
|
135
|
+
content: m.content,
|
|
136
|
+
toolCallId: m.toolCallId,
|
|
137
|
+
toolCalls: m.toolCalls
|
|
138
|
+
}));
|
|
139
|
+
agentRuntime.setMessages(pastMessages);
|
|
140
|
+
}
|
|
141
|
+
let systemContext = await buildContext(cwd, config);
|
|
142
|
+
// Clean Header
|
|
143
|
+
console.log();
|
|
144
|
+
ui.banner();
|
|
145
|
+
console.log(styleText('dim', '────────────────────────────────────────'));
|
|
146
|
+
console.log(` ${styleText('dim', 'Project:')} ${cwd}`);
|
|
147
|
+
console.log(` ${styleText('dim', 'Model:')} ${providerResult.displayName} ${styleText('dim', `(${providerResult.model})`)}`);
|
|
148
|
+
console.log(` ${styleText('dim', 'Session:')} ${session.id.slice(0, 8)} ${session.messages.length > 0 ? styleText('dim', '(resumed)') : styleText('dim', '(new)')}`);
|
|
149
|
+
console.log(` ${styleText('dim', 'Agent Mode:')} ${config.agentMode} | CCSS: ${config.ccss} | Ponytail: ${config.ponytail}`);
|
|
150
|
+
console.log(styleText('dim', '────────────────────────────────────────'));
|
|
151
|
+
console.log(`${ui.dim('Type')} /help ${ui.dim('for categorized commands, or enter a prompt to begin.\n')}`);
|
|
152
|
+
let isGenerating = false;
|
|
153
|
+
let isExiting = false;
|
|
154
|
+
process.on('SIGINT', async () => {
|
|
155
|
+
if (isGenerating) {
|
|
156
|
+
agentRuntime.abort();
|
|
157
|
+
console.log(`\n${ui.yellow('!')} Operation aborted.`);
|
|
158
|
+
isGenerating = false;
|
|
159
|
+
}
|
|
160
|
+
else {
|
|
161
|
+
if (!isExiting) {
|
|
162
|
+
isExiting = true;
|
|
163
|
+
try {
|
|
164
|
+
await sessionManager.save(session);
|
|
165
|
+
}
|
|
166
|
+
catch { }
|
|
167
|
+
console.log(`\n${ui.accent(ui.getAvatar())} Session saved. Goodbye!`);
|
|
168
|
+
process.exit(0);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
});
|
|
172
|
+
const toolTimers = new Map();
|
|
173
|
+
let thinkingSpin = null;
|
|
174
|
+
const stopThinking = () => {
|
|
175
|
+
if (thinkingSpin) {
|
|
176
|
+
thinkingSpin.stop();
|
|
177
|
+
thinkingSpin = null;
|
|
178
|
+
}
|
|
179
|
+
};
|
|
180
|
+
const callbacks = {
|
|
181
|
+
onText: (chunk) => {
|
|
182
|
+
stopThinking();
|
|
183
|
+
process.stdout.write(chunk);
|
|
184
|
+
},
|
|
185
|
+
onToolStart: (name, params) => {
|
|
186
|
+
stopThinking();
|
|
187
|
+
const summary = Object.entries(params).map(([k, v]) => {
|
|
188
|
+
const val = typeof v === 'string' ? (v.length > 40 ? v.slice(0, 40) + '...' : v) : JSON.stringify(v);
|
|
189
|
+
return `${k}=${val}`;
|
|
190
|
+
}).join(' ');
|
|
191
|
+
toolTimers.set(name, performance.now());
|
|
192
|
+
ui.toolStart(name, summary);
|
|
193
|
+
},
|
|
194
|
+
onToolEnd: (name, result) => {
|
|
195
|
+
const start = toolTimers.get(name);
|
|
196
|
+
const ms = start ? Math.round(performance.now() - start) : 0;
|
|
197
|
+
ui.toolEnd(name, result.success, ms);
|
|
198
|
+
if (result.metadata?.diff) {
|
|
199
|
+
const diffOutput = ui.formatDiff(String(result.metadata.diff));
|
|
200
|
+
if (diffOutput) {
|
|
201
|
+
console.log(diffOutput);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
if (!result.success && result.error) {
|
|
205
|
+
ui.fail(`${name}: ${result.error}`);
|
|
206
|
+
}
|
|
207
|
+
// Resume thinking spinner while LLM processes tool result
|
|
208
|
+
thinkingSpin = ui.thinkingSpinner();
|
|
209
|
+
},
|
|
210
|
+
onPlan: (plan) => {
|
|
211
|
+
stopThinking();
|
|
212
|
+
console.log();
|
|
213
|
+
ui.heading('Task Roadmap');
|
|
214
|
+
plan.steps.forEach((s, i) => {
|
|
215
|
+
const status = s.status === 'done' ? 'done' : s.status === 'failed' ? 'failed' : s.status === 'running' ? 'in-progress' : 'pending';
|
|
216
|
+
ui.planStep(i + 1, s.description, status);
|
|
217
|
+
});
|
|
218
|
+
console.log();
|
|
219
|
+
},
|
|
220
|
+
onError: (error) => {
|
|
221
|
+
stopThinking();
|
|
222
|
+
ui.error(error.message);
|
|
223
|
+
},
|
|
224
|
+
onStateChange: (newState, message) => {
|
|
225
|
+
petdexBridge.broadcastState(newState, message);
|
|
226
|
+
if (newState === 'thinking') {
|
|
227
|
+
if (!thinkingSpin) {
|
|
228
|
+
thinkingSpin = ui.thinkingSpinner(message);
|
|
229
|
+
}
|
|
230
|
+
else if (message) {
|
|
231
|
+
thinkingSpin.setAction(message);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
else {
|
|
235
|
+
stopThinking();
|
|
236
|
+
}
|
|
237
|
+
},
|
|
238
|
+
onReport: (report) => {
|
|
239
|
+
stopThinking();
|
|
240
|
+
if (report.filesModified.length > 0) {
|
|
241
|
+
console.log(`\n${ui.dim('◆')} ${ui.accent('Changes:')} ${report.filesModified.length} file(s) modified ${ui.dim(`(${report.verificationStatus})`)}`);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
};
|
|
245
|
+
const runAgent = async (promptText) => {
|
|
246
|
+
if (providerResult.isOffline) {
|
|
247
|
+
ui.warn('Offline Mode is active. AI completions are disabled.');
|
|
248
|
+
console.log(ui.dim('Run /setup or /config provider to connect an AI provider.'));
|
|
249
|
+
return;
|
|
250
|
+
}
|
|
251
|
+
isGenerating = true;
|
|
252
|
+
resetAbort();
|
|
253
|
+
sessionManager.addMessage(session, {
|
|
254
|
+
role: 'user',
|
|
255
|
+
content: promptText,
|
|
256
|
+
timestamp: new Date().toISOString(),
|
|
257
|
+
});
|
|
258
|
+
console.log(`\n${ui.dim(`─── ${ui.getAvatar()} Overarch ──────────────────────────────`)}`);
|
|
259
|
+
const start = performance.now();
|
|
260
|
+
try {
|
|
261
|
+
thinkingSpin = ui.thinkingSpinner();
|
|
262
|
+
await agentRuntime.run(promptText, systemContext, callbacks);
|
|
263
|
+
}
|
|
264
|
+
catch (err) {
|
|
265
|
+
stopThinking();
|
|
266
|
+
if (err.name !== 'AbortError' && err.message !== 'Aborted') {
|
|
267
|
+
ui.error(`\nProvider Error: ${err.message}`);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
finally {
|
|
271
|
+
stopThinking();
|
|
272
|
+
}
|
|
273
|
+
const elapsed = ((performance.now() - start) / 1000).toFixed(1);
|
|
274
|
+
console.log(`\n${ui.dim(`──────────────────────────────────── (${elapsed}s)`)}\n`);
|
|
275
|
+
// Sync messages
|
|
276
|
+
const currentMessages = agentRuntime.getMessages();
|
|
277
|
+
const newMessages = currentMessages.slice(session.messages.length);
|
|
278
|
+
for (const msg of newMessages) {
|
|
279
|
+
sessionManager.addMessage(session, {
|
|
280
|
+
role: msg.role,
|
|
281
|
+
content: msg.content,
|
|
282
|
+
toolCallId: msg.toolCallId,
|
|
283
|
+
toolCalls: msg.toolCalls,
|
|
284
|
+
timestamp: new Date().toISOString(),
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
await sessionManager.save(session);
|
|
288
|
+
isGenerating = false;
|
|
289
|
+
};
|
|
290
|
+
if (args.prompt) {
|
|
291
|
+
ui.userMessage(args.prompt);
|
|
292
|
+
await runAgent(args.prompt);
|
|
293
|
+
}
|
|
294
|
+
while (!isExiting) {
|
|
295
|
+
let input;
|
|
296
|
+
try {
|
|
297
|
+
input = await rl.question(ui.prompt());
|
|
298
|
+
}
|
|
299
|
+
catch {
|
|
300
|
+
break;
|
|
301
|
+
}
|
|
302
|
+
const command = input.trim();
|
|
303
|
+
if (!command)
|
|
304
|
+
continue;
|
|
305
|
+
// ─────────────────────────────────────────────────────────────
|
|
306
|
+
// 1. SESSION COMMANDS
|
|
307
|
+
// ─────────────────────────────────────────────────────────────
|
|
308
|
+
if (command === '/exit' || command === '/quit') {
|
|
309
|
+
await sessionManager.save(session);
|
|
310
|
+
ui.success('Session saved. Goodbye!');
|
|
311
|
+
isExiting = true;
|
|
312
|
+
break;
|
|
313
|
+
}
|
|
314
|
+
if (command === '/clear') {
|
|
315
|
+
agentRuntime.setMessages([]);
|
|
316
|
+
session.messages = [];
|
|
317
|
+
await sessionManager.save(session);
|
|
318
|
+
ui.info('Conversation history cleared.');
|
|
319
|
+
continue;
|
|
320
|
+
}
|
|
321
|
+
if (command === '/new') {
|
|
322
|
+
await sessionManager.save(session);
|
|
323
|
+
session = await sessionManager.create(cwd, providerResult.model);
|
|
324
|
+
agentRuntime.setMessages([]);
|
|
325
|
+
ui.success(`Created fresh session: ${ui.bold(session.id.slice(0, 8))}`);
|
|
326
|
+
continue;
|
|
327
|
+
}
|
|
328
|
+
if (command.startsWith('/resume')) {
|
|
329
|
+
const targetId = command.replace(/^\/resume\s*/, '').trim();
|
|
330
|
+
const targetSession = targetId ? await sessionManager.findSession(targetId) : await sessionManager.latest();
|
|
331
|
+
if (!targetSession) {
|
|
332
|
+
ui.warn(`No session matching '${targetId || 'latest'}' found.`);
|
|
333
|
+
continue;
|
|
334
|
+
}
|
|
335
|
+
await sessionManager.save(session);
|
|
336
|
+
session = targetSession;
|
|
337
|
+
agentRuntime.setMessages(session.messages.map(m => ({
|
|
338
|
+
role: m.role,
|
|
339
|
+
content: m.content,
|
|
340
|
+
toolCallId: m.toolCallId,
|
|
341
|
+
toolCalls: m.toolCalls
|
|
342
|
+
})));
|
|
343
|
+
ui.success(`Resumed session ${ui.bold(session.id.slice(0, 8))} (${session.messages.length} messages)`);
|
|
344
|
+
continue;
|
|
345
|
+
}
|
|
346
|
+
if (command === '/sessions') {
|
|
347
|
+
const list = await sessionManager.list();
|
|
348
|
+
ui.heading('Saved Sessions');
|
|
349
|
+
if (list.length === 0) {
|
|
350
|
+
console.log(ui.dim('No saved sessions.'));
|
|
351
|
+
}
|
|
352
|
+
else {
|
|
353
|
+
for (const s of list.slice(0, 8)) {
|
|
354
|
+
const isCur = s.id === session.id ? ui.accent('*') : ' ';
|
|
355
|
+
const summary = s.summary ? ` — "${s.summary}"` : '';
|
|
356
|
+
console.log(` ${isCur} ${ui.bold(s.id.slice(0, 8))} ${ui.dim(`[${s.model}]`)} ${ui.dim(`(${s.messageCount} msgs)`)}${summary}`);
|
|
357
|
+
console.log(` ${ui.dim(new Date(s.lastActiveAt).toLocaleString())} ${ui.dim(s.cwd)}`);
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
continue;
|
|
361
|
+
}
|
|
362
|
+
// ─────────────────────────────────────────────────────────────
|
|
363
|
+
// 2. PROJECT & CONTEXT COMMANDS
|
|
364
|
+
// ─────────────────────────────────────────────────────────────
|
|
365
|
+
if (command === '/init') {
|
|
366
|
+
await init();
|
|
367
|
+
systemContext = await buildContext(cwd, config);
|
|
368
|
+
continue;
|
|
369
|
+
}
|
|
370
|
+
if (command === '/context') {
|
|
371
|
+
const metrics = await inspectProjectContext(cwd);
|
|
372
|
+
ui.statusCard('Workspace Context Intelligence', {
|
|
373
|
+
'Directory': metrics.cwd,
|
|
374
|
+
'Project Stacks': metrics.projectTypes.join(', ') || 'General',
|
|
375
|
+
'Git Branch / Status': `${metrics.gitBranch} (${metrics.gitStatus})`,
|
|
376
|
+
'Discovered Files': `${metrics.filesDiscovered} files (${metrics.filesSample.join(', ')}...)`,
|
|
377
|
+
'Instructions File': metrics.instructionsSource || 'None loaded',
|
|
378
|
+
'Memory Entries': `${metrics.memoryEntriesCount} entries in .overarch/memory.md`,
|
|
379
|
+
'Estimated Tokens': `~${metrics.estimatedTokens} tokens`
|
|
380
|
+
});
|
|
381
|
+
continue;
|
|
382
|
+
}
|
|
383
|
+
if (command === '/reload' || command === '/refresh') {
|
|
384
|
+
config = await loadConfig();
|
|
385
|
+
systemContext = await buildContext(cwd, config);
|
|
386
|
+
if (config.ui?.accentColor)
|
|
387
|
+
ui.setAccent(config.ui.accentColor);
|
|
388
|
+
if (config.ui?.avatar)
|
|
389
|
+
ui.setAvatar(config.ui.avatar);
|
|
390
|
+
ui.success('Project context, instructions, and configuration reloaded.');
|
|
391
|
+
continue;
|
|
392
|
+
}
|
|
393
|
+
if (command === '/project' || command.startsWith('/project ')) {
|
|
394
|
+
const targetDir = command.replace(/^\/project\s*/, '').trim();
|
|
395
|
+
if (!targetDir || targetDir === 'status') {
|
|
396
|
+
const metrics = await inspectProjectContext(cwd);
|
|
397
|
+
ui.statusCard('Active Project Information', {
|
|
398
|
+
'Directory': cwd,
|
|
399
|
+
'Tech Ecosystem': metrics.projectTypes.join(', ') || 'General',
|
|
400
|
+
'Git Branch': metrics.gitBranch,
|
|
401
|
+
'Files Discovered': metrics.filesDiscovered,
|
|
402
|
+
'Instructions': metrics.instructionsSource || 'None',
|
|
403
|
+
'Memory Log': `${metrics.memoryEntriesCount} items`
|
|
404
|
+
});
|
|
405
|
+
}
|
|
406
|
+
else if (targetDir === 'recent') {
|
|
407
|
+
const sessions = await sessionManager.list();
|
|
408
|
+
const projects = Array.from(new Set(sessions.map(s => s.cwd)));
|
|
409
|
+
ui.heading('Recently Accessed Projects');
|
|
410
|
+
projects.slice(0, 6).forEach((p, idx) => {
|
|
411
|
+
const isCur = p === cwd ? ui.accent(' (current)') : '';
|
|
412
|
+
console.log(` ${idx + 1}. ${p}${isCur}`);
|
|
413
|
+
});
|
|
414
|
+
}
|
|
415
|
+
else {
|
|
416
|
+
const resolved = path.resolve(cwd, targetDir);
|
|
417
|
+
try {
|
|
418
|
+
const stats = await fs.stat(resolved);
|
|
419
|
+
if (!stats.isDirectory()) {
|
|
420
|
+
ui.error(`Not a directory: ${resolved}`);
|
|
421
|
+
continue;
|
|
422
|
+
}
|
|
423
|
+
process.chdir(resolved);
|
|
424
|
+
cwd = resolved;
|
|
425
|
+
await autoEnsureOverarch(cwd);
|
|
426
|
+
sessionManager = new SessionManager(undefined, cwd);
|
|
427
|
+
checkpointManager = new CheckpointManager(cwd);
|
|
428
|
+
systemContext = await buildContext(cwd, config);
|
|
429
|
+
session = await sessionManager.create(cwd, session.model);
|
|
430
|
+
agentRuntime.setMessages([]);
|
|
431
|
+
ui.success(`Switched workspace to: ${cwd}`);
|
|
432
|
+
ui.info(`Fresh session created for workspace: ${session.id.slice(0, 8)}`);
|
|
433
|
+
}
|
|
434
|
+
catch (e) {
|
|
435
|
+
ui.error(`Cannot switch directory: ${e.message}`);
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
continue;
|
|
439
|
+
}
|
|
440
|
+
if (command === '/memory' || command.startsWith('/memory ')) {
|
|
441
|
+
const rest = command.replace(/^\/memory\s*/, '').trim();
|
|
442
|
+
const parts = rest.split(/\s+/);
|
|
443
|
+
const sub = parts[0] || 'show';
|
|
444
|
+
const arg1 = parts.slice(1).join(' ');
|
|
445
|
+
if (sub === 'search') {
|
|
446
|
+
await handleMemoryCommand('search', arg1);
|
|
447
|
+
}
|
|
448
|
+
else if (sub === 'forget') {
|
|
449
|
+
await handleMemoryCommand('forget', parts[1]);
|
|
450
|
+
}
|
|
451
|
+
else if (sub === 'show' || sub === 'list' || !rest) {
|
|
452
|
+
await handleMemoryCommand('show');
|
|
453
|
+
}
|
|
454
|
+
else {
|
|
455
|
+
// If user typed a raw note e.g. /memory Use pnpm for tests
|
|
456
|
+
await handleMemoryCommand('learn', rest);
|
|
457
|
+
}
|
|
458
|
+
continue;
|
|
459
|
+
}
|
|
460
|
+
if (command === '/learn' || command.startsWith('/learn ')) {
|
|
461
|
+
const lessonText = command.replace(/^\/learn\s*/, '').trim();
|
|
462
|
+
await handleLearnCommand(lessonText);
|
|
463
|
+
continue;
|
|
464
|
+
}
|
|
465
|
+
// ─────────────────────────────────────────────────────────────
|
|
466
|
+
// 3. AI, MODEL & AGENT MODE COMMANDS
|
|
467
|
+
// ─────────────────────────────────────────────────────────────
|
|
468
|
+
if (command.startsWith('/model')) {
|
|
469
|
+
const newModel = command.replace(/^\/model\s*/, '').trim();
|
|
470
|
+
if (!newModel) {
|
|
471
|
+
const models = listModels();
|
|
472
|
+
ui.heading('Available Models');
|
|
473
|
+
models.forEach(m => {
|
|
474
|
+
const isCur = m.id === session.model ? ui.accent(' (active)') : '';
|
|
475
|
+
console.log(` ${ui.bold(m.id)} ${ui.dim(`[${m.provider}]`)} — ${m.name}${isCur}`);
|
|
476
|
+
});
|
|
477
|
+
console.log(`\nUsage: /model <model_id> (e.g. /model claude-3-5-sonnet-20241022 or /model gpt-4o)`);
|
|
478
|
+
continue;
|
|
479
|
+
}
|
|
480
|
+
try {
|
|
481
|
+
const resolved = resolveModel(newModel);
|
|
482
|
+
if (!resolved) {
|
|
483
|
+
ui.warn(`Model '${newModel}' could not be resolved automatically. Attempting raw switch.`);
|
|
484
|
+
}
|
|
485
|
+
const modelId = resolved ? resolved.model : newModel;
|
|
486
|
+
session.model = modelId;
|
|
487
|
+
config.defaultModel = modelId;
|
|
488
|
+
config.mode = 'online';
|
|
489
|
+
providerResult = resolveRuntimeProvider(config, modelId);
|
|
490
|
+
agentRuntime = new AgentRuntime(providerResult.provider, registry, {
|
|
491
|
+
...config,
|
|
492
|
+
defaultModel: providerResult.model
|
|
493
|
+
}, { autoApprove: args.yes || config.agentMode === 'autonomous', rl });
|
|
494
|
+
agentRuntime.setMessages(session.messages.map(m => ({ role: m.role, content: m.content })));
|
|
495
|
+
await saveGlobalConfig({ defaultModel: modelId, mode: 'online' });
|
|
496
|
+
ui.success(`Switched model to: ${ui.bold(providerResult.displayName)} (${providerResult.model})`);
|
|
497
|
+
}
|
|
498
|
+
catch (err) {
|
|
499
|
+
ui.error(`Failed to switch model: ${err.message}`);
|
|
500
|
+
}
|
|
501
|
+
continue;
|
|
502
|
+
}
|
|
503
|
+
if (command === '/plan' || command.startsWith('/plan ')) {
|
|
504
|
+
const arg = command.replace(/^\/plan\s*/, '').trim().toLowerCase();
|
|
505
|
+
if (arg === 'on') {
|
|
506
|
+
config.agentMode = 'plan';
|
|
507
|
+
ui.success('Plan Mode: ENABLED. Overarch will structure explicit roadmaps before code changes.');
|
|
508
|
+
}
|
|
509
|
+
else if (arg === 'off') {
|
|
510
|
+
config.agentMode = 'approval';
|
|
511
|
+
ui.info('Plan Mode: DISABLED (Standard approval mode).');
|
|
512
|
+
}
|
|
513
|
+
else {
|
|
514
|
+
const isPlan = config.agentMode === 'plan';
|
|
515
|
+
config.agentMode = isPlan ? 'approval' : 'plan';
|
|
516
|
+
ui.info(`Plan Mode toggled: ${config.agentMode === 'plan' ? ui.green('ON') : ui.dim('OFF')}`);
|
|
517
|
+
}
|
|
518
|
+
agentRuntime.setConfig(config);
|
|
519
|
+
continue;
|
|
520
|
+
}
|
|
521
|
+
// ─────────────────────────────────────────────────────────────
|
|
522
|
+
// 4. PRESERVED & ENHANCED TRIAD COMMANDS: /ponytail, /ccss-5-0, /cchcfai, /design-critique
|
|
523
|
+
// ─────────────────────────────────────────────────────────────
|
|
524
|
+
if (command === '/ponytail' || command.startsWith('/ponytail ')) {
|
|
525
|
+
const level = command.replace(/^\/ponytail\s*/, '').trim().toLowerCase();
|
|
526
|
+
if (level === 'lite' || level === 'full' || level === 'ultra') {
|
|
527
|
+
config.ponytail = level;
|
|
528
|
+
await saveGlobalConfig({ ponytail: level });
|
|
529
|
+
systemContext = await buildContext(cwd, config);
|
|
530
|
+
ui.success(`Ponytail mode set to: ${ui.bold(level.toUpperCase())}`);
|
|
531
|
+
console.log(ui.dim(level === 'ultra' ? 'Ultra YAGNI: shortest diff, zero extra abstractions, stdlib first.' : 'Enforcing senior dev simplicity ladder.'));
|
|
532
|
+
}
|
|
533
|
+
else {
|
|
534
|
+
console.log(`\n${ui.accent(ui.getAvatar())} ${ui.bold('Ponytail Mode')}: ${config.ponytail.toUpperCase()}`);
|
|
535
|
+
console.log('The lazy senior dev ladder: YAGNI, stdlib over deps, shortest working diff.');
|
|
536
|
+
console.log(`Usage: /ponytail lite | full | ultra\n`);
|
|
537
|
+
}
|
|
538
|
+
continue;
|
|
539
|
+
}
|
|
540
|
+
if (command === '/ccss' || command === '/ccss-5-0' || command.startsWith('/ccss ') || command.startsWith('/ccss-5-0 ')) {
|
|
541
|
+
const parsed = parseCcssCommand(command);
|
|
542
|
+
if (parsed.isStatus) {
|
|
543
|
+
const activeCfg = CCSS_MODE_CONFIGS[config.ccss || 'auto'];
|
|
544
|
+
ui.statusCard('CCSS 5.0 Orchestration Status', {
|
|
545
|
+
'Active Mode': activeCfg.name,
|
|
546
|
+
'Description': activeCfg.description,
|
|
547
|
+
'Planning Depth': activeCfg.planningDepth,
|
|
548
|
+
'Pre-Mutation Checkpoint': activeCfg.requireCheckpoint ? 'Enforced' : 'Optional',
|
|
549
|
+
'Verification Intensity': activeCfg.verificationIntensity.toUpperCase(),
|
|
550
|
+
'Parallel Inspection': activeCfg.parallelInspection ? 'Enabled' : 'Disabled',
|
|
551
|
+
'Approval Gate': activeCfg.approvalGate
|
|
552
|
+
});
|
|
553
|
+
continue;
|
|
554
|
+
}
|
|
555
|
+
if (parsed.mode) {
|
|
556
|
+
config.ccss = parsed.mode;
|
|
557
|
+
await saveGlobalConfig({ ccss: parsed.mode });
|
|
558
|
+
agentRuntime.setConfig(config);
|
|
559
|
+
systemContext = await buildContext(cwd, config);
|
|
560
|
+
ui.success(`CCSS 5.0 mode set to: ${ui.bold(parsed.mode.toUpperCase())}`);
|
|
561
|
+
if (parsed.prompt) {
|
|
562
|
+
ui.userMessage(parsed.prompt);
|
|
563
|
+
await runAgent(parsed.prompt);
|
|
564
|
+
}
|
|
565
|
+
continue;
|
|
566
|
+
}
|
|
567
|
+
if (parsed.prompt) {
|
|
568
|
+
await runAgent(parsed.prompt);
|
|
569
|
+
continue;
|
|
570
|
+
}
|
|
571
|
+
console.log(`\n${ui.accent(ui.getAvatar())} ${ui.bold('CCSS 5.0 Mission Orchestration')}: ${config.ccss.toUpperCase()}`);
|
|
572
|
+
console.log('Orchestrate complex tasks with capability discovery, verification checkpoints, and recovery.');
|
|
573
|
+
console.log(`Usage: /ccss auto | master | god | control [prompt]\n`);
|
|
574
|
+
continue;
|
|
575
|
+
}
|
|
576
|
+
if (command === '/checkpoint' || command.startsWith('/checkpoint ')) {
|
|
577
|
+
const name = command.replace(/^\/checkpoint\s*/, '').trim() || 'manual';
|
|
578
|
+
try {
|
|
579
|
+
const info = await checkpointManager.create(name);
|
|
580
|
+
ui.success(`Created checkpoint: ${ui.bold(info.id)} (${info.name}) [${info.type}]`);
|
|
581
|
+
}
|
|
582
|
+
catch (err) {
|
|
583
|
+
ui.error(`Failed to create checkpoint: ${err.message}`);
|
|
584
|
+
}
|
|
585
|
+
continue;
|
|
586
|
+
}
|
|
587
|
+
if (command === '/checkpoints') {
|
|
588
|
+
const list = await checkpointManager.list();
|
|
589
|
+
ui.heading('Saved Checkpoints');
|
|
590
|
+
if (list.length === 0) {
|
|
591
|
+
console.log(ui.dim('No checkpoints saved yet. Create one using: /checkpoint [name]'));
|
|
592
|
+
}
|
|
593
|
+
else {
|
|
594
|
+
list.slice(0, 10).forEach((c, idx) => {
|
|
595
|
+
console.log(` ${idx + 1}. ${ui.bold(c.id)} — ${c.name} ${ui.dim(`(${c.type} at ${new Date(c.timestamp).toLocaleTimeString()})`)}`);
|
|
596
|
+
});
|
|
597
|
+
}
|
|
598
|
+
continue;
|
|
599
|
+
}
|
|
600
|
+
if (command === '/rollback' || command.startsWith('/rollback ')) {
|
|
601
|
+
const targetId = command.replace(/^\/rollback\s*/, '').trim() || undefined;
|
|
602
|
+
const res = await checkpointManager.rollback(targetId);
|
|
603
|
+
if (res.success) {
|
|
604
|
+
ui.success(`Rolled back successfully to checkpoint ${targetId || 'latest'}.`);
|
|
605
|
+
if (res.output)
|
|
606
|
+
console.log(ui.dim(res.output));
|
|
607
|
+
}
|
|
608
|
+
else {
|
|
609
|
+
ui.fail(`Rollback failed: ${res.error}`);
|
|
610
|
+
}
|
|
611
|
+
continue;
|
|
612
|
+
}
|
|
613
|
+
if (command === '/diff' || command === '/changes') {
|
|
614
|
+
try {
|
|
615
|
+
const { stdout: diffOut } = await execAsync('git diff HEAD', { cwd });
|
|
616
|
+
if (!diffOut.trim()) {
|
|
617
|
+
ui.info('Working tree is clean (no uncommitted diffs).');
|
|
618
|
+
}
|
|
619
|
+
else {
|
|
620
|
+
const formatted = ui.formatDiff(diffOut);
|
|
621
|
+
console.log(formatted || diffOut);
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
catch {
|
|
625
|
+
ui.warn('Git diff unavailable in current directory.');
|
|
626
|
+
}
|
|
627
|
+
continue;
|
|
628
|
+
}
|
|
629
|
+
if (command === '/retry') {
|
|
630
|
+
const userMsgs = session.messages.filter(m => m.role === 'user');
|
|
631
|
+
const lastMsg = userMsgs[userMsgs.length - 1];
|
|
632
|
+
if (!lastMsg) {
|
|
633
|
+
ui.warn('No previous prompt to retry.');
|
|
634
|
+
continue;
|
|
635
|
+
}
|
|
636
|
+
ui.info(`Retrying last prompt: "${ui.bold(lastMsg.content)}"`);
|
|
637
|
+
await runAgent(lastMsg.content);
|
|
638
|
+
continue;
|
|
639
|
+
}
|
|
640
|
+
if (command === '/cchcfai' || command.startsWith('/cchcfai ')) {
|
|
641
|
+
const action = command.replace(/^\/cchcfai\s*/, '').trim().toLowerCase();
|
|
642
|
+
if (action === 'gate') {
|
|
643
|
+
config.agentMode = 'approval';
|
|
644
|
+
ui.success('CCHCFAI Gate: Human approval required for all state-mutating actions.');
|
|
645
|
+
}
|
|
646
|
+
else if (action === 'halt' || action === 'stop') {
|
|
647
|
+
agentRuntime.abort();
|
|
648
|
+
ui.warn('CCHCFAI Emergency Stop: Agent paused and yielded control.');
|
|
649
|
+
}
|
|
650
|
+
else if (action === 'audit') {
|
|
651
|
+
ui.statusCard('CCHCFAI Human Control Audit', {
|
|
652
|
+
'Autonomy Mode': config.agentMode.toUpperCase(),
|
|
653
|
+
'Auto-Approved': config.permissions.autoApprove.join(', '),
|
|
654
|
+
'Requires Approval': config.permissions.requireApproval.join(', '),
|
|
655
|
+
'Denied Tools': config.permissions.deny.join(', ') || 'none',
|
|
656
|
+
'Emergency Stop': 'Active (Ctrl+C or /halt)'
|
|
657
|
+
});
|
|
658
|
+
}
|
|
659
|
+
else {
|
|
660
|
+
console.log(`\n${ui.accent(ui.getAvatar())} ${ui.bold('CCHCFAI Human Control Architecture')}`);
|
|
661
|
+
console.log('Autonomy is bounded, observable, interruptible, and subordinate to the human.');
|
|
662
|
+
console.log(`Usage: /cchcfai gate | audit | halt | status\n`);
|
|
663
|
+
}
|
|
664
|
+
continue;
|
|
665
|
+
}
|
|
666
|
+
if (command === '/design-critique' || command.startsWith('/design-critique ')) {
|
|
667
|
+
ui.heading('Design & UX Review Mode');
|
|
668
|
+
console.log('Overarch evaluates visual hierarchy, layout, typography, and accessibility.');
|
|
669
|
+
console.log('Enter prompt describing UI component, terminal output, or design spec to critique.\n');
|
|
670
|
+
continue;
|
|
671
|
+
}
|
|
672
|
+
// ─────────────────────────────────────────────────────────────
|
|
673
|
+
// 5. FLOATING DESKTOP COMPANION: /companion, /petdex [start|stop|pets|select|install|status]
|
|
674
|
+
// ─────────────────────────────────────────────────────────────
|
|
675
|
+
if (command === '/companion' || command.startsWith('/companion ') || command === '/petdex' || command.startsWith('/petdex ')) {
|
|
676
|
+
const isPetdex = command.startsWith('/petdex');
|
|
677
|
+
const prefix = isPetdex ? /^\/petdex\s*/ : /^\/companion\s*/;
|
|
678
|
+
const rest = command.replace(prefix, '').trim();
|
|
679
|
+
const parts = rest.split(/\s+/);
|
|
680
|
+
const sub = parts[0] || 'status';
|
|
681
|
+
const arg = parts.slice(1).join(' ');
|
|
682
|
+
await handleCompanionCommand(sub, arg);
|
|
683
|
+
continue;
|
|
684
|
+
}
|
|
685
|
+
// ─────────────────────────────────────────────────────────────
|
|
686
|
+
// 6. SYSTEM & DIAGNOSTIC COMMANDS: /doctor, /status, /config, /setup, /tools
|
|
687
|
+
// ─────────────────────────────────────────────────────────────
|
|
688
|
+
if (command === '/doctor') {
|
|
689
|
+
await doctor();
|
|
690
|
+
continue;
|
|
691
|
+
}
|
|
692
|
+
if (command === '/status') {
|
|
693
|
+
await status();
|
|
694
|
+
continue;
|
|
695
|
+
}
|
|
696
|
+
if (command.startsWith('/config')) {
|
|
697
|
+
const rawParts = command.replace(/^\/config\s*/, '').trim().split(/\s+/);
|
|
698
|
+
const sub = rawParts[0] || 'show';
|
|
699
|
+
const key = rawParts[1];
|
|
700
|
+
const val = rawParts.slice(2).join(' ');
|
|
701
|
+
await handleConfigCommand(sub, key, val);
|
|
702
|
+
continue;
|
|
703
|
+
}
|
|
704
|
+
if (command === '/setup') {
|
|
705
|
+
await runSetup(rl);
|
|
706
|
+
config = await loadConfig();
|
|
707
|
+
providerResult = resolveRuntimeProvider(config);
|
|
708
|
+
agentRuntime = new AgentRuntime(providerResult.provider, registry, {
|
|
709
|
+
...config,
|
|
710
|
+
defaultModel: providerResult.model
|
|
711
|
+
}, { autoApprove: args.yes || config.agentMode === 'autonomous', rl });
|
|
712
|
+
systemContext = await buildContext(cwd, config);
|
|
713
|
+
ui.success('Overarch configuration updated and reloaded.');
|
|
714
|
+
continue;
|
|
715
|
+
}
|
|
716
|
+
if (command === '/tools') {
|
|
717
|
+
const tools = registry.list();
|
|
718
|
+
ui.heading('Registered Tools & MCP Connectors');
|
|
719
|
+
for (const t of tools) {
|
|
720
|
+
const isMcp = t.name.startsWith('mcp_') ? ui.accent('[MCP] ') : '';
|
|
721
|
+
console.log(` ${ui.accent('▸')} ${isMcp}${ui.bold(t.name)} ${ui.dim(`[${t.tier}]`)} — ${t.description}`);
|
|
722
|
+
}
|
|
723
|
+
continue;
|
|
724
|
+
}
|
|
725
|
+
if (command === '/compact') {
|
|
726
|
+
args.compact = !args.compact;
|
|
727
|
+
ui.info(`Compact terminal mode: ${args.compact ? 'enabled' : 'disabled'}`);
|
|
728
|
+
continue;
|
|
729
|
+
}
|
|
730
|
+
if (command === '/abort' || command === '/halt') {
|
|
731
|
+
agentRuntime.abort();
|
|
732
|
+
ui.info('Aborted current action.');
|
|
733
|
+
continue;
|
|
734
|
+
}
|
|
735
|
+
// ─────────────────────────────────────────────────────────────
|
|
736
|
+
// 7. CATEGORIZED /HELP & CONTEXTUAL HELP
|
|
737
|
+
// ─────────────────────────────────────────────────────────────
|
|
738
|
+
if (command === '/help' || command.startsWith('/help ')) {
|
|
739
|
+
const topic = command.replace(/^\/help\s*/, '').trim().toLowerCase();
|
|
740
|
+
if (topic === 'petdex') {
|
|
741
|
+
console.log(`\n${ui.bold('Help: Petdex Integration')}\nOptional bridge to external Petdex desktop companion app via local event server.\nCommands: /petdex status, /petdex launch, /petdex pets, /petdex select <id>`);
|
|
742
|
+
}
|
|
743
|
+
else if (topic === 'model') {
|
|
744
|
+
console.log(`\n${ui.bold('Help: Model Switching')}\nSwitch active models mid-session: /model <name>\nExamples: /model claude-3-5-sonnet-20241022, /model gpt-4o, /model gemini-2.5-flash`);
|
|
745
|
+
}
|
|
746
|
+
else if (topic === 'project') {
|
|
747
|
+
console.log(`\n${ui.bold('Help: Project Management')}\nManage repository workspace context: /project [dir], /project recent, /init, /context, /reload, /memory`);
|
|
748
|
+
}
|
|
749
|
+
else if (topic === 'ponytail') {
|
|
750
|
+
console.log(`\n${ui.bold('Help: Ponytail Ladder')}\nEnforce senior dev minimalism: /ponytail lite | full | ultra\nYAGNI, stdlib before custom code, shortest working diff.`);
|
|
751
|
+
}
|
|
752
|
+
else if (topic === 'ccss') {
|
|
753
|
+
console.log(`\n${ui.bold('Help: CCSS 5.0')}\nMission orchestration: /ccss auto | master | god | control\nDecomposes tasks into verified checkpoints.`);
|
|
754
|
+
}
|
|
755
|
+
else if (topic === 'cchcfai') {
|
|
756
|
+
console.log(`\n${ui.bold('Help: CCHCFAI')}\nHuman control framework: /cchcfai gate | audit | halt | status\nEnforces 4-state separation and confirmation gates.`);
|
|
757
|
+
}
|
|
758
|
+
else {
|
|
759
|
+
console.log(`
|
|
760
|
+
${ui.bold('SESSION')}
|
|
761
|
+
/status Display diagnostic dashboard card
|
|
762
|
+
/new Start a fresh session in current project
|
|
763
|
+
/resume [id] Resume past session by ID or prefix
|
|
764
|
+
/sessions List saved sessions with summaries
|
|
765
|
+
/clear Clear conversation history
|
|
766
|
+
/exit, /quit Save session and exit cleanly
|
|
767
|
+
|
|
768
|
+
${ui.bold('PROJECT & REPOSITORY')}
|
|
769
|
+
/project [dir] Inspect current workspace or switch directory
|
|
770
|
+
/project recent List recently accessed projects
|
|
771
|
+
/init Initialize .overarch subsystem for this project
|
|
772
|
+
/context Deep inspection of files, stack, and tokens
|
|
773
|
+
/reload Re-scan workspace context and instructions
|
|
774
|
+
/memory [note] View or append notes in .overarch/memory.md
|
|
775
|
+
/checkpoint [name] Create an instant git/snapshot checkpoint
|
|
776
|
+
/checkpoints List all saved checkpoints
|
|
777
|
+
/rollback [id] Revert repository to a previous checkpoint
|
|
778
|
+
/diff, /changes Inspect uncommitted git working tree changes
|
|
779
|
+
|
|
780
|
+
${ui.bold('AI & AGENT')}
|
|
781
|
+
/model [name] List available models or switch active model
|
|
782
|
+
/plan [on|off] Toggle Plan Mode (roadmap before changes)
|
|
783
|
+
/retry Re-execute last prompt with updated approach
|
|
784
|
+
/ponytail [level] Set Ponytail minimalism: lite | full | ultra
|
|
785
|
+
/ccss [mode] Set CCSS mission orchestration: auto | master | god | control
|
|
786
|
+
/cchcfai [action] Human control layer: gate | audit | halt | status
|
|
787
|
+
/design-critique Design review mode for UI, layout, and a11y
|
|
788
|
+
/tools List available tools, permission tiers, and MCP
|
|
789
|
+
|
|
790
|
+
${ui.bold('PERSISTENT LEARNING & MEMORY')}
|
|
791
|
+
/memory [show] Display project memory & learned episodic lessons
|
|
792
|
+
/memory search <q> Search lessons, conventions, and architectural decisions
|
|
793
|
+
/memory forget <id> Remove a specific learned memory
|
|
794
|
+
/learn "<lesson>" Record a new engineering insight into memory
|
|
795
|
+
|
|
796
|
+
${ui.bold('FLOATING DESKTOP COMPANION (PETDEX)')}
|
|
797
|
+
/companion start Launch floating transparent desktop companion
|
|
798
|
+
/companion stop Close desktop companion
|
|
799
|
+
/companion status Display companion window state and active pet
|
|
800
|
+
/companion pets List all installed Petdex companions
|
|
801
|
+
/companion select Switch active desktop companion
|
|
802
|
+
/companion install Install any pet from Petdex (e.g. /companion install savage-codex-hacker)
|
|
803
|
+
|
|
804
|
+
${ui.bold('SYSTEM')}
|
|
805
|
+
/doctor Run full health and diagnostic checks
|
|
806
|
+
/config [target] Direct configuration (provider, model, accent, permissions)
|
|
807
|
+
/setup Reopen guided setup wizard
|
|
808
|
+
/compact Toggle compact terminal output
|
|
809
|
+
/abort, /halt Emergency stop current agent operation
|
|
810
|
+
`);
|
|
811
|
+
}
|
|
812
|
+
continue;
|
|
813
|
+
}
|
|
814
|
+
if (command.startsWith('/')) {
|
|
815
|
+
ui.warn(`Unknown command: ${command}. Type /help for available commands.`);
|
|
816
|
+
continue;
|
|
817
|
+
}
|
|
818
|
+
await runAgent(command);
|
|
819
|
+
}
|
|
820
|
+
rl.close();
|
|
821
|
+
}
|