@oneie/claude 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 (59) hide show
  1. package/.claude-plugin/plugin.json +16 -0
  2. package/.mcp.json +12 -0
  3. package/README.md +204 -0
  4. package/agents/w1-recon.md +102 -0
  5. package/agents/w2-decide.md +164 -0
  6. package/agents/w3-edit.md +91 -0
  7. package/agents/w4-verify.md +416 -0
  8. package/commands/browser.md +55 -0
  9. package/commands/cc-connect.md +67 -0
  10. package/commands/claw.md +135 -0
  11. package/commands/close.md +143 -0
  12. package/commands/create.md +78 -0
  13. package/commands/deploy.md +415 -0
  14. package/commands/do-autonomous.md +80 -0
  15. package/commands/do-improve.md +51 -0
  16. package/commands/do-show.md +89 -0
  17. package/commands/do.md +226 -0
  18. package/commands/improve.md +99 -0
  19. package/commands/kill.md +45 -0
  20. package/commands/release.md +144 -0
  21. package/commands/see.md +161 -0
  22. package/commands/setup.md +75 -0
  23. package/commands/sync.md +185 -0
  24. package/hooks/hooks.json +90 -0
  25. package/hooks/lib/signal.sh +28 -0
  26. package/hooks/scripts/design-check.sh +83 -0
  27. package/hooks/scripts/post-edit-check.sh +32 -0
  28. package/hooks/scripts/session-end-verify.sh +51 -0
  29. package/hooks/scripts/session-start.sh +88 -0
  30. package/hooks/scripts/stop-reflect.sh +95 -0
  31. package/hooks/scripts/sync-todo-docs.sh +46 -0
  32. package/hooks/scripts/task-complete-verify.sh +52 -0
  33. package/hooks/scripts/tool-signal.sh +48 -0
  34. package/package.json +33 -0
  35. package/rules/api.md +50 -0
  36. package/rules/astro.md +206 -0
  37. package/rules/design.md +221 -0
  38. package/rules/documentation.md +218 -0
  39. package/rules/engine.md +297 -0
  40. package/rules/react.md +137 -0
  41. package/rules/ui.md +82 -0
  42. package/scripts/cc-connect.sh +345 -0
  43. package/scripts/do-analyze.sh +42 -0
  44. package/scripts/do-folder.sh +63 -0
  45. package/scripts/do-prove.sh +51 -0
  46. package/scripts/do-reconcile.sh +28 -0
  47. package/scripts/do-smoke.sh +60 -0
  48. package/scripts/do-survey.sh +30 -0
  49. package/scripts/do-tier.sh +43 -0
  50. package/skills/build/SKILL.md +52 -0
  51. package/skills/cloudflare/SKILL.md +503 -0
  52. package/skills/dev/SKILL.md +58 -0
  53. package/skills/do/SKILL.md +24 -0
  54. package/skills/oneie/SKILL.md +51 -0
  55. package/skills/perf/SKILL.md +45 -0
  56. package/skills/signal/SKILL.md +108 -0
  57. package/skills/sui/SKILL.md +441 -0
  58. package/skills/tutorial/SKILL.md +96 -0
  59. package/skills/typecheck/SKILL.md +66 -0
@@ -0,0 +1,345 @@
1
+ #!/usr/bin/env bash
2
+ # cc-connect — Claude Code ↔ Claude Code messaging over the substrate.
3
+ # SSE push (no client poll). Background listener writes to .cc-connect/<group>.jsonl.
4
+ #
5
+ # Multi-group: one local config holds your sender name + the groups you're
6
+ # subscribed to. `listen` starts one SSE listener per group. `send`/`read`
7
+ # default to your "default" group; pass --to / <group> to address another.
8
+ #
9
+ # Subcommands:
10
+ # init <sender> [group] first-time setup
11
+ # join <group> subscribe + start its listener
12
+ # leave <group> stop listener + unsubscribe
13
+ # listen start a listener for every subscribed group
14
+ # stop [group] kill one listener (or all if no arg)
15
+ # status show config + listener state per group
16
+ # listeners list all running listener PIDs
17
+ # groups ask claw what groups exist (discovery)
18
+ # send [--to <group>] <text> send to default (or specific) group
19
+ # read [<group>] show new messages since last read (one group)
20
+ # read --all merge new across every subscribed group
21
+ # (no args) = read default group
22
+ #
23
+ # Requires: bash, curl, jq.
24
+
25
+ set -e
26
+
27
+ CLAW_URL="${CLAW_URL:-https://claw.oneie.workers.dev}"
28
+ if ROOT="$(git rev-parse --show-toplevel 2>/dev/null)" && [ -n "$ROOT" ] && [ -d "$ROOT/.cc-connect" ]; then
29
+ DIR="$ROOT/.cc-connect"
30
+ else
31
+ DIR="$HOME/.cc-connect"
32
+ fi
33
+ CFG="$DIR/config.json"
34
+
35
+ mkdir -p "$DIR"
36
+
37
+ # ─── config helpers ─────────────────────────────────────────────────────────
38
+
39
+ cfg_read() {
40
+ [ -f "$CFG" ] && /bin/cat "$CFG" || /bin/echo '{}'
41
+ }
42
+
43
+ # Read sender; default $USER
44
+ cfg_sender() {
45
+ cfg_read | jq -r '.sender // empty' 2>/dev/null | { read v; /bin/echo "${v:-$USER}"; }
46
+ }
47
+
48
+ # Read subscribed groups as a newline-separated list.
49
+ # Back-compat: old config used `group: "x"`; new uses `groups: ["x", ...]`.
50
+ cfg_groups() {
51
+ cfg_read | jq -r '
52
+ if .groups and (.groups | length > 0) then .groups[]
53
+ elif .group then .group
54
+ else "newco" end
55
+ ' 2>/dev/null
56
+ }
57
+
58
+ # Read default group (first in list if not set explicitly)
59
+ cfg_default() {
60
+ cfg_read | jq -r '.default // .group // (.groups[0] // "newco")' 2>/dev/null
61
+ }
62
+
63
+ cfg_save() {
64
+ local sender="$1" default="$2"
65
+ shift 2
66
+ /bin/echo "$@" \
67
+ | jq -R 'split(" ") | map(select(length > 0))' \
68
+ | jq --arg s "$sender" --arg d "$default" \
69
+ '{sender: $s, default: $d, groups: .}' \
70
+ > "$CFG"
71
+ }
72
+
73
+ # ─── listener primitives (one per group) ───────────────────────────────────
74
+
75
+ listener_pid_file() { /bin/echo "$DIR/$1.pid"; }
76
+ listener_jsonl() { /bin/echo "$DIR/$1.jsonl"; }
77
+ listener_offset() { /bin/echo "$DIR/$1.offset"; }
78
+
79
+ listener_running() {
80
+ local pidf
81
+ pidf=$(listener_pid_file "$1")
82
+ [ -f "$pidf" ] && kill -0 "$(/bin/cat "$pidf" 2>/dev/null)" 2>/dev/null
83
+ }
84
+
85
+ listener_start() {
86
+ local group="$1"
87
+ local pidf jsonl last_ts_file
88
+ pidf=$(listener_pid_file "$group")
89
+ jsonl=$(listener_jsonl "$group")
90
+ last_ts_file="$DIR/$group.last_ts"
91
+ if listener_running "$group"; then
92
+ /bin/echo " - $group already listening (pid=$(/bin/cat "$pidf"))"
93
+ return 0
94
+ fi
95
+ /usr/bin/touch "$jsonl"
96
+ [ -f "$(listener_offset "$group")" ] || /bin/echo 0 > "$(listener_offset "$group")"
97
+ # Seed last_ts file from the tail of the existing jsonl (or 0 for fresh).
98
+ # The outer loop re-reads this file each iteration so curl reconnects
99
+ # honour the latest ts — without this, the inner `| while` subshell drops
100
+ # the variable update and every reconnect replays the full backlog.
101
+ if [ ! -f "$last_ts_file" ]; then
102
+ /usr/bin/tail -n 1 "$jsonl" 2>/dev/null | jq -r '.ts // 0' 2>/dev/null > "$last_ts_file" \
103
+ || /bin/echo 0 > "$last_ts_file"
104
+ fi
105
+ (
106
+ while true; do
107
+ last_ts=$(/bin/cat "$last_ts_file" 2>/dev/null || /bin/echo 0)
108
+ [ -z "$last_ts" ] && last_ts=0
109
+ curl -N -sS --max-time 90 \
110
+ --header "Last-Event-ID: $last_ts" \
111
+ "$CLAW_URL/stream/$group?since=$last_ts" 2>/dev/null \
112
+ | while IFS= read -r line; do
113
+ case "$line" in
114
+ "data: "*)
115
+ payload="${line#data: }"
116
+ /bin/echo "$payload" >> "$jsonl"
117
+ new_ts=$(/bin/echo "$payload" | jq -r '.ts // empty' 2>/dev/null)
118
+ [ -n "$new_ts" ] && /bin/echo "$new_ts" > "$last_ts_file"
119
+ sender=$(/bin/echo "$payload" | jq -r '.sender // empty' 2>/dev/null)
120
+ snippet=$(/bin/echo "$payload" | jq -r '.content // empty' 2>/dev/null | /usr/bin/cut -c1-80)
121
+ [ -n "$sender" ] && osascript -e "display notification \"$snippet\" with title \"cc-connect · $group\" subtitle \"$sender\"" 2>/dev/null || true
122
+ ;;
123
+ esac
124
+ done
125
+ sleep 1
126
+ done
127
+ ) > /dev/null 2>&1 &
128
+ /bin/echo $! > "$pidf"
129
+ /bin/echo " + $group listening (pid=$(/bin/cat "$pidf"))"
130
+ }
131
+
132
+ listener_stop() {
133
+ local group="$1"
134
+ local pidf
135
+ pidf=$(listener_pid_file "$group")
136
+ if [ -f "$pidf" ]; then
137
+ local pid
138
+ pid=$(/bin/cat "$pidf")
139
+ if [ -n "$pid" ]; then
140
+ pkill -P "$pid" 2>/dev/null || true
141
+ kill "$pid" 2>/dev/null || true
142
+ fi
143
+ /bin/rm -f "$pidf"
144
+ /bin/echo " - $group stopped (pid=$pid)"
145
+ else
146
+ /bin/echo " - $group not running"
147
+ fi
148
+ }
149
+
150
+ # ─── pretty print messages ─────────────────────────────────────────────────
151
+
152
+ print_messages() {
153
+ local me="$1"
154
+ jq -r --arg me "$me" '
155
+ def t: (.ts / 1000 | strftime("%H:%M:%S"));
156
+ if (.group | length > 0) then
157
+ "[\(t)] \(.group) · \(.sender) → \(if .sender == $me then "(you)" else $me end): \(.content)"
158
+ else
159
+ "[\(t)] \(.sender) → \(if .sender == $me then "(you)" else $me end): \(.content)"
160
+ end
161
+ '
162
+ }
163
+
164
+ # ─── dispatch ──────────────────────────────────────────────────────────────
165
+
166
+ SENDER="$(cfg_sender)"
167
+ DEFAULT="$(cfg_default)"
168
+
169
+ cmd="${1:-read}"
170
+ shift 2>/dev/null || true
171
+
172
+ case "$cmd" in
173
+ init)
174
+ s="${1:-$USER}"
175
+ g="${2:-newco}"
176
+ cfg_save "$s" "$g" "$g"
177
+ /bin/echo "ok sender=$s default=$g groups=[$g] config=$CFG"
178
+ ;;
179
+
180
+ join)
181
+ g="${1:?usage: cc-connect join <group>}"
182
+ cur=$(cfg_groups | /usr/bin/tr '\n' ' ')
183
+ if /bin/echo " $cur " | /usr/bin/grep -q " $g "; then
184
+ /bin/echo "ok already subscribed to $g"
185
+ else
186
+ cfg_save "$SENDER" "$DEFAULT" "$cur $g"
187
+ /bin/echo "ok joined $g (groups now: $(cfg_groups | /usr/bin/tr '\n' ' '))"
188
+ fi
189
+ listener_start "$g"
190
+ ;;
191
+
192
+ leave)
193
+ g="${1:?usage: cc-connect leave <group>}"
194
+ new=$(cfg_groups | /usr/bin/grep -v "^$g$" | /usr/bin/tr '\n' ' ')
195
+ new_default="$DEFAULT"
196
+ [ "$DEFAULT" = "$g" ] && new_default=$(/bin/echo "$new" | /usr/bin/awk '{print $1}')
197
+ [ -z "$new_default" ] && new_default="$g"
198
+ cfg_save "$SENDER" "$new_default" "$new"
199
+ listener_stop "$g"
200
+ /bin/echo "ok left $g (default=$new_default)"
201
+ ;;
202
+
203
+ listen)
204
+ while IFS= read -r g; do
205
+ [ -n "$g" ] && listener_start "$g"
206
+ done < <(cfg_groups)
207
+ ;;
208
+
209
+ stop)
210
+ g="${1:-}"
211
+ if [ -n "$g" ]; then
212
+ listener_stop "$g"
213
+ else
214
+ while IFS= read -r grp; do
215
+ [ -n "$grp" ] && listener_stop "$grp"
216
+ done < <(cfg_groups)
217
+ fi
218
+ ;;
219
+
220
+ status)
221
+ /bin/echo "sender=$SENDER default=$DEFAULT"
222
+ /bin/echo "subscribed groups:"
223
+ while IFS= read -r g; do
224
+ [ -z "$g" ] && continue
225
+ jsonl=$(listener_jsonl "$g")
226
+ total=0
227
+ [ -f "$jsonl" ] && total=$(/usr/bin/wc -l < "$jsonl" | /usr/bin/tr -d ' ')
228
+ off=0
229
+ [ -f "$(listener_offset "$g")" ] && off=$(/bin/cat "$(listener_offset "$g")" 2>/dev/null | /usr/bin/tr -d ' ')
230
+ [ -z "$off" ] && off=0
231
+ unread=$((total - off))
232
+ if listener_running "$g"; then
233
+ live="pid=$(/bin/cat "$(listener_pid_file "$g")")"
234
+ else
235
+ live="not running"
236
+ fi
237
+ /bin/echo " $g total=$total unread=$unread listener=$live"
238
+ done < <(cfg_groups)
239
+ ;;
240
+
241
+ listeners)
242
+ while IFS= read -r g; do
243
+ [ -z "$g" ] && continue
244
+ if listener_running "$g"; then
245
+ /bin/echo " $g pid=$(/bin/cat "$(listener_pid_file "$g")")"
246
+ fi
247
+ done < <(cfg_groups)
248
+ ;;
249
+
250
+ groups)
251
+ /bin/echo "claw says:"
252
+ curl -s "$CLAW_URL/groups" -m 5 \
253
+ | jq -r '.groups[] | " \(.id) msgs=\(.message_count) last=\(.last_sender // "—"): \((.last_content // "")[0:60])"' \
254
+ || /bin/echo " (could not reach claw)"
255
+ /bin/echo "you are subscribed to:"
256
+ cfg_groups | /usr/bin/sed 's/^/ /'
257
+ ;;
258
+
259
+ send)
260
+ # send [--to <group>] <text...> (--group is an alias for --to; flag can appear anywhere)
261
+ target="$DEFAULT"
262
+ args=()
263
+ while [ $# -gt 0 ]; do
264
+ case "$1" in
265
+ --to|--group)
266
+ target="${2:?usage: cc-connect send --to <group> <text>}"
267
+ shift 2
268
+ ;;
269
+ *)
270
+ args+=("$1")
271
+ shift
272
+ ;;
273
+ esac
274
+ done
275
+ text="${args[*]}"
276
+ [ -z "$text" ] && { /bin/echo "error usage: cc-connect send [--to <group>] <text>"; exit 1; }
277
+ body=$(jq -n --arg s "$SENDER" --arg c "$text" '{sender: $s, content: $c}')
278
+ resp=$(curl -s -X POST "$CLAW_URL/signal/$target" \
279
+ -H 'Content-Type: application/json' \
280
+ -d "$body")
281
+ /bin/echo "ok →$target $resp"
282
+ ;;
283
+
284
+ read)
285
+ target="$DEFAULT"
286
+ all=false
287
+ if [ "$1" = "--all" ]; then
288
+ all=true
289
+ elif [ -n "$1" ]; then
290
+ target="$1"
291
+ fi
292
+ # auto-start listeners if not running
293
+ if $all; then
294
+ while IFS= read -r g; do
295
+ [ -n "$g" ] && ! listener_running "$g" && listener_start "$g" >/dev/null 2>&1
296
+ done < <(cfg_groups)
297
+ else
298
+ ! listener_running "$target" && listener_start "$target" >/dev/null 2>&1
299
+ fi
300
+ if $all; then
301
+ # Merge new across all subscribed groups, sort by ts
302
+ tmp=$(/usr/bin/mktemp)
303
+ while IFS= read -r g; do
304
+ [ -z "$g" ] && continue
305
+ jsonl=$(listener_jsonl "$g")
306
+ [ -f "$jsonl" ] || continue
307
+ total=$(/usr/bin/wc -l < "$jsonl" | /usr/bin/tr -d ' ')
308
+ off=$(/bin/cat "$(listener_offset "$g")" 2>/dev/null | /usr/bin/tr -d ' ')
309
+ [ -z "$off" ] && off=0
310
+ if [ "$total" -gt "$off" ]; then
311
+ new=$((total - off))
312
+ /usr/bin/tail -n "$new" "$jsonl" | jq -c --arg g "$g" '. + {group: $g}' >> "$tmp"
313
+ /bin/echo "$total" > "$(listener_offset "$g")"
314
+ fi
315
+ done < <(cfg_groups)
316
+ if [ -s "$tmp" ]; then
317
+ /usr/bin/sort -t '"' -k '6' "$tmp" 2>/dev/null | print_messages "$SENDER"
318
+ else
319
+ /bin/echo "ok no new messages across $(cfg_groups | /usr/bin/wc -l | /usr/bin/tr -d ' ') groups (you=$SENDER)"
320
+ fi
321
+ /bin/rm -f "$tmp"
322
+ else
323
+ jsonl=$(listener_jsonl "$target")
324
+ offset=$(listener_offset "$target")
325
+ /usr/bin/touch "$jsonl"
326
+ [ -f "$offset" ] || /bin/echo 0 > "$offset"
327
+ total=$(/usr/bin/wc -l < "$jsonl" | /usr/bin/tr -d ' ')
328
+ off=$(/bin/cat "$offset" 2>/dev/null | /usr/bin/tr -d ' ')
329
+ [ -z "$off" ] && off=0
330
+ [ -z "$total" ] && total=0
331
+ if [ "$total" -le "$off" ]; then
332
+ /bin/echo "ok no new messages in $target (you=$SENDER total=$total)"
333
+ exit 0
334
+ fi
335
+ new=$((total - off))
336
+ /usr/bin/tail -n "$new" "$jsonl" | jq -c --arg g "$target" '. + {group: $g}' | print_messages "$SENDER"
337
+ /bin/echo "$total" > "$offset"
338
+ fi
339
+ ;;
340
+
341
+ *)
342
+ /bin/echo "usage: cc-connect {init|join|leave|listen|stop|status|listeners|groups|send|read}"
343
+ exit 1
344
+ ;;
345
+ esac
@@ -0,0 +1,42 @@
1
+ #!/usr/bin/env bash
2
+ # do-analyze.sh — ANALYZE coverage gate (ex-Spec-Kit /analyze), read-only. Run at the
3
+ # todo→code boundary, BEFORE build spends worktree tokens. Builds the D#↔C# coverage matrix
4
+ # and the AC→test check. CRITICAL (uncovered deliverable) → exit 1, blocks build. Never edits.
5
+ # Usage: do-analyze.sh <todo.md>
6
+ set -euo pipefail
7
+ todo="${1:?usage: do-analyze.sh <todo.md>}"
8
+ [ -f "$todo" ] || { echo "CRITICAL: todo not found: $todo"; exit 1; }
9
+
10
+ crit=0; high=0
11
+
12
+ # 1. coverage: every deliverable line (- D#) must cite at least one cycle (C#)
13
+ uncovered=0
14
+ while IFS= read -r line; do
15
+ did=$(printf '%s' "$line" | grep -oE 'D[0-9]+' | head -1)
16
+ if ! printf '%s' "$line" | grep -qE 'C[0-9]+'; then
17
+ echo "CRITICAL: $did maps to no cycle"; crit=1; uncovered=$((uncovered+1))
18
+ fi
19
+ done < <(grep -E '^[[:space:]]*- D[0-9]+ ' "$todo")
20
+
21
+ ndel=$(grep -cE '^[[:space:]]*- D[0-9]+ ' "$todo" || true)
22
+ ncyc=$(grep -cE '^## C[0-9]+ ' "$todo" || true)
23
+
24
+ # 2. orphan cycles: every ## C# section should be cited by some deliverable
25
+ cited=$(grep -oE 'C[0-9]+' "$todo" | sort -u)
26
+ for c in $(grep -oE '^## C[0-9]+ ' "$todo" | grep -oE 'C[0-9]+'); do
27
+ # a cycle is cited if it appears outside its own header (i.e. in a deliverable line)
28
+ hits=$(grep -E "^[[:space:]]*- D[0-9]+ .*\b$c\b" "$todo" | wc -l | tr -d ' ')
29
+ [ "$hits" -eq 0 ] && { echo "HIGH: cycle $c has no deliverable (orphan)"; high=$((high+1)); }
30
+ done
31
+
32
+ # 3. AC→test: every cycle section should carry a demo: line (DoD row 2, planned half)
33
+ nodemo=0
34
+ for c in $(grep -oE '^## C[0-9]+ ' "$todo" | grep -oE 'C[0-9]+'); do
35
+ block=$(awk "/^## $c /{f=1} f&&/^## C[0-9]+ /&&!/^## $c /{if(seen)exit} {if(f)print; if(/^## $c /)seen=1}" "$todo")
36
+ printf '%s' "$block" | grep -qiE 'demo:' || { echo "HIGH: $c has no planned test (demo: line)"; high=$((high+1)); nodemo=$((nodemo+1)); }
37
+ done
38
+
39
+ echo "----"
40
+ echo "coverage: $ndel deliverables / $ncyc cycles · uncovered=$uncovered · no-demo=$nodemo · high=$high"
41
+ if [ "$crit" -ne 0 ]; then echo "ANALYZE: CRITICAL — fix coverage before build"; exit 1; fi
42
+ echo "ANALYZE: pass (coverage 100%)"; exit 0
@@ -0,0 +1,63 @@
1
+ #!/usr/bin/env bash
2
+ # do-folder.sh — resolve target repo folder(s) from a changed-file list and emit each
3
+ # folder's verify/build commands. There is NO root package.json by design: every top
4
+ # folder under one-ie/ is its own buildable repo. /do W0/W4 use this instead of a root
5
+ # `bun run verify`. Doc-only cycles (.md / .claude / plans / text / docs) skip bun entirely.
6
+ #
7
+ # Usage: do-folder.sh <path>... | git diff --name-only | do-folder.sh
8
+ # Output: one JSON object per resolved folder (or a single doc_only line):
9
+ # {"folder":"one.ie/web","verify":"bun run verify","build":"bun run build","doc_only":false}
10
+ # {"folder":null,"verify":null,"build":null,"doc_only":true}
11
+ set -euo pipefail
12
+
13
+ ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" # .claude/scripts -> one-ie root
14
+
15
+ # 1. gather paths from args or stdin
16
+ paths=()
17
+ if [ "$#" -gt 0 ]; then
18
+ paths=("$@")
19
+ else
20
+ while IFS= read -r line; do [ -n "$line" ] && paths+=("$line"); done
21
+ fi
22
+ [ "${#paths[@]}" -eq 0 ] && { echo '{"folder":null,"verify":null,"build":null,"doc_only":true}'; exit 0; }
23
+
24
+ # 2. doc-only = every path is markdown or lives in a non-built dir
25
+ doc_only=true
26
+ for p in "${paths[@]}"; do
27
+ case "$p" in
28
+ *.md|.claude/*|plans/*|text/*|docs/*) ;;
29
+ *) doc_only=false ;;
30
+ esac
31
+ done
32
+ if $doc_only; then
33
+ echo '{"folder":null,"verify":null,"build":null,"doc_only":true}'
34
+ exit 0
35
+ fi
36
+
37
+ # 3. longest-prefix match against known buildable folders (one.ie/web BEFORE one.ie).
38
+ # bash 3.2-safe (macOS): no associative arrays — dedup a newline list with sort -u.
39
+ folders="one.ie/web one.ie packages agents api schema sync backup"
40
+ matched=""
41
+ for p in "${paths[@]}"; do
42
+ for f in $folders; do
43
+ case "$p" in "$f"/*) matched="${matched}${f}
44
+ "; break;; esac
45
+ done
46
+ done
47
+ matched=$(printf '%s' "$matched" | sed '/^$/d' | sort -u)
48
+
49
+ if [ -z "$matched" ]; then
50
+ echo '{"folder":null,"verify":null,"build":null,"doc_only":false,"reason":"unmapped"}'
51
+ exit 0
52
+ fi
53
+
54
+ # 4. emit per folder, reading its own package.json for the script names
55
+ printf '%s\n' "$matched" | while IFS= read -r f; do
56
+ pj="$ROOT/$f/package.json"
57
+ verify="null"; build="null"
58
+ if [ -f "$pj" ]; then
59
+ jq -e '.scripts.verify' "$pj" >/dev/null 2>&1 && verify='"bun run verify"'
60
+ jq -e '.scripts.build' "$pj" >/dev/null 2>&1 && build='"bun run build"'
61
+ fi
62
+ printf '{"folder":"%s","verify":%s,"build":%s,"doc_only":false}\n' "$f" "$verify" "$build"
63
+ done
@@ -0,0 +1,51 @@
1
+ #!/usr/bin/env bash
2
+ # do-prove.sh — PROVE (P5). Auto-detect the surface from the changed files and emit the proof
3
+ # action; optional promise-check vs the P0 copy. The shipped thing must let the user do what the
4
+ # promise said. Exit 1 only on a clear over-promise (artifact mentions none of the promise terms).
5
+ # Usage: do-prove.sh [--promise text/<f>.md] <changed-path>...
6
+ set -euo pipefail
7
+
8
+ promise=""
9
+ if [ "${1:-}" = "--promise" ]; then promise="$2"; shift 2; fi
10
+
11
+ paths=()
12
+ if [ "$#" -gt 0 ]; then paths=("$@"); else while IFS= read -r l; do [ -n "$l" ] && paths+=("$l"); done; fi
13
+ [ "${#paths[@]}" -eq 0 ] && { echo "PROVE: no changes"; exit 0; }
14
+
15
+ # surface detect — substrate wins (upstream), then api, then frontend, else backend
16
+ surface=backend
17
+ for p in "${paths[@]}"; do
18
+ case "$p" in
19
+ *.tql|schema/*) surface=substrate; break ;;
20
+ */pages/api/*|*/api/*) surface=api ;;
21
+ *.astro|*.tsx) [ "$surface" != "api" ] && surface=frontend ;;
22
+ esac
23
+ done
24
+
25
+ case "$surface" in
26
+ frontend) proof="/browser (real Chrome: HTTP + JS/console errors + rail before/after + screenshot) + Lighthouse" ;;
27
+ api) proof="contract test (vitest+msw) + curl per surface — route + SDK + MCP + CLI (four-surface rule)" ;;
28
+ backend) proof="curl against the DEPLOYED runtime (local verify is necessary, not sufficient)" ;;
29
+ substrate) proof="/sync reconcile (TypeDB↔KV↔D1↔SUI) + TypeQL returns the new shape + types compile downhill" ;;
30
+ esac
31
+ echo "surface: $surface"
32
+ echo "proof: $proof"
33
+
34
+ # promise-check: at least one substantive term from the P0 copy must appear in the shipped files
35
+ if [ -n "$promise" ] && [ -f "$promise" ]; then
36
+ # drop code keywords + generic verbs so the check keys on domain terms, not coincidences
37
+ stop='export|function|return|const|class|import|async|await|value|string|number|default|users|allow|enable|create|update|delete|where|which|their'
38
+ terms=$(grep -oiE '[a-z]{5,}' "$promise" | tr 'A-Z' 'a-z' | sort -u | grep -vwE "$stop" | head -40)
39
+ hit=0
40
+ for t in $terms; do
41
+ for p in "${paths[@]}"; do
42
+ if [ -f "$p" ] && grep -qi "$t" "$p" 2>/dev/null; then hit=1; break 2; fi
43
+ done
44
+ done
45
+ if [ "$hit" -eq 0 ]; then
46
+ echo "PROMISE-CHECK: FAIL — shipped artifact mentions none of the promise's terms (over-promise → back to P4 or re-FRAME)"
47
+ exit 1
48
+ fi
49
+ echo "PROMISE-CHECK: ok"
50
+ fi
51
+ echo "PROVE: pass"; exit 0
@@ -0,0 +1,28 @@
1
+ #!/usr/bin/env bash
2
+ # do-reconcile.sh — substrate reconciliation gate (P1.0). Reads a proposal (files or stdin
3
+ # text) and FAILS (exit 1) on a dead name or a proposed new dimension/verb. The schema is
4
+ # truth: a feature that needs a new dim/verb is rejected before any design spend.
5
+ # Usage: do-reconcile.sh <file|text>... | echo "proposal" | do-reconcile.sh
6
+ set -euo pipefail
7
+
8
+ # Locked vocabulary (root CLAUDE.md). Dead names auto-fail.
9
+ DEAD="knowledge connections node scent alarm trail colony"
10
+ DIMS="groups actors things paths events learning"
11
+ VERBS="signal mark warn fade follow harden"
12
+
13
+ text=""
14
+ if [ "$#" -gt 0 ]; then
15
+ for a in "$@"; do if [ -f "$a" ]; then text="$text $(cat "$a")"; else text="$text $a"; fi; done
16
+ else
17
+ text="$(cat)"
18
+ fi
19
+
20
+ fail=0
21
+ for d in $DEAD; do
22
+ if printf '%s' "$text" | grep -qiw "$d"; then
23
+ echo "DEAD-NAME: '$d' — use the canonical term (dims: $DIMS)"; fail=1
24
+ fi
25
+ done
26
+
27
+ if [ "$fail" -ne 0 ]; then echo "RECONCILE: FAIL (dead name)"; exit 1; fi
28
+ echo "RECONCILE: clean — names canonical, no new dim/verb"; exit 0
@@ -0,0 +1,60 @@
1
+ #!/usr/bin/env bash
2
+ # do-smoke.sh — deterministic outcome gate for the /do upgrade. Drives fixtures through every
3
+ # helper + the state-file schemas + the seed lifecycle. Exits 0 only if ALL pass. The LLM
4
+ # phase-sequencing itself is proven by a logged canary `/do <idea>` run, NOT by this script
5
+ # (a bash script cannot drive Claude — see plans/do-loop-todo.md outcome contract).
6
+ set -uo pipefail
7
+ cd "$(dirname "${BASH_SOURCE[0]}")/../.." || exit 2
8
+ S=.claude/scripts
9
+ fail=0
10
+ ok(){ printf ' \xe2\x9c\x93 %s\n' "$1"; }
11
+ no(){ printf ' \xe2\x9c\x97 %s\n' "$1"; fail=1; }
12
+
13
+ echo "== C0 do-folder =="
14
+ "$S/do-folder.sh" one.ie/web/src/x.ts | grep -q '"folder":"one.ie/web"' && ok "web file → folder" || no "web file"
15
+ "$S/do-folder.sh" .claude/commands/do.md plans/x.md | grep -q '"doc_only":true' && ok "doc-only → skip" || no "doc-only"
16
+
17
+ echo "== C7 do-tier =="
18
+ "$S/do-tier.sh" text/x.md | grep -q '"tier":"PATCH"' && ok "typo → PATCH" || no "PATCH"
19
+ "$S/do-tier.sh" schema/one.tql | grep -q '"tier":"SCHEMA"' && ok ".tql → SCHEMA" || no "SCHEMA"
20
+ "$S/do-tier.sh" --intent "add billing" one.ie/web/src/api/b.ts | grep -q '"tier":"FEATURE"' && ok "intent → FEATURE" || no "FEATURE"
21
+
22
+ echo "== C10 do-reconcile =="
23
+ echo "uses a node in the colony" | "$S/do-reconcile.sh" >/dev/null 2>&1 && no "dead name should fail" || ok "dead name → exit 1"
24
+ echo "signal a mark on a path" | "$S/do-reconcile.sh" >/dev/null 2>&1 && ok "canonical → exit 0" || no "canonical should pass"
25
+
26
+ echo "== C9 do-survey =="
27
+ "$S/do-survey.sh" zxqwfoobar 2>/dev/null | grep -q 'VERDICT: build' && ok "novel → build" || no "novel"
28
+ "$S/do-survey.sh" signal 2>/dev/null | grep -qE 'VERDICT: (extend|expose)' && ok "existing → extend/expose" || no "existing"
29
+
30
+ echo "== C14 do-analyze =="
31
+ "$S/do-analyze.sh" plans/do-loop-todo.md >/dev/null 2>&1 && ok "this plan → 100% coverage" || no "plan coverage"
32
+ tmp=$(mktemp); printf 'deliverables:\n - D1 x (C1)\n - D2 orphan no cycle\n## C1 — y\n' > "$tmp"
33
+ "$S/do-analyze.sh" "$tmp" >/dev/null 2>&1 && no "uncovered should fail" || ok "uncovered deliverable → CRITICAL exit 1"; rm -f "$tmp"
34
+
35
+ echo "== C11 do-prove =="
36
+ pv=$("$S/do-prove.sh" one.ie/web/src/components/X.tsx 2>/dev/null); echo "$pv" | grep -q 'surface: frontend' && ok "frontend → /browser" || no "frontend"
37
+ pm=$(mktemp); art=$(mktemp); echo "users export revenue analytics" > "$pm"; echo "function foo(){}" > "$art"
38
+ "$S/do-prove.sh" --promise "$pm" "$art" >/dev/null 2>&1 && no "over-promise should fail" || ok "over-promise → exit 1"; rm -f "$pm" "$art"
39
+
40
+ echo "== state-file schemas =="
41
+ echo '{"diff_specs":[{"current_state":"x","must_not_break":"y","serves":"D1"}]}' | jq -e '.diff_specs[0]|.current_state and .must_not_break and .serves' >/dev/null && ok ".w2-spec context pack" || no ".w2-spec"
42
+ echo '{"renames":[],"touched_docs":[],"contract_dirs":[]}' | jq -e 'has("renames") and has("touched_docs") and has("contract_dirs")' >/dev/null && ok ".w2-doc-plan" || no ".w2-doc-plan"
43
+ echo '{"level":"standard","consecutive":1,"composite":0.78,"updated":"t"}' | jq -e '.level and (.composite|type=="number")' >/dev/null && ok ".do-trust" || no ".do-trust"
44
+ echo '{"receiver":"cost:cycle","data":{"tokens":{"input":1},"model":"sonnet","composite":0.7}}' | jq -e '.receiver=="cost:cycle" and .data.model' >/dev/null && ok "cost:cycle grammar" || no "cost:cycle"
45
+
46
+ echo "== /do-loop removed (single front door) =="
47
+ # scan commands+agents only (the engine surface); the 'no separate' note is the lone allowed mention
48
+ if grep -rn '/do-loop' .claude/commands .claude/agents 2>/dev/null | grep -vq 'no separate'; then no "/do-loop still referenced"; else ok "/do-loop gone (only the 'no separate' note)"; fi
49
+ [ -f .claude/commands/do-lifecycle.md ] && ok "do-lifecycle.md is the spec" || no "do-lifecycle.md missing"
50
+
51
+ echo "== C5 seed lifecycle (one.ie/web vitest) =="
52
+ if command -v bunx >/dev/null 2>&1; then
53
+ ( cd one.ie/web && bunx vitest run tests/unit/substrate-seed-c5.test.ts >/dev/null 2>&1 ) && ok "seed→promote→fade (4/4)" || no "seed lifecycle test"
54
+ else
55
+ echo " ~ bunx not found — seed test skipped (run in one.ie/web)"
56
+ fi
57
+
58
+ echo "----"
59
+ if [ "$fail" -ne 0 ]; then echo "do-smoke: FAIL"; exit 1; fi
60
+ echo "do-smoke: PASS — deterministic substrate green. (LLM loop: run a canary /do <idea> and log it.)"; exit 0
@@ -0,0 +1,30 @@
1
+ #!/usr/bin/env bash
2
+ # do-survey.sh — P0.5 SURVEY. Grep the 4 surfaces + plans/ for an existing ≥70% match so
3
+ # /do stops rebuilding what already ships. Emits a simplicity verdict. (The cheapest feature
4
+ # is the one you already have.) Always exits 0 — it informs, it doesn't gate.
5
+ # Usage: do-survey.sh <keyword>
6
+ set -euo pipefail
7
+ ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
8
+ q="${1:?usage: do-survey.sh <keyword>}"
9
+
10
+ surfaces="one.ie/web/src/pages/api one.ie/web/src/components packages/sdk agents plans"
11
+ total=0
12
+ for d in $surfaces; do
13
+ if [ -d "$ROOT/$d" ]; then
14
+ n=$( { grep -rilw "$q" "$ROOT/$d" 2>/dev/null || true; } | wc -l | tr -d ' ')
15
+ else
16
+ n=0
17
+ fi
18
+ printf ' %-28s %s match(es)\n' "$d" "$n"
19
+ total=$((total + n))
20
+ done
21
+
22
+ echo "----"
23
+ if [ "$total" -ge 3 ]; then
24
+ echo "VERDICT: expose/extend — $total existing matches; reuse, do not rebuild (collapse to FIX tier)"
25
+ elif [ "$total" -ge 1 ]; then
26
+ echo "VERDICT: extend — $total match; add a field/slot to what exists"
27
+ else
28
+ echo "VERDICT: build — no existing match for '$q'"
29
+ fi
30
+ exit 0
@@ -0,0 +1,43 @@
1
+ #!/usr/bin/env bash
2
+ # do-tier.sh — the token-economy spine-pruner. Infer the loop tier from the changed files
3
+ # (+ optional --intent) and emit the pruned spine, the inner classifier, and a per-tier
4
+ # token ceiling. The human never picks: this one signal sizes both outer and inner work.
5
+ # DEFAULT DOWN when unsure — an under-built FIX re-opens cheaply; an over-built PATCH is burnt.
6
+ # Usage: do-tier.sh [--intent "text"] <path>... | git diff --name-only | do-tier.sh [--intent ...]
7
+ set -euo pipefail
8
+
9
+ intent=""
10
+ if [ "${1:-}" = "--intent" ]; then intent="$2"; shift 2; fi
11
+
12
+ paths=()
13
+ if [ "$#" -gt 0 ]; then paths=("$@"); else while IFS= read -r l; do [ -n "$l" ] && paths+=("$l"); done; fi
14
+ [ "${#paths[@]}" -eq 0 ] && paths=("")
15
+
16
+ n=${#paths[@]}
17
+ schema=false; code=false; doconly=true
18
+ for p in "${paths[@]}"; do
19
+ case "$p" in
20
+ *.tql|schema/*) schema=true; code=true; doconly=false ;;
21
+ *.md|.claude/*|plans/*|text/*|docs/*) ;;
22
+ "") ;;
23
+ *) code=true; doconly=false ;;
24
+ esac
25
+ done
26
+
27
+ # tier inference — first match wins, biased downward
28
+ if $schema; then tier=SCHEMA
29
+ elif printf '%s' "$intent" | grep -qiE '\b(add|new|introduce|build a|feature|capability)\b'; then tier=FEATURE
30
+ elif $doconly && [ "$n" -le 2 ]; then tier=PATCH
31
+ elif ! $code && [ "$n" -le 2 ]; then tier=PATCH
32
+ elif ! $code; then tier=FIX
33
+ else tier=FIX
34
+ fi
35
+
36
+ case "$tier" in
37
+ PATCH) spine="code verify"; cls=TRIVIAL; ceil=5000 ;;
38
+ FIX) spine="survey code tests proof"; cls=SIMPLE; ceil=30000 ;;
39
+ FEATURE) spine="promise survey spec clarify todo analyze code tests proof docs release"; cls=COMPLEX; ceil=150000 ;;
40
+ SCHEMA) spine="promise survey spec reconcile clarify todo analyze code tests proof docs release"; cls=COMPLEX; ceil=200000 ;;
41
+ esac
42
+
43
+ printf '{"tier":"%s","spine":"%s","classifier":"%s","ceiling_tokens":%s}\n' "$tier" "$spine" "$cls" "$ceil"