@khanhspring/forge-module 1.3.0 → 1.4.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.3.0",
3
+ "version": "1.4.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,17 @@ 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`.
18
+ - Read `.forge/module.json` for `module` and `spec_submodule_path`.
19
19
  If missing, say "Run forge-init to set up this module repo first."
20
+ - Read `{spec_submodule_path}/.forge/project.json` and find the `modules[]` entry where
21
+ `name == module` — port and submodule structure live there, not in module.json.
20
22
  - 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.
23
+ - Entry has no `submodules[]` → single module; derive `contract_glob =
24
+ {spec_submodule_path}/contracts/{module}/*.yaml` and `test_base_url =
25
+ http://localhost:{entry.port}`.
26
+ - Entry has `submodules[]` → module with submodules; show one command block per submodule,
27
+ deriving each one's `contract_glob = {spec_submodule_path}/contracts/{submodule.name}/*.yaml`
28
+ and `test_base_url = http://localhost:{submodule.port}`.
23
29
 
24
30
  ## If $ARGUMENTS is empty — Output the test command(s)
25
31
 
@@ -32,9 +32,11 @@ is handled separately by `/forge-close` in the spec repo.
32
32
 
33
33
  Read `{spec_submodule_path}/features/{slug}/tasks.md`.
34
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
+ Read `{spec_submodule_path}/.forge/project.json` and find the `modules[]` entry where
36
+ `name == module` submodule structure lives there, not in module.json:
37
+ - Entry has no `submodules[]`: find the `### {module}` section.
38
+ - Entry has `submodules[]`: find the `### {submodule.name}` section for each entry; show them
39
+ grouped by submodule name in Step 2.
38
40
 
39
41
  If `tasks.md` is missing:
40
42
  > "No tasks file found at `specs/features/{slug}/tasks.md`.
@@ -25,11 +25,17 @@ 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 `spec_submodule_path` and `spec_link_type` 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`.
28
+ - Get `module`, `spec_submodule_path`, and `spec_link_type` from module.json.
29
+ - **Determine working scope** from the spec repo, not module.json:
30
+ - Read `{spec_submodule_path}/.forge/project.json` and find the `modules[]` entry where
31
+ `name == module`. If not found, warn: "`{module}` isn't registered in the spec repo's
32
+ `project.json` check the name matches, or add it there via `/forge-config`."
33
+ - Entry has no `submodules[]` → scope = the module itself; `test_base_url =
34
+ http://localhost:{entry.port}`, `contract_glob = {spec_submodule_path}/contracts/{module}/*.yaml`.
35
+ - Entry has `submodules[]` → ask "Which submodule are you implementing? ({list submodule names
36
+ from the entry})", then use that submodule's `path`, and derive `test_base_url =
37
+ http://localhost:{submodule.port}`, `contract_glob =
38
+ {spec_submodule_path}/contracts/{submodule.name}/*.yaml`.
33
39
  - Feature slug from $ARGUMENTS.
34
40
  - If empty, scan `{spec_submodule_path}/features/*/tasks.md` for `### {scope-name}` headings
35
41
  (module name for simple modules, submodule name for submodules), list features with pending
@@ -55,33 +55,14 @@ Report findings before asking anything:
55
55
  >
56
56
  > I'll use these as defaults — just confirm or correct as we go."
57
57
 
58
- **If monorepo/multi-module signals were found**, ask immediately after the report:
59
- > "This looks like a module with submodules ({names}).
60
- > Should I configure them as submodules so each gets its own tasks and contracts? (yes / no)"
58
+ **If monorepo/multi-module signals were found**, note it in passing — no question needed:
59
+ > "This looks like a module with submodules ({names}). Submodule structure (ports, stack,
60
+ > paths) lives in the spec repo's `project.json`, not here I'll cross-check against that
61
+ > once `specs/` is linked."
61
62
 
62
- If yes collect submodule details in Step 2-B before the main questions.
63
- If no proceed as a single module.
64
-
65
- ---
66
-
67
- ## Step 2-B — Submodule collection (only when confirmed)
68
-
69
- Submodules share the parent module's repo — they are not separate repos.
70
- Collect them one at a time. Pre-fill from what was detected and ask the user to confirm or correct:
71
-
72
- - "Submodule name? _(Must match a `name` under this module's `submodules` in the spec repo's `project.json`; default: `{detected-dir-name}`)_"
73
- - "Type? (backend / frontend / worker)"
74
- - "Stack? (detected: {stack-if-found} — or enter manually)"
75
- - "Port? _(each submodule has its own port — detected: {port-if-found})_"
76
- - "Path within this repo? (default: `{detected-path}`)"
77
-
78
- Confirm: "`{sub}` — {type} — {stack} — :{port} — {path}. Another submodule? (yes / no)"
79
-
80
- **Rules enforced here:**
81
- - Submodules have no `repo` field — they are in the same repo as the parent module
82
- - Port is collected per submodule; the parent module has no `port`
83
-
84
- After collecting all submodules, continue to Step 2 (Q4 port will be skipped automatically).
63
+ Submodule structure is never collected or stored in this repo's `.forge/module.json`. It's
64
+ configured once in the spec repo via `/forge-config` there, and every module skill reads it
65
+ from `specs/.forge/project.json` at runtime. This repo only needs to know its own module name.
85
66
 
