@ui8kit/uxdx 0.1.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.
Files changed (49) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +38 -0
  3. package/dist/uxdx.mjs +296 -0
  4. package/package.json +53 -0
  5. package/packages/sdlc/harness/README.md +58 -0
  6. package/packages/sdlc/harness/core/AGENTS.md +12 -0
  7. package/packages/sdlc/harness/core/README.md +15 -0
  8. package/packages/sdlc/harness/core/REVIEW.md +18 -0
  9. package/packages/sdlc/harness/core/changes/README.md +4 -0
  10. package/packages/sdlc/harness/core/cursor/rules/sdlc-core.mdc +15 -0
  11. package/packages/sdlc/harness/core/install.sh +3 -0
  12. package/packages/sdlc/harness/core/intent.md +25 -0
  13. package/packages/sdlc/harness/core/manifest.yaml +15 -0
  14. package/packages/sdlc/harness/core/plan.md +25 -0
  15. package/packages/sdlc/harness/core/sdlc.sh +230 -0
  16. package/packages/sdlc/harness/core/skills/sdlc-invariants/SKILL.md +20 -0
  17. package/packages/sdlc/harness/core/skills/sdlc-plan/SKILL.md +15 -0
  18. package/packages/sdlc/harness/core/spec.md +24 -0
  19. package/packages/sdlc/harness/core/start.sh +3 -0
  20. package/packages/sdlc/harness/full/AGENTS.md +15 -0
  21. package/packages/sdlc/harness/full/README.md +23 -0
  22. package/packages/sdlc/harness/full/REVIEW.md +19 -0
  23. package/packages/sdlc/harness/full/bands.yaml +13 -0
  24. package/packages/sdlc/harness/full/changes/README.md +4 -0
  25. package/packages/sdlc/harness/full/cursor/rules/sdlc-full.mdc +17 -0
  26. package/packages/sdlc/harness/full/evals/README.md +16 -0
  27. package/packages/sdlc/harness/full/hooks/README.md +17 -0
  28. package/packages/sdlc/harness/full/install.sh +3 -0
  29. package/packages/sdlc/harness/full/intent.md +26 -0
  30. package/packages/sdlc/harness/full/manifest.yaml +17 -0
  31. package/packages/sdlc/harness/full/plan.md +30 -0
  32. package/packages/sdlc/harness/full/sdlc.sh +230 -0
  33. package/packages/sdlc/harness/full/skills/sdlc-feedback/SKILL.md +18 -0
  34. package/packages/sdlc/harness/full/skills/sdlc-loop/SKILL.md +21 -0
  35. package/packages/sdlc/harness/full/skills/sdlc-review/SKILL.md +15 -0
  36. package/packages/sdlc/harness/full/spec.md +28 -0
  37. package/packages/sdlc/harness/full/start.sh +3 -0
  38. package/packages/sdlc/harness/light/AGENTS.md +13 -0
  39. package/packages/sdlc/harness/light/README.md +15 -0
  40. package/packages/sdlc/harness/light/changes/README.md +6 -0
  41. package/packages/sdlc/harness/light/cursor/rules/sdlc-light.mdc +16 -0
  42. package/packages/sdlc/harness/light/install.sh +3 -0
  43. package/packages/sdlc/harness/light/intent.md +24 -0
  44. package/packages/sdlc/harness/light/manifest.yaml +15 -0
  45. package/packages/sdlc/harness/light/plan.md +28 -0
  46. package/packages/sdlc/harness/light/sdlc.sh +230 -0
  47. package/packages/sdlc/harness/light/skills/sdlc-light/SKILL.md +30 -0
  48. package/packages/sdlc/harness/light/spec.md +23 -0
  49. package/packages/sdlc/harness/light/start.sh +3 -0
