imhcode 1.1.1 ā 2.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 +18 -0
- package/USER_MANUAL.md +29 -0
- package/bin/imhcode.js +798 -0
- package/dist/orchestrator/builder.d.ts.map +1 -1
- package/dist/orchestrator/builder.js +27 -4
- package/dist/orchestrator/builder.js.map +1 -1
- package/dist/orchestrator/context-scanner.d.ts +23 -0
- package/dist/orchestrator/context-scanner.d.ts.map +1 -0
- package/dist/orchestrator/context-scanner.js +248 -0
- package/dist/orchestrator/context-scanner.js.map +1 -0
- package/dist/orchestrator/import-engine.d.ts +18 -0
- package/dist/orchestrator/import-engine.d.ts.map +1 -0
- package/dist/orchestrator/import-engine.js +116 -0
- package/dist/orchestrator/import-engine.js.map +1 -0
- package/dist/orchestrator/index.d.ts +4 -0
- package/dist/orchestrator/index.d.ts.map +1 -1
- package/dist/orchestrator/index.js +13 -1
- package/dist/orchestrator/index.js.map +1 -1
- package/dist/orchestrator/modification-engine.d.ts +12 -0
- package/dist/orchestrator/modification-engine.d.ts.map +1 -0
- package/dist/orchestrator/modification-engine.js +109 -0
- package/dist/orchestrator/modification-engine.js.map +1 -0
- package/dist/orchestrator/project-scanner.d.ts +16 -0
- package/dist/orchestrator/project-scanner.d.ts.map +1 -0
- package/dist/orchestrator/project-scanner.js +164 -0
- package/dist/orchestrator/project-scanner.js.map +1 -0
- package/package.json +1 -1
- package/skills/typeui-main/test/orchestrator.test.ts +88 -41
- package/src/orchestrator/builder.ts +29 -4
- package/src/orchestrator/context-scanner.ts +221 -0
- package/src/orchestrator/import-engine.ts +106 -0
- package/src/orchestrator/index.ts +8 -0
- package/src/orchestrator/modification-engine.ts +108 -0
- package/src/orchestrator/project-scanner.ts +134 -0
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export interface ModificationOptions {
|
|
2
|
+
agent?: string;
|
|
3
|
+
engine?: string;
|
|
4
|
+
model?: string;
|
|
5
|
+
dryRun?: boolean;
|
|
6
|
+
scope?: string;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Execute a targeted project modification.
|
|
10
|
+
*/
|
|
11
|
+
export declare function runModification(cwd: string, description: string, options?: ModificationOptions): Promise<any>;
|
|
12
|
+
//# sourceMappingURL=modification-engine.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"modification-engine.d.ts","sourceRoot":"","sources":["../../src/orchestrator/modification-engine.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,mBAAmB;IAClC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;GAEG;AACH,wBAAsB,eAAe,CACnC,GAAG,EAAE,MAAM,EACX,WAAW,EAAE,MAAM,EACnB,OAAO,GAAE,mBAAwB,GAChC,OAAO,CAAC,GAAG,CAAC,CAsDd"}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.runModification = runModification;
|
|
37
|
+
const fs = __importStar(require("fs"));
|
|
38
|
+
const path = __importStar(require("path"));
|
|
39
|
+
const context_scanner_1 = require("./context-scanner");
|
|
40
|
+
const registry_1 = require("./registry");
|
|
41
|
+
const index_1 = require("./index");
|
|
42
|
+
/**
|
|
43
|
+
* Execute a targeted project modification.
|
|
44
|
+
*/
|
|
45
|
+
async function runModification(cwd, description, options = {}) {
|
|
46
|
+
// 1. Scan context
|
|
47
|
+
const context = (0, context_scanner_1.scanProjectContext)(cwd);
|
|
48
|
+
// 2. Select agent
|
|
49
|
+
const selectedAgentId = options.agent || (0, context_scanner_1.detectBestAgent)(description, context);
|
|
50
|
+
console.log(`\nš ļø [IMH-Code] Initializing Modification Engine`);
|
|
51
|
+
console.log(` Task: "${description}"`);
|
|
52
|
+
console.log(` Agent: ${selectedAgentId}`);
|
|
53
|
+
// Resolve agent manifests directory
|
|
54
|
+
const globalAgents = path.join(process.env.HOME || "", ".imhcode", "agents");
|
|
55
|
+
const localAgents = path.join(cwd, "agents");
|
|
56
|
+
const pkgAgents = path.join(__dirname, "..", "..", "agents");
|
|
57
|
+
const agentsDir = fs.existsSync(globalAgents) ? globalAgents :
|
|
58
|
+
fs.existsSync(localAgents) ? localAgents :
|
|
59
|
+
pkgAgents;
|
|
60
|
+
// 3. Load Registry
|
|
61
|
+
const { agents, errors } = await (0, registry_1.loadRegistry)(agentsDir, cwd);
|
|
62
|
+
if (errors.some(e => e.agentId === selectedAgentId)) {
|
|
63
|
+
throw new Error(`Failed to load agent "${selectedAgentId}": ${errors.find(e => e.agentId === selectedAgentId)?.error}`);
|
|
64
|
+
}
|
|
65
|
+
const agent = (0, registry_1.getAgent)(agents, selectedAgentId);
|
|
66
|
+
// 4. Build custom modification task
|
|
67
|
+
const modificationTask = (0, context_scanner_1.buildModificationPrompt)(context, description);
|
|
68
|
+
// 5. Run the agent
|
|
69
|
+
const result = await (0, index_1.runAgent)(agent, modificationTask, {
|
|
70
|
+
dryRun: options.dryRun !== false,
|
|
71
|
+
engine: options.engine,
|
|
72
|
+
model: options.model,
|
|
73
|
+
outputDir: path.join(cwd, ".imhcode", "sessions"),
|
|
74
|
+
cwd
|
|
75
|
+
});
|
|
76
|
+
// 6. Log changes if live and successful
|
|
77
|
+
if (!result.dryRun && result.errors.length === 0) {
|
|
78
|
+
logModification(cwd, {
|
|
79
|
+
task: description,
|
|
80
|
+
agentId: selectedAgentId,
|
|
81
|
+
model: result.model,
|
|
82
|
+
durationMs: result.durationMs
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
return result;
|
|
86
|
+
}
|
|
87
|
+
function logModification(cwd, entry) {
|
|
88
|
+
const docsDir = path.join(cwd, "docs");
|
|
89
|
+
if (!fs.existsSync(docsDir)) {
|
|
90
|
+
fs.mkdirSync(docsDir, { recursive: true });
|
|
91
|
+
}
|
|
92
|
+
const logPath = path.join(docsDir, "modifications.md");
|
|
93
|
+
const timestamp = new Date().toISOString().replace("T", " ").substring(0, 19);
|
|
94
|
+
let header = `# š ļø IMH-Code Project Modifications\n\n| Date | Task | Agent | Model | Duration |\n|------|------|-------|-------|----------|\n`;
|
|
95
|
+
if (fs.existsSync(logPath)) {
|
|
96
|
+
try {
|
|
97
|
+
const existing = fs.readFileSync(logPath, "utf-8");
|
|
98
|
+
if (existing.trim().length > 10) {
|
|
99
|
+
header = existing;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
catch {
|
|
103
|
+
// Ignore
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
const row = `| ${timestamp} | ${entry.task} | \`${entry.agentId}\` | ${entry.model} | ${(entry.durationMs / 1000).toFixed(1)}s |\n`;
|
|
107
|
+
fs.writeFileSync(logPath, header.endsWith("\n") ? header + row : header + "\n" + row, "utf-8");
|
|
108
|
+
}
|
|
109
|
+
//# sourceMappingURL=modification-engine.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"modification-engine.js","sourceRoot":"","sources":["../../src/orchestrator/modification-engine.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export interface ScanResult {
|
|
2
|
+
detectedFrontend: string | null;
|
|
3
|
+
detectedBackend: string | null;
|
|
4
|
+
frontendPath: string | null;
|
|
5
|
+
backendPath: string | null;
|
|
6
|
+
database: string | null;
|
|
7
|
+
dockerized: boolean;
|
|
8
|
+
hasCICD: boolean;
|
|
9
|
+
packageJson?: any;
|
|
10
|
+
composerJson?: any;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Deep scan an existing codebase to detect stack and structure.
|
|
14
|
+
*/
|
|
15
|
+
export declare function scanProject(cwd: string): ScanResult;
|
|
16
|
+
//# sourceMappingURL=project-scanner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"project-scanner.d.ts","sourceRoot":"","sources":["../../src/orchestrator/project-scanner.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,UAAU;IACzB,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,UAAU,EAAE,OAAO,CAAC;IACpB,OAAO,EAAE,OAAO,CAAC;IACjB,WAAW,CAAC,EAAE,GAAG,CAAC;IAClB,YAAY,CAAC,EAAE,GAAG,CAAC;CACpB;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,UAAU,CAmHnD"}
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.scanProject = scanProject;
|
|
37
|
+
const fs = __importStar(require("fs"));
|
|
38
|
+
const path = __importStar(require("path"));
|
|
39
|
+
/**
|
|
40
|
+
* Deep scan an existing codebase to detect stack and structure.
|
|
41
|
+
*/
|
|
42
|
+
function scanProject(cwd) {
|
|
43
|
+
const result = {
|
|
44
|
+
detectedFrontend: null,
|
|
45
|
+
detectedBackend: null,
|
|
46
|
+
frontendPath: null,
|
|
47
|
+
backendPath: null,
|
|
48
|
+
database: null,
|
|
49
|
+
dockerized: false,
|
|
50
|
+
hasCICD: false,
|
|
51
|
+
};
|
|
52
|
+
// 1. Helper to search for files recursively with limits
|
|
53
|
+
function findFile(dir, name, depth = 0) {
|
|
54
|
+
if (depth > 3)
|
|
55
|
+
return null;
|
|
56
|
+
try {
|
|
57
|
+
const files = fs.readdirSync(dir);
|
|
58
|
+
if (files.includes(name)) {
|
|
59
|
+
return path.join(dir, name);
|
|
60
|
+
}
|
|
61
|
+
for (const file of files) {
|
|
62
|
+
if (file.startsWith(".") || file === "node_modules" || file === "dist" || file === "vendor")
|
|
63
|
+
continue;
|
|
64
|
+
const fp = path.join(dir, file);
|
|
65
|
+
if (fs.statSync(fp).isDirectory()) {
|
|
66
|
+
const found = findFile(fp, name, depth + 1);
|
|
67
|
+
if (found)
|
|
68
|
+
return found;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
// Ignore
|
|
74
|
+
}
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
// 2. Scan for package.json (Frontend detection)
|
|
78
|
+
const pkgJsonPath = findFile(cwd, "package.json");
|
|
79
|
+
if (pkgJsonPath) {
|
|
80
|
+
try {
|
|
81
|
+
const content = JSON.parse(fs.readFileSync(pkgJsonPath, "utf-8"));
|
|
82
|
+
result.packageJson = content;
|
|
83
|
+
const deps = { ...(content.dependencies || {}), ...(content.devDependencies || {}) };
|
|
84
|
+
if (deps.next)
|
|
85
|
+
result.detectedFrontend = "Next.js";
|
|
86
|
+
else if (deps.nuxt || deps.nuxt3 || deps.nuxt4)
|
|
87
|
+
result.detectedFrontend = "Nuxt/Vue";
|
|
88
|
+
else if (deps.react)
|
|
89
|
+
result.detectedFrontend = "React SPA";
|
|
90
|
+
else if (deps.vue)
|
|
91
|
+
result.detectedFrontend = "Vue SPA";
|
|
92
|
+
else if (deps.svelte)
|
|
93
|
+
result.detectedFrontend = "Svelte";
|
|
94
|
+
else
|
|
95
|
+
result.detectedFrontend = "Node/JS";
|
|
96
|
+
// If package.json is in a subdirectory, use that subdirectory
|
|
97
|
+
const relative = path.relative(cwd, path.dirname(pkgJsonPath));
|
|
98
|
+
result.frontendPath = relative === "" ? "." : relative;
|
|
99
|
+
}
|
|
100
|
+
catch {
|
|
101
|
+
// Ignore
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
// 3. Scan for composer.json, requirements.txt, or pom.xml (Backend detection)
|
|
105
|
+
const composerPath = findFile(cwd, "composer.json");
|
|
106
|
+
if (composerPath) {
|
|
107
|
+
try {
|
|
108
|
+
const content = JSON.parse(fs.readFileSync(composerPath, "utf-8"));
|
|
109
|
+
result.composerJson = content;
|
|
110
|
+
if (content.require && (content.require["laravel/framework"] || content.require["laravel/lumen-framework"])) {
|
|
111
|
+
result.detectedBackend = "Laravel";
|
|
112
|
+
}
|
|
113
|
+
else {
|
|
114
|
+
result.detectedBackend = "PHP";
|
|
115
|
+
}
|
|
116
|
+
const relative = path.relative(cwd, path.dirname(composerPath));
|
|
117
|
+
result.backendPath = relative === "" ? "." : relative;
|
|
118
|
+
}
|
|
119
|
+
catch {
|
|
120
|
+
// Ignore
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
if (!result.detectedBackend) {
|
|
124
|
+
const reqsTxtPath = findFile(cwd, "requirements.txt");
|
|
125
|
+
const pyprojectPath = findFile(cwd, "pyproject.toml");
|
|
126
|
+
if (reqsTxtPath || pyprojectPath) {
|
|
127
|
+
result.detectedBackend = "Python (Django/FastAPI)";
|
|
128
|
+
const matchedPath = reqsTxtPath || pyprojectPath || "";
|
|
129
|
+
const relative = path.relative(cwd, path.dirname(matchedPath));
|
|
130
|
+
result.backendPath = relative === "" ? "." : relative;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
// 4. Database Detection from env or configs
|
|
134
|
+
const envPath = findFile(cwd, ".env") || findFile(cwd, ".env.example");
|
|
135
|
+
if (envPath) {
|
|
136
|
+
try {
|
|
137
|
+
const content = fs.readFileSync(envPath, "utf-8");
|
|
138
|
+
if (content.includes("DB_CONNECTION=pgsql") || content.includes("postgresql://")) {
|
|
139
|
+
result.database = "PostgreSQL";
|
|
140
|
+
}
|
|
141
|
+
else if (content.includes("DB_CONNECTION=mysql") || content.includes("mysql://")) {
|
|
142
|
+
result.database = "MySQL";
|
|
143
|
+
}
|
|
144
|
+
else if (content.includes("DB_CONNECTION=sqlite") || content.includes("sqlite3")) {
|
|
145
|
+
result.database = "SQLite";
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
catch {
|
|
149
|
+
// Ignore
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
// 5. Infrastructure detection
|
|
153
|
+
const dockerCompose = findFile(cwd, "docker-compose.yml");
|
|
154
|
+
const dockerfile = findFile(cwd, "Dockerfile");
|
|
155
|
+
if (dockerCompose || dockerfile) {
|
|
156
|
+
result.dockerized = true;
|
|
157
|
+
}
|
|
158
|
+
const githubWorkflows = findFile(cwd, ".github/workflows");
|
|
159
|
+
if (githubWorkflows) {
|
|
160
|
+
result.hasCICD = true;
|
|
161
|
+
}
|
|
162
|
+
return result;
|
|
163
|
+
}
|
|
164
|
+
//# sourceMappingURL=project-scanner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"project-scanner.js","sourceRoot":"","sources":["../../src/orchestrator/project-scanner.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "imhcode",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"description": "IMH-Code ā Imam Hussain Coding Harness Platform. A fast-first multi-agent AI coding framework with intelligent model routing. 19 generic role-based agents (planner, nextjs-executor, laravel-executor, etc.), configurable testing strategy, and 7 token-saving optimizations for rapid MVP development.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"bin": {
|
|
@@ -3,55 +3,108 @@ import * as fs from "fs";
|
|
|
3
3
|
import * as path from "path";
|
|
4
4
|
import { loadMemoryFiles } from "../../../src/orchestrator/loader";
|
|
5
5
|
import { getRoleKeyForAgent, resolveAgentIdAlias } from "../../../src/orchestrator/index";
|
|
6
|
+
import { scanProjectContext, detectBestAgent } from "../../../src/orchestrator/context-scanner";
|
|
7
|
+
import { scanProject, ScanResult } from "../../../src/orchestrator/project-scanner";
|
|
8
|
+
import { importProject } from "../../../src/orchestrator/import-engine";
|
|
6
9
|
|
|
7
10
|
describe("orchestrator resolveAgentIdAlias", () => {
|
|
8
|
-
it("resolves
|
|
9
|
-
expect(resolveAgentIdAlias("
|
|
10
|
-
expect(resolveAgentIdAlias("
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
expect(resolveAgentIdAlias("
|
|
15
|
-
expect(resolveAgentIdAlias("farhan")).toBe("farhan-marketing");
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
it("resolves other mismatched agent aliases", () => {
|
|
19
|
-
expect(resolveAgentIdAlias("taha-slides")).toBe("taha-presentation");
|
|
20
|
-
expect(resolveAgentIdAlias("sami-spatial")).toBe("sami-computational");
|
|
21
|
-
expect(resolveAgentIdAlias("yahya-phd")).toBe("yahya-researcher");
|
|
22
|
-
expect(resolveAgentIdAlias("maryam-business")).toBe("maryam-ops");
|
|
23
|
-
expect(resolveAgentIdAlias("zainab-product-manager")).toBe("zainab-pm");
|
|
24
|
-
expect(resolveAgentIdAlias("gohar")).toBe("gohar-ceo");
|
|
11
|
+
it("resolves generic agent aliases correctly", () => {
|
|
12
|
+
expect(resolveAgentIdAlias("nextjs")).toBe("nextjs-executor");
|
|
13
|
+
expect(resolveAgentIdAlias("laravel")).toBe("laravel-executor");
|
|
14
|
+
expect(resolveAgentIdAlias("qa")).toBe("tester");
|
|
15
|
+
expect(resolveAgentIdAlias("security")).toBe("security-auditor");
|
|
16
|
+
expect(resolveAgentIdAlias("seo")).toBe("seo-optimizer");
|
|
17
|
+
expect(resolveAgentIdAlias("debug")).toBe("debugger");
|
|
25
18
|
});
|
|
26
19
|
|
|
27
20
|
it("returns canonical ID unchanged", () => {
|
|
28
|
-
expect(resolveAgentIdAlias("
|
|
21
|
+
expect(resolveAgentIdAlias("laravel-executor")).toBe("laravel-executor");
|
|
29
22
|
});
|
|
30
23
|
});
|
|
31
24
|
|
|
32
25
|
describe("orchestrator getRoleKeyForAgent", () => {
|
|
33
|
-
it("resolves
|
|
34
|
-
expect(getRoleKeyForAgent("
|
|
35
|
-
expect(getRoleKeyForAgent("
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
expect(getRoleKeyForAgent("sajjad-debugger", "Fix login bug")).toBe("fast_bug_fixing");
|
|
26
|
+
it("resolves generic executor roles correctly", () => {
|
|
27
|
+
expect(getRoleKeyForAgent("nextjs-executor", "Build a dashboard")).toBe("frontend");
|
|
28
|
+
expect(getRoleKeyForAgent("laravel-executor", "Create API route")).toBe("backend");
|
|
29
|
+
expect(getRoleKeyForAgent("planner", "Define architecture")).toBe("planning");
|
|
30
|
+
expect(getRoleKeyForAgent("tester", "Run E2E tests")).toBe("testing");
|
|
31
|
+
expect(getRoleKeyForAgent("debugger", "Fix authentication issue")).toBe("review");
|
|
40
32
|
});
|
|
33
|
+
});
|
|
41
34
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
expect(getRoleKeyForAgent("mahdi-designer", "Polish the UX layout")).toBe("design_polish_ux_review");
|
|
45
|
-
});
|
|
35
|
+
describe("project scanners and imports", () => {
|
|
36
|
+
const tempDir = path.join(process.cwd(), ".tmp-tests", `import-test-${Date.now()}`);
|
|
46
37
|
|
|
47
|
-
it("
|
|
48
|
-
|
|
49
|
-
|
|
38
|
+
it("scans and imports project structures correctly", () => {
|
|
39
|
+
// 1. Create a dummy Next.js + Laravel project layout
|
|
40
|
+
fs.mkdirSync(tempDir, { recursive: true });
|
|
41
|
+
fs.mkdirSync(path.join(tempDir, "frontend"), { recursive: true });
|
|
42
|
+
fs.mkdirSync(path.join(tempDir, "backend"), { recursive: true });
|
|
43
|
+
|
|
44
|
+
// Write package.json
|
|
45
|
+
fs.writeFileSync(
|
|
46
|
+
path.join(tempDir, "frontend", "package.json"),
|
|
47
|
+
JSON.stringify({
|
|
48
|
+
name: "test-frontend",
|
|
49
|
+
dependencies: {
|
|
50
|
+
next: "^15.0.0",
|
|
51
|
+
react: "^19.0.0"
|
|
52
|
+
}
|
|
53
|
+
})
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
// Write composer.json
|
|
57
|
+
fs.writeFileSync(
|
|
58
|
+
path.join(tempDir, "backend", "composer.json"),
|
|
59
|
+
JSON.stringify({
|
|
60
|
+
name: "laravel/laravel",
|
|
61
|
+
require: {
|
|
62
|
+
"laravel/framework": "^11.0"
|
|
63
|
+
}
|
|
64
|
+
})
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
// Write .env with pgsql database config
|
|
68
|
+
fs.writeFileSync(
|
|
69
|
+
path.join(tempDir, ".env"),
|
|
70
|
+
"DB_CONNECTION=pgsql\nDATABASE_URL=postgresql://localhost:5432/test"
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
// 2. Test project scanner
|
|
74
|
+
const scanResult = scanProject(tempDir);
|
|
75
|
+
expect(scanResult.detectedFrontend).toBe("Next.js");
|
|
76
|
+
expect(scanResult.detectedBackend).toBe("Laravel");
|
|
77
|
+
expect(scanResult.frontendPath).toBe("frontend");
|
|
78
|
+
expect(scanResult.backendPath).toBe("backend");
|
|
79
|
+
expect(scanResult.database).toBe("PostgreSQL");
|
|
80
|
+
|
|
81
|
+
// 3. Test context scanner
|
|
82
|
+
const contextScan = scanProjectContext(tempDir);
|
|
83
|
+
expect(contextScan.hasFrontend).toBe(true);
|
|
84
|
+
expect(contextScan.hasBackend).toBe(true);
|
|
85
|
+
expect(contextScan.frontendFramework).toBe("Next.js");
|
|
86
|
+
expect(contextScan.backendFramework).toBe("Laravel");
|
|
87
|
+
|
|
88
|
+
// 4. Test best agent detection
|
|
89
|
+
expect(detectBestAgent("fix a bug in navbar component", contextScan)).toBe("nextjs-executor");
|
|
90
|
+
expect(detectBestAgent("create a database migration for comments table", contextScan)).toBe("laravel-executor");
|
|
91
|
+
|
|
92
|
+
// 5. Test import execution
|
|
93
|
+
const importResult = importProject(tempDir);
|
|
94
|
+
expect(importResult.success).toBe(true);
|
|
95
|
+
expect(importResult.importMap.frontend).toBe("frontend");
|
|
96
|
+
expect(importResult.importMap.backend).toBe("backend");
|
|
97
|
+
|
|
98
|
+
// Check generated files exist
|
|
99
|
+
expect(fs.existsSync(path.join(tempDir, ".imhcode", "import-map.json"))).toBe(true);
|
|
100
|
+
expect(fs.existsSync(path.join(tempDir, "PROJECT_BRIEF.md"))).toBe(true);
|
|
50
101
|
});
|
|
51
102
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
103
|
+
// Clean up
|
|
104
|
+
afterAll(() => {
|
|
105
|
+
if (fs.existsSync(tempDir)) {
|
|
106
|
+
fs.rmSync(tempDir, { recursive: true, force: true });
|
|
107
|
+
}
|
|
55
108
|
});
|
|
56
109
|
});
|
|
57
110
|
|
|
@@ -59,14 +112,12 @@ describe("loadMemoryFiles exclusions and limits", () => {
|
|
|
59
112
|
const tempDir = path.join(process.cwd(), ".tmp-tests", `mem-test-${Date.now()}`);
|
|
60
113
|
|
|
61
114
|
it("filters out binary files, lockfiles, excluded directories, and large files", async () => {
|
|
62
|
-
// 1. Create temp structures
|
|
63
115
|
fs.mkdirSync(tempDir, { recursive: true });
|
|
64
116
|
fs.mkdirSync(path.join(tempDir, "docs"), { recursive: true });
|
|
65
117
|
fs.mkdirSync(path.join(tempDir, "node_modules"), { recursive: true });
|
|
66
118
|
fs.mkdirSync(path.join(tempDir, "frontend", ".next"), { recursive: true });
|
|
67
119
|
fs.mkdirSync(path.join(tempDir, "backend", "build"), { recursive: true });
|
|
68
120
|
|
|
69
|
-
// 2. Write files
|
|
70
121
|
fs.writeFileSync(path.join(tempDir, "docs", "test.md"), "hello docs");
|
|
71
122
|
fs.writeFileSync(path.join(tempDir, "docs", "info.txt"), "hello info");
|
|
72
123
|
fs.writeFileSync(path.join(tempDir, "node_modules", "bad.js"), "console.log('bad')");
|
|
@@ -75,7 +126,6 @@ describe("loadMemoryFiles exclusions and limits", () => {
|
|
|
75
126
|
fs.writeFileSync(path.join(tempDir, "frontend", "image.png"), "binary png contents");
|
|
76
127
|
fs.writeFileSync(path.join(tempDir, "package-lock.json"), "{}");
|
|
77
128
|
|
|
78
|
-
// Write a file larger than 1MB
|
|
79
129
|
const largeFilePath = path.join(tempDir, "docs", "large.txt");
|
|
80
130
|
const largeContent = "a".repeat(1024 * 1024 + 10);
|
|
81
131
|
fs.writeFileSync(largeFilePath, largeContent);
|
|
@@ -86,10 +136,8 @@ describe("loadMemoryFiles exclusions and limits", () => {
|
|
|
86
136
|
}
|
|
87
137
|
};
|
|
88
138
|
|
|
89
|
-
// 3. Execute loadMemoryFiles
|
|
90
139
|
const files = await loadMemoryFiles(mockManifest, tempDir);
|
|
91
140
|
|
|
92
|
-
// 4. Verify results
|
|
93
141
|
const relativePaths = files.map(f => path.relative(tempDir, f.path).replace(/\\/g, "/"));
|
|
94
142
|
expect(relativePaths).toContain("docs/test.md");
|
|
95
143
|
expect(relativePaths).toContain("docs/info.txt");
|
|
@@ -104,7 +152,6 @@ describe("loadMemoryFiles exclusions and limits", () => {
|
|
|
104
152
|
expect(files.find(f => f.path.endsWith("info.txt"))?.content).toBe("hello info");
|
|
105
153
|
});
|
|
106
154
|
|
|
107
|
-
// Clean up
|
|
108
155
|
afterAll(() => {
|
|
109
156
|
if (fs.existsSync(tempDir)) {
|
|
110
157
|
fs.rmSync(tempDir, { recursive: true, force: true });
|
|
@@ -222,7 +222,32 @@ export function buildPrompt(
|
|
|
222
222
|
}
|
|
223
223
|
|
|
224
224
|
// āā 9. Category-Specific Output Rules āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
225
|
-
|
|
225
|
+
let frontendDir = "frontend/";
|
|
226
|
+
let backendDir = "backend/";
|
|
227
|
+
|
|
228
|
+
if (cwd) {
|
|
229
|
+
const mapPath = path.join(cwd, ".imhcode", "import-map.json");
|
|
230
|
+
if (fs.existsSync(mapPath)) {
|
|
231
|
+
try {
|
|
232
|
+
const importMap = JSON.parse(fs.readFileSync(mapPath, "utf-8"));
|
|
233
|
+
if (importMap.frontend) {
|
|
234
|
+
frontendDir = importMap.frontend === "." ? "project root (./)" : `${importMap.frontend}/`;
|
|
235
|
+
}
|
|
236
|
+
if (importMap.backend) {
|
|
237
|
+
backendDir = importMap.backend === "." ? "project root (./)" : `${importMap.backend}/`;
|
|
238
|
+
}
|
|
239
|
+
} catch {
|
|
240
|
+
// Ignore
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
let categoryRules = CATEGORY_OUTPUT_RULES[category] ?? CATEGORY_OUTPUT_RULES["backend"];
|
|
246
|
+
// Dynamically replace default folder names with actual mapped ones
|
|
247
|
+
categoryRules = categoryRules
|
|
248
|
+
.replace(/`frontend\/`/g, `\`${frontendDir}\``)
|
|
249
|
+
.replace(/`backend\/`/g, `\`${backendDir}\``);
|
|
250
|
+
|
|
226
251
|
const outputLines = [
|
|
227
252
|
`**Category**: ${category}`,
|
|
228
253
|
`**Format**: ${manifest.output.format}`,
|
|
@@ -236,10 +261,10 @@ export function buildPrompt(
|
|
|
236
261
|
|
|
237
262
|
// āā 10. Codebase Containment Rules āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
238
263
|
const containmentRules = [
|
|
239
|
-
`- All frontend source files, styles, assets, and components ā \`
|
|
240
|
-
`- All backend source files, APIs, migrations, schemas, and logic ā \`
|
|
264
|
+
`- All frontend source files, styles, assets, and components ā \`${frontendDir}\` directory ONLY.`,
|
|
265
|
+
`- All backend source files, APIs, migrations, schemas, and logic ā \`${backendDir}\` directory ONLY.`,
|
|
241
266
|
`- All planning and documentation ā \`docs/\` directory ONLY.`,
|
|
242
|
-
`- NEVER create application code at the workspace root
|
|
267
|
+
`- NEVER create application code at the workspace root unless the mapped directory is root (./).`,
|
|
243
268
|
].join("\n");
|
|
244
269
|
parts.push(section("Codebase Containment Rules", containmentRules));
|
|
245
270
|
|