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
docs: split architecture.md into a lean index plus docs/architecture/
architecture.md becomes a short index (intro, tech stack, project structure, a
'read this when…' link table) so agents load only the sub-doc they need:
- docs/architecture/abstractions.md — Stack/config, FeatureDefinition, operations, shell, security
- docs/architecture/data-flow.md — non-interactive + interactive flows
- docs/architecture/extending.md — add a stack / feature / operation
Design specs at docs/ root stay local-only; these reference docs are committed.
`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.
73
-
74
-
`getFeatureNames(stack)` and `isFeatureNameValid(stack, name)` are the per-stack feature accessors. There is no global `featureDefinitions` export — that would imply a single stack.
75
-
76
-
### Feature Definitions
77
-
78
-
Stored inside each stack's `features` map. Shape:
79
-
80
-
```ts
81
-
typeFeatureDefinition= {
82
-
description:string// --info output
83
-
label:string// TUI multiselect display
84
-
packages:string[] // package-manager packages to remove when deselected (empty for canton features today)
85
-
default:boolean// --info output
86
-
postInstall?:string[] // post-install instructions for non-interactive JSON output
87
-
paths?:string[] // files/dirs removed when the feature is deselected (Canton, data-driven cleanup)
88
-
requires?:FeatureName[] // features this one depends on (one-directional, transitive)
89
-
}
90
-
```
91
-
92
-
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 `cleanupFiles.ts` 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.
93
-
94
-
**Feature dependencies (`requires`)** are resolved by pure helpers in `utils.ts`. `resolveSelectedFeatures(stack, selected)` expands a selection to include every transitive requirement (used by the non-interactive path, so `--featurese2e` yields `[counter, e2e]`). `applyFeatureToggle(stack, selection, toggled, action)` keeps the interactive multiselect consistent: selecting a feature pulls its requirements in, deselecting one cascades its dependents out. `e2erequirescounter` is the only dependency today. `--info` surfaces each feature's `requires` so agents can resolve dependencies themselves.
95
-
96
-
### Operations Layer (`source/operations/`)
97
-
98
-
Plain async functions, no UI dependencies. Each operation that varies per stack takes `stack:Stack` as its first argument. Multi-step operations accept an optional `onProgress` callback for the TUI; the non-interactive path omits it.
99
-
100
-
| Function | What it does |
101
-
|---|---|
102
-
| `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. |
103
-
| `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). |
104
-
| `installPackages(stack, projectFolder, mode, features, onProgress?)` | Uses `stack.packageManager`. Full: `<pm> install`. Custom with packages to remove: `<pm> remove` (pnpm) or `<pm> uninstall` (npm) + `<pm> runpostinstall`. Custom with all features: `<pm> install`. `execFile` only — never shell. |
105
-
| `cleanupFiles(stack, projectFolder, mode, features, onProgress?)` | First runs **repository hygiene** (every stack/mode): both stacks always remove `.github` (CI) and the husky/commitlint automation (`.husky`, `.lintstagedrc.mjs`, `commitlint.config.js`) and sanitize tooling deps/scripts from `package.json`; **EVM additionally** always removes its own agent metadata (`.claude`, `AGENTS.md`, `CLAUDE.md`, `architecture.md`), whereas **Canton keeps that metadata** under the optional `llm` feature. Then dispatches to `cleanupEvmFiles` or `cleanupCantonFiles`. EVM removes deselected feature files via per-feature functions plus the `.install-files` staging directory, and patches `package.json` by feature name. Canton cleanup is **data-driven**: it loops the stack's features and, in custom mode, removes each deselected feature's `paths` (e.g. `counter/`, `e2e/`, `carpincho-wallet`, the `llm` artifact paths). The removed directories then drive `package.json` script stripping by **command target** — any script whose command invokes a removed directory is dropped (so deselecting `carpincho` strips `wallet:dev` / `carpincho:build:extension`). Command-based matching keeps cleanup correct as the upstream repo renames or adds scripts. In `full` mode no feature paths are removed, so a full Canton scaffold keeps `carpincho-wallet`, the agent docs, and every script. Canton then makes an initial `git` commit of the scaffold. |
106
-
107
-
### Shell Execution (`source/operations/exec.ts`)
108
-
109
-
Two helpers with different security profiles:
110
-
111
-
- **`execFile(file, args, options)`** — wraps `child_process.spawn` without a shell. Arguments are passed as an array, so user input cannot be interpreted as shell metacharacters. Use this whenever user-provided values (e.g., `projectName`) appear in the command.
112
-
- **`exec(command, options)`** — wraps `child_process.spawn` to run `/bin/sh -c <command>` (spawns a shell). Only for commands that require shell features like `$(...)` substitution. Never interpolate user input into the command string.
113
-
114
-
Both helpers use `spawn` with stdout ignored and stderr piped. They do not capture or return stdout — output is not buffered for the caller. They throw on non-zero exit codes with the stderr message, or report the signal name when the process is killed by a signal.
Any error produces `{ "success":false, "error":"..." }` and exit code 1. Errors set `process.exitCode = 1` and throw rather than calling `process.exit()` directly, ensuring stdout flushes before the process terminates when piped.
154
-
155
-
**Success output:**
156
-
```json
157
-
{
158
-
"success":true,
159
-
"stack":"evm|canton",
160
-
"projectName":"...",
161
-
"mode":"full|custom",
162
-
"features": ["..."],
163
-
"path":"/absolute/path",
164
-
"postInstall": ["..."]
165
-
}
166
-
```
167
-
168
-
For full mode, `features` lists all of the stack's feature names. For custom mode, only the selected ones.
When `cli.tsx` resolves a stack flag, it passes `preselectedStack` to `<App>`, which skips the StackSelection step by starting `currentStep` at 2.
184
-
185
-
Components are presentation-only — they call operations via `useEffect` and render status. Components receive `MultiSelectItem[]` for feature selection (TUI concern) and convert to `FeatureName[]` before calling operations. `PostInstall` renders stack-specific instructions; the EVM branch shows the subgraph warning when applicable, the Canton branch shows the `canton:up`/`app:dev` commands and — when the `carpincho` feature is selected (or full mode) — the Carpincho extension build/load instructions.
186
-
187
-
## How to Add a New Stack
188
-
189
-
1. **`source/constants/config.ts`** — add a `Stack` union member and a `stackDefinitions` entry: `label`, `description`, `repoUrl`, `refType`, optional `ref`, `packageManager`, `removeAfterClone`, `envFiles`, `features`.
190
-
2. **`source/operations/cleanupFiles.ts`** — add a `cleanupXxxFiles` function and route to it from the top-level `cleanupFiles` dispatcher.
6. **Verify** — `pnpmbuild&&pnpmlint&&pnpmtest`. Smoke-test with `DAPPBOOSTER_<STACK>_REPO_URL=file:///path/to/local/clone`.
195
-
196
-
## HowtoAddaNewFeaturetoanExistingStack
197
-
198
-
1.**`source/constants/config.ts`** — addanentrytothestack's `features` map. For **Canton**, also list the feature's`paths`: cleanupisdata-driven, sonocleanupcodeisneededandscriptsthattargetaremoveddirectoryarestrippedautomatically. Ifitshipsanenvfile, addan`ifFeature`-gated`envFiles`entry.
199
-
2.**`source/operations/cleanupFiles.ts`** — **EVMonly**: addacleanupfunction for the feature and call it from `cleanupEvmFiles` when deselected; ifithasscripts, addremovalto`patchPackageJsonEvm`. Cantonneedsnochangehere.
0 commit comments