@mindees/cli 0.1.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.
Files changed (56) hide show
  1. package/LICENSE +31 -0
  2. package/README.md +70 -0
  3. package/dist/ai.d.ts +15 -0
  4. package/dist/ai.d.ts.map +1 -0
  5. package/dist/ai.js +57 -0
  6. package/dist/ai.js.map +1 -0
  7. package/dist/bin.d.ts +1 -0
  8. package/dist/bin.js +94 -0
  9. package/dist/bin.js.map +1 -0
  10. package/dist/build.d.ts +37 -0
  11. package/dist/build.d.ts.map +1 -0
  12. package/dist/build.js +97 -0
  13. package/dist/build.js.map +1 -0
  14. package/dist/cli.d.ts +32 -0
  15. package/dist/cli.d.ts.map +1 -0
  16. package/dist/cli.js +194 -0
  17. package/dist/cli.js.map +1 -0
  18. package/dist/create-target.d.ts +36 -0
  19. package/dist/create-target.d.ts.map +1 -0
  20. package/dist/create-target.js +90 -0
  21. package/dist/create-target.js.map +1 -0
  22. package/dist/dev.d.ts +32 -0
  23. package/dist/dev.d.ts.map +1 -0
  24. package/dist/dev.js +64 -0
  25. package/dist/dev.js.map +1 -0
  26. package/dist/doctor.d.ts +15 -0
  27. package/dist/doctor.d.ts.map +1 -0
  28. package/dist/doctor.js +92 -0
  29. package/dist/doctor.js.map +1 -0
  30. package/dist/fs.d.ts +28 -0
  31. package/dist/fs.d.ts.map +1 -0
  32. package/dist/fs.js +39 -0
  33. package/dist/fs.js.map +1 -0
  34. package/dist/index.d.ts +34 -0
  35. package/dist/index.d.ts.map +1 -0
  36. package/dist/index.js +37 -0
  37. package/dist/index.js.map +1 -0
  38. package/dist/nl.d.ts +26 -0
  39. package/dist/nl.d.ts.map +1 -0
  40. package/dist/nl.js +49 -0
  41. package/dist/nl.js.map +1 -0
  42. package/dist/scaffold.d.ts +32 -0
  43. package/dist/scaffold.d.ts.map +1 -0
  44. package/dist/scaffold.js +52 -0
  45. package/dist/scaffold.js.map +1 -0
  46. package/dist/templates.d.ts +32 -0
  47. package/dist/templates.d.ts.map +1 -0
  48. package/dist/templates.js +131 -0
  49. package/dist/templates.js.map +1 -0
  50. package/dist/types.d.ts +54 -0
  51. package/dist/types.d.ts.map +1 -0
  52. package/dist/version.d.ts +17 -0
  53. package/dist/version.d.ts.map +1 -0
  54. package/dist/version.js +18 -0
  55. package/dist/version.js.map +1 -0
  56. package/package.json +37 -0
