@mainahq/core 1.0.1 → 1.0.2
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/package.json +1 -1
- package/src/init/index.ts +173 -5
package/package.json
CHANGED
package/src/init/index.ts
CHANGED
|
@@ -41,6 +41,14 @@ export interface DetectedStack {
|
|
|
41
41
|
testRunner: string;
|
|
42
42
|
linter: string;
|
|
43
43
|
framework: string;
|
|
44
|
+
/** package.json scripts (e.g. { test: "vitest", build: "tsc" }) */
|
|
45
|
+
scripts: Record<string, string>;
|
|
46
|
+
/** Build tool detected (e.g. "vite", "webpack", "tsup", "bunup", "esbuild") */
|
|
47
|
+
buildTool: string;
|
|
48
|
+
/** Whether this is a monorepo (workspaces detected) */
|
|
49
|
+
monorepo: boolean;
|
|
50
|
+
/** Inferred conventions from project context */
|
|
51
|
+
conventions: string[];
|
|
44
52
|
}
|
|
45
53
|
|
|
46
54
|
// ── Project Detection ───────────────────────────────────────────────────────
|
|
@@ -53,6 +61,10 @@ function detectStack(repoRoot: string): DetectedStack {
|
|
|
53
61
|
testRunner: "unknown",
|
|
54
62
|
linter: "unknown",
|
|
55
63
|
framework: "none",
|
|
64
|
+
scripts: {},
|
|
65
|
+
buildTool: "unknown",
|
|
66
|
+
monorepo: false,
|
|
67
|
+
conventions: [],
|
|
56
68
|
};
|
|
57
69
|
|
|
58
70
|
// ── Multi-language detection (file-marker based) ─────────────────────
|
|
@@ -118,6 +130,16 @@ function detectStack(repoRoot: string): DetectedStack {
|
|
|
118
130
|
...(pkg.devDependencies as Record<string, string> | undefined),
|
|
119
131
|
...(pkg.peerDependencies as Record<string, string> | undefined),
|
|
120
132
|
};
|
|
133
|
+
|
|
134
|
+
// Extract scripts
|
|
135
|
+
if (pkg.scripts && typeof pkg.scripts === "object") {
|
|
136
|
+
stack.scripts = pkg.scripts as Record<string, string>;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Detect monorepo (workspaces)
|
|
140
|
+
if (pkg.workspaces) {
|
|
141
|
+
stack.monorepo = true;
|
|
142
|
+
}
|
|
121
143
|
} catch {
|
|
122
144
|
// Malformed package.json — skip
|
|
123
145
|
}
|
|
@@ -187,8 +209,108 @@ function detectStack(repoRoot: string): DetectedStack {
|
|
|
187
209
|
} else if (allDeps.svelte) {
|
|
188
210
|
stack.framework = "svelte";
|
|
189
211
|
}
|
|
212
|
+
|
|
213
|
+
// Build tool detection
|
|
214
|
+
if (allDeps.bunup) {
|
|
215
|
+
stack.buildTool = "bunup";
|
|
216
|
+
} else if (allDeps.tsup) {
|
|
217
|
+
stack.buildTool = "tsup";
|
|
218
|
+
} else if (allDeps.vite) {
|
|
219
|
+
stack.buildTool = "vite";
|
|
220
|
+
} else if (allDeps.webpack) {
|
|
221
|
+
stack.buildTool = "webpack";
|
|
222
|
+
} else if (allDeps.esbuild) {
|
|
223
|
+
stack.buildTool = "esbuild";
|
|
224
|
+
} else if (allDeps.rollup) {
|
|
225
|
+
stack.buildTool = "rollup";
|
|
226
|
+
} else if (allDeps.turbo) {
|
|
227
|
+
stack.buildTool = "turborepo";
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// Also check for monorepo tools
|
|
231
|
+
if (
|
|
232
|
+
allDeps.turbo ||
|
|
233
|
+
allDeps.nx ||
|
|
234
|
+
allDeps.lerna ||
|
|
235
|
+
existsSync(join(repoRoot, "pnpm-workspace.yaml"))
|
|
236
|
+
) {
|
|
237
|
+
stack.monorepo = true;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// ── Infer conventions from project context ───────────────────────────
|
|
242
|
+
const conventions: string[] = [];
|
|
243
|
+
|
|
244
|
+
// Check for conventional commits
|
|
245
|
+
if (
|
|
246
|
+
existsSync(join(repoRoot, "commitlint.config.js")) ||
|
|
247
|
+
existsSync(join(repoRoot, "commitlint.config.ts")) ||
|
|
248
|
+
existsSync(join(repoRoot, ".commitlintrc.json")) ||
|
|
249
|
+
existsSync(join(repoRoot, ".commitlintrc.yml"))
|
|
250
|
+
) {
|
|
251
|
+
conventions.push("Conventional commits enforced via commitlint");
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// Check for git hooks
|
|
255
|
+
if (existsSync(join(repoRoot, "lefthook.yml"))) {
|
|
256
|
+
conventions.push("Git hooks via lefthook");
|
|
257
|
+
} else if (existsSync(join(repoRoot, ".husky"))) {
|
|
258
|
+
conventions.push("Git hooks via husky");
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// Check for strict TypeScript
|
|
262
|
+
if (existsSync(join(repoRoot, "tsconfig.json"))) {
|
|
263
|
+
try {
|
|
264
|
+
const tsconfig = readFileSync(join(repoRoot, "tsconfig.json"), "utf-8");
|
|
265
|
+
if (tsconfig.includes('"strict"') && tsconfig.includes("true")) {
|
|
266
|
+
conventions.push("TypeScript strict mode enabled");
|
|
267
|
+
}
|
|
268
|
+
} catch {
|
|
269
|
+
// ignore
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// Check for Docker
|
|
274
|
+
if (
|
|
275
|
+
existsSync(join(repoRoot, "Dockerfile")) ||
|
|
276
|
+
existsSync(join(repoRoot, "docker-compose.yml")) ||
|
|
277
|
+
existsSync(join(repoRoot, "docker-compose.yaml"))
|
|
278
|
+
) {
|
|
279
|
+
conventions.push("Docker containerization");
|
|
190
280
|
}
|
|
191
281
|
|
|
282
|
+
// Check for CI
|
|
283
|
+
if (existsSync(join(repoRoot, ".github/workflows"))) {
|
|
284
|
+
conventions.push("GitHub Actions CI/CD");
|
|
285
|
+
} else if (existsSync(join(repoRoot, ".gitlab-ci.yml"))) {
|
|
286
|
+
conventions.push("GitLab CI/CD");
|
|
287
|
+
} else if (existsSync(join(repoRoot, ".circleci"))) {
|
|
288
|
+
conventions.push("CircleCI");
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
// Check for env management
|
|
292
|
+
if (existsSync(join(repoRoot, ".env.example"))) {
|
|
293
|
+
conventions.push("Environment variables documented in .env.example");
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// Infer from package.json scripts
|
|
297
|
+
if (stack.scripts.lint || stack.scripts["lint:fix"]) {
|
|
298
|
+
conventions.push(
|
|
299
|
+
`Lint command: \`${stack.runtime === "bun" ? "bun" : "npm"} run lint\``,
|
|
300
|
+
);
|
|
301
|
+
}
|
|
302
|
+
if (stack.scripts.test) {
|
|
303
|
+
conventions.push(`Test command: \`${stack.scripts.test}\``);
|
|
304
|
+
}
|
|
305
|
+
if (stack.scripts.build) {
|
|
306
|
+
conventions.push(`Build command: \`${stack.scripts.build}\``);
|
|
307
|
+
}
|
|
308
|
+
if (stack.scripts.typecheck || stack.scripts["type-check"]) {
|
|
309
|
+
conventions.push("Type checking enforced");
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
stack.conventions = conventions;
|
|
313
|
+
|
|
192
314
|
// If no languages detected, mark as unknown
|
|
193
315
|
stack.languages = languages.length > 0 ? languages : ["unknown"];
|
|
194
316
|
|
|
@@ -237,6 +359,53 @@ function buildConstitution(stack: DetectedStack): string {
|
|
|
237
359
|
const frameworkLine =
|
|
238
360
|
stack.framework !== "none" ? `- Framework: ${stack.framework}\n` : "";
|
|
239
361
|
|
|
362
|
+
const buildLine =
|
|
363
|
+
stack.buildTool !== "unknown" ? `- Build: ${stack.buildTool}\n` : "";
|
|
364
|
+
|
|
365
|
+
const monorepoLine = stack.monorepo ? "- Monorepo: yes (workspaces)\n" : "";
|
|
366
|
+
|
|
367
|
+
// Build architecture section from context
|
|
368
|
+
const archLines: string[] = [];
|
|
369
|
+
if (stack.monorepo) {
|
|
370
|
+
archLines.push("- Monorepo with shared packages");
|
|
371
|
+
}
|
|
372
|
+
if (stack.framework !== "none") {
|
|
373
|
+
archLines.push(`- ${stack.framework} application`);
|
|
374
|
+
}
|
|
375
|
+
if (stack.languages.length > 1) {
|
|
376
|
+
archLines.push(`- Multi-language: ${stack.languages.join(", ")}`);
|
|
377
|
+
}
|
|
378
|
+
const archSection =
|
|
379
|
+
archLines.length > 0
|
|
380
|
+
? archLines.join("\n")
|
|
381
|
+
: "- [NEEDS CLARIFICATION] Define architectural constraints.";
|
|
382
|
+
|
|
383
|
+
// Build verification section from scripts
|
|
384
|
+
const verifyLines: string[] = [];
|
|
385
|
+
const runCmd = stack.runtime === "bun" ? "bun" : "npm";
|
|
386
|
+
if (stack.scripts.lint || stack.linter !== "unknown") {
|
|
387
|
+
verifyLines.push(
|
|
388
|
+
`- Lint: \`${stack.scripts.lint ?? `${runCmd} run lint`}\``,
|
|
389
|
+
);
|
|
390
|
+
}
|
|
391
|
+
if (stack.language === "typescript") {
|
|
392
|
+
verifyLines.push(
|
|
393
|
+
`- Typecheck: \`${stack.scripts.typecheck ?? stack.scripts["type-check"] ?? `${runCmd} run typecheck`}\``,
|
|
394
|
+
);
|
|
395
|
+
}
|
|
396
|
+
if (stack.scripts.test) {
|
|
397
|
+
verifyLines.push(`- Test: \`${stack.scripts.test}\``);
|
|
398
|
+
} else if (stack.testRunner !== "unknown") {
|
|
399
|
+
verifyLines.push(`- Test: \`${runCmd} test\``);
|
|
400
|
+
}
|
|
401
|
+
verifyLines.push("- Diff-only: only report findings on changed lines");
|
|
402
|
+
|
|
403
|
+
// Build conventions section from detected conventions
|
|
404
|
+
const conventionLines =
|
|
405
|
+
stack.conventions.length > 0
|
|
406
|
+
? stack.conventions.map((c) => `- ${c}`).join("\n")
|
|
407
|
+
: "- [NEEDS CLARIFICATION] Add project-specific conventions.";
|
|
408
|
+
|
|
240
409
|
return `# Project Constitution
|
|
241
410
|
|
|
242
411
|
Non-negotiable rules. Injected into every AI call.
|
|
@@ -246,16 +415,15 @@ ${runtimeLine}
|
|
|
246
415
|
${langLine}
|
|
247
416
|
${lintLine}
|
|
248
417
|
${testLine}
|
|
249
|
-
${frameworkLine}
|
|
418
|
+
${frameworkLine}${buildLine}${monorepoLine}
|
|
250
419
|
## Architecture
|
|
251
|
-
|
|
420
|
+
${archSection}
|
|
252
421
|
|
|
253
422
|
## Verification
|
|
254
|
-
|
|
255
|
-
- Diff-only: only report findings on changed lines
|
|
423
|
+
${verifyLines.join("\n")}
|
|
256
424
|
|
|
257
425
|
## Conventions
|
|
258
|
-
|
|
426
|
+
${conventionLines}
|
|
259
427
|
`;
|
|
260
428
|
}
|
|
261
429
|
|