86
67
  ---
87
68
 
@@ -91,13 +72,22 @@ Ask one question per message. Wait for the answer before asking the next.
91
72
  Where research already gives a confident answer, present it as a default to confirm
92
73
  rather than asking from scratch.
93
74
 
94
- **Q1 — Module name** _(skip if Step 2-B was run)_
75
+ **Q1 — Module name**
95
76
  > "What's the module name for this repo?
96
77
  > _(Must exactly match a `name` entry in the spec repo's `.forge/project.json`)_"
97
78
 
98
79
  Do not suggest a default — module names must be exact matches. Warn clearly:
99
80
  > "This name must match exactly. A mismatch will break `/forge-implement` and `/forge-done`."
100
81
 
82
+ If `specs/` was already detected in Step 1 (already linked), read
83
+ `{specs-path}/.forge/project.json` now and find the `modules[]` entry where `name` matches
84
+ this answer. If found, silently keep its `port`, `stack`, `type`, and `submodules[]` (if any)
85
+ for use in the Step 3 preview and the CLAUDE.md content written in Step 4 — do not re-ask for
86
+ any of it. If not found, note it and continue: "Heads up — `{module}` isn't in the spec repo's
87
+ `project.json` yet. Add it there with `/forge-config`, or continue and I'll leave stack/port
88
+ as TBD in CLAUDE.md for now." If `specs/` isn't linked yet, this lookup happens later, in
89
+ Step 4, right after the link is created.
90
+
101
91
  **Q2 — Module description**
102
92
  > "What does this module do? (one sentence)"
103
93
 
@@ -126,38 +116,26 @@ If `specs/` does not exist, ask how to link it:
126
116
  > or `/home/me/my-specs`)"
127
117
  Set `spec_link_type: "junction"` and `spec_source_path` to the given absolute path.
128
118
 
129
- **Q4 Port** _(skip entirely if Step 2-B was runport belongs to each submodule, not the module)_
130
-
131
- If port was detected in Step 1:
132
- > "I found port `{port}` in your config — is that the right local dev port? (yes / enter different port)"
133
-
134
- If not detected:
135
- > "What port does this module run on locally?"
119
+ Note: port, stack, and submodule structure are never asked herethey come from the spec
120
+ repo's `project.json` (looked up in Q1 if `specs/` already existed, or in Step 4 right after
121
+ linking otherwise). `.forge/module.json` doesn't store any of it either way.
136
122
 
137
- **Q5Tech stack**
138
-
139
- If stack was detected in Step 1:
140
- > "Looks like this is a `{detected stack}` module — is that right? Anything to add?"
141
-
142
- If not detected:
143
- > "What's the tech stack? (e.g. 'Spring Boot 3, Java 21' or 'React, TypeScript')"
144
-
145
- **Q6 — Principles**
123
+ **Q4Principles**
146
124
  > "What are the key architectural principles for this module?
147
125
  > _(e.g. 'stateless', 'no business logic in controllers', 'repository pattern for DB access')
148
126
  > Say 'none yet' to skip._"
149
127
 
150
- **Q7 — Conventions**
128
+ **Q5 — Conventions**
151
129
  > "Any coding conventions the team follows in this repo?
152
130
  > _(e.g. 'constructor injection only', 'all public methods must have unit tests', 'no magic strings')
153
131
  > Say 'none yet' to skip._"
154
132
 
155
- **Q8 — Never**
133
+ **Q6 — Never**
156
134
  > "Anything developers should never do in this codebase?
157
135
  > _(e.g. 'no direct DB calls from the API layer', 'never change a contract to fix a failing test')
158
136
  > Say 'none yet' to skip._"
159
137
 
160
- **Q9 — GitHub Actions CI**
138
+ **Q7 — GitHub Actions CI**
161
139
 
162
140
  If `.github/workflows/` already exists with a contract test:
163
141
  > "I see a CI workflow already exists — skip adding another? (yes to skip)"
