Skip to content

Commit a8dae5b

Browse files
authored
Merge pull request #6 from BootNodeDev/feat/canton-installer-v3.3.0
feat: Canton installer v3.3.0 — optional GitHub & pre-commit via a default mode
2 parents 3dfb4fc + 2f90593 commit a8dae5b

26 files changed

Lines changed: 499 additions & 437 deletions

AGENTS.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@
99
A CLI installer tool for dAppBooster projects. It supports two **stacks** and two **modes**:
1010

1111
- **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.
12-
- **Interactive** (default): React + Ink TUI that prompts for stack first, then project name, then clone → installation mode → optional packages → install → cleanup → post-install. The stack prompt is skipped when `--canton`, `--evm`, or `--stack` is supplied.
13-
- **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`]. Omitting a stack flag in non-interactive mode defaults to `evm` for backward compatibility.
12+
- **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.
13+
- **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.
1414

1515
## Stack & Conventions
1616

architecture.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,8 @@ source/
4444
StackSelection.tsx First step: pick a stack (skipped if preselectedStack is passed)
4545
ProjectName.tsx Prompt for project name
4646
CloneRepo/CloneRepo.tsx Clone progress display (receives stack)
47-
InstallationMode.tsx Full / Custom selection
48-
OptionalPackages.tsx Feature multiselect (per-stack, enforces feature dependencies)
47+
InstallationMode.tsx Mode selection (Canton: Default/Full/Custom; EVM: Full/Custom)
48+
OptionalPackages.tsx Feature multiselect (per-stack; pre-checks default:true features)
4949
Install/Install.tsx Install progress display (receives stack)
5050
FileCleanup.tsx Cleanup progress display (receives stack)
5151
PostInstall.tsx Post-install instructions, stack-specific

docs/architecture/abstractions.md

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,14 @@ type StackConfig = {
1616
ref?: string // required when refType === 'branch'
1717
packageManager: 'pnpm' | 'npm'
1818
removeAfterClone: string[] // paths nuked between clone and `git init` (empty for both stacks today)
19+
postInstall?: string[] // stack-level post-install guidance, shown for every scaffold (Canton run steps)
1920
envFiles: Array<{ from: string; to: string; ifFeature?: string }>
2021
features: Record<string, FeatureDefinition>
2122
}
2223
```
2324
25+
Installation modes are stack-aware via `getInstallationModes(stack)` — Canton offers `default` / `full` / `custom`, EVM offers `full` / `custom`. The `default: boolean` flag on a feature has two roles: it pre-checks the feature in the custom multiselect **and** defines membership in `default` mode (`getDefaultFeatureNames(stack)` = the `default: true` set). `default` mode is Canton-only because EVM has no `default: false` features (there it would equal `full`).
26+
2427
`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.
2528
2629
`getFeatureNames(stack)` and `isFeatureNameValid(stack, name)` are the per-stack feature accessors. There is no global `featureDefinitions` export — that would imply a single stack.
@@ -43,7 +46,7 @@ type FeatureDefinition = {
4346
4447
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.
4548
46-
**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 `--features e2e` 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. `e2e requires counter` is the only dependency today. `--info` surfaces each feature's `requires` so agents can resolve dependencies themselves.
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.
4750
4851
## Operations Layer (`source/operations/`)
4952
@@ -53,8 +56,8 @@ Plain async functions, no UI dependencies. Each operation that varies per stack
5356
|---|---|
5457
| `cloneRepo(stack, projectName, onProgress?)` | Reads `stack.refType`. **tag-latest**: shallow clone with `--no-checkout`, `git fetch --tags`, then `git checkout $(git describe --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 `git init`. Uses `execFile` everywhere except the tag-latest shell substitution. |
5558
| `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). |
56-
| `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> run postinstall`. Custom with all features: `<pm> install`. `execFile` only — never shell. |
57-
| `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. |
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> run postinstall`; with nothing to remove: `<pm> install`. Canton features all carry `packages: []`, so Canton always runs a plain `npm install` (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 `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`). 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. |
5861
5962
### Interrupt safety (`installGuard`)
6063

docs/architecture/data-flow.md

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -28,16 +28,18 @@ default → dynamic import ink + App (preselectedStack passed if resolved) →
2828
2. `--name` required
2929
3. `--mode` required
3030
4. `--name` matches `/^[a-zA-Z0-9_]+$/`
31-
5. `--mode` is `full` or `custom`
32-
6. Full mode: skip to step 10 (features ignored, all stack features installed)
33-
7. `--features` required for custom mode
34-
8. Parsed features list is non-empty (rejects trailing commas, whitespace-only entries)
35-
9. Every feature name is valid **for the selected stack**
36-
10. Project directory does not already exist
37-
38-
Custom-mode selections are then expanded with `resolveSelectedFeatures` (see
39-
[abstractions](./abstractions.md#feature-definitions)), so feature dependencies are pulled in
40-
before the operations run and before the result is reported.
31+
5. `--mode` is `full`, `default`, or `custom`
32+
6. `default` mode is rejected for the `evm` stack (Canton-only)
33+
7. Full / default mode: skip to step 11 (features come from the mode, `--features` ignored — `full` = all, `default` = the `default: true` set)
34+
8. `--features` required for custom mode
35+
9. Parsed features list is non-empty (rejects trailing commas, whitespace-only entries)
36+
10. Every feature name is valid **for the selected stack**
37+
11. Project directory does not already exist
38+
39+
The kept-feature list is derived from the mode via `resolveModeFeatures` (see
40+
[abstractions](./abstractions.md#feature-definitions)); for custom mode this expands the selection
41+
with `resolveSelectedFeatures` so any feature dependencies are pulled in before the operations run
42+
and before the result is reported.
4143

4244
**Non-interactive execution order:**
4345
`cloneRepo``createEnvFile``installPackages``cleanupFiles` → success JSON
@@ -50,14 +52,14 @@ Any error produces `{ "success": false, "error": "..." }` and exit code 1. Error
5052
"success": true,
5153
"stack": "evm|canton",
5254
"projectName": "...",
53-
"mode": "full|custom",
55+
"mode": "full|default|custom",
5456
"features": ["..."],
5557
"path": "/absolute/path",
5658
"postInstall": ["..."]
5759
}
5860
```
5961

60-
For full mode, `features` lists all of the stack's feature names. For custom mode, the selected ones plus any dependencies they pulled in.
62+
For full mode, `features` lists all of the stack's feature names; for default mode, the `default: true` set (Canton: `carpincho`, `llm`); for custom mode, the selected ones plus any dependencies they pulled in.
6163

6264
## Interactive (human)
6365

@@ -81,4 +83,4 @@ Operations (disk): CloneRepo → Install → FileCleanup → PostInstall
8183

8284
Once operations begin, `CloneRepo` calls `beginInstall` (see [abstractions → installGuard](./abstractions.md#interrupt-safety-installguard)) and `FileCleanup` calls `completeInstall` on success, so a Ctrl+C mid-scaffold removes the partial directory while a finished project is left intact.
8385

84-
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. The `OptionalPackages` multiselect enforces feature dependencies live via `applyFeatureToggle`. `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.
86+
Components are presentation-only — they call operations via `useEffect` and render status. Components receive `MultiSelectItem[]` for feature selection (TUI concern), then derive the kept-feature `FeatureName[]` via `resolveModeFeatures(stack, mode, selected)` before calling operations — so `full`/`default` resolve correctly even though the multiselect is skipped. The `OptionalPackages` multiselect pre-checks `default: true` features and enforces feature dependencies live via `applyFeatureToggle`. `PostInstall` renders stack-specific instructions; the EVM branch shows the subgraph warning when applicable, the Canton branch always shows the `canton:up`/`app:dev` commands and — when the `carpincho` feature is in the resolved set — the Carpincho extension build/load instructions.

docs/architecture/extending.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414

1515
## How to Add a New Feature to an Existing Stack
1616

17-
1. **`source/constants/config.ts`** — add an entry to the stack's `features` map. For **Canton**, also list the feature's `paths`: cleanup is data-driven, so no cleanup code is needed and scripts that target a removed directory are stripped automatically. If it ships an env file, add an `ifFeature`-gated `envFiles` entry. If it depends on another feature, add `requires` — resolution is automatic in both the interactive and non-interactive paths.
17+
1. **`source/constants/config.ts`** — add an entry to the stack's `features` map. The `default` flag governs both the custom-mode pre-check and `default`-mode membership: set `default: true` for "kept by the recommended install", `default: false` for "removed by default / opt-in" (Canton's `github` and `precommit`). For **Canton**, also list the feature's `paths`: cleanup is data-driven, so no cleanup code is needed and scripts that target a removed directory are stripped automatically. If it ships an env file, add an `ifFeature`-gated `envFiles` entry. If it depends on another feature, add `requires` — resolution is automatic in both the interactive and non-interactive paths.
1818
2. **`source/operations/cleanupFiles.ts`****EVM only**: add a cleanup function for the feature and call it from `cleanupEvmFiles` when deselected; if it has scripts, add removal to `patchPackageJsonEvm`. Canton needs no change here.
1919
3. **`source/components/steps/PostInstall.tsx`** — extend stack-specific instructions if needed.
2020
4. **`source/cli.tsx`** — update the `--help` text.

package.json

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "dappbooster",
3-
"version": "3.2.0",
3+
"version": "3.3.0",
44
"description": "Agent-friendly dAppBooster installer that scaffolds Web3 dApps via TUI or non-interactive CLI/CI.",
55
"keywords": [
66
"dappbooster",
@@ -39,9 +39,7 @@
3939
"lint": "pnpm biome check",
4040
"lint:fix": "pnpm biome check --write"
4141
},
42-
"files": [
43-
"dist"
44-
],
42+
"files": ["dist"],
4543
"dependencies": {
4644
"figures": "^6.1.0",
4745
"ink": "^5.2.1",

0 commit comments

Comments
 (0)