codeforge-dev 1.9.0 → 1.10.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/.devcontainer/.env +3 -0
- package/.devcontainer/CHANGELOG.md +56 -0
- package/.devcontainer/CLAUDE.md +29 -8
- package/.devcontainer/README.md +61 -2
- package/.devcontainer/config/defaults/main-system-prompt.md +162 -128
- package/.devcontainer/config/defaults/rules/spec-workflow.md +10 -2
- package/.devcontainer/connect-external-terminal.sh +17 -17
- package/.devcontainer/devcontainer.json +143 -144
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/agents/architect.md +4 -3
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/agents/doc-writer.md +3 -3
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/agents/spec-writer.md +21 -11
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/hooks/hooks.json +1 -1
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/scripts/advisory-test-runner.py +186 -13
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/scripts/spec-reminder.py +2 -1
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/documentation-patterns/SKILL.md +1 -1
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/spec-check/SKILL.md +22 -10
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/spec-init/SKILL.md +7 -5
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/spec-init/references/backlog-template.md +19 -3
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/spec-init/references/roadmap-template.md +28 -8
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/spec-new/SKILL.md +15 -6
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/spec-new/references/template.md +24 -5
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/spec-refine/SKILL.md +194 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/spec-update/SKILL.md +19 -1
- package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/specification-writing/SKILL.md +19 -12
- package/.devcontainer/scripts/check-setup.sh +24 -25
- package/.devcontainer/scripts/setup-aliases.sh +88 -76
- package/.devcontainer/scripts/setup-projects.sh +172 -131
- package/.devcontainer/scripts/setup-terminal.sh +48 -0
- package/.devcontainer/scripts/setup-update-claude.sh +49 -107
- package/.devcontainer/scripts/setup.sh +4 -17
- package/README.md +2 -2
- package/package.json +1 -1
|
@@ -2,9 +2,10 @@
|
|
|
2
2
|
"""
|
|
3
3
|
Advisory test runner — Stop hook that injects test results as context.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
Reads the list of files edited this session (written by collect-edited-files.py),
|
|
6
|
+
maps them to affected test files, and runs only those tests. Skips entirely
|
|
7
|
+
if no files were edited. Results are returned as additionalContext so Claude
|
|
8
|
+
sees pass/fail info without being blocked.
|
|
8
9
|
|
|
9
10
|
Reads hook input from stdin (JSON). Returns JSON on stdout.
|
|
10
11
|
Always exits 0 (advisory, never blocking).
|
|
@@ -15,15 +16,37 @@ import os
|
|
|
15
16
|
import subprocess
|
|
16
17
|
import sys
|
|
17
18
|
|
|
19
|
+
TIMEOUT_SECONDS = 15
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def get_edited_files(session_id: str) -> list[str]:
|
|
23
|
+
"""Read the list of files edited this session.
|
|
24
|
+
|
|
25
|
+
Relies on collect-edited-files.py writing paths to a temp file.
|
|
26
|
+
Returns deduplicated list of paths that still exist on disk.
|
|
27
|
+
"""
|
|
28
|
+
tmp_path = f"/tmp/claude-edited-files-{session_id}"
|
|
29
|
+
try:
|
|
30
|
+
with open(tmp_path, "r") as f:
|
|
31
|
+
raw = f.read()
|
|
32
|
+
except OSError:
|
|
33
|
+
return []
|
|
34
|
+
|
|
35
|
+
seen: set[str] = set()
|
|
36
|
+
result: list[str] = []
|
|
37
|
+
for line in raw.strip().splitlines():
|
|
38
|
+
path = line.strip()
|
|
39
|
+
if path and path not in seen and os.path.isfile(path):
|
|
40
|
+
seen.add(path)
|
|
41
|
+
result.append(path)
|
|
42
|
+
return result
|
|
43
|
+
|
|
18
44
|
|
|
19
45
|
def detect_test_framework(cwd: str) -> tuple[str, list[str]]:
|
|
20
46
|
"""Detect which test framework is available in the project.
|
|
21
47
|
|
|
22
|
-
Checks for: pytest, vitest, jest, mocha, go test, cargo test.
|
|
23
|
-
Falls back to npm test if a test script is defined.
|
|
24
|
-
|
|
25
48
|
Returns:
|
|
26
|
-
Tuple of (framework_name,
|
|
49
|
+
Tuple of (framework_name, base_command) or ("", []) if none found.
|
|
27
50
|
"""
|
|
28
51
|
try:
|
|
29
52
|
entries = set(os.listdir(cwd))
|
|
@@ -102,7 +125,7 @@ def detect_test_framework(cwd: str) -> tuple[str, list[str]]:
|
|
|
102
125
|
|
|
103
126
|
# --- Go ---
|
|
104
127
|
if "go.mod" in entries:
|
|
105
|
-
return ("go", ["go", "test", "
|
|
128
|
+
return ("go", ["go", "test", "-count=1"])
|
|
106
129
|
|
|
107
130
|
# --- Rust ---
|
|
108
131
|
if "Cargo.toml" in entries:
|
|
@@ -111,6 +134,139 @@ def detect_test_framework(cwd: str) -> tuple[str, list[str]]:
|
|
|
111
134
|
return ("", [])
|
|
112
135
|
|
|
113
136
|
|
|
137
|
+
def resolve_pytest_tests(edited_files: list[str], cwd: str) -> tuple[list[str], bool]:
|
|
138
|
+
"""Map edited Python files to their corresponding pytest test files.
|
|
139
|
+
|
|
140
|
+
Returns:
|
|
141
|
+
(test_files, run_all) — if run_all is True, run the whole suite
|
|
142
|
+
(e.g. conftest.py was edited).
|
|
143
|
+
"""
|
|
144
|
+
test_files: list[str] = []
|
|
145
|
+
|
|
146
|
+
for path in edited_files:
|
|
147
|
+
if not path.endswith(".py"):
|
|
148
|
+
continue
|
|
149
|
+
|
|
150
|
+
basename = os.path.basename(path)
|
|
151
|
+
|
|
152
|
+
# conftest changes can affect anything — run full suite
|
|
153
|
+
if basename == "conftest.py":
|
|
154
|
+
return ([], True)
|
|
155
|
+
|
|
156
|
+
# Already a test file — include directly
|
|
157
|
+
if basename.startswith("test_") or "/tests/" in path:
|
|
158
|
+
if os.path.isfile(path):
|
|
159
|
+
test_files.append(path)
|
|
160
|
+
continue
|
|
161
|
+
|
|
162
|
+
# Map source → test via directory mirroring
|
|
163
|
+
# e.g. src/engine/db/sessions.py → tests/engine/db/test_sessions.py
|
|
164
|
+
# e.g. src/engine/api/routes/github.py → tests/engine/api/test_routes_github.py
|
|
165
|
+
rel = os.path.relpath(path, cwd)
|
|
166
|
+
parts = rel.split(os.sep)
|
|
167
|
+
|
|
168
|
+
# Strip leading "src/" if present
|
|
169
|
+
if parts and parts[0] == "src":
|
|
170
|
+
parts = parts[1:]
|
|
171
|
+
|
|
172
|
+
if not parts:
|
|
173
|
+
continue
|
|
174
|
+
|
|
175
|
+
module = parts[-1] # e.g. "sessions.py"
|
|
176
|
+
module_name = module.removesuffix(".py")
|
|
177
|
+
parent_parts = parts[:-1] # e.g. ["engine", "db"]
|
|
178
|
+
|
|
179
|
+
# Standard mapping: tests/<parent>/test_<module>.py
|
|
180
|
+
test_path = os.path.join(cwd, "tests", *parent_parts, f"test_{module_name}.py")
|
|
181
|
+
if os.path.isfile(test_path):
|
|
182
|
+
test_files.append(test_path)
|
|
183
|
+
continue
|
|
184
|
+
|
|
185
|
+
# Routes mapping: src/engine/api/routes/github.py
|
|
186
|
+
# → tests/engine/api/test_routes_github.py
|
|
187
|
+
if len(parent_parts) >= 2 and parent_parts[-1] == "routes":
|
|
188
|
+
route_test = os.path.join(
|
|
189
|
+
cwd,
|
|
190
|
+
"tests",
|
|
191
|
+
*parent_parts[:-1],
|
|
192
|
+
f"test_routes_{module_name}.py",
|
|
193
|
+
)
|
|
194
|
+
if os.path.isfile(route_test):
|
|
195
|
+
test_files.append(route_test)
|
|
196
|
+
|
|
197
|
+
# Deduplicate while preserving order
|
|
198
|
+
seen: set[str] = set()
|
|
199
|
+
unique: list[str] = []
|
|
200
|
+
for t in test_files:
|
|
201
|
+
if t not in seen:
|
|
202
|
+
seen.add(t)
|
|
203
|
+
unique.append(t)
|
|
204
|
+
|
|
205
|
+
return (unique, False)
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
def resolve_affected_tests(
|
|
209
|
+
edited_files: list[str], cwd: str, framework: str
|
|
210
|
+
) -> tuple[list[str], bool]:
|
|
211
|
+
"""Resolve edited files to framework-specific test arguments.
|
|
212
|
+
|
|
213
|
+
Returns:
|
|
214
|
+
(extra_args, run_all) — extra_args to append to the base command.
|
|
215
|
+
If run_all is True, run the whole suite (no extra args needed).
|
|
216
|
+
If extra_args is empty and run_all is False, skip testing entirely.
|
|
217
|
+
"""
|
|
218
|
+
if framework == "pytest":
|
|
219
|
+
test_files, run_all = resolve_pytest_tests(edited_files, cwd)
|
|
220
|
+
return (test_files, run_all)
|
|
221
|
+
|
|
222
|
+
if framework == "vitest":
|
|
223
|
+
# vitest --related does dep-graph analysis natively
|
|
224
|
+
source_files = [
|
|
225
|
+
f
|
|
226
|
+
for f in edited_files
|
|
227
|
+
if not f.endswith(
|
|
228
|
+
(".md", ".json", ".yaml", ".yml", ".toml", ".txt", ".css")
|
|
229
|
+
)
|
|
230
|
+
]
|
|
231
|
+
if not source_files:
|
|
232
|
+
return ([], False)
|
|
233
|
+
return (["--related"] + source_files, False)
|
|
234
|
+
|
|
235
|
+
if framework == "jest":
|
|
236
|
+
source_files = [
|
|
237
|
+
f
|
|
238
|
+
for f in edited_files
|
|
239
|
+
if not f.endswith(
|
|
240
|
+
(".md", ".json", ".yaml", ".yml", ".toml", ".txt", ".css")
|
|
241
|
+
)
|
|
242
|
+
]
|
|
243
|
+
if not source_files:
|
|
244
|
+
return ([], False)
|
|
245
|
+
return (["--findRelatedTests"] + source_files, False)
|
|
246
|
+
|
|
247
|
+
if framework == "go":
|
|
248
|
+
# Map edited .go files to their package directories
|
|
249
|
+
pkgs: set[str] = set()
|
|
250
|
+
for path in edited_files:
|
|
251
|
+
if path.endswith(".go"):
|
|
252
|
+
pkg_dir = os.path.dirname(path)
|
|
253
|
+
rel = os.path.relpath(pkg_dir, cwd)
|
|
254
|
+
pkgs.add(f"./{rel}")
|
|
255
|
+
if not pkgs:
|
|
256
|
+
return ([], False)
|
|
257
|
+
return (sorted(pkgs), False)
|
|
258
|
+
|
|
259
|
+
# cargo, mocha, npm-test — no granular selection, run full suite
|
|
260
|
+
code_files = [
|
|
261
|
+
f
|
|
262
|
+
for f in edited_files
|
|
263
|
+
if not f.endswith((".md", ".json", ".yaml", ".yml", ".toml", ".txt"))
|
|
264
|
+
]
|
|
265
|
+
if not code_files:
|
|
266
|
+
return ([], False)
|
|
267
|
+
return ([], True)
|
|
268
|
+
|
|
269
|
+
|
|
114
270
|
def main():
|
|
115
271
|
try:
|
|
116
272
|
input_data = json.load(sys.stdin)
|
|
@@ -121,34 +277,51 @@ def main():
|
|
|
121
277
|
if input_data.get("stop_hook_active"):
|
|
122
278
|
sys.exit(0)
|
|
123
279
|
|
|
280
|
+
session_id = input_data.get("session_id", "")
|
|
281
|
+
if not session_id:
|
|
282
|
+
sys.exit(0)
|
|
283
|
+
|
|
284
|
+
# No files edited this session — nothing to test
|
|
285
|
+
edited_files = get_edited_files(session_id)
|
|
286
|
+
if not edited_files:
|
|
287
|
+
sys.exit(0)
|
|
288
|
+
|
|
124
289
|
cwd = os.getcwd()
|
|
125
|
-
framework,
|
|
290
|
+
framework, base_cmd = detect_test_framework(cwd)
|
|
126
291
|
|
|
127
292
|
if not framework:
|
|
128
293
|
sys.exit(0)
|
|
129
294
|
|
|
295
|
+
extra_args, run_all = resolve_affected_tests(edited_files, cwd, framework)
|
|
296
|
+
|
|
297
|
+
# No affected tests and not a run-all situation — skip
|
|
298
|
+
if not extra_args and not run_all:
|
|
299
|
+
sys.exit(0)
|
|
300
|
+
|
|
301
|
+
cmd = base_cmd + extra_args
|
|
302
|
+
|
|
130
303
|
try:
|
|
131
304
|
result = subprocess.run(
|
|
132
305
|
cmd,
|
|
133
306
|
cwd=cwd,
|
|
134
307
|
capture_output=True,
|
|
135
308
|
text=True,
|
|
136
|
-
timeout=
|
|
309
|
+
timeout=TIMEOUT_SECONDS,
|
|
137
310
|
)
|
|
138
311
|
except subprocess.TimeoutExpired:
|
|
139
312
|
json.dump(
|
|
140
|
-
{
|
|
313
|
+
{
|
|
314
|
+
"additionalContext": f"[Tests] {framework} timed out after {TIMEOUT_SECONDS}s"
|
|
315
|
+
},
|
|
141
316
|
sys.stdout,
|
|
142
317
|
)
|
|
143
318
|
sys.exit(0)
|
|
144
319
|
except (FileNotFoundError, OSError):
|
|
145
|
-
# Test runner not installed or not accessible
|
|
146
320
|
sys.exit(0)
|
|
147
321
|
|
|
148
322
|
output = (result.stdout + "\n" + result.stderr).strip()
|
|
149
323
|
|
|
150
324
|
if result.returncode == 0:
|
|
151
|
-
# Extract test count from output if possible
|
|
152
325
|
json.dump(
|
|
153
326
|
{"additionalContext": f"[Tests] All tests passed ({framework})"},
|
|
154
327
|
sys.stdout,
|
package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/scripts/spec-reminder.py
CHANGED
|
@@ -110,7 +110,8 @@ def main():
|
|
|
110
110
|
f"[Spec Reminder] Code was modified in {dirs_str} "
|
|
111
111
|
"but no specs were updated. "
|
|
112
112
|
"Use /spec-update to update the relevant spec, "
|
|
113
|
-
"
|
|
113
|
+
"/spec-new if no spec exists for this feature, "
|
|
114
|
+
"or /spec-refine if the spec is still in draft status."
|
|
114
115
|
)
|
|
115
116
|
|
|
116
117
|
json.dump({"additionalContext": message}, sys.stdout)
|
|
@@ -65,7 +65,7 @@ Development setup, how to run tests, how to submit changes. Link to CONTRIBUTING
|
|
|
65
65
|
|
|
66
66
|
## Sizing Rules
|
|
67
67
|
|
|
68
|
-
Documentation files consumed by AI tools (CLAUDE.md, specs, architecture docs) should
|
|
68
|
+
Documentation files consumed by AI tools (CLAUDE.md, specs, architecture docs) should aim for **~200 lines** each. Split large documents by concern when practical. Each file should be independently useful.
|
|
69
69
|
|
|
70
70
|
For human-facing docs (README, API reference), there is no hard limit, but prefer shorter docs that link to detailed sub-pages over monolithic documents.
|
|
71
71
|
|
package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/spec-check/SKILL.md
CHANGED
|
@@ -38,9 +38,11 @@ For each spec file, extract:
|
|
|
38
38
|
- **Version** from the `**Version:**` field
|
|
39
39
|
- **Status** from the `**Status:**` field
|
|
40
40
|
- **Last Updated** from the `**Last Updated:**` field
|
|
41
|
+
- **Approval** from the `**Approval:**` field (default `draft` if missing)
|
|
41
42
|
- **Line count** (wc -l)
|
|
42
43
|
- **Sections present** — check for each required section header
|
|
43
44
|
- **Acceptance criteria** — count total, count checked `[x]`
|
|
45
|
+
- **Requirements** — count total, count `[assumed]`, count `[user-approved]`
|
|
44
46
|
- **Discrepancies** — check if section has content
|
|
45
47
|
|
|
46
48
|
### Step 3: Flag Issues
|
|
@@ -49,13 +51,17 @@ For each spec, check these conditions:
|
|
|
49
51
|
|
|
50
52
|
| Issue | Condition | Severity |
|
|
51
53
|
|-------|-----------|----------|
|
|
54
|
+
| **Unapproved** | Approval is `draft` or missing | High |
|
|
55
|
+
| **Assumed requirements** | Has requirements tagged `[assumed]` | Medium |
|
|
52
56
|
| **Stale** | Status is `planned` but Last Updated is >30 days ago | High |
|
|
53
57
|
| **Incomplete** | Missing required sections (Intent, Acceptance Criteria, Key Files, Requirements, Out of Scope) | High |
|
|
54
|
-
| **
|
|
58
|
+
| **Long spec** | Exceeds ~200 lines — consider splitting | Info |
|
|
55
59
|
| **No criteria** | Acceptance Criteria section is empty or has no checkboxes | High |
|
|
56
60
|
| **Open discrepancies** | Discrepancies section has content | Medium |
|
|
57
61
|
| **Missing as-built** | Status is `implemented` but Implementation Notes is empty | Medium |
|
|
58
62
|
| **Stale paths** | Key Files references paths that don't exist | Low |
|
|
63
|
+
| **Draft + implemented** | Status is `implemented` but Approval is `draft` — approval gate was bypassed | High |
|
|
64
|
+
| **Inconsistent approval** | Approval is `user-approved` but spec has `[assumed]` requirements | High |
|
|
59
65
|
|
|
60
66
|
### Step 4: Report
|
|
61
67
|
|
|
@@ -64,11 +70,11 @@ Output a summary table:
|
|
|
64
70
|
```
|
|
65
71
|
## Spec Health Report
|
|
66
72
|
|
|
67
|
-
| Feature | Version | Status | Updated | Lines | Issues |
|
|
68
|
-
|
|
69
|
-
| Session History | v0.2.0 | implemented | 2026-02-08 | 74 | None |
|
|
70
|
-
| Auth Flow | v0.3.0 | planned | 2026-01-15 | 45 | Stale (26 days) |
|
|
71
|
-
| Settings Page | v0.2.0 | partial | 2026-02-05 | 210 |
|
|
73
|
+
| Feature | Version | Status | Approval | Updated | Lines | Issues |
|
|
74
|
+
|---------|---------|--------|----------|---------|-------|--------|
|
|
75
|
+
| Session History | v0.2.0 | implemented | user-approved | 2026-02-08 | 74 | None |
|
|
76
|
+
| Auth Flow | v0.3.0 | planned | draft | 2026-01-15 | 45 | Unapproved, Stale (26 days) |
|
|
77
|
+
| Settings Page | v0.2.0 | partial | draft | 2026-02-05 | 210 | Unapproved, Long spec |
|
|
72
78
|
|
|
73
79
|
## Issues Found
|
|
74
80
|
|
|
@@ -76,11 +82,17 @@ Output a summary table:
|
|
|
76
82
|
- **Auth Flow** (`.specs/v0.3.0/auth-flow.md`): Status is `planned` but last updated 26 days ago. Either implementation is stalled or the spec needs an as-built update.
|
|
77
83
|
|
|
78
84
|
### Medium Priority
|
|
79
|
-
- **Settings Page** (`.specs/v0.2.0/settings-page.md`): 210 lines
|
|
85
|
+
- **Settings Page** (`.specs/v0.2.0/settings-page.md`): 210 lines — consider splitting into sub-specs for easier consumption.
|
|
80
86
|
|
|
81
87
|
### Suggested Actions
|
|
82
|
-
1. Run `/spec-
|
|
83
|
-
2.
|
|
88
|
+
1. Run `/spec-refine auth-flow` to validate assumptions and get user approval
|
|
89
|
+
2. Run `/spec-update auth-flow` to update the auth flow spec
|
|
90
|
+
3. Split settings-page.md into sub-specs
|
|
91
|
+
|
|
92
|
+
### Approval Summary
|
|
93
|
+
- **User-approved:** 1 spec
|
|
94
|
+
- **Draft (needs /spec-refine):** 2 specs
|
|
95
|
+
- **Assumed requirements across all specs:** 8
|
|
84
96
|
```
|
|
85
97
|
|
|
86
|
-
If no issues are found, report: "All specs healthy. N specs across M versions."
|
|
98
|
+
If no issues are found, report: "All specs healthy. N specs across M versions. All user-approved."
|
package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/spec-init/SKILL.md
CHANGED
|
@@ -67,18 +67,20 @@ Created:
|
|
|
67
67
|
- `.specs/BACKLOG.md` — deferred items list
|
|
68
68
|
|
|
69
69
|
Next steps:
|
|
70
|
-
-
|
|
70
|
+
- Add features to `BACKLOG.md` with priority grades (P0–P3)
|
|
71
|
+
- Pull features into a version in `ROADMAP.md` when ready to scope
|
|
72
|
+
- Use `/spec-new <feature-name> <version>` to create a spec
|
|
73
|
+
- Use `/spec-refine <feature-name>` to validate before implementation
|
|
74
|
+
- After implementing, use `/spec-update` to close the loop
|
|
71
75
|
- Use `/spec-check` to audit spec health at any time
|
|
72
76
|
```
|
|
73
77
|
|
|
74
78
|
---
|
|
75
79
|
|
|
76
|
-
##
|
|
80
|
+
## Constraints
|
|
77
81
|
|
|
78
82
|
- **Never overwrite** an existing `.specs/` directory or its contents.
|
|
79
|
-
-
|
|
80
|
-
- **BACKLOG.md** must stay under 15 lines (it grows as items are added).
|
|
81
|
-
- Templates are starting points — the user will extend them.
|
|
83
|
+
- Templates are starting points — the user will extend them as the project grows.
|
|
82
84
|
|
|
83
85
|
---
|
|
84
86
|
|
|
@@ -1,7 +1,23 @@
|
|
|
1
1
|
# Backlog
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Priority-graded feature and infrastructure backlog. Items are pulled into versions when ready to scope and spec. See `ROADMAP.md` for the versioning workflow.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## P0 — High Priority
|
|
6
6
|
|
|
7
|
-
- [ ] [
|
|
7
|
+
- [ ] [Feature] — [Description]
|
|
8
|
+
|
|
9
|
+
## P1 — Important
|
|
10
|
+
|
|
11
|
+
- [ ] [Feature] — [Description]
|
|
12
|
+
|
|
13
|
+
## P2 — Desired
|
|
14
|
+
|
|
15
|
+
- [ ] [Feature] — [Description]
|
|
16
|
+
|
|
17
|
+
## P3 — Nice to Have
|
|
18
|
+
|
|
19
|
+
- [ ] [Feature] — [Description]
|
|
20
|
+
|
|
21
|
+
## Infrastructure & CI
|
|
22
|
+
|
|
23
|
+
- [ ] [Item] — [Description]
|
|
@@ -1,13 +1,33 @@
|
|
|
1
1
|
# Roadmap
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
> Features live in the priority-graded backlog until pulled into a version.
|
|
4
|
+
> Versions are scoped and spec'd when ready to build — not pre-assigned.
|
|
5
|
+
> See `BACKLOG.md` for the feature backlog.
|
|
4
6
|
|
|
5
|
-
|
|
6
|
-
|---------|--------|---------|
|
|
7
|
-
| v0.1.0 | planned | [Initial release — describe core features] |
|
|
7
|
+
## How Versioning Works
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
1. **Backlog** — All desired features live in `BACKLOG.md`, graded by priority.
|
|
10
|
+
2. **Version scoping** — When ready to start a new version, pull features from the backlog.
|
|
11
|
+
3. **Spec first** — Each feature in a version gets a spec before implementation begins.
|
|
12
|
+
4. **Ship** — Version is done when all its specs are implemented and verified.
|
|
10
13
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
+
Only the **next version** is defined in detail. Everything else is backlog.
|
|
15
|
+
|
|
16
|
+
## Released
|
|
17
|
+
|
|
18
|
+
_None yet._
|
|
19
|
+
|
|
20
|
+
## Current
|
|
21
|
+
|
|
22
|
+
### v0.1.0 — [Name] 🔧
|
|
23
|
+
|
|
24
|
+
- [ ] [Feature pulled from backlog]
|
|
25
|
+
- [ ] [Feature pulled from backlog]
|
|
26
|
+
|
|
27
|
+
## Next
|
|
28
|
+
|
|
29
|
+
> Scoped from `BACKLOG.md` when current version is complete.
|
|
30
|
+
|
|
31
|
+
## Out of Scope
|
|
32
|
+
|
|
33
|
+
- [Items explicitly not planned]
|
package/.devcontainer/plugins/devs-marketplace/plugins/code-directive/skills/spec-new/SKILL.md
CHANGED
|
@@ -14,7 +14,7 @@ version: 0.1.0
|
|
|
14
14
|
|
|
15
15
|
A specification is a contract between the person requesting a feature and the person building it. Writing the spec BEFORE implementation forces you to think through edge cases, acceptance criteria, and scope boundaries while changes are cheap — before any code exists.
|
|
16
16
|
|
|
17
|
-
Every project uses `.specs/` as the specification directory. Specs are version-organized, independently loadable, and
|
|
17
|
+
Every project uses `.specs/` as the specification directory. Specs are version-organized, independently loadable, and should aim for ~200 lines.
|
|
18
18
|
|
|
19
19
|
---
|
|
20
20
|
|
|
@@ -30,6 +30,8 @@ If arguments are missing, ask the user for:
|
|
|
30
30
|
1. Feature name (what is being built)
|
|
31
31
|
2. Target version (which release this belongs to)
|
|
32
32
|
|
|
33
|
+
**Note:** Features should be pulled from the project's backlog (`BACKLOG.md`) into a version before creating a spec. If the feature isn't in the backlog yet, add it first, then assign it to a version.
|
|
34
|
+
|
|
33
35
|
### Step 2: Determine File Path
|
|
34
36
|
|
|
35
37
|
- **Multi-feature version** (directory already exists or multiple features planned):
|
|
@@ -49,6 +51,7 @@ Pre-fill:
|
|
|
49
51
|
- **Version**: from arguments
|
|
50
52
|
- **Status**: `planned`
|
|
51
53
|
- **Last Updated**: today's date (YYYY-MM-DD)
|
|
54
|
+
- **Approval**: `draft`
|
|
52
55
|
- **Feature name**: from arguments
|
|
53
56
|
|
|
54
57
|
Leave all other sections as placeholders for the user to fill.
|
|
@@ -62,24 +65,30 @@ After creating the file, guide the user through filling it out:
|
|
|
62
65
|
3. **Key Files** — Glob the codebase to identify existing files relevant to this feature
|
|
63
66
|
4. **Schema / Data Model** — Reference file paths only, never inline schemas
|
|
64
67
|
5. **API Endpoints** — Table format: Method | Path | Description
|
|
65
|
-
6. **Requirements** — EARS format, numbered FR-1, FR-2, NFR-1, etc.
|
|
68
|
+
6. **Requirements** — EARS format, numbered FR-1, FR-2, NFR-1, etc. Tag all requirements `[assumed]` at creation time — they become `[user-approved]` only after explicit user validation via `/spec-refine`.
|
|
66
69
|
7. **Dependencies** — What this feature depends on
|
|
67
70
|
8. **Out of Scope** — Explicit non-goals to prevent scope creep
|
|
71
|
+
9. **Resolved Questions** — Leave empty at creation; populated by `/spec-refine`
|
|
68
72
|
|
|
69
73
|
### Step 5: Validate
|
|
70
74
|
|
|
71
75
|
Before finishing:
|
|
72
|
-
- [ ]
|
|
76
|
+
- [ ] If the file exceeds ~200 lines, consider splitting into sub-specs
|
|
73
77
|
- [ ] No source code, SQL, or type definitions reproduced inline
|
|
74
|
-
- [ ] Status is `planned`
|
|
78
|
+
- [ ] Status is `planned` and Approval is `draft`
|
|
75
79
|
- [ ] All required sections present (even if some are "N/A" or "TBD")
|
|
76
80
|
- [ ] Acceptance criteria are testable
|
|
81
|
+
- [ ] All requirements are tagged `[assumed]`
|
|
82
|
+
|
|
83
|
+
After validation, inform the user: **"This spec MUST go through `/spec-refine` before implementation begins.** All requirements are marked `[assumed]` until explicitly validated."
|
|
84
|
+
|
|
85
|
+
The `/spec-refine` skill walks through every `[assumed]` requirement with the user, validates tech decisions and scope boundaries, and upgrades approved items to `[user-approved]`. The spec's `**Approval:**` becomes `user-approved` only after all requirements pass review.
|
|
77
86
|
|
|
78
87
|
---
|
|
79
88
|
|
|
80
|
-
##
|
|
89
|
+
## Sizing Guidelines
|
|
81
90
|
|
|
82
|
-
-
|
|
91
|
+
- **Aim for ~200 lines per spec.** If a feature needs more, consider splitting into sub-specs with a parent `_overview.md` linking them.
|
|
83
92
|
- **Reference, don't reproduce.** Write `see src/engine/db/migrations/002.sql lines 48-70` — never paste the SQL.
|
|
84
93
|
- **Independently loadable.** Each spec file must be useful without loading any other file.
|
|
85
94
|
- **EARS format for requirements.** Use the `specification-writing` skill for templates and examples.
|
|
@@ -12,6 +12,7 @@ Standard template for all feature specifications. Copy this structure when creat
|
|
|
12
12
|
**Version:** v0.X.0
|
|
13
13
|
**Status:** planned
|
|
14
14
|
**Last Updated:** YYYY-MM-DD
|
|
15
|
+
**Approval:** draft
|
|
15
16
|
|
|
16
17
|
## Intent
|
|
17
18
|
|
|
@@ -56,14 +57,14 @@ Standard template for all feature specifications. Copy this structure when creat
|
|
|
56
57
|
|
|
57
58
|
### Functional Requirements
|
|
58
59
|
|
|
59
|
-
- FR-1: [EARS format requirement — see specification-writing skill for templates]
|
|
60
|
-
- FR-2: When [event], the system shall [action].
|
|
61
|
-
- FR-3: If [unwanted condition], then the system shall [action].
|
|
60
|
+
- FR-1 [assumed]: [EARS format requirement — see specification-writing skill for templates]
|
|
61
|
+
- FR-2 [assumed]: When [event], the system shall [action].
|
|
62
|
+
- FR-3 [assumed]: If [unwanted condition], then the system shall [action].
|
|
62
63
|
|
|
63
64
|
### Non-Functional Requirements
|
|
64
65
|
|
|
65
|
-
- NFR-1: The system shall respond to [endpoint] within [N]ms at the [percentile] percentile.
|
|
66
|
-
- NFR-2: [Security, accessibility, scalability requirement]
|
|
66
|
+
- NFR-1 [assumed]: The system shall respond to [endpoint] within [N]ms at the [percentile] percentile.
|
|
67
|
+
- NFR-2 [assumed]: [Security, accessibility, scalability requirement]
|
|
67
68
|
|
|
68
69
|
## Dependencies
|
|
69
70
|
|
|
@@ -75,6 +76,10 @@ Standard template for all feature specifications. Copy this structure when creat
|
|
|
75
76
|
- [Explicit non-goal 1 — prevents scope creep]
|
|
76
77
|
- [Explicit non-goal 2]
|
|
77
78
|
|
|
79
|
+
## Resolved Questions
|
|
80
|
+
|
|
81
|
+
[Decisions explicitly approved by the user via `/spec-refine`. Each entry: decision topic, chosen option, date, brief rationale.]
|
|
82
|
+
|
|
78
83
|
## Implementation Notes
|
|
79
84
|
|
|
80
85
|
[Post-implementation only. Leave empty in planned specs. After building, document what actually shipped vs. what was planned.]
|
|
@@ -108,3 +113,17 @@ Standard template for all feature specifications. Copy this structure when creat
|
|
|
108
113
|
| `planned` | Spec written, implementation not started |
|
|
109
114
|
| `partial` | Some acceptance criteria implemented, work ongoing |
|
|
110
115
|
| `implemented` | All acceptance criteria met, as-built notes complete |
|
|
116
|
+
|
|
117
|
+
## Approval Workflow
|
|
118
|
+
|
|
119
|
+
| Tag | Meaning |
|
|
120
|
+
|-----|---------|
|
|
121
|
+
| `[assumed]` | Requirement was drafted by AI or inferred — treated as a hypothesis |
|
|
122
|
+
| `[user-approved]` | Requirement was explicitly reviewed and approved by the user via `/spec-refine` |
|
|
123
|
+
|
|
124
|
+
| Approval Status | Meaning |
|
|
125
|
+
|-----------------|---------|
|
|
126
|
+
| `draft` | Spec has unvalidated assumptions — NOT approved for implementation |
|
|
127
|
+
| `user-approved` | All requirements are `[user-approved]` — ready for implementation |
|
|
128
|
+
|
|
129
|
+
**Workflow:** `/spec-new` creates → `/spec-refine` validates → implementation begins → `/spec-update` closes the loop.
|