@@ -185,20 +163,15 @@ Ready to initialize. Here's what I'll do:
185
163
  append `specs/` to .gitignore (junction contents aren't tracked by this repo)
186
164
 
187
165
  .forge/module.json
188
- {if single app}
189
- module: {module-name}
190
- spec_link_type: {submodule | junction}
191
- test_base_url: http://localhost:{port}
192
- contract_glob: specs/contracts/{module-name}/*.yaml
193
- {if submodules}
194
- submodules:
195
- {sub-name} path:{path} :{port} contract: specs/contracts/{sub-name}/*.yaml
196
- ...
166
+ module: {module-name}
167
+ spec_submodule_path: specs
168
+ spec_link_type: {submodule | junction}
169
+ {if junction} spec_source_path: {spec-source-path}
197
170
 
198
171
  CLAUDE.md
199
172
  Module: {module-name}
200
173
  Description: {description}
201
- Stack: {stack} Port: {port}
174
+ Stack/Port: {from spec repo's project.json, or "TBD — set via /forge-config in the spec repo"}
202
175
  Principles: {list or "none yet"}
203
176
  Conventions: {list or "none yet"}
204
177
  Never: {list or "none yet"}
@@ -234,48 +207,33 @@ ln -s "{spec-source-path}" specs
234
207
  Then append `specs/` to `.gitignore` — a junction/symlink's contents belong to the spec repo,
235
208
  not this one, and must not be tracked or committed here.
236
209
 
237
- Write `.forge/module.json`:
210
+ If the Q1 lookup didn't already happen (i.e. `specs/` was just created above, not already
211
+ present in Step 1), read `{spec_submodule_path}/.forge/project.json` now and find the
212
+ `modules[]` entry where `name` matches the module name from Q1. Use its `port`, `stack`, and
213
+ `submodules[]` (if any) to fill the CLAUDE.md content below. If the module still isn't found
214
+ there, write "TBD — set via `/forge-config` in the spec repo" for the stack/port line instead.
238
215
 
239
- **For a single-app module** (no sub-apps):
216
+ Write `.forge/module.json` same shape whether or not the module has submodules in the spec
217
+ repo, since submodule structure is never duplicated here:
240
218
  ```json
241
219
  {
242
220
  "module": "{module-name}",
243
221
  "spec_submodule_path": "specs",
244
- "spec_link_type": "submodule",
245
- "specmatic_version": "2.x",
246
- "test_base_url": "http://localhost:{port}",
247
- "contract_glob": "specs/contracts/{module-name}/*.yaml"
222
+ "spec_link_type": "submodule"
248
223
  }
249
224
  ```
250
225
  `spec_link_type` is `"submodule"` (default) or `"junction"`. When `"junction"`, also write
251
226
  `"spec_source_path": "{absolute-local-path}"` so the link can be recreated if it's ever lost.
252
227
 
253
- **Module with submodules** — no top-level `port` or `test_base_url`; each submodule owns those.
254
- Submodules have no `repo` field they are in the same repo as the parent module:
255
- ```json
256
- {
257
- "module": "{module-name}",
258
- "spec_submodule_path": "specs",
259
- "spec_link_type": "submodule",
260
- "specmatic_version": "2.x",
261
- "submodules": [
262
- {
263
- "name": "{sub-name}",
264
- "path": "{relative-path}",
265
- "test_base_url": "http://localhost:{port}",
266
- "contract_glob": "specs/contracts/{sub-name}/*.yaml"
267
- }
268
- ]
269
- }
270
- ```
271
-
272
- Write `CLAUDE.md`:
228
+ Write `CLAUDE.md`. `{stack}`/`{port}`/`{path}` below come from the spec repo's `project.json`
229
+ lookup above (or "TBD" if not found there yet) never from a question asked in this skill:
273
230
  ```markdown
274
231
  # {module-name}
275
232
 
276
233
  {description}
277
234
 
278
- _(Single app: show Stack + Port inline. Monorepo: replace with a table.)_
235
+ _(Single app: show Stack + Port inline. Monorepo: replace with a table, one row per submodule
236
+ found in the spec repo's project.json.)_
279
237
 
280
238
  **Stack:** {stack} **Port:** {port}
281
239
 
@@ -350,6 +308,9 @@ Run `git status` and confirm:
350
308
  - Never overwrite existing files
351
309
  - All written files must be complete — no unfilled placeholders
352
310
  - `module` in module.json must exactly match the name in the spec repo's project.json
311
+ - `module.json` never stores `port`, `stack`, or submodule structure — those live only in the
312
+ spec repo's `project.json`; every module skill resolves them at runtime by reading
313
+ `specs/.forge/project.json`
353
314
  - **Extract, don't re-ask**: if the user's answer contains information for upcoming fields
354
315
  (e.g. "it's a NestJS app on port 3000 at apps/auth-ui"), extract and fill those fields
355
316
  silently — only ask about what is genuinely missing. Never ask a question the user has
@@ -27,9 +27,11 @@ Show all tasks assigned to this module across all features.
27
27
 
28
28
  ## Steps
29
29
 
30
- Determine the set of task headings to look for:
31
- - `module.json` has no `submodules` look for `### {module}` (one heading).
32
- - `module.json` has `submodules[]` → look for `### {submodule.name}` for each entry.
30
+ Determine the set of task headings to look for. Read `{spec_submodule_path}/.forge/project.json`
31
+ and find the `modules[]` entry where `name == module` (from module.json) — submodule structure
32
+ is never stored in module.json itself:
33
+ - Entry has no `submodules[]` → look for `### {module}` (one heading).
34
+ - Entry has `submodules[]` → look for `### {submodule.name}` for each entry.
33
35
  Submodule task headings use the submodule's own name — no parent prefix — so they are
34
36
  identical to standalone module headings and can be promoted without touching tasks.md.
35
37