imhcode 2.0.5 → 3.0.1
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/bin/imhcode.js +289 -217
- package/dist/orchestrator/executor.d.ts +6 -1
- package/dist/orchestrator/executor.d.ts.map +1 -1
- package/dist/orchestrator/executor.js +73 -16
- package/dist/orchestrator/executor.js.map +1 -1
- package/dist/orchestrator/index.d.ts +9 -1
- package/dist/orchestrator/index.d.ts.map +1 -1
- package/dist/orchestrator/index.js +29 -51
- package/dist/orchestrator/index.js.map +1 -1
- package/dist/orchestrator/types.d.ts +1 -1
- package/dist/orchestrator/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/orchestrator/executor.ts +80 -15
- package/src/orchestrator/index.ts +28 -57
- package/src/orchestrator/types.ts +1 -1
package/bin/imhcode.js
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
* imhcode agent list → List all discovered agents
|
|
13
13
|
* imhcode agent inspect <id> → Show full manifest + loaded skills
|
|
14
14
|
* imhcode agent run <id> "<task>" → Run agent (dry-run by default)
|
|
15
|
-
* --engine <cli> → Override engine (claude|opencode|codex|agy|qwen|mimo)
|
|
15
|
+
* --engine <cli> → Override engine (claude|opencode|codex|codex-fugu|agy|qwen|mimo)
|
|
16
16
|
* -m, --model <model> → Override model
|
|
17
17
|
* --live → Call real LLM
|
|
18
18
|
* --output <path> → Custom session output directory
|
|
@@ -40,6 +40,88 @@ const BRAINSTORM_MD = path.join(DOCS_DIR, 'brainstorming.md');
|
|
|
40
40
|
const BRIEF_MD = path.join(DOCS_DIR, 'PROJECT_BRIEF.md');
|
|
41
41
|
const CONTEXT_MD = path.join(LOCAL_DIR_NAME, 'context.md');
|
|
42
42
|
|
|
43
|
+
/**
|
|
44
|
+
* Ranked priority lists per category.
|
|
45
|
+
* The algorithm scores each available model against these lists
|
|
46
|
+
* and selects the highest-ranked match found in the installed engines.
|
|
47
|
+
*/
|
|
48
|
+
const MODEL_PRIORITY_RANKS = {
|
|
49
|
+
frontend: [
|
|
50
|
+
// Engine Model substring to match (lowercase, no hyphens/dots)
|
|
51
|
+
['mimo', 'mimov25pro'],
|
|
52
|
+
['mimo', 'mimov25'],
|
|
53
|
+
['opencode', 'mimov25pro'],
|
|
54
|
+
['opencode', 'mimov25'],
|
|
55
|
+
['codex', 'gpt55'],
|
|
56
|
+
['codex', 'gpt5'],
|
|
57
|
+
['agy', 'claudeopus46'],
|
|
58
|
+
['agy', 'claudeopus'],
|
|
59
|
+
['opencode', 'gemini35flash'],
|
|
60
|
+
['opencode', 'gemini25pro'],
|
|
61
|
+
['claude', 'claudeopus46'],
|
|
62
|
+
['claude', 'opus'],
|
|
63
|
+
],
|
|
64
|
+
backend: [
|
|
65
|
+
['opencode', 'deepseekv4pro'],
|
|
66
|
+
['opencode', 'deepseekv4'],
|
|
67
|
+
['opencode', 'kimik27code'],
|
|
68
|
+
['opencode', 'kimik2'],
|
|
69
|
+
['opencode', 'qwen3coder480b'],
|
|
70
|
+
['opencode', 'qwen3coder'],
|
|
71
|
+
['codex', 'gpt55'],
|
|
72
|
+
['codex', 'gpt5'],
|
|
73
|
+
['opencode', 'gptoss120b'],
|
|
74
|
+
['qwen', 'qwen3codermax'],
|
|
75
|
+
['qwen', 'qwen3coder'],
|
|
76
|
+
['claude', 'claudesonnet46'],
|
|
77
|
+
['claude', 'sonnet'],
|
|
78
|
+
],
|
|
79
|
+
planning: [
|
|
80
|
+
['agy', 'claudeopus46'],
|
|
81
|
+
['agy', 'claudeopus'],
|
|
82
|
+
['claude', 'claudeopus46'],
|
|
83
|
+
['claude', 'opus'],
|
|
84
|
+
['codex', 'gpt55'],
|
|
85
|
+
['codex', 'gpt5'],
|
|
86
|
+
['opencode', 'gemini31propreview'],
|
|
87
|
+
['opencode', 'gemini31pro'],
|
|
88
|
+
['opencode', 'deepseekv4pro'],
|
|
89
|
+
],
|
|
90
|
+
testing: [
|
|
91
|
+
['codex-fugu', 'fuguultra'],
|
|
92
|
+
['codex-fugu', 'fugu'],
|
|
93
|
+
['codex', 'gpt55'],
|
|
94
|
+
['codex', 'gpt5'],
|
|
95
|
+
['agy', 'gptoss120bmedium'],
|
|
96
|
+
['agy', 'gptoss120b'],
|
|
97
|
+
['agy', 'claudeopus46'],
|
|
98
|
+
['agy', 'claudeopus'],
|
|
99
|
+
['opencode', 'deepseekv4pro'],
|
|
100
|
+
['claude', 'claudeopus46'],
|
|
101
|
+
],
|
|
102
|
+
review: [
|
|
103
|
+
['codex-fugu', 'fuguultra'],
|
|
104
|
+
['codex-fugu', 'fugu'],
|
|
105
|
+
['codex', 'gpt55'],
|
|
106
|
+
['codex', 'gpt5'],
|
|
107
|
+
['agy', 'claudesonnet46'],
|
|
108
|
+
['agy', 'claudesonnet'],
|
|
109
|
+
['opencode', 'gptoss120b'],
|
|
110
|
+
['claude', 'claudesonnet46'],
|
|
111
|
+
['opencode', 'deepseekv4pro'],
|
|
112
|
+
],
|
|
113
|
+
fast: [
|
|
114
|
+
['opencode', 'deepseekv4flash'],
|
|
115
|
+
['opencode', 'deepseekv4flash'],
|
|
116
|
+
['opencode', 'gemini35flash'],
|
|
117
|
+
['codex', 'gpt54mini'],
|
|
118
|
+
['codex', 'gptmini'],
|
|
119
|
+
['qwen', 'qwen3coderflash'],
|
|
120
|
+
['claude', 'claudehaiku35'],
|
|
121
|
+
['claude', 'haiku'],
|
|
122
|
+
],
|
|
123
|
+
};
|
|
124
|
+
|
|
43
125
|
// ─── Entry Point ───────────────────────────────────────────────────────────────
|
|
44
126
|
|
|
45
127
|
const args = process.argv.slice(2);
|
|
@@ -155,7 +237,7 @@ function printGeneralHelp() {
|
|
|
155
237
|
${CLI_CMD} agent inspect <id> → Show agent manifest + loaded skills
|
|
156
238
|
${CLI_CMD} agent run <id> "<task>" → Run agent (dry-run by default)
|
|
157
239
|
--live → Call real LLM
|
|
158
|
-
--engine <cli> → Override engine (claude|opencode|codex|agy|qwen|mimo)
|
|
240
|
+
--engine <cli> → Override engine (claude|opencode|codex|codex-fugu|agy|qwen|mimo)
|
|
159
241
|
-m, --model <model> → Override model
|
|
160
242
|
--output <path> → Save session to custom path
|
|
161
243
|
|
|
@@ -1017,6 +1099,132 @@ async function runInit() {
|
|
|
1017
1099
|
console.log(` Created docs/start.md (write your project here)`);
|
|
1018
1100
|
}
|
|
1019
1101
|
|
|
1102
|
+
const globalConfigPath = path.join(GLOBAL_DIR, 'global.config.json');
|
|
1103
|
+
let useGlobal = false;
|
|
1104
|
+
let globalConfig = null;
|
|
1105
|
+
|
|
1106
|
+
if (fs.existsSync(globalConfigPath)) {
|
|
1107
|
+
try {
|
|
1108
|
+
globalConfig = JSON.parse(fs.readFileSync(globalConfigPath, 'utf8'));
|
|
1109
|
+
} catch {}
|
|
1110
|
+
}
|
|
1111
|
+
|
|
1112
|
+
const isInteractive = process.stdout.isTTY;
|
|
1113
|
+
|
|
1114
|
+
if (globalConfig && isInteractive) {
|
|
1115
|
+
console.log(`\n🕌 Found saved global default configuration:`);
|
|
1116
|
+
console.log(` Primary Engine: ${globalConfig.primary_engine} (${globalConfig.default_model || 'auto'})`);
|
|
1117
|
+
console.log(` Testing Route: ${globalConfig.model_routing?.testing?.model || 'auto'} via ${globalConfig.model_routing?.testing?.engine || 'auto'}`);
|
|
1118
|
+
console.log(` Review Route: ${globalConfig.model_routing?.review?.model || 'auto'} via ${globalConfig.model_routing?.review?.engine || 'auto'}`);
|
|
1119
|
+
|
|
1120
|
+
const ans = await askQuestion('\nUse default global engine and model routing? [Y/n] ');
|
|
1121
|
+
if (ans.toLowerCase() !== 'n' && ans.toLowerCase() !== 'no') {
|
|
1122
|
+
useGlobal = true;
|
|
1123
|
+
}
|
|
1124
|
+
} else if (globalConfig) {
|
|
1125
|
+
useGlobal = true;
|
|
1126
|
+
}
|
|
1127
|
+
|
|
1128
|
+
if (useGlobal && globalConfig) {
|
|
1129
|
+
console.log('\n✅ Applying global default configuration...');
|
|
1130
|
+
|
|
1131
|
+
const dirsToCreate = [
|
|
1132
|
+
DOCS_DIR,
|
|
1133
|
+
LOCAL_DIR_NAME,
|
|
1134
|
+
path.join(LOCAL_DIR_NAME, 'commands'),
|
|
1135
|
+
path.join(LOCAL_DIR_NAME, 'sessions'),
|
|
1136
|
+
];
|
|
1137
|
+
for (const dir of dirsToCreate) {
|
|
1138
|
+
const dirPath = path.join(cwd, dir);
|
|
1139
|
+
if (!fs.existsSync(dirPath)) {
|
|
1140
|
+
fs.mkdirSync(dirPath, { recursive: true });
|
|
1141
|
+
console.log(` Created local directory: ${dir}`);
|
|
1142
|
+
}
|
|
1143
|
+
}
|
|
1144
|
+
|
|
1145
|
+
const startMdPath = path.join(cwd, START_MD);
|
|
1146
|
+
if (!fs.existsSync(startMdPath)) {
|
|
1147
|
+
const startMdContent = [
|
|
1148
|
+
'# 🕌 IMH-Code — Project Start',
|
|
1149
|
+
'',
|
|
1150
|
+
'> **Imam Hussain Coding Harness Platform**',
|
|
1151
|
+
'> Answer the scope questions below, write your description, then run `imhcode plan`.',
|
|
1152
|
+
'',
|
|
1153
|
+
'---',
|
|
1154
|
+
'',
|
|
1155
|
+
'## ⚡ Quick Scope Check',
|
|
1156
|
+
'',
|
|
1157
|
+
'**Do you need a backend API / server-side logic?**',
|
|
1158
|
+
'> **Answer:** yes *(change to: yes / no / unsure)*',
|
|
1159
|
+
'',
|
|
1160
|
+
'**Do you need a mobile app (iOS/Android)?**',
|
|
1161
|
+
'> **Answer:** no *(change to: yes / no)*',
|
|
1162
|
+
'',
|
|
1163
|
+
'---',
|
|
1164
|
+
'',
|
|
1165
|
+
'## 📝 Your Project Description',
|
|
1166
|
+
'',
|
|
1167
|
+
'Write your complete project idea below. Be as detailed as possible.',
|
|
1168
|
+
'Include: what you\'re building, who it\'s for, key features, preferred stack,',
|
|
1169
|
+
'design preferences, integrations needed, business constraints.',
|
|
1170
|
+
'',
|
|
1171
|
+
'<!-- WRITE_PROMPT_HERE -->',
|
|
1172
|
+
'I want to build a SaaS dashboard for managing hotel room bookings with real-time availability,',
|
|
1173
|
+
'a Next.js frontend, Laravel API backend, and PostgreSQL database. Target users are hotel managers.',
|
|
1174
|
+
'Key features: room calendar, booking CRUD, guest management, reporting, email notifications.',
|
|
1175
|
+
'<!-- END_PROMPT -->',
|
|
1176
|
+
'',
|
|
1177
|
+
'---',
|
|
1178
|
+
'',
|
|
1179
|
+
'## 🚀 Next Step',
|
|
1180
|
+
'',
|
|
1181
|
+
'After filling in the scope and your description, run:',
|
|
1182
|
+
'',
|
|
1183
|
+
'```bash',
|
|
1184
|
+
'imhcode plan',
|
|
1185
|
+
'```',
|
|
1186
|
+
'',
|
|
1187
|
+
'IMH-Code will invoke your configured **planning AI model** (e.g. Claude Opus, GPT-5.5)',
|
|
1188
|
+
'to generate `docs/brainstorming.md` with smart, project-specific questions and answers.',
|
|
1189
|
+
].join('\n');
|
|
1190
|
+
fs.writeFileSync(startMdPath, startMdContent, 'utf8');
|
|
1191
|
+
console.log(` Created docs/start.md (write your project here)`);
|
|
1192
|
+
}
|
|
1193
|
+
|
|
1194
|
+
const localGitignore = path.join(cwd, '.gitignore');
|
|
1195
|
+
if (!fs.existsSync(localGitignore)) {
|
|
1196
|
+
const gitignoreTemplate = path.join(packageRoot, '.gitignore.template');
|
|
1197
|
+
if (fs.existsSync(gitignoreTemplate)) {
|
|
1198
|
+
fs.copyFileSync(gitignoreTemplate, localGitignore);
|
|
1199
|
+
console.log(` Created local .gitignore`);
|
|
1200
|
+
}
|
|
1201
|
+
}
|
|
1202
|
+
|
|
1203
|
+
registerCliGlobally(imhcodeScriptPath);
|
|
1204
|
+
|
|
1205
|
+
const configPath = path.join(cwd, CONFIG_FILE);
|
|
1206
|
+
fs.writeFileSync(configPath, JSON.stringify(globalConfig, null, 2), 'utf8');
|
|
1207
|
+
console.log(`\n💾 Configuration saved: ${configPath}`);
|
|
1208
|
+
|
|
1209
|
+
console.log(`\n✅ ${PLATFORM_NAME} initialized successfully!`);
|
|
1210
|
+
console.log(`─`.repeat(60));
|
|
1211
|
+
console.log(`\n🕌 HOW TO BUILD WITH IMH-CODE:\n`);
|
|
1212
|
+
console.log(` 1. Open docs/start.md → Answer scope questions + write your description`);
|
|
1213
|
+
console.log(` 2. Run: imhcode plan`);
|
|
1214
|
+
console.log(` → Your planning AI generates brainstorming.md`);
|
|
1215
|
+
console.log(` 3. Open docs/brainstorming.md → Review/edit AI-recommended answers`);
|
|
1216
|
+
console.log(` 4. Run: imhcode plan`);
|
|
1217
|
+
console.log(` → Your planning AI generates sprint plans with correct agent routing`);
|
|
1218
|
+
console.log(` 5. Run: imhcode execute 1 → Sprint 1 (frontend tasks)`);
|
|
1219
|
+
console.log(` 6. Run: imhcode execute 2 → Sprint 2 (backend tasks)`);
|
|
1220
|
+
console.log(` 7. Run: imhcode test → Final testing + security + SEO`);
|
|
1221
|
+
console.log(` 8. Run: imhcode report → Generate PROJECT_REPORT.md`);
|
|
1222
|
+
console.log(`\n Run "imhcode --help" for all commands.`);
|
|
1223
|
+
console.log(`─`.repeat(60));
|
|
1224
|
+
console.log('');
|
|
1225
|
+
return;
|
|
1226
|
+
}
|
|
1227
|
+
|
|
1020
1228
|
// Create .gitignore
|
|
1021
1229
|
const localGitignore = path.join(cwd, '.gitignore');
|
|
1022
1230
|
if (!fs.existsSync(localGitignore)) {
|
|
@@ -1028,7 +1236,7 @@ async function runInit() {
|
|
|
1028
1236
|
}
|
|
1029
1237
|
|
|
1030
1238
|
registerCliGlobally(imhcodeScriptPath);
|
|
1031
|
-
ensureCavemanAndGraphify();
|
|
1239
|
+
ensureCavemanAndGraphify(globalConfig?.skills_configured);
|
|
1032
1240
|
|
|
1033
1241
|
// ── Interactive Engine & Model Setup ────────────────────────────────────────
|
|
1034
1242
|
console.log('\n🔍 Scanning for local coding assistant CLIs...');
|
|
@@ -1037,12 +1245,11 @@ async function runInit() {
|
|
|
1037
1245
|
|
|
1038
1246
|
if (foundEngines.length === 0) {
|
|
1039
1247
|
console.warn('\n⚠️ No local coding assistant CLIs detected.');
|
|
1040
|
-
console.warn(' Supported: claude, opencode, codex, agy (Antigravity), qwen (QwenCode), mimo (MimoCode)');
|
|
1248
|
+
console.warn(' Supported: claude, opencode, codex, codex-fugu, agy (Antigravity), qwen (QwenCode), mimo (MimoCode)');
|
|
1041
1249
|
console.warn(' Install one and re-run "imhcode" to configure model routing.\n');
|
|
1042
1250
|
}
|
|
1043
1251
|
|
|
1044
1252
|
let primaryEngine = 'claude';
|
|
1045
|
-
const isInteractive = process.stdout.isTTY;
|
|
1046
1253
|
|
|
1047
1254
|
if (isInteractive && foundEngines.length > 0) {
|
|
1048
1255
|
console.log('\nFound the following local coding assistant CLIs:');
|
|
@@ -1091,16 +1298,12 @@ async function runInit() {
|
|
|
1091
1298
|
console.log(`✅ Default model: ${defaultModel}`);
|
|
1092
1299
|
}
|
|
1093
1300
|
|
|
1094
|
-
// Fix 4: Smart Model Routing Setup with ranked scoring
|
|
1095
|
-
const modelRouting = await setupModelRouting(engines, foundEngines, isInteractive);
|
|
1096
|
-
|
|
1097
1301
|
// Write config
|
|
1098
1302
|
const configPath = path.join(cwd, CONFIG_FILE);
|
|
1099
1303
|
const configData = {
|
|
1100
1304
|
primary_engine: primaryEngine,
|
|
1101
1305
|
default_model: defaultModel || undefined,
|
|
1102
1306
|
testing_mode: 'fast',
|
|
1103
|
-
model_routing: modelRouting,
|
|
1104
1307
|
available_engines: {},
|
|
1105
1308
|
};
|
|
1106
1309
|
|
|
@@ -1114,221 +1317,36 @@ async function runInit() {
|
|
|
1114
1317
|
fs.writeFileSync(configPath, JSON.stringify(configData, null, 2), 'utf8');
|
|
1115
1318
|
console.log(`\n💾 Configuration saved: ${configPath}`);
|
|
1116
1319
|
|
|
1320
|
+
// Save to global config defaults as well
|
|
1321
|
+
const globalConfigData = {
|
|
1322
|
+
...configData,
|
|
1323
|
+
skills_configured: true,
|
|
1324
|
+
};
|
|
1325
|
+
try {
|
|
1326
|
+
fs.writeFileSync(globalConfigPath, JSON.stringify(globalConfigData, null, 2), 'utf8');
|
|
1327
|
+
console.log(`💾 Global defaults saved: ${globalConfigPath}`);
|
|
1328
|
+
} catch (e) {
|
|
1329
|
+
console.warn(`⚠️ Could not save global default configuration: ${e.message}`);
|
|
1330
|
+
}
|
|
1331
|
+
|
|
1117
1332
|
// Print final guide
|
|
1118
1333
|
console.log(`\n✅ ${PLATFORM_NAME} initialized successfully!`);
|
|
1119
1334
|
console.log(`─`.repeat(60));
|
|
1120
1335
|
console.log(`\n🕌 HOW TO BUILD WITH IMH-CODE:\n`);
|
|
1121
1336
|
console.log(` 1. Open docs/start.md → Answer scope questions + write your description`);
|
|
1122
1337
|
console.log(` 2. Run: imhcode plan`);
|
|
1123
|
-
console.log(` → Your planning AI
|
|
1338
|
+
console.log(` → Your planning AI generates brainstorming.md`);
|
|
1124
1339
|
console.log(` 3. Open docs/brainstorming.md → Review/edit AI-recommended answers`);
|
|
1125
1340
|
console.log(` 4. Run: imhcode plan`);
|
|
1126
1341
|
console.log(` → Your planning AI generates sprint plans with correct agent routing`);
|
|
1127
|
-
console.log(` 5. Run: imhcode execute 1 → Sprint 1 (frontend tasks
|
|
1128
|
-
console.log(` 6. Run: imhcode execute 2 → Sprint 2 (backend tasks
|
|
1342
|
+
console.log(` 5. Run: imhcode execute 1 → Sprint 1 (frontend tasks)`);
|
|
1343
|
+
console.log(` 6. Run: imhcode execute 2 → Sprint 2 (backend tasks)`);
|
|
1129
1344
|
console.log(` 7. Run: imhcode test → Final testing + security + SEO`);
|
|
1130
1345
|
console.log(` 8. Run: imhcode report → Generate PROJECT_REPORT.md`);
|
|
1131
1346
|
console.log(`\n Run "imhcode --help" for all commands.`);
|
|
1132
1347
|
console.log(`─`.repeat(60));
|
|
1133
1348
|
console.log('');
|
|
1134
1349
|
}
|
|
1135
|
-
|
|
1136
|
-
// ─── Fix 4: Model Routing Setup Wizard (Ranked Scoring Algorithm) ─────────────
|
|
1137
|
-
|
|
1138
|
-
/**
|
|
1139
|
-
* Ranked priority lists per category.
|
|
1140
|
-
* The algorithm scores each available model against these lists
|
|
1141
|
-
* and selects the highest-ranked match found in the installed engines.
|
|
1142
|
-
*/
|
|
1143
|
-
const MODEL_PRIORITY_RANKS = {
|
|
1144
|
-
frontend: [
|
|
1145
|
-
// Engine Model substring to match (lowercase, no hyphens/dots)
|
|
1146
|
-
['mimo', 'mimov25pro'],
|
|
1147
|
-
['mimo', 'mimov25'],
|
|
1148
|
-
['opencode', 'mimov25pro'],
|
|
1149
|
-
['opencode', 'mimov25'],
|
|
1150
|
-
['codex', 'gpt55'],
|
|
1151
|
-
['codex', 'gpt5'],
|
|
1152
|
-
['agy', 'claudeopus46'],
|
|
1153
|
-
['agy', 'claudeopus'],
|
|
1154
|
-
['opencode', 'gemini35flash'],
|
|
1155
|
-
['opencode', 'gemini25pro'],
|
|
1156
|
-
['claude', 'claudeopus46'],
|
|
1157
|
-
['claude', 'opus'],
|
|
1158
|
-
],
|
|
1159
|
-
backend: [
|
|
1160
|
-
['opencode', 'deepseekv4pro'],
|
|
1161
|
-
['opencode', 'deepseekv4'],
|
|
1162
|
-
['opencode', 'kimik27code'],
|
|
1163
|
-
['opencode', 'kimik2'],
|
|
1164
|
-
['opencode', 'qwen3coder480b'],
|
|
1165
|
-
['opencode', 'qwen3coder'],
|
|
1166
|
-
['codex', 'gpt55'],
|
|
1167
|
-
['codex', 'gpt5'],
|
|
1168
|
-
['opencode', 'gptoss120b'],
|
|
1169
|
-
['qwen', 'qwen3codermax'],
|
|
1170
|
-
['qwen', 'qwen3coder'],
|
|
1171
|
-
['claude', 'claudesonnet46'],
|
|
1172
|
-
['claude', 'sonnet'],
|
|
1173
|
-
],
|
|
1174
|
-
planning: [
|
|
1175
|
-
['agy', 'claudeopus46'],
|
|
1176
|
-
['agy', 'claudeopus'],
|
|
1177
|
-
['claude', 'claudeopus46'],
|
|
1178
|
-
['claude', 'opus'],
|
|
1179
|
-
['codex', 'gpt55'],
|
|
1180
|
-
['codex', 'gpt5'],
|
|
1181
|
-
['opencode', 'gemini31propreview'],
|
|
1182
|
-
['opencode', 'gemini31pro'],
|
|
1183
|
-
['opencode', 'deepseekv4pro'],
|
|
1184
|
-
],
|
|
1185
|
-
testing: [
|
|
1186
|
-
['codex', 'gpt55'],
|
|
1187
|
-
['codex', 'gpt5'],
|
|
1188
|
-
['agy', 'gptoss120bmedium'],
|
|
1189
|
-
['agy', 'gptoss120b'],
|
|
1190
|
-
['agy', 'claudeopus46'],
|
|
1191
|
-
['agy', 'claudeopus'],
|
|
1192
|
-
['opencode', 'deepseekv4pro'],
|
|
1193
|
-
['claude', 'claudeopus46'],
|
|
1194
|
-
],
|
|
1195
|
-
review: [
|
|
1196
|
-
['codex', 'gpt55'],
|
|
1197
|
-
['codex', 'gpt5'],
|
|
1198
|
-
['agy', 'claudesonnet46'],
|
|
1199
|
-
['agy', 'claudesonnet'],
|
|
1200
|
-
['opencode', 'gptoss120b'],
|
|
1201
|
-
['claude', 'claudesonnet46'],
|
|
1202
|
-
['opencode', 'deepseekv4pro'],
|
|
1203
|
-
],
|
|
1204
|
-
fast: [
|
|
1205
|
-
['opencode', 'deepseekv4flash'],
|
|
1206
|
-
['opencode', 'deepseekv4flash'],
|
|
1207
|
-
['opencode', 'gemini35flash'],
|
|
1208
|
-
['codex', 'gpt54mini'],
|
|
1209
|
-
['codex', 'gptmini'],
|
|
1210
|
-
['qwen', 'qwen3coderflash'],
|
|
1211
|
-
['claude', 'claudehaiku35'],
|
|
1212
|
-
['claude', 'haiku'],
|
|
1213
|
-
],
|
|
1214
|
-
};
|
|
1215
|
-
|
|
1216
|
-
/**
|
|
1217
|
-
* Normalize a model/engine string for fuzzy matching:
|
|
1218
|
-
* lowercase, remove hyphens, dots, underscores, spaces.
|
|
1219
|
-
*/
|
|
1220
|
-
function normalizeForMatch(s) {
|
|
1221
|
-
return (s || '').toLowerCase().replace(/[-._\s]/g, '');
|
|
1222
|
-
}
|
|
1223
|
-
|
|
1224
|
-
/**
|
|
1225
|
-
* Select the best model for a category from available engines using ranked scoring.
|
|
1226
|
-
* Returns { engine, model } or null if nothing found.
|
|
1227
|
-
*/
|
|
1228
|
-
function selectBestModel(category, engines) {
|
|
1229
|
-
const ranks = MODEL_PRIORITY_RANKS[category] || [];
|
|
1230
|
-
for (const [preferredEngine, modelSubstring] of ranks) {
|
|
1231
|
-
const engData = engines[preferredEngine];
|
|
1232
|
-
if (!engData?.path || !engData.models?.length) continue;
|
|
1233
|
-
const match = engData.models.find(m => normalizeForMatch(m).includes(modelSubstring));
|
|
1234
|
-
if (match) return { engine: preferredEngine, model: match };
|
|
1235
|
-
}
|
|
1236
|
-
return null;
|
|
1237
|
-
}
|
|
1238
|
-
|
|
1239
|
-
async function setupModelRouting(engines, foundEngines, isInteractive) {
|
|
1240
|
-
const categories = {
|
|
1241
|
-
frontend: { label: 'Frontend (UI/UX, components, animations)', note: 'Mimo v2.5 Pro → GPT-5.5 → Claude Opus' },
|
|
1242
|
-
backend: { label: 'Backend (APIs, database, business logic)', note: 'DeepSeek V4 Pro → Kimi K2.7 → Qwen3 Coder → GPT-5.5' },
|
|
1243
|
-
planning: { label: 'Planning (brainstorming, sprint planning)', note: 'Claude Opus 4.6 → GPT-5.5 → Gemini 3.1 Pro' },
|
|
1244
|
-
testing: { label: 'Testing (QA, security audit, E2E)', note: 'GPT-5.5 → GPT-OSS 120B → Claude Opus' },
|
|
1245
|
-
review: { label: 'Review (SEO, debugging, code review)', note: 'GPT-5.5 → Claude Sonnet → GPT-OSS 120B' },
|
|
1246
|
-
fast: { label: 'Fast (boilerplate, config, simple tasks)', note: 'DeepSeek V4 Flash → Gemini 3.5 Flash → GPT Mini' },
|
|
1247
|
-
};
|
|
1248
|
-
|
|
1249
|
-
const recommended = {};
|
|
1250
|
-
for (const cat of Object.keys(categories)) {
|
|
1251
|
-
const best = selectBestModel(cat, engines);
|
|
1252
|
-
if (best) {
|
|
1253
|
-
recommended[cat] = best;
|
|
1254
|
-
} else if (foundEngines.length > 0) {
|
|
1255
|
-
// Fallback: primary engine first model
|
|
1256
|
-
const fe = foundEngines[0];
|
|
1257
|
-
recommended[cat] = { engine: fe, model: engines[fe].models[0] || 'default' };
|
|
1258
|
-
}
|
|
1259
|
-
}
|
|
1260
|
-
|
|
1261
|
-
// Show recommended routing table
|
|
1262
|
-
console.log('\n' + '─'.repeat(70));
|
|
1263
|
-
console.log('🧠 Recommended Model Routing (ranked by quality for each category):\n');
|
|
1264
|
-
console.log(' Category │ Engine │ Model');
|
|
1265
|
-
console.log(' ──────────────┼───────────────┼───────────────────────────────────────');
|
|
1266
|
-
for (const [cat, rec] of Object.entries(recommended)) {
|
|
1267
|
-
const catLabel = cat.padEnd(12);
|
|
1268
|
-
const eng = (rec.engine || '?').padEnd(13);
|
|
1269
|
-
const mdl = rec.model || '?';
|
|
1270
|
-
const note = categories[cat]?.note || '';
|
|
1271
|
-
console.log(` ${catLabel} │ ${eng} │ ${mdl}`);
|
|
1272
|
-
}
|
|
1273
|
-
console.log(' ' + '─'.repeat(67));
|
|
1274
|
-
console.log(`\n Priority: Mimo v2.5 Pro (frontend) | DeepSeek V4 Pro (backend)`);
|
|
1275
|
-
console.log(` Claude Opus (planning) | GPT-5.5 (testing/review)`);
|
|
1276
|
-
|
|
1277
|
-
const routing = {};
|
|
1278
|
-
|
|
1279
|
-
if (isInteractive) {
|
|
1280
|
-
const answer = await askQuestion('\nAccept recommended routing? [Y/n] ');
|
|
1281
|
-
if (answer.toLowerCase() === 'n' || answer.toLowerCase() === 'no') {
|
|
1282
|
-
// Let user customize each category
|
|
1283
|
-
for (const [cat, cfg] of Object.entries(categories)) {
|
|
1284
|
-
console.log(`\n Configure model for [${cat}] — ${cfg.label}`);
|
|
1285
|
-
console.log(` Priority: ${cfg.note}`);
|
|
1286
|
-
|
|
1287
|
-
const allModels = [];
|
|
1288
|
-
for (const eng of foundEngines) {
|
|
1289
|
-
for (const m of engines[eng].models) {
|
|
1290
|
-
allModels.push({ engine: eng, model: m });
|
|
1291
|
-
}
|
|
1292
|
-
}
|
|
1293
|
-
|
|
1294
|
-
if (allModels.length === 0) {
|
|
1295
|
-
routing[cat] = recommended[cat];
|
|
1296
|
-
continue;
|
|
1297
|
-
}
|
|
1298
|
-
|
|
1299
|
-
allModels.forEach((item, i) => {
|
|
1300
|
-
const rec = recommended[cat];
|
|
1301
|
-
const isRec = rec && rec.engine === item.engine && rec.model === item.model;
|
|
1302
|
-
console.log(` [${i + 1}] ${item.model} (${item.engine})${isRec ? ' ← Recommended ✅' : ''}`);
|
|
1303
|
-
});
|
|
1304
|
-
|
|
1305
|
-
let selectedIdx = allModels.findIndex(m => {
|
|
1306
|
-
const rec = recommended[cat];
|
|
1307
|
-
return rec && m.engine === rec.engine && m.model === rec.model;
|
|
1308
|
-
});
|
|
1309
|
-
if (selectedIdx < 0) selectedIdx = 0;
|
|
1310
|
-
|
|
1311
|
-
while (true) {
|
|
1312
|
-
const ans = await askQuestion(` Select model for ${cat} [1-${allModels.length}] (default: ${selectedIdx + 1}): `);
|
|
1313
|
-
if (ans === '') { routing[cat] = allModels[selectedIdx]; break; }
|
|
1314
|
-
const p = parseInt(ans, 10);
|
|
1315
|
-
if (p >= 1 && p <= allModels.length) { routing[cat] = allModels[p - 1]; break; }
|
|
1316
|
-
console.log(` ❌ Invalid. Enter 1–${allModels.length}.`);
|
|
1317
|
-
}
|
|
1318
|
-
console.log(` ✅ ${cat}: ${routing[cat].model} (${routing[cat].engine})`);
|
|
1319
|
-
}
|
|
1320
|
-
} else {
|
|
1321
|
-
Object.assign(routing, recommended);
|
|
1322
|
-
console.log('\n✅ Recommended routing accepted.');
|
|
1323
|
-
}
|
|
1324
|
-
} else {
|
|
1325
|
-
Object.assign(routing, recommended);
|
|
1326
|
-
console.log('\n✅ Recommended routing applied automatically.');
|
|
1327
|
-
}
|
|
1328
|
-
|
|
1329
|
-
return routing;
|
|
1330
|
-
}
|
|
1331
|
-
|
|
1332
1350
|
// ─── Fix 0: LLM-Powered Planning ─────────────────────────────────────────────
|
|
1333
1351
|
|
|
1334
1352
|
/**
|
|
@@ -2633,11 +2651,40 @@ function scanAssistantCLIs() {
|
|
|
2633
2651
|
claude: { name: 'Claude Code', path: resolveBinary('claude', ['~/.local/bin/claude', '/usr/local/bin/claude', '/opt/homebrew/bin/claude']), models: [], modelsCmd: b => `"${b}" --list-models 2>/dev/null || echo ''` },
|
|
2634
2652
|
opencode: { name: 'OpenCode', path: resolveBinary('opencode', ['~/.opencode/bin/opencode', '/usr/local/bin/opencode']), models: [], modelsCmd: b => `"${b}" models 2>/dev/null` },
|
|
2635
2653
|
codex: { name: 'Codex (OpenAI)', path: resolveBinary('codex', ['~/Library/PhpWebStudy/env/node/bin/codex', '/usr/local/bin/codex']), models: [], modelsCmd: b => `"${b}" debug models 2>/dev/null` },
|
|
2654
|
+
'codex-fugu': { name: 'Codex Fugu', path: resolveBinary('codex-fugu', ['~/.local/bin/codex-fugu', '/usr/local/bin/codex-fugu', '/opt/homebrew/bin/codex-fugu']), models: [], modelsCmd: b => `"${b}" debug models 2>/dev/null || echo ''` },
|
|
2636
2655
|
agy: { name: 'Antigravity CLI (agy)', path: resolveBinary('agy', ['~/.local/bin/agy', '/usr/local/bin/agy', '/opt/homebrew/bin/agy']), models: [], modelsCmd: b => `"${b}" models 2>/dev/null || echo ''` },
|
|
2637
2656
|
qwen: { name: 'QwenCode (qwen)', path: resolveBinary('qwen', ['~/.local/bin/qwen', '/usr/local/bin/qwen', '/opt/homebrew/bin/qwen']), models: [], modelsCmd: b => `"${b}" models 2>/dev/null || echo ''` },
|
|
2638
2657
|
mimo: { name: 'MimoCode (mimo)', path: resolveBinary('mimo', ['~/.local/bin/mimo', '/usr/local/bin/mimo', '/opt/homebrew/bin/mimo', '~/.mimo/bin/mimo']), models: [], modelsCmd: b => `"${b}" models 2>/dev/null || echo ''` },
|
|
2639
2658
|
};
|
|
2640
2659
|
|
|
2660
|
+
// Scan for agy2, agy3, agy4, etc.
|
|
2661
|
+
for (let i = 2; i <= 10; i++) {
|
|
2662
|
+
const key = `agy${i}`;
|
|
2663
|
+
const binaryPath = resolveBinary(key, [
|
|
2664
|
+
`~/.local/bin/${key}`,
|
|
2665
|
+
`/usr/local/bin/${key}`,
|
|
2666
|
+
`/opt/homebrew/bin/${key}`,
|
|
2667
|
+
]);
|
|
2668
|
+
if (binaryPath) {
|
|
2669
|
+
engines[key] = {
|
|
2670
|
+
name: `Antigravity CLI (${key})`,
|
|
2671
|
+
path: binaryPath,
|
|
2672
|
+
models: [],
|
|
2673
|
+
modelsCmd: b => `"${b}" models 2>/dev/null || echo ''`
|
|
2674
|
+
};
|
|
2675
|
+
}
|
|
2676
|
+
}
|
|
2677
|
+
|
|
2678
|
+
const fallbackModels = {
|
|
2679
|
+
claude: ['claude-sonnet-5', 'claude-fable-5', 'claude-opus-4-8', 'opus', 'sonnet', 'haiku'],
|
|
2680
|
+
opencode: ['mimo-vl-v2.5-pro', 'deepseek-v4-pro', 'deepseek-v4-flash', 'gemini-3.5-flash', 'kimi-k2.7-code', 'qwen-3-coder-480b', 'gpt-oss-120b'],
|
|
2681
|
+
codex: ['gpt-5.5', 'gpt-5.4-mini', 'gpt-mini'],
|
|
2682
|
+
'codex-fugu': ['fugu', 'fugu-ultra'],
|
|
2683
|
+
agy: ['claude-opus-4-6', 'gemini-3.5-flash'],
|
|
2684
|
+
qwen: ['qwen3-coder-max', 'qwen3-coder-flash'],
|
|
2685
|
+
mimo: ['mimo-vl-v2.5-pro', 'mimo-vl-v2.5']
|
|
2686
|
+
};
|
|
2687
|
+
|
|
2641
2688
|
for (const [key, eng] of Object.entries(engines)) {
|
|
2642
2689
|
if (!eng.path) continue;
|
|
2643
2690
|
try {
|
|
@@ -2652,6 +2699,11 @@ function scanAssistantCLIs() {
|
|
|
2652
2699
|
}
|
|
2653
2700
|
if (models.length > 0) eng.models = models;
|
|
2654
2701
|
} catch { eng.models = []; }
|
|
2702
|
+
|
|
2703
|
+
const baseKey = key.startsWith('agy') ? 'agy' : key;
|
|
2704
|
+
if (eng.models.length === 0 && fallbackModels[baseKey]) {
|
|
2705
|
+
eng.models = fallbackModels[baseKey];
|
|
2706
|
+
}
|
|
2655
2707
|
}
|
|
2656
2708
|
|
|
2657
2709
|
return engines;
|
|
@@ -2725,7 +2777,11 @@ function registerCliGlobally(imhcodeScriptPath) {
|
|
|
2725
2777
|
}
|
|
2726
2778
|
}
|
|
2727
2779
|
|
|
2728
|
-
function ensureCavemanAndGraphify() {
|
|
2780
|
+
function ensureCavemanAndGraphify(skillsAlreadyConfigured) {
|
|
2781
|
+
if (skillsAlreadyConfigured) {
|
|
2782
|
+
console.log('\n🔍 Token-saving skills already configured globally. Skipping installation checks.');
|
|
2783
|
+
return;
|
|
2784
|
+
}
|
|
2729
2785
|
console.log('\n🔍 Checking token-saving skills...');
|
|
2730
2786
|
let skillsInstalled = false;
|
|
2731
2787
|
try { execSync('npx --no-install skills --version', { stdio: 'ignore' }); skillsInstalled = true; } catch { /* not installed */ }
|
|
@@ -3129,7 +3185,17 @@ async function runTuiCommand() {
|
|
|
3129
3185
|
|
|
3130
3186
|
function detectProjectState(dir) {
|
|
3131
3187
|
const hasConfig = fs.existsSync(path.join(dir, CONFIG_FILE));
|
|
3132
|
-
|
|
3188
|
+
let hasStart = fs.existsSync(path.join(dir, START_MD));
|
|
3189
|
+
if (hasStart) {
|
|
3190
|
+
try {
|
|
3191
|
+
const content = fs.readFileSync(path.join(dir, START_MD), 'utf8');
|
|
3192
|
+
if (content.includes('hotel room bookings with real-time availability')) {
|
|
3193
|
+
hasStart = false; // default template hasn't been edited/replaced yet
|
|
3194
|
+
}
|
|
3195
|
+
} catch {
|
|
3196
|
+
hasStart = false;
|
|
3197
|
+
}
|
|
3198
|
+
}
|
|
3133
3199
|
const hasBrainstorm = fs.existsSync(path.join(dir, BRAINSTORM_MD));
|
|
3134
3200
|
const hasSprints = detectSprintDocs(dir);
|
|
3135
3201
|
|
|
@@ -3369,21 +3435,25 @@ async function runTuiCommand() {
|
|
|
3369
3435
|
process.stdout.write(ANSI.showCursor + '\n');
|
|
3370
3436
|
|
|
3371
3437
|
console.log(ANSI.cyan + '\n🕌 IMH-Code — Write Requirements' + ANSI.reset);
|
|
3372
|
-
console.log('Enter your project description below
|
|
3438
|
+
console.log('Enter your project description below. When done, type /submit or a single . on a new line to finish:\n');
|
|
3373
3439
|
|
|
3374
3440
|
const lines = [];
|
|
3375
3441
|
const rlInput = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
3376
3442
|
|
|
3377
3443
|
while (true) {
|
|
3378
3444
|
const line = await new Promise(res => rlInput.question('> ', res));
|
|
3379
|
-
|
|
3445
|
+
const trimmed = line.trim();
|
|
3446
|
+
if (trimmed === '/submit' || trimmed === '.') {
|
|
3447
|
+
break;
|
|
3448
|
+
}
|
|
3380
3449
|
lines.push(line);
|
|
3381
3450
|
}
|
|
3382
3451
|
|
|
3383
3452
|
const desc = lines.join('\n');
|
|
3453
|
+
rlInput.close();
|
|
3454
|
+
|
|
3384
3455
|
if (desc.trim() === '') {
|
|
3385
3456
|
console.log('⚠️ No requirements entered. Aborting.');
|
|
3386
|
-
rlInput.close();
|
|
3387
3457
|
inSubMode = false;
|
|
3388
3458
|
if (isRaw) process.stdin.setRawMode(true);
|
|
3389
3459
|
process.stdin.resume();
|
|
@@ -3391,10 +3461,12 @@ async function runTuiCommand() {
|
|
|
3391
3461
|
break;
|
|
3392
3462
|
}
|
|
3393
3463
|
|
|
3464
|
+
const rlQuestions = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
3465
|
+
|
|
3394
3466
|
// Ask for backend
|
|
3395
3467
|
let backend = 'yes';
|
|
3396
3468
|
while (true) {
|
|
3397
|
-
const ans = await new Promise(res =>
|
|
3469
|
+
const ans = await new Promise(res => rlQuestions.question('\nDo you need a backend API / server-side logic? (yes/no/unsure) [default: yes]: ', res));
|
|
3398
3470
|
const cleanAns = ans.trim().toLowerCase();
|
|
3399
3471
|
if (cleanAns === '') { backend = 'yes'; break; }
|
|
3400
3472
|
if (['yes', 'no', 'unsure'].includes(cleanAns)) { backend = cleanAns; break; }
|
|
@@ -3404,14 +3476,14 @@ async function runTuiCommand() {
|
|
|
3404
3476
|
// Ask for mobile
|
|
3405
3477
|
let mobile = 'no';
|
|
3406
3478
|
while (true) {
|
|
3407
|
-
const ans = await new Promise(res =>
|
|
3479
|
+
const ans = await new Promise(res => rlQuestions.question('Do you need a mobile app (iOS/Android)? (yes/no) [default: no]: ', res));
|
|
3408
3480
|
const cleanAns = ans.trim().toLowerCase();
|
|
3409
3481
|
if (cleanAns === '') { mobile = 'no'; break; }
|
|
3410
3482
|
if (['yes', 'no'].includes(cleanAns)) { mobile = cleanAns; break; }
|
|
3411
3483
|
console.log('❌ Invalid input. Please enter yes or no.');
|
|
3412
3484
|
}
|
|
3413
3485
|
|
|
3414
|
-
|
|
3486
|
+
rlQuestions.close();
|
|
3415
3487
|
|
|
3416
3488
|
// Write start.md template
|
|
3417
3489
|
const startMdPath = path.join(cwd, START_MD);
|
|
@@ -27,8 +27,13 @@ export declare class CodexCLIAdapter implements EngineAdapter {
|
|
|
27
27
|
readonly name = "codex";
|
|
28
28
|
run(prompt: string, model: string): Promise<string>;
|
|
29
29
|
}
|
|
30
|
+
export declare class CodexFuguCLIAdapter implements EngineAdapter {
|
|
31
|
+
readonly name = "codex-fugu";
|
|
32
|
+
run(prompt: string, model: string): Promise<string>;
|
|
33
|
+
}
|
|
30
34
|
export declare class AgyCLIAdapter implements EngineAdapter {
|
|
31
|
-
readonly name
|
|
35
|
+
readonly name: string;
|
|
36
|
+
constructor(name?: string);
|
|
32
37
|
run(prompt: string, model: string): Promise<string>;
|
|
33
38
|
}
|
|
34
39
|
export declare class QwenCodeCLIAdapter implements EngineAdapter {
|