@@ -0,0 +1,230 @@
1
+ #!/usr/bin/env bash
2
+ # Run from a product repo after this pack is copied to .sdlc/
3
+ # .sdlc/sdlc.sh install
4
+ # .sdlc/sdlc.sh start <slug>
5
+ # .sdlc/sdlc.sh status
6
+ set -euo pipefail
7
+
8
+ SDLC_DIR="$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)"
9
+ REPO_ROOT="$(CDPATH= cd -- "$SDLC_DIR/.." && pwd)"
10
+ MANIFEST="$SDLC_DIR/manifest.yaml"
11
+ BEGIN='<!-- sdlc-harness:begin -->'
12
+ END='<!-- sdlc-harness:end -->'
13
+
14
+ die() { printf 'sdlc: %s\n' "$*" >&2; exit 1; }
15
+
16
+ need_manifest() {
17
+ [[ -f "$MANIFEST" ]] || die "missing $MANIFEST (copy a harness pack into .sdlc/)"
18
+ }
19
+
20
+ level() {
21
+ awk '/^level:[[:space:]]/ { print $2; exit }' "$MANIFEST"
22
+ }
23
+
24
+ warn_location() {
25
+ local base
26
+ base="$(basename -- "$SDLC_DIR")"
27
+ if [[ "$base" != ".sdlc" ]]; then
28
+ printf 'sdlc: warning: script dir is %s (expected .sdlc in a product repo)\n' "$SDLC_DIR" >&2
29
+ fi
30
+ }
31
+
32
+ usage() {
33
+ cat <<EOF
34
+ Usage: .sdlc/sdlc.sh <command>
35
+
36
+ install Overlay Cursor rules/skills; merge AGENTS.md at repo root
37
+ start <slug> [opts] Copy templates into .sdlc/changes/<slug>/
38
+ status Show level, overlay, and changes
39
+
40
+ start options:
41
+ --force Overwrite files that already exist in the change folder
42
+ --intent Also copy intent.md (light/core; full always copies it)
43
+ --spec Also copy spec.md (light; core/full as below)
44
+ --boundary core: copy intent.md + spec.md + plan.md
45
+
46
+ Repo root is the parent of this .sdlc directory:
47
+ $REPO_ROOT
48
+ EOF
49
+ }
50
+
51
+ copy_rules() {
52
+ local src="$SDLC_DIR/cursor/rules" dest="$REPO_ROOT/.cursor/rules" f
53
+ [[ -d "$src" ]] || die "missing $src"
54
+ mkdir -p "$dest"
55
+ shopt -s nullglob
56
+ for f in "$src"/*.mdc; do
57
+ cp -- "$f" "$dest/"
58
+ printf 'sdlc: rule %s\n' "$dest/$(basename -- "$f")"
59
+ done
60
+ shopt -u nullglob
61
+ }
62
+
63
+ copy_skills() {
64
+ local src="$SDLC_DIR/skills" dest="$REPO_ROOT/.cursor/skills" d name
65
+ [[ -d "$src" ]] || die "missing $src"
66
+ mkdir -p "$dest"
67
+ shopt -s nullglob
68
+ for d in "$src"/*/; do
69
+ name="$(basename -- "$d")"
70
+ rm -rf -- "$dest/$name"
71
+ cp -R -- "$d" "$dest/$name"
72
+ printf 'sdlc: skill %s\n' "$dest/$name"
73
+ done
74
+ shopt -u nullglob
75
+ }
76
+
77
+ merge_agents() {
78
+ local overlay="$SDLC_DIR/AGENTS.md" dest="$REPO_ROOT/AGENTS.md" tmp
79
+ [[ -f "$overlay" ]] || die "missing $overlay"
80
+ if [[ ! -f "$dest" ]]; then
81
+ cp -- "$overlay" "$dest"
82
+ printf 'sdlc: created %s\n' "$dest"
83
+ return
84
+ fi
85
+ tmp="$(mktemp)"
86
+ if grep -F -q -- "$BEGIN" "$dest"; then
87
+ awk -v begin="$BEGIN" -v end="$END" -v overlay="$overlay" '
88
+ BEGIN {
89
+ while ((getline line < overlay) > 0) { o = o line "\n" }
90
+ close(overlay)
91
+ }
92
+ index($0, begin) == 1 {
93
+ printf "%s", o
94
+ skip = 1
95
+ next
96
+ }
97
+ skip {
98
+ if (index($0, end) == 1) skip = 0
99
+ next
100
+ }
101
+ { print }
102
+ ' "$dest" >"$tmp"
103
+ else
104
+ cat -- "$dest" >"$tmp"
105
+ printf '\n' >>"$tmp"
106
+ cat -- "$overlay" >>"$tmp"
107
+ fi
108
+ mv -- "$tmp" "$dest"
109
+ printf 'sdlc: merged overlay into %s\n' "$dest"
110
+ }
111
+
112
+ cmd_install() {
113
+ need_manifest
114
+ warn_location
115
+ copy_rules
116
+ copy_skills
117
+ merge_agents
118
+ printf 'sdlc: install done (level %s) repo %s\n' "$(level)" "$REPO_ROOT"
119
+ }
120
+
121
+ valid_slug() {
122
+ [[ "$1" =~ ^[a-z0-9][a-z0-9-]*$ ]] || die "slug must be kebab-case: $1"
123
+ }
124
+
125
+ copy_template() {
126
+ local src="$SDLC_DIR/$1" dest="$2/$1"
127
+ [[ -f "$src" ]] || die "missing template $src"
128
+ if [[ -e "$dest" && "$FORCE" -eq 0 ]]; then
129
+ printf 'sdlc: skip existing %s (use --force)\n' "$dest"
130
+ return
131
+ fi
132
+ cp -- "$src" "$dest"
133
+ printf 'sdlc: %s\n' "$dest"
134
+ }
135
+
136
+ cmd_start() {
137
+ need_manifest
138
+ warn_location
139
+ local slug="" with_intent=0 with_spec=0 boundary=0
140
+ FORCE=0
141
+ while [[ $# -gt 0 ]]; do
142
+ case "$1" in
143
+ --force) FORCE=1; shift ;;
144
+ --intent) with_intent=1; shift ;;
145
+ --spec) with_spec=1; shift ;;
146
+ --boundary) boundary=1; shift ;;
147
+ --help|-h) usage; exit 0 ;;
148
+ -*) die "unknown option $1" ;;
149
+ *)
150
+ [[ -z "$slug" ]] || die "unexpected argument $1"
151
+ slug="$1"
152
+ shift
153
+ ;;
154
+ esac
155
+ done
156
+ [[ -n "$slug" ]] || die "start requires <slug>"
157
+ valid_slug "$slug"
158
+
159
+ local lvl dest
160
+ lvl="$(level)"
161
+ dest="$SDLC_DIR/changes/$slug"
162
+ mkdir -p "$dest"
163
+
164
+ case "$lvl" in
165
+ light)
166
+ copy_template plan.md "$dest"
167
+ [[ "$with_intent" -eq 1 ]] && copy_template intent.md "$dest"
168
+ [[ "$with_spec" -eq 1 ]] && copy_template spec.md "$dest"
169
+ ;;
170
+ core)
171
+ copy_template plan.md "$dest"
172
+ if [[ "$boundary" -eq 1 || "$with_intent" -eq 1 || "$with_spec" -eq 1 ]]; then
173
+ copy_template intent.md "$dest"
174
+ copy_template spec.md "$dest"
175
+ fi
176
+ ;;
177
+ full)
178
+ copy_template intent.md "$dest"
179
+ copy_template spec.md "$dest"
180
+ copy_template plan.md "$dest"
181
+ ;;
182
+ *)
183
+ die "unknown level in manifest: $lvl"
184
+ ;;
185
+ esac
186
+ printf 'sdlc: change %s ready under %s\n' "$slug" "$dest"
187
+ }
188
+
189
+ cmd_status() {
190
+ need_manifest
191
+ warn_location
192
+ local lvl
193
+ lvl="$(level)"
194
+ printf 'level: %s\n' "$lvl"
195
+ printf 'sdlc dir: %s\n' "$SDLC_DIR"
196
+ printf 'repo: %s\n' "$REPO_ROOT"
197
+ if [[ -f "$REPO_ROOT/AGENTS.md" ]] && grep -F -q -- "$BEGIN" "$REPO_ROOT/AGENTS.md"; then
198
+ printf 'AGENTS.md: overlay present\n'
199
+ else
200
+ printf 'AGENTS.md: overlay missing (run install)\n'
201
+ fi
202
+ if [[ -d "$REPO_ROOT/.cursor/rules" ]]; then
203
+ printf 'rules:\n'
204
+ ls -1 "$REPO_ROOT/.cursor/rules"/sdlc-*.mdc 2>/dev/null || printf ' (no sdlc-*.mdc)\n'
205
+ fi
206
+ printf 'changes:\n'
207
+ if [[ -d "$SDLC_DIR/changes" ]]; then
208
+ local d
209
+ shopt -s nullglob
210
+ for d in "$SDLC_DIR/changes"/*/; do
211
+ printf ' %s\n' "$(basename -- "$d")"
212
+ done
213
+ shopt -u nullglob
214
+ fi
215
+ }
216
+
217
+ main() {
218
+ local cmd="${1:-}"
219
+ [[ -n "$cmd" ]] || { usage; exit 1; }
220
+ shift || true
221
+ case "$cmd" in
222
+ install) cmd_install "$@" ;;
223
+ start) cmd_start "$@" ;;
224
+ status) cmd_status "$@" ;;
225
+ -h|--help|help) usage ;;
226
+ *) die "unknown command $cmd" ;;
227
+ esac
228
+ }
229
+
230
+ main "$@"
@@ -0,0 +1,18 @@
1
+ ---
2
+ name: sdlc-feedback
3
+ description: >-
4
+ Verify agent work with the repo's existing commands before claiming done.
5
+ Use in full-harness repos after implementation. Do not weaken tests to
6
+ make a task pass.
7
+ ---
8
+
9
+ # Feedback loop
10
+
11
+ 1. Read verify commands from root `AGENTS.md` (or Makefile / package.json
12
+ scripts named there).
13
+ 2. Run them. Paste or summarize the outcome.
14
+ 3. If tests fail, fix production code, not the test, unless the change is the
15
+ test and `plan.md` says so.
16
+ 4. For UI, use the repo's existing check (`bun run check`, screenshot, or
17
+ documented browser step). Do not add Playwright as a default gate.
18
+ 5. Never report complete without the proof named in `plan.md`.
@@ -0,0 +1,21 @@
1
+ ---
2
+ name: sdlc-loop
3
+ description: >-
4
+ Full AI-native SDLC loop for a solo developer. Use when working in a repo
5
+ with .sdlc/manifest.yaml level full. Drive intent.md, spec.md, plan.md in
6
+ order; do not skip to code on a non-trivial change.
7
+ ---
8
+
9
+ # Full SDLC loop
10
+
11
+ 1. Write or update `.sdlc/changes/<slug>/intent.md`. The human corrects it
12
+ before spec.
13
+ 2. Produce `spec.md` from the intent. Flag policy conflicts. Do not start
14
+ plan mode until the human accepts the spec (or explicitly says "small,
15
+ proceed").
16
+ 3. Plan mode: `plan.md` with files, order, risks, proof. No file edits until
17
+ the plan is accepted.
18
+ 4. Implement in one pass when the plan is tight. Update `plan.md` if you
19
+ diverge.
20
+ 5. Hand off to skill `sdlc-feedback` before reporting done.
21
+ 6. PR text points at the three artifacts. Review uses `sdlc-review`.
@@ -0,0 +1,15 @@
1
+ ---
2
+ name: sdlc-review
3
+ description: >-
4
+ Review a PR against .sdlc/REVIEW.md, spec.md, and plan.md in a full-harness
5
+ repo. Use when reviewing diffs or addressing review comments.
6
+ ---
7
+
8
+ # PR review (full)
9
+
10
+ 1. Read `.sdlc/REVIEW.md` and the change's `spec.md` / `plan.md`.
11
+ 2. Three passes: bugs, security, compliance with the artifacts.
12
+ 3. Findings do not merge the PR. A human approves.
13
+ 4. If the same mistake appears twice, add a line to product `AGENTS.md` or a
14
+ skill — not a one-off comment that will rot.
15
+ 5. The agent that wrote the diff must not be treated as the approver.
@@ -0,0 +1,28 @@
1
+ # Spec: <!-- title from intent -->
2
+
3
+ Intent: `.sdlc/changes/<slug>/intent.md`
4
+ Status: draft
5
+
6
+ ## Requirements
7
+
8
+ What the change must do. Trace each item to the intent.
9
+
10
+ ## Design
11
+
12
+ How it fits the existing codebase. Name surfaces (API, admin, BFF, host) —
13
+ do not invent a new plane.
14
+
15
+ ## Skills and policies applied
16
+
17
+ Brand, security, UX, stack-contract — list the files that constrained this
18
+ spec (`AGENTS.md`, `.cursor/rules/*`, `.project/adr/*`).
19
+
20
+ ## Areas of concern
21
+
22
+ Where policies conflict or evidence is missing. A human resolves each item
23
+ before plan mode. Do not leave a red flag for engineering to discover in the
24
+ diff.
25
+
26
+ ## Open questions carried forward
27
+
28
+ Questions from `intent.md` that remain, with an owner.
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ exec "$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)/sdlc.sh" start "$@"
@@ -0,0 +1,13 @@
1
+ <!-- sdlc-harness:begin -->
2
+ ## SDLC harness (light)
3
+
4
+ This repo uses the **light** pack from `hinddy/brainstorm` (`.harness-sdlc/light`).
5
+ Working copy: `.sdlc/`. Per-change artifacts: `.sdlc/changes/<slug>/`.
6
+
7
+ - Non-trivial work starts in plan mode. Commit `plan.md` before a large diff.
8
+ - `spec.md` is optional. Do not start a requirements theatre for a one-file fix.
9
+ - Verify with this repository's existing commands, listed in this `AGENTS.md`
10
+ or `.project/`. Do not add Playwright or extra browser suites as a default gate.
11
+ - Overlay rules: `.cursor/rules/sdlc-light.mdc`. Overlay skill: `sdlc-light`.
12
+ - Product rules and this file's stack contract win over the harness if they conflict.
13
+ <!-- sdlc-harness:end -->
@@ -0,0 +1,15 @@
1
+ # Light harness
2
+
3
+ For product repos that already have a contract (`AGENTS.md`, stack rules, tests).
4
+ Examples: AlexY OS, GitCourse, Lab, BrandOSS, ui8px-cli, FastyGo UI.
5
+
6
+ Copy this directory to `<repo>/.sdlc/`. Then overlay Cursor files — see
7
+ `../README.md`.
8
+
9
+ ## Per change
10
+
11
+ 1. `mkdir -p .sdlc/changes/<slug>`
12
+ 2. Copy `plan.md` into that folder and fill it.
13
+ 3. Add `intent.md` only if the origin is unclear (incident, external request).
14
+ 4. Skip `spec.md` unless you are changing published IA or a user-visible contract.
15
+ 5. Run the repo's existing verify command before calling the task done.
@@ -0,0 +1,6 @@
1
+ # changes/
2
+
3
+ One directory per change: copy `../intent.md` (optional), `../plan.md` (required)
4
+ from the pack root into `./<slug>/` and fill them.
5
+
6
+ Do not edit the pack-root templates in place once a real change is in flight.
@@ -0,0 +1,16 @@
1
+ ---
2
+ description: Light SDLC — plan.md and existing verify gates; no spec theatre
3
+ alwaysApply: true
4
+ ---
5
+
6
+ # Light SDLC overlay
7
+
8
+ This repository opted into `.sdlc` harness **light**.
9
+
10
+ - Put change artifacts under `.sdlc/changes/<slug>/`.
11
+ - Require `plan.md` for non-trivial diffs. `intent.md` is optional. Skip
12
+ `spec.md` unless published IA or a public contract changes.
13
+ - After code changes, run the verify command already documented in `AGENTS.md`.
14
+ - Do not add eval suites, managed hooks, or autonomous maintain loops.
15
+ - If this overlay disagrees with stack-contract or product `AGENTS.md`, the
16
+ product files win.
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ exec "$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)/sdlc.sh" install "$@"
@@ -0,0 +1,24 @@
1
+ # Intent (light — optional)
2
+
3
+ Author: <!-- name -->
4
+ Status: draft
5
+ Slug: <!-- kebab-case, matches .sdlc/changes/<slug>/ -->
6
+
7
+ ## Problem
8
+
9
+ What cannot be done today, in the originator's words.
10
+
11
+ ## Proposed outcome
12
+
13
+ What better looks like. One paragraph.
14
+
15
+ ## Out of scope
16
+
17
+ What this change will not do.
18
+
19
+ ## Constraints
20
+
21
+ Repo `AGENTS.md` and existing `.cursor/rules` stay in force. Do not invent
22
+ kinds, routes, or a second API.
23
+
24
+ ## Open questions
@@ -0,0 +1,15 @@
1
+ level: light
2
+ version: 1
3
+ copy_to: .sdlc
4
+ cursor:
5
+ rules: cursor/rules
6
+ skills: skills
7
+ agents_md:
8
+ overlay: AGENTS.md
9
+ merge_markers:
10
+ begin: "<!-- sdlc-harness:begin -->"
11
+ end: "<!-- sdlc-harness:end -->"
12
+ artifacts:
13
+ required: [plan.md]
14
+ optional: [intent.md]
15
+ skip_by_default: [spec.md]
@@ -0,0 +1,28 @@
1
+ # Plan (light)
2
+
3
+ From: <!-- intent/spec slug or issue URL -->
4
+ Status: draft
5
+ Author: <!-- engineer -->
6
+
7
+ ## Files that change
8
+
9
+ -
10
+
11
+ ## Order of work
12
+
13
+ 1.
14
+ 2.
15
+
16
+ ## Tests / proof
17
+
18
+ Name the existing command that must pass (`make verify`, `bun run validate`,
19
+ `go test ./...`, or the repo's documented gate). Do not invent a parallel suite.
20
+
21
+ ## Risks
22
+
23
+ -
24
+
25
+ ## Non-goals
26
+
27
+ Do not expand product IA, add a second REST shape, or edit pinned modules to
28
+ ship this change.
@@ -0,0 +1,230 @@
1
+ #!/usr/bin/env bash
2
+ # Run from a product repo after this pack is copied to .sdlc/
3
+ # .sdlc/sdlc.sh install
4
+ # .sdlc/sdlc.sh start <slug>
5
+ # .sdlc/sdlc.sh status
6
+ set -euo pipefail
7
+
8
+ SDLC_DIR="$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)"
9
+ REPO_ROOT="$(CDPATH= cd -- "$SDLC_DIR/.." && pwd)"
10
+ MANIFEST="$SDLC_DIR/manifest.yaml"
11
+ BEGIN='<!-- sdlc-harness:begin -->'
12
+ END='<!-- sdlc-harness:end -->'
13
+
14
+ die() { printf 'sdlc: %s\n' "$*" >&2; exit 1; }
15
+
16
+ need_manifest() {
17
+ [[ -f "$MANIFEST" ]] || die "missing $MANIFEST (copy a harness pack into .sdlc/)"
18
+ }
19
+
20
+ level() {
21
+ awk '/^level:[[:space:]]/ { print $2; exit }' "$MANIFEST"
22
+ }
23
+
24
+ warn_location() {
25
+ local base
26
+ base="$(basename -- "$SDLC_DIR")"
27
+ if [[ "$base" != ".sdlc" ]]; then
28
+ printf 'sdlc: warning: script dir is %s (expected .sdlc in a product repo)\n' "$SDLC_DIR" >&2
29
+ fi
30
+ }
31
+
32
+ usage() {
33
+ cat <<EOF
34
+ Usage: .sdlc/sdlc.sh <command>
35
+
36
+ install Overlay Cursor rules/skills; merge AGENTS.md at repo root
37
+ start <slug> [opts] Copy templates into .sdlc/changes/<slug>/
38
+ status Show level, overlay, and changes
39
+
40
+ start options:
41
+ --force Overwrite files that already exist in the change folder
42
+ --intent Also copy intent.md (light/core; full always copies it)
43
+ --spec Also copy spec.md (light; core/full as below)
44
+ --boundary core: copy intent.md + spec.md + plan.md
45
+
46
+ Repo root is the parent of this .sdlc directory:
47
+ $REPO_ROOT
48
+ EOF
49
+ }
50
+
51
+ copy_rules() {
52
+ local src="$SDLC_DIR/cursor/rules" dest="$REPO_ROOT/.cursor/rules" f
53
+ [[ -d "$src" ]] || die "missing $src"
54
+ mkdir -p "$dest"
55
+ shopt -s nullglob
56
+ for f in "$src"/*.mdc; do
57
+ cp -- "$f" "$dest/"
58
+ printf 'sdlc: rule %s\n' "$dest/$(basename -- "$f")"
59
+ done
60
+ shopt -u nullglob
61
+ }
62
+
63
+ copy_skills() {
64
+ local src="$SDLC_DIR/skills" dest="$REPO_ROOT/.cursor/skills" d name
65
+ [[ -d "$src" ]] || die "missing $src"
66
+ mkdir -p "$dest"
67
+ shopt -s nullglob
68
+ for d in "$src"/*/; do
69
+ name="$(basename -- "$d")"
70
+ rm -rf -- "$dest/$name"
71
+ cp -R -- "$d" "$dest/$name"
72
+ printf 'sdlc: skill %s\n' "$dest/$name"
73
+ done
74
+ shopt -u nullglob
75
+ }
76
+
77
+ merge_agents() {
78
+ local overlay="$SDLC_DIR/AGENTS.md" dest="$REPO_ROOT/AGENTS.md" tmp
79
+ [[ -f "$overlay" ]] || die "missing $overlay"
80
+ if [[ ! -f "$dest" ]]; then
81
+ cp -- "$overlay" "$dest"
82
+ printf 'sdlc: created %s\n' "$dest"
83
+ return
84
+ fi
85
+ tmp="$(mktemp)"
86
+ if grep -F -q -- "$BEGIN" "$dest"; then
87
+ awk -v begin="$BEGIN" -v end="$END" -v overlay="$overlay" '
88
+ BEGIN {
89
+ while ((getline line < overlay) > 0) { o = o line "\n" }
90
+ close(overlay)
91
+ }
92
+ index($0, begin) == 1 {
93
+ printf "%s", o
94
+ skip = 1
95
+ next
96
+ }
97
+ skip {
98
+ if (index($0, end) == 1) skip = 0
99
+ next
100
+ }
101
+ { print }
102
+ ' "$dest" >"$tmp"
103
+ else
104
+ cat -- "$dest" >"$tmp"
105
+ printf '\n' >>"$tmp"
106
+ cat -- "$overlay" >>"$tmp"
107
+ fi
108
+ mv -- "$tmp" "$dest"
109
+ printf 'sdlc: merged overlay into %s\n' "$dest"
110
+ }
111
+
112
+ cmd_install() {
113
+ need_manifest
114
+ warn_location
115
+ copy_rules
116
+ copy_skills
117
+ merge_agents
118
+ printf 'sdlc: install done (level %s) repo %s\n' "$(level)" "$REPO_ROOT"
119
+ }
120
+
121
+ valid_slug() {
122
+ [[ "$1" =~ ^[a-z0-9][a-z0-9-]*$ ]] || die "slug must be kebab-case: $1"
123
+ }
124
+
125
+ copy_template() {
126
+ local src="$SDLC_DIR/$1" dest="$2/$1"
127
+ [[ -f "$src" ]] || die "missing template $src"
128
+ if [[ -e "$dest" && "$FORCE" -eq 0 ]]; then
129
+ printf 'sdlc: skip existing %s (use --force)\n' "$dest"
130
+ return
131
+ fi
132
+ cp -- "$src" "$dest"
133
+ printf 'sdlc: %s\n' "$dest"
134
+ }
135
+
136
+ cmd_start() {
137
+ need_manifest
138
+ warn_location
139
+ local slug="" with_intent=0 with_spec=0 boundary=0
140
+ FORCE=0
141
+ while [[ $# -gt 0 ]]; do
142
+ case "$1" in
143
+ --force) FORCE=1; shift ;;
144
+ --intent) with_intent=1; shift ;;
145
+ --spec) with_spec=1; shift ;;
146
+ --boundary) boundary=1; shift ;;
147
+ --help|-h) usage; exit 0 ;;
148
+ -*) die "unknown option $1" ;;
149
+ *)
150
+ [[ -z "$slug" ]] || die "unexpected argument $1"
151
+ slug="$1"
152
+ shift
153
+ ;;
154
+ esac
155
+ done
156
+ [[ -n "$slug" ]] || die "start requires <slug>"
157
+ valid_slug "$slug"
158
+
159
+ local lvl dest
160
+ lvl="$(level)"
161
+ dest="$SDLC_DIR/changes/$slug"
162
+ mkdir -p "$dest"
163
+
164
+ case "$lvl" in
165
+ light)
166
+ copy_template plan.md "$dest"
167
+ [[ "$with_intent" -eq 1 ]] && copy_template intent.md "$dest"
168
+ [[ "$with_spec" -eq 1 ]] && copy_template spec.md "$dest"
169
+ ;;
170
+ core)
171
+ copy_template plan.md "$dest"
172
+ if [[ "$boundary" -eq 1 || "$with_intent" -eq 1 || "$with_spec" -eq 1 ]]; then
173
+ copy_template intent.md "$dest"
174
+ copy_template spec.md "$dest"
175
+ fi
176
+ ;;
177
+ full)
178
+ copy_template intent.md "$dest"
179
+ copy_template spec.md "$dest"
180
+ copy_template plan.md "$dest"
181
+ ;;
182
+ *)
183
+ die "unknown level in manifest: $lvl"
184
+ ;;
185
+ esac
186
+ printf 'sdlc: change %s ready under %s\n' "$slug" "$dest"
187
+ }
188
+
189
+ cmd_status() {
190
+ need_manifest
191
+ warn_location
192
+ local lvl
193
+ lvl="$(level)"
194
+ printf 'level: %s\n' "$lvl"
195
+ printf 'sdlc dir: %s\n' "$SDLC_DIR"
196
+ printf 'repo: %s\n' "$REPO_ROOT"
197
+ if [[ -f "$REPO_ROOT/AGENTS.md" ]] && grep -F -q -- "$BEGIN" "$REPO_ROOT/AGENTS.md"; then
198
+ printf 'AGENTS.md: overlay present\n'
199
+ else
200
+ printf 'AGENTS.md: overlay missing (run install)\n'
201
+ fi
202
+ if [[ -d "$REPO_ROOT/.cursor/rules" ]]; then
203
+ printf 'rules:\n'
204
+ ls -1 "$REPO_ROOT/.cursor/rules"/sdlc-*.mdc 2>/dev/null || printf ' (no sdlc-*.mdc)\n'
205
+ fi
206
+ printf 'changes:\n'
207
+ if [[ -d "$SDLC_DIR/changes" ]]; then
208
+ local d
209
+ shopt -s nullglob
210
+ for d in "$SDLC_DIR/changes"/*/; do
211
+ printf ' %s\n' "$(basename -- "$d")"
212
+ done
213
+ shopt -u nullglob
214
+ fi
215
+ }
216
+
217
+ main() {
218
+ local cmd="${1:-}"
219
+ [[ -n "$cmd" ]] || { usage; exit 1; }
220
+ shift || true
221
+ case "$cmd" in
222
+ install) cmd_install "$@" ;;
223
+ start) cmd_start "$@" ;;
224
+ status) cmd_status "$@" ;;
225
+ -h|--help|help) usage ;;
226
+ *) die "unknown command $cmd" ;;
227
+ esac
228
+ }
229
+
230
+ main "$@"