@khanhspring/forge-module 1.0.1 → 1.2.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@khanhspring/forge-module",
3
- "version": "1.0.1",
3
+ "version": "1.2.0",
4
4
  "description": "Forge module repo skills for Claude Code — implement, contract-test, done, and more",
5
5
  "type": "module",
6
6
  "bin": {
@@ -15,11 +15,15 @@ disable-model-invocation: true
15
15
  Run or analyze Specmatic contract tests for this module.
16
16
 
17
17
  ## Pre-check
18
- - Read `.forge/module.json` for `contract_glob` and `test_base_url`.
18
+ - Read `.forge/module.json`.
19
19
  If missing, say "Run forge-init to set up this module repo first."
20
+ - Determine scope:
21
+ - Has no `submodules` key → single module; use top-level `contract_glob` and `test_base_url`.
22
+ - Has `submodules[]` → module with submodules; show one command block per submodule.
20
23
 
21
- ## If $ARGUMENTS is empty — Output the test command
24
+ ## If $ARGUMENTS is empty — Output the test command(s)
22
25
 
26
+ **Single-module repo:**
23
27
  ```
24
28
  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
25
29
  Contract Tests: {module}
@@ -36,6 +40,24 @@ Contracts under test:
36
40
  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
37
41
  ```
38
42
 
43
+ **Module with submodules (one block per submodule):**
44
+ ```
45
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
46
+ Contract Tests: {submodule.name}
47
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
48
+
49
+ Make sure {submodule.name} is running at {submodule.test_base_url}, then run:
50
+
51
+ specmatic test \
52
+ --contract "{submodule.contract_glob}" \
53
+ --testBaseURL {submodule.test_base_url}
54
+
55
+ Contracts under test:
56
+ {list files matching submodule.contract_glob}
57
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
58
+ ```
59
+ _(Repeat for each entry in `submodules[]`.)_
60
+
39
61
  ## If $ARGUMENTS contains test output — Analyze failures
40
62
 
41
63
  For each failure, provide:
@@ -31,7 +31,10 @@ is handled separately by `/forge-close` in the spec repo.
31
31
  ## Step 1 — Read task list (read-only from submodule)
32
32
 
33
33
  Read `{spec_submodule_path}/features/{slug}/tasks.md`.
34
- Find the `### {module}` section. Extract all tasks with their checkbox state.
34
+
35
+ - If `module.json` has no `submodules` key: find the `### {module}` section.
36
+ - If `module.json` has `submodules[]`: find the `### {submodule.name}` section for each entry;
37
+ show them grouped by submodule name in Step 2.
35
38
 
36
39
  If `tasks.md` is missing:
37
40
  > "No tasks file found at `specs/features/{slug}/tasks.md`.
@@ -65,6 +68,7 @@ Wait for confirmation before continuing.
65
68
 
66
69
  ## Step 3 — Generate commit message
67
70
 
71
+ **Module with no submodules:**
68
72
  ```
69
73
  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
70
74
  Commit message
@@ -73,15 +77,33 @@ feat({module}): implement {feature-slug}
73
77
 
74
78
  - TASK-1: {title}
75
79
  - TASK-2: {title}
76
- - TASK-3: {title}
77
80
 
78
81
  Spec: specs/features/{slug}/spec.md
79
- Tasks: specs/features/{slug}/tasks.md
80
82
  Contract: specs/contracts/{module}/{slug}.yaml
81
83
  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
82
84
  ```
83
85
 
84
86
  Then remind:
85
87
  > "Once committed and pushed, go to the spec repo and run:
86
- > `/forge-close {slug} {module}`
87
- > to mark your tasks as done and check if the feature is fully complete."
88
+ > `/forge-close {slug} {module}`"
89
+
90
+ **Module with submodules (tasks grouped by submodule):**
91
+ ```
92
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
93
+ Commit message
94
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
95
+ feat(webapps): implement {feature-slug}
96
+
97
+ [admin]
98
+ - TASK-3: {title}
99
+ [portal]
100
+ - TASK-4: {title}
101
+
102
+ Spec: specs/features/{slug}/spec.md
103
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
104
+ ```
105
+
106
+ Then remind — one `/forge-close` per submodule, using the **submodule name directly**
107
+ (no parent prefix — same command you'd use if it were a standalone module):
108
+ > "/forge-close {slug} admin
109
+ > /forge-close {slug} portal"
@@ -25,10 +25,15 @@ presented the Implementation Plan and the user has explicitly confirmed it.
25
25
  ## Pre-check
26
26
 
27
27
  - Read `.forge/module.json` — if missing, say "Run `/forge-init` to set up this module repo first."
28
- - Get `module`, `spec_submodule_path`, `test_base_url`, `contract_glob` from module.json.
28
+ - Get `spec_submodule_path` from module.json.
29
+ - **Determine working scope:**
30
+ - `module.json` has no `submodules` → scope = the module itself; use top-level `test_base_url` and `contract_glob`.
31
+ - `module.json` has `submodules[]` → ask "Which submodule are you implementing? ({list submodule names})"
32
+ then use that submodule's `path`, `test_base_url`, and `contract_glob`.
29
33
  - Feature slug from $ARGUMENTS.
30
- - If empty, scan `{spec_submodule_path}/features/*/tasks.md` for `### {module}` sections,
31
- list features with pending tasks, ask which to implement.
34
+ - If empty, scan `{spec_submodule_path}/features/*/tasks.md` for `### {scope-name}` headings
35
+ (module name for simple modules, submodule name for submodules), list features with pending
36
+ tasks, ask which to implement.
32
37
  - Check `git submodule status` — if specs/ is out of date, say:
33
38
  > "Your specs submodule may be out of date. Run `git submodule update --remote specs` first, or continue with the current version?"
34
39
 
@@ -42,25 +47,29 @@ Load the feature documents silently:
42
47
 
43
48
  If `tasks.md` is missing → stop: "No tasks found. Run `/forge-tasks {slug}` in the spec repo first."
44
49
 
45
- **Determine which contract(s) apply to this module:**
46
- - Look for an own-module contract: `{spec_submodule_path}/contracts/{module}/{slug}.yaml`.
47
- - **If it exists** → this module *provides* the API. Use it as the source of truth for the
48
- endpoints you implement.
49
- - **If it does NOT exist** (typical for frontend/consumer modules) scan
50
+ **Determine which contract(s) apply to this scope:**
51
+ - The contract path is always `{spec_submodule_path}/contracts/{scope-name}/{slug}.yaml` where
52
+ `{scope-name}` is the module name (for simple modules) or the submodule name (for submodules).
53
+ Submodules use their own name — no parent prefix — so the path is identical to a standalone module.
54
+ - **If the contract exists** this scope *provides* the API. Use it as the source of truth.
55
+ - **If it does NOT exist** (typical for frontend/consumer scopes) → scan
50
56
  `{spec_submodule_path}/contracts/*/{slug}.yaml` for sibling contracts. These are the APIs
51
- this module *consumes*. Report:
52
- > "No contract for `{module}` — this module consumes APIs from: {list backend modules}.
57
+ this scope *consumes*. Report:
58
+ > "No contract for `{scope-name}` — consuming APIs from: {list providers}.
53
59
  > I'll use their contracts as the integration source of truth."
54
60
  - If no contract exists anywhere for this feature → note it and continue with spec + tasks only.
55
61
 
56
62
  Then research the existing codebase — do this before asking any questions:
57
63
 
58
- 1. **Read `CLAUDE.md`** note architectural principles, conventions, and forbidden patterns.
59
- 2. **Find similar existing endpoints** look for controllers/routes with similar patterns to what the contract defines. Note how they're structured.
60
- 3. **Find existing service/repository patterns** how are services and data access layers organized in this codebase?
61
- 4. **Find existing test patterns** how are unit and integration tests structured? What test utilities exist?
62
- 5. **Find existing error handling** — how does this service return errors? Does it match the `ApiError` schema (`shared/api-error.yaml`) in the contract — `code`, `message`, `traceId`, `details[]`?
63
- 6. **Find existing auth/middleware** — how is authentication enforced on existing endpoints?
64
+ 1. **Read `{spec_submodule_path}/CONTEXT.md`** (if present) project-wide domain context:
65
+ actors, domain glossary, principles, and forbidden patterns. Use glossary terms verbatim
66
+ when naming thingsnever invent synonyms for existing domain terms.
67
+ 2. **Read `CLAUDE.md`** this repo's architectural principles, conventions, and forbidden patterns.
68
+ 3. **Find similar existing endpoints** — look for controllers/routes with similar patterns to what the contract defines. Note how they're structured.
69
+ 4. **Find existing service/repository patterns** — how are services and data access layers organized in this codebase?
70
+ 5. **Find existing test patterns** — how are unit and integration tests structured? What test utilities exist?
71
+ 6. **Find existing error handling** — how does this service return errors? Does it match the `ApiError` schema (`shared/api-error.yaml`) in the contract — `code`, `message`, `traceId`, `details[]`?
72
+ 7. **Find existing auth/middleware** — how is authentication enforced on existing endpoints?
64
73
 
65
74
  Report findings before asking questions:
66
75
 
@@ -178,7 +187,7 @@ Wait for the answer before continuing.
178
187
  - When **consuming** an API (frontend/client): send requests and parse responses exactly as the
179
188
  consumed contract defines — treat its shapes as fixed, code defensively against its error responses
180
189
  - If the contract looks wrong: "This looks like a contract issue — raise a PR in the spec repo"
181
- - Only implement tasks under `### {module}` in tasks.md — flag any cross-module work
190
+ - Only implement tasks under `### {scope-name}` in tasks.md (module name or submodule name) — flag any cross-scope work
182
191
  - Follow existing codebase patterns found in Step 1 — consistency over personal preference
183
192
  - No code before the user confirms the plan in Step 4
184
193
 
@@ -19,9 +19,9 @@ disable-model-invocation: false
19
19
 
20
20
  # Forge Init — Module Repo
21
21
 
22
- Initialize this repo as a Forge module repo (one module backend or frontend).
23
- Research what's already in the codebase first, then ask one question at a time —
24
- pre-filling anything already detectable. Write all files in one go after confirmation.
22
+ Initialize this repo as a Forge module repo. It may contain one module (a dedicated service or
23
+ app) or multiple modules sharing the same repo to reduce overhead (a monorepo). Either way,
24
+ research first, then ask one question at a time before writing anything.
25
25
 
26
26
  ## Pre-check
27
27
  If `.forge/module.json` already exists → say "Module repo already initialized." and stop.
@@ -34,9 +34,13 @@ Before asking anything, silently scan the repo and note:
34
34
 
35
35
  1. **Stack detection** — does `pom.xml`, `build.gradle`, `package.json`, `go.mod`, `requirements.txt`, or similar exist? What versions are declared?
36
36
  2. **Port detection** — is a port configured in `application.yml`, `application.properties`, `.env`, `.env.example`, or `docker-compose.yml`?
37
- 3. **Submodule** — does `specs/` already exist as a directory or submodule?
37
+ 3. **Specs submodule** — does `specs/` already exist as a directory or submodule?
38
38
  4. **CI** — does `.github/workflows/` already exist? Any contract test workflow present?
39
39
  5. **Existing CLAUDE.md** — already has project context written?
40
+ 6. **Monorepo signals** — does `nx.json`, `turbo.json`, `lerna.json`, `pnpm-workspace.yaml`, or
41
+ a `workspaces` key in `package.json` exist? Is there an `apps/` or `packages/` directory with
42
+ multiple sub-directories each containing their own `package.json`, `pom.xml`, etc.?
43
+ If yes, list the detected sub-apps (directory names).
40
44
 
41
45
  Report findings before asking anything:
42
46
 
@@ -45,9 +49,38 @@ Report findings before asking anything:
45
49
  > - Port: {detected port — or 'not found in config'}
46
50
  > - specs/ submodule: {exists / not present}
47
51
  > - GitHub Actions: {exists / not present}
52
+ > {if monorepo signals}: - Looks like a monorepo — detected sub-apps: {list of dir names}
48
53
  >
49
54
  > I'll use these as defaults — just confirm or correct as we go."
50
55
 
56
+ **If monorepo/multi-module signals were found**, ask immediately after the report:
57
+ > "This looks like a module with submodules ({names}).
58
+ > Should I configure them as submodules so each gets its own tasks and contracts? (yes / no)"
59
+
60
+ If yes → collect submodule details in Step 2-B before the main questions.
61
+ If no → proceed as a single module.
62
+
63
+ ---
64
+
65
+ ## Step 2-B — Submodule collection (only when confirmed)
66
+
67
+ Submodules share the parent module's repo — they are not separate repos.
68
+ Collect them one at a time. Pre-fill from what was detected and ask the user to confirm or correct:
69
+
70
+ - "Submodule name? _(Must match a `name` under this module's `submodules` in the spec repo's `project.json`; default: `{detected-dir-name}`)_"
71
+ - "Type? (backend / frontend / worker)"
72
+ - "Stack? (detected: {stack-if-found} — or enter manually)"
73
+ - "Port? _(each submodule has its own port — detected: {port-if-found})_"
74
+ - "Path within this repo? (default: `{detected-path}`)"
75
+
76
+ Confirm: "`{sub}` — {type} — {stack} — :{port} — {path}. Another submodule? (yes / no)"
77
+
78
+ **Rules enforced here:**
79
+ - Submodules have no `repo` field — they are in the same repo as the parent module
80
+ - Port is collected per submodule; the parent module has no `port`
81
+
82
+ After collecting all submodules, continue to Step 2 (Q4 port will be skipped automatically).
83
+
51
84
  ---
52
85
 
53
86
  ## Step 2 — Questions (one at a time)
@@ -56,7 +89,7 @@ Ask one question per message. Wait for the answer before asking the next.
56
89
  Where research already gives a confident answer, present it as a default to confirm
57
90
  rather than asking from scratch.
58
91
 
59
- **Q1 — Module name**
92
+ **Q1 — Module name** _(skip if Step 2-B was run)_
60
93
  > "What's the module name for this repo?
61
94
  > _(Must exactly match a `name` entry in the spec repo's `.forge/project.json`)_"
62
95
 
@@ -75,7 +108,7 @@ If `specs/` already exists:
75
108
  If not:
76
109
  > "What's the spec repo URL? (it will be added as a git submodule at `specs/`)"
77
110
 
78
- **Q4 — Port**
111
+ **Q4 — Port** _(skip entirely if Step 2-B was run — port belongs to each submodule, not the module)_
79
112
 
80
113
  If port was detected in Step 1:
81
114
  > "I found port `{port}` in your config — is that the right local dev port? (yes / enter different port)"
@@ -129,9 +162,14 @@ Ready to initialize. Here's what I'll do:
129
162
  git submodule update --init --recursive
130
163
 
131
164
  .forge/module.json
132
- module: {module-name}
133
- test_base_url: http://localhost:{port}
134
- contract_glob: specs/contracts/{module-name}/*.yaml
165
+ {if single app}
166
+ module: {module-name}
167
+ test_base_url: http://localhost:{port}
168
+ contract_glob: specs/contracts/{module-name}/*.yaml
169
+ {if submodules}
170
+ submodules:
171
+ {sub-name} path:{path} :{port} contract: specs/contracts/{sub-name}/*.yaml
172
+ ...
135
173
 
136
174
  CLAUDE.md
137
175
  Module: {module-name}
@@ -161,6 +199,8 @@ git submodule update --init --recursive
161
199
  ```
162
200
 
163
201
  Write `.forge/module.json`:
202
+
203
+ **For a single-app module** (no sub-apps):
164
204
  ```json
165
205
  {
166
206
  "module": "{module-name}",
@@ -171,17 +211,42 @@ Write `.forge/module.json`:
171
211
  }
172
212
  ```
173
213
 
214
+ **Module with submodules** — no top-level `port` or `test_base_url`; each submodule owns those.
215
+ Submodules have no `repo` field — they are in the same repo as the parent module:
216
+ ```json
217
+ {
218
+ "module": "{module-name}",
219
+ "spec_submodule_path": "specs",
220
+ "specmatic_version": "2.x",
221
+ "submodules": [
222
+ {
223
+ "name": "{sub-name}",
224
+ "path": "{relative-path}",
225
+ "test_base_url": "http://localhost:{port}",
226
+ "contract_glob": "specs/contracts/{sub-name}/*.yaml"
227
+ }
228
+ ]
229
+ }
230
+ ```
231
+
174
232
  Write `CLAUDE.md`:
175
233
  ```markdown
176
234
  # {module-name}
177
235
 
178
236
  {description}
179
237
 
180
- **Stack:** {stack}
181
- **Port:** {port}
238
+ _(Single app: show Stack + Port inline. Monorepo: replace with a table.)_
239
+
240
+ **Stack:** {stack} **Port:** {port}
241
+
242
+ — or for a monorepo —
243
+
244
+ | Module | Stack | Port | Path |
245
+ |--------|-------|------|------|
246
+ | {name} | {stack} | {port} | {path} |
182
247
 
183
248
  ## Forge Workflow
184
- 1. `/forge-tasks` — see all pending tasks for this module
249
+ 1. `/forge-tasks` — see all pending tasks for this repo's module(s)
185
250
  2. `/forge-implement` — implement a feature task by task
186
251
  3. `/forge-contract-test` — run Specmatic contract tests
187
252
  4. `/forge-done` — confirm tasks done + generate commit message
@@ -191,6 +256,8 @@ Write `CLAUDE.md`:
191
256
  Linked via git submodule at `specs/`.
192
257
  Run `git submodule update --remote specs` before starting a new feature,
193
258
  and again after `/forge-close` is run in the spec repo to sync task status.
259
+ Project-wide domain context (actors, glossary, principles) lives at `specs/CONTEXT.md`
260
+ — read it before implementing.
194
261
 
195
262
  ## Principles
196
263
  {list each as a bullet — or "None defined yet."}
@@ -237,4 +304,8 @@ Run `git status` and confirm:
237
304
  - Never overwrite existing files
238
305
  - All written files must be complete — no unfilled placeholders
239
306
  - `module` in module.json must exactly match the name in the spec repo's project.json
307
+ - **Extract, don't re-ask**: if the user's answer contains information for upcoming fields
308
+ (e.g. "it's a NestJS app on port 3000 at apps/auth-ui"), extract and fill those fields
309
+ silently — only ask about what is genuinely missing. Never ask a question the user has
310
+ already answered, even indirectly.
240
311
  - If the user provides multiple answers in one message, accept them gracefully and move forward
@@ -22,7 +22,13 @@ Show all tasks assigned to this module across all features.
22
22
 
23
23
  ## Steps
24
24
 
25
- Scan `{spec_submodule_path}/features/*/tasks.md` find `### {module}` sections extract tasks
25
+ Determine the set of task headings to look for:
26
+ - `module.json` has no `submodules` → look for `### {module}` (one heading).
27
+ - `module.json` has `submodules[]` → look for `### {submodule.name}` for each entry.
28
+ Submodule task headings use the submodule's own name — no parent prefix — so they are
29
+ identical to standalone module headings and can be promoted without touching tasks.md.
30
+
31
+ Scan `{spec_submodule_path}/features/*/tasks.md` → find all matching headings → extract tasks
26
32
  with their checkbox state and the parent feature's `Status`.
27
33
 
28
34
  ```
@@ -42,6 +48,22 @@ Feature: user-profile [Open]
42
48
  2 features · 4 remaining · 1 done
43
49
  ```
44
50
 
51
+ For a module with submodules, group tasks by submodule under each feature:
52
+ ```
53
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
54
+ Tasks for: webapps (submodules: admin · landing · portal)
55
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
56
+
57
+ Feature: user-registration [Open]
58
+ [admin]
59
+ - [ ] TASK-3 [ui] Registration form
60
+ [portal]
61
+ - [ ] TASK-4 [ui] Registration confirmation page
62
+
63
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
64
+ 1 feature · 2 remaining · 0 done
65
+ ```
66
+
45
67
  If no tasks reference this module across any feature:
46
68
  > "No tasks found for `{module}`. Either no feature targets this module yet, or the specs
47
69
  > submodule is out of date — try `git submodule update --remote specs`."