@mindees/cli 0.4.0 → 0.6.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/assets/logo.png +0 -0
- package/dist/banner.js +40 -0
- package/dist/banner.js.map +1 -0
- package/dist/bin.js +60 -2
- package/dist/bin.js.map +1 -1
- package/dist/cli.d.ts +2 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +9 -3
- package/dist/cli.js.map +1 -1
- package/dist/dev-server.d.ts +68 -0
- package/dist/dev-server.d.ts.map +1 -0
- package/dist/dev-server.js +86 -0
- package/dist/dev-server.js.map +1 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/version.js.map +1 -1
- package/package.json +6 -5
package/assets/logo.png
ADDED
|
Binary file
|
package/dist/banner.js
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
//#region src/banner.ts
|
|
2
|
+
const RESET = "\x1B[0m";
|
|
3
|
+
const DIM = "\x1B[2m";
|
|
4
|
+
const paint = (on, code, text) => on ? `${code}${text}${RESET}` : text;
|
|
5
|
+
/** The ANSI wordmark (always portable — works in every terminal, CI, and piped output). */
|
|
6
|
+
function wordmark(opts = {}) {
|
|
7
|
+
const on = opts.color !== false;
|
|
8
|
+
const ver = opts.version ? paint(on, DIM, `v${opts.version}`) : "";
|
|
9
|
+
return [
|
|
10
|
+
`${paint(on, "\x1B[38;2;99;102;241m\x1B[1m", "◆ MindeesNative")}${ver ? ` ${ver}` : ""}`,
|
|
11
|
+
paint(on, DIM, "The cross-platform framework built to make React Native & Flutter obsolete."),
|
|
12
|
+
paint(on, DIM, "One language · native performance · native look · instant updates")
|
|
13
|
+
].join("\n");
|
|
14
|
+
}
|
|
15
|
+
/** The full banner: the inline logo image (if provided) above the {@link wordmark}. */
|
|
16
|
+
function renderBanner(opts = {}) {
|
|
17
|
+
const wm = wordmark(opts);
|
|
18
|
+
return opts.image ? `${opts.image}\n${wm}` : wm;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Build an iTerm2 / WezTerm inline-image escape (OSC 1337) for a base64-encoded PNG. The image is
|
|
22
|
+
* sized in terminal cells so the logo stays small.
|
|
23
|
+
*/
|
|
24
|
+
function itermImage(base64, opts = {}) {
|
|
25
|
+
const args = ["inline=1", "preserveAspectRatio=1"];
|
|
26
|
+
if (opts.width) args.push(`width=${opts.width}`);
|
|
27
|
+
if (opts.height) args.push(`height=${opts.height}`);
|
|
28
|
+
return `\x1b]1337;File=${args.join(";")}:${base64}\x07`;
|
|
29
|
+
}
|
|
30
|
+
/** Detect terminal inline-image (iTerm2-protocol) support from environment variables. */
|
|
31
|
+
function detectImageSupport(env) {
|
|
32
|
+
if (env.LC_TERMINAL === "iTerm2") return "iterm";
|
|
33
|
+
const program = env.TERM_PROGRAM ?? "";
|
|
34
|
+
if (/iTerm|WezTerm|vscode/i.test(program)) return "iterm";
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
//#endregion
|
|
38
|
+
export { detectImageSupport, itermImage, renderBanner };
|
|
39
|
+
|
|
40
|
+
//# sourceMappingURL=banner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"banner.js","names":[],"sources":["../src/banner.ts"],"sourcesContent":["/**\n * The `mindees` CLI banner — shows the MindeesNative logo in a friendly way. On image-capable\n * terminals (iTerm2 / WezTerm / VS Code) it prints the actual logo PNG inline; everywhere else it\n * prints an ANSI wordmark. Pure functions only — the real terminal detection + file read live in\n * `bin.ts` and inject the result, so this stays testable.\n *\n * @module\n */\n\n/** Inputs for {@link renderBanner}. */\nexport interface BannerOptions {\n /** Emit ANSI color. `false` for piped output / `NO_COLOR`. */\n readonly color?: boolean\n /** A pre-built terminal inline-image escape (see {@link itermImage}); printed above the wordmark. */\n readonly image?: string | null\n /** Version string, shown next to the wordmark. */\n readonly version?: string\n}\n\nconst RESET = '\\x1b[0m'\nconst BRAND = '\\x1b[38;2;99;102;241m' // indigo — the flat-vector logo's accent\nconst BOLD = '\\x1b[1m'\nconst DIM = '\\x1b[2m'\n\nconst paint = (on: boolean, code: string, text: string): string =>\n on ? `${code}${text}${RESET}` : text\n\n/** The ANSI wordmark (always portable — works in every terminal, CI, and piped output). */\nexport function wordmark(opts: BannerOptions = {}): string {\n const on = opts.color !== false\n const ver = opts.version ? paint(on, DIM, `v${opts.version}`) : ''\n const title = `${paint(on, BRAND + BOLD, '◆ MindeesNative')}${ver ? ` ${ver}` : ''}`\n return [\n title,\n paint(on, DIM, 'The cross-platform framework built to make React Native & Flutter obsolete.'),\n paint(on, DIM, 'One language · native performance · native look · instant updates'),\n ].join('\\n')\n}\n\n/** The full banner: the inline logo image (if provided) above the {@link wordmark}. */\nexport function renderBanner(opts: BannerOptions = {}): string {\n const wm = wordmark(opts)\n return opts.image ? `${opts.image}\\n${wm}` : wm\n}\n\n/**\n * Build an iTerm2 / WezTerm inline-image escape (OSC 1337) for a base64-encoded PNG. The image is\n * sized in terminal cells so the logo stays small.\n */\nexport function itermImage(base64: string, opts: { width?: number; height?: number } = {}): string {\n const args = ['inline=1', 'preserveAspectRatio=1']\n if (opts.width) args.push(`width=${opts.width}`)\n if (opts.height) args.push(`height=${opts.height}`)\n return `\\x1b]1337;File=${args.join(';')}:${base64}\\x07`\n}\n\n/** Detect terminal inline-image (iTerm2-protocol) support from environment variables. */\nexport function detectImageSupport(env: Record<string, string | undefined>): 'iterm' | null {\n if (env.LC_TERMINAL === 'iTerm2') return 'iterm'\n const program = env.TERM_PROGRAM ?? ''\n if (/iTerm|WezTerm|vscode/i.test(program)) return 'iterm'\n return null\n}\n"],"mappings":";AAmBA,MAAM,QAAQ;AAGd,MAAM,MAAM;AAEZ,MAAM,SAAS,IAAa,MAAc,SACxC,KAAK,GAAG,OAAO,OAAO,UAAU;;AAGlC,SAAgB,SAAS,OAAsB,CAAC,GAAW;CACzD,MAAM,KAAK,KAAK,UAAU;CAC1B,MAAM,MAAM,KAAK,UAAU,MAAM,IAAI,KAAK,IAAI,KAAK,SAAS,IAAI;CAEhE,OAAO;EACL,GAFe,MAAM,IAAI,gCAAc,iBAAiB,IAAI,MAAM,KAAK,QAAQ;EAG/E,MAAM,IAAI,KAAK,6EAA6E;EAC5F,MAAM,IAAI,KAAK,mEAAmE;CACpF,EAAE,KAAK,IAAI;AACb;;AAGA,SAAgB,aAAa,OAAsB,CAAC,GAAW;CAC7D,MAAM,KAAK,SAAS,IAAI;CACxB,OAAO,KAAK,QAAQ,GAAG,KAAK,MAAM,IAAI,OAAO;AAC/C;;;;;AAMA,SAAgB,WAAW,QAAgB,OAA4C,CAAC,GAAW;CACjG,MAAM,OAAO,CAAC,YAAY,uBAAuB;CACjD,IAAI,KAAK,OAAO,KAAK,KAAK,SAAS,KAAK,OAAO;CAC/C,IAAI,KAAK,QAAQ,KAAK,KAAK,UAAU,KAAK,QAAQ;CAClD,OAAO,kBAAkB,KAAK,KAAK,GAAG,EAAE,GAAG,OAAO;AACpD;;AAGA,SAAgB,mBAAmB,KAAyD;CAC1F,IAAI,IAAI,gBAAgB,UAAU,OAAO;CACzC,MAAM,UAAU,IAAI,gBAAgB;CACpC,IAAI,wBAAwB,KAAK,OAAO,GAAG,OAAO;CAClD,OAAO;AACT"}
|
package/dist/bin.js
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { VERSION } from "./version.js";
|
|
3
3
|
import { runCliAsync } from "./cli.js";
|
|
4
|
-
import {
|
|
4
|
+
import { startDev } from "./dev.js";
|
|
5
|
+
import { createDevServer, createNodeWatcher, renderDevPage } from "./dev-server.js";
|
|
6
|
+
import { detectImageSupport, itermImage, renderBanner } from "./banner.js";
|
|
7
|
+
import { existsSync, mkdirSync, readFileSync, readdirSync, statSync, watch, writeFileSync } from "node:fs";
|
|
8
|
+
import { createServer } from "node:http";
|
|
5
9
|
import { dirname, join, relative, sep } from "node:path";
|
|
6
10
|
import process from "node:process";
|
|
7
11
|
import { createServerBackend } from "@mindees/ai/server";
|
|
@@ -71,20 +75,74 @@ function aiBackendFromEnv() {
|
|
|
71
75
|
...process.env.MINDEES_AI_API_KEY ? { apiKey: process.env.MINDEES_AI_API_KEY } : {}
|
|
72
76
|
});
|
|
73
77
|
}
|
|
78
|
+
/**
|
|
79
|
+
* `mindees dev` — the long-running transport over the tested {@link startDev} orchestrator:
|
|
80
|
+
* build + watch `src/`, serve a live-reload preview, and reload the browser on each rebuild. This
|
|
81
|
+
* is the I/O glue; the watcher, server, and orchestrator it wires are unit-tested in their modules.
|
|
82
|
+
*/
|
|
83
|
+
function runDevServer(ctx) {
|
|
84
|
+
const port = Number(process.env.MINDEES_DEV_PORT ?? 3e3) || 3e3;
|
|
85
|
+
const server = createDevServer({ html: "<body>MindeesNative dev — building…</body>" });
|
|
86
|
+
const watcher = createNodeWatcher(["src"], { watch: (path, opts, listener) => watch(path, { recursive: opts.recursive ?? false }, (event, filename) => listener(event, typeof filename === "string" ? filename : null)) });
|
|
87
|
+
startDev(ctx.fs, watcher, { onRebuild: (result) => {
|
|
88
|
+
server.setHtml(renderDevPage(result));
|
|
89
|
+
server.bump();
|
|
90
|
+
ctx.write({
|
|
91
|
+
text: result.ok ? `rebuilt: ${result.compiled.length} file(s) ok` : `rebuild failed: ${result.diagnostics.filter((d) => d.severity === "error").length} error(s)`,
|
|
92
|
+
stream: result.ok ? "out" : "err"
|
|
93
|
+
});
|
|
94
|
+
} });
|
|
95
|
+
createServer((req, res) => {
|
|
96
|
+
const out = server.handle(req.method ?? "GET", req.url ?? "/");
|
|
97
|
+
res.writeHead(out.status, out.headers);
|
|
98
|
+
res.end(out.body);
|
|
99
|
+
}).listen(port, () => {
|
|
100
|
+
ctx.write({
|
|
101
|
+
text: `mindees dev — serving http://localhost:${port} (live reload on)`,
|
|
102
|
+
stream: "out"
|
|
103
|
+
});
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Build the welcome banner for an interactive (TTY) session: the ANSI wordmark always, plus the
|
|
108
|
+
* actual logo PNG inline on image-capable terminals (iTerm2 / WezTerm). Returns `undefined` when
|
|
109
|
+
* stdout is piped (scripts get clean, parseable output).
|
|
110
|
+
*/
|
|
111
|
+
function buildBanner() {
|
|
112
|
+
if (!process.stdout.isTTY) return void 0;
|
|
113
|
+
const color = !process.env.NO_COLOR;
|
|
114
|
+
let image = null;
|
|
115
|
+
if (detectImageSupport(process.env)) try {
|
|
116
|
+
image = itermImage(readFileSync(new URL("../assets/logo.png", import.meta.url)).toString("base64"), { width: 14 });
|
|
117
|
+
} catch {
|
|
118
|
+
image = null;
|
|
119
|
+
}
|
|
120
|
+
return renderBanner({
|
|
121
|
+
color,
|
|
122
|
+
image,
|
|
123
|
+
version: VERSION
|
|
124
|
+
});
|
|
125
|
+
}
|
|
74
126
|
async function main() {
|
|
75
127
|
const cwd = process.cwd();
|
|
76
128
|
const write = (line) => {
|
|
77
129
|
(line.stream === "err" ? process.stderr : process.stdout).write(`${line.text}\n`);
|
|
78
130
|
};
|
|
79
131
|
const backend = aiBackendFromEnv();
|
|
132
|
+
const banner = buildBanner();
|
|
80
133
|
const ctx = {
|
|
81
134
|
fs: nodeFileSystem(),
|
|
82
135
|
env: probeEnv(cwd),
|
|
83
136
|
cwd,
|
|
84
137
|
version: VERSION,
|
|
85
138
|
write,
|
|
86
|
-
...backend ? { aiBackend: backend } : {}
|
|
139
|
+
...backend ? { aiBackend: backend } : {},
|
|
140
|
+
...banner ? { banner } : {}
|
|
87
141
|
};
|
|
142
|
+
if (process.argv[2] === "dev") {
|
|
143
|
+
runDevServer(ctx);
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
88
146
|
const { exitCode } = await runCliAsync(process.argv.slice(2), ctx);
|
|
89
147
|
process.exitCode = exitCode;
|
|
90
148
|
}
|
package/dist/bin.js.map
CHANGED
|
@@ -1 +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 {
|
|
1
|
+
{"version":3,"file":"bin.js","names":["fsWatch"],"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 {\n existsSync,\n watch as fsWatch,\n mkdirSync,\n readdirSync,\n readFileSync,\n statSync,\n writeFileSync,\n} from 'node:fs'\nimport { createServer } from 'node:http'\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 { detectImageSupport, itermImage, renderBanner } from './banner'\nimport { type CliContext, runCliAsync } from './cli'\nimport { startDev } from './dev'\nimport { createDevServer, createNodeWatcher, renderDevPage } from './dev-server'\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\n/**\n * `mindees dev` — the long-running transport over the tested {@link startDev} orchestrator:\n * build + watch `src/`, serve a live-reload preview, and reload the browser on each rebuild. This\n * is the I/O glue; the watcher, server, and orchestrator it wires are unit-tested in their modules.\n */\nfunction runDevServer(ctx: CliContext): void {\n const port = Number(process.env.MINDEES_DEV_PORT ?? 3000) || 3000\n const server = createDevServer({ html: '<body>MindeesNative dev — building…</body>' })\n const watcher = createNodeWatcher(['src'], {\n watch: (path, opts, listener) =>\n fsWatch(path, { recursive: opts.recursive ?? false }, (event, filename) =>\n listener(event, typeof filename === 'string' ? filename : null),\n ),\n })\n startDev(ctx.fs, watcher, {\n onRebuild: (result) => {\n server.setHtml(renderDevPage(result))\n server.bump()\n ctx.write({\n text: result.ok\n ? `rebuilt: ${result.compiled.length} file(s) ok`\n : `rebuild failed: ${result.diagnostics.filter((d) => d.severity === 'error').length} error(s)`,\n stream: result.ok ? 'out' : 'err',\n })\n },\n })\n createServer((req, res) => {\n const out = server.handle(req.method ?? 'GET', req.url ?? '/')\n res.writeHead(out.status, out.headers)\n res.end(out.body)\n }).listen(port, () => {\n ctx.write({\n text: `mindees dev — serving http://localhost:${port} (live reload on)`,\n stream: 'out',\n })\n })\n}\n\n/**\n * Build the welcome banner for an interactive (TTY) session: the ANSI wordmark always, plus the\n * actual logo PNG inline on image-capable terminals (iTerm2 / WezTerm). Returns `undefined` when\n * stdout is piped (scripts get clean, parseable output).\n */\nfunction buildBanner(): string | undefined {\n if (!process.stdout.isTTY) return undefined\n const color = !process.env.NO_COLOR\n let image: string | null = null\n if (detectImageSupport(process.env)) {\n try {\n const bytes = readFileSync(new URL('../assets/logo.png', import.meta.url))\n image = itermImage(bytes.toString('base64'), { width: 14 })\n } catch {\n image = null // asset missing → wordmark only\n }\n }\n return renderBanner({ color, image, version: VERSION })\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 banner = buildBanner()\n const ctx: CliContext = {\n fs: nodeFileSystem(),\n env: probeEnv(cwd),\n cwd,\n version: VERSION,\n write,\n ...(backend ? { aiBackend: backend } : {}),\n ...(banner ? { banner } : {}),\n }\n // `dev` is a long-running transport (not a one-shot command), so it's wired here in the I/O entry\n // rather than the synchronous CLI dispatch. Everything else goes through the tested core.\n if (process.argv[2] === 'dev') {\n runDevServer(ctx)\n return\n }\n const { exitCode } = await runCliAsync(process.argv.slice(2), ctx)\n process.exitCode = exitCode\n}\n\nmain()\n"],"mappings":"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"}
|
package/dist/cli.d.ts
CHANGED
|
@@ -15,6 +15,8 @@ interface CliContext {
|
|
|
15
15
|
write: Writer;
|
|
16
16
|
/** AI backend for `ai` commands (wired from `MINDEES_AI_*` env in `bin`). */
|
|
17
17
|
aiBackend?: AiBackend;
|
|
18
|
+
/** Pre-rendered welcome banner (the MindeesNative logo). Shown on `help` + `create` success; injected by `bin` for TTYs. */
|
|
19
|
+
banner?: string;
|
|
18
20
|
}
|
|
19
21
|
/**
|
|
20
22
|
* Run the CLI. Returns a {@link CommandResult} with the process exit code.
|
package/dist/cli.d.ts.map
CHANGED
|
@@ -1 +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;
|
|
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;EAEN;EAAN,MAAA;AAAA;;;;;iBAyCc,MAAA,CAAO,IAAA,qBAAyB,GAAA,EAAK,UAAA,GAAa,aAAa;;;;AAAA;AAoC/E;iBAAgB,WAAA,CAAY,IAAA,qBAAyB,GAAA,EAAK,UAAA,GAAa,OAAA,CAAQ,aAAA"}
|
package/dist/cli.js
CHANGED
|
@@ -44,6 +44,10 @@ function out(write, text) {
|
|
|
44
44
|
text
|
|
45
45
|
});
|
|
46
46
|
}
|
|
47
|
+
/** Print the welcome banner (the logo), if one was injected (TTY sessions). */
|
|
48
|
+
function printBanner(ctx) {
|
|
49
|
+
if (ctx.banner) out(ctx.write, ctx.banner);
|
|
50
|
+
}
|
|
47
51
|
function err(write, text) {
|
|
48
52
|
write({
|
|
49
53
|
stream: "err",
|
|
@@ -57,6 +61,7 @@ function err(write, text) {
|
|
|
57
61
|
function runCli(argv, ctx) {
|
|
58
62
|
const [command, ...rest] = argv;
|
|
59
63
|
if (!command || command === "help" || command === "--help" || command === "-h") {
|
|
64
|
+
printBanner(ctx);
|
|
60
65
|
out(ctx.write, HELP);
|
|
61
66
|
return { exitCode: 0 };
|
|
62
67
|
}
|
|
@@ -140,6 +145,7 @@ function cmdCreate(args, ctx) {
|
|
|
140
145
|
err(ctx.write, result.error ?? "create failed");
|
|
141
146
|
return { exitCode: 1 };
|
|
142
147
|
}
|
|
148
|
+
printBanner(ctx);
|
|
143
149
|
out(ctx.write, `Created "${target.packageName}" from the ${result.template} template (${result.written.length} files).`);
|
|
144
150
|
out(ctx.write, `Next: cd ${quoteShellPath(target.displayDir)} && pnpm install && mindees dev`);
|
|
145
151
|
return { exitCode: 0 };
|
|
@@ -171,9 +177,9 @@ function cmdBuild(args, ctx) {
|
|
|
171
177
|
return { exitCode: 0 };
|
|
172
178
|
}
|
|
173
179
|
function cmdDev(ctx) {
|
|
174
|
-
out(ctx.write, "mindees dev —
|
|
175
|
-
out(ctx.write, "
|
|
176
|
-
out(ctx.write, "
|
|
180
|
+
out(ctx.write, "mindees dev — build + watch + live-reload preview.");
|
|
181
|
+
out(ctx.write, "Run via the `mindees` binary (it starts the HTTP server); set MINDEES_DEV_PORT to");
|
|
182
|
+
out(ctx.write, "override the port (default 3000). The browser reloads automatically on each rebuild.");
|
|
177
183
|
return { exitCode: 0 };
|
|
178
184
|
}
|
|
179
185
|
function cmdDoctor(ctx) {
|
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.js","names":[],"sources":["../src/cli.ts"],"sourcesContent":["/**\n * Forge CLI dispatch — `mindees <command> [args]`.\n *\n * `runCli` is a pure function of (argv, context) → exit code, writing structured\n * output through an injected {@link Writer}. All side-effecting capabilities\n * (filesystem, env probe) are injected via {@link CliContext}, so the entire CLI\n * is deterministically testable; the thin `bin` entrypoint wires real adapters.\n *\n * @module\n */\n\nimport { parseArgs } from 'node:util'\nimport type { AiBackend } from '@mindees/ai'\nimport { runAiCommand } from './ai'\nimport { buildProject } from './build'\nimport { quoteShellPath, resolveCreateTarget } from './create-target'\nimport { doctorSummary, renderDoctor, runDoctor } from './doctor'\nimport type { FileSystem } from './fs'\nimport { naturalLanguageToTemplate } from './nl'\nimport { scaffold } from './scaffold'\nimport { DEFAULT_TEMPLATE, templateNames } from './templates'\nimport type { CommandResult, EnvProbe, Writer } from './types'\n\n/** Everything the CLI needs from the outside world (injected for testability). */\nexport interface CliContext {\n fs: FileSystem\n env: EnvProbe\n /** Working directory (where `create` writes, what `build` reads). */\n cwd: string\n /** CLI version string (from package metadata). */\n version: string\n /** Output sink. */\n write: Writer\n /** AI backend for `ai` commands (wired from `MINDEES_AI_*` env in `bin`). */\n aiBackend?: AiBackend\n}\n\nconst HELP = `mindees — the MindeesNative CLI (Forge)\n\nUsage: mindees <command> [options]\n\nCommands:\n create <name> Scaffold a new app (--template <name>, --force)\n build Type-check + compile the project (--out-dir <dir>)\n dev Build and rebuild on change (developer preview)\n doctor Diagnose your environment\n info Show CLI + environment info\n ai explain <err> Explain an error with AI (needs MINDEES_AI_* env)\n help Show this help\n\nRun \\`mindees create --help\\` style flags inline. Templates: ${templateNames().join(', ')}.`\n\nconst CREATE_HELP = `Usage: mindees create <name-or-path> [options]\n\nOptions:\n -t, --template <name> Template to scaffold (${templateNames().join(', ')})\n -p, --prompt <text> Pick a template from a short prompt\n --force Overwrite a non-empty target directory\n -h, --help Show this help`\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/**\n * Run the CLI. Returns a {@link CommandResult} with the process exit code.\n * Never throws for expected failures — it reports them and returns non-zero.\n */\nexport function runCli(argv: readonly string[], ctx: CliContext): CommandResult {\n const [command, ...rest] = argv\n\n if (!command || command === 'help' || command === '--help' || command === '-h') {\n out(ctx.write, HELP)\n return { exitCode: 0 }\n }\n\n if (command === '--version' || command === '-v' || command === 'version') {\n out(ctx.write, ctx.version)\n return { exitCode: 0 }\n }\n\n switch (command) {\n case 'create':\n return cmdCreate(rest, ctx)\n case 'build':\n return cmdBuild(rest, ctx)\n case 'dev':\n return cmdDev(ctx)\n case 'doctor':\n return cmdDoctor(ctx)\n case 'info':\n return cmdInfo(ctx)\n default:\n err(ctx.write, `Unknown command \"${command}\". Run \\`mindees help\\`.`)\n return { exitCode: 1 }\n }\n}\n\n/**\n * The async CLI entry. Handles the model-calling `ai` command (which is asynchronous) and\n * delegates every synchronous command to {@link runCli}. The `bin` calls this; tests can call\n * either (sync commands stay testable through `runCli`).\n */\nexport function runCliAsync(argv: readonly string[], ctx: CliContext): Promise<CommandResult> {\n const [command, ...rest] = argv\n if (command === 'ai') {\n return runAiCommand(rest, {\n write: ctx.write,\n ...(ctx.aiBackend ? { backend: ctx.aiBackend } : {}),\n })\n }\n return Promise.resolve(runCli(argv, ctx))\n}\n\nfunction cmdCreate(args: readonly string[], ctx: CliContext): CommandResult {\n const { values, positionals } = parseArgs({\n args: [...args],\n allowPositionals: true,\n strict: false,\n options: {\n help: { type: 'boolean', short: 'h' },\n template: { type: 'string', short: 't' },\n force: { type: 'boolean' },\n prompt: { type: 'string', short: 'p' },\n },\n })\n\n if (values.help === true || positionals[0] === 'help') {\n out(ctx.write, CREATE_HELP)\n return { exitCode: 0 }\n }\n\n const name = positionals[0]\n if (!name) {\n err(ctx.write, 'create: missing app name or target path. Usage: mindees create <name-or-path>')\n return { exitCode: 1 }\n }\n\n // NL → template: `--prompt \"a counter app\"` picks a template deterministically\n // (offline). Real AI generation arrives with Synapse in Phase 10; until then\n // this is an honest keyword-based mapping that never blocks `create`. An\n // explicit `--template` always wins; the prompt only resolves a template when\n // the caller didn't choose one (mirrors `create-mindees`'s runCreate so both\n // entrypoints agree on precedence).\n // Treat a present-but-empty `--template \"\"` as \"not chosen\" (defer to prompt/default),\n // matching create-mindees's runCreate so both entrypoints agree on precedence.\n const explicitTemplate =\n typeof values.template === 'string' && values.template.length > 0 ? values.template : undefined\n let template = explicitTemplate ?? DEFAULT_TEMPLATE\n if (\n explicitTemplate === undefined &&\n typeof values.prompt === 'string' &&\n values.prompt.length > 0\n ) {\n const picked = naturalLanguageToTemplate(values.prompt)\n template = picked.template\n out(ctx.write, `Interpreted prompt → \"${picked.template}\" template (${picked.reason}).`)\n }\n\n const target = resolveCreateTarget(name, ctx.cwd)\n if (!target.ok) {\n err(ctx.write, target.error)\n return { exitCode: 1 }\n }\n\n const result = scaffold(ctx.fs, {\n appName: target.packageName,\n targetDir: target.targetDir,\n template,\n force: values.force === true,\n })\n\n if (!result.ok) {\n err(ctx.write, result.error ?? 'create failed')\n return { exitCode: 1 }\n }\n\n out(\n ctx.write,\n `Created \"${target.packageName}\" from the ${result.template} template (${result.written.length} files).`,\n )\n out(ctx.write, `Next: cd ${quoteShellPath(target.displayDir)} && pnpm install && mindees dev`)\n return { exitCode: 0 }\n}\n\nfunction cmdBuild(args: readonly string[], ctx: CliContext): CommandResult {\n const { values } = parseArgs({\n args: [...args],\n allowPositionals: true,\n strict: false,\n options: { 'out-dir': { type: 'string' }, 'no-source-map': { type: 'boolean' } },\n })\n\n const result = buildProject(ctx.fs, {\n root: ctx.cwd,\n outDir: typeof values['out-dir'] === 'string' ? values['out-dir'] : `${ctx.cwd}/dist`,\n sourceMap: values['no-source-map'] !== true,\n })\n\n for (const d of result.diagnostics) {\n const where = d.file ? `${d.file}${d.position ? `:${d.position.line}` : ''}: ` : ''\n err(ctx.write, `${d.severity} ${d.code} ${where}${d.message}`)\n }\n\n if (!result.ok) {\n err(ctx.write, 'Build failed: fix the type errors above.')\n return { exitCode: 1 }\n }\n out(\n ctx.write,\n `Built ${result.compiled.length} module(s); flattened ${result.stats.flattenedNodes}/${result.stats.totalElements} elements.`,\n )\n return { exitCode: 0 }\n}\n\nfunction cmdDev(ctx: CliContext): CommandResult {\n // The dev server transport (HTTP + HMR socket) is a developer-preview layer in\n // `bin`. The CLI command here reports how to use it; the tested rebuild\n // orchestrator lives in `dev.ts` (`startDev`).\n out(ctx.write, 'mindees dev — developer preview.')\n out(ctx.write, 'The rebuild-on-change orchestrator is available via startDev(); the')\n out(ctx.write, 'HTTP/HMR transport is being finalized. Use `mindees build` for now.')\n return { exitCode: 0 }\n}\n\nfunction cmdDoctor(ctx: CliContext): CommandResult {\n const checks = runDoctor(ctx.env)\n for (const line of renderDoctor(checks)) out(ctx.write, line)\n const summary = doctorSummary(checks)\n return { exitCode: summary === 'fail' ? 1 : 0 }\n}\n\nfunction cmdInfo(ctx: CliContext): CommandResult {\n out(ctx.write, `mindees CLI ${ctx.version}`)\n out(ctx.write, `Node ${ctx.env.nodeVersion}`)\n out(\n ctx.write,\n `Package manager: ${ctx.env.packageManager ? `${ctx.env.packageManager.name} ${ctx.env.packageManager.version}` : 'none'}`,\n )\n out(ctx.write, `Templates: ${templateNames().join(', ')}`)\n return { exitCode: 0 }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"cli.js","names":[],"sources":["../src/cli.ts"],"sourcesContent":["/**\n * Forge CLI dispatch — `mindees <command> [args]`.\n *\n * `runCli` is a pure function of (argv, context) → exit code, writing structured\n * output through an injected {@link Writer}. All side-effecting capabilities\n * (filesystem, env probe) are injected via {@link CliContext}, so the entire CLI\n * is deterministically testable; the thin `bin` entrypoint wires real adapters.\n *\n * @module\n */\n\nimport { parseArgs } from 'node:util'\nimport type { AiBackend } from '@mindees/ai'\nimport { runAiCommand } from './ai'\nimport { buildProject } from './build'\nimport { quoteShellPath, resolveCreateTarget } from './create-target'\nimport { doctorSummary, renderDoctor, runDoctor } from './doctor'\nimport type { FileSystem } from './fs'\nimport { naturalLanguageToTemplate } from './nl'\nimport { scaffold } from './scaffold'\nimport { DEFAULT_TEMPLATE, templateNames } from './templates'\nimport type { CommandResult, EnvProbe, Writer } from './types'\n\n/** Everything the CLI needs from the outside world (injected for testability). */\nexport interface CliContext {\n fs: FileSystem\n env: EnvProbe\n /** Working directory (where `create` writes, what `build` reads). */\n cwd: string\n /** CLI version string (from package metadata). */\n version: string\n /** Output sink. */\n write: Writer\n /** AI backend for `ai` commands (wired from `MINDEES_AI_*` env in `bin`). */\n aiBackend?: AiBackend\n /** Pre-rendered welcome banner (the MindeesNative logo). Shown on `help` + `create` success; injected by `bin` for TTYs. */\n banner?: string\n}\n\nconst HELP = `mindees — the MindeesNative CLI (Forge)\n\nUsage: mindees <command> [options]\n\nCommands:\n create <name> Scaffold a new app (--template <name>, --force)\n build Type-check + compile the project (--out-dir <dir>)\n dev Build and rebuild on change (developer preview)\n doctor Diagnose your environment\n info Show CLI + environment info\n ai explain <err> Explain an error with AI (needs MINDEES_AI_* env)\n help Show this help\n\nRun \\`mindees create --help\\` style flags inline. Templates: ${templateNames().join(', ')}.`\n\nconst CREATE_HELP = `Usage: mindees create <name-or-path> [options]\n\nOptions:\n -t, --template <name> Template to scaffold (${templateNames().join(', ')})\n -p, --prompt <text> Pick a template from a short prompt\n --force Overwrite a non-empty target directory\n -h, --help Show this help`\n\nfunction out(write: Writer, text: string): void {\n write({ stream: 'out', text })\n}\n/** Print the welcome banner (the logo), if one was injected (TTY sessions). */\nfunction printBanner(ctx: CliContext): void {\n if (ctx.banner) out(ctx.write, ctx.banner)\n}\nfunction err(write: Writer, text: string): void {\n write({ stream: 'err', text })\n}\n\n/**\n * Run the CLI. Returns a {@link CommandResult} with the process exit code.\n * Never throws for expected failures — it reports them and returns non-zero.\n */\nexport function runCli(argv: readonly string[], ctx: CliContext): CommandResult {\n const [command, ...rest] = argv\n\n if (!command || command === 'help' || command === '--help' || command === '-h') {\n printBanner(ctx)\n out(ctx.write, HELP)\n return { exitCode: 0 }\n }\n\n if (command === '--version' || command === '-v' || command === 'version') {\n out(ctx.write, ctx.version)\n return { exitCode: 0 }\n }\n\n switch (command) {\n case 'create':\n return cmdCreate(rest, ctx)\n case 'build':\n return cmdBuild(rest, ctx)\n case 'dev':\n return cmdDev(ctx)\n case 'doctor':\n return cmdDoctor(ctx)\n case 'info':\n return cmdInfo(ctx)\n default:\n err(ctx.write, `Unknown command \"${command}\". Run \\`mindees help\\`.`)\n return { exitCode: 1 }\n }\n}\n\n/**\n * The async CLI entry. Handles the model-calling `ai` command (which is asynchronous) and\n * delegates every synchronous command to {@link runCli}. The `bin` calls this; tests can call\n * either (sync commands stay testable through `runCli`).\n */\nexport function runCliAsync(argv: readonly string[], ctx: CliContext): Promise<CommandResult> {\n const [command, ...rest] = argv\n if (command === 'ai') {\n return runAiCommand(rest, {\n write: ctx.write,\n ...(ctx.aiBackend ? { backend: ctx.aiBackend } : {}),\n })\n }\n return Promise.resolve(runCli(argv, ctx))\n}\n\nfunction cmdCreate(args: readonly string[], ctx: CliContext): CommandResult {\n const { values, positionals } = parseArgs({\n args: [...args],\n allowPositionals: true,\n strict: false,\n options: {\n help: { type: 'boolean', short: 'h' },\n template: { type: 'string', short: 't' },\n force: { type: 'boolean' },\n prompt: { type: 'string', short: 'p' },\n },\n })\n\n if (values.help === true || positionals[0] === 'help') {\n out(ctx.write, CREATE_HELP)\n return { exitCode: 0 }\n }\n\n const name = positionals[0]\n if (!name) {\n err(ctx.write, 'create: missing app name or target path. Usage: mindees create <name-or-path>')\n return { exitCode: 1 }\n }\n\n // NL → template: `--prompt \"a counter app\"` picks a template deterministically\n // (offline). Real AI generation arrives with Synapse in Phase 10; until then\n // this is an honest keyword-based mapping that never blocks `create`. An\n // explicit `--template` always wins; the prompt only resolves a template when\n // the caller didn't choose one (mirrors `create-mindees`'s runCreate so both\n // entrypoints agree on precedence).\n // Treat a present-but-empty `--template \"\"` as \"not chosen\" (defer to prompt/default),\n // matching create-mindees's runCreate so both entrypoints agree on precedence.\n const explicitTemplate =\n typeof values.template === 'string' && values.template.length > 0 ? values.template : undefined\n let template = explicitTemplate ?? DEFAULT_TEMPLATE\n if (\n explicitTemplate === undefined &&\n typeof values.prompt === 'string' &&\n values.prompt.length > 0\n ) {\n const picked = naturalLanguageToTemplate(values.prompt)\n template = picked.template\n out(ctx.write, `Interpreted prompt → \"${picked.template}\" template (${picked.reason}).`)\n }\n\n const target = resolveCreateTarget(name, ctx.cwd)\n if (!target.ok) {\n err(ctx.write, target.error)\n return { exitCode: 1 }\n }\n\n const result = scaffold(ctx.fs, {\n appName: target.packageName,\n targetDir: target.targetDir,\n template,\n force: values.force === true,\n })\n\n if (!result.ok) {\n err(ctx.write, result.error ?? 'create failed')\n return { exitCode: 1 }\n }\n\n printBanner(ctx)\n out(\n ctx.write,\n `Created \"${target.packageName}\" from the ${result.template} template (${result.written.length} files).`,\n )\n out(ctx.write, `Next: cd ${quoteShellPath(target.displayDir)} && pnpm install && mindees dev`)\n return { exitCode: 0 }\n}\n\nfunction cmdBuild(args: readonly string[], ctx: CliContext): CommandResult {\n const { values } = parseArgs({\n args: [...args],\n allowPositionals: true,\n strict: false,\n options: { 'out-dir': { type: 'string' }, 'no-source-map': { type: 'boolean' } },\n })\n\n const result = buildProject(ctx.fs, {\n root: ctx.cwd,\n outDir: typeof values['out-dir'] === 'string' ? values['out-dir'] : `${ctx.cwd}/dist`,\n sourceMap: values['no-source-map'] !== true,\n })\n\n for (const d of result.diagnostics) {\n const where = d.file ? `${d.file}${d.position ? `:${d.position.line}` : ''}: ` : ''\n err(ctx.write, `${d.severity} ${d.code} ${where}${d.message}`)\n }\n\n if (!result.ok) {\n err(ctx.write, 'Build failed: fix the type errors above.')\n return { exitCode: 1 }\n }\n out(\n ctx.write,\n `Built ${result.compiled.length} module(s); flattened ${result.stats.flattenedNodes}/${result.stats.totalElements} elements.`,\n )\n return { exitCode: 0 }\n}\n\nfunction cmdDev(ctx: CliContext): CommandResult {\n // The dev server transport (HTTP + HMR socket) is a developer-preview layer in\n // `bin`. The CLI command here reports how to use it; the tested rebuild\n // orchestrator lives in `dev.ts` (`startDev`).\n out(ctx.write, 'mindees dev — build + watch + live-reload preview.')\n out(\n ctx.write,\n 'Run via the `mindees` binary (it starts the HTTP server); set MINDEES_DEV_PORT to',\n )\n out(\n ctx.write,\n 'override the port (default 3000). The browser reloads automatically on each rebuild.',\n )\n return { exitCode: 0 }\n}\n\nfunction cmdDoctor(ctx: CliContext): CommandResult {\n const checks = runDoctor(ctx.env)\n for (const line of renderDoctor(checks)) out(ctx.write, line)\n const summary = doctorSummary(checks)\n return { exitCode: summary === 'fail' ? 1 : 0 }\n}\n\nfunction cmdInfo(ctx: CliContext): CommandResult {\n out(ctx.write, `mindees CLI ${ctx.version}`)\n out(ctx.write, `Node ${ctx.env.nodeVersion}`)\n out(\n ctx.write,\n `Package manager: ${ctx.env.packageManager ? `${ctx.env.packageManager.name} ${ctx.env.packageManager.version}` : 'none'}`,\n )\n out(ctx.write, `Templates: ${templateNames().join(', ')}`)\n return { exitCode: 0 }\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { Watcher } from "./dev.js";
|
|
2
|
+
|
|
3
|
+
//#region src/dev-server.d.ts
|
|
4
|
+
/** A handle to an active filesystem watch (the `node:fs.watch` `FSWatcher` shape we use). */
|
|
5
|
+
interface WatchHandle {
|
|
6
|
+
close(): void;
|
|
7
|
+
}
|
|
8
|
+
/** The `node:fs.watch` signature (injected so the watcher is testable without a real filesystem). */
|
|
9
|
+
type WatchFn = (path: string, options: {
|
|
10
|
+
recursive?: boolean;
|
|
11
|
+
}, listener: (event: string, filename: string | null) => void) => WatchHandle;
|
|
12
|
+
/** Options for {@link createNodeWatcher}. */
|
|
13
|
+
interface NodeWatcherOptions {
|
|
14
|
+
/** The watch implementation — `node:fs.watch` in `bin`, a fake in tests. */
|
|
15
|
+
readonly watch: WatchFn;
|
|
16
|
+
/** Coalesce a burst of events into one change after this quiet period (ms, default 50). */
|
|
17
|
+
readonly debounceMs?: number;
|
|
18
|
+
/** Schedule a debounce timer (default `setTimeout`); injectable for tests. */
|
|
19
|
+
readonly setTimer?: (fn: () => void, ms: number) => unknown;
|
|
20
|
+
/** Cancel a debounce timer (default `clearTimeout`); injectable for tests. */
|
|
21
|
+
readonly clearTimer?: (handle: unknown) => void;
|
|
22
|
+
}
|
|
23
|
+
/** A {@link Watcher} (plus `close()`) backed by `node:fs.watch`, debounced. */
|
|
24
|
+
declare function createNodeWatcher(paths: readonly string[], options: NodeWatcherOptions): Watcher & {
|
|
25
|
+
close(): void;
|
|
26
|
+
};
|
|
27
|
+
/** A minimal HTTP response the `bin` glue writes to the socket. */
|
|
28
|
+
interface DevServerResponse {
|
|
29
|
+
readonly status: number;
|
|
30
|
+
readonly headers: Record<string, string>;
|
|
31
|
+
readonly body: string;
|
|
32
|
+
}
|
|
33
|
+
/** Options for {@link createDevServer}. */
|
|
34
|
+
interface DevServerOptions {
|
|
35
|
+
/** The app HTML to serve at `/` (the live-reload client is injected automatically). */
|
|
36
|
+
readonly html: string;
|
|
37
|
+
/** Path the live-reload client polls for the build version (default `/__mindees/version`). */
|
|
38
|
+
readonly reloadPath?: string;
|
|
39
|
+
/** Poll interval the injected client uses, in ms (default 1000). */
|
|
40
|
+
readonly pollMs?: number;
|
|
41
|
+
}
|
|
42
|
+
/** A pure dev-server: a request handler + a `bump()` that makes polling browsers reload. */
|
|
43
|
+
interface DevServer {
|
|
44
|
+
/** Handle a request → a response (pure given the current html + version). */
|
|
45
|
+
handle(method: string, url: string): DevServerResponse;
|
|
46
|
+
/** Replace the served app HTML (call after a rebuild changes the output). */
|
|
47
|
+
setHtml(html: string): void;
|
|
48
|
+
/** Bump the build version (call after each rebuild) so connected clients reload. */
|
|
49
|
+
bump(): void;
|
|
50
|
+
/** The current build version. */
|
|
51
|
+
version(): number;
|
|
52
|
+
}
|
|
53
|
+
/** Create a live-reload {@link DevServer}. The handler serves the app HTML + a version endpoint. */
|
|
54
|
+
declare function createDevServer(options: DevServerOptions): DevServer;
|
|
55
|
+
/** A build-status result shape (a subset of the CLI `BuildResult`) for {@link renderDevPage}. */
|
|
56
|
+
interface DevBuildStatus {
|
|
57
|
+
readonly ok: boolean;
|
|
58
|
+
readonly compiled: readonly string[];
|
|
59
|
+
readonly diagnostics: ReadonlyArray<{
|
|
60
|
+
readonly severity: string;
|
|
61
|
+
readonly message: string;
|
|
62
|
+
}>;
|
|
63
|
+
}
|
|
64
|
+
/** Render a minimal dev status page for a build result (preview shell; live-reload is injected by the server). */
|
|
65
|
+
declare function renderDevPage(status: DevBuildStatus): string;
|
|
66
|
+
//#endregion
|
|
67
|
+
export { DevBuildStatus, DevServer, DevServerOptions, NodeWatcherOptions, WatchFn, WatchHandle, createDevServer, createNodeWatcher, renderDevPage };
|
|
68
|
+
//# sourceMappingURL=dev-server.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dev-server.d.ts","names":[],"sources":["../src/dev-server.ts"],"mappings":";;;;UAmBiB,WAAA;EACf,KAAK;AAAA;AAQS;AAAA,KAJJ,OAAA,IACV,IAAA,UACA,OAAA;EAAW,SAAA;AAAA,GACX,QAAA,GAAW,KAAA,UAAe,QAAA,6BACvB,WAAW;;UAGC,kBAAA;EAEC;EAAA,SAAP,KAAA,EAAO,OAAO;EAId;EAAA,SAFA,UAAA;EAE4B;EAAA,SAA5B,QAAA,IAAY,EAAA,cAAgB,EAAA;EAEd;EAAA,SAAd,UAAA,IAAc,MAAA;AAAA;AAIzB;AAAA,iBAAgB,iBAAA,CACd,KAAA,qBACA,OAAA,EAAS,kBAAA,GACR,OAAO;EAAK,KAAA;AAAA;;UAkCE,iBAAA;EAAA,SACN,MAAA;EAAA,SACA,OAAA,EAAS,MAAM;EAAA,SACf,IAAA;AAAA;AArCS;AAAA,UAyCH,gBAAA;EAPiB;EAAA,SASvB,IAAA;EAPe;EAAA,SASf,UAAA;EATA;EAAA,SAWA,MAAA;AAAA;;UAIM,SAAA;EAVA;EAYf,MAAA,CAAO,MAAA,UAAgB,GAAA,WAAc,iBAAiB;;EAEtD,OAAA,CAAQ,IAAA;EAZC;EAcT,IAAA;EAVS;EAYT,OAAA;AAAA;AARF;AAAA,iBAegB,eAAA,CAAgB,OAAA,EAAS,gBAAA,GAAmB,SAAS;;UA4CpD,cAAA;EAAA,SACN,EAAA;EAAA,SACA,QAAA;EAAA,SACA,WAAA,EAAa,aAAa;IAAA,SAAY,QAAA;IAAA,SAA2B,OAAA;EAAA;AAAA;;iBAI5D,aAAA,CAAc,MAAsB,EAAd,cAAc"}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
//#region src/dev-server.ts
|
|
2
|
+
/** A {@link Watcher} (plus `close()`) backed by `node:fs.watch`, debounced. */
|
|
3
|
+
function createNodeWatcher(paths, options) {
|
|
4
|
+
const debounceMs = options.debounceMs ?? 50;
|
|
5
|
+
const setTimer = options.setTimer ?? ((fn, ms) => setTimeout(fn, ms));
|
|
6
|
+
const clearTimer = options.clearTimer ?? ((h) => clearTimeout(h));
|
|
7
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
8
|
+
let timer = null;
|
|
9
|
+
let pending = "";
|
|
10
|
+
const handles = paths.map((path) => options.watch(path, { recursive: true }, (_event, filename) => {
|
|
11
|
+
pending = filename ?? path;
|
|
12
|
+
if (timer !== null) clearTimer(timer);
|
|
13
|
+
timer = setTimer(() => {
|
|
14
|
+
timer = null;
|
|
15
|
+
const changed = pending;
|
|
16
|
+
for (const listener of listeners) listener(changed);
|
|
17
|
+
}, debounceMs);
|
|
18
|
+
}));
|
|
19
|
+
return {
|
|
20
|
+
onChange(listener) {
|
|
21
|
+
listeners.add(listener);
|
|
22
|
+
return () => listeners.delete(listener);
|
|
23
|
+
},
|
|
24
|
+
close() {
|
|
25
|
+
if (timer !== null) clearTimer(timer);
|
|
26
|
+
for (const handle of handles) handle.close();
|
|
27
|
+
listeners.clear();
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
const LIVE_RELOAD = (reloadPath, pollMs) => `<script>(function(){var v=null;setInterval(function(){fetch(${JSON.stringify(reloadPath)}).then(function(r){return r.text()}).then(function(t){if(v===null){v=t}else if(t!==v){location.reload()}}).catch(function(){})},${pollMs})})()<\/script>`;
|
|
32
|
+
/** Create a live-reload {@link DevServer}. The handler serves the app HTML + a version endpoint. */
|
|
33
|
+
function createDevServer(options) {
|
|
34
|
+
const reloadPath = options.reloadPath ?? "/__mindees/version";
|
|
35
|
+
const client = LIVE_RELOAD(reloadPath, options.pollMs ?? 1e3);
|
|
36
|
+
let version = 0;
|
|
37
|
+
let html = options.html;
|
|
38
|
+
const withClient = (h) => h.includes("</body>") ? h.replace("</body>", `${client}</body>`) : h + client;
|
|
39
|
+
return {
|
|
40
|
+
version: () => version,
|
|
41
|
+
setHtml(next) {
|
|
42
|
+
html = next;
|
|
43
|
+
},
|
|
44
|
+
bump() {
|
|
45
|
+
version += 1;
|
|
46
|
+
},
|
|
47
|
+
handle(method, url) {
|
|
48
|
+
if (method !== "GET") return {
|
|
49
|
+
status: 405,
|
|
50
|
+
headers: { allow: "GET" },
|
|
51
|
+
body: "Method Not Allowed"
|
|
52
|
+
};
|
|
53
|
+
const path = url.split("?", 1)[0];
|
|
54
|
+
if (path === "/" || path === "/index.html") return {
|
|
55
|
+
status: 200,
|
|
56
|
+
headers: { "content-type": "text/html; charset=utf-8" },
|
|
57
|
+
body: withClient(html)
|
|
58
|
+
};
|
|
59
|
+
if (path === reloadPath) return {
|
|
60
|
+
status: 200,
|
|
61
|
+
headers: {
|
|
62
|
+
"content-type": "text/plain",
|
|
63
|
+
"cache-control": "no-store"
|
|
64
|
+
},
|
|
65
|
+
body: String(version)
|
|
66
|
+
};
|
|
67
|
+
return {
|
|
68
|
+
status: 404,
|
|
69
|
+
headers: { "content-type": "text/plain" },
|
|
70
|
+
body: "Not Found"
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
/** Render a minimal dev status page for a build result (preview shell; live-reload is injected by the server). */
|
|
76
|
+
function renderDevPage(status) {
|
|
77
|
+
const esc = (s) => s.replace(/[&<>]/g, (c) => c === "&" ? "&" : c === "<" ? "<" : ">");
|
|
78
|
+
const errors = status.diagnostics.filter((d) => d.severity === "error");
|
|
79
|
+
const banner = status.ok ? `<p style="color:#16a34a">✓ build ok — ${status.compiled.length} file(s) compiled</p>` : `<p style="color:#dc2626">✗ build failed — ${errors.length} error(s)</p>`;
|
|
80
|
+
const list = status.diagnostics.map((d) => `<li><strong>${esc(d.severity)}</strong>: ${esc(d.message)}</li>`).join("");
|
|
81
|
+
return `<!doctype html><html><head><meta charset="utf-8"><title>MindeesNative dev</title></head><body><h1>MindeesNative — dev preview</h1>${banner}${list ? `<ul>${list}</ul>` : ""}</body></html>`;
|
|
82
|
+
}
|
|
83
|
+
//#endregion
|
|
84
|
+
export { createDevServer, createNodeWatcher, renderDevPage };
|
|
85
|
+
|
|
86
|
+
//# sourceMappingURL=dev-server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dev-server.js","names":[],"sources":["../src/dev-server.ts"],"sourcesContent":["/**\n * `mindees dev` transport — the live-reload layer over the tested {@link \"./dev\".startDev}\n * orchestrator. Two small, dependency-injected (therefore testable) pieces:\n *\n * - {@link createNodeWatcher} adapts `node:fs.watch` to the {@link \"./dev\".Watcher} interface,\n * debounced so an editor's multi-event save triggers ONE rebuild.\n * - {@link createDevServer} is a pure request handler that serves the app HTML (with a tiny\n * live-reload client injected) and a version endpoint the client polls; `bump()` on each rebuild\n * makes connected browsers reload.\n *\n * The actual `http.createServer` + `fs.watch` instantiation is the thin glue in `bin.ts`; the\n * behavior proven here (debounced change → rebuild → version bump → reload) is the substance.\n *\n * @module\n */\n\nimport type { Watcher } from './dev'\n\n/** A handle to an active filesystem watch (the `node:fs.watch` `FSWatcher` shape we use). */\nexport interface WatchHandle {\n close(): void\n}\n\n/** The `node:fs.watch` signature (injected so the watcher is testable without a real filesystem). */\nexport type WatchFn = (\n path: string,\n options: { recursive?: boolean },\n listener: (event: string, filename: string | null) => void,\n) => WatchHandle\n\n/** Options for {@link createNodeWatcher}. */\nexport interface NodeWatcherOptions {\n /** The watch implementation — `node:fs.watch` in `bin`, a fake in tests. */\n readonly watch: WatchFn\n /** Coalesce a burst of events into one change after this quiet period (ms, default 50). */\n readonly debounceMs?: number\n /** Schedule a debounce timer (default `setTimeout`); injectable for tests. */\n readonly setTimer?: (fn: () => void, ms: number) => unknown\n /** Cancel a debounce timer (default `clearTimeout`); injectable for tests. */\n readonly clearTimer?: (handle: unknown) => void\n}\n\n/** A {@link Watcher} (plus `close()`) backed by `node:fs.watch`, debounced. */\nexport function createNodeWatcher(\n paths: readonly string[],\n options: NodeWatcherOptions,\n): Watcher & { close(): void } {\n const debounceMs = options.debounceMs ?? 50\n const setTimer = options.setTimer ?? ((fn, ms) => setTimeout(fn, ms))\n const clearTimer = options.clearTimer ?? ((h) => clearTimeout(h as ReturnType<typeof setTimeout>))\n const listeners = new Set<(changedPath: string) => void>()\n let timer: unknown = null\n let pending = ''\n\n const handles = paths.map((path) =>\n options.watch(path, { recursive: true }, (_event, filename) => {\n pending = filename ?? path\n if (timer !== null) clearTimer(timer)\n timer = setTimer(() => {\n timer = null\n const changed = pending\n for (const listener of listeners) listener(changed)\n }, debounceMs)\n }),\n )\n\n return {\n onChange(listener): () => void {\n listeners.add(listener)\n return () => listeners.delete(listener)\n },\n close(): void {\n if (timer !== null) clearTimer(timer)\n for (const handle of handles) handle.close()\n listeners.clear()\n },\n }\n}\n\n/** A minimal HTTP response the `bin` glue writes to the socket. */\nexport interface DevServerResponse {\n readonly status: number\n readonly headers: Record<string, string>\n readonly body: string\n}\n\n/** Options for {@link createDevServer}. */\nexport interface DevServerOptions {\n /** The app HTML to serve at `/` (the live-reload client is injected automatically). */\n readonly html: string\n /** Path the live-reload client polls for the build version (default `/__mindees/version`). */\n readonly reloadPath?: string\n /** Poll interval the injected client uses, in ms (default 1000). */\n readonly pollMs?: number\n}\n\n/** A pure dev-server: a request handler + a `bump()` that makes polling browsers reload. */\nexport interface DevServer {\n /** Handle a request → a response (pure given the current html + version). */\n handle(method: string, url: string): DevServerResponse\n /** Replace the served app HTML (call after a rebuild changes the output). */\n setHtml(html: string): void\n /** Bump the build version (call after each rebuild) so connected clients reload. */\n bump(): void\n /** The current build version. */\n version(): number\n}\n\nconst LIVE_RELOAD = (reloadPath: string, pollMs: number): string =>\n `<script>(function(){var v=null;setInterval(function(){fetch(${JSON.stringify(reloadPath)}).then(function(r){return r.text()}).then(function(t){if(v===null){v=t}else if(t!==v){location.reload()}}).catch(function(){})},${pollMs})})()</script>`\n\n/** Create a live-reload {@link DevServer}. The handler serves the app HTML + a version endpoint. */\nexport function createDevServer(options: DevServerOptions): DevServer {\n const reloadPath = options.reloadPath ?? '/__mindees/version'\n const pollMs = options.pollMs ?? 1000\n const client = LIVE_RELOAD(reloadPath, pollMs)\n let version = 0\n let html = options.html\n\n // Inject the live-reload client just before </body> (or append if there's no body tag).\n const withClient = (h: string): string =>\n h.includes('</body>') ? h.replace('</body>', `${client}</body>`) : h + client\n\n return {\n version: () => version,\n setHtml(next): void {\n html = next\n },\n bump(): void {\n version += 1\n },\n handle(method, url): DevServerResponse {\n if (method !== 'GET') {\n return { status: 405, headers: { allow: 'GET' }, body: 'Method Not Allowed' }\n }\n const path = url.split('?', 1)[0]\n if (path === '/' || path === '/index.html') {\n return {\n status: 200,\n headers: { 'content-type': 'text/html; charset=utf-8' },\n body: withClient(html),\n }\n }\n if (path === reloadPath) {\n return {\n status: 200,\n headers: { 'content-type': 'text/plain', 'cache-control': 'no-store' },\n body: String(version),\n }\n }\n return { status: 404, headers: { 'content-type': 'text/plain' }, body: 'Not Found' }\n },\n }\n}\n\n/** A build-status result shape (a subset of the CLI `BuildResult`) for {@link renderDevPage}. */\nexport interface DevBuildStatus {\n readonly ok: boolean\n readonly compiled: readonly string[]\n readonly diagnostics: ReadonlyArray<{ readonly severity: string; readonly message: string }>\n}\n\n/** Render a minimal dev status page for a build result (preview shell; live-reload is injected by the server). */\nexport function renderDevPage(status: DevBuildStatus): string {\n const esc = (s: string): string =>\n s.replace(/[&<>]/g, (c) => (c === '&' ? '&' : c === '<' ? '<' : '>'))\n const errors = status.diagnostics.filter((d) => d.severity === 'error')\n const banner = status.ok\n ? `<p style=\"color:#16a34a\">✓ build ok — ${status.compiled.length} file(s) compiled</p>`\n : `<p style=\"color:#dc2626\">✗ build failed — ${errors.length} error(s)</p>`\n const list = status.diagnostics\n .map((d) => `<li><strong>${esc(d.severity)}</strong>: ${esc(d.message)}</li>`)\n .join('')\n return `<!doctype html><html><head><meta charset=\"utf-8\"><title>MindeesNative dev</title></head><body><h1>MindeesNative — dev preview</h1>${banner}${list ? `<ul>${list}</ul>` : ''}</body></html>`\n}\n"],"mappings":";;AA2CA,SAAgB,kBACd,OACA,SAC6B;CAC7B,MAAM,aAAa,QAAQ,cAAc;CACzC,MAAM,WAAW,QAAQ,cAAc,IAAI,OAAO,WAAW,IAAI,EAAE;CACnE,MAAM,aAAa,QAAQ,gBAAgB,MAAM,aAAa,CAAkC;CAChG,MAAM,4BAAY,IAAI,IAAmC;CACzD,IAAI,QAAiB;CACrB,IAAI,UAAU;CAEd,MAAM,UAAU,MAAM,KAAK,SACzB,QAAQ,MAAM,MAAM,EAAE,WAAW,KAAK,IAAI,QAAQ,aAAa;EAC7D,UAAU,YAAY;EACtB,IAAI,UAAU,MAAM,WAAW,KAAK;EACpC,QAAQ,eAAe;GACrB,QAAQ;GACR,MAAM,UAAU;GAChB,KAAK,MAAM,YAAY,WAAW,SAAS,OAAO;EACpD,GAAG,UAAU;CACf,CAAC,CACH;CAEA,OAAO;EACL,SAAS,UAAsB;GAC7B,UAAU,IAAI,QAAQ;GACtB,aAAa,UAAU,OAAO,QAAQ;EACxC;EACA,QAAc;GACZ,IAAI,UAAU,MAAM,WAAW,KAAK;GACpC,KAAK,MAAM,UAAU,SAAS,OAAO,MAAM;GAC3C,UAAU,MAAM;EAClB;CACF;AACF;AA+BA,MAAM,eAAe,YAAoB,WACvC,+DAA+D,KAAK,UAAU,UAAU,EAAE,kIAAkI,OAAO;;AAGrO,SAAgB,gBAAgB,SAAsC;CACpE,MAAM,aAAa,QAAQ,cAAc;CAEzC,MAAM,SAAS,YAAY,YADZ,QAAQ,UAAU,GACY;CAC7C,IAAI,UAAU;CACd,IAAI,OAAO,QAAQ;CAGnB,MAAM,cAAc,MAClB,EAAE,SAAS,SAAS,IAAI,EAAE,QAAQ,WAAW,GAAG,OAAO,QAAQ,IAAI,IAAI;CAEzE,OAAO;EACL,eAAe;EACf,QAAQ,MAAY;GAClB,OAAO;EACT;EACA,OAAa;GACX,WAAW;EACb;EACA,OAAO,QAAQ,KAAwB;GACrC,IAAI,WAAW,OACb,OAAO;IAAE,QAAQ;IAAK,SAAS,EAAE,OAAO,MAAM;IAAG,MAAM;GAAqB;GAE9E,MAAM,OAAO,IAAI,MAAM,KAAK,CAAC,EAAE;GAC/B,IAAI,SAAS,OAAO,SAAS,eAC3B,OAAO;IACL,QAAQ;IACR,SAAS,EAAE,gBAAgB,2BAA2B;IACtD,MAAM,WAAW,IAAI;GACvB;GAEF,IAAI,SAAS,YACX,OAAO;IACL,QAAQ;IACR,SAAS;KAAE,gBAAgB;KAAc,iBAAiB;IAAW;IACrE,MAAM,OAAO,OAAO;GACtB;GAEF,OAAO;IAAE,QAAQ;IAAK,SAAS,EAAE,gBAAgB,aAAa;IAAG,MAAM;GAAY;EACrF;CACF;AACF;;AAUA,SAAgB,cAAc,QAAgC;CAC5D,MAAM,OAAO,MACX,EAAE,QAAQ,WAAW,MAAO,MAAM,MAAM,UAAU,MAAM,MAAM,SAAS,MAAO;CAChF,MAAM,SAAS,OAAO,YAAY,QAAQ,MAAM,EAAE,aAAa,OAAO;CACtE,MAAM,SAAS,OAAO,KAClB,yCAAyC,OAAO,SAAS,OAAO,yBAChE,6CAA6C,OAAO,OAAO;CAC/D,MAAM,OAAO,OAAO,YACjB,KAAK,MAAM,eAAe,IAAI,EAAE,QAAQ,EAAE,aAAa,IAAI,EAAE,OAAO,EAAE,MAAM,EAC5E,KAAK,EAAE;CACV,OAAO,qIAAqI,SAAS,OAAO,OAAO,KAAK,SAAS,GAAG;AACtL"}
|
package/dist/index.d.ts
CHANGED
|
@@ -6,6 +6,7 @@ import { BuildOptions, BuildResult, buildProject } from "./build.js";
|
|
|
6
6
|
import { CliContext, runCli, runCliAsync } from "./cli.js";
|
|
7
7
|
import { CreateTarget, CreateTargetResult, quoteShellPath, resolveCreateTarget } from "./create-target.js";
|
|
8
8
|
import { DevOptions, DevSession, Watcher, startDev } from "./dev.js";
|
|
9
|
+
import { DevBuildStatus, DevServer, DevServerOptions, NodeWatcherOptions, WatchFn, WatchHandle, createDevServer, createNodeWatcher, renderDevPage } from "./dev-server.js";
|
|
9
10
|
import { doctorSummary, renderDoctor, runDoctor } from "./doctor.js";
|
|
10
11
|
import { TemplatePick, naturalLanguageToTemplate } from "./nl.js";
|
|
11
12
|
import { ScaffoldOptions, ScaffoldResult, scaffold } from "./scaffold.js";
|
|
@@ -30,5 +31,5 @@ declare const maturity: Maturity;
|
|
|
30
31
|
*/
|
|
31
32
|
declare const info: PackageInfo;
|
|
32
33
|
//#endregion
|
|
33
|
-
export { type AiCommandContext, type BuildOptions, type BuildResult, type CliContext, type CommandResult, type CreateTarget, type CreateTargetResult, DEFAULT_TEMPLATE, type DevOptions, type DevSession, type DoctorCheck, type DoctorStatus, type EnvProbe, type ExitCode, type FileSystem, type Maturity, NotImplementedError, type OutputLine, type PackageInfo, type ScaffoldOptions, type ScaffoldResult, TEMPLATES, type Template, type TemplatePick, VERSION, type Watcher, type Writer, buildProject, createMemoryFileSystem, doctorSummary, getTemplate, info, materialize, maturity, name, naturalLanguageToTemplate, notImplemented, quoteShellPath, renderDoctor, resolveCreateTarget, runAiCommand, runCli, runCliAsync, runDoctor, scaffold, startDev, templateNames };
|
|
34
|
+
export { type AiCommandContext, type BuildOptions, type BuildResult, type CliContext, type CommandResult, type CreateTarget, type CreateTargetResult, DEFAULT_TEMPLATE, type DevBuildStatus, type DevOptions, type DevServer, type DevServerOptions, type DevSession, type DoctorCheck, type DoctorStatus, type EnvProbe, type ExitCode, type FileSystem, type Maturity, type NodeWatcherOptions, NotImplementedError, type OutputLine, type PackageInfo, type ScaffoldOptions, type ScaffoldResult, TEMPLATES, type Template, type TemplatePick, VERSION, type WatchFn, type WatchHandle, type Watcher, type Writer, buildProject, createDevServer, createMemoryFileSystem, createNodeWatcher, doctorSummary, getTemplate, info, materialize, maturity, name, naturalLanguageToTemplate, notImplemented, quoteShellPath, renderDevPage, renderDoctor, resolveCreateTarget, runAiCommand, runCli, runCliAsync, runDoctor, scaffold, startDev, templateNames };
|
|
34
35
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/index.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/index.ts"],"mappings":";;;;;;;;;;;;;;;;AAkFA;AAAA,cAnBa,IAAA;AAmBuE;;;;;;;AAAA,cAPvE,QAAA,EAAU,QAAyB;;;;;;cAOnC,IAAA,EAAM,WAAiE"}
|
package/dist/index.js
CHANGED
|
@@ -8,6 +8,7 @@ import { naturalLanguageToTemplate } from "./nl.js";
|
|
|
8
8
|
import { scaffold } from "./scaffold.js";
|
|
9
9
|
import { runCli, runCliAsync } from "./cli.js";
|
|
10
10
|
import { startDev } from "./dev.js";
|
|
11
|
+
import { createDevServer, createNodeWatcher, renderDevPage } from "./dev-server.js";
|
|
11
12
|
import { createMemoryFileSystem } from "./fs.js";
|
|
12
13
|
import { NotImplementedError, notImplemented } from "@mindees/core";
|
|
13
14
|
//#region src/index.ts
|
|
@@ -32,6 +33,6 @@ const info = Object.freeze({
|
|
|
32
33
|
maturity
|
|
33
34
|
});
|
|
34
35
|
//#endregion
|
|
35
|
-
export { DEFAULT_TEMPLATE, NotImplementedError, TEMPLATES, VERSION, buildProject, createMemoryFileSystem, doctorSummary, getTemplate, info, materialize, maturity, name, naturalLanguageToTemplate, notImplemented, quoteShellPath, renderDoctor, resolveCreateTarget, runAiCommand, runCli, runCliAsync, runDoctor, scaffold, startDev, templateNames };
|
|
36
|
+
export { DEFAULT_TEMPLATE, NotImplementedError, TEMPLATES, VERSION, buildProject, createDevServer, createMemoryFileSystem, createNodeWatcher, doctorSummary, getTemplate, info, materialize, maturity, name, naturalLanguageToTemplate, notImplemented, quoteShellPath, renderDevPage, renderDoctor, resolveCreateTarget, runAiCommand, runCli, runCliAsync, runDoctor, scaffold, startDev, templateNames };
|
|
36
37
|
|
|
37
38
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["import type { Maturity, PackageInfo } from '@mindees/core'\nimport { NotImplementedError, notImplemented } from '@mindees/core'\nimport { VERSION } from './version'\n\n/** AI command handler (used by `runCliAsync`; exposed for embedders). */\nexport { type AiCommandContext, runAiCommand } from './ai'\n/** Project build (via @mindees/compiler). */\nexport { type BuildOptions, type BuildResult, buildProject } from './build'\n/** CLI dispatch. `runCli` is synchronous; `runCliAsync` adds the async `ai` command. */\nexport { type CliContext, runCli, runCliAsync } from './cli'\n/** Create target path + package-name resolution. */\nexport {\n type CreateTarget,\n type CreateTargetResult,\n quoteShellPath,\n resolveCreateTarget,\n} from './create-target'\n/** Dev orchestrator (rebuild-on-change). */\nexport { type DevOptions, type DevSession, startDev, type Watcher } from './dev'\n/** Environment diagnostics. */\nexport { doctorSummary, renderDoctor, runDoctor } from './doctor'\n/** Filesystem abstraction. */\nexport { createMemoryFileSystem, type FileSystem } from './fs'\n/** Natural-language → template mapping (offline; AI is Phase 10). */\nexport { naturalLanguageToTemplate, type TemplatePick } from './nl'\n/** Scaffolding. */\nexport {\n type ScaffoldOptions,\n type ScaffoldResult,\n scaffold,\n} from './scaffold'\n/** Templates. */\nexport {\n DEFAULT_TEMPLATE,\n getTemplate,\n materialize,\n TEMPLATES,\n type Template,\n templateNames,\n} from './templates'\n/** Shared types. */\nexport type {\n CommandResult,\n DoctorCheck,\n DoctorStatus,\n EnvProbe,\n ExitCode,\n OutputLine,\n Writer,\n} from './types'\n\n/** The npm package name. */\nexport const name = '@mindees/cli'\n\n/** The package version. All `@mindees/*` packages share one locked version line. */\nexport { VERSION }\n\n/**\n * Current maturity. The CLI core — dispatch, `create` (+ templates), `build`\n * (via the compiler), `doctor`, `info`, and the dev rebuild orchestrator — is\n * implemented and tested. The live dev-server HTTP/HMR transport is a developer\n * preview; on-device NL→app generation is Phase 10 (today `--prompt` maps to a\n * template deterministically).\n */\nexport const maturity: Maturity = 'experimental'\n\n/**\n * Static identity + maturity metadata for this package. Frozen so the\n * self-reported identity tooling introspects cannot be mutated at runtime,\n * matching the `readonly` fields of {@link PackageInfo}.\n */\nexport const info: PackageInfo = Object.freeze({ name, version: VERSION, maturity })\n\nexport type { Maturity, PackageInfo }\nexport { NotImplementedError, notImplemented }\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["import type { Maturity, PackageInfo } from '@mindees/core'\nimport { NotImplementedError, notImplemented } from '@mindees/core'\nimport { VERSION } from './version'\n\n/** AI command handler (used by `runCliAsync`; exposed for embedders). */\nexport { type AiCommandContext, runAiCommand } from './ai'\n/** Project build (via @mindees/compiler). */\nexport { type BuildOptions, type BuildResult, buildProject } from './build'\n/** CLI dispatch. `runCli` is synchronous; `runCliAsync` adds the async `ai` command. */\nexport { type CliContext, runCli, runCliAsync } from './cli'\n/** Create target path + package-name resolution. */\nexport {\n type CreateTarget,\n type CreateTargetResult,\n quoteShellPath,\n resolveCreateTarget,\n} from './create-target'\n/** Dev orchestrator (rebuild-on-change). */\nexport { type DevOptions, type DevSession, startDev, type Watcher } from './dev'\nexport {\n createDevServer,\n createNodeWatcher,\n type DevBuildStatus,\n type DevServer,\n type DevServerOptions,\n type NodeWatcherOptions,\n renderDevPage,\n type WatchFn,\n type WatchHandle,\n} from './dev-server'\n/** Environment diagnostics. */\nexport { doctorSummary, renderDoctor, runDoctor } from './doctor'\n/** Filesystem abstraction. */\nexport { createMemoryFileSystem, type FileSystem } from './fs'\n/** Natural-language → template mapping (offline; AI is Phase 10). */\nexport { naturalLanguageToTemplate, type TemplatePick } from './nl'\n/** Scaffolding. */\nexport {\n type ScaffoldOptions,\n type ScaffoldResult,\n scaffold,\n} from './scaffold'\n/** Templates. */\nexport {\n DEFAULT_TEMPLATE,\n getTemplate,\n materialize,\n TEMPLATES,\n type Template,\n templateNames,\n} from './templates'\n/** Shared types. */\nexport type {\n CommandResult,\n DoctorCheck,\n DoctorStatus,\n EnvProbe,\n ExitCode,\n OutputLine,\n Writer,\n} from './types'\n\n/** The npm package name. */\nexport const name = '@mindees/cli'\n\n/** The package version. All `@mindees/*` packages share one locked version line. */\nexport { VERSION }\n\n/**\n * Current maturity. The CLI core — dispatch, `create` (+ templates), `build`\n * (via the compiler), `doctor`, `info`, and the dev rebuild orchestrator — is\n * implemented and tested. The live dev-server HTTP/HMR transport is a developer\n * preview; on-device NL→app generation is Phase 10 (today `--prompt` maps to a\n * template deterministically).\n */\nexport const maturity: Maturity = 'experimental'\n\n/**\n * Static identity + maturity metadata for this package. Frozen so the\n * self-reported identity tooling introspects cannot be mutated at runtime,\n * matching the `readonly` fields of {@link PackageInfo}.\n */\nexport const info: PackageInfo = Object.freeze({ name, version: VERSION, maturity })\n\nexport type { Maturity, PackageInfo }\nexport { NotImplementedError, notImplemented }\n"],"mappings":";;;;;;;;;;;;;;;AA+DA,MAAa,OAAO;;;;;;;;AAYpB,MAAa,WAAqB;;;;;;AAOlC,MAAa,OAAoB,OAAO,OAAO;CAAE;CAAM,SAAS;CAAS;AAAS,CAAC"}
|
package/dist/version.d.ts
CHANGED
package/dist/version.js
CHANGED
package/dist/version.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"version.js","names":[],"sources":["../src/version.ts"],"sourcesContent":["/**\n * Single source of truth for the `@mindees/cli` package version.\n *\n * All `@mindees/*` packages share one locked version line (Changesets, fixed\n * group). Keeping it in its own dependency-free module lets both the public\n * package metadata ({@link index}) and the scaffolder's generated\n * `package.json` ({@link templates}) pin to the same value without a circular\n * import.\n *\n * @module\n */\n\n/** The package version. All `@mindees/*` packages share one locked version line. */\nexport const VERSION = '0.
|
|
1
|
+
{"version":3,"file":"version.js","names":[],"sources":["../src/version.ts"],"sourcesContent":["/**\n * Single source of truth for the `@mindees/cli` package version.\n *\n * All `@mindees/*` packages share one locked version line (Changesets, fixed\n * group). Keeping it in its own dependency-free module lets both the public\n * package metadata ({@link index}) and the scaffolder's generated\n * `package.json` ({@link templates}) pin to the same value without a circular\n * import.\n *\n * @module\n */\n\n/** The package version. All `@mindees/*` packages share one locked version line. */\nexport const VERSION = '0.6.0'\n"],"mappings":";;;;;;;;;;;;;AAaA,MAAa,UAAU"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mindees/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "MindeesNative Forge — the `mindees` CLI: create, build (via the compiler), dev (rebuild orchestrator), doctor, info.",
|
|
5
5
|
"license": "MIT OR Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -9,7 +9,8 @@
|
|
|
9
9
|
"mindees": "./dist/bin.js"
|
|
10
10
|
},
|
|
11
11
|
"files": [
|
|
12
|
-
"dist"
|
|
12
|
+
"dist",
|
|
13
|
+
"assets"
|
|
13
14
|
],
|
|
14
15
|
"exports": {
|
|
15
16
|
".": {
|
|
@@ -26,9 +27,9 @@
|
|
|
26
27
|
"directory": "packages/cli"
|
|
27
28
|
},
|
|
28
29
|
"dependencies": {
|
|
29
|
-
"@mindees/core": "0.
|
|
30
|
-
"@mindees/compiler": "0.
|
|
31
|
-
"@mindees/ai": "0.
|
|
30
|
+
"@mindees/core": "0.6.0",
|
|
31
|
+
"@mindees/compiler": "0.6.0",
|
|
32
|
+
"@mindees/ai": "0.6.0"
|
|
32
33
|
},
|
|
33
34
|
"scripts": {
|
|
34
35
|
"build": "tsdown",
|