@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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/init/index.ts +173 -5
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mainahq/core",
3
- "version": "1.0.1",
3
+ "version": "1.0.2",
4
4
  "type": "module",
5
5
  "license": "Apache-2.0",
6
6
  "description": "Maina core engines — Context, Prompt, and Verify for verification-first development",
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
- - [NEEDS CLARIFICATION] Define architectural constraints.
420
+ ${archSection}
252
421
 
253
422
  ## Verification
254
- - All commits pass: lint + typecheck + test
255
- - Diff-only: only report findings on changed lines
423
+ ${verifyLines.join("\n")}
256
424
 
257
425
  ## Conventions
258
- - [NEEDS CLARIFICATION] Add project-specific conventions.
426
+ ${conventionLines}
259
427
  `;
260
428
  }
261
429