package/LICENSE ADDED
@@ -0,0 +1,31 @@
1
+ # License
2
+
3
+ MindeesNative is dual-licensed under either of:
4
+
5
+ - **Apache License, Version 2.0** ([LICENSE-APACHE](./LICENSE-APACHE) or
6
+ <https://www.apache.org/licenses/LICENSE-2.0>)
7
+ - **MIT license** ([LICENSE-MIT](./LICENSE-MIT) or
8
+ <https://opensource.org/licenses/MIT>)
9
+
10
+ at your option.
11
+
12
+ This `MIT OR Apache-2.0` dual-license is the same model used by the Rust
13
+ ecosystem and many modern open-source projects. It gives downstream users
14
+ maximum flexibility: the MIT option is short and permissive, while the Apache
15
+ option adds an explicit patent grant.
16
+
17
+ ## SPDX identifier
18
+
19
+ ```
20
+ SPDX-License-Identifier: MIT OR Apache-2.0
21
+ ```
22
+
23
+ ## Contribution
24
+
25
+ Unless you explicitly state otherwise, any contribution intentionally
26
+ submitted for inclusion in the work by you, as defined in the Apache-2.0
27
+ license, shall be dual-licensed as above, without any additional terms or
28
+ conditions.
29
+
30
+ Contributions are accepted under the **Developer Certificate of Origin (DCO)**.
31
+ See [CONTRIBUTING.md](./CONTRIBUTING.md) for details on signing off your commits.
package/README.md ADDED
@@ -0,0 +1,70 @@
1
+ # @mindees/cli
2
+
3
+ **Forge** โ€” the `mindees` command-line interface. Scaffold apps, type-check +
4
+ build them with the Mindees Compiler, and diagnose your environment. Zero
5
+ third-party dependencies (built on `node:util` `parseArgs`).
6
+
7
+ > **Status: ๐Ÿงช Experimental (Phase 5).** `create`, `build`, `doctor`, `info`,
8
+ > and the dev **rebuild orchestrator** are implemented and tested. The live
9
+ > dev-server **HTTP/HMR transport** is a developer preview. On-device NLโ†’app
10
+ > generation is Phase 10 โ€” today `--prompt` maps to a template deterministically.
11
+
12
+ ## Commands
13
+
14
+ ```bash
15
+ mindees create <name-or-path> [--template blank|counter] [--prompt "..."] [--force]
16
+ mindees build [--out-dir <dir>] [--no-source-map]
17
+ mindees doctor # diagnose Node / package manager / project / deps
18
+ mindees info # CLI + environment info
19
+ mindees dev # rebuild-on-change (developer preview)
20
+ mindees help
21
+ ```
22
+
23
+ ```text
24
+ $ mindees create my-app --template counter
25
+ Created "my-app" from the counter template (6 files).
26
+ Next: cd "my-app" && pnpm install && mindees dev
27
+
28
+ $ mindees build
29
+ Built 3 module(s); flattened 4/9 elements.
30
+
31
+ $ mindees doctor
32
+ โœ“ Node.js: v24.7.0
33
+ โœ“ Package manager: pnpm 11.5.0
34
+ ! Dependencies: node_modules missing
35
+ โ†’ Install dependencies: `pnpm install` (or `npm exec --yes --package=pnpm@11.5.0 -- pnpm install` if the pnpm shim is unavailable).
36
+ ```
37
+
38
+ ## Design: a testable core, a thin shell
39
+
40
+ Every command is a **pure function** of injected capabilities โ€” a `FileSystem`,
41
+ an `EnvProbe`, and a `Writer` โ€” so the entire CLI is deterministic in tests. The
42
+ `mindees` executable (`bin`) is a thin adapter that wires real `node:fs` /
43
+ `process` into `runCli`. Highlights:
44
+
45
+ - **`scaffold`** โ€” writes a template through the injected FS (templates are
46
+ in-memory; no on-disk fixtures). Refuses a non-empty target without `--force`.
47
+ - **`resolveCreateTarget`** โ€” normalizes simple names, relative paths, and
48
+ absolute Windows/POSIX paths into a target directory plus npm-safe package
49
+ name, so path separators never leak into generated `package.json` files.
50
+ - **`buildProject`** โ€” compiles `src/**` via `@mindees/compiler` (the type-check
51
+ gate + emit), writing `dist/` and a `routes.manifest.json` when `src/routes/`
52
+ exists. Genuine type errors fail the build; isolation-only diagnostics
53
+ (unresolved cross-module imports, missing ambient JSX types) are reported as
54
+ warnings (full project-graph checking is future work).
55
+ - **`runDoctor`** โ€” environment checks with actionable fixes, over an injected
56
+ `EnvProbe`.
57
+ - **`startDev`** โ€” the rebuild orchestrator: builds once, rebuilds on each
58
+ `Watcher` change, tracks build count + last result. The watcher and HMR
59
+ transport are injected (the live server is the preview layer).
60
+
61
+ ## Library API
62
+
63
+ Forge's internals are exported so other tooling (and `create-mindees`) can reuse
64
+ them: `runCli`, `scaffold`, `buildProject`, `startDev`, `runDoctor`,
65
+ `resolveCreateTarget`, `createMemoryFileSystem`, `TEMPLATES`,
66
+ `naturalLanguageToTemplate`, and their types.
67
+
68
+ ## License
69
+
70
+ `MIT OR Apache-2.0`
package/dist/ai.d.ts ADDED
@@ -0,0 +1,15 @@
1
+ import { CommandResult, Writer } from "./types.js";
2
+ import { AiBackend } from "@mindees/ai";
3
+
4
+ //#region src/ai.d.ts
5
+ /** What `runAiCommand` needs (injected for testability). */
6
+ interface AiCommandContext {
7
+ write: Writer;
8
+ /** The AI backend; absent when no `MINDEES_AI_*` env is configured. */
9
+ backend?: AiBackend;
10
+ }
11
+ /** Run an `ai` subcommand. Async because it talks to a model. */
12
+ declare function runAiCommand(args: readonly string[], ctx: AiCommandContext): Promise<CommandResult>;
13
+ //#endregion
14
+ export { AiCommandContext, runAiCommand };
15
+ //# sourceMappingURL=ai.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ai.d.ts","names":[],"sources":["../src/ai.ts"],"mappings":";;;;;UAeiB,gBAAA;EACf,KAAA,EAAO,MAAA;EAwBa;EAtBpB,OAAA,GAAU,SAAS;AAAA;;iBAsBC,YAAA,CACpB,IAAA,qBACA,GAAA,EAAK,gBAAA,GACJ,OAAA,CAAQ,aAAA"}
package/dist/ai.js ADDED
@@ -0,0 +1,57 @@
1
+ import { explainError, formatExplanation } from "@mindees/ai/devtools";
2
+ //#region src/ai.ts
3
+ const AI_HELP = `mindees ai โ€” dev-time AI helpers
4
+
5
+ Usage:
6
+ mindees ai explain <error message...> Explain an error and suggest fixes
7
+
8
+ Configure a backend with environment variables:
9
+ MINDEES_AI_BASE_URL e.g. https://api.openai.com/v1
10
+ MINDEES_AI_MODEL e.g. gpt-4o-mini
11
+ MINDEES_AI_API_KEY your key
12
+ MINDEES_AI_ADAPTER openai (default) | anthropic`;
13
+ function out(write, text) {
14
+ write({
15
+ stream: "out",
16
+ text
17
+ });
18
+ }
19
+ function err(write, text) {
20
+ write({
21
+ stream: "err",
22
+ text
23
+ });
24
+ }
25
+ /** Run an `ai` subcommand. Async because it talks to a model. */
26
+ async function runAiCommand(args, ctx) {
27
+ const [sub, ...rest] = args;
28
+ if (!sub || sub === "help" || sub === "--help" || sub === "-h") {
29
+ out(ctx.write, AI_HELP);
30
+ return { exitCode: 0 };
31
+ }
32
+ if (sub !== "explain") {
33
+ err(ctx.write, `Unknown ai command "${sub}". Try \`mindees ai explain <error>\`.`);
34
+ return { exitCode: 1 };
35
+ }
36
+ const message = rest.join(" ").trim();
37
+ if (!message) {
38
+ err(ctx.write, "ai explain: provide an error message. Usage: mindees ai explain <error>");
39
+ return { exitCode: 1 };
40
+ }
41
+ if (!ctx.backend) {
42
+ err(ctx.write, "No AI backend configured. Set MINDEES_AI_BASE_URL and MINDEES_AI_MODEL (and MINDEES_AI_API_KEY if your provider needs one).");
43
+ return { exitCode: 1 };
44
+ }
45
+ try {
46
+ const explanation = await explainError(ctx.backend, { message });
47
+ out(ctx.write, formatExplanation(explanation));
48
+ return { exitCode: 0 };
49
+ } catch (e) {
50
+ err(ctx.write, `ai explain failed: ${e instanceof Error ? e.message : String(e)}`);
51
+ return { exitCode: 1 };
52
+ }
53
+ }
54
+ //#endregion
55
+ export { runAiCommand };
56
+
57
+ //# sourceMappingURL=ai.js.map
package/dist/ai.js.map ADDED
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ai.js","names":[],"sources":["../src/ai.ts"],"sourcesContent":["/**\n * `mindees ai` โ€” dev-time AI helpers. Currently `ai explain <error>`, which runs Synapse's\n * {@link explainError} (from `@mindees/ai/devtools`) over an injected backend and prints a\n * structured explanation. Like every command, it's a pure function of injected capabilities\n * (the backend + writer), so it's deterministically testable with the mock backend; the real\n * `bin` wires a server backend from environment variables.\n *\n * @module\n */\n\nimport type { AiBackend } from '@mindees/ai'\nimport { explainError, formatExplanation } from '@mindees/ai/devtools'\nimport type { CommandResult, Writer } from './types'\n\n/** What `runAiCommand` needs (injected for testability). */\nexport interface AiCommandContext {\n write: Writer\n /** The AI backend; absent when no `MINDEES_AI_*` env is configured. */\n backend?: AiBackend\n}\n\nconst AI_HELP = `mindees ai โ€” dev-time AI helpers\n\nUsage:\n mindees ai explain <error message...> Explain an error and suggest fixes\n\nConfigure a backend with environment variables:\n MINDEES_AI_BASE_URL e.g. https://api.openai.com/v1\n MINDEES_AI_MODEL e.g. gpt-4o-mini\n MINDEES_AI_API_KEY your key\n MINDEES_AI_ADAPTER openai (default) | anthropic`\n\nfunction out(write: Writer, text: string): void {\n write({ stream: 'out', text })\n}\nfunction err(write: Writer, text: string): void {\n write({ stream: 'err', text })\n}\n\n/** Run an `ai` subcommand. Async because it talks to a model. */\nexport async function runAiCommand(\n args: readonly string[],\n ctx: AiCommandContext,\n): Promise<CommandResult> {\n const [sub, ...rest] = args\n\n if (!sub || sub === 'help' || sub === '--help' || sub === '-h') {\n out(ctx.write, AI_HELP)\n return { exitCode: 0 }\n }\n if (sub !== 'explain') {\n err(ctx.write, `Unknown ai command \"${sub}\". Try \\`mindees ai explain <error>\\`.`)\n return { exitCode: 1 }\n }\n\n const message = rest.join(' ').trim()\n if (!message) {\n err(ctx.write, 'ai explain: provide an error message. Usage: mindees ai explain <error>')\n return { exitCode: 1 }\n }\n if (!ctx.backend) {\n err(\n ctx.write,\n 'No AI backend configured. Set MINDEES_AI_BASE_URL and MINDEES_AI_MODEL (and MINDEES_AI_API_KEY if your provider needs one).',\n )\n return { exitCode: 1 }\n }\n\n try {\n const explanation = await explainError(ctx.backend, { message })\n out(ctx.write, formatExplanation(explanation))\n return { exitCode: 0 }\n } catch (e) {\n err(ctx.write, `ai explain failed: ${e instanceof Error ? e.message : String(e)}`)\n return { exitCode: 1 }\n }\n}\n"],"mappings":";;AAqBA,MAAM,UAAU;;;;;;;;;;AAWhB,SAAS,IAAI,OAAe,MAAoB;CAC9C,MAAM;EAAE,QAAQ;EAAO;CAAK,CAAC;AAC/B;AACA,SAAS,IAAI,OAAe,MAAoB;CAC9C,MAAM;EAAE,QAAQ;EAAO;CAAK,CAAC;AAC/B;;AAGA,eAAsB,aACpB,MACA,KACwB;CACxB,MAAM,CAAC,KAAK,GAAG,QAAQ;CAEvB,IAAI,CAAC,OAAO,QAAQ,UAAU,QAAQ,YAAY,QAAQ,MAAM;EAC9D,IAAI,IAAI,OAAO,OAAO;EACtB,OAAO,EAAE,UAAU,EAAE;CACvB;CACA,IAAI,QAAQ,WAAW;EACrB,IAAI,IAAI,OAAO,uBAAuB,IAAI,uCAAuC;EACjF,OAAO,EAAE,UAAU,EAAE;CACvB;CAEA,MAAM,UAAU,KAAK,KAAK,GAAG,EAAE,KAAK;CACpC,IAAI,CAAC,SAAS;EACZ,IAAI,IAAI,OAAO,yEAAyE;EACxF,OAAO,EAAE,UAAU,EAAE;CACvB;CACA,IAAI,CAAC,IAAI,SAAS;EAChB,IACE,IAAI,OACJ,6HACF;EACA,OAAO,EAAE,UAAU,EAAE;CACvB;CAEA,IAAI;EACF,MAAM,cAAc,MAAM,aAAa,IAAI,SAAS,EAAE,QAAQ,CAAC;EAC/D,IAAI,IAAI,OAAO,kBAAkB,WAAW,CAAC;EAC7C,OAAO,EAAE,UAAU,EAAE;CACvB,SAAS,GAAG;EACV,IAAI,IAAI,OAAO,sBAAsB,aAAa,QAAQ,EAAE,UAAU,OAAO,CAAC,GAAG;EACjF,OAAO,EAAE,UAAU,EAAE;CACvB;AACF"}
package/dist/bin.d.ts ADDED
@@ -0,0 +1 @@
1
+ export { };
package/dist/bin.js ADDED
@@ -0,0 +1,94 @@
1
+ #!/usr/bin/env node
2
+ import { VERSION } from "./version.js";
3
+ import { runCliAsync } from "./cli.js";
4
+ import { existsSync, mkdirSync, readFileSync, readdirSync, statSync, writeFileSync } from "node:fs";
5
+ import { dirname, join, relative, sep } from "node:path";
6
+ import process from "node:process";
7
+ import { createServerBackend } from "@mindees/ai/server";
8
+ //#region src/bin.ts
9
+ /**
10
+ * The `mindees` executable โ€” a thin adapter that wires real Node capabilities
11
+ * (filesystem, environment probe, stdout/stderr, AI backend) into the tested
12
+ * {@link runCliAsync} core. All logic lives in the core; this file only does I/O wiring.
13
+ *
14
+ * @module
15
+ */
16
+ /** A `node:fs`-backed {@link FileSystem}. */
17
+ function nodeFileSystem() {
18
+ return {
19
+ exists: (path) => existsSync(path),
20
+ readFile: (path) => readFileSync(path, "utf8"),
21
+ writeFile: (path, contents) => {
22
+ mkdirSync(dirname(path), { recursive: true });
23
+ writeFileSync(path, contents, "utf8");
24
+ },
25
+ mkdir: (path) => {
26
+ mkdirSync(path, { recursive: true });
27
+ },
28
+ readDir: (dir) => {
29
+ const walk = (current, acc) => {
30
+ if (!existsSync(current)) return acc;
31
+ for (const entry of readdirSync(current)) {
32
+ const full = join(current, entry);
33
+ if (statSync(full).isDirectory()) walk(full, acc);
34
+ else acc.push(relative(dir, full).split(sep).join("/"));
35
+ }
36
+ return acc;
37
+ };
38
+ return walk(dir, []).sort();
39
+ }
40
+ };
41
+ }
42
+ /** Probe the real environment for `doctor`/`info`. */
43
+ function probeEnv(cwd) {
44
+ const pmMatch = (process.env.npm_config_user_agent ?? "").match(/^(\w+)\/(\S+)/);
45
+ return {
46
+ nodeVersion: process.version,
47
+ packageManager: pmMatch?.[1] && pmMatch[2] ? {
48
+ name: pmMatch[1],
49
+ version: pmMatch[2]
50
+ } : null,
51
+ hasPackageJson: existsSync(join(cwd, "package.json")),
52
+ hasNodeModules: existsSync(join(cwd, "node_modules"))
53
+ };
54
+ }
55
+ /** Build a server AI backend from `MINDEES_AI_*` env, or `undefined` if not configured. */
56
+ function aiBackendFromEnv() {
57
+ const baseUrl = process.env.MINDEES_AI_BASE_URL;
58
+ const model = process.env.MINDEES_AI_MODEL;
59
+ if (!baseUrl || !model) return void 0;
60
+ const adapterEnv = process.env.MINDEES_AI_ADAPTER;
61
+ if (adapterEnv && adapterEnv !== "openai" && adapterEnv !== "anthropic") {
62
+ process.stderr.write(`mindees: unknown MINDEES_AI_ADAPTER "${adapterEnv}" (expected "openai" or "anthropic"); AI backend not configured.\n`);
63
+ return;
64
+ }
65
+ const adapter = adapterEnv === "anthropic" ? "anthropic" : "openai";
66
+ return createServerBackend({
67
+ fetch: globalThis.fetch,
68
+ baseUrl,
69
+ model,
70
+ adapter,
71
+ ...process.env.MINDEES_AI_API_KEY ? { apiKey: process.env.MINDEES_AI_API_KEY } : {}
72
+ });
73
+ }
74
+ async function main() {
75
+ const cwd = process.cwd();
76
+ const write = (line) => {
77
+ (line.stream === "err" ? process.stderr : process.stdout).write(`${line.text}\n`);
78
+ };
79
+ const backend = aiBackendFromEnv();
80
+ const ctx = {
81
+ fs: nodeFileSystem(),
82
+ env: probeEnv(cwd),
83
+ cwd,
84
+ version: VERSION,
85
+ write,
86
+ ...backend ? { aiBackend: backend } : {}
87
+ };
88
+ const { exitCode } = await runCliAsync(process.argv.slice(2), ctx);
89
+ process.exitCode = exitCode;
90
+ }
91
+ main();
92
+ //#endregion
93
+
94
+ //# sourceMappingURL=bin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bin.js","names":[],"sources":["../src/bin.ts"],"sourcesContent":["#!/usr/bin/env node\n\n/**\n * The `mindees` executable โ€” a thin adapter that wires real Node capabilities\n * (filesystem, environment probe, stdout/stderr, AI backend) into the tested\n * {@link runCliAsync} core. All logic lives in the core; this file only does I/O wiring.\n *\n * @module\n */\n\nimport { existsSync, mkdirSync, readdirSync, readFileSync, statSync, writeFileSync } from 'node:fs'\nimport { dirname, join, relative, sep } from 'node:path'\nimport process from 'node:process'\nimport type { AiBackend } from '@mindees/ai'\nimport { type AdapterName, createServerBackend, type FetchLike } from '@mindees/ai/server'\nimport { type CliContext, runCliAsync } from './cli'\nimport type { FileSystem } from './fs'\nimport { VERSION } from './index'\nimport type { EnvProbe, OutputLine } from './types'\n\n/** A `node:fs`-backed {@link FileSystem}. */\nfunction nodeFileSystem(): FileSystem {\n return {\n exists: (path) => existsSync(path),\n readFile: (path) => readFileSync(path, 'utf8'),\n writeFile: (path, contents) => {\n mkdirSync(dirname(path), { recursive: true })\n writeFileSync(path, contents, 'utf8')\n },\n mkdir: (path) => {\n mkdirSync(path, { recursive: true })\n },\n readDir: (dir) => {\n const walk = (current: string, acc: string[]): string[] => {\n if (!existsSync(current)) return acc\n for (const entry of readdirSync(current)) {\n const full = join(current, entry)\n if (statSync(full).isDirectory()) walk(full, acc)\n else acc.push(relative(dir, full).split(sep).join('/'))\n }\n return acc\n }\n return walk(dir, []).sort()\n },\n }\n}\n\n/** Probe the real environment for `doctor`/`info`. */\nfunction probeEnv(cwd: string): EnvProbe {\n const pmSpec = process.env.npm_config_user_agent ?? ''\n // user agent looks like \"pnpm/11.5.0 npm/? node/v24 ...\".\n const pmMatch = pmSpec.match(/^(\\w+)\\/(\\S+)/)\n return {\n nodeVersion: process.version,\n packageManager: pmMatch?.[1] && pmMatch[2] ? { name: pmMatch[1], version: pmMatch[2] } : null,\n hasPackageJson: existsSync(join(cwd, 'package.json')),\n hasNodeModules: existsSync(join(cwd, 'node_modules')),\n }\n}\n\n/** Build a server AI backend from `MINDEES_AI_*` env, or `undefined` if not configured. */\nfunction aiBackendFromEnv(): AiBackend | undefined {\n const baseUrl = process.env.MINDEES_AI_BASE_URL\n const model = process.env.MINDEES_AI_MODEL\n if (!baseUrl || !model) return undefined\n // Fail loud on a mistyped adapter rather than silently defaulting to openai (which would\n // build the wrong auth headers and yield confusing HTTP errors). Empty/unset โ†’ openai.\n const adapterEnv = process.env.MINDEES_AI_ADAPTER\n if (adapterEnv && adapterEnv !== 'openai' && adapterEnv !== 'anthropic') {\n process.stderr.write(\n `mindees: unknown MINDEES_AI_ADAPTER \"${adapterEnv}\" (expected \"openai\" or \"anthropic\"); AI backend not configured.\\n`,\n )\n return undefined\n }\n const adapter: AdapterName = adapterEnv === 'anthropic' ? 'anthropic' : 'openai'\n return createServerBackend({\n // The global `fetch` is structurally compatible at runtime; the minimal FetchLike\n // intentionally avoids the DOM lib, so cast rather than pull in those types.\n fetch: globalThis.fetch as unknown as FetchLike,\n baseUrl,\n model,\n adapter,\n ...(process.env.MINDEES_AI_API_KEY ? { apiKey: process.env.MINDEES_AI_API_KEY } : {}),\n })\n}\n\nasync function main(): Promise<void> {\n const cwd = process.cwd()\n const write = (line: OutputLine): void => {\n const stream = line.stream === 'err' ? process.stderr : process.stdout\n stream.write(`${line.text}\\n`)\n }\n const backend = aiBackendFromEnv()\n const ctx: CliContext = {\n fs: nodeFileSystem(),\n env: probeEnv(cwd),\n cwd,\n version: VERSION,\n write,\n ...(backend ? { aiBackend: backend } : {}),\n }\n const { exitCode } = await runCliAsync(process.argv.slice(2), ctx)\n process.exitCode = exitCode\n}\n\nmain()\n"],"mappings":";;;;;;;;;;;;;;;;AAqBA,SAAS,iBAA6B;CACpC,OAAO;EACL,SAAS,SAAS,WAAW,IAAI;EACjC,WAAW,SAAS,aAAa,MAAM,MAAM;EAC7C,YAAY,MAAM,aAAa;GAC7B,UAAU,QAAQ,IAAI,GAAG,EAAE,WAAW,KAAK,CAAC;GAC5C,cAAc,MAAM,UAAU,MAAM;EACtC;EACA,QAAQ,SAAS;GACf,UAAU,MAAM,EAAE,WAAW,KAAK,CAAC;EACrC;EACA,UAAU,QAAQ;GAChB,MAAM,QAAQ,SAAiB,QAA4B;IACzD,IAAI,CAAC,WAAW,OAAO,GAAG,OAAO;IACjC,KAAK,MAAM,SAAS,YAAY,OAAO,GAAG;KACxC,MAAM,OAAO,KAAK,SAAS,KAAK;KAChC,IAAI,SAAS,IAAI,EAAE,YAAY,GAAG,KAAK,MAAM,GAAG;UAC3C,IAAI,KAAK,SAAS,KAAK,IAAI,EAAE,MAAM,GAAG,EAAE,KAAK,GAAG,CAAC;IACxD;IACA,OAAO;GACT;GACA,OAAO,KAAK,KAAK,CAAC,CAAC,EAAE,KAAK;EAC5B;CACF;AACF;;AAGA,SAAS,SAAS,KAAuB;CAGvC,MAAM,WAFS,QAAQ,IAAI,yBAAyB,IAE7B,MAAM,eAAe;CAC5C,OAAO;EACL,aAAa,QAAQ;EACrB,gBAAgB,UAAU,MAAM,QAAQ,KAAK;GAAE,MAAM,QAAQ;GAAI,SAAS,QAAQ;EAAG,IAAI;EACzF,gBAAgB,WAAW,KAAK,KAAK,cAAc,CAAC;EACpD,gBAAgB,WAAW,KAAK,KAAK,cAAc,CAAC;CACtD;AACF;;AAGA,SAAS,mBAA0C;CACjD,MAAM,UAAU,QAAQ,IAAI;CAC5B,MAAM,QAAQ,QAAQ,IAAI;CAC1B,IAAI,CAAC,WAAW,CAAC,OAAO,OAAO,KAAA;CAG/B,MAAM,aAAa,QAAQ,IAAI;CAC/B,IAAI,cAAc,eAAe,YAAY,eAAe,aAAa;EACvE,QAAQ,OAAO,MACb,wCAAwC,WAAW,mEACrD;EACA;CACF;CACA,MAAM,UAAuB,eAAe,cAAc,cAAc;CACxE,OAAO,oBAAoB;EAGzB,OAAO,WAAW;EAClB;EACA;EACA;EACA,GAAI,QAAQ,IAAI,qBAAqB,EAAE,QAAQ,QAAQ,IAAI,mBAAmB,IAAI,CAAC;CACrF,CAAC;AACH;AAEA,eAAe,OAAsB;CACnC,MAAM,MAAM,QAAQ,IAAI;CACxB,MAAM,SAAS,SAA2B;EAExC,CADe,KAAK,WAAW,QAAQ,QAAQ,SAAS,QAAQ,QACzD,MAAM,GAAG,KAAK,KAAK,GAAG;CAC/B;CACA,MAAM,UAAU,iBAAiB;CACjC,MAAM,MAAkB;EACtB,IAAI,eAAe;EACnB,KAAK,SAAS,GAAG;EACjB;EACA,SAAS;EACT;EACA,GAAI,UAAU,EAAE,WAAW,QAAQ,IAAI,CAAC;CAC1C;CACA,MAAM,EAAE,aAAa,MAAM,YAAY,QAAQ,KAAK,MAAM,CAAC,GAAG,GAAG;CACjE,QAAQ,WAAW;AACrB;AAEA,KAAK"}
@@ -0,0 +1,37 @@
1
+ import { FileSystem } from "./fs.js";
2
+ import { Diagnostic, buildRouteManifest } from "@mindees/compiler";
3
+
4
+ //#region src/build.d.ts
5
+ /** Options for {@link buildProject}. */
6
+ interface BuildOptions {
7
+ /** Project root (contains `src/`). Default `"."`. */
8
+ root?: string;
9
+ /** Output directory. Default `"dist"`. */
10
+ outDir?: string;
11
+ /** Emit source maps. Default `true`. */
12
+ sourceMap?: boolean;
13
+ }
14
+ /** Result of a project build. */
15
+ interface BuildResult {
16
+ ok: boolean;
17
+ /** Source files compiled (relative paths), sorted. */
18
+ compiled: string[];
19
+ /** All diagnostics across modules (errors fail the build). */
20
+ diagnostics: Diagnostic[];
21
+ /** Route manifest, if `src/routes/` existed. */
22
+ routes?: ReturnType<typeof buildRouteManifest>;
23
+ /** Optimizer totals across all modules. */
24
+ stats: {
25
+ flattenedNodes: number;
26
+ totalElements: number;
27
+ };
28
+ }
29
+ /**
30
+ * Build the project at `root`. Compiles every `src/**\/*.{ts,tsx}` (except
31
+ * `.d.ts`) and writes outputs to `outDir`. Stops emitting a module on type
32
+ * errors but collects diagnostics from all modules so the report is complete.
33
+ */
34
+ declare function buildProject(fs: FileSystem, options?: BuildOptions): BuildResult;
35
+ //#endregion
36
+ export { BuildOptions, BuildResult, buildProject };
37
+ //# sourceMappingURL=build.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"build.d.ts","names":[],"sources":["../src/build.ts"],"mappings":";;;;AA6CA;AAAA,UAViB,YAAA;;EAEf,IAAA;EAe2B;EAb3B,MAAA;EAamB;EAXnB,SAAA;AAAA;;UAIe,WAAA;EACf,EAAA;EAMA;EAJA,QAAA;EAI2B;EAF3B,WAAA,EAAa,UAAA;EAIJ;EAFT,MAAA,GAAS,UAAA,QAAkB,kBAAA;EAEmB;EAA9C,KAAA;IAAS,cAAA;IAAwB,aAAA;EAAA;AAAA;;;;;;iBAWnB,YAAA,CAAa,EAAA,EAAI,UAAA,EAAY,OAAA,GAAS,YAAA,GAAoB,WAAA"}
package/dist/build.js ADDED
@@ -0,0 +1,97 @@
1
+ import { buildRouteManifest, compile, typecheck } from "@mindees/compiler";
2
+ //#region src/build.ts
3
+ /**
4
+ * `buildProject` โ€” compile a project's sources with the Mindees Compiler.
5
+ *
6
+ * Walks `src/**` via an injected {@link FileSystem}, runs each TS/TSX module
7
+ * through `@mindees/compiler`'s `typecheck` gate and then `compile` (emit),
8
+ * writes the JS (and source map) to `dist/`, and โ€” if a `src/routes/` dir
9
+ * exists โ€” emits a per-route manifest. Returns structured results so the CLI can
10
+ * report diagnostics and fail the build on type errors.
11
+ *
12
+ * @module
13
+ */
14
+ /**
15
+ * Diagnostic codes that are artifacts of type-checking a module **in isolation**
16
+ * (without the project's dependency graph or ambient JSX types), not real app
17
+ * errors. If any ever reaches the build it is **downgraded to a warning** rather
18
+ * than failing the build:
19
+ *
20
+ * - `TS2307` โ€” "Cannot find module 'โ€ฆ'": imports aren't resolved in single-module mode.
21
+ * - `TS7026` โ€” "no interface 'JSX.IntrinsicElements'": the framework's JSX env
22
+ * types aren't loaded in isolation.
23
+ *
24
+ * In practice the compiler's single-module gate already **filters** these upstream
25
+ * (it drops unresolved-import codes and injects ambient JSX types), so they
26
+ * normally don't appear here at all โ€” this set is a defensive backstop in case a
27
+ * future gate surfaces them. Genuine type errors (e.g. `TS2322` not-assignable)
28
+ * are untouched and still fail the build. A full project-graph type-check is
29
+ * future work (see ROADMAP).
30
+ */
31
+ const ISOLATION_NOISE = new Set(["TS2307", "TS7026"]);
32
+ const COMPILABLE = /\.(tsx|ts)$/;
33
+ const DECLARATION = /\.d\.ts$/;
34
+ /**
35
+ * Build the project at `root`. Compiles every `src/**\/*.{ts,tsx}` (except
36
+ * `.d.ts`) and writes outputs to `outDir`. Stops emitting a module on type
37
+ * errors but collects diagnostics from all modules so the report is complete.
38
+ */
39
+ function buildProject(fs, options = {}) {
40
+ const { root = ".", outDir = "dist", sourceMap = true } = options;
41
+ const srcDir = root === "." ? "src" : `${root}/src`;
42
+ const diagnostics = [];
43
+ const compiled = [];
44
+ const stats = {
45
+ flattenedNodes: 0,
46
+ totalElements: 0
47
+ };
48
+ const entries = fs.exists(srcDir) ? fs.readDir(srcDir) : [];
49
+ for (const rel of entries) {
50
+ if (!COMPILABLE.test(rel) || DECLARATION.test(rel)) continue;
51
+ const srcPath = `${srcDir}/${rel}`;
52
+ const source = fs.readFile(srcPath);
53
+ const moduleDiags = typecheck(source, rel).map((d) => d.severity === "error" && ISOLATION_NOISE.has(d.code) ? {
54
+ ...d,
55
+ severity: "warning"
56
+ } : d);
57
+ diagnostics.push(...moduleDiags);
58
+ if (!moduleDiags.some((d) => d.severity === "error")) {
59
+ const emitted = compile(source, {
60
+ fileName: rel,
61
+ sourceMap
62
+ });
63
+ stats.flattenedNodes += emitted.stats.flattenedNodes;
64
+ stats.totalElements += emitted.stats.totalElements;
65
+ const outPath = `${outDir}/${rel.replace(COMPILABLE, ".js")}`;
66
+ fs.writeFile(outPath, emitted.code);
67
+ if (emitted.map) fs.writeFile(`${outPath}.map`, emitted.map);
68
+ compiled.push(rel);
69
+ }
70
+ }
71
+ let routes;
72
+ const routesDir = `${srcDir}/routes`;
73
+ if (fs.exists(routesDir)) try {
74
+ routes = buildRouteManifest(fs.readDir(routesDir));
75
+ fs.writeFile(`${outDir}/routes.manifest.json`, `${JSON.stringify(routes, null, 2)}\n`);
76
+ } catch (e) {
77
+ diagnostics.push({
78
+ severity: "error",
79
+ code: "MDC_ROUTES",
80
+ message: e instanceof Error ? e.message : String(e),
81
+ file: routesDir
82
+ });
83
+ }
84
+ compiled.sort();
85
+ const result = {
86
+ ok: !diagnostics.some((d) => d.severity === "error"),
87
+ compiled,
88
+ diagnostics,
89
+ stats
90
+ };
91
+ if (routes) result.routes = routes;
92
+ return result;
93
+ }
94
+ //#endregion
95
+ export { buildProject };
96
+
97
+ //# sourceMappingURL=build.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"build.js","names":[],"sources":["../src/build.ts"],"sourcesContent":["/**\n * `buildProject` โ€” compile a project's sources with the Mindees Compiler.\n *\n * Walks `src/**` via an injected {@link FileSystem}, runs each TS/TSX module\n * through `@mindees/compiler`'s `typecheck` gate and then `compile` (emit),\n * writes the JS (and source map) to `dist/`, and โ€” if a `src/routes/` dir\n * exists โ€” emits a per-route manifest. Returns structured results so the CLI can\n * report diagnostics and fail the build on type errors.\n *\n * @module\n */\n\nimport { buildRouteManifest, compile, type Diagnostic, typecheck } from '@mindees/compiler'\nimport type { FileSystem } from './fs'\n\n/**\n * Diagnostic codes that are artifacts of type-checking a module **in isolation**\n * (without the project's dependency graph or ambient JSX types), not real app\n * errors. If any ever reaches the build it is **downgraded to a warning** rather\n * than failing the build:\n *\n * - `TS2307` โ€” \"Cannot find module 'โ€ฆ'\": imports aren't resolved in single-module mode.\n * - `TS7026` โ€” \"no interface 'JSX.IntrinsicElements'\": the framework's JSX env\n * types aren't loaded in isolation.\n *\n * In practice the compiler's single-module gate already **filters** these upstream\n * (it drops unresolved-import codes and injects ambient JSX types), so they\n * normally don't appear here at all โ€” this set is a defensive backstop in case a\n * future gate surfaces them. Genuine type errors (e.g. `TS2322` not-assignable)\n * are untouched and still fail the build. A full project-graph type-check is\n * future work (see ROADMAP).\n */\nconst ISOLATION_NOISE = new Set(['TS2307', 'TS7026'])\n\n/** Options for {@link buildProject}. */\nexport interface BuildOptions {\n /** Project root (contains `src/`). Default `\".\"`. */\n root?: string\n /** Output directory. Default `\"dist\"`. */\n outDir?: string\n /** Emit source maps. Default `true`. */\n sourceMap?: boolean\n}\n\n/** Result of a project build. */\nexport interface BuildResult {\n ok: boolean\n /** Source files compiled (relative paths), sorted. */\n compiled: string[]\n /** All diagnostics across modules (errors fail the build). */\n diagnostics: Diagnostic[]\n /** Route manifest, if `src/routes/` existed. */\n routes?: ReturnType<typeof buildRouteManifest>\n /** Optimizer totals across all modules. */\n stats: { flattenedNodes: number; totalElements: number }\n}\n\nconst COMPILABLE = /\\.(tsx|ts)$/\nconst DECLARATION = /\\.d\\.ts$/\n\n/**\n * Build the project at `root`. Compiles every `src/**\\/*.{ts,tsx}` (except\n * `.d.ts`) and writes outputs to `outDir`. Stops emitting a module on type\n * errors but collects diagnostics from all modules so the report is complete.\n */\nexport function buildProject(fs: FileSystem, options: BuildOptions = {}): BuildResult {\n const { root = '.', outDir = 'dist', sourceMap = true } = options\n const srcDir = root === '.' ? 'src' : `${root}/src`\n\n const diagnostics: Diagnostic[] = []\n const compiled: string[] = []\n const stats = { flattenedNodes: 0, totalElements: 0 }\n\n const entries = fs.exists(srcDir) ? fs.readDir(srcDir) : []\n for (const rel of entries) {\n if (!COMPILABLE.test(rel) || DECLARATION.test(rel)) continue\n const srcPath = `${srcDir}/${rel}`\n const source = fs.readFile(srcPath)\n\n // Type-check for diagnostics, but downgrade isolation-only noise to warnings\n // so a normal app (with cross-module imports + JSX) still builds. Genuine\n // type errors remain errors and fail the build below.\n const moduleDiags = typecheck(source, rel).map((d) =>\n d.severity === 'error' && ISOLATION_NOISE.has(d.code)\n ? { ...d, severity: 'warning' as const }\n : d,\n )\n diagnostics.push(...moduleDiags)\n\n // Emit only if this module has no real (post-downgrade) error.\n const moduleHasError = moduleDiags.some((d) => d.severity === 'error')\n if (!moduleHasError) {\n const emitted = compile(source, { fileName: rel, sourceMap })\n stats.flattenedNodes += emitted.stats.flattenedNodes\n stats.totalElements += emitted.stats.totalElements\n const outPath = `${outDir}/${rel.replace(COMPILABLE, '.js')}`\n fs.writeFile(outPath, emitted.code)\n if (emitted.map) fs.writeFile(`${outPath}.map`, emitted.map)\n compiled.push(rel)\n }\n }\n\n // Per-route manifest, if a routes dir is present. buildRouteManifest THROWS on\n // a malformed routes dir (duplicate route path, or a non-terminal catch-all) โ€”\n // ordinary user misconfigurations that `mindees build` exists to report. Turn\n // them into a build diagnostic + failing result so the CLI prints a clean error\n // and exits non-zero, honoring runCli's \"never throws for expected failures\"\n // contract instead of crashing with a raw stack trace.\n let routes: BuildResult['routes']\n const routesDir = `${srcDir}/routes`\n if (fs.exists(routesDir)) {\n try {\n routes = buildRouteManifest(fs.readDir(routesDir))\n fs.writeFile(`${outDir}/routes.manifest.json`, `${JSON.stringify(routes, null, 2)}\\n`)\n } catch (e) {\n diagnostics.push({\n severity: 'error',\n code: 'MDC_ROUTES',\n message: e instanceof Error ? e.message : String(e),\n file: routesDir,\n })\n }\n }\n\n compiled.sort()\n const ok = !diagnostics.some((d) => d.severity === 'error')\n const result: BuildResult = { ok, compiled, diagnostics, stats }\n if (routes) result.routes = routes\n return result\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgCA,MAAM,kBAAkB,IAAI,IAAI,CAAC,UAAU,QAAQ,CAAC;AAyBpD,MAAM,aAAa;AACnB,MAAM,cAAc;;;;;;AAOpB,SAAgB,aAAa,IAAgB,UAAwB,CAAC,GAAgB;CACpF,MAAM,EAAE,OAAO,KAAK,SAAS,QAAQ,YAAY,SAAS;CAC1D,MAAM,SAAS,SAAS,MAAM,QAAQ,GAAG,KAAK;CAE9C,MAAM,cAA4B,CAAC;CACnC,MAAM,WAAqB,CAAC;CAC5B,MAAM,QAAQ;EAAE,gBAAgB;EAAG,eAAe;CAAE;CAEpD,MAAM,UAAU,GAAG,OAAO,MAAM,IAAI,GAAG,QAAQ,MAAM,IAAI,CAAC;CAC1D,KAAK,MAAM,OAAO,SAAS;EACzB,IAAI,CAAC,WAAW,KAAK,GAAG,KAAK,YAAY,KAAK,GAAG,GAAG;EACpD,MAAM,UAAU,GAAG,OAAO,GAAG;EAC7B,MAAM,SAAS,GAAG,SAAS,OAAO;EAKlC,MAAM,cAAc,UAAU,QAAQ,GAAG,EAAE,KAAK,MAC9C,EAAE,aAAa,WAAW,gBAAgB,IAAI,EAAE,IAAI,IAChD;GAAE,GAAG;GAAG,UAAU;EAAmB,IACrC,CACN;EACA,YAAY,KAAK,GAAG,WAAW;EAI/B,IAAI,CADmB,YAAY,MAAM,MAAM,EAAE,aAAa,OAC5C,GAAG;GACnB,MAAM,UAAU,QAAQ,QAAQ;IAAE,UAAU;IAAK;GAAU,CAAC;GAC5D,MAAM,kBAAkB,QAAQ,MAAM;GACtC,MAAM,iBAAiB,QAAQ,MAAM;GACrC,MAAM,UAAU,GAAG,OAAO,GAAG,IAAI,QAAQ,YAAY,KAAK;GAC1D,GAAG,UAAU,SAAS,QAAQ,IAAI;GAClC,IAAI,QAAQ,KAAK,GAAG,UAAU,GAAG,QAAQ,OAAO,QAAQ,GAAG;GAC3D,SAAS,KAAK,GAAG;EACnB;CACF;CAQA,IAAI;CACJ,MAAM,YAAY,GAAG,OAAO;CAC5B,IAAI,GAAG,OAAO,SAAS,GACrB,IAAI;EACF,SAAS,mBAAmB,GAAG,QAAQ,SAAS,CAAC;EACjD,GAAG,UAAU,GAAG,OAAO,wBAAwB,GAAG,KAAK,UAAU,QAAQ,MAAM,CAAC,EAAE,GAAG;CACvF,SAAS,GAAG;EACV,YAAY,KAAK;GACf,UAAU;GACV,MAAM;GACN,SAAS,aAAa,QAAQ,EAAE,UAAU,OAAO,CAAC;GAClD,MAAM;EACR,CAAC;CACH;CAGF,SAAS,KAAK;CAEd,MAAM,SAAsB;EAAE,IAAA,CADlB,YAAY,MAAM,MAAM,EAAE,aAAa,OAAO;EACxB;EAAU;EAAa;CAAM;CAC/D,IAAI,QAAQ,OAAO,SAAS;CAC5B,OAAO;AACT"}
package/dist/cli.d.ts ADDED
@@ -0,0 +1,32 @@
1
+ import { CommandResult, EnvProbe, Writer } from "./types.js";
2
+ import { FileSystem } from "./fs.js";
3
+ import { AiBackend } from "@mindees/ai";
4
+
5
+ //#region src/cli.d.ts
6
+ /** Everything the CLI needs from the outside world (injected for testability). */
7
+ interface CliContext {
8
+ fs: FileSystem;
9
+ env: EnvProbe;
10
+ /** Working directory (where `create` writes, what `build` reads). */
11
+ cwd: string;
12
+ /** CLI version string (from package metadata). */
13
+ version: string;
14
+ /** Output sink. */
15
+ write: Writer;
16
+ /** AI backend for `ai` commands (wired from `MINDEES_AI_*` env in `bin`). */
17
+ aiBackend?: AiBackend;
18
+ }
19
+ /**
20
+ * Run the CLI. Returns a {@link CommandResult} with the process exit code.
21
+ * Never throws for expected failures โ€” it reports them and returns non-zero.
22
+ */
23
+ declare function runCli(argv: readonly string[], ctx: CliContext): CommandResult;
24
+ /**
25
+ * The async CLI entry. Handles the model-calling `ai` command (which is asynchronous) and
26
+ * delegates every synchronous command to {@link runCli}. The `bin` calls this; tests can call
27
+ * either (sync commands stay testable through `runCli`).
28
+ */
29
+ declare function runCliAsync(argv: readonly string[], ctx: CliContext): Promise<CommandResult>;
30
+ //#endregion
31
+ export { CliContext, runCli, runCliAsync };
32
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","names":[],"sources":["../src/cli.ts"],"mappings":";;;;;;UAwBiB,UAAA;EACf,EAAA,EAAI,UAAA;EACJ,GAAA,EAAK,QAAA;EAAA;EAEL,GAAA;EAEA;EAAA,OAAA;EAEO;EAAP,KAAA,EAAO,MAAA;EAEK;EAAZ,SAAA,GAAY,SAAA;AAAA;AAqCd;;;;AAAA,iBAAgB,MAAA,CAAO,IAAA,qBAAyB,GAAA,EAAK,UAAA,GAAa,aAAa;;;;;AAAA;iBAmC/D,WAAA,CAAY,IAAA,qBAAyB,GAAA,EAAK,UAAA,GAAa,OAAA,CAAQ,aAAA"}