@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
|
@@ -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
|
-
-
|
|
22
|
-
|
|
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
|
-
|
|
36
|
-
|
|
37
|
-
|
|
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
|
|
29
|
-
- **Determine working scope
|
|
30
|
-
- `
|
|
31
|
-
|
|
32
|
-
|
|
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**,
|
|
59
|
-
> "This looks like a module with submodules ({names}).
|
|
60
|
-
>
|
|
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
|
-
|
|
63
|
-
|
|
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**
|
|
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
|
-
|
|
130
|
-
|
|
131
|
-
|
|
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 here — they 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
|
-
**
|
|
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
|
+
**Q4 — Principles**
|
|
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
|
-
**
|
|
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
|
-
**
|
|
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
|
-
**
|
|
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
|
-
{
|
|
189
|
-
|
|
190
|
-
spec_link_type:
|
|
191
|
-
|
|
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:
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
254
|
-
|
|
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
|
-
|
|
32
|
-
|
|
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
|
|