@drawcall/create 0.0.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/README.md +249 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +18 -0
- package/dist/command.d.ts +16 -0
- package/dist/command.js +123 -0
- package/dist/constants.d.ts +26 -0
- package/dist/constants.js +94 -0
- package/dist/create.d.ts +35 -0
- package/dist/create.js +348 -0
- package/dist/harness.d.ts +13 -0
- package/dist/harness.js +81 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +8 -0
- package/dist/progress-log.d.ts +31 -0
- package/dist/progress-log.js +95 -0
- package/dist/prompts.d.ts +6 -0
- package/dist/prompts.js +138 -0
- package/dist/scaffold.d.ts +15 -0
- package/dist/scaffold.js +220 -0
- package/dist/subprocess.d.ts +21 -0
- package/dist/subprocess.js +117 -0
- package/package.json +47 -0
package/README.md
ADDED
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
# @drawcall/create
|
|
2
|
+
|
|
3
|
+
`@drawcall/create` is the project creator for the Drawcall 3D app/game stack. It does more than create files: it starts a new repo, installs the local agent toolkit, runs a harness to survey the available Drawcall assets and technologies, turns the user's prompt into a fixed goal, writes a plan, and then runs build turns until the plan is closed or the turn budget is reached.
|
|
4
|
+
|
|
5
|
+
The package is a local-orchestration layer over the rest of the Drawcall workspace. The sibling projects provide the actual capabilities:
|
|
6
|
+
|
|
7
|
+
- `../skills` routes an agent from a topic in the user's goal to the right technology, pattern, and quality bar.
|
|
8
|
+
- `../market` provides access to semantically searchable templates and assets.
|
|
9
|
+
- `../acta` provides 3D character behavior and animation logic.
|
|
10
|
+
- `../uikitml` provides spatial/XR UI authoring and conversion.
|
|
11
|
+
- `../vitexec` provides runtime proof from inside a running Vite app.
|
|
12
|
+
- `../speech` provides text-to-speech for apps that need voice output.
|
|
13
|
+
- `../flipbook` allows to render flipbook animations.
|
|
14
|
+
|
|
15
|
+
`create` is the glue that installs those capabilities into a generated project and gives the selected harness a staged way to use them without shrinking the user's goal into a nominal prototype.
|
|
16
|
+
|
|
17
|
+
## Why This Exists
|
|
18
|
+
|
|
19
|
+
The hard part of AI-generated 3D apps is not making the first canvas appear. It is keeping each harness turn grounded in what is real:
|
|
20
|
+
|
|
21
|
+
- what the user actually asked for,
|
|
22
|
+
- what the current product actually does,
|
|
23
|
+
- which assets and technologies actually fit,
|
|
24
|
+
- what still remains,
|
|
25
|
+
- and what has been proven in the running product.
|
|
26
|
+
|
|
27
|
+
`@drawcall/create` turns that into a small set of records and stages. A turn either thinks and writes context, or builds and proves a slice. The harness is never supposed to silently replace the goal with a smaller demo just because a part is hard or no fitting tool exists.
|
|
28
|
+
|
|
29
|
+
## Pipeline
|
|
30
|
+
|
|
31
|
+
The full pipeline is:
|
|
32
|
+
|
|
33
|
+
```text
|
|
34
|
+
scaffold -> (template || survey-assets || survey-technology) -> goal -> plan -> build...
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
The middle group runs concurrently during a `full` run because those stages write disjoint outputs:
|
|
38
|
+
|
|
39
|
+
- `template` may apply a fitting Market starter into the product.
|
|
40
|
+
- `survey-assets` writes a scratch survey of fitting Market assets.
|
|
41
|
+
- `survey-technology` writes a scratch survey of fitting installed skills and packages.
|
|
42
|
+
|
|
43
|
+
After that barrier, the `goal` stage writes the goal record, the `plan` stage writes ordered build steps, and each `build` turn implements the first remaining step, proves it in the real product, and updates the records.
|
|
44
|
+
|
|
45
|
+
## Records
|
|
46
|
+
|
|
47
|
+
Generated projects use three committed records:
|
|
48
|
+
|
|
49
|
+
| File | Role |
|
|
50
|
+
| ----------- | ----------------------------------------------------------------- |
|
|
51
|
+
| `README.md` | State record: what the product actually is right now. |
|
|
52
|
+
| `GOAL.md` | Goal record: the fixed finished product and what real-done means. |
|
|
53
|
+
| `PLAN.md` | Plan record: ordered steps from current state to goal. |
|
|
54
|
+
|
|
55
|
+
They also use scratch files that should not be committed:
|
|
56
|
+
|
|
57
|
+
| File | Role |
|
|
58
|
+
| ----------------------- | ------------------------------------------------------------------------------ |
|
|
59
|
+
| `surveys/ASSETS.md` | Asset survey: fitting Market templates/assets and named fit-gaps. |
|
|
60
|
+
| `surveys/TECHNOLOGY.md` | Technology survey: fitting installed skills/packages and the APIs that matter. |
|
|
61
|
+
| `proof/` | Screenshots, clips, traces, and other proof files. |
|
|
62
|
+
| `drawcall-create.log` | Full subprocess session log. |
|
|
63
|
+
|
|
64
|
+
The surveys are deliberately scratch. They ground the goal and plan stages, but the lasting truth of the project lives in `README.md`, `GOAL.md`, and `PLAN.md`.
|
|
65
|
+
|
|
66
|
+
## Stages
|
|
67
|
+
|
|
68
|
+
`drawcall-create` exposes the pipeline as stages:
|
|
69
|
+
|
|
70
|
+
| Stage | What it does |
|
|
71
|
+
| ------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
72
|
+
| `scaffold` | Creates a new repo, base folders, `.gitignore`, package file, installed skills, and npm dependencies. No harness turn and no README. |
|
|
73
|
+
| `template` | Runs one harness turn to search `@drawcall/market` for a fitting starter and apply it into the existing repo. Ensures a state-record README exists. |
|
|
74
|
+
| `survey-assets` | Runs one survey turn and writes `surveys/ASSETS.md`. |
|
|
75
|
+
| `survey-technology` | Runs one survey turn and writes `surveys/TECHNOLOGY.md`. |
|
|
76
|
+
| `goal` | Writes `GOAL.md` from the user's prompt, current state, and surveys. |
|
|
77
|
+
| `plan` | Writes `PLAN.md` from the fixed goal, current state, and surveys, then updates `README.md`. |
|
|
78
|
+
| `build` | Runs one build turn against the first remaining plan step. |
|
|
79
|
+
| `full` | Runs the whole chain, then loops build turns up to the configured turn budget. |
|
|
80
|
+
|
|
81
|
+
Stages after `scaffold` operate on an existing git repo in the current directory. `full` and `scaffold` create a new project directory.
|
|
82
|
+
|
|
83
|
+
## CLI
|
|
84
|
+
|
|
85
|
+
```sh
|
|
86
|
+
npm run dev -- --name my-game "make a third-person island adventure"
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
After publishing/building, the binary is:
|
|
90
|
+
|
|
91
|
+
```sh
|
|
92
|
+
drawcall-create --name my-game "make a third-person island adventure"
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Useful options:
|
|
96
|
+
|
|
97
|
+
```sh
|
|
98
|
+
drawcall-create --stage scaffold --name my-game "make a marble maze"
|
|
99
|
+
drawcall-create --stage template "make a marble maze"
|
|
100
|
+
drawcall-create --stage survey-assets "make a marble maze"
|
|
101
|
+
drawcall-create --stage survey-technology "make a marble maze"
|
|
102
|
+
drawcall-create --stage goal "make a marble maze"
|
|
103
|
+
drawcall-create --stage plan "make a marble maze"
|
|
104
|
+
drawcall-create --stage build "make a marble maze"
|
|
105
|
+
|
|
106
|
+
drawcall-create --harness codex "make a marble maze"
|
|
107
|
+
drawcall-create --harness-timeout-minutes 30 "make a marble maze"
|
|
108
|
+
drawcall-create --max-turns 5 "make a marble maze"
|
|
109
|
+
drawcall-create --skip-template "make a marble maze"
|
|
110
|
+
drawcall-create "make a marble maze" -- --model gpt-5
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
Supported harnesses are `opencode`, `codex`, `claude`, `pi`, and `gemini`. If no harness is specified, `create` picks the first supported command available on `PATH`.
|
|
114
|
+
|
|
115
|
+
Each harness invocation defaults to a 20 minute timeout. A `full` run defaults to 10 build turns.
|
|
116
|
+
Use `--skip-template` on a `full` run to skip starter-template search and build from scratch while still surveying assets and technology.
|
|
117
|
+
|
|
118
|
+
## Installed Toolkit
|
|
119
|
+
|
|
120
|
+
Fresh projects get agent skills from:
|
|
121
|
+
|
|
122
|
+
- `drawcall-ai/vitexec`
|
|
123
|
+
- `drawcall-ai/uikitml`
|
|
124
|
+
- `drawcall-ai/acta`
|
|
125
|
+
- `drawcall-ai/market`
|
|
126
|
+
- `drawcall-ai/speech`
|
|
127
|
+
- `drawcall-ai/flipbook`
|
|
128
|
+
- `drawcall-ai/skills`
|
|
129
|
+
|
|
130
|
+
Fresh projects also install the runtime packages that those skills commonly route to:
|
|
131
|
+
|
|
132
|
+
- `vitexec`
|
|
133
|
+
- `@drawcall/uikitml`
|
|
134
|
+
- `@drawcall/acta`
|
|
135
|
+
- `@drawcall/market`
|
|
136
|
+
- `@drawcall/flipbook`
|
|
137
|
+
- `@pmndrs/uikit`
|
|
138
|
+
- `@pmndrs/pointer-events`
|
|
139
|
+
- `@pmndrs/viverse`
|
|
140
|
+
- `navcat`
|
|
141
|
+
- `three`
|
|
142
|
+
- `vite`
|
|
143
|
+
- `typescript`
|
|
144
|
+
- `elics`
|
|
145
|
+
- `postprocessing`
|
|
146
|
+
|
|
147
|
+
`create` also patches local CLI shims for `@drawcall/acta` and `@drawcall/market` after installation so their CLIs resolve correctly inside a fresh generated project.
|
|
148
|
+
|
|
149
|
+
## Related Projects
|
|
150
|
+
|
|
151
|
+
### `../skills`
|
|
152
|
+
|
|
153
|
+
`../skills` is the general Drawcall skill pack. It is not one runtime library; it is the routing layer that teaches a harness which technology and pattern to use for a need in a 3D app.
|
|
154
|
+
|
|
155
|
+
Its skills cover:
|
|
156
|
+
|
|
157
|
+
- actions and input mapping through `@pmndrs/viverse`,
|
|
158
|
+
- audio coverage for feedback moments,
|
|
159
|
+
- camera behavior and camera math,
|
|
160
|
+
- ECS structure with `elics`,
|
|
161
|
+
- lighting and cascaded shadows,
|
|
162
|
+
- materials, textures, and color space,
|
|
163
|
+
- navigation meshes with `navcat`,
|
|
164
|
+
- performance profiling and optimization,
|
|
165
|
+
- BVH character physics through `@pmndrs/viverse`,
|
|
166
|
+
- pointer events through `@pmndrs/pointer-events`,
|
|
167
|
+
- postprocessing,
|
|
168
|
+
- spatial vs. 2D user interface routing,
|
|
169
|
+
- VFX and particles,
|
|
170
|
+
- world scale, terrain, fog, horizon, and density.
|
|
171
|
+
|
|
172
|
+
This repository installs `drawcall-ai/skills` so the technology-survey can map a user goal like "open-world shooter" to the specific skills and packages that carry world scale, input, camera, navigation, character motion, effects, UI, proof, and performance.
|
|
173
|
+
|
|
174
|
+
### `../market`
|
|
175
|
+
|
|
176
|
+
`../market` is the asset marketplace. It defines versioned asset types such as `model`, `humanoid-model`, `texture`, `humanoid-animation`, `template`, `sound-effect`, `background-music`, `environment`, and `flipbook`.
|
|
177
|
+
|
|
178
|
+
`create` uses it in two places:
|
|
179
|
+
|
|
180
|
+
- the `template` stage searches for a fitting starter template and applies it into the generated repo;
|
|
181
|
+
- the `asset-survey` searches and previews assets so the later goal and plan can choose real content instead of vague placeholders.
|
|
182
|
+
|
|
183
|
+
Generated projects get the `@drawcall/market` package and the `market` skill so harnesses can search, preview, install, and reason about assets with the real CLI and API.
|
|
184
|
+
|
|
185
|
+
### `../acta`
|
|
186
|
+
|
|
187
|
+
`../acta` is the character-behavior system. Acta behavior JSON describes locomotion, actions, animation states, timed effects, movement output, and jump output for Three.js characters. It supports playable characters, NPCs, enemies, companions, and crowds.
|
|
188
|
+
|
|
189
|
+
In generated projects, Acta is the preferred fit when the user asks for an embodied thing that moves, acts, aims, jumps, attacks, or reacts. The harness can validate behavior JSON, test animation/effect timelines, or convert behavior JSON into TypeScript character classes.
|
|
190
|
+
|
|
191
|
+
### `../uikitml`
|
|
192
|
+
|
|
193
|
+
`../uikitml` is an HTML-like markup format and CLI for spatial UI. Harnesses can write `.uikitml`, validate it, render a preview, and convert it into Three.js, React Three Fiber, IWSDK, or pmndrs/uikit code.
|
|
194
|
+
|
|
195
|
+
The general `../skills` UI skill routes UI work:
|
|
196
|
+
|
|
197
|
+
- spatial or XR panels use `drawcall-ai/uikitml`;
|
|
198
|
+
- ordinary non-XR HUDs and menus can stay as HTML/CSS over the canvas.
|
|
199
|
+
|
|
200
|
+
### `../vitexec`
|
|
201
|
+
|
|
202
|
+
`../vitexec` lets a harness run scripts inside a live Vite app with Playwright. It can drive input, inspect runtime state, collect logs, take screenshots, record video, and capture traces.
|
|
203
|
+
|
|
204
|
+
`create` treats this as the default proof tool for generated Vite/Three.js apps. A build turn is not done just because TypeScript compiles; it needs a proof-run that demonstrates the product behavior in the running app.
|
|
205
|
+
|
|
206
|
+
### `../speech`
|
|
207
|
+
|
|
208
|
+
`../speech` is a production REST API for text-to-speech. It is installed as an agent skill so goals involving voice, narration, NPC speech, or accessibility can route to a real service instead of hand-waving audio output.
|
|
209
|
+
|
|
210
|
+
### `../flipbook`
|
|
211
|
+
|
|
212
|
+
`../flipbook` provides `@drawcall/flipbook`, a tiny Three.js runtime for KTX2 flipbook billboards such as explosions, flames, and particles. Market `flipbook` assets are meant to render through this package.
|
|
213
|
+
|
|
214
|
+
## Design Contract
|
|
215
|
+
|
|
216
|
+
The prompts in `src/prompts.ts` enforce the same principles across all stages:
|
|
217
|
+
|
|
218
|
+
- Real, not nominal: a step is done only when the product actually does the thing when run.
|
|
219
|
+
- Fit the tools: use the installed Drawcall skills, packages, and Market assets where they genuinely fit.
|
|
220
|
+
- Name fit-gaps honestly instead of silently substituting fake stand-ins.
|
|
221
|
+
- Present feedback where the player perceives it, without debug UI leaking into the default product.
|
|
222
|
+
- See and judge each step against the goal's look and feel by observing the real running product from the player's seat, iterating until it reads right — not by checking mere presence.
|
|
223
|
+
- Treat moving or acting things as embodied: motion, animation, behavior, and feedback belong to the feature, not to a later polish pass.
|
|
224
|
+
|
|
225
|
+
The vocabulary behind those rules lives in `LANGUAGE.md`.
|
|
226
|
+
|
|
227
|
+
## Development
|
|
228
|
+
|
|
229
|
+
```sh
|
|
230
|
+
npm install
|
|
231
|
+
npm test
|
|
232
|
+
npm run typecheck
|
|
233
|
+
npm run build
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
Important source files:
|
|
237
|
+
|
|
238
|
+
- `src/command.ts` defines the CLI surface and argument parsing (the create-command).
|
|
239
|
+
- `src/create.ts` runs the create-session: it orchestrates the pipeline of stages and the build-turn loop.
|
|
240
|
+
- `src/scaffold.ts` creates the repo, installs skills/packages, commits records, and patches local CLI shims.
|
|
241
|
+
- `src/harness.ts` builds the harness-command for each supported harness and bundles it as the harness-runner that runs one harness-turn.
|
|
242
|
+
- `src/subprocess.ts` defines the command-runner interface and the built-in subprocess-runner (spawns the process, writes the session-log, forwards signals, enforces the timeout).
|
|
243
|
+
- `src/progress-log.ts` writes progress-notes and mirrors them into the session-log.
|
|
244
|
+
- `src/prompts.ts` defines the harness prompts for each stage.
|
|
245
|
+
- `src/constants.ts` defines stage names, installed skills/packages, record paths, scratch paths, timeouts, and the build-turn-budget.
|
|
246
|
+
|
|
247
|
+
The vocabulary used throughout (records, surveys, turns, stages, runners) is defined in `LANGUAGE.md`.
|
|
248
|
+
|
|
249
|
+
The tests in `test/index.test.ts` encode the expected stage order, stage isolation, harness selection, generated files, commits, and prompt contracts.
|
package/dist/cli.d.ts
ADDED
package/dist/cli.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { createRequire } from "node:module";
|
|
3
|
+
import { Command } from "commander";
|
|
4
|
+
import { CliError } from "./constants.js";
|
|
5
|
+
import { createCreateCommand } from "./command.js";
|
|
6
|
+
const packageJson = createRequire(import.meta.url)("../package.json");
|
|
7
|
+
const program = createCreateCommand(new Command(), {
|
|
8
|
+
version: packageJson.version
|
|
9
|
+
});
|
|
10
|
+
try {
|
|
11
|
+
await program.parseAsync(process.argv);
|
|
12
|
+
}
|
|
13
|
+
catch (error) {
|
|
14
|
+
if (!(error instanceof CliError))
|
|
15
|
+
throw error;
|
|
16
|
+
console.error(`error ${error.message}`);
|
|
17
|
+
process.exit(error.exitCode);
|
|
18
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import { type HarnessName, type Stage } from "./constants.js";
|
|
3
|
+
import { createProject } from "./create.js";
|
|
4
|
+
export declare function createCreateCommand(command?: Command, options?: {
|
|
5
|
+
version?: string;
|
|
6
|
+
createProject?: typeof createProject;
|
|
7
|
+
}): Command;
|
|
8
|
+
export declare function splitHarnessArgs(args: string[]): {
|
|
9
|
+
promptParts: string[];
|
|
10
|
+
harnessArgs: string[];
|
|
11
|
+
};
|
|
12
|
+
export declare function parsePromptParts(promptParts: string[]): string;
|
|
13
|
+
export declare function parseHarnessName(value: string | undefined): HarnessName | undefined;
|
|
14
|
+
export declare function parseStage(value: string | undefined): Stage | undefined;
|
|
15
|
+
export declare function parsePositiveInteger(value: string | undefined, optionName: string): number | undefined;
|
|
16
|
+
export declare function parseHarnessTimeoutMs(value: string | undefined): number | undefined;
|
package/dist/command.js
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import { CliError, DEFAULT_HARNESS_TIMEOUT_MS, HARNESS_NAMES, MAX_BUILD_TURNS, STAGES } from "./constants.js";
|
|
3
|
+
import { createProject } from "./create.js";
|
|
4
|
+
import { formatDuration } from "./progress-log.js";
|
|
5
|
+
const CLI_OPTION_NAMES = [
|
|
6
|
+
"--stage",
|
|
7
|
+
"--harness",
|
|
8
|
+
"--harness-timeout-minutes",
|
|
9
|
+
"--max-turns",
|
|
10
|
+
"--name",
|
|
11
|
+
"--skip-template"
|
|
12
|
+
];
|
|
13
|
+
export function createCreateCommand(command = new Command(), options = {}) {
|
|
14
|
+
const create = options.createProject ?? createProject;
|
|
15
|
+
command
|
|
16
|
+
.name("drawcall-create")
|
|
17
|
+
.description("Create a project with an installed local harness")
|
|
18
|
+
.argument("<args...>", "what should be created; use -- to pass following args to the harness")
|
|
19
|
+
.option("--stage <name>", `which stage to run (${STAGES.join(", ")})`)
|
|
20
|
+
.option("--harness <name>", `harness to use (${HARNESS_NAMES.join(", ")})`)
|
|
21
|
+
.option("--harness-timeout-minutes <count>", `timeout for each harness invocation in minutes (default: ${DEFAULT_HARNESS_TIMEOUT_MS / 60_000})`)
|
|
22
|
+
.option("--max-turns <count>", `maximum build turns (default: ${MAX_BUILD_TURNS})`)
|
|
23
|
+
.option("--name <name>", "project directory name (default: a generated dc-xxxxxx name)")
|
|
24
|
+
.option("--skip-template", "during a full run, skip starter template search and build from scratch")
|
|
25
|
+
.passThroughOptions()
|
|
26
|
+
.version(options.version ?? "0.0.0")
|
|
27
|
+
.action(async (args, commandOptions) => {
|
|
28
|
+
const { promptParts, harnessArgs } = splitHarnessArgs(args);
|
|
29
|
+
const prompt = parsePromptParts(promptParts);
|
|
30
|
+
const result = await create(prompt, {
|
|
31
|
+
stage: parseStage(commandOptions.stage),
|
|
32
|
+
harness: parseHarnessName(commandOptions.harness),
|
|
33
|
+
harnessTimeoutMs: parseHarnessTimeoutMs(commandOptions.harnessTimeoutMinutes),
|
|
34
|
+
harnessArgs,
|
|
35
|
+
maxTurns: parsePositiveInteger(commandOptions.maxTurns, "--max-turns"),
|
|
36
|
+
projectName: commandOptions.name,
|
|
37
|
+
skipTemplate: commandOptions.skipTemplate === true
|
|
38
|
+
});
|
|
39
|
+
const ok = result.exitCode === 0;
|
|
40
|
+
const write = ok ? console.log : console.error;
|
|
41
|
+
write(ok ? formatSuccess(result) : formatFailure(result));
|
|
42
|
+
process.exitCode = result.exitCode;
|
|
43
|
+
});
|
|
44
|
+
return command;
|
|
45
|
+
}
|
|
46
|
+
export function splitHarnessArgs(args) {
|
|
47
|
+
const separatorIndex = args.indexOf("--");
|
|
48
|
+
if (separatorIndex === -1)
|
|
49
|
+
return { promptParts: args, harnessArgs: [] };
|
|
50
|
+
return {
|
|
51
|
+
promptParts: args.slice(0, separatorIndex),
|
|
52
|
+
harnessArgs: args.slice(separatorIndex + 1)
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
export function parsePromptParts(promptParts) {
|
|
56
|
+
// passThroughOptions() turns options placed after the prompt into prompt words; reject them
|
|
57
|
+
// instead of silently sending them to the harness.
|
|
58
|
+
const misplaced = promptParts.find((part) => CLI_OPTION_NAMES.includes(part));
|
|
59
|
+
if (misplaced) {
|
|
60
|
+
const valuePlaceholder = misplaced === "--skip-template" ? "" : " <value>";
|
|
61
|
+
throw new CliError(`${misplaced} must come before the prompt, e.g. drawcall-create ${misplaced}${valuePlaceholder} "<prompt>"`);
|
|
62
|
+
}
|
|
63
|
+
const prompt = promptParts.join(" ").trim();
|
|
64
|
+
if (!prompt)
|
|
65
|
+
throw new CliError("prompt is required");
|
|
66
|
+
return prompt;
|
|
67
|
+
}
|
|
68
|
+
export function parseHarnessName(value) {
|
|
69
|
+
if (!value)
|
|
70
|
+
return undefined;
|
|
71
|
+
if (HARNESS_NAMES.includes(value))
|
|
72
|
+
return value;
|
|
73
|
+
throw new CliError(`unsupported harness "${value}". Supported: ${HARNESS_NAMES.join(", ")}`);
|
|
74
|
+
}
|
|
75
|
+
export function parseStage(value) {
|
|
76
|
+
if (!value)
|
|
77
|
+
return undefined;
|
|
78
|
+
if (STAGES.includes(value))
|
|
79
|
+
return value;
|
|
80
|
+
throw new CliError(`unsupported stage "${value}". Supported: ${STAGES.join(", ")}`);
|
|
81
|
+
}
|
|
82
|
+
export function parsePositiveInteger(value, optionName) {
|
|
83
|
+
if (value === undefined)
|
|
84
|
+
return undefined;
|
|
85
|
+
if (!/^[1-9]\d*$/.test(value)) {
|
|
86
|
+
throw new CliError(`${optionName} must be a positive integer`);
|
|
87
|
+
}
|
|
88
|
+
return Number(value);
|
|
89
|
+
}
|
|
90
|
+
export function parseHarnessTimeoutMs(value) {
|
|
91
|
+
const minutes = parsePositiveInteger(value, "--harness-timeout-minutes");
|
|
92
|
+
return minutes === undefined ? undefined : minutes * 60_000;
|
|
93
|
+
}
|
|
94
|
+
function formatSuccess(result) {
|
|
95
|
+
const lines = [
|
|
96
|
+
"",
|
|
97
|
+
`Project ready in ${formatDuration(result.durationMs)}`,
|
|
98
|
+
`Path ${result.projectDir}`,
|
|
99
|
+
`Log drawcall-create.log`
|
|
100
|
+
];
|
|
101
|
+
const turns = formatTurns(result);
|
|
102
|
+
if (turns)
|
|
103
|
+
lines.splice(3, 0, `Turns ${turns}`);
|
|
104
|
+
return lines.join("\n");
|
|
105
|
+
}
|
|
106
|
+
function formatFailure(result) {
|
|
107
|
+
const lines = [
|
|
108
|
+
"",
|
|
109
|
+
`Stopped after ${formatDuration(result.durationMs)}`,
|
|
110
|
+
`Exit code ${result.exitCode}`,
|
|
111
|
+
`Path ${result.projectDir}`,
|
|
112
|
+
`Log drawcall-create.log`
|
|
113
|
+
];
|
|
114
|
+
const turns = formatTurns(result);
|
|
115
|
+
if (turns)
|
|
116
|
+
lines.splice(3, 0, `Turns ${turns}`);
|
|
117
|
+
return lines.join("\n");
|
|
118
|
+
}
|
|
119
|
+
function formatTurns(result) {
|
|
120
|
+
if (result.maxTurns === 0)
|
|
121
|
+
return undefined;
|
|
122
|
+
return `${result.turns}/${result.maxTurns}`;
|
|
123
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export declare const HARNESS_NAMES: readonly ["opencode", "codex", "claude", "pi", "gemini", "grok", "forge"];
|
|
2
|
+
export type HarnessName = (typeof HARNESS_NAMES)[number];
|
|
3
|
+
export declare const STAGES: readonly ["scaffold", "template", "survey-assets", "survey-technology", "goal", "plan", "build", "full"];
|
|
4
|
+
export type Stage = (typeof STAGES)[number];
|
|
5
|
+
export declare const PARALLEL_STAGES: readonly ["template", "survey-assets", "survey-technology"];
|
|
6
|
+
export declare const SKILLS: readonly ["drawcall-ai/vitexec", "drawcall-ai/uikitml", "drawcall-ai/acta", "drawcall-ai/market", "drawcall-ai/speech", "drawcall-ai/flipbook", "drawcall-ai/skills"];
|
|
7
|
+
export declare const PACKAGES: readonly ["vitexec@latest", "@drawcall/uikitml@latest", "@drawcall/acta@latest", "@drawcall/market@latest", "@drawcall/flipbook@latest", "@pmndrs/uikit@latest", "@pmndrs/pointer-events@latest", "@pmndrs/viverse@latest", "navcat@^0.4.1", "three@^0.184.0", "vite@^8.0.16", "typescript@^6.0.3", "elics@^3.4.2", "postprocessing@^6.39.1"];
|
|
8
|
+
export declare const PACKAGE_NAMES: readonly ["vitexec", "@drawcall/uikitml", "@drawcall/acta", "@drawcall/market", "@drawcall/flipbook", "@pmndrs/uikit", "@pmndrs/pointer-events", "@pmndrs/viverse", "navcat", "three", "vite", "typescript", "elics"];
|
|
9
|
+
export declare const MAX_BUILD_TURNS = 10;
|
|
10
|
+
export declare const DEFAULT_HARNESS_TIMEOUT_MS: number;
|
|
11
|
+
export declare const TIMEOUT_EXIT_CODE = 124;
|
|
12
|
+
export declare const TIMEOUT_KILL_GRACE_MS = 5000;
|
|
13
|
+
export declare const GOAL_FILE = "GOAL.md";
|
|
14
|
+
export declare const README_FILE = "README.md";
|
|
15
|
+
export declare const PLAN_FILE = "PLAN.md";
|
|
16
|
+
export declare const SURVEY_DIR = "surveys";
|
|
17
|
+
export declare const ASSET_SURVEY_FILE = "surveys/ASSETS.md";
|
|
18
|
+
export declare const TECH_SURVEY_FILE = "surveys/TECHNOLOGY.md";
|
|
19
|
+
export declare const PROOF_DIR = "proof";
|
|
20
|
+
export declare const SESSION_LOG_FILE = "drawcall-create.log";
|
|
21
|
+
export declare const GITIGNORE_ENTRIES: readonly ["node_modules/", "dist/", "build/", "coverage/", ".env", ".env.*", ".DS_Store", "surveys/", "proof/", "drawcall-create.log"];
|
|
22
|
+
/** Error whose message is shown to the user and whose code becomes the process exit code. */
|
|
23
|
+
export declare class CliError extends Error {
|
|
24
|
+
readonly exitCode: number;
|
|
25
|
+
constructor(message: string, exitCode?: number);
|
|
26
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
export const HARNESS_NAMES = ["opencode", "codex", "claude", "pi", "gemini", "grok", "forge"];
|
|
2
|
+
export const STAGES = [
|
|
3
|
+
"scaffold",
|
|
4
|
+
"template",
|
|
5
|
+
"survey-assets",
|
|
6
|
+
"survey-technology",
|
|
7
|
+
"goal",
|
|
8
|
+
"plan",
|
|
9
|
+
"build",
|
|
10
|
+
"full"
|
|
11
|
+
];
|
|
12
|
+
// In a "full" run these three stages run concurrently after scaffolding: they write disjoint
|
|
13
|
+
// outputs (template touches the product; the surveys write gitignored scratch files) and none
|
|
14
|
+
// of them depends on the others, so they share a barrier before the goal stage.
|
|
15
|
+
export const PARALLEL_STAGES = ["template", "survey-assets", "survey-technology"];
|
|
16
|
+
export const SKILLS = [
|
|
17
|
+
"drawcall-ai/vitexec",
|
|
18
|
+
"drawcall-ai/uikitml",
|
|
19
|
+
"drawcall-ai/acta",
|
|
20
|
+
"drawcall-ai/market",
|
|
21
|
+
"drawcall-ai/speech",
|
|
22
|
+
"drawcall-ai/flipbook",
|
|
23
|
+
"drawcall-ai/skills"
|
|
24
|
+
];
|
|
25
|
+
export const PACKAGES = [
|
|
26
|
+
"vitexec@latest",
|
|
27
|
+
"@drawcall/uikitml@latest",
|
|
28
|
+
"@drawcall/acta@latest",
|
|
29
|
+
"@drawcall/market@latest",
|
|
30
|
+
"@drawcall/flipbook@latest",
|
|
31
|
+
"@pmndrs/uikit@latest",
|
|
32
|
+
"@pmndrs/pointer-events@latest",
|
|
33
|
+
"@pmndrs/viverse@latest",
|
|
34
|
+
"navcat@^0.4.1",
|
|
35
|
+
"three@^0.184.0",
|
|
36
|
+
"vite@^8.0.16",
|
|
37
|
+
"typescript@^6.0.3",
|
|
38
|
+
"elics@^3.4.2",
|
|
39
|
+
"postprocessing@^6.39.1"
|
|
40
|
+
];
|
|
41
|
+
export const PACKAGE_NAMES = [
|
|
42
|
+
"vitexec",
|
|
43
|
+
"@drawcall/uikitml",
|
|
44
|
+
"@drawcall/acta",
|
|
45
|
+
"@drawcall/market",
|
|
46
|
+
"@drawcall/flipbook",
|
|
47
|
+
"@pmndrs/uikit",
|
|
48
|
+
"@pmndrs/pointer-events",
|
|
49
|
+
"@pmndrs/viverse",
|
|
50
|
+
"navcat",
|
|
51
|
+
"three",
|
|
52
|
+
"vite",
|
|
53
|
+
"typescript",
|
|
54
|
+
"elics"
|
|
55
|
+
];
|
|
56
|
+
export const MAX_BUILD_TURNS = 10;
|
|
57
|
+
export const DEFAULT_HARNESS_TIMEOUT_MS = 20 * 60 * 1000;
|
|
58
|
+
export const TIMEOUT_EXIT_CODE = 124;
|
|
59
|
+
export const TIMEOUT_KILL_GRACE_MS = 5000;
|
|
60
|
+
// The three committed records (see LANGUAGE.md): the fixed goal, the current state,
|
|
61
|
+
// and the plan between them.
|
|
62
|
+
export const GOAL_FILE = "GOAL.md";
|
|
63
|
+
export const README_FILE = "README.md";
|
|
64
|
+
export const PLAN_FILE = "PLAN.md";
|
|
65
|
+
// The two scratch surveys: a harness walks every fitting Market asset / installed skill
|
|
66
|
+
// and writes what fits here. They feed the goal and plan stages and are never committed.
|
|
67
|
+
export const SURVEY_DIR = "surveys";
|
|
68
|
+
export const ASSET_SURVEY_FILE = `${SURVEY_DIR}/ASSETS.md`;
|
|
69
|
+
export const TECH_SURVEY_FILE = `${SURVEY_DIR}/TECHNOLOGY.md`;
|
|
70
|
+
// Build turns drop proof files here; gitignored scratch (the durable record of what was proven
|
|
71
|
+
// lives in README.md), so generated repos don't fill with committed binaries.
|
|
72
|
+
export const PROOF_DIR = "proof";
|
|
73
|
+
export const SESSION_LOG_FILE = "drawcall-create.log";
|
|
74
|
+
export const GITIGNORE_ENTRIES = [
|
|
75
|
+
"node_modules/",
|
|
76
|
+
"dist/",
|
|
77
|
+
"build/",
|
|
78
|
+
"coverage/",
|
|
79
|
+
".env",
|
|
80
|
+
".env.*",
|
|
81
|
+
".DS_Store",
|
|
82
|
+
`${SURVEY_DIR}/`,
|
|
83
|
+
`${PROOF_DIR}/`,
|
|
84
|
+
SESSION_LOG_FILE
|
|
85
|
+
];
|
|
86
|
+
/** Error whose message is shown to the user and whose code becomes the process exit code. */
|
|
87
|
+
export class CliError extends Error {
|
|
88
|
+
exitCode;
|
|
89
|
+
constructor(message, exitCode = 1) {
|
|
90
|
+
super(message);
|
|
91
|
+
this.name = "CliError";
|
|
92
|
+
this.exitCode = exitCode;
|
|
93
|
+
}
|
|
94
|
+
}
|
package/dist/create.d.ts
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { type HarnessName, type Stage } from "./constants.js";
|
|
2
|
+
import { type HarnessRunner } from "./harness.js";
|
|
3
|
+
import { type CommandExists, type CommandRunner } from "./subprocess.js";
|
|
4
|
+
import { ProgressLog } from "./progress-log.js";
|
|
5
|
+
export type CreateProjectOptions = {
|
|
6
|
+
cwd?: string;
|
|
7
|
+
env?: NodeJS.ProcessEnv;
|
|
8
|
+
stage?: Stage;
|
|
9
|
+
harness?: HarnessName;
|
|
10
|
+
harnessArgs?: string[];
|
|
11
|
+
harnessTimeoutMs?: number;
|
|
12
|
+
maxTurns?: number;
|
|
13
|
+
projectName?: string;
|
|
14
|
+
skipTemplate?: boolean;
|
|
15
|
+
runner?: CommandRunner;
|
|
16
|
+
commandExists?: CommandExists;
|
|
17
|
+
};
|
|
18
|
+
export type CreateProjectResult = {
|
|
19
|
+
projectDir: string;
|
|
20
|
+
projectName: string;
|
|
21
|
+
harness: HarnessName;
|
|
22
|
+
exitCode: number;
|
|
23
|
+
turns: number;
|
|
24
|
+
maxTurns: number;
|
|
25
|
+
durationMs: number;
|
|
26
|
+
};
|
|
27
|
+
export declare function createProject(prompt: string, options?: CreateProjectOptions): Promise<CreateProjectResult>;
|
|
28
|
+
/** Build-stage: loop build-turns against the plan up to the build-turn-budget. */
|
|
29
|
+
export declare function runBuildTurns(harnessRunner: HarnessRunner, userPrompt: string, options?: {
|
|
30
|
+
maxTurns?: number;
|
|
31
|
+
progressLog?: ProgressLog;
|
|
32
|
+
}): Promise<{
|
|
33
|
+
exitCode: number;
|
|
34
|
+
turns: number;
|
|
35
|
+
}>;
|