tcsetup 1.1.0 → 1.3.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/bin/cli.js +34 -86
- package/commands/feature.workflow.md +246 -0
- package/package.json +2 -1
- package/src/installer.js +86 -0
- package/src/updater.js +116 -0
package/bin/cli.js
CHANGED
|
@@ -1,24 +1,28 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import { readFileSync, mkdirSync, copyFileSync, existsSync } from "node:fs";
|
|
3
|
+
import { argv, exit } from "node:process";
|
|
4
|
+
import { readFileSync } from "node:fs";
|
|
6
5
|
import { fileURLToPath } from "node:url";
|
|
7
6
|
import { dirname, join } from "node:path";
|
|
7
|
+
import { install } from "../src/installer.js";
|
|
8
|
+
import { update } from "../src/updater.js";
|
|
8
9
|
|
|
9
10
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
10
11
|
const { version } = JSON.parse(readFileSync(join(__dirname, "..", "package.json"), "utf8"));
|
|
11
12
|
|
|
12
|
-
const
|
|
13
|
+
const command = argv[2];
|
|
14
|
+
const flags = argv.slice(3);
|
|
13
15
|
|
|
14
16
|
const HELP = `
|
|
15
|
-
tcsetup — Bootstrap
|
|
17
|
+
tcsetup v${version} — Bootstrap and update the TC toolchain.
|
|
16
18
|
|
|
17
19
|
Usage:
|
|
18
|
-
npx tcsetup Run all setup steps
|
|
20
|
+
npx tcsetup Run all setup steps (same as init)
|
|
21
|
+
npx tcsetup init Install the full TC toolchain
|
|
22
|
+
npx tcsetup update Update all installed TC tools to latest
|
|
19
23
|
npx tcsetup help Show this help message
|
|
20
24
|
|
|
21
|
-
Options:
|
|
25
|
+
Options (init):
|
|
22
26
|
--skip-bmad Skip BMAD Method install
|
|
23
27
|
--skip-speckit Skip Spec Kit init
|
|
24
28
|
--skip-agreements Skip Agreement System init
|
|
@@ -27,84 +31,28 @@ Options:
|
|
|
27
31
|
--skip-lifecycle Skip Feature Lifecycle Tracker init
|
|
28
32
|
`;
|
|
29
33
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
flag: "--skip-adr",
|
|
54
|
-
cmd: "npx adr-system init --yes",
|
|
55
|
-
},
|
|
56
|
-
{
|
|
57
|
-
name: "Mermaid Workbench",
|
|
58
|
-
flag: "--skip-mermaid",
|
|
59
|
-
cmd: "npx mermaid-workbench init",
|
|
60
|
-
},
|
|
61
|
-
{
|
|
62
|
-
name: "Feature Lifecycle Tracker",
|
|
63
|
-
flag: "--skip-lifecycle",
|
|
64
|
-
cmd: "npx feature-lifecycle init --yes",
|
|
65
|
-
},
|
|
66
|
-
];
|
|
67
|
-
|
|
68
|
-
console.log(`\n tcsetup v${version}\n`);
|
|
69
|
-
|
|
70
|
-
let current = 0;
|
|
71
|
-
const total = steps.filter((s) => !flags.includes(s.flag)).length;
|
|
72
|
-
|
|
73
|
-
for (const step of steps) {
|
|
74
|
-
if (flags.includes(step.flag)) {
|
|
75
|
-
console.log(` [skip] ${step.name} (${step.flag})\n`);
|
|
76
|
-
continue;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
current++;
|
|
80
|
-
console.log(` [${current}/${total}] ${step.name}`);
|
|
81
|
-
console.log(` > ${step.cmd}\n`);
|
|
82
|
-
|
|
83
|
-
try {
|
|
84
|
-
execSync(step.cmd, { stdio: "inherit" });
|
|
85
|
-
console.log();
|
|
86
|
-
} catch (err) {
|
|
87
|
-
console.error(`\n ⚠ ${step.name} failed (exit code ${err.status}).`);
|
|
88
|
-
console.error(` Continuing with remaining steps...\n`);
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
// ── Install Claude Code commands ──────────────────────
|
|
93
|
-
const commandsSource = join(__dirname, "..", "commands");
|
|
94
|
-
const commandsDest = join(process.cwd(), ".claude", "commands");
|
|
95
|
-
|
|
96
|
-
if (existsSync(commandsSource)) {
|
|
97
|
-
const commandFiles = ["tcsetup.onboard.md"];
|
|
98
|
-
mkdirSync(commandsDest, { recursive: true });
|
|
99
|
-
for (const file of commandFiles) {
|
|
100
|
-
const src = join(commandsSource, file);
|
|
101
|
-
const dest = join(commandsDest, file);
|
|
102
|
-
if (existsSync(src)) {
|
|
103
|
-
copyFileSync(src, dest);
|
|
104
|
-
console.log(` [cmd] Installed .claude/commands/${file}`);
|
|
34
|
+
switch (command) {
|
|
35
|
+
case "init":
|
|
36
|
+
install(flags);
|
|
37
|
+
break;
|
|
38
|
+
case "update":
|
|
39
|
+
update(flags);
|
|
40
|
+
break;
|
|
41
|
+
case "help":
|
|
42
|
+
case "--help":
|
|
43
|
+
case "-h":
|
|
44
|
+
console.log(HELP);
|
|
45
|
+
break;
|
|
46
|
+
case undefined:
|
|
47
|
+
install([]);
|
|
48
|
+
break;
|
|
49
|
+
default:
|
|
50
|
+
// Flags without subcommand (e.g., npx tcsetup --skip-bmad) → treat as init
|
|
51
|
+
if (command.startsWith("-")) {
|
|
52
|
+
install(argv.slice(2));
|
|
53
|
+
} else {
|
|
54
|
+
console.error(` Unknown command: ${command}\n`);
|
|
55
|
+
console.log(HELP);
|
|
56
|
+
exit(1);
|
|
105
57
|
}
|
|
106
|
-
}
|
|
107
|
-
console.log();
|
|
108
58
|
}
|
|
109
|
-
|
|
110
|
-
console.log(" Done! Project setup complete.\n");
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
# /feature.workflow — Playbook Router
|
|
2
|
+
|
|
3
|
+
**Input**: `$ARGUMENTS` (feature name or ID, e.g., `user-auth` or `001-user-auth`)
|
|
4
|
+
|
|
5
|
+
## Execution
|
|
6
|
+
|
|
7
|
+
Follow these steps exactly:
|
|
8
|
+
|
|
9
|
+
### 1. Read config
|
|
10
|
+
|
|
11
|
+
Read `.features/config.yaml` and extract all path settings:
|
|
12
|
+
- `bmad_output_dir`, `speckit_specs_dir`, `agreements_dir`, `adr_dir`, `mermaid_dir`
|
|
13
|
+
- `default_owner`
|
|
14
|
+
|
|
15
|
+
Read `.features/index.yaml` to get the list of registered features.
|
|
16
|
+
|
|
17
|
+
### 2. Resolve feature identity
|
|
18
|
+
|
|
19
|
+
**If `$ARGUMENTS` is empty or missing:**
|
|
20
|
+
- Read `.features/index.yaml` and list all known features
|
|
21
|
+
- Ask the user: "Which feature do you want to work on? Enter a feature ID or a new name."
|
|
22
|
+
- Use the response as the feature identifier going forward
|
|
23
|
+
|
|
24
|
+
**If `$ARGUMENTS` matches an existing feature ID** (e.g., `001-adr-system`):
|
|
25
|
+
- Use it directly
|
|
26
|
+
|
|
27
|
+
**If `$ARGUMENTS` is a name without a number prefix** (e.g., `user-auth`):
|
|
28
|
+
- Check `.features/index.yaml` for a feature whose ID ends with that name
|
|
29
|
+
- If found, use that existing feature ID
|
|
30
|
+
- If NOT found, this is a **new feature** — auto-assign the next number:
|
|
31
|
+
- Read all `###-*.yaml` files in `.features/`
|
|
32
|
+
- Find the highest number prefix
|
|
33
|
+
- Assign `{highest + 1, zero-padded to 3}-{name}` (e.g., `006-user-auth`)
|
|
34
|
+
|
|
35
|
+
Store the resolved feature ID as `FEATURE` for all subsequent steps.
|
|
36
|
+
|
|
37
|
+
### 3. Detect workflow path
|
|
38
|
+
|
|
39
|
+
**If this is an existing feature** (feature YAML already exists):
|
|
40
|
+
- Skip to step 5 (artifact scan) — the workflow path was already chosen
|
|
41
|
+
|
|
42
|
+
**If this is a new feature**, ask the user with `AskUserQuestion`:
|
|
43
|
+
|
|
44
|
+
```
|
|
45
|
+
Which workflow path for "{FEATURE}"?
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Options:
|
|
49
|
+
1. **Full Method** — "Complex features or product bets. Includes product brief, PRD, architecture, epics, then spec, plan, tasks, agreement, implement."
|
|
50
|
+
2. **Quick Flow** — "Small features or fixes. Quick spec, then specify, tasks, implement."
|
|
51
|
+
|
|
52
|
+
Store the choice as `WORKFLOW_PATH` (`full` or `quick`).
|
|
53
|
+
|
|
54
|
+
### 4. Scaffold new feature
|
|
55
|
+
|
|
56
|
+
Only if this is a new feature:
|
|
57
|
+
|
|
58
|
+
**Create feature YAML:**
|
|
59
|
+
- Create `.features/{FEATURE}.yaml` from `.features/_templates/feature.tpl.yaml`
|
|
60
|
+
- Replace `{{feature_id}}` with `FEATURE`
|
|
61
|
+
- Replace `{{title}}` with human-readable form of the name (kebab-case to Title Case)
|
|
62
|
+
- Replace `{{owner}}` with `default_owner` from config
|
|
63
|
+
- Replace `{{date}}` and `{{timestamp}}` with today's date/time
|
|
64
|
+
- Set `lifecycle.stage` to `ideation`
|
|
65
|
+
- Add a custom field `workflow_path: "full"` or `workflow_path: "quick"`
|
|
66
|
+
|
|
67
|
+
**Create per-feature BMAD directory:**
|
|
68
|
+
- Create directory: `{bmad_output_dir}/planning-artifacts/{FEATURE}/`
|
|
69
|
+
|
|
70
|
+
**Create SpecKit directory:**
|
|
71
|
+
- Create directory: `{speckit_specs_dir}/{FEATURE}/`
|
|
72
|
+
|
|
73
|
+
**Update index:**
|
|
74
|
+
- Add the new feature to `.features/index.yaml`
|
|
75
|
+
|
|
76
|
+
### 5. Scan all artifacts
|
|
77
|
+
|
|
78
|
+
Scan the filesystem for every artifact in the dependency chain. For each, record `true`/`false` (or count for tasks):
|
|
79
|
+
|
|
80
|
+
| Key | Detection Path |
|
|
81
|
+
|-----|---------------|
|
|
82
|
+
| `bmad.brief` | `{bmad_output_dir}/planning-artifacts/{FEATURE}/` — any file matching `*brief*` |
|
|
83
|
+
| `bmad.prd` | `{bmad_output_dir}/planning-artifacts/{FEATURE}/` — any file matching `*prd*` |
|
|
84
|
+
| `bmad.architecture` | `{bmad_output_dir}/planning-artifacts/{FEATURE}/` — any file matching `*architecture*` |
|
|
85
|
+
| `bmad.epics` | `{bmad_output_dir}/planning-artifacts/{FEATURE}/` — any file matching `*epic*` or `*stories*` |
|
|
86
|
+
| `bmad.quick_spec` | `{bmad_output_dir}/planning-artifacts/{FEATURE}/` — any file matching `*quick*spec*` or `*quick-spec*` |
|
|
87
|
+
| `speckit.spec` | `{speckit_specs_dir}/{FEATURE}/spec.md` |
|
|
88
|
+
| `speckit.plan` | `{speckit_specs_dir}/{FEATURE}/plan.md` |
|
|
89
|
+
| `speckit.tasks` | `{speckit_specs_dir}/{FEATURE}/tasks.md` — also count `- [x]` (done) and `- [ ]` (pending) |
|
|
90
|
+
| `agreement.exists` | `{agreements_dir}/{FEATURE}/agreement.yaml` |
|
|
91
|
+
| `agreement.check` | `{agreements_dir}/{FEATURE}/check-report.md` — read verdict if present |
|
|
92
|
+
| `feature.yaml` | `.features/{FEATURE}.yaml` |
|
|
93
|
+
| `mermaid.index` | `{mermaid_dir}/{FEATURE}/_index.yaml` |
|
|
94
|
+
|
|
95
|
+
Read `workflow_path` from the feature YAML (defaults to `full` if missing).
|
|
96
|
+
|
|
97
|
+
### 6. Evaluate dependency chain and find next step
|
|
98
|
+
|
|
99
|
+
**Full Method dependency chain:**
|
|
100
|
+
|
|
101
|
+
```
|
|
102
|
+
Step Name Requires Artifact Key Command
|
|
103
|
+
───── ────────────── ──────────────────────────── ─────────────────── ─────────────────────────────────────────
|
|
104
|
+
1 Brief (none) bmad.brief /bmad-bmm-create-product-brief
|
|
105
|
+
2 PRD brief bmad.prd /bmad-bmm-create-prd
|
|
106
|
+
3 Architecture PRD (optional step) bmad.architecture /bmad-bmm-create-architecture
|
|
107
|
+
4 Epics/Stories PRD (optional step) bmad.epics /bmad-bmm-create-epics-and-stories
|
|
108
|
+
── GATE A: brief + PRD required ──
|
|
109
|
+
5 Specify GATE A speckit.spec /speckit.specify {FEATURE}
|
|
110
|
+
6 Plan spec speckit.plan /speckit.plan
|
|
111
|
+
7 Tasks spec speckit.tasks /speckit.tasks
|
|
112
|
+
── GATE B: spec + tasks + agreement ──
|
|
113
|
+
8 Agreement spec + tasks agreement.exists /agreement.create {FEATURE}
|
|
114
|
+
9 Implement GATE B (task progress) /speckit.implement
|
|
115
|
+
── GATE C: tasks show meaningful progress ──
|
|
116
|
+
10 Agree. Check task progress >= 50% agreement.check /agreement.check {FEATURE}
|
|
117
|
+
11 Feature Status (any time) feature.yaml /feature.status {FEATURE}
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
**Quick Flow dependency chain:**
|
|
121
|
+
|
|
122
|
+
```
|
|
123
|
+
Step Name Requires Artifact Key Command
|
|
124
|
+
───── ────────────── ──────────────────────────── ─────────────────── ─────────────────────────────────────────
|
|
125
|
+
1 Quick Spec (none) bmad.quick_spec /bmad-bmm-quick-spec
|
|
126
|
+
── GATE A: quick_spec required ──
|
|
127
|
+
2 Specify GATE A speckit.spec /speckit.specify {FEATURE}
|
|
128
|
+
3 Tasks spec speckit.tasks /speckit.tasks
|
|
129
|
+
── GATE B: spec + tasks required ──
|
|
130
|
+
4 Implement GATE B (task progress) /speckit.implement
|
|
131
|
+
5 Feature Status (any time) feature.yaml /feature.status {FEATURE}
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
**Finding the next step:**
|
|
135
|
+
- Walk the chain top to bottom
|
|
136
|
+
- The first step whose artifact key is `false` (or whose gate is not satisfied) is the **next step**
|
|
137
|
+
- If ALL steps are complete, the feature is **done**
|
|
138
|
+
|
|
139
|
+
**Gate evaluation:**
|
|
140
|
+
- **GATE A (Full)**: `bmad.brief == true AND bmad.prd == true`
|
|
141
|
+
- **GATE A (Quick)**: `bmad.quick_spec == true`
|
|
142
|
+
- **GATE B (Full)**: `speckit.spec == true AND speckit.tasks == true AND agreement.exists == true`
|
|
143
|
+
- **GATE B (Quick)**: `speckit.spec == true AND speckit.tasks == true`
|
|
144
|
+
- **GATE C**: tasks completion >= 50% (tasks_done / tasks_total >= 0.5)
|
|
145
|
+
|
|
146
|
+
### 7. Display progress dashboard
|
|
147
|
+
|
|
148
|
+
Output the following Markdown report:
|
|
149
|
+
|
|
150
|
+
```markdown
|
|
151
|
+
## Workflow: {FEATURE} — {title}
|
|
152
|
+
|
|
153
|
+
**Path**: {Full Method | Quick Flow} | **Stage**: {current lifecycle stage}
|
|
154
|
+
**Owner**: {owner} | **Next Step**: Step {N} — {step name}
|
|
155
|
+
|
|
156
|
+
### Progress
|
|
157
|
+
|
|
158
|
+
{For each step in the chain, show a status indicator}
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
**For Full Method, display:**
|
|
162
|
+
|
|
163
|
+
```markdown
|
|
164
|
+
### Progress — Full Method
|
|
165
|
+
|
|
166
|
+
| # | Step | Status | Artifact |
|
|
167
|
+
|---|------|--------|----------|
|
|
168
|
+
| 1 | Brief | {done/pending/current} | {file path or —} |
|
|
169
|
+
| 2 | PRD | {done/pending/current} | {file path or —} |
|
|
170
|
+
| 3 | Architecture | {done/skip/pending} | {file path or —} |
|
|
171
|
+
| 4 | Epics/Stories | {done/skip/pending} | {file path or —} |
|
|
172
|
+
| | **GATE A** | {pass/blocked} | Brief + PRD |
|
|
173
|
+
| 5 | Specify | {done/pending/blocked} | {file path or —} |
|
|
174
|
+
| 6 | Plan | {done/pending/blocked} | {file path or —} |
|
|
175
|
+
| 7 | Tasks | {done/pending/blocked} | {file path or —} |
|
|
176
|
+
| | **GATE B** | {pass/blocked} | Spec + Tasks + Agreement |
|
|
177
|
+
| 8 | Agreement | {done/pending/blocked} | {file path or —} |
|
|
178
|
+
| 9 | Implement | {done/in-progress/blocked} | {done}/{total} tasks ({pct}%) |
|
|
179
|
+
| | **GATE C** | {pass/blocked} | Tasks >= 50% |
|
|
180
|
+
| 10 | Agreement Check | {done/pending/blocked} | {PASS/FAIL/—} |
|
|
181
|
+
| 11 | Feature Status | {done/pending} | .features/{FEATURE}.yaml |
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
**For Quick Flow, display the equivalent shorter table.**
|
|
185
|
+
|
|
186
|
+
Use these status indicators:
|
|
187
|
+
- `done` — artifact exists
|
|
188
|
+
- `current` — this is the next step to do (highlight with **bold**)
|
|
189
|
+
- `pending` — not yet reachable (prerequisites not met)
|
|
190
|
+
- `blocked` — behind an unsatisfied gate
|
|
191
|
+
- `skip` — optional step, skipped
|
|
192
|
+
- `in-progress` — partially complete (for tasks with progress)
|
|
193
|
+
|
|
194
|
+
### 8. Propose next action
|
|
195
|
+
|
|
196
|
+
Based on the next step identified in step 6, output:
|
|
197
|
+
|
|
198
|
+
```markdown
|
|
199
|
+
### Next Action
|
|
200
|
+
|
|
201
|
+
{description of what to do next}
|
|
202
|
+
|
|
203
|
+
**Run:** `/command-name {arguments}`
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
**BMAD output path instruction:** When the next step is a BMAD command (steps 1-4 in Full, step 1 in Quick), add this note:
|
|
207
|
+
|
|
208
|
+
```markdown
|
|
209
|
+
> When the BMAD agent asks where to save, tell it:
|
|
210
|
+
> **Save to:** `{bmad_output_dir}/planning-artifacts/{FEATURE}/`
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
**Special cases:**
|
|
214
|
+
|
|
215
|
+
- If the next step is `/speckit.specify`, include `{FEATURE}` as the argument
|
|
216
|
+
- If the next step is `/agreement.create`, include `{FEATURE}` as the argument
|
|
217
|
+
- If the next step is `/agreement.check`, include `{FEATURE}` as the argument
|
|
218
|
+
- If the next step is `/feature.status`, include `{FEATURE}` as the argument
|
|
219
|
+
- If ALL steps are complete:
|
|
220
|
+
```markdown
|
|
221
|
+
### Status: Complete
|
|
222
|
+
|
|
223
|
+
All workflow steps for **{FEATURE}** are done.
|
|
224
|
+
|
|
225
|
+
**Suggested next actions:**
|
|
226
|
+
- `/feature.status {FEATURE}` — refresh feature status
|
|
227
|
+
- `/agreement.check {FEATURE}` — verify agreement compliance
|
|
228
|
+
- `/feature.list` — see all features
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
### 9. Handle re-entry
|
|
232
|
+
|
|
233
|
+
This command is **re-entrant**. Every invocation:
|
|
234
|
+
1. Re-scans all artifacts (state may have changed since last run)
|
|
235
|
+
2. Recalculates the next step
|
|
236
|
+
3. Shows the updated dashboard
|
|
237
|
+
|
|
238
|
+
The user runs `/feature.workflow {FEATURE}` after completing each step to see what's next. No state is stored beyond what the filesystem already contains.
|
|
239
|
+
|
|
240
|
+
## Handoffs
|
|
241
|
+
|
|
242
|
+
- After any BMAD step completes → re-run `/feature.workflow {FEATURE}`
|
|
243
|
+
- After any SpecKit step completes → re-run `/feature.workflow {FEATURE}`
|
|
244
|
+
- To see detailed feature status → `/feature.status {FEATURE}`
|
|
245
|
+
- To see all features → `/feature.list`
|
|
246
|
+
- To check agreement health → `/agreement.check {FEATURE}`
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tcsetup",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Bootstrap a new project with BMAD, Spec Kit, Agreement System, and Mermaid Workbench in one command.",
|
|
6
6
|
"bin": {
|
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
},
|
|
23
23
|
"files": [
|
|
24
24
|
"bin/",
|
|
25
|
+
"src/",
|
|
25
26
|
"commands/"
|
|
26
27
|
],
|
|
27
28
|
"engines": {
|
package/src/installer.js
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { execSync } from "node:child_process";
|
|
2
|
+
import { readFileSync, mkdirSync, copyFileSync, existsSync } from "node:fs";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
import { dirname, join } from "node:path";
|
|
5
|
+
|
|
6
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
7
|
+
const { version } = JSON.parse(readFileSync(join(__dirname, "..", "package.json"), "utf8"));
|
|
8
|
+
|
|
9
|
+
const steps = [
|
|
10
|
+
{
|
|
11
|
+
name: "BMAD Method",
|
|
12
|
+
flag: "--skip-bmad",
|
|
13
|
+
cmd: "npx bmad-method install",
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
name: "Spec Kit",
|
|
17
|
+
flag: "--skip-speckit",
|
|
18
|
+
cmd: "specify init --here --ai claude",
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
name: "Agreement System",
|
|
22
|
+
flag: "--skip-agreements",
|
|
23
|
+
cmd: "npx agreement-system init --yes",
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
name: "ADR System",
|
|
27
|
+
flag: "--skip-adr",
|
|
28
|
+
cmd: "npx adr-system init --yes",
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
name: "Mermaid Workbench",
|
|
32
|
+
flag: "--skip-mermaid",
|
|
33
|
+
cmd: "npx mermaid-workbench init",
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
name: "Feature Lifecycle Tracker",
|
|
37
|
+
flag: "--skip-lifecycle",
|
|
38
|
+
cmd: "npx feature-lifecycle init --yes",
|
|
39
|
+
},
|
|
40
|
+
];
|
|
41
|
+
|
|
42
|
+
export function install(flags = []) {
|
|
43
|
+
console.log(`\n tcsetup v${version}\n`);
|
|
44
|
+
|
|
45
|
+
let current = 0;
|
|
46
|
+
const total = steps.filter((s) => !flags.includes(s.flag)).length;
|
|
47
|
+
|
|
48
|
+
for (const step of steps) {
|
|
49
|
+
if (flags.includes(step.flag)) {
|
|
50
|
+
console.log(` [skip] ${step.name} (${step.flag})\n`);
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
current++;
|
|
55
|
+
console.log(` [${current}/${total}] ${step.name}`);
|
|
56
|
+
console.log(` > ${step.cmd}\n`);
|
|
57
|
+
|
|
58
|
+
try {
|
|
59
|
+
execSync(step.cmd, { stdio: "inherit" });
|
|
60
|
+
console.log();
|
|
61
|
+
} catch (err) {
|
|
62
|
+
console.error(`\n ⚠ ${step.name} failed (exit code ${err.status}).`);
|
|
63
|
+
console.error(` Continuing with remaining steps...\n`);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// ── Install Claude Code commands ──────────────────────
|
|
68
|
+
const commandsSource = join(__dirname, "..", "commands");
|
|
69
|
+
const commandsDest = join(process.cwd(), ".claude", "commands");
|
|
70
|
+
|
|
71
|
+
if (existsSync(commandsSource)) {
|
|
72
|
+
const commandFiles = ["tcsetup.onboard.md", "feature.workflow.md"];
|
|
73
|
+
mkdirSync(commandsDest, { recursive: true });
|
|
74
|
+
for (const file of commandFiles) {
|
|
75
|
+
const src = join(commandsSource, file);
|
|
76
|
+
const dest = join(commandsDest, file);
|
|
77
|
+
if (existsSync(src)) {
|
|
78
|
+
copyFileSync(src, dest);
|
|
79
|
+
console.log(` [cmd] Installed .claude/commands/${file}`);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
console.log();
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
console.log(" Done! Project setup complete.\n");
|
|
86
|
+
}
|
package/src/updater.js
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { execSync } from "node:child_process";
|
|
2
|
+
import { existsSync, mkdirSync, copyFileSync, readFileSync } from "node:fs";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
import { dirname, join } from "node:path";
|
|
5
|
+
|
|
6
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
7
|
+
const { version } = JSON.parse(readFileSync(join(__dirname, "..", "package.json"), "utf8"));
|
|
8
|
+
|
|
9
|
+
const TOOLS = [
|
|
10
|
+
{
|
|
11
|
+
name: "ADR System",
|
|
12
|
+
marker: ".adr",
|
|
13
|
+
pkg: "adr-system",
|
|
14
|
+
cmd: "npx adr-system update",
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
name: "Agreement System",
|
|
18
|
+
marker: ".agreements",
|
|
19
|
+
pkg: "agreement-system",
|
|
20
|
+
cmd: "npx agreement-system update",
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
name: "Feature Lifecycle",
|
|
24
|
+
marker: ".features",
|
|
25
|
+
pkg: "feature-lifecycle",
|
|
26
|
+
cmd: "npx feature-lifecycle update",
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
name: "Mermaid Workbench",
|
|
30
|
+
marker: ["_bmad/modules/mermaid-workbench", ".bmad/modules/mermaid-workbench"],
|
|
31
|
+
pkg: "mermaid-workbench",
|
|
32
|
+
cmd: "npx mermaid-workbench init",
|
|
33
|
+
},
|
|
34
|
+
];
|
|
35
|
+
|
|
36
|
+
const COMMAND_FILES = ["tcsetup.onboard.md", "feature.workflow.md"];
|
|
37
|
+
|
|
38
|
+
function isInstalled(marker, projectRoot) {
|
|
39
|
+
if (Array.isArray(marker)) {
|
|
40
|
+
return marker.some((m) => existsSync(join(projectRoot, m)));
|
|
41
|
+
}
|
|
42
|
+
return existsSync(join(projectRoot, marker));
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function update(flags = []) {
|
|
46
|
+
const projectRoot = process.cwd();
|
|
47
|
+
|
|
48
|
+
console.log(`\n tcsetup update v${version}\n`);
|
|
49
|
+
|
|
50
|
+
// ── Detect installed tools ────────────────────────────
|
|
51
|
+
console.log(" Detecting installed tools...\n");
|
|
52
|
+
|
|
53
|
+
const detected = TOOLS.filter((tool) => isInstalled(tool.marker, projectRoot));
|
|
54
|
+
|
|
55
|
+
if (detected.length === 0) {
|
|
56
|
+
console.log(" No TC tools detected. Run `npx tcsetup` to onboard first.\n");
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
for (const tool of detected) {
|
|
61
|
+
console.log(` ✓ ${tool.name}`);
|
|
62
|
+
}
|
|
63
|
+
console.log();
|
|
64
|
+
|
|
65
|
+
// ── Update npm packages ───────────────────────────────
|
|
66
|
+
const pkgs = detected.map((t) => `${t.pkg}@latest`).join(" ");
|
|
67
|
+
console.log(` [1/3] Updating npm packages...`);
|
|
68
|
+
console.log(` > npm install ${pkgs}\n`);
|
|
69
|
+
|
|
70
|
+
try {
|
|
71
|
+
execSync(`npm install ${pkgs}`, { stdio: "inherit" });
|
|
72
|
+
console.log();
|
|
73
|
+
} catch (err) {
|
|
74
|
+
console.error(`\n ⚠ npm install failed (exit code ${err.status}).`);
|
|
75
|
+
console.error(` Continuing with sub-tool updates...\n`);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// ── Call sub-tool updates ─────────────────────────────
|
|
79
|
+
console.log(` [2/3] Running sub-tool updates...\n`);
|
|
80
|
+
|
|
81
|
+
for (const tool of detected) {
|
|
82
|
+
console.log(` > ${tool.cmd}`);
|
|
83
|
+
try {
|
|
84
|
+
execSync(tool.cmd, { stdio: "inherit" });
|
|
85
|
+
console.log();
|
|
86
|
+
} catch (err) {
|
|
87
|
+
console.error(`\n ⚠ ${tool.name} update failed (exit code ${err.status}).`);
|
|
88
|
+
console.error(` Continuing with remaining tools...\n`);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// ── Refresh tcsetup commands ──────────────────────────
|
|
93
|
+
console.log(` [3/3] Refreshing tcsetup commands...\n`);
|
|
94
|
+
|
|
95
|
+
const commandsSource = join(__dirname, "..", "commands");
|
|
96
|
+
const commandsDest = join(projectRoot, ".claude", "commands");
|
|
97
|
+
|
|
98
|
+
mkdirSync(commandsDest, { recursive: true });
|
|
99
|
+
|
|
100
|
+
for (const file of COMMAND_FILES) {
|
|
101
|
+
const src = join(commandsSource, file);
|
|
102
|
+
const dest = join(commandsDest, file);
|
|
103
|
+
if (existsSync(src)) {
|
|
104
|
+
copyFileSync(src, dest);
|
|
105
|
+
console.log(` update .claude/commands/${file}`);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
console.log();
|
|
109
|
+
|
|
110
|
+
// ── Summary ───────────────────────────────────────────
|
|
111
|
+
console.log(` Done! Updated ${detected.length} tool${detected.length === 1 ? "" : "s"}:`);
|
|
112
|
+
for (const tool of detected) {
|
|
113
|
+
console.log(` - ${tool.name}`);
|
|
114
|
+
}
|
|
115
|
+
console.log();
|
|
116
|
+
}
|