@polymorphism-tech/morph-spec 4.8.1 → 4.8.4
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 +2 -2
- package/claude-plugin.json +1 -1
- package/docs/CHEATSHEET.md +1 -1
- package/docs/QUICKSTART.md +1 -1
- package/framework/hooks/dev/guard-version-numbers.js +1 -1
- package/framework/skills/level-1-workflows/phase-clarify/SKILL.md +1 -1
- package/framework/skills/level-1-workflows/phase-codebase-analysis/SKILL.md +1 -1
- package/framework/skills/level-1-workflows/phase-design/SKILL.md +1 -1
- package/framework/skills/level-1-workflows/phase-implement/SKILL.md +1 -1
- package/framework/skills/level-1-workflows/phase-setup/SKILL.md +1 -1
- package/framework/skills/level-1-workflows/phase-tasks/SKILL.md +1 -1
- package/framework/skills/level-1-workflows/phase-uiux/SKILL.md +1 -1
- package/package.json +4 -4
- package/.morph/analytics/threads-log.jsonl +0 -54
- package/.morph/state.json +0 -198
- package/docs/ARCHITECTURE.md +0 -328
- package/docs/COMMAND-FLOWS.md +0 -398
- package/docs/plans/2026-02-22-claude-docs-morph-alignment-analysis.md +0 -514
- package/docs/plans/2026-02-22-claude-settings.md +0 -517
- package/docs/plans/2026-02-22-morph-cc-alignment-impl.md +0 -730
- package/docs/plans/2026-02-22-morph-spec-next.md +0 -480
- package/docs/plans/2026-02-22-native-alignment-design.md +0 -201
- package/docs/plans/2026-02-22-native-alignment-impl.md +0 -927
- package/docs/plans/2026-02-22-native-enrichment-design.md +0 -246
- package/docs/plans/2026-02-22-native-enrichment.md +0 -737
- package/docs/plans/2026-02-23-ddd-architecture-refactor.md +0 -1155
- package/docs/plans/2026-02-23-ddd-nextsteps.md +0 -684
- package/docs/plans/2026-02-23-infra-architect-refactor.md +0 -439
- package/docs/plans/2026-02-23-nextjs-code-review-design.md +0 -157
- package/docs/plans/2026-02-23-nextjs-code-review-impl.md +0 -1256
- package/docs/plans/2026-02-23-nextjs-standards-design.md +0 -150
- package/docs/plans/2026-02-23-nextjs-standards-impl.md +0 -1848
- package/docs/plans/2026-02-24-cli-radical-simplification.md +0 -592
- package/docs/plans/2026-02-24-framework-failure-points.md +0 -125
- package/docs/plans/2026-02-24-morph-init-design.md +0 -337
- package/docs/plans/2026-02-24-morph-init-impl.md +0 -1269
- package/docs/plans/2026-02-24-tutorial-command-design.md +0 -71
- package/docs/plans/2026-02-24-tutorial-command.md +0 -298
- package/scripts/bump-version.js +0 -248
- package/scripts/generate-refs.js +0 -336
- package/scripts/generate-standards-registry.js +0 -44
- package/scripts/install-dev-hooks.js +0 -138
- package/scripts/scan-nextjs.mjs +0 -169
- package/scripts/validate-real.mjs +0 -255
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
# Design: `morph-spec tutorial` Command
|
|
2
|
-
|
|
3
|
-
**Status:** COMPLETE (see implementation plan)
|
|
4
|
-
|
|
5
|
-
**Date:** 2026-02-24
|
|
6
|
-
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
## Problem
|
|
10
|
-
|
|
11
|
-
After `morph-spec init`, first-time users see a suggestion to run `morph-spec tutorial`, but the command doesn't exist. New users also lack a quick way to understand the spec-first workflow and its phase pipeline.
|
|
12
|
-
|
|
13
|
-
---
|
|
14
|
-
|
|
15
|
-
## Goal
|
|
16
|
-
|
|
17
|
-
A self-contained CLI command that teaches the MORPH-SPEC mental model: the phase pipeline, what each phase produces, and how to trigger each phase. Pure stdout, no side effects.
|
|
18
|
-
|
|
19
|
-
---
|
|
20
|
-
|
|
21
|
-
## Approach: Workflow Walkthrough
|
|
22
|
-
|
|
23
|
-
A static, colored terminal output that explains the full pipeline top-to-bottom. No arguments, no options, no network calls, no file I/O.
|
|
24
|
-
|
|
25
|
-
Chosen over:
|
|
26
|
-
- **Interactive mode** — risks accidentally modifying project state
|
|
27
|
-
- **Quick reference** — duplicates `--help` with little added value
|
|
28
|
-
|
|
29
|
-
---
|
|
30
|
-
|
|
31
|
-
## Output Structure
|
|
32
|
-
|
|
33
|
-
1. **Header** — title + one-sentence philosophy
|
|
34
|
-
2. **Pipeline diagram** — ASCII showing phase sequence with optional phases bracketed
|
|
35
|
-
3. **Per-phase block** — for each of the 8 phases:
|
|
36
|
-
- Phase name + number
|
|
37
|
-
- What it does (1 sentence)
|
|
38
|
-
- What it produces (output files)
|
|
39
|
-
- How to trigger it (CLI command or `/slash-command` in Claude Code)
|
|
40
|
-
4. **Getting Started** — 3-step CTA ending with `morph-spec doctor` tip
|
|
41
|
-
|
|
42
|
-
---
|
|
43
|
-
|
|
44
|
-
## Implementation
|
|
45
|
-
|
|
46
|
-
| Item | Detail |
|
|
47
|
-
|------|--------|
|
|
48
|
-
| File | `src/commands/project/tutorial.js` |
|
|
49
|
-
| Export | `tutorialCommand` |
|
|
50
|
-
| Registration (index) | `src/commands/project/index.js` |
|
|
51
|
-
| Registration (bin) | `bin/morph-spec.js` |
|
|
52
|
-
| Dependencies | `chalk` (already installed) |
|
|
53
|
-
| Side effects | None — pure stdout |
|
|
54
|
-
| Size | ~100 lines |
|
|
55
|
-
|
|
56
|
-
Also: revert `init.js:518` back to `morph-spec tutorial` (the original intent).
|
|
57
|
-
|
|
58
|
-
---
|
|
59
|
-
|
|
60
|
-
## Phases Covered
|
|
61
|
-
|
|
62
|
-
| Phase | Trigger |
|
|
63
|
-
|-------|---------|
|
|
64
|
-
| 0 · Proposal | `/morph-proposal <feature>` |
|
|
65
|
-
| 1 · Setup | auto-triggered inside `/morph-proposal` |
|
|
66
|
-
| 1.5 · UI/UX (optional) | `/phase-uiux <feature>` |
|
|
67
|
-
| 2 · Design | `/phase-design <feature>` |
|
|
68
|
-
| 3 · Clarify | `/phase-clarify <feature>` |
|
|
69
|
-
| 4 · Tasks | auto-generated, or `/phase-tasks <feature>` |
|
|
70
|
-
| 5 · Implement | `/morph-apply <feature>` |
|
|
71
|
-
| 6 · Sync (optional) | `morph-spec sync <feature>` |
|
|
@@ -1,298 +0,0 @@
|
|
|
1
|
-
# Tutorial Command Implementation Plan
|
|
2
|
-
|
|
3
|
-
**Status:** COMPLETE
|
|
4
|
-
|
|
5
|
-
> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
|
|
6
|
-
|
|
7
|
-
**Goal:** Add a `morph-spec tutorial` command that prints the full MORPH-SPEC phase pipeline and getting-started steps to stdout.
|
|
8
|
-
|
|
9
|
-
**Architecture:** Single new file `src/commands/project/tutorial.js` exporting `tutorialCommand`. Registered in `bin/morph-spec.js` and re-exported from `src/commands/project/index.js`. Pure stdout using `chalk` — no file I/O, no network, no side effects. Also reverts `init.js` to suggest `morph-spec tutorial` (instead of the broken replacement made in a prior session).
|
|
10
|
-
|
|
11
|
-
**Tech Stack:** Node.js ESM, chalk (already installed), node:test + node:assert for tests.
|
|
12
|
-
|
|
13
|
-
---
|
|
14
|
-
|
|
15
|
-
### Task 1: Create `tutorial.js`
|
|
16
|
-
|
|
17
|
-
**Files:**
|
|
18
|
-
- Create: `src/commands/project/tutorial.js`
|
|
19
|
-
- Test: `test/commands/tutorial.test.js`
|
|
20
|
-
|
|
21
|
-
**Step 1: Write the failing test**
|
|
22
|
-
|
|
23
|
-
Create `test/commands/tutorial.test.js`:
|
|
24
|
-
|
|
25
|
-
```js
|
|
26
|
-
import { test, describe } from 'node:test';
|
|
27
|
-
import assert from 'node:assert/strict';
|
|
28
|
-
import { tutorialCommand } from '../../src/commands/project/tutorial.js';
|
|
29
|
-
|
|
30
|
-
describe('tutorialCommand', () => {
|
|
31
|
-
test('tutorialCommand is a function', () => {
|
|
32
|
-
assert.equal(typeof tutorialCommand, 'function');
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
test('tutorialCommand runs without throwing', () => {
|
|
36
|
-
assert.doesNotThrow(() => tutorialCommand());
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
test('tutorialCommand returns undefined (pure side-effect)', () => {
|
|
40
|
-
const result = tutorialCommand();
|
|
41
|
-
assert.equal(result, undefined);
|
|
42
|
-
});
|
|
43
|
-
});
|
|
44
|
-
```
|
|
45
|
-
|
|
46
|
-
**Step 2: Run test to verify it fails**
|
|
47
|
-
|
|
48
|
-
```bash
|
|
49
|
-
node --test test/commands/tutorial.test.js
|
|
50
|
-
```
|
|
51
|
-
Expected: FAIL — "Cannot find module"
|
|
52
|
-
|
|
53
|
-
**Step 3: Write the implementation**
|
|
54
|
-
|
|
55
|
-
Create `src/commands/project/tutorial.js`:
|
|
56
|
-
|
|
57
|
-
```js
|
|
58
|
-
/**
|
|
59
|
-
* Tutorial Command
|
|
60
|
-
*
|
|
61
|
-
* Prints the MORPH-SPEC workflow pipeline and getting-started steps.
|
|
62
|
-
* Pure stdout — no file I/O, no network, no side effects.
|
|
63
|
-
*
|
|
64
|
-
* Usage: morph-spec tutorial
|
|
65
|
-
*/
|
|
66
|
-
|
|
67
|
-
import chalk from 'chalk';
|
|
68
|
-
|
|
69
|
-
const PHASES = [
|
|
70
|
-
{
|
|
71
|
-
label: 'FASE 0 · PROPOSAL',
|
|
72
|
-
what: 'Captures user story + acceptance criteria.',
|
|
73
|
-
produces: '0-proposal/proposal.md',
|
|
74
|
-
how: '/morph-proposal <feature> (in Claude Code)',
|
|
75
|
-
},
|
|
76
|
-
{
|
|
77
|
-
label: 'FASE 1 · SETUP',
|
|
78
|
-
what: 'Detects stack, activates agents, confirms environment.',
|
|
79
|
-
produces: '.morph/state.json initialized',
|
|
80
|
-
how: 'auto-triggered inside /morph-proposal',
|
|
81
|
-
},
|
|
82
|
-
{
|
|
83
|
-
label: 'FASE 1.5 · UI/UX',
|
|
84
|
-
what: 'Design system, mockups, component specs, user flows.',
|
|
85
|
-
produces: '2-ui/{design-system,mockups,components,flows}.md',
|
|
86
|
-
how: '/phase-uiux <feature> (in Claude Code)',
|
|
87
|
-
optional: true,
|
|
88
|
-
},
|
|
89
|
-
{
|
|
90
|
-
label: 'FASE 2 · DESIGN',
|
|
91
|
-
what: 'Technical spec + C# contracts + architecture decisions.',
|
|
92
|
-
produces: '1-design/{spec.md, contracts-level{N}.cs, decisions.md}',
|
|
93
|
-
how: '/phase-design <feature> (in Claude Code)',
|
|
94
|
-
},
|
|
95
|
-
{
|
|
96
|
-
label: 'FASE 3 · CLARIFY',
|
|
97
|
-
what: 'Reviews spec for ambiguities, adds edge cases.',
|
|
98
|
-
produces: '1-design/spec.md (updated with clarifications)',
|
|
99
|
-
how: '/phase-clarify <feature> (in Claude Code)',
|
|
100
|
-
},
|
|
101
|
-
{
|
|
102
|
-
label: 'FASE 4 · TASKS',
|
|
103
|
-
what: 'Atomic task breakdown, DDD-aware.',
|
|
104
|
-
produces: '3-tasks/tasks.md',
|
|
105
|
-
how: 'auto-generated during /morph-proposal, or /phase-tasks <feature>',
|
|
106
|
-
},
|
|
107
|
-
{
|
|
108
|
-
label: 'FASE 5 · IMPLEMENT',
|
|
109
|
-
what: 'Code implementation with checkpoints every 3 tasks.',
|
|
110
|
-
produces: '4-implement/recap.md + source code',
|
|
111
|
-
how: '/morph-apply <feature> (in Claude Code)',
|
|
112
|
-
},
|
|
113
|
-
{
|
|
114
|
-
label: 'FASE 6 · SYNC',
|
|
115
|
-
what: 'Syncs decisions back to project standards.',
|
|
116
|
-
produces: '.morph/framework/standards/ (updated)',
|
|
117
|
-
how: 'morph-spec sync <feature>',
|
|
118
|
-
optional: true,
|
|
119
|
-
},
|
|
120
|
-
];
|
|
121
|
-
|
|
122
|
-
const SEP = chalk.dim('─'.repeat(58));
|
|
123
|
-
|
|
124
|
-
export function tutorialCommand() {
|
|
125
|
-
console.log('');
|
|
126
|
-
console.log(chalk.cyan.bold(' MORPH-SPEC Workflow Tutorial'));
|
|
127
|
-
console.log(SEP);
|
|
128
|
-
console.log('');
|
|
129
|
-
console.log(
|
|
130
|
-
chalk.white(
|
|
131
|
-
' MORPH-SPEC is spec-first: every feature goes through phases\n' +
|
|
132
|
-
' before any code is written. Each phase produces structured\n' +
|
|
133
|
-
' outputs that feed the next.'
|
|
134
|
-
)
|
|
135
|
-
);
|
|
136
|
-
console.log('');
|
|
137
|
-
console.log(SEP);
|
|
138
|
-
console.log(chalk.cyan.bold(' PIPELINE'));
|
|
139
|
-
console.log(SEP);
|
|
140
|
-
console.log('');
|
|
141
|
-
console.log(
|
|
142
|
-
chalk.white(' proposal → setup → ') +
|
|
143
|
-
chalk.dim('[uiux]') +
|
|
144
|
-
chalk.white(' → design → clarify → tasks → implement → ') +
|
|
145
|
-
chalk.dim('[sync]')
|
|
146
|
-
);
|
|
147
|
-
console.log(chalk.dim(' (phases in brackets are optional)'));
|
|
148
|
-
console.log('');
|
|
149
|
-
|
|
150
|
-
for (const phase of PHASES) {
|
|
151
|
-
console.log(SEP);
|
|
152
|
-
const label = phase.optional
|
|
153
|
-
? chalk.cyan.bold(` ${phase.label}`) + chalk.dim(' [optional]')
|
|
154
|
-
: chalk.cyan.bold(` ${phase.label}`);
|
|
155
|
-
console.log(label);
|
|
156
|
-
console.log(chalk.white(` What: ${phase.what}`));
|
|
157
|
-
console.log(chalk.dim(` Produces: ${phase.produces}`));
|
|
158
|
-
console.log(chalk.green(` How: ${phase.how}`));
|
|
159
|
-
console.log('');
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
console.log(SEP);
|
|
163
|
-
console.log(chalk.cyan.bold(' GETTING STARTED'));
|
|
164
|
-
console.log(SEP);
|
|
165
|
-
console.log('');
|
|
166
|
-
console.log(chalk.white(' 1. Open Claude Code in your project'));
|
|
167
|
-
console.log(chalk.white(' 2. Run: ') + chalk.green('/morph-proposal <your-feature-name>'));
|
|
168
|
-
console.log(chalk.white(' 3. Answer the questions — morph-spec handles the rest'));
|
|
169
|
-
console.log('');
|
|
170
|
-
console.log(chalk.dim(' Tip: run `morph-spec doctor` to verify your installation first.'));
|
|
171
|
-
console.log('');
|
|
172
|
-
}
|
|
173
|
-
```
|
|
174
|
-
|
|
175
|
-
**Step 4: Run test to verify it passes**
|
|
176
|
-
|
|
177
|
-
```bash
|
|
178
|
-
node --test test/commands/tutorial.test.js
|
|
179
|
-
```
|
|
180
|
-
Expected: 3 passing
|
|
181
|
-
|
|
182
|
-
**Step 5: Commit**
|
|
183
|
-
|
|
184
|
-
```bash
|
|
185
|
-
git add src/commands/project/tutorial.js test/commands/tutorial.test.js
|
|
186
|
-
git commit -m "feat(tutorial): add tutorial command with phase pipeline walkthrough"
|
|
187
|
-
```
|
|
188
|
-
|
|
189
|
-
---
|
|
190
|
-
|
|
191
|
-
### Task 2: Wire into project index and bin
|
|
192
|
-
|
|
193
|
-
**Files:**
|
|
194
|
-
- Modify: `src/commands/project/index.js`
|
|
195
|
-
- Modify: `bin/morph-spec.js`
|
|
196
|
-
|
|
197
|
-
**Step 1: Add export to `src/commands/project/index.js`**
|
|
198
|
-
|
|
199
|
-
After the existing `export { updateCommand }` line, add:
|
|
200
|
-
|
|
201
|
-
```js
|
|
202
|
-
export { tutorialCommand } from './tutorial.js';
|
|
203
|
-
```
|
|
204
|
-
|
|
205
|
-
**Step 2: Add import to `bin/morph-spec.js`**
|
|
206
|
-
|
|
207
|
-
After the `import { doctorCommand }` line (line ~13), add:
|
|
208
|
-
|
|
209
|
-
```js
|
|
210
|
-
import { tutorialCommand } from '../src/commands/project/tutorial.js';
|
|
211
|
-
```
|
|
212
|
-
|
|
213
|
-
**Step 3: Register the command in `bin/morph-spec.js`**
|
|
214
|
-
|
|
215
|
-
After the `doctor` command block (~line 128), add:
|
|
216
|
-
|
|
217
|
-
```js
|
|
218
|
-
program
|
|
219
|
-
.command('tutorial')
|
|
220
|
-
.description('Learn the MORPH-SPEC workflow — phase pipeline and getting started')
|
|
221
|
-
.action(tutorialCommand);
|
|
222
|
-
```
|
|
223
|
-
|
|
224
|
-
**Step 4: Smoke-test the CLI**
|
|
225
|
-
|
|
226
|
-
```bash
|
|
227
|
-
node bin/morph-spec.js tutorial
|
|
228
|
-
```
|
|
229
|
-
Expected: Full pipeline output printed to terminal without errors.
|
|
230
|
-
|
|
231
|
-
```bash
|
|
232
|
-
node bin/morph-spec.js --help | grep tutorial
|
|
233
|
-
```
|
|
234
|
-
Expected: `tutorial Learn the MORPH-SPEC workflow...`
|
|
235
|
-
|
|
236
|
-
**Step 5: Commit**
|
|
237
|
-
|
|
238
|
-
```bash
|
|
239
|
-
git add src/commands/project/index.js bin/morph-spec.js
|
|
240
|
-
git commit -m "feat(tutorial): register tutorial command in CLI"
|
|
241
|
-
```
|
|
242
|
-
|
|
243
|
-
---
|
|
244
|
-
|
|
245
|
-
### Task 3: Restore the `init.js` suggestion message
|
|
246
|
-
|
|
247
|
-
**Files:**
|
|
248
|
-
- Modify: `src/commands/project/init.js:518`
|
|
249
|
-
|
|
250
|
-
**Step 1: Revert the message**
|
|
251
|
-
|
|
252
|
-
Find line 518 in `src/commands/project/init.js`. Replace:
|
|
253
|
-
|
|
254
|
-
```js
|
|
255
|
-
logger.info('First time using morph-spec? Run `morph-spec doctor` to verify your setup, then `/morph-proposal <feature>` to start.');
|
|
256
|
-
```
|
|
257
|
-
|
|
258
|
-
With:
|
|
259
|
-
|
|
260
|
-
```js
|
|
261
|
-
logger.info('First time using morph-spec? Run `morph-spec tutorial` to get started.');
|
|
262
|
-
```
|
|
263
|
-
|
|
264
|
-
**Step 2: Verify the change**
|
|
265
|
-
|
|
266
|
-
```bash
|
|
267
|
-
grep -n "tutorial" src/commands/project/init.js
|
|
268
|
-
```
|
|
269
|
-
Expected: line 518 shows `morph-spec tutorial`
|
|
270
|
-
|
|
271
|
-
**Step 3: Commit**
|
|
272
|
-
|
|
273
|
-
```bash
|
|
274
|
-
git add src/commands/project/init.js
|
|
275
|
-
git commit -m "fix(init): restore morph-spec tutorial suggestion after command now exists"
|
|
276
|
-
```
|
|
277
|
-
|
|
278
|
-
---
|
|
279
|
-
|
|
280
|
-
### Task 4: Run full test suite
|
|
281
|
-
|
|
282
|
-
**Step 1: Run all tests**
|
|
283
|
-
|
|
284
|
-
```bash
|
|
285
|
-
node --test
|
|
286
|
-
```
|
|
287
|
-
Expected: all existing tests pass + 3 new tutorial tests pass, 0 failures.
|
|
288
|
-
|
|
289
|
-
**Step 2: If any failures, fix before continuing**
|
|
290
|
-
|
|
291
|
-
Do not proceed until `node --test` shows 0 failures.
|
|
292
|
-
|
|
293
|
-
**Step 3: Commit if any fixes were needed**
|
|
294
|
-
|
|
295
|
-
```bash
|
|
296
|
-
git add -p
|
|
297
|
-
git commit -m "fix(tutorial): address test suite issues"
|
|
298
|
-
```
|
package/scripts/bump-version.js
DELETED
|
@@ -1,248 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Version Bump Script for MORPH-SPEC Framework
|
|
5
|
-
*
|
|
6
|
-
* Updates the version number across all files that contain it.
|
|
7
|
-
* Single source of truth: package.json
|
|
8
|
-
*
|
|
9
|
-
* Usage:
|
|
10
|
-
* node scripts/bump-version.js patch # 4.8.0 → 4.8.1
|
|
11
|
-
* node scripts/bump-version.js minor # 4.8.0 → 4.9.0
|
|
12
|
-
* node scripts/bump-version.js major # 4.8.0 → 5.0.0
|
|
13
|
-
* node scripts/bump-version.js 4.9.0 # explicit version
|
|
14
|
-
* node scripts/bump-version.js --dry-run patch # preview changes
|
|
15
|
-
*
|
|
16
|
-
* npm script:
|
|
17
|
-
* npm run version:bump -- patch
|
|
18
|
-
* npm run version:bump -- --dry-run minor
|
|
19
|
-
*/
|
|
20
|
-
|
|
21
|
-
import { readFileSync, writeFileSync, existsSync } from 'fs';
|
|
22
|
-
import { join, dirname } from 'path';
|
|
23
|
-
import { fileURLToPath } from 'url';
|
|
24
|
-
import { glob } from 'glob';
|
|
25
|
-
|
|
26
|
-
// ── Exported helpers (used by tests) ─────────────────────────────────
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* Compute a new version from a current version and bump type.
|
|
30
|
-
* @param {string} current - Current semver (e.g. "4.8.0")
|
|
31
|
-
* @param {string} type - "patch", "minor", "major", or explicit "X.Y.Z"
|
|
32
|
-
* @returns {string} New version string
|
|
33
|
-
*/
|
|
34
|
-
export function bumpVersion(current, type) {
|
|
35
|
-
const [major, minor, patch] = current.split('.').map(Number);
|
|
36
|
-
switch (type) {
|
|
37
|
-
case 'major': return `${major + 1}.0.0`;
|
|
38
|
-
case 'minor': return `${major}.${minor + 1}.0`;
|
|
39
|
-
case 'patch': return `${major}.${minor}.${patch + 1}`;
|
|
40
|
-
default: {
|
|
41
|
-
if (/^\d+\.\d+\.\d+$/.test(type)) return type;
|
|
42
|
-
throw new Error(`Invalid version or bump type: "${type}". Expected: patch, minor, major, or X.Y.Z`);
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
function escapeRegex(str) {
|
|
48
|
-
return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* Build the list of file updates for a version bump.
|
|
53
|
-
* @param {string} rootDir - Project root directory
|
|
54
|
-
* @param {string} currentVersion - Current version string
|
|
55
|
-
* @param {string} newVersion - Target version string
|
|
56
|
-
* @returns {Array<{file: string, replacements: Array<{pattern: RegExp, replacement: string}>}>}
|
|
57
|
-
*/
|
|
58
|
-
export function buildFileUpdates(rootDir, currentVersion, newVersion) {
|
|
59
|
-
const cv = escapeRegex(currentVersion);
|
|
60
|
-
|
|
61
|
-
const updates = [
|
|
62
|
-
{
|
|
63
|
-
file: 'package.json',
|
|
64
|
-
replacements: [
|
|
65
|
-
{ pattern: new RegExp(`"version":\\s*"${cv}"`), replacement: `"version": "${newVersion}"` },
|
|
66
|
-
],
|
|
67
|
-
},
|
|
68
|
-
{
|
|
69
|
-
file: 'claude-plugin.json',
|
|
70
|
-
replacements: [
|
|
71
|
-
{ pattern: new RegExp(`"version":\\s*"${cv}"`), replacement: `"version": "${newVersion}"` },
|
|
72
|
-
],
|
|
73
|
-
},
|
|
74
|
-
{
|
|
75
|
-
file: 'README.md',
|
|
76
|
-
replacements: [
|
|
77
|
-
{ pattern: new RegExp(`\\*\\*Version:\\*\\*\\s*${cv}`), replacement: `**Version:** ${newVersion}` },
|
|
78
|
-
{ pattern: new RegExp(`morph-spec v${cv}`), replacement: `morph-spec v${newVersion}` },
|
|
79
|
-
],
|
|
80
|
-
},
|
|
81
|
-
{
|
|
82
|
-
file: 'docs/ARCHITECTURE.md',
|
|
83
|
-
replacements: [
|
|
84
|
-
{ pattern: new RegExp(`morph-spec v${cv}`, 'g'), replacement: `morph-spec v${newVersion}` },
|
|
85
|
-
],
|
|
86
|
-
},
|
|
87
|
-
{
|
|
88
|
-
file: 'docs/CHEATSHEET.md',
|
|
89
|
-
replacements: [
|
|
90
|
-
{ pattern: new RegExp(`morph-spec v${cv}`), replacement: `morph-spec v${newVersion}` },
|
|
91
|
-
],
|
|
92
|
-
},
|
|
93
|
-
{
|
|
94
|
-
file: 'docs/COMMAND-FLOWS.md',
|
|
95
|
-
replacements: [
|
|
96
|
-
{ pattern: new RegExp(`Version:\\s*${cv}`), replacement: `Version: ${newVersion}` },
|
|
97
|
-
{ pattern: new RegExp(`morph-spec v${cv}`), replacement: `morph-spec v${newVersion}` },
|
|
98
|
-
],
|
|
99
|
-
},
|
|
100
|
-
{
|
|
101
|
-
file: 'docs/QUICKSTART.md',
|
|
102
|
-
replacements: [
|
|
103
|
-
{ pattern: new RegExp(`morph-spec v${cv}`), replacement: `morph-spec v${newVersion}` },
|
|
104
|
-
],
|
|
105
|
-
},
|
|
106
|
-
{
|
|
107
|
-
file: 'framework/hooks/dev/guard-version-numbers.js',
|
|
108
|
-
replacements: [
|
|
109
|
-
{ pattern: new RegExp(`MORPH-SPEC v${cv}`), replacement: `MORPH-SPEC v${newVersion}` },
|
|
110
|
-
],
|
|
111
|
-
},
|
|
112
|
-
];
|
|
113
|
-
|
|
114
|
-
// Skill files (cliVersion in YAML frontmatter)
|
|
115
|
-
const skillFiles = glob.sync('framework/skills/level-1-workflows/*/SKILL.md', { cwd: rootDir });
|
|
116
|
-
for (const sf of skillFiles) {
|
|
117
|
-
updates.push({
|
|
118
|
-
file: sf,
|
|
119
|
-
replacements: [
|
|
120
|
-
{ pattern: new RegExp(`cliVersion:\\s*"${cv}"`), replacement: `cliVersion: "${newVersion}"` },
|
|
121
|
-
],
|
|
122
|
-
});
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
return updates;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
/**
|
|
129
|
-
* Apply version updates to files on disk.
|
|
130
|
-
* @param {string} rootDir - Project root directory
|
|
131
|
-
* @param {Array} fileUpdates - From buildFileUpdates()
|
|
132
|
-
* @param {{ dryRun?: boolean }} options
|
|
133
|
-
* @returns {{ updatedCount: number, replacementCount: number, warnings: string[] }}
|
|
134
|
-
*/
|
|
135
|
-
export function applyUpdates(rootDir, fileUpdates, { dryRun = false } = {}) {
|
|
136
|
-
let updatedCount = 0;
|
|
137
|
-
let replacementCount = 0;
|
|
138
|
-
const warnings = [];
|
|
139
|
-
|
|
140
|
-
for (const { file, replacements } of fileUpdates) {
|
|
141
|
-
const fullPath = join(rootDir, file);
|
|
142
|
-
|
|
143
|
-
if (!existsSync(fullPath)) {
|
|
144
|
-
warnings.push(`SKIP ${file} (file not found)`);
|
|
145
|
-
continue;
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
let content = readFileSync(fullPath, 'utf-8');
|
|
149
|
-
let fileChanged = false;
|
|
150
|
-
|
|
151
|
-
for (const { pattern, replacement } of replacements) {
|
|
152
|
-
if (pattern.test(content)) {
|
|
153
|
-
content = content.replace(pattern, replacement);
|
|
154
|
-
fileChanged = true;
|
|
155
|
-
replacementCount++;
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
if (fileChanged) {
|
|
160
|
-
if (!dryRun) {
|
|
161
|
-
writeFileSync(fullPath, content, 'utf-8');
|
|
162
|
-
}
|
|
163
|
-
updatedCount++;
|
|
164
|
-
} else {
|
|
165
|
-
warnings.push(`${file} (no matches — pattern may have changed)`);
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
return { updatedCount, replacementCount, warnings };
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
// ── CLI entry point ──────────────────────────────────────────────────
|
|
173
|
-
|
|
174
|
-
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
175
|
-
const isMainModule = process.argv[1]?.replace(/\\/g, '/').endsWith('scripts/bump-version.js');
|
|
176
|
-
|
|
177
|
-
if (isMainModule) {
|
|
178
|
-
const ROOT = join(__dirname, '..');
|
|
179
|
-
const args = process.argv.slice(2);
|
|
180
|
-
const dryRun = args.includes('--dry-run');
|
|
181
|
-
const filtered = args.filter(a => a !== '--dry-run');
|
|
182
|
-
|
|
183
|
-
if (filtered.length === 0) {
|
|
184
|
-
console.error(
|
|
185
|
-
'Usage: node scripts/bump-version.js [--dry-run] <patch|minor|major|X.Y.Z>'
|
|
186
|
-
);
|
|
187
|
-
process.exit(1);
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
const input = filtered[0];
|
|
191
|
-
const pkg = JSON.parse(readFileSync(join(ROOT, 'package.json'), 'utf-8'));
|
|
192
|
-
const currentVersion = pkg.version;
|
|
193
|
-
|
|
194
|
-
let newVersion;
|
|
195
|
-
try {
|
|
196
|
-
newVersion = bumpVersion(currentVersion, input);
|
|
197
|
-
} catch (err) {
|
|
198
|
-
console.error(err.message);
|
|
199
|
-
process.exit(1);
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
if (newVersion === currentVersion) {
|
|
203
|
-
console.log(`Version is already ${currentVersion}. Nothing to do.`);
|
|
204
|
-
process.exit(0);
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
console.log(`\nBumping version: ${currentVersion} → ${newVersion}`);
|
|
208
|
-
if (dryRun) console.log('(dry run — no files will be modified)\n');
|
|
209
|
-
else console.log('');
|
|
210
|
-
|
|
211
|
-
const fileUpdates = buildFileUpdates(ROOT, currentVersion, newVersion);
|
|
212
|
-
const result = applyUpdates(ROOT, fileUpdates, { dryRun });
|
|
213
|
-
|
|
214
|
-
for (const file of fileUpdates) {
|
|
215
|
-
const fullPath = join(ROOT, file.file);
|
|
216
|
-
if (existsSync(fullPath)) {
|
|
217
|
-
const content = readFileSync(fullPath, 'utf-8');
|
|
218
|
-
const hasMatch = file.replacements.some(r =>
|
|
219
|
-
dryRun
|
|
220
|
-
? new RegExp(escapeRegex(currentVersion)).test(content)
|
|
221
|
-
: new RegExp(escapeRegex(newVersion)).test(content)
|
|
222
|
-
);
|
|
223
|
-
if (hasMatch) console.log(` ✓ ${file.file}`);
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
console.log('');
|
|
228
|
-
if (result.warnings.length > 0) {
|
|
229
|
-
console.log('Warnings:');
|
|
230
|
-
for (const w of result.warnings) console.log(` ⚠ ${w}`);
|
|
231
|
-
console.log('');
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
console.log(
|
|
235
|
-
`${dryRun ? 'Would update' : 'Updated'} ${result.updatedCount} files (${result.replacementCount} replacements).`
|
|
236
|
-
);
|
|
237
|
-
|
|
238
|
-
if (dryRun) {
|
|
239
|
-
console.log('\nRe-run without --dry-run to apply changes.');
|
|
240
|
-
} else {
|
|
241
|
-
console.log(`\nVersion is now ${newVersion}.`);
|
|
242
|
-
console.log('Next steps:');
|
|
243
|
-
console.log(' 1. Review changes: git diff');
|
|
244
|
-
console.log(` 2. Commit: git commit -am "chore: bump version to ${newVersion}"`);
|
|
245
|
-
console.log(` 3. Tag: git tag v${newVersion}`);
|
|
246
|
-
console.log(' 4. Publish: npm publish');
|
|
247
|
-
}
|
|
248
|
-
}
|