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.
Files changed (92) hide show
  1. package/README.md +116 -11
  2. package/installer.ps1 +8 -2
  3. package/installer.py +6 -2
  4. package/installer.sh +9 -2
  5. package/package.json +2 -15
  6. package/{.cursor → template/.cursor}/commands/execute.md +2 -0
  7. package/{.cursor → template/.cursor}/commands/qa.md +5 -1
  8. package/{.cursor → template/.cursor}/rules/quality.mdc +9 -1
  9. package/{.github → template/.github}/workflows/ci.yml +137 -2
  10. package/template/README.md +636 -0
  11. package/template/docs/engineering/runbook.md +41 -0
  12. package/template/scripts/validate-and-push.ps1 +165 -0
  13. package/template/scripts/validate-and-push.sh +148 -0
  14. package/docs/engineering/runbook.md +0 -32
  15. /package/{.cursor → template/.cursor}/agents/curator.mdc +0 -0
  16. /package/{.cursor → template/.cursor}/agents/dev.mdc +0 -0
  17. /package/{.cursor → template/.cursor}/agents/po.mdc +0 -0
  18. /package/{.cursor → template/.cursor}/agents/qa.mdc +0 -0
  19. /package/{.cursor → template/.cursor}/agents/release.mdc +0 -0
  20. /package/{.cursor → template/.cursor}/agents/tech-lead.mdc +0 -0
  21. /package/{.cursor → template/.cursor}/commands/architecture.md +0 -0
  22. /package/{.cursor → template/.cursor}/commands/auto.md +0 -0
  23. /package/{.cursor → template/.cursor}/commands/discovery.md +0 -0
  24. /package/{.cursor → template/.cursor}/commands/intake.md +0 -0
  25. /package/{.cursor → template/.cursor}/commands/map-codebase.md +0 -0
  26. /package/{.cursor → template/.cursor}/commands/milestone-complete.md +0 -0
  27. /package/{.cursor → template/.cursor}/commands/milestone-start.md +0 -0
  28. /package/{.cursor → template/.cursor}/commands/pause.md +0 -0
  29. /package/{.cursor → template/.cursor}/commands/phase-context.md +0 -0
  30. /package/{.cursor → template/.cursor}/commands/plan-verify.md +0 -0
  31. /package/{.cursor → template/.cursor}/commands/quick.md +0 -0
  32. /package/{.cursor → template/.cursor}/commands/refresh-context.md +0 -0
  33. /package/{.cursor → template/.cursor}/commands/release.md +0 -0
  34. /package/{.cursor → template/.cursor}/commands/research.md +0 -0
  35. /package/{.cursor → template/.cursor}/commands/resume.md +0 -0
  36. /package/{.cursor → template/.cursor}/commands/sprint-plan.md +0 -0
  37. /package/{.cursor → template/.cursor}/commands/verify-work.md +0 -0
  38. /package/{.cursor → template/.cursor}/hooks/README.md +0 -0
  39. /package/{.cursor → template/.cursor}/hooks/hook.py +0 -0
  40. /package/{.cursor → template/.cursor}/hooks.json +0 -0
  41. /package/{.cursor → template/.cursor}/rules/coding-standards.mdc +0 -0
  42. /package/{.cursor → template/.cursor}/rules/core.mdc +0 -0
  43. /package/{.cursor → template/.cursor}/rules/escalation.mdc +0 -0
  44. /package/{.cursor → template/.cursor}/rules/handoffs.mdc +0 -0
  45. /package/{.cursor → template/.cursor}/scratchpad.local.example.md +0 -0
  46. /package/{.cursor → template/.cursor}/scratchpad.md +0 -0
  47. /package/{.cursor → template/.cursor}/skills/its-magic/SKILL.md +0 -0
  48. /package/{.cursor → template/.cursor}/skills/its-magic/templates/acceptance.json +0 -0
  49. /package/{.cursor → template/.cursor}/skills/its-magic/templates/acceptance.md +0 -0
  50. /package/{.cursor → template/.cursor}/skills/its-magic/templates/architecture.json +0 -0
  51. /package/{.cursor → template/.cursor}/skills/its-magic/templates/architecture.md +0 -0
  52. /package/{.cursor → template/.cursor}/skills/its-magic/templates/decision.json +0 -0
  53. /package/{.cursor → template/.cursor}/skills/its-magic/templates/decision.md +0 -0
  54. /package/{.cursor → template/.cursor}/skills/its-magic/templates/handoff.json +0 -0
  55. /package/{.cursor → template/.cursor}/skills/its-magic/templates/handoff.md +0 -0
  56. /package/{.cursor → template/.cursor}/skills/its-magic/templates/milestone.json +0 -0
  57. /package/{.cursor → template/.cursor}/skills/its-magic/templates/phase-context.json +0 -0
  58. /package/{.cursor → template/.cursor}/skills/its-magic/templates/plan-verify.json +0 -0
  59. /package/{.cursor → template/.cursor}/skills/its-magic/templates/sprint.json +0 -0
  60. /package/{.cursor → template/.cursor}/skills/its-magic/templates/sprint.md +0 -0
  61. /package/{.cursor → template/.cursor}/skills/its-magic/templates/story.json +0 -0
  62. /package/{.cursor → template/.cursor}/skills/its-magic/templates/story.md +0 -0
  63. /package/{.cursor → template/.cursor}/skills/its-magic/templates/uat.json +0 -0
  64. /package/{.github → template/.github}/workflows/deploy.yml +0 -0
  65. /package/{decisions → template/decisions}/DEC-0001.md +0 -0
  66. /package/{decisions → template/decisions}/DEC-0002.md +0 -0
  67. /package/{docs → template/docs}/engineering/architecture.md +0 -0
  68. /package/{docs → template/docs}/engineering/codebase-map.md +0 -0
  69. /package/{docs → template/docs}/engineering/context/phase-template.json +0 -0
  70. /package/{docs → template/docs}/engineering/decisions.md +0 -0
  71. /package/{docs → template/docs}/engineering/dependencies.json +0 -0
  72. /package/{docs → template/docs}/engineering/research.md +0 -0
  73. /package/{docs → template/docs}/engineering/state.md +0 -0
  74. /package/{docs → template/docs}/product/acceptance.md +0 -0
  75. /package/{docs → template/docs}/product/backlog.md +0 -0
  76. /package/{docs → template/docs}/product/vision.md +0 -0
  77. /package/{handoffs → template/handoffs}/dev_to_qa.md +0 -0
  78. /package/{handoffs → template/handoffs}/po_to_tl.md +0 -0
  79. /package/{handoffs → template/handoffs}/qa_to_dev.md +0 -0
  80. /package/{handoffs → template/handoffs}/release_notes.md +0 -0
  81. /package/{handoffs → template/handoffs}/resume_brief.md +0 -0
  82. /package/{handoffs → template/handoffs}/tl_to_dev.md +0 -0
  83. /package/{sprints → template/sprints}/S0001/plan-verify.json +0 -0
  84. /package/{sprints → template/sprints}/S0001/progress.md +0 -0
  85. /package/{sprints → template/sprints}/S0001/qa-findings.md +0 -0
  86. /package/{sprints → template/sprints}/S0001/sprint.md +0 -0
  87. /package/{sprints → template/sprints}/S0001/summary.md +0 -0
  88. /package/{sprints → template/sprints}/S0001/tasks.md +0 -0
  89. /package/{sprints → template/sprints}/S0001/uat.json +0 -0
  90. /package/{sprints → template/sprints}/S0001/uat.md +0 -0
  91. /package/{sprints → template/sprints}/quick/Q0001/summary.md +0 -0
  92. /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
- .github/workflows/
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
- $sourceRoot = Normalize-PathSafe (Split-Path -Parent $MyInvocation.MyCommand.Path)
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 $sourceRoot
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
- source_root = normalize(os.path.dirname(__file__))
164
- version = read_version(source_root)
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
- SOURCE_ROOT=$(cd "$(dirname "$0")" && pwd)
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' "$SOURCE_ROOT/package.json" | head -n 1)
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-15",
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
- ".cursor/commands/",
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
-