You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: CLAUDE.md
+1-1Lines changed: 1 addition & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -11,7 +11,7 @@
11
11
A CLI installer tool for dAppBooster projects. It supports two **stacks** and two **modes**:
12
12
13
13
-**Stacks:**`evm` (the original dAppBooster for EVM chains) and `canton` (dAppBooster for Canton: Daml ledger, Carpincho wallet, off-chain services). Each stack declares its own source repository, ref strategy (tag-latest vs branch), package manager, env files, optional `removeAfterClone` paths, and features.
14
-
-**Interactive** (default): React + Ink TUI that prompts for stack first, then project name, then installation mode (Canton offers **default** / full / custom; EVM offers full / custom) → optional packages → install → cleanup → post-install. The stack prompt is skipped when `--canton`, `--evm`, or `--stack` is supplied.
14
+
-**Interactive** (default): React + Ink TUI that prompts for the project name first, then the stack, then the installation mode (Canton offers **default** / full / custom; EVM offers full / custom) → optional packages → install → cleanup → post-install. The stack prompt is skipped when `--canton`, `--evm`, or `--stack` is supplied.
15
15
-**Non-interactive**: Flag-driven (`--ni` or auto-detected when not a TTY) for AI agents and CI. Outputs JSON to stdout. Run `--info` for stack + feature discovery, then `--canton`/`--evm` (or `--stack`) + `--name` + `--mode`[+ `--features`]. Canton supports `--mode default` (the recommended set: keeps `carpincho` + `llm`, removes `github` + `precommit`); `default` is rejected for EVM. Omitting a stack flag in non-interactive mode defaults to `evm` for backward compatibility.
@@ -26,7 +29,9 @@ Installation modes are stack-aware via `getInstallationModes(stack)` — Canton
26
29
27
30
`getStackConfig(stack)` reads the base config and overlays the env-var overrides `DAPPBOOSTER_<STACK>_REPO_URL` and `DAPPBOOSTER_<STACK>_REF` before returning — that's the single hook for retargeting either stack at a fork or pre-release branch without editing code.
28
31
29
-
`getFeatureNames(stack)` and `isFeatureNameValid(stack, name)` are the per-stack feature accessors. There is no global `featureDefinitions` export — that would imply a single stack.
32
+
`getFeatureNames(stack)`, `getFeatureEntries(stack)` and `isFeatureNameValid(stack, name)` are the per-stack feature accessors. There is no global `featureDefinitions` export — that would imply a single stack.
33
+
34
+
`FeatureName` is the union of every feature name both stacks define, taken from the `featureNamesByStack` list at the top of `config.ts`. The list exists so the config can refer to its own feature names by type (`requires`, `ifFeature`) without a circular reference; the `satisfies` clause on `stackDefinitions` requires the feature maps to hold exactly those keys, so the two cannot drift apart without a compile error. Renaming a feature therefore turns every stale `'oldName'` string in the codebase into a compile error. `isFeatureNameValid` is a type guard, so validated CLI input narrows from `string` to `FeatureName`. The type is deliberately not per-stack: passing an EVM feature name to a Canton call still compiles, and the runtime check in `nonInteractive.ts` catches it.
packages:string[] //package-manager packages to remove when deselected (empty for canton features today)
44
+
packages:string[] //dependencies the package manager removes when the feature is deselected
40
45
default:boolean// --info output
41
46
postInstall?:string[] // post-install instructions for non-interactive JSON output
42
-
paths?:string[] // files/dirs removed when the feature is deselected (Canton, data-driven cleanup)
47
+
paths?:string[] // files/dirs removed when the feature is deselected
48
+
scripts?:string[] // package.json scripts removed when the feature is deselected
43
49
requires?:FeatureName[] // features this one depends on (one-directional, transitive)
44
50
}
45
51
```
46
52
47
-
When adding a new feature, add it to the relevant stack's `features` map. Programmatic consumers pick it up automatically. Canton feature cleanup is fully data-driven from `paths` (see the Operations Layer below), so a new Canton feature needs no cleanup code — only its `paths`. EVM features still need an explicit per-feature cleanup function. The CLI `--help` text in `cli.tsx` maintains its own copy in both cases.
53
+
When adding a new feature, add its name to `featureNamesByStack` and an entry to the stack's `features` map. Programmatic consumers pick it up automatically. Feature cleanup is data-driven from `paths` and `scripts` for both stacks, and removal of its `packages` is data-driven too (see the Operations Layer below), so a new feature usually needs no code. The one exception is EVM's `demo` and `subgraph`, which restore replacement source files from the template's staging directory. The CLI `--help` text in `cli.tsx` maintains its own copy either way.
54
+
55
+
`packages` are always removed by the package manager (`pnpmremove` / `npmuninstall`), which updates package.json and the lockfile together. Nothing hand-edits dependencies.
48
56
49
-
**Feature dependencies (`requires`)** are resolved by pure helpers in `utils.ts`. `resolveSelectedFeatures(stack, selected)` expands a selection to include every transitive requirement; `resolveModeFeatures(stack, mode, customSelection)` maps a mode to its kept-feature list (full → all, default → the `default:true` set, custom → the resolved selection) and is shared by the non-interactive path and the interactive Install/FileCleanup/PostInstall steps. `applyFeatureToggle(stack, selection, toggled, action)` keeps the interactive multiselect consistent: selecting a feature pulls its requirements in, deselecting one cascades its dependents out. No feature declares `requires` today (the machinery remains for future use); `--info` surfaces each feature's `requires` so agents can resolve dependencies themselves.
57
+
**Feature dependencies (`requires`)** are resolved by pure helpers in `utils.ts`. `resolveSelectedFeatures(stack, selected)` expands a selection to include every transitive requirement; `resolveModeFeatures(stack, mode, customSelection)` maps a mode to its kept-feature list (full → all, default → the `default:true` set, custom → the resolved selection), each with its `requires` resolved. The non-interactive path resolves it in `validate`; the interactive path resolves it once in `app.tsx` and passes the result to every step, so the review screen lists exactly what gets installed. `applyFeatureToggle(stack, selection, toggled, action)` keeps the interactive multiselect consistent: selecting a feature pulls its requirements in, deselecting one cascades its dependents out. No feature declares `requires` today (the machinery remains for future use); `--info` surfaces each feature's `requires` so agents can resolve dependencies themselves.
50
58
51
59
## Operations Layer (`source/operations/`)
52
60
@@ -56,12 +64,12 @@ Plain async functions, no UI dependencies. Each operation that varies per stack
56
64
|---|---|
57
65
| `cloneRepo(stack, projectName, onProgress?)` | Reads `stack.refType`. **tag-latest**: shallow clone with `--no-checkout`, `gitfetch --tags`, then `gitcheckout$(gitdescribe --tags …)` (shell required for `$()`). **branch**: shallow clone with `--branch <stack.ref> --single-branch` (no shell). After that, runs `fs.rm` for every entry in `stack.removeAfterClone` (empty for both stacks today), removes `.git`, and reinitializes with `gitinit`. Uses `execFile` everywhere except the tag-latest shell substitution. |
58
66
| `createEnvFile(stack, projectFolder, features?)` | Copies every entry from `stack.envFiles`. Entries with `ifFeature` are skipped unless the named feature is in the selection (e.g. Canton's `carpincho-wallet/.env.local` only when `carpincho` is selected). |
59
-
| `installPackages(stack, projectFolder, mode, features, onProgress?)` | Uses `stack.packageManager`. Full: `<pm> install`. `default`/`custom` with packages to remove: `<pm> remove` (pnpm) or `<pm> uninstall` (npm) + `<pm> runpostinstall`; with nothing to remove: `<pm> install`. Canton features all carry `packages: []`, so Canton always runs a plain `npminstall` (husky-dep removal happens in cleanup, not here — the Canton template has no `postinstall` script). `execFile` only — never shell. |
60
-
| `cleanupFiles(stack, projectFolder, mode, features, onProgress?)` | **EVM** runs **repository hygiene** first (always): removes `.github` (CI), the husky/commitlint automation (`.husky`, `.lintstagedrc.mjs`, `commitlint.config.js`), and its own agent metadata (`.claude`, `AGENTS.md`, `CLAUDE.md`, `architecture.md`), and sanitizes tooling deps/scripts from `package.json`; then `cleanupEvmFiles` removes deselected feature files via per-feature functions plus the `.install-files` staging directory. **Canton** runs **no forced hygiene** — `.github` and the pre-commit automation are the optional `github` and `precommit` features. `cleanupCantonFiles` is **data-driven**: for `default` and `custom` modes (not `full`) it loops the stack's features and removes each deselected feature's `paths` (`github` → `.github`; `precommit` → the husky files; `carpincho` → `carpincho-wallet`; `llm` → the agent/LLM artifacts). Removed directories drive two `package.json` edits: **script stripping** by command target — any script whose command invokes a removed directory is dropped (so deselecting `carpincho` strips `wallet:dev` / `carpincho:build:extension`) — and **`workspaces` pruning**, dropping any workspace entry that points at a removed directory (both the `string[]` and `{ packages: string[] }` forms), so deselecting `carpincho` leaves no dangling `carpincho-wallet` workspace. When `precommit` is removed it additionally strips the `prepare`/commitlint scripts and the husky/lint-staged/commitlint dev-dependencies. In `full` mode nothing is removed, so a full Canton scaffold keeps `.github`, the hooks, `carpincho-wallet`, and the agent docs. Canton then makes an initial `git` commit of the scaffold. |
67
+
| `installPackages(stack, projectFolder, mode, features, onProgress?)` | Uses `stack.packageManager`. Nothing to remove (full mode, or a selection that drops no packages): `<pm> install`. Otherwise `<pm> remove` (pnpm) or `<pm> uninstall` (npm), which prunes the manifest and the lockfile together, then `<pm> runpostinstall` **only if the template defines that script** — the Canton template does not. Runs after `cleanupFiles`, so it resolves the pruned manifest once. `execFile` only — never shell. |
68
+
| `cleanupFiles(stack, projectFolder, mode, features, onProgress?)` | Config-driven for both stacks, and runs **before** the install. First the stack's `hygiene` group, the paths belonging to the template's own repository (EVM: `.github` plus `.claude`, `AGENTS.md`, `CLAUDE.md`, `architecture.md`; Canton declares none, since it models those as its `github` and `llm` features). Then, in `default` and `custom` modes, it loops the stack's features and for each one the user left out removes its `paths` and collects its `scripts`. Removed **directories** drive two further package.json edits: **script stripping** by command target — any script whose command invokes a removed directory is dropped, so dropping `carpincho` strips `wallet:dev` and `carpincho:build:extension` — and **`workspaces` pruning**, dropping any entry pointing at a removed directory (both the `string[]` and `{ packages: string[] }` forms). Removed *files* never strip scripts, so a script that merely mentions `CLAUDE.md` survives. package.json is read once and written only when a value changed; dependencies are left to `installPackages`. EVM additionally restores the demo-free home page from the staged copies when `demo` or `subgraph` is dropped. Last comes the stack's `staging` group (EVM's `.install-files`), once the restores no longer need it. |
61
69
62
70
### Interrupt safety (`installGuard`)
63
71
64
-
`source/operations/installGuard.ts` makes a Ctrl+C mid-scaffold leave no partial directory behind. `beginInstall(projectFolder)` is called the instant disk work starts (before `cloneRepo`) and registers `SIGINT`/`SIGTERM` handlers; `completeInstall()` is called once cleanup finishes. On an interrupt while a scaffold is in progress, the handler removes the project directory; after `completeInstall` it is a no-op, so a finished project (or a Ctrl+C on the post-install screen) is never deleted. It only ever removes a directory created this run — both entry paths reject a pre-existing directory up front — so user data is never touched. Both paths wire it in: the non-interactive runner brackets its operation block, and interactively `CloneRepo` calls `beginInstall` while `FileCleanup` calls `completeInstall`.
72
+
`source/operations/installGuard.ts` makes a Ctrl+C or a failure mid-scaffold leave no partial directory behind. `beginInstall(projectFolder)` is called the instant disk work starts (before `cloneRepo`) and registers `SIGINT`/`SIGTERM` handlers; `completeInstall()` is called once cleanup finishes; `abortInstall()` is called when an operation throws — it removes the partial directory and sets `process.exitCode = 1`, so a failed interactive run reports failure to the shell instead of exiting 0. The three interactive operation steps (`CloneRepo`, `Install`, `FileCleanup`) all call it from their `catch`. On an interrupt while a scaffold is in progress, the handler removes the project directory; after `completeInstall` it is a no-op, so a finished project (or a Ctrl+C on the post-install screen) is never deleted. It only ever removes a directory created this run — both entry paths reject a pre-existing directory up front — so user data is never touched. Both paths wire it in: the non-interactive runner brackets its operation block, and interactively `CloneRepo` calls `beginInstall` while `Install`, the last operation step, calls `completeInstall`.
0 commit comments