@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.
- package/.claude-plugin/plugin.json +16 -0
- package/.mcp.json +12 -0
- package/README.md +204 -0
- package/agents/w1-recon.md +102 -0
- package/agents/w2-decide.md +164 -0
- package/agents/w3-edit.md +91 -0
- package/agents/w4-verify.md +416 -0
- package/commands/browser.md +55 -0
- package/commands/cc-connect.md +67 -0
- package/commands/claw.md +135 -0
- package/commands/close.md +143 -0
- package/commands/create.md +78 -0
- package/commands/deploy.md +415 -0
- package/commands/do-autonomous.md +80 -0
- package/commands/do-improve.md +51 -0
- package/commands/do-show.md +89 -0
- package/commands/do.md +226 -0
- package/commands/improve.md +99 -0
- package/commands/kill.md +45 -0
- package/commands/release.md +144 -0
- package/commands/see.md +161 -0
- package/commands/setup.md +75 -0
- package/commands/sync.md +185 -0
- package/hooks/hooks.json +90 -0
- package/hooks/lib/signal.sh +28 -0
- package/hooks/scripts/design-check.sh +83 -0
- package/hooks/scripts/post-edit-check.sh +32 -0
- package/hooks/scripts/session-end-verify.sh +51 -0
- package/hooks/scripts/session-start.sh +88 -0
- package/hooks/scripts/stop-reflect.sh +95 -0
- package/hooks/scripts/sync-todo-docs.sh +46 -0
- package/hooks/scripts/task-complete-verify.sh +52 -0
- package/hooks/scripts/tool-signal.sh +48 -0
- package/package.json +33 -0
- package/rules/api.md +50 -0
- package/rules/astro.md +206 -0
- package/rules/design.md +221 -0
- package/rules/documentation.md +218 -0
- package/rules/engine.md +297 -0
- package/rules/react.md +137 -0
- package/rules/ui.md +82 -0
- package/scripts/cc-connect.sh +345 -0
- package/scripts/do-analyze.sh +42 -0
- package/scripts/do-folder.sh +63 -0
- package/scripts/do-prove.sh +51 -0
- package/scripts/do-reconcile.sh +28 -0
- package/scripts/do-smoke.sh +60 -0
- package/scripts/do-survey.sh +30 -0
- package/scripts/do-tier.sh +43 -0
- package/skills/build/SKILL.md +52 -0
- package/skills/cloudflare/SKILL.md +503 -0
- package/skills/dev/SKILL.md +58 -0
- package/skills/do/SKILL.md +24 -0
- package/skills/oneie/SKILL.md +51 -0
- package/skills/perf/SKILL.md +45 -0
- package/skills/signal/SKILL.md +108 -0
- package/skills/sui/SKILL.md +441 -0
- package/skills/tutorial/SKILL.md +96 -0
- 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"
|