- commit
- a48fbb257a6cd55bbb5d862f9a124557d57af2a7
- parent
- 57128233575158ab3b59092ad036f8c811d4387d
- author
- Dami <dami.etoile@gmail.com>
| M | internal/ | 4 | +- |
| A | scripts/ | 145 | ++++++++++++++++++++++++++++++ |
@@ -165,8 +165,10 @@ func (m *Model) syncMain() tea.Cmd {
165165 m.vp.SetContent(m.theme.muted.Render(fmt.Sprintf(166166 "\n No %s/ directory in %s yet.\n Waiting for one to appear (run `openspec init`).",167167 spec.DirName, m.opts.Root)))168- case !t.ok || !hasTab:168+ case !t.ok:169169 m.vp.SetContent(m.theme.muted.Render("\n Nothing selected."))170+ case !hasTab:171+ m.vp.SetContent(m.theme.muted.Render("\n No artifacts yet. Waiting for proposal.md…"))170172 case m.diffMode:171173 var content string172174 content, cmd = m.diffContent(tb, dirty)@@ -0,0 +1,145 @@
1+2+# Simulates a small swarm of agents working through OpenSpec changes so you can3+# watch lazyopenspec react. Works with macOS bash 3.2 and BSD tools.4+#5+# scripts/swarm-sim.sh [target-dir] (default /tmp/lazyopenspec-demo)6+# go run . [target-dir] --follow (in another pane)7+set -euo pipefail8+9+here="$(cd "$(dirname "$0")/.." && pwd)"10+target="${1:-/tmp/lazyopenspec-demo}"11+agents="${AGENTS:-3}"12+changes="$target/openspec/changes"13+topics="add-recurring-tasks:task-management add-api-tokens:user-auth add-audit-log:audit-log add-bulk-edit:task-management improve-search-ranking:search add-slack-alerts:notifications"14+15+rm -rf "$target"16+mkdir -p "$target"17+cp -Rp "$here/testdata/fixture/." "$target/"18+git -C "$target" init -q19+git -C "$target" add -A20+git -C "$target" -c user.name=sim -c user.email=sim@example.com commit -qm "baseline"21+22+echo "swarm-sim: $agents agents working in $target (ctrl+c to stop)"23+echo "watch with: go run $here $target --follow"24+echo25+26+say() { printf '%s [agent-%s] %s\n' "$(date +%H:%M:%S)" "$1" "$2"; }27+28+# Atomic write, the way editors and agents save: write a temp file, then rename.29+atomic() { # atomic <file> (content on stdin)30+ local tmp31+ tmp="$(dirname "$1")/.$(basename "$1").$$.$RANDOM.tmp"32+ cat >"$tmp"33+ mv -f "$tmp" "$1"34+}35+36+active() { find "$changes" -mindepth 1 -maxdepth 1 -type d ! -name archive 2>/dev/null; }37+pick() { awk -v r="$RANDOM" 'NF { a[n++] = $0 } END { if (n) print a[r % n] }'; }38+39+check_task() { # check off the next unchecked top-level task somewhere40+ local f41+ f="$(grep -l '^- \[ \]' $(active | sed 's|$|/tasks.md|') 2>/dev/null | pick || true)"42+ [ -n "$f" ] || return 143+ local task44+ task="$(grep -m1 '^- \[ \]' "$f" | sed 's/^- \[ \] //' | cut -c1-50)"45+ awk '!done && /^- \[ \]/ { sub(/\[ \]/, "[x]"); done = 1 } { print }' "$f" | atomic "$f"46+ say "$1" "checked \"$task\" in $(basename "$(dirname "$f")")"47+}48+49+add_requirement() { # append an ADDED requirement to some delta spec50+ local f n51+ f="$(active | while read -r d; do ls "$d"/specs/*/spec.md 2>/dev/null; done | pick || true)"52+ [ -n "$f" ] || return 153+ n=$((RANDOM % 900 + 100))54+ if ! grep -q '^## ADDED Requirements' "$f"; then printf '\n## ADDED Requirements\n' >>"$f"; fi55+ awk -v n="$n" '56+ { print }57+ /^## ADDED Requirements/ {58+ print ""59+ print "### Requirement: Generated behavior " n60+ print "The system SHALL handle case " n " consistently."61+ print ""62+ print "#### Scenario: Case " n63+ print "- **WHEN** case " n " occurs"64+ print "- **THEN** the system handles it"65+ }' "$f" | atomic "$f"66+ say "$1" "added requirement $n to $(echo "$f" | sed "s|$changes/||")"67+}68+69+advance_change() { # move one simulated change to its next artifact70+ local entry name cap dir71+ entry="$(echo $topics | tr ' ' '\n' | pick)"72+ name="${entry%%:*}" cap="${entry##*:}" dir="$changes/$name"73+ if [ ! -d "$dir" ]; then74+ [ -d "$changes/archive" ] && ls "$changes/archive" | grep -q -- "-$name\$" && return 175+ mkdir -p "$dir"76+ printf 'schema: spec-driven\ncreated: %s\n' "$(date +%F)" | atomic "$dir/.openspec.yaml"77+ say "$1" "openspec new change $name"78+ elif [ ! -f "$dir/proposal.md" ]; then79+ printf '## Why\n\nUsers keep asking for %s; doing it by hand is slow and error-prone.\n\n## What Changes\n\n- Introduce %s\n- Update the %s capability\n\n## Impact\n\n- Affected specs: %s\n' \80+ "${name//-/ }" "${name//-/ }" "$cap" "$cap" | atomic "$dir/proposal.md"81+ say "$1" "wrote proposal for $name"82+ elif [ ! -d "$dir/specs" ]; then83+ mkdir -p "$dir/specs/$cap"84+ printf '## ADDED Requirements\n\n### Requirement: %s\nThe system SHALL support %s.\n\n#### Scenario: Happy path\n- **WHEN** a member uses %s\n- **THEN** it works\n' \85+ "${name//-/ }" "${name//-/ }" "${name//-/ }" | atomic "$dir/specs/$cap/spec.md"86+ say "$1" "drafted spec delta $name/specs/$cap"87+ elif [ ! -f "$dir/design.md" ] && [ $((RANDOM % 2)) -eq 0 ]; then88+ printf '## Context\n\nSketching %s.\n\n## Decisions\n\n- Keep it behind a feature flag\n\n## Open Questions\n\n- Rollout order?\n' "$name" | atomic "$dir/design.md"89+ say "$1" "wrote design for $name"90+ elif [ ! -f "$dir/tasks.md" ]; then91+ {92+ printf '## 1. Implementation\n\n'93+ for i in 1 2 3 4 5; do printf -- '- [ ] 1.%s Step %s of %s\n' "$i" "$i" "$name"; done94+ } | atomic "$dir/tasks.md"95+ say "$1" "planned tasks for $name"96+ else97+ return 198+ fi99+}100+101+edit_design() {102+ local f103+ f="$(active | sed 's|$|/design.md|' | while read -r p; do [ -f "$p" ] && echo "$p"; done | pick || true)"104+ [ -n "$f" ] || return 1105+ printf -- '- Note from agent-%s at %s\n' "$1" "$(date +%T)" >>"$f"106+ say "$1" "noted a decision in $(basename "$(dirname "$f")")/design.md"107+}108+109+archive_done() { # archive a change whose tasks are all checked110+ local d name f cap main111+ d="$(active | while read -r c; do112+ [ -f "$c/tasks.md" ] && grep -q '^- \[x\]' "$c/tasks.md" && ! grep -q '^- \[ \]' "$c/tasks.md" && echo "$c"113+ done | pick || true)"114+ [ -n "$d" ] || return 1115+ name="$(basename "$d")"116+ for f in "$d"/specs/*/spec.md; do117+ [ -f "$f" ] || continue118+ cap="$(basename "$(dirname "$f")")" main="$target/openspec/specs/$cap/spec.md"119+ mkdir -p "$(dirname "$main")"120+ [ -f "$main" ] || printf '# %s Specification\n\n## Purpose\nTBD - created by archiving change %s.\n\n## Requirements\n' "$cap" "$name" >"$main"121+ awk '/^## ADDED Requirements/ { on = 1; next } /^## / { on = 0 } on' "$f" >>"$main"122+ done123+ mkdir -p "$changes/archive"124+ mv "$d" "$changes/archive/$(date +%F)-$name"125+ say "$1" "archived $name"126+}127+128+agent() {129+ local id="$1"130+ while true; do131+ sleep "$(awk -v r="$RANDOM" 'BEGIN { printf "%.1f", 0.5 + (r % 26) / 10 }')"132+ case $((RANDOM % 10)) in133+ 0 | 1 | 2 | 3) check_task "$id" || advance_change "$id" || true ;;134+ 4 | 5) advance_change "$id" || check_task "$id" || true ;;135+ 6) add_requirement "$id" || true ;;136+ 7) edit_design "$id" || true ;;137+ 8) archive_done "$id" || check_task "$id" || true ;;138+ 9) [ $((RANDOM % 3)) -eq 0 ] && git -C "$target" add -A && git -C "$target" -c user.name=sim -c user.email=sim@example.com commit -qm "agent-$id checkpoint" && say "$id" "committed checkpoint" || true ;;139+ esac140+ done141+}142+143+trap 'trap - INT TERM EXIT; kill 0 2>/dev/null' INT TERM EXIT144+for i in $(seq 1 "$agents"); do agent "$i" & done145+wait