@oneie/claude 0.6.0 → 0.7.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 (111) hide show
  1. package/agents/w1-recon.md +9 -4
  2. package/agents/w2-decide.md +51 -28
  3. package/agents/w3-edit.md +70 -78
  4. package/agents/w4-verify.md +164 -69
  5. package/commands/browser.md +98 -35
  6. package/commands/cc-connect.md +6 -0
  7. package/commands/chat.md +13 -1
  8. package/commands/close.md +45 -19
  9. package/commands/create.md +20 -10
  10. package/commands/db-sync.md +5 -1
  11. package/commands/deploy.md +174 -87
  12. package/commands/do-autonomous.md +31 -20
  13. package/commands/do-improve.md +4 -4
  14. package/commands/do-show.md +4 -3
  15. package/commands/do.md +53 -13
  16. package/commands/go.md +75 -0
  17. package/commands/improve.md +4 -2
  18. package/commands/kill.md +6 -8
  19. package/commands/notify.md +15 -2
  20. package/commands/oo-push.md +24 -9
  21. package/commands/rag.md +6 -0
  22. package/commands/release.md +46 -25
  23. package/commands/restart.md +5 -4
  24. package/commands/see.md +45 -35
  25. package/commands/skill-create.md +38 -5
  26. package/commands/sync.md +21 -11
  27. package/hooks/scripts/auto-continue.sh +70 -0
  28. package/hooks/scripts/branch-pin.sh +85 -0
  29. package/hooks/scripts/git-add-guard.sh +16 -5
  30. package/hooks/scripts/post-edit-check.sh +16 -0
  31. package/hooks/scripts/session-start.sh +13 -8
  32. package/hooks/scripts/sync-priority-todo.sh +5 -0
  33. package/package.json +1 -1
  34. package/rules/api.md +9 -0
  35. package/rules/design.md +13 -3
  36. package/rules/documentation.md +30 -71
  37. package/rules/engine.md +8 -1
  38. package/rules/ui.md +5 -1
  39. package/scripts/__pycache__/do-rank.cpython-313.pyc +0 -0
  40. package/scripts/browser-check.mjs +44 -103
  41. package/scripts/cc-events-proof.sh +13 -12
  42. package/scripts/chrome.mjs +381 -0
  43. package/scripts/do-analyze.sh +109 -4
  44. package/scripts/do-auto.sh +350 -43
  45. package/scripts/do-consumer-sweep.sh +72 -0
  46. package/scripts/do-derives-check.sh +88 -0
  47. package/scripts/do-fleet.sh +50 -1
  48. package/scripts/do-killswitch-audit.py +33 -14
  49. package/scripts/do-promise-lint.sh +150 -4
  50. package/scripts/do-promise-settle.sh +131 -9
  51. package/scripts/do-prove.sh +197 -17
  52. package/scripts/do-rank.py +606 -20
  53. package/scripts/do-rubric.py +21 -2
  54. package/scripts/do-signal.sh +157 -17
  55. package/scripts/do-smoke.sh +9 -0
  56. package/scripts/do-substrate-check.sh +49 -0
  57. package/scripts/do-tasks-wire-check.sh +81 -0
  58. package/scripts/do-tier.sh +90 -5
  59. package/scripts/do-ui-gate.sh +332 -0
  60. package/scripts/do-untracked-gate.sh +231 -0
  61. package/scripts/do-walk.sh +344 -0
  62. package/scripts/factory-check.sh +747 -0
  63. package/scripts/factory-repo.sh +1361 -0
  64. package/scripts/factory-tasks-check.sh +85 -0
  65. package/scripts/outcome-pull.ts +279 -0
  66. package/scripts/promise-manifest.mjs +167 -0
  67. package/scripts/reconcile-allow.txt +5 -0
  68. package/scripts/rubric-weights.json +64 -0
  69. package/scripts/spine-canary.sh +20 -0
  70. package/scripts/typedb-cluster-status.sh +128 -0
  71. package/scripts/typedb-env.sh +182 -0
  72. package/scripts/typedb-flap-recorder.sh +85 -0
  73. package/scripts/typedb-probes/containment-probe.py +88 -0
  74. package/scripts/typedb-probes/panic-probe-version.py +92 -0
  75. package/scripts/typedb-probes/panic-probe.py +77 -0
  76. package/scripts/typedb-scratch.sh +73 -0
  77. package/scripts/urls-lint.sh +58 -0
  78. package/scripts/w4-rubric.ts +96 -12
  79. package/scripts/wf-check.mjs +75 -0
  80. package/skills/ai-ui/SKILL.md +1 -1
  81. package/skills/astro/SKILL.md +177 -132
  82. package/skills/astro/add-content-collection.md +29 -13
  83. package/skills/astro/check-build.md +19 -13
  84. package/skills/astro/create-component.md +36 -9
  85. package/skills/astro/create-page.md +39 -10
  86. package/skills/astro/optimize-performance.md +36 -8
  87. package/skills/cli/SKILL.md +254 -220
  88. package/skills/dev/SKILL.md +1 -1
  89. package/skills/directory-autofill/SKILL.md +68 -0
  90. package/skills/docs/SKILL.md +4 -4
  91. package/skills/mcp/SKILL.md +186 -156
  92. package/skills/promise-make/SKILL.md +2 -2
  93. package/skills/puck/SKILL.md +465 -250
  94. package/skills/react19/SKILL.md +196 -296
  95. package/skills/reactflow/SKILL.md +387 -412
  96. package/skills/sdk/SKILL.md +167 -106
  97. package/skills/shadcn/SKILL.md +245 -201
  98. package/skills/signal/SKILL.md +5 -0
  99. package/skills/sui/SKILL.md +6 -1
  100. package/skills/tutorial/SKILL.md +15 -0
  101. package/skills/typedb/SKILL.md +427 -217
  102. package/skills/typedb/examples/python-patterns.py +11 -4
  103. package/skills/typedb/examples/query-patterns.tql +8 -3
  104. package/skills/typedb/examples/schema-patterns.tql +4 -2
  105. package/skills/typedb/reference/migration-2x-3x.md +19 -1
  106. package/skills/typedb/reference/python-driver.md +17 -1
  107. package/skills/voice/SKILL.md +117 -0
  108. package/skills/writer/SKILL.md +29 -12
  109. package/templates/template-feature.md +26 -1
  110. package/templates/template-tests.md +4 -0
  111. package/templates/template-todo.md +65 -10
@@ -0,0 +1,73 @@
1
+ #!/usr/bin/env bash
2
+ # typedb-scratch.sh — create (or refresh) a LOCAL scratch database carrying the
3
+ # live `one` schema, for integration tests that must never touch `one`.
4
+ #
5
+ # Why this exists: `factory-check.sh belief-guard` runs a real-substrate test
6
+ # (repo rule: never mock TypeDB) and needs TYPEDB_TEST_DB pointing at a database
7
+ # that already has the schema — the test defines none of its own. Hand-building
8
+ # that db is a step nobody remembers, and an empty db reports as `cannot run`, so
9
+ # the check silently never proves anything.
10
+ #
11
+ # Usage:
12
+ # bash .claude/scripts/typedb-scratch.sh [name] # default: belieftest
13
+ # export TYPEDB_TEST_DB=belieftest # then run the checks
14
+ #
15
+ # Refuses anything but a local substrate: this DROPS and recreates the database.
16
+ set -uo pipefail
17
+ ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
18
+ NAME="${1:-belieftest}"
19
+
20
+ if [ "$NAME" = "one" ]; then
21
+ echo "refusing: 'one' is the working database, not a scratch db" >&2; exit 2
22
+ fi
23
+
24
+ # An explicitly-exported TYPEDB_URL wins over .env. Without this the eval below
25
+ # silently overwrites it, which makes the local-only guard untestable and means a
26
+ # caller who thinks they are pointing somewhere else is not.
27
+ _ENV_URL="${TYPEDB_URL:-}"
28
+ eval "$(python3 - "$ROOT/one.ie/web/.env" <<'PY'
29
+ import re,sys,shlex
30
+ for line in open(sys.argv[1], errors="replace"):
31
+ m = re.match(r'^\s*(TYPEDB_(?:URL|DATABASE|USERNAME|PASSWORD))\s*=\s*(.*?)\s*$', line)
32
+ if m: print(f"{m.group(1)}={shlex.quote(m.group(2).strip(chr(34)+chr(39)))}")
33
+ PY
34
+ )"
35
+ : "${TYPEDB_URL:=}" "${TYPEDB_DATABASE:=one}" "${TYPEDB_USERNAME:=admin}" "${TYPEDB_PASSWORD:=}"
36
+ [ -n "$_ENV_URL" ] && TYPEDB_URL="$_ENV_URL"
37
+
38
+ case "$TYPEDB_URL" in
39
+ *127.0.0.1*|*localhost*) ;;
40
+ *) echo "refusing: TYPEDB_URL is not local ($TYPEDB_URL). Run typedb-env.sh dev first." >&2; exit 2 ;;
41
+ esac
42
+
43
+ TOK=$(curl -s -m 15 -X POST "$TYPEDB_URL/v1/signin" -H 'Content-Type: application/json' \
44
+ -d "{\"username\":\"$TYPEDB_USERNAME\",\"password\":\"$TYPEDB_PASSWORD\"}" \
45
+ | python3 -c 'import sys,json;print(json.load(sys.stdin).get("token",""))' 2>/dev/null)
46
+ [ -z "$TOK" ] && { echo "cannot run: local TypeDB signin failed at $TYPEDB_URL" >&2; exit 3; }
47
+
48
+ SCHEMA=$(mktemp); trap 'rm -f "$SCHEMA" "$REQ"' EXIT
49
+ curl -s -m 30 -H "Authorization: Bearer $TOK" "$TYPEDB_URL/v1/databases/$TYPEDB_DATABASE/schema" > "$SCHEMA"
50
+ [ -s "$SCHEMA" ] || { echo "cannot run: could not read the $TYPEDB_DATABASE schema" >&2; exit 3; }
51
+
52
+ curl -s -o /dev/null -X DELETE "$TYPEDB_URL/v1/databases/$NAME" -H "Authorization: Bearer $TOK"
53
+ curl -s -o /dev/null -X POST "$TYPEDB_URL/v1/databases/$NAME" -H "Authorization: Bearer $TOK"
54
+
55
+ # ensure_ascii=False is load-bearing: json.dumps' default escapes non-ASCII to
56
+ # \uXXXX, and a \uXXXX inside TypeQL PANICS TypeDB 3.8.3 — it kills the server
57
+ # process rather than returning an error. See
58
+ # text/typedb-production-problem-solutions.md § Two queries that kill a 3.8.3 server.
59
+ REQ=$(mktemp)
60
+ python3 - "$SCHEMA" "$NAME" > "$REQ" <<'PY'
61
+ import json,sys
62
+ print(json.dumps({"databaseName": sys.argv[2], "transactionType": "schema",
63
+ "query": open(sys.argv[1]).read()}, ensure_ascii=False))
64
+ PY
65
+ RES=$(curl -s -m 60 -X POST "$TYPEDB_URL/v1/query" -H "Authorization: Bearer $TOK" \
66
+ -H 'Content-Type: application/json' --data-binary @"$REQ")
67
+
68
+ case "$RES" in
69
+ *'"answerType":"ok"'*)
70
+ echo "scratch db '$NAME' ready — $(wc -c < "$SCHEMA" | tr -d ' ') chars of schema"
71
+ echo " export TYPEDB_TEST_DB=$NAME" ;;
72
+ *) echo "failed to define schema in '$NAME': ${RES:0:300}" >&2; exit 1 ;;
73
+ esac
@@ -0,0 +1,58 @@
1
+ #!/usr/bin/env bash
2
+ # urls-lint.sh — shrink-only root lint (text/urls.md § reserved prefix set).
3
+ #
4
+ # one.ie's root-level src/pages/*.astro is frozen at the set below (verified
5
+ # 2026-07-20). A new top-level page must be added to this allowlist by hand —
6
+ # an amendment to the law, never a silent file drop. The set can only shrink
7
+ # (a deletion needs no allowlist change); any file NOT on the list fails.
8
+ set -euo pipefail
9
+
10
+ PAGES_DIR="one.ie/web/src/pages"
11
+
12
+ # The frozen baseline — every root .astro page that existed at promise-making,
13
+ # grouped by text/urls-plan.md's route-classification buckets (platform /
14
+ # console-slugless / identity / system / redirect-pending-deletion / dead).
15
+ FROZEN_ROOT_PAGES=(
16
+ # platform — marketing/product
17
+ index pricing agents agency affiliates brand chatbots components contracts
18
+ crm-platform design developers "enterprise-license" "free-license" "get-yours"
19
+ join leaderboard license marketing "marketing-studio" marketplace models
20
+ partners playbook proof scale security showcase speed sui teams tools
21
+ upgrade "thankyou-playbook"
22
+ # console-slugless
23
+ dashboard settings analytics chat skills build create editor in memory
24
+ profile credits payments rewards tracking watch stream meet classroom
25
+ learning activity do cc device motion "self-improving"
26
+ # identity
27
+ signin signup recover "recovery-codes" "auth-security" unsubscribe
28
+ # system
29
+ 404 500
30
+ # redirect — superseded, C4 deletes these; kept on the allowlist so the lint
31
+ # is green both before and after that deletion (shrinking, never growing)
32
+ "index-new" "index-open" home
33
+ # dead — unreferenced, flagged for a future lever-4 cycle, not this promise
34
+ dotsdemo
35
+ )
36
+
37
+ is_allowed() {
38
+ local name="$1"
39
+ for allowed in "${FROZEN_ROOT_PAGES[@]}"; do
40
+ [ "$name" = "$allowed" ] && return 0
41
+ done
42
+ return 1
43
+ }
44
+
45
+ fail=0
46
+ for file in "$PAGES_DIR"/*.astro; do
47
+ [ -e "$file" ] || continue
48
+ base="$(basename "$file" .astro)"
49
+ if ! is_allowed "$base"; then
50
+ echo "urls-lint: FAIL — '$base.astro' is not on the frozen root allowlist (text/urls.md § reserved prefix set). Root can only shrink." >&2
51
+ fail=1
52
+ fi
53
+ done
54
+
55
+ if [ "$fail" -eq 0 ]; then
56
+ echo "urls-lint: OK — every root page is accounted for."
57
+ fi
58
+ exit "$fail"
@@ -1,5 +1,15 @@
1
1
  #!/usr/bin/env bun
2
- // w4-rubric.ts — real rubric judge for /do W4.
2
+ // w4-rubric.ts — the TASK rubric judge (5 axes, no goal-fit).
3
+ //
4
+ // This computes the `task` rubric, NOT the in-cycle W4 cycle gate. The two are
5
+ // different by design: a diff handed to this script carries no plan context, so
6
+ // goal-fit cannot be scored here. The cycle gate adds goal-fit at 0.30 and gates
7
+ // on it at >= 0.50 — see .claude/commands/do.md § W4. Its output is labelled
8
+ // `task-composite` so it can never be read as a cycle verdict.
9
+ //
10
+ // Weights and axis definitions come from rubric-weights.json — the single
11
+ // source. Do not restate them here.
12
+ //
3
13
  // Usage: w4-rubric.ts [edge] [--self-test] [--gate 0.65]
4
14
  // Reads diff from stdin. Exits 0=pass, 1=fail, 2=defer-to-agent.
5
15
  import { readFileSync, existsSync } from 'fs'
@@ -7,30 +17,76 @@ import { readFileSync, existsSync } from 'fs'
7
17
  const args = process.argv.slice(2)
8
18
  const selfTest = args.includes('--self-test')
9
19
  const edge = args.find(a => !a.startsWith('--')) ?? (process.env.DO_SLUG ? `do/${process.env.DO_SLUG}` : 'do/unknown')
10
- const gate = parseFloat(args.find(a => a.startsWith('--gate='))?.slice(7) ?? '0.65')
20
+
21
+ // The weights file sits beside this script. A missing or malformed file is a
22
+ // hard failure, never a fallback to inlined numbers — a silent divergence
23
+ // between the file and a hardcoded copy is the exact drift this file removes.
24
+ const WEIGHTS_PATH = new URL('./rubric-weights.json', import.meta.url).pathname
25
+ type Rubric = {
26
+ gate: number
27
+ task: { weights: Record<string, number> }
28
+ definitions: Record<string, string>
29
+ }
30
+ function loadRubric(): Rubric {
31
+ try {
32
+ return JSON.parse(readFileSync(WEIGHTS_PATH, 'utf8')) as Rubric
33
+ } catch (e) {
34
+ process.stderr.write(`[w4-rubric] cannot read ${WEIGHTS_PATH}: ${(e as Error).message}\n`)
35
+ process.exit(1)
36
+ throw e // unreachable — process.exit never returns; satisfies the checker without @types/node
37
+ }
38
+ }
39
+ const RUBRIC = loadRubric()
40
+ const W = RUBRIC.task.weights
41
+ const AXES = Object.keys(W)
42
+ const wSum = AXES.reduce((n, k) => n + W[k], 0)
43
+ if (Math.abs(wSum - 1) > 1e-9) {
44
+ process.stderr.write(`[w4-rubric] task weights sum to ${wSum}, not 1.00 — refusing to score\n`)
45
+ process.exit(1)
46
+ }
47
+
48
+ const gate = parseFloat(args.find(a => a.startsWith('--gate='))?.slice(7) ?? String(RUBRIC.gate))
11
49
  // integration is deterministic when the caller knows it (W4 computes it from
12
50
  // .w2-surface-checklist.json + do-reconcile.sh sdk|navigation|docs and passes
13
51
  // --integration=0.NN); only when absent does the LLM score it from the diff.
14
52
  const integrationArg = args.find(a => a.startsWith('--integration='))?.slice(14)
15
- const W = { security: 0.30, stability: 0.25, simplicity: 0.20, integration: 0.15, speed: 0.10 }
16
53
 
17
54
  function composite(s: Record<string,number>) {
18
- return s.security*W.security + s.stability*W.stability + s.simplicity*W.simplicity + s.integration*W.integration + s.speed*W.speed
55
+ return AXES.reduce((n, k) => n + s[k] * W[k], 0)
19
56
  }
20
57
 
58
+ // --self-test vectors. `pass` is the historical fixture; `fail` exists so the
59
+ // gate can be shown going RED. A gate that has only ever been observed passing
60
+ // is indistinguishable from a gate that cannot fail.
61
+ const SELF_TEST_SCORES: Record<string, number> =
62
+ { security: 0.90, stability: 1.00, simplicity: 0.80, integration: 0.85, speed: 0.75 }
63
+ const SELF_TEST_FAIL: Record<string, number> =
64
+ { security: 0.40, stability: 0.40, simplicity: 0.40, integration: 0.40, speed: 0.40 }
65
+
21
66
  async function judge(diff: string): Promise<Record<string,number>> {
22
- const envFile = process.env.DO_ENV_FILE ?? 'one.ie/web/.env'
67
+ const envFile = process.env.ONE_ENV_FILE ?? process.env.DO_ENV_FILE ?? 'one.ie/web/.env'
23
68
  let key = process.env.OPENROUTER_API_KEY ?? ''
24
69
  if (!key && existsSync(envFile)) {
25
70
  const m = readFileSync(envFile,'utf8').split('\n').find(l => l.startsWith('OPENROUTER_API_KEY='))
26
71
  if (m) key = m.slice(m.indexOf('=')+1).replace(/^["']|["']$/g,'')
27
72
  }
28
73
  if (!key) { process.stderr.write('[w4-rubric] no OPENROUTER_API_KEY — deferring\n'); process.exit(2) }
29
- const res = await fetch('https://openrouter.ai/api/v1/chat/completions', {
74
+ const gwAccount = process.env.CF_AI_GATEWAY_ACCOUNT_ID
75
+ const gwId = process.env.CF_AI_GATEWAY_ID
76
+ const orBase = gwAccount && gwId
77
+ ? `https://gateway.ai.cloudflare.com/v1/${gwAccount}/${gwId}/openrouter`
78
+ : 'https://openrouter.ai/api/v1'
79
+ const res = await fetch(`${orBase}/chat/completions`, {
30
80
  method:'POST', headers:{'Authorization':`Bearer ${key}`,'Content-Type':'application/json'},
31
81
  body:JSON.stringify({ model:'anthropic/claude-haiku-4-5', max_tokens:80,
32
82
  messages:[
33
- {role:'system',content:'Score this diff on security/stability/simplicity/integration/speed (0-1). integration = the change is wired into its surroundings (imports/exports used, routes registered, links added, docs synced), not orphaned. JSON only: {"security":0.9,"stability":0.8,"simplicity":0.7,"integration":0.8,"speed":0.8}'},
83
+ // Every axis carries its definition from rubric-weights.json. An axis
84
+ // scored without one is scored against whatever the model invents that
85
+ // run — `stability` carried 0.25 weight undefined until 2026-08-02.
86
+ {role:'system',content:
87
+ `Score this diff on ${AXES.join('/')} (0-1). Definitions:\n`
88
+ + AXES.map(k => `- ${k}: ${RUBRIC.definitions[k] ?? '(UNDEFINED — score 0 and say so)'}`).join('\n')
89
+ + `\nJSON only, exactly these keys: {${AXES.map(k => `"${k}":0.8`).join(',')}}`},
34
90
  {role:'user',content:diff.slice(0,3000)}
35
91
  ]})
36
92
  })
@@ -39,21 +95,49 @@ async function judge(diff: string): Promise<Record<string,number>> {
39
95
  if (!m2) { process.stderr.write('[w4-rubric] bad LLM response — deferring\n'); process.exit(2) }
40
96
  const d = JSON.parse(m2[0]) as Record<string,unknown>
41
97
  const c = (v:unknown) => Math.max(0, Math.min(1, Number(v)||0))
42
- return { security:c(d.security), stability:c(d.stability), simplicity:c(d.simplicity), integration:c(d.integration), speed:c(d.speed) }
98
+ // An axis the model omitted must not silently become 0 inside a weighted sum —
99
+ // that reads as "scored badly" when it means "not scored at all".
100
+ const missing = AXES.filter(k => d[k] === undefined)
101
+ if (missing.length) {
102
+ process.stderr.write(`[w4-rubric] LLM omitted ${missing.join(',')} — deferring\n`)
103
+ process.exit(2)
104
+ }
105
+ return Object.fromEntries(AXES.map(k => [k, c(d[k])])) as Record<string, number>
43
106
  }
44
107
 
45
108
  async function main() {
46
- const scores = selfTest
47
- ? { security:0.90, stability:1.00, simplicity:0.80, integration:0.85, speed:0.75 }
109
+ const failFixture = args.includes('--self-test-fail')
110
+ const scores: Record<string, number> = (selfTest || failFixture)
111
+ ? Object.fromEntries(AXES.map(k => [k, (failFixture ? SELF_TEST_FAIL : SELF_TEST_SCORES)[k] ?? 0.80]))
48
112
  : await judge(await new Promise<string>(r => { let s=''; process.stdin.on('data',c=>s+=c); process.stdin.on('end',()=>r(s)) }))
49
113
  // deterministic override wins: the surface checklist is truth, not vibes
50
114
  if (integrationArg !== undefined) scores.integration = Math.max(0, Math.min(1, parseFloat(integrationArg)||0))
51
115
  const comp = composite(scores)
52
116
  const baseUrl = process.env.DO_SIGNAL_URL ?? 'https://one.ie'
53
117
  if (!selfTest && edge !== 'do/unknown') {
54
- await fetch(`${baseUrl}/api/mark-dims`, {method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({edge,dims:scores})}).catch(()=>{})
118
+ // /api/mark-dims is gate()-guarded (security-gates #5) — it authenticates a service
119
+ // caller by Bearer === GATEWAY_API_KEY. Resolve it the same way judge() resolves the
120
+ // OpenRouter key (process.env → DO_ENV_FILE). Without it the marks silently 401 and
121
+ // the /do rubric-learning signal stops landing — a break the #5 gate introduced.
122
+ const envFile = process.env.ONE_ENV_FILE ?? process.env.DO_ENV_FILE ?? 'one.ie/web/.env'
123
+ let gwKey = process.env.GATEWAY_API_KEY ?? ''
124
+ if (!gwKey && existsSync(envFile)) {
125
+ const m = readFileSync(envFile,'utf8').split('\n').find(l => l.startsWith('GATEWAY_API_KEY='))
126
+ if (m) gwKey = m.slice(m.indexOf('=')+1).replace(/^["']|["']$/g,'')
127
+ }
128
+ await fetch(`${baseUrl}/api/mark-dims`, {
129
+ method:'POST',
130
+ headers:{'Content-Type':'application/json', ...(gwKey ? {'Authorization':`Bearer ${gwKey}`} : {})},
131
+ body:JSON.stringify({edge,dims:scores}),
132
+ }).catch(()=>{})
55
133
  }
56
- process.stdout.write(`[w4-rubric] composite=${comp.toFixed(2)} security=${scores.security.toFixed(2)} stability=${scores.stability.toFixed(2)} simplicity=${scores.simplicity.toFixed(2)} integration=${scores.integration.toFixed(2)} speed=${scores.speed.toFixed(2)} gate=${gate} ${comp>=gate?'PASS':'FAIL'}\n`)
134
+ // `task-composite`, never bare `composite` the cycle gate carries goal-fit
135
+ // at 0.30 and this number does not. Anything that logs, compares, or ratchets
136
+ // on it is comparing a task score, and the label has to say so.
137
+ const dims = AXES.map(k => `${k}=${scores[k].toFixed(2)}`).join(' ')
138
+ process.stdout.write(
139
+ `[w4-rubric] task-composite=${comp.toFixed(2)} ${dims} gate=${gate} ${comp>=gate?'PASS':'FAIL'}`
140
+ + ` (task rubric — no goal-fit; NOT the in-cycle W4 verdict)\n`)
57
141
  process.exit(comp >= gate ? 0 : 1)
58
142
  }
59
143
 
@@ -0,0 +1,75 @@
1
+ #!/usr/bin/env node
2
+ // wf-check — syntax-check dynamic workflow scripts the way the RUNTIME parses them.
3
+ //
4
+ // Why this exists: a syntax error in a workflow script doesn't fail loudly, it stops
5
+ // the workflow from launching at all. do-engine.js already carries a scar comment
6
+ // about one such case (a second top-level `export` is illegal in a workflow body).
7
+ //
8
+ // Why the obvious checkers are wrong for this file shape:
9
+ // node --check <f>.js → silently exits 0 for ANY file starting with `export`
10
+ // (verified on node v24.15.0 — the ESM path is a no-op).
11
+ // node --check <f>.mjs → real ESM parse, but rejects the top-level `return` and
12
+ // `await` that every workflow body legitimately uses.
13
+ // The runtime wraps the body in an async function, so that is what we parse it as.
14
+ //
15
+ // Usage: node .claude/scripts/wf-check.mjs [file ...]
16
+ // (no args → every .js under .claude/workflows/)
17
+ // Exit 0 = all green. Exit 1 = at least one script would fail to launch.
18
+
19
+ import { readFileSync, readdirSync, existsSync } from 'node:fs'
20
+ import { join } from 'node:path'
21
+
22
+ const WF_DIR = '.claude/workflows'
23
+ const AsyncFunction = Object.getPrototypeOf(async function () {}).constructor
24
+ const HOOKS = ['args', 'budget', 'agent', 'parallel', 'pipeline', 'log', 'phase', 'workflow']
25
+
26
+ function check(file) {
27
+ let src
28
+ try {
29
+ src = readFileSync(file, 'utf8')
30
+ } catch (e) {
31
+ return { file, ok: false, msg: `unreadable: ${e.message}` }
32
+ }
33
+
34
+ // The loader permits exactly one top-level export: the leading `export const meta`.
35
+ const exports = src.match(/^export\s/gm) ?? []
36
+ if (exports.length > 1) {
37
+ return { file, ok: false, msg: `${exports.length} top-level exports — only the leading \`export const meta\` is permitted` }
38
+ }
39
+ if (!/^export\s+const\s+meta\s*=/m.test(src)) {
40
+ return { file, ok: false, msg: 'missing the required leading `export const meta = {...}`' }
41
+ }
42
+
43
+ try {
44
+ new AsyncFunction(...HOOKS, src.replace(/^export\s+const\s+meta/m, 'const meta'))
45
+ return { file, ok: true }
46
+ } catch (e) {
47
+ return { file, ok: false, msg: e.message }
48
+ }
49
+ }
50
+
51
+ const args = process.argv.slice(2)
52
+ const files = args.length
53
+ ? args
54
+ : existsSync(WF_DIR)
55
+ ? readdirSync(WF_DIR).filter((f) => f.endsWith('.js')).map((f) => join(WF_DIR, f))
56
+ : []
57
+
58
+ if (!files.length) {
59
+ console.log('wf-check: no workflow scripts found')
60
+ process.exit(0)
61
+ }
62
+
63
+ let failed = 0
64
+ for (const f of files) {
65
+ const r = check(f)
66
+ if (r.ok) {
67
+ console.log(` ok ${r.file}`)
68
+ } else {
69
+ failed++
70
+ console.log(` FAIL ${r.file}: ${r.msg}`)
71
+ }
72
+ }
73
+
74
+ console.log(`wf-check: ${files.length - failed}/${files.length} green`)
75
+ process.exit(failed ? 1 : 0)
@@ -1,5 +1,5 @@
1
1
  ---
2
- name: ai-elements
2
+ name: ai-ui
3
3
  description: Build advanced AI UIs with agent reasoning, tool visualization, generative UI components, and composable AI Elements for agentic interfaces
4
4
  ---
5
5