Skip to content

Commit 52c16ba

Browse files
authored
feat(cli): build command (#34)
1 parent c46ef0b commit 52c16ba

22 files changed

Lines changed: 473 additions & 153 deletions

AGENTS.md

Lines changed: 42 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,9 @@ Do not introduce extra packages, adapters, or broad CLI workflows unless they
1717
are required to make the current IPC milestone work. The existing
1818
`@gd-kirie/ipc` package is a thin browser-side transport wrapper; do not expand
1919
it into an application event or invocation layer unless the user explicitly asks
20-
for that higher-level work. The planned Kirie CLI v1 exception is limited to
21-
`kirie dev` for desktop development through Vite and Godot.
20+
for that higher-level work. The planned Kirie CLI exception is limited to the
21+
core app workflow described below: development, local build inputs,
22+
initialization, and diagnostics.
2223

2324
The mobile IPC v1 experiment keeps Kirie core byte-oriented and CBOR-based with
2425
text, binary, and data lanes. JSON belongs to callers or adapters, not to Kirie
@@ -142,22 +143,47 @@ The planned Kirie app layout is:
142143
- `addons/kirie/`
143144
- optional `addons/godot_cef/`
144145

145-
Kirie CLI v1 should only implement `kirie dev` for desktop Godot development.
146-
It should be installed through npm, call Vite's JavaScript API directly, let
147-
Vite resolve port conflicts, launch Godot as a child process, and inject
148-
`KIRIE_DEV=1` and `KIRIE_WEB_URL=<resolved Vite URL>`.
149-
150-
Keep `kirie create`, `kirie export`, addon installation, Godot CEF installation,
151-
export preset management, and mobile dev targets outside the CLI v1 scope.
152-
These may be implemented later when explicitly planned. Future mobile dev
153-
targets should use a unified platform and device selector such as
146+
Kirie CLI should be installed through npm and expose these planned commands:
147+
148+
- `kirie dev`: start the Vite development server, launch Godot as a child
149+
process, and inject `KIRIE_DEV=1` and
150+
`KIRIE_WEB_URL=<resolved Vite URL>`.
151+
- `kirie build`: build every configured local input needed by a runnable or
152+
exportable Godot project, without exporting platform packages.
153+
- `kirie build web`: build only the Vite web output for Godot resource loading.
154+
- `kirie build dotnet`: build only the Godot C#/.NET project when one is
155+
configured or discovered.
156+
- `kirie init`: explicitly initialize a Kirie project and write required
157+
project configuration.
158+
- `kirie doctor`: diagnose project configuration without writing files.
159+
- `kirie doctor --fix`: explicitly repair supported configuration problems.
160+
161+
Keep `kirie create`, `kirie export`, and mobile dev targets outside the current
162+
CLI scope. These may be implemented later when explicitly planned. Future
163+
mobile dev targets should use a unified platform and device selector such as
154164
`kirie dev ios --device <selector>` or
155165
`kirie dev android --device <selector>`; do not expose simulator and real device
156166
as separate user-facing target names.
157167

158168
Kirie enforces Vite for user web source. Advanced Vite options belong in
159169
`kirie.config.ts` under `web.vite`, but Kirie owns `root`, `base`,
160-
`server.host`, `server.port`, `server.open`, and `build.outDir`.
170+
`server.host`, `server.port`, `server.open`, and `build.outDir`. Explicit CLI
171+
flags may override runtime server values for a single command invocation.
172+
Planned `kirie dev` flags include `--config <path>` for the Kirie config,
173+
`--project <dir>` for the Godot project, `--godot <path>` for a Godot executable
174+
override, and Vite-shaped flags such as `--host <host>`, `--port <number>`,
175+
`--strict-port`, `--mode <mode>`, `--force`, `--log-level <level>`,
176+
`--clear-screen`, and `--no-clear-screen`. Kirie must either parse and map
177+
Vite-shaped flags explicitly to Vite's public JavaScript API or proxy them to
178+
the real Vite CLI; unknown flags must not be silently ignored. Arguments after
179+
`--` on `kirie dev` belong to Godot.
180+
181+
Only explicit setup and repair commands may write Godot configuration.
182+
`kirie init` and `kirie doctor --fix` may modify `project.godot` or
183+
`export_presets.cfg`, but those writes must go through Godot itself, for
184+
example a headless Godot helper using `ProjectSettings` or `ConfigFile`. Runtime
185+
commands such as `kirie dev`, `kirie build`, and future `kirie export` should
186+
fail on wrong configuration and point users to `kirie doctor`.
161187

162188
Kirie user projects should not contain Capacitor-style `ios/` or `android/`
163189
native project directories. Native features belong in Godot plugins.
@@ -191,6 +217,10 @@ For the current milestone, iOS should be owned by the standard addon tree:
191217

192218
- Keep changes aligned with the current milestone.
193219
- When a same-session temporary decision is replaced, converge on the latest decision directly; do not add compatibility unless explicitly requested.
220+
- Do not add regression tests solely to prevent an implementation pattern that
221+
is already known to be invalid or contrary to the chosen API. Remove or
222+
correct the invalid usage instead; keep tests focused on supported behavior
223+
and realistic regressions.
194224
- Use English only for agent-facing communication, project-maintenance notes,
195225
AGENTS updates, and project documentation unless the user explicitly requests
196226
a non-English artifact.

docs/architecture.md

Lines changed: 78 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -147,21 +147,28 @@ Directory responsibilities are:
147147
- `src-web`: Vite web UI source and production build output.
148148
- `addons`: Godot plugins. Kirie remains installed as `addons/kirie`, and Godot
149149
CEF remains installed as `addons/godot_cef`.
150-
- `kirie.config.ts`: Kirie CLI development-time configuration.
150+
- `kirie.config.ts`: Kirie CLI configuration for coordinating Godot, Vite, and
151+
local build inputs.
151152

152153
Kirie does not own native platform project directories. Do not introduce
153154
Capacitor-style `ios/` or `android/` project trees into Kirie user projects.
154155
Native capabilities should be provided by Godot plugins.
155156

156-
Kirie CLI v1 is scoped to one command:
157+
The planned Kirie CLI surface is intentionally small but covers the full local
158+
app workflow:
157159

158160
```sh
159161
kirie dev
162+
kirie build
163+
kirie build web
164+
kirie build dotnet
165+
kirie init
166+
kirie doctor
167+
kirie doctor --fix
160168
```
161169

162-
`kirie dev` targets desktop Godot only in v1. It starts a Vite development
163-
server through Vite's JavaScript API, reads the actual resolved URL after Vite
164-
listens, launches Godot as a child process, and injects:
170+
`kirie dev` starts a Vite development server, reads the actual resolved URL
171+
after Vite listens, launches Godot as a child process, and injects:
165172

166173
```text
167174
KIRIE_DEV=1
@@ -182,15 +189,27 @@ named-class references.
182189
The CLI does not support Finder, Dock, or other non-CLI launched macOS app
183190
processes. It only supports development sessions that the CLI starts and owns.
184191

185-
Kirie CLI v1 does not implement:
186-
187-
- `kirie create`
188-
- `kirie build`
189-
- `kirie export`
190-
- addon installation
191-
- Godot CEF installation
192-
- export preset management
193-
- BrowserWindow APIs
192+
`kirie build` builds local intermediate artifacts needed by a runnable or
193+
exportable Godot project, but it does not produce platform application packages.
194+
It should build every configured or clearly discovered input. `kirie build web`
195+
builds only the Vite web output, and `kirie build dotnet` builds only the
196+
Godot C#/.NET project when one is configured or discovered. If no C# project is
197+
configured or discovered, the aggregate `kirie build` command may skip the
198+
`.NET` step; if a C# project is present, C# build failure must fail the command.
199+
200+
`kirie export` remains future work. When it exists, it should mean a complete
201+
platform export workflow: build local inputs first, then call Godot's export
202+
flow for the selected platform or preset. It should not silently create or
203+
repair project configuration as part of export.
204+
205+
`kirie init` and `kirie doctor --fix` are the explicit commands allowed to
206+
write configuration. `kirie init` initializes Kirie-owned files and may register
207+
required Godot project settings. `kirie doctor` is read-only diagnostics.
208+
`kirie doctor --fix` may apply supported repairs. Any writes to Godot-owned
209+
configuration files, including `project.godot` and `export_presets.cfg`, must
210+
go through Godot itself, for example a headless helper script using
211+
`ProjectSettings` or `ConfigFile`. JavaScript code must not patch
212+
Godot configuration text directly.
194213

195214
Kirie enforces Vite as the web toolchain. Users should not hand-write a fixed
196215
development URL. The CLI should let Vite handle port conflicts, then pass the
@@ -211,17 +230,54 @@ User-supplied `web.vite` may extend Vite for plugins, aliases, defines, CSS,
211230
JSON, extra assets, proxying, headers, HMR details, Rollup options, and
212231
dependency optimization. It must not override Kirie-owned invariants such as
213232
`root`, `base`, `server.host`, `server.port`, `server.open`, or
214-
`build.outDir`.
233+
`build.outDir`. Explicit command-line flags may override runtime server values
234+
for a single invocation.
235+
236+
Kirie command-line flags are Kirie API, not an implicit promise to support the
237+
entire Vite CLI surface. The planned `kirie dev` flags are:
238+
239+
```text
240+
--config <path> Kirie config file path, not a Vite config path.
241+
--project <dir> Godot project directory, defaulting to the current project.
242+
--godot <path> Godot executable override.
243+
--host <host> Vite dev server host override.
244+
--port <number> Vite dev server port override.
245+
--strict-port Fail if the requested Vite port is unavailable.
246+
--mode <mode> Vite mode.
247+
--force Force Vite dependency pre-bundling.
248+
--log-level <level> Vite log level: info, warn, error, or silent.
249+
--clear-screen Allow Vite to clear the terminal.
250+
--no-clear-screen Prevent Vite from clearing the terminal.
251+
```
252+
253+
Kirie must either parse and map Vite-shaped flags explicitly to Vite's public
254+
JavaScript API or proxy them to the real Vite CLI. Unknown flags must fail
255+
instead of being silently ignored. Arguments after `--` on `kirie dev` are
256+
reserved for Godot:
257+
258+
```sh
259+
kirie dev --host 0.0.0.0 --port 5173 --mode staging -- --verbose
260+
```
261+
262+
`--open` is intentionally not part of `kirie dev` because Kirie launches Godot
263+
instead of opening a browser. `--base`, `--outDir`, and Vite's own `--config`
264+
are also not part of the `kirie dev` surface because Kirie owns those values
265+
through `kirie.config.ts` and the app layout.
266+
267+
The current CLI plan does not implement:
268+
269+
- `kirie create`
270+
- `kirie export`
271+
- mobile dev targets
272+
- BrowserWindow APIs
215273

216274
Future mobile development targets should use one platform command with unified
217275
device selection, for example `kirie dev ios --device <selector>` and
218276
`kirie dev android --device <selector>`. The user-facing API should not split
219277
iOS simulator and iOS device into separate target names. Kirie may still use
220278
different launch backends internally for simulators, real devices, Android
221279
emulators, and Android devices. Mobile development targets are explicitly out
222-
of scope for CLI v1. Future export support should own the production web build
223-
and Godot export as one workflow, instead of exposing a separate `kirie build`
224-
command that only wraps Vite.
280+
of scope for the current CLI plan.
225281

226282
## Packaged web resource loading
227283

@@ -242,8 +298,10 @@ default production entry. When that migration is implemented, the addon export
242298
plugin should package `res://src-web/dist` for Android and iOS exports, fail
243299
export when `res://src-web/dist/index.html` is missing, and drop the previous
244300
`res://web` behavior instead of preserving a compatibility layer. Users should
245-
continue to use Godot's official export preset flow; Kirie should not create or
246-
manage export presets.
301+
continue to use Godot's official export preset flow by default. Kirie may
302+
diagnose export preset issues, and explicit setup or repair commands may write
303+
supported preset changes through Godot, but normal run, build, and export
304+
commands must not silently mutate export presets.
247305

248306
## Desktop Godot CEF direction
249307

docs/dreams/browser-window.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ Until that resolver exists, users can manually compose Godot `Window` nodes and
4848

4949
## Out of scope
5050

51-
Keep BrowserWindow separate from the current CLI v1 work. Do not combine it
51+
Keep BrowserWindow separate from the current CLI work. Do not combine it
5252
with:
5353

5454
- `kirie dev`

docs/references.md

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,14 @@ packaging, or platform WebView bridge details.
1616
- [Godot Android plugins (stable)](https://docs.godotengine.org/en/stable/tutorials/platform/android/android_plugin.html)
1717
Main reference for Godot Android plugin v2 packaging and export flow.
1818
- [Command line tutorial (stable)](https://docs.godotengine.org/en/stable/tutorials/editor/command_line_tutorial.html)
19-
Reference for `--import`, `--path`, `--remote-debug`, command-line running,
20-
and export behavior.
19+
Reference for `--import`, `--path`, `--remote-debug`, `--script`,
20+
`--build-solutions`, command-line running, and export behavior.
21+
- [ProjectSettings (stable)](https://docs.godotengine.org/en/stable/classes/class_projectsettings.html)
22+
Reference for reading and saving `project.godot` through Godot instead of
23+
hand-editing the file from JavaScript.
24+
- [ConfigFile (stable)](https://docs.godotengine.org/en/stable/classes/class_configfile.html)
25+
Reference for Godot's INI-like configuration file API and Variant-aware
26+
reading and writing.
2127
- [GDScript reference: registering named classes](https://docs.godotengine.org/en/stable/tutorials/scripting/gdscript/gdscript_basics.html#registering-named-classes)
2228
Reference for `class_name` scripts such as `GdKirie` and `KirieNode`, which
2329
Godot exposes as named/global script classes after project scanning.
@@ -146,6 +152,8 @@ packaging, or platform WebView bridge details.
146152
Reference for source files included in PackageReference-based NuGet packages.
147153
- [dotnet pack](https://learn.microsoft.com/en-us/dotnet/core/tools/dotnet-pack)
148154
Reference for creating NuGet packages from .NET projects.
155+
- [dotnet build](https://learn.microsoft.com/en-us/dotnet/core/tools/dotnet-build)
156+
Reference for compiling .NET projects without publishing or packaging them.
149157
- [dotnet nuget push](https://learn.microsoft.com/en-us/dotnet/core/tools/dotnet-nuget-push)
150158
Reference for publishing NuGet packages.
151159

@@ -164,6 +172,9 @@ packaging, or platform WebView bridge details.
164172
- [Vite JavaScript API](https://vite.dev/guide/api-javascript.html)
165173
Reference for `createServer()`, `build()`, `mergeConfig()`, and dev server
166174
`resolvedUrls`, which underpin the planned `kirie dev` implementation.
175+
- [Vite CLI](https://vite.dev/guide/cli)
176+
Reference for Vite command-line flags when Kirie chooses to expose or proxy
177+
Vite-shaped arguments.
167178
- [Vite server options](https://vite.dev/config/server-options.html)
168179
Reference for development server host, port, strict port behavior, and related
169180
options owned by Kirie CLI defaults.

packages/cli/src/build.test.ts

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
import fs from "node:fs/promises";
2+
import path from "node:path";
3+
import { afterEach, describe, expect, it } from "vitest";
4+
5+
import { runBuild, runBuildDotnet, runBuildWeb } from "./build.ts";
6+
import { createBasicKirieCliProjectTracker, installKirieConfigFixture } from "./test-project.ts";
7+
8+
const projects = createBasicKirieCliProjectTracker("kirie-cli-build-");
9+
10+
afterEach(async () => {
11+
await projects.cleanup();
12+
});
13+
14+
describe("runBuildWeb", () => {
15+
it("builds the Vite web output into src-web/dist", async () => {
16+
const project = await projects.copy();
17+
18+
await runBuildWeb({ cwd: project });
19+
20+
await expect(fs.access(path.join(project, "src-web", "dist", "index.html"))).resolves.toBe(
21+
undefined,
22+
);
23+
});
24+
25+
it("rejects Kirie-owned Vite build options", async () => {
26+
const project = await projects.copy();
27+
await installKirieConfigFixture(project, "build-owned-out-dir.kirie.config.ts");
28+
29+
// TODO: Revisit whether `build.outDir` should be configurable once the
30+
// packaged resource layout is settled.
31+
await expect(runBuildWeb({ cwd: project })).rejects.toThrow(
32+
"web.vite.build.outDir is owned by Kirie",
33+
);
34+
});
35+
36+
it("loads kirie.config.ts with Vite build command context", async () => {
37+
const project = await projects.copy();
38+
await installKirieConfigFixture(project, "build-context.kirie.config.ts");
39+
40+
await runBuildWeb({ cwd: project });
41+
42+
const assets = await fs.readdir(path.join(project, "src-web", "dist", "assets"));
43+
44+
expect(assets.some((asset) => asset.endsWith(".js.map"))).toBe(true);
45+
});
46+
});
47+
48+
describe("runBuild", () => {
49+
it("skips dotnet when dotnet reports no project or solution", async () => {
50+
const project = await projects.copy();
51+
52+
await runBuild({ cwd: project });
53+
54+
await expect(fs.access(path.join(project, "src-web", "dist", "index.html"))).resolves.toBe(
55+
undefined,
56+
);
57+
});
58+
});
59+
60+
describe("runBuildDotnet", () => {
61+
it("builds a discovered .NET project", async () => {
62+
const project = await projects.copy();
63+
await fs.writeFile(
64+
path.join(project, "Example.csproj"),
65+
`<Project Sdk="Microsoft.NET.Sdk">
66+
<PropertyGroup>
67+
<EnableDefaultCompileItems>false</EnableDefaultCompileItems>
68+
<TargetFramework>net10.0</TargetFramework>
69+
</PropertyGroup>
70+
</Project>
71+
`,
72+
);
73+
74+
await runBuildDotnet({ cwd: project });
75+
76+
await expect(fs.access(path.join(project, "bin", "Debug", "net10.0"))).resolves.toBe(undefined);
77+
});
78+
79+
it("fails when dotnet reports no project or solution", async () => {
80+
const project = await projects.copy();
81+
82+
await expect(runBuildDotnet({ cwd: project })).rejects.toThrow("dotnet build failed");
83+
});
84+
});

0 commit comments

Comments
 (0)