its-magic 0.1.2-15 → 0.1.2-16
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 +116 -11
- package/installer.ps1 +8 -2
- package/installer.py +6 -2
- package/installer.sh +9 -2
- package/package.json +2 -15
- package/{.cursor → template/.cursor}/commands/execute.md +2 -0
- package/{.cursor → template/.cursor}/commands/qa.md +5 -1
- package/{.cursor → template/.cursor}/rules/quality.mdc +9 -1
- package/{.github → template/.github}/workflows/ci.yml +137 -2
- package/template/README.md +636 -0
- package/template/docs/engineering/runbook.md +41 -0
- package/template/scripts/validate-and-push.ps1 +165 -0
- package/template/scripts/validate-and-push.sh +148 -0
- package/docs/engineering/runbook.md +0 -32
- /package/{.cursor → template/.cursor}/agents/curator.mdc +0 -0
- /package/{.cursor → template/.cursor}/agents/dev.mdc +0 -0
- /package/{.cursor → template/.cursor}/agents/po.mdc +0 -0
- /package/{.cursor → template/.cursor}/agents/qa.mdc +0 -0
- /package/{.cursor → template/.cursor}/agents/release.mdc +0 -0
- /package/{.cursor → template/.cursor}/agents/tech-lead.mdc +0 -0
- /package/{.cursor → template/.cursor}/commands/architecture.md +0 -0
- /package/{.cursor → template/.cursor}/commands/auto.md +0 -0
- /package/{.cursor → template/.cursor}/commands/discovery.md +0 -0
- /package/{.cursor → template/.cursor}/commands/intake.md +0 -0
- /package/{.cursor → template/.cursor}/commands/map-codebase.md +0 -0
- /package/{.cursor → template/.cursor}/commands/milestone-complete.md +0 -0
- /package/{.cursor → template/.cursor}/commands/milestone-start.md +0 -0
- /package/{.cursor → template/.cursor}/commands/pause.md +0 -0
- /package/{.cursor → template/.cursor}/commands/phase-context.md +0 -0
- /package/{.cursor → template/.cursor}/commands/plan-verify.md +0 -0
- /package/{.cursor → template/.cursor}/commands/quick.md +0 -0
- /package/{.cursor → template/.cursor}/commands/refresh-context.md +0 -0
- /package/{.cursor → template/.cursor}/commands/release.md +0 -0
- /package/{.cursor → template/.cursor}/commands/research.md +0 -0
- /package/{.cursor → template/.cursor}/commands/resume.md +0 -0
- /package/{.cursor → template/.cursor}/commands/sprint-plan.md +0 -0
- /package/{.cursor → template/.cursor}/commands/verify-work.md +0 -0
- /package/{.cursor → template/.cursor}/hooks/README.md +0 -0
- /package/{.cursor → template/.cursor}/hooks/hook.py +0 -0
- /package/{.cursor → template/.cursor}/hooks.json +0 -0
- /package/{.cursor → template/.cursor}/rules/coding-standards.mdc +0 -0
- /package/{.cursor → template/.cursor}/rules/core.mdc +0 -0
- /package/{.cursor → template/.cursor}/rules/escalation.mdc +0 -0
- /package/{.cursor → template/.cursor}/rules/handoffs.mdc +0 -0
- /package/{.cursor → template/.cursor}/scratchpad.local.example.md +0 -0
- /package/{.cursor → template/.cursor}/scratchpad.md +0 -0
- /package/{.cursor → template/.cursor}/skills/its-magic/SKILL.md +0 -0
- /package/{.cursor → template/.cursor}/skills/its-magic/templates/acceptance.json +0 -0
- /package/{.cursor → template/.cursor}/skills/its-magic/templates/acceptance.md +0 -0
- /package/{.cursor → template/.cursor}/skills/its-magic/templates/architecture.json +0 -0
- /package/{.cursor → template/.cursor}/skills/its-magic/templates/architecture.md +0 -0
- /package/{.cursor → template/.cursor}/skills/its-magic/templates/decision.json +0 -0
- /package/{.cursor → template/.cursor}/skills/its-magic/templates/decision.md +0 -0
- /package/{.cursor → template/.cursor}/skills/its-magic/templates/handoff.json +0 -0
- /package/{.cursor → template/.cursor}/skills/its-magic/templates/handoff.md +0 -0
- /package/{.cursor → template/.cursor}/skills/its-magic/templates/milestone.json +0 -0
- /package/{.cursor → template/.cursor}/skills/its-magic/templates/phase-context.json +0 -0
- /package/{.cursor → template/.cursor}/skills/its-magic/templates/plan-verify.json +0 -0
- /package/{.cursor → template/.cursor}/skills/its-magic/templates/sprint.json +0 -0
- /package/{.cursor → template/.cursor}/skills/its-magic/templates/sprint.md +0 -0
- /package/{.cursor → template/.cursor}/skills/its-magic/templates/story.json +0 -0
- /package/{.cursor → template/.cursor}/skills/its-magic/templates/story.md +0 -0
- /package/{.cursor → template/.cursor}/skills/its-magic/templates/uat.json +0 -0
- /package/{.github → template/.github}/workflows/deploy.yml +0 -0
- /package/{decisions → template/decisions}/DEC-0001.md +0 -0
- /package/{decisions → template/decisions}/DEC-0002.md +0 -0
- /package/{docs → template/docs}/engineering/architecture.md +0 -0
- /package/{docs → template/docs}/engineering/codebase-map.md +0 -0
- /package/{docs → template/docs}/engineering/context/phase-template.json +0 -0
- /package/{docs → template/docs}/engineering/decisions.md +0 -0
- /package/{docs → template/docs}/engineering/dependencies.json +0 -0
- /package/{docs → template/docs}/engineering/research.md +0 -0
- /package/{docs → template/docs}/engineering/state.md +0 -0
- /package/{docs → template/docs}/product/acceptance.md +0 -0
- /package/{docs → template/docs}/product/backlog.md +0 -0
- /package/{docs → template/docs}/product/vision.md +0 -0
- /package/{handoffs → template/handoffs}/dev_to_qa.md +0 -0
- /package/{handoffs → template/handoffs}/po_to_tl.md +0 -0
- /package/{handoffs → template/handoffs}/qa_to_dev.md +0 -0
- /package/{handoffs → template/handoffs}/release_notes.md +0 -0
- /package/{handoffs → template/handoffs}/resume_brief.md +0 -0
- /package/{handoffs → template/handoffs}/tl_to_dev.md +0 -0
- /package/{sprints → template/sprints}/S0001/plan-verify.json +0 -0
- /package/{sprints → template/sprints}/S0001/progress.md +0 -0
- /package/{sprints → template/sprints}/S0001/qa-findings.md +0 -0
- /package/{sprints → template/sprints}/S0001/sprint.md +0 -0
- /package/{sprints → template/sprints}/S0001/summary.md +0 -0
- /package/{sprints → template/sprints}/S0001/tasks.md +0 -0
- /package/{sprints → template/sprints}/S0001/uat.json +0 -0
- /package/{sprints → template/sprints}/S0001/uat.md +0 -0
- /package/{sprints → template/sprints}/quick/Q0001/summary.md +0 -0
- /package/{sprints → template/sprints}/quick/Q0001/task.json +0 -0
package/README.md
CHANGED
|
@@ -15,6 +15,7 @@ with pause/resume, decision gates, and persistent artifacts.
|
|
|
15
15
|
- Decision gate + escalation (`decisions/DEC-xxxx.md`).
|
|
16
16
|
- Pause/resume with checkpoints (`handoffs/resume_brief.md`).
|
|
17
17
|
- Automated execute/QA loop with safety caps (optional).
|
|
18
|
+
- 3-layer quality chain: AI loop → local validate-and-push → CI auto-fix.
|
|
18
19
|
- CI/CD templates driven by `docs/engineering/runbook.md`.
|
|
19
20
|
- Team-friendly local overrides (`scratchpad.local.md`).
|
|
20
21
|
- Optional remote/docker execution and autonomous installs.
|
|
@@ -123,18 +124,20 @@ its-magic --clean-repo --target .
|
|
|
123
124
|
|
|
124
125
|
```text
|
|
125
126
|
your-project/
|
|
126
|
-
.cursor/commands
|
|
127
|
-
.cursor/rules
|
|
128
|
-
.cursor/agents
|
|
129
|
-
.cursor/skills
|
|
130
|
-
.cursor/hooks
|
|
131
|
-
.cursor/scratchpad.md
|
|
127
|
+
.cursor/commands/ Cursor slash commands
|
|
128
|
+
.cursor/rules/ AI behavior rules
|
|
129
|
+
.cursor/agents/ Subagent definitions
|
|
130
|
+
.cursor/skills/ Reusable skills
|
|
131
|
+
.cursor/hooks/ Automation hooks
|
|
132
|
+
.cursor/scratchpad.md Shared configuration flags
|
|
132
133
|
.cursor/scratchpad.local.example.md
|
|
133
|
-
docs/
|
|
134
|
-
sprints/
|
|
135
|
-
handoffs/
|
|
136
|
-
decisions/
|
|
137
|
-
.
|
|
134
|
+
docs/ Engineering & product docs, runbook
|
|
135
|
+
sprints/ Sprint tracking artifacts
|
|
136
|
+
handoffs/ Phase handoff artifacts
|
|
137
|
+
decisions/ Decision records
|
|
138
|
+
scripts/validate-and-push.ps1 Local test-fix-push loop (Windows)
|
|
139
|
+
scripts/validate-and-push.sh Local test-fix-push loop (Linux/Mac)
|
|
140
|
+
.github/workflows/ CI with auto-fix loop
|
|
138
141
|
README.md
|
|
139
142
|
```
|
|
140
143
|
|
|
@@ -320,6 +323,108 @@ Graceful stop (for shutdown/end of day):
|
|
|
320
323
|
- `AUTO_INSTALL_DEPS=0`
|
|
321
324
|
- `AUTO_PAUSE_POLICY=after_task`
|
|
322
325
|
|
|
326
|
+
### Quality chain (3-layer auto-fix)
|
|
327
|
+
|
|
328
|
+
its-magic provides a complete quality chain that catches issues at three levels.
|
|
329
|
+
Each layer catches problems the previous layer missed:
|
|
330
|
+
|
|
331
|
+
```text
|
|
332
|
+
┌─────────────────────────────────────────────────────────────────┐
|
|
333
|
+
│ Layer 1: Cursor AI loop (in-editor) OFF by default │
|
|
334
|
+
│ AUTO_IMPLEMENTATION_LOOP + LOOP_UNTIL_GREEN │
|
|
335
|
+
│ execute → QA → fix → execute (bounded by AUTO_LOOP_MAX_CYCLES)│
|
|
336
|
+
└──────────────────────────┬──────────────────────────────────────┘
|
|
337
|
+
│ code ready to push
|
|
338
|
+
┌──────────────────────────▼──────────────────────────────────────┐
|
|
339
|
+
│ Layer 2: validate-and-push (local pre-push) MANUAL (run it)│
|
|
340
|
+
│ scripts/validate-and-push.sh / .ps1 │
|
|
341
|
+
│ test → format → lint-fix → test → commit + push │
|
|
342
|
+
└──────────────────────────┬──────────────────────────────────────┘
|
|
343
|
+
│ pushed to GitHub
|
|
344
|
+
┌──────────────────────────▼──────────────────────────────────────┐
|
|
345
|
+
│ Layer 3: CI auto-fix (GitHub Actions) OFF by default │
|
|
346
|
+
│ .github/workflows/ci.yml │
|
|
347
|
+
│ test/lint → auto-fix → commit → re-run (up to 3 retries) │
|
|
348
|
+
└─────────────────────────────────────────────────────────────────┘
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
| Layer | Default | Enable |
|
|
352
|
+
|-------|---------|--------|
|
|
353
|
+
| 1 - Cursor AI loop | off | Set `AUTO_IMPLEMENTATION_LOOP=1` + `LOOP_UNTIL_GREEN=1` in scratchpad |
|
|
354
|
+
| 2 - validate-and-push | manual | Run `scripts/validate-and-push.sh` or `.ps1` before pushing |
|
|
355
|
+
| 3 - CI auto-fix | off | Set `CI_AUTO_FIX: true` in `docs/engineering/runbook.md` |
|
|
356
|
+
|
|
357
|
+
CI itself (tests, lint, typecheck) always runs on push/PR. Only the **auto-fix
|
|
358
|
+
retry loop** is gated behind `CI_AUTO_FIX`. When disabled, CI still reports
|
|
359
|
+
failures -- it just won't try to fix and commit automatically.
|
|
360
|
+
|
|
361
|
+
All commands are read from `docs/engineering/runbook.md`. Fill in your
|
|
362
|
+
project-specific commands once and every layer uses them:
|
|
363
|
+
|
|
364
|
+
```text
|
|
365
|
+
TEST_COMMAND: npm test
|
|
366
|
+
LINT_COMMAND: npx eslint .
|
|
367
|
+
LINT_FIX_COMMAND: npx eslint --fix .
|
|
368
|
+
FORMAT_COMMAND: npx prettier --write .
|
|
369
|
+
CI_AUTO_FIX: true
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
#### Layer 1: Cursor AI loop
|
|
373
|
+
|
|
374
|
+
Enabled via scratchpad flags (see [Automation modes](#automation-modes)).
|
|
375
|
+
The AI runs execute → QA → fix cycles inside Cursor until tests pass or
|
|
376
|
+
the safety cap (`AUTO_LOOP_MAX_CYCLES`) is reached.
|
|
377
|
+
|
|
378
|
+
#### Layer 2: Local validate-and-push
|
|
379
|
+
|
|
380
|
+
Run before pushing to catch anything the AI loop missed:
|
|
381
|
+
|
|
382
|
+
```bash
|
|
383
|
+
# Bash (Linux / macOS)
|
|
384
|
+
sh scripts/validate-and-push.sh
|
|
385
|
+
|
|
386
|
+
# PowerShell (Windows)
|
|
387
|
+
powershell scripts/validate-and-push.ps1
|
|
388
|
+
powershell scripts/validate-and-push.ps1 -MaxAttempts 3
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
The script:
|
|
392
|
+
1. Runs `FORMAT_COMMAND` and `LINT_FIX_COMMAND` to auto-fix what it can
|
|
393
|
+
2. Runs `LINT_COMMAND` and `TEST_COMMAND` to verify
|
|
394
|
+
3. If checks fail, pauses and waits for you to fix
|
|
395
|
+
4. Re-runs (up to 5 attempts, configurable)
|
|
396
|
+
5. When green, commits and pushes automatically
|
|
397
|
+
|
|
398
|
+
Use `-NoCommit` (PowerShell) or `false` as third arg (Bash) to skip auto-push.
|
|
399
|
+
|
|
400
|
+
#### Layer 3: CI auto-fix (GitHub Actions)
|
|
401
|
+
|
|
402
|
+
**Disabled by default.** Set `CI_AUTO_FIX: true` in `docs/engineering/runbook.md`
|
|
403
|
+
to enable. When enabled and CI fails after a push, the auto-fix job kicks in:
|
|
404
|
+
|
|
405
|
+
```text
|
|
406
|
+
push / PR ──> checks ──> PASS ──> done
|
|
407
|
+
│
|
|
408
|
+
FAIL
|
|
409
|
+
│
|
|
410
|
+
auto-fix job
|
|
411
|
+
│
|
|
412
|
+
run LINT_FIX_COMMAND
|
|
413
|
+
run FORMAT_COMMAND
|
|
414
|
+
│
|
|
415
|
+
changes found?
|
|
416
|
+
╱ ╲
|
|
417
|
+
yes no
|
|
418
|
+
│ │
|
|
419
|
+
commit + push report failure
|
|
420
|
+
│ (manual fix needed)
|
|
421
|
+
CI re-runs
|
|
422
|
+
(up to 3x)
|
|
423
|
+
```
|
|
424
|
+
|
|
425
|
+
Auto-fix commits appear as `ci: auto-fix attempt N/3`. After 3 retries the
|
|
426
|
+
workflow stops and points you to `scripts/validate-and-push` for local fixing.
|
|
427
|
+
|
|
323
428
|
## Examples
|
|
324
429
|
|
|
325
430
|
### Example 1: New feature from idea
|
package/installer.ps1
CHANGED
|
@@ -147,9 +147,13 @@ function Show-ItsMagicHelp($VersionString, $RepoUrl) {
|
|
|
147
147
|
Write-Host ""
|
|
148
148
|
}
|
|
149
149
|
|
|
150
|
-
$
|
|
150
|
+
$scriptDir = Normalize-PathSafe (Split-Path -Parent $MyInvocation.MyCommand.Path)
|
|
151
|
+
$sourceRoot = Join-Path $scriptDir "template"
|
|
152
|
+
if (-not (Test-Path $sourceRoot -PathType Container)) {
|
|
153
|
+
$sourceRoot = $scriptDir
|
|
154
|
+
}
|
|
151
155
|
$repoUrl = "https://github.com/fl0wm0ti0n/its-magic"
|
|
152
|
-
$appVersion = Get-AppVersion $
|
|
156
|
+
$appVersion = Get-AppVersion $scriptDir
|
|
153
157
|
$noArgs = $PSBoundParameters.Count -eq 0
|
|
154
158
|
|
|
155
159
|
if ($Version) {
|
|
@@ -228,6 +232,8 @@ $includePaths = @(
|
|
|
228
232
|
"sprints",
|
|
229
233
|
"handoffs",
|
|
230
234
|
"decisions",
|
|
235
|
+
"scripts/validate-and-push.ps1",
|
|
236
|
+
"scripts/validate-and-push.sh",
|
|
231
237
|
".github/workflows",
|
|
232
238
|
"README.md"
|
|
233
239
|
)
|
package/installer.py
CHANGED
|
@@ -160,8 +160,10 @@ def clean_repo(target_root):
|
|
|
160
160
|
|
|
161
161
|
|
|
162
162
|
def main():
|
|
163
|
-
|
|
164
|
-
|
|
163
|
+
script_dir = normalize(os.path.dirname(__file__))
|
|
164
|
+
template_dir = os.path.join(script_dir, "template")
|
|
165
|
+
source_root = template_dir if os.path.isdir(template_dir) else script_dir
|
|
166
|
+
version = read_version(script_dir)
|
|
165
167
|
|
|
166
168
|
parser = argparse.ArgumentParser(
|
|
167
169
|
description="Install its-magic into a repo",
|
|
@@ -227,6 +229,8 @@ def main():
|
|
|
227
229
|
"sprints",
|
|
228
230
|
"handoffs",
|
|
229
231
|
"decisions",
|
|
232
|
+
"scripts/validate-and-push.ps1",
|
|
233
|
+
"scripts/validate-and-push.sh",
|
|
230
234
|
".github/workflows",
|
|
231
235
|
"README.md",
|
|
232
236
|
]
|
package/installer.sh
CHANGED
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env sh
|
|
2
2
|
set -e
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd)
|
|
5
|
+
if [ -d "$SCRIPT_DIR/template" ]; then
|
|
6
|
+
SOURCE_ROOT="$SCRIPT_DIR/template"
|
|
7
|
+
else
|
|
8
|
+
SOURCE_ROOT="$SCRIPT_DIR"
|
|
9
|
+
fi
|
|
5
10
|
REPO_URL="https://github.com/fl0wm0ti0n/its-magic"
|
|
6
|
-
APP_VERSION=$(sed -n 's/.*"version"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' "$
|
|
11
|
+
APP_VERSION=$(sed -n 's/.*"version"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' "$SCRIPT_DIR/package.json" 2>/dev/null | head -n 1)
|
|
7
12
|
[ -z "$APP_VERSION" ] && APP_VERSION="unknown"
|
|
8
13
|
|
|
9
14
|
show_banner() {
|
|
@@ -228,6 +233,8 @@ docs
|
|
|
228
233
|
sprints
|
|
229
234
|
handoffs
|
|
230
235
|
decisions
|
|
236
|
+
scripts/validate-and-push.ps1
|
|
237
|
+
scripts/validate-and-push.sh
|
|
231
238
|
.github/workflows
|
|
232
239
|
README.md
|
|
233
240
|
"
|
package/package.json
CHANGED
|
@@ -1,29 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "its-magic",
|
|
3
|
-
"version": "0.1.2-
|
|
3
|
+
"version": "0.1.2-16",
|
|
4
4
|
"description": "its-magic - AI dev team workflow for Cursor.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"bin": {
|
|
7
7
|
"its-magic": "bin/its-magic.js"
|
|
8
8
|
},
|
|
9
9
|
"files": [
|
|
10
|
-
"
|
|
11
|
-
".cursor/rules/",
|
|
12
|
-
".cursor/skills/",
|
|
13
|
-
".cursor/agents/",
|
|
14
|
-
".cursor/hooks/",
|
|
15
|
-
".cursor/hooks.json",
|
|
16
|
-
".cursor/scratchpad.md",
|
|
17
|
-
".cursor/scratchpad.local.example.md",
|
|
18
|
-
".github/workflows/",
|
|
19
|
-
"decisions/",
|
|
20
|
-
"docs/",
|
|
21
|
-
"handoffs/",
|
|
22
|
-
"sprints/",
|
|
10
|
+
"template/",
|
|
23
11
|
"installer.ps1",
|
|
24
12
|
"installer.sh",
|
|
25
13
|
"installer.py",
|
|
26
|
-
"README.md",
|
|
27
14
|
"bin/its-magic.js",
|
|
28
15
|
"bin/postinstall.js"
|
|
29
16
|
],
|
|
@@ -34,4 +34,6 @@ description: "its-magic execute: implement tasks with artifacts and state update
|
|
|
34
34
|
7. If `LOOP_UNTIL_GREEN=1`, fix failing tests in small iterations until green,
|
|
35
35
|
or stop and document blockers in `docs/engineering/state.md`.
|
|
36
36
|
8. If `AUTO_PAUSE_REQUEST=1` and boundary rules permit, checkpoint via `/pause`.
|
|
37
|
+
9. When ready to push, suggest `scripts/validate-and-push` to run the full
|
|
38
|
+
quality chain locally before CI.
|
|
37
39
|
|
|
@@ -21,11 +21,15 @@ description: "its-magic QA: test plan, findings, verify fixes."
|
|
|
21
21
|
- Missing test plan coverage
|
|
22
22
|
|
|
23
23
|
## Steps
|
|
24
|
-
1. Define a test plan and run verification
|
|
24
|
+
1. Define a test plan and run verification using the runbook commands
|
|
25
|
+
(`TEST_COMMAND`, `LINT_COMMAND`, `TYPECHECK_COMMAND` in `docs/engineering/runbook.md`).
|
|
25
26
|
2. Record findings and severity.
|
|
26
27
|
3. Update state and handoff to dev if needed.
|
|
27
28
|
4. If `AUTO_IMPLEMENTATION_LOOP=1` and blocking issues exist, handoff to dev and
|
|
28
29
|
return to `/execute` automatically (bounded by `AUTO_LOOP_MAX_CYCLES`).
|
|
29
30
|
5. Follow with `/verify-work` for user acceptance when blocking issues are closed.
|
|
30
31
|
6. If `AUTO_PAUSE_REQUEST=1` at safe boundary, run `/pause` before next phase.
|
|
32
|
+
7. Before pushing, suggest running `scripts/validate-and-push` to catch failures
|
|
33
|
+
locally before they reach CI. If CI fails, the auto-fix job in
|
|
34
|
+
`.github/workflows/ci.yml` attempts automatic lint/format fixes and retries.
|
|
31
35
|
|
|
@@ -4,7 +4,8 @@ globs: ["**/*"]
|
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
- Prefer small, atomic changes tied to a single task.
|
|
7
|
-
- Run tests, lint, and typecheck when commands are configured
|
|
7
|
+
- Run tests, lint, and typecheck when commands are configured in
|
|
8
|
+
`docs/engineering/runbook.md` (`TEST_COMMAND`, `LINT_COMMAND`, `TYPECHECK_COMMAND`).
|
|
8
9
|
- If `AUTO_INSTALL_DEPS=1` in `.cursor/scratchpad.md`, install required
|
|
9
10
|
runtimes/libs without asking (package manager only).
|
|
10
11
|
- If `AUTO_RELEASE_NOTES=1` in `.cursor/scratchpad.md`, generate release notes
|
|
@@ -15,4 +16,11 @@ globs: ["**/*"]
|
|
|
15
16
|
`AUTO_LOOP_MAX_CYCLES` when set.
|
|
16
17
|
- Update `docs/engineering/state.md` after meaningful changes.
|
|
17
18
|
- If tests fail and commands are set, fix or document before proceeding.
|
|
19
|
+
- Quality chain: Cursor AI loop (in-editor) → `scripts/validate-and-push`
|
|
20
|
+
(local pre-push) → CI auto-fix (GitHub Actions). Each layer catches issues
|
|
21
|
+
the previous layer missed.
|
|
22
|
+
- Before pushing, recommend running `scripts/validate-and-push.ps1` (Windows)
|
|
23
|
+
or `scripts/validate-and-push.sh` (Linux/Mac) to catch failures locally.
|
|
24
|
+
- `LINT_FIX_COMMAND` and `FORMAT_COMMAND` in the runbook enable automatic
|
|
25
|
+
formatting/lint fixes both locally and in CI.
|
|
18
26
|
|
|
@@ -4,13 +4,34 @@ on:
|
|
|
4
4
|
push:
|
|
5
5
|
branches: ["main"]
|
|
6
6
|
pull_request:
|
|
7
|
+
workflow_dispatch:
|
|
8
|
+
inputs:
|
|
9
|
+
retry_count:
|
|
10
|
+
description: "Internal retry counter (do not set manually)"
|
|
11
|
+
required: false
|
|
12
|
+
default: "0"
|
|
13
|
+
|
|
14
|
+
permissions:
|
|
15
|
+
contents: write
|
|
16
|
+
pull-requests: write
|
|
17
|
+
|
|
18
|
+
env:
|
|
19
|
+
MAX_CI_RETRIES: 3
|
|
7
20
|
|
|
8
21
|
jobs:
|
|
9
22
|
# ── Unit / structure tests (from runbook) ──────────────────────────
|
|
10
23
|
checks:
|
|
11
24
|
runs-on: ubuntu-latest
|
|
25
|
+
outputs:
|
|
26
|
+
test_result: ${{ steps.test.outcome }}
|
|
27
|
+
lint_result: ${{ steps.lint.outcome }}
|
|
28
|
+
ci_auto_fix: ${{ steps.runbook.outputs.CI_AUTO_FIX }}
|
|
12
29
|
steps:
|
|
13
30
|
- uses: actions/checkout@v4
|
|
31
|
+
with:
|
|
32
|
+
ref: ${{ github.head_ref || github.ref_name }}
|
|
33
|
+
token: ${{ secrets.GITHUB_TOKEN }}
|
|
34
|
+
|
|
14
35
|
- id: runbook
|
|
15
36
|
name: Read runbook commands
|
|
16
37
|
run: |
|
|
@@ -25,7 +46,7 @@ jobs:
|
|
|
25
46
|
if val in ("", "...", "<...>", "TODO"):
|
|
26
47
|
return ""
|
|
27
48
|
return val
|
|
28
|
-
keys = ["TEST_COMMAND", "LINT_COMMAND", "TYPECHECK_COMMAND"]
|
|
49
|
+
keys = ["TEST_COMMAND", "LINT_COMMAND", "TYPECHECK_COMMAND", "CI_AUTO_FIX"]
|
|
29
50
|
with open(os.environ["GITHUB_OUTPUT"], "a") as f:
|
|
30
51
|
for key in keys:
|
|
31
52
|
val = get(key)
|
|
@@ -35,19 +56,134 @@ jobs:
|
|
|
35
56
|
else:
|
|
36
57
|
print(f" {key} = (skipped, empty)")
|
|
37
58
|
PY
|
|
59
|
+
|
|
38
60
|
- name: Test
|
|
61
|
+
id: test
|
|
39
62
|
if: ${{ steps.runbook.outputs.TEST_COMMAND != '' }}
|
|
63
|
+
continue-on-error: true
|
|
40
64
|
run: ${{ steps.runbook.outputs.TEST_COMMAND }}
|
|
65
|
+
|
|
41
66
|
- name: Lint
|
|
67
|
+
id: lint
|
|
42
68
|
if: ${{ steps.runbook.outputs.LINT_COMMAND != '' }}
|
|
69
|
+
continue-on-error: true
|
|
43
70
|
run: ${{ steps.runbook.outputs.LINT_COMMAND }}
|
|
71
|
+
|
|
44
72
|
- name: Typecheck
|
|
73
|
+
id: typecheck
|
|
45
74
|
if: ${{ steps.runbook.outputs.TYPECHECK_COMMAND != '' }}
|
|
46
75
|
run: ${{ steps.runbook.outputs.TYPECHECK_COMMAND }}
|
|
76
|
+
|
|
47
77
|
- name: No commands set
|
|
48
78
|
if: ${{ steps.runbook.outputs.TEST_COMMAND == '' && steps.runbook.outputs.LINT_COMMAND == '' && steps.runbook.outputs.TYPECHECK_COMMAND == '' }}
|
|
49
79
|
run: echo "No runbook commands set."
|
|
50
80
|
|
|
81
|
+
- name: Summarise results
|
|
82
|
+
if: always()
|
|
83
|
+
run: |
|
|
84
|
+
echo "### CI Results" >> "$GITHUB_STEP_SUMMARY"
|
|
85
|
+
echo "| Step | Result |" >> "$GITHUB_STEP_SUMMARY"
|
|
86
|
+
echo "|------|--------|" >> "$GITHUB_STEP_SUMMARY"
|
|
87
|
+
echo "| Test | ${{ steps.test.outcome || 'skipped' }} |" >> "$GITHUB_STEP_SUMMARY"
|
|
88
|
+
echo "| Lint | ${{ steps.lint.outcome || 'skipped' }} |" >> "$GITHUB_STEP_SUMMARY"
|
|
89
|
+
echo "| Typecheck | ${{ steps.typecheck.outcome || 'skipped' }} |" >> "$GITHUB_STEP_SUMMARY"
|
|
90
|
+
|
|
91
|
+
- name: Fail if tests or lint failed
|
|
92
|
+
if: ${{ steps.test.outcome == 'failure' || steps.lint.outcome == 'failure' }}
|
|
93
|
+
run: |
|
|
94
|
+
echo "::error::Tests or lint failed. See summary above."
|
|
95
|
+
exit 1
|
|
96
|
+
|
|
97
|
+
# ── Auto-fix attempt (runs only when checks fail) ─────────────────
|
|
98
|
+
auto-fix:
|
|
99
|
+
needs: checks
|
|
100
|
+
if: ${{ failure() && needs.checks.outputs.ci_auto_fix == 'true' && (github.event_name == 'push' || github.event_name == 'pull_request') }}
|
|
101
|
+
runs-on: ubuntu-latest
|
|
102
|
+
steps:
|
|
103
|
+
- uses: actions/checkout@v4
|
|
104
|
+
with:
|
|
105
|
+
ref: ${{ github.head_ref || github.ref_name }}
|
|
106
|
+
fetch-depth: 0
|
|
107
|
+
token: ${{ secrets.GITHUB_TOKEN }}
|
|
108
|
+
|
|
109
|
+
- name: Determine retry count
|
|
110
|
+
id: retry
|
|
111
|
+
run: |
|
|
112
|
+
COUNT="${{ github.event.inputs.retry_count || '0' }}"
|
|
113
|
+
echo "count=$COUNT" >> "$GITHUB_OUTPUT"
|
|
114
|
+
echo "Current retry: $COUNT / ${{ env.MAX_CI_RETRIES }}"
|
|
115
|
+
if [ "$COUNT" -ge "${{ env.MAX_CI_RETRIES }}" ]; then
|
|
116
|
+
echo "skip=true" >> "$GITHUB_OUTPUT"
|
|
117
|
+
echo "::warning::Max CI retries (${{ env.MAX_CI_RETRIES }}) reached. Manual intervention required."
|
|
118
|
+
else
|
|
119
|
+
echo "skip=false" >> "$GITHUB_OUTPUT"
|
|
120
|
+
fi
|
|
121
|
+
|
|
122
|
+
- name: Read runbook for fix commands
|
|
123
|
+
id: runbook
|
|
124
|
+
if: ${{ steps.retry.outputs.skip != 'true' }}
|
|
125
|
+
run: |
|
|
126
|
+
python3 - <<'PY'
|
|
127
|
+
import os, pathlib, re
|
|
128
|
+
text = pathlib.Path("docs/engineering/runbook.md").read_text(encoding="utf-8")
|
|
129
|
+
def get(key):
|
|
130
|
+
m = re.search(rf"^{key}:\s*(.+)$", text, re.M)
|
|
131
|
+
if not m:
|
|
132
|
+
return ""
|
|
133
|
+
val = m.group(1).strip()
|
|
134
|
+
if val in ("", "...", "<...>", "TODO"):
|
|
135
|
+
return ""
|
|
136
|
+
return val
|
|
137
|
+
keys = ["LINT_FIX_COMMAND", "FORMAT_COMMAND"]
|
|
138
|
+
with open(os.environ["GITHUB_OUTPUT"], "a") as f:
|
|
139
|
+
for key in keys:
|
|
140
|
+
val = get(key)
|
|
141
|
+
if val:
|
|
142
|
+
f.write(f"{key}={val}\n")
|
|
143
|
+
print(f" {key} = '{val}'")
|
|
144
|
+
else:
|
|
145
|
+
print(f" {key} = (not set)")
|
|
146
|
+
PY
|
|
147
|
+
|
|
148
|
+
- name: Run lint auto-fix
|
|
149
|
+
if: ${{ steps.retry.outputs.skip != 'true' && steps.runbook.outputs.LINT_FIX_COMMAND != '' }}
|
|
150
|
+
continue-on-error: true
|
|
151
|
+
run: ${{ steps.runbook.outputs.LINT_FIX_COMMAND }}
|
|
152
|
+
|
|
153
|
+
- name: Run formatter
|
|
154
|
+
if: ${{ steps.retry.outputs.skip != 'true' && steps.runbook.outputs.FORMAT_COMMAND != '' }}
|
|
155
|
+
continue-on-error: true
|
|
156
|
+
run: ${{ steps.runbook.outputs.FORMAT_COMMAND }}
|
|
157
|
+
|
|
158
|
+
- name: Check for changes and commit
|
|
159
|
+
if: ${{ steps.retry.outputs.skip != 'true' }}
|
|
160
|
+
id: commit
|
|
161
|
+
run: |
|
|
162
|
+
git config user.name "its-magic[bot]"
|
|
163
|
+
git config user.email "its-magic[bot]@users.noreply.github.com"
|
|
164
|
+
if git diff --quiet && git diff --cached --quiet; then
|
|
165
|
+
echo "No auto-fix changes to commit."
|
|
166
|
+
echo "has_changes=false" >> "$GITHUB_OUTPUT"
|
|
167
|
+
else
|
|
168
|
+
NEXT=$(( ${{ steps.retry.outputs.count }} + 1 ))
|
|
169
|
+
git add -A
|
|
170
|
+
git commit -m "ci: auto-fix attempt $NEXT/${{ env.MAX_CI_RETRIES }}"
|
|
171
|
+
git push
|
|
172
|
+
echo "has_changes=true" >> "$GITHUB_OUTPUT"
|
|
173
|
+
echo "Fixes committed and pushed. CI will re-run automatically."
|
|
174
|
+
fi
|
|
175
|
+
|
|
176
|
+
- name: Post failure summary
|
|
177
|
+
if: ${{ steps.retry.outputs.skip == 'true' || steps.commit.outputs.has_changes == 'false' }}
|
|
178
|
+
run: |
|
|
179
|
+
echo "::error::CI failed and auto-fix could not resolve it."
|
|
180
|
+
echo ""
|
|
181
|
+
echo "What to do next:"
|
|
182
|
+
echo " 1. Pull the latest changes"
|
|
183
|
+
echo " 2. Run: sh scripts/validate-and-push.sh"
|
|
184
|
+
echo " (or: powershell scripts/validate-and-push.ps1)"
|
|
185
|
+
echo " 3. Fix failures, the script loops until green, then pushes."
|
|
186
|
+
|
|
51
187
|
# ── npm package test (Ubuntu) ──────────────────────────────────────
|
|
52
188
|
npm-test:
|
|
53
189
|
runs-on: ubuntu-latest
|
|
@@ -160,4 +296,3 @@ jobs:
|
|
|
160
296
|
}
|
|
161
297
|
Remove-Item -Recurse -Force $testDir
|
|
162
298
|
exit $fail
|
|
163
|
-
|