@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,83 @@
1
+ #!/bin/bash
2
+ # DESIGN CHECK — enforces the 6-token design system on every Write/Edit.
3
+ #
4
+ # Fires after Write/Edit/MultiEdit on web/**/*.{tsx,astro,css}.
5
+ # Greps the touched file for banned palette classes (zinc, indigo, ...),
6
+ # hex literals, raw hsl()/rgb() calls, and off-scale arbitrary values
7
+ # (p-[13px], rounded-[7px], text-[15px]) that escape the spacing/radius
8
+ # /typography scales. CSS var refs (text-[var(--size)]) are allowed.
9
+ #
10
+ # Exit 2 on violation — Claude Code feeds stderr back to the model so it
11
+ # self-corrects on the next turn. The edit already landed; the model fixes it.
12
+ #
13
+ # Allowlist: Layout.astro (token source) and design.astro (showcase).
14
+ # Spec: /Users/toc/Server/one-ie/one/design.md
15
+ # Rule: .claude/rules/design.md
16
+
17
+ # shellcheck source=lib/signal.sh
18
+ source "$CLAUDE_PROJECT_DIR/.claude/hooks/lib/signal.sh"
19
+
20
+ FILE=$(echo "$1" | jq -r '.tool_input.file_path // .tool_input.path // empty' 2>/dev/null)
21
+
22
+ # Scope: only web source files in the design system
23
+ if [[ -z "$FILE" ]]; then exit 0; fi
24
+ if [[ ! "$FILE" =~ /web/src/.*\.(tsx|astro|css)$ ]]; then exit 0; fi
25
+
26
+ # Allowlist — the token source and the showcase intentionally use raw HSL
27
+ BASE=$(basename "$FILE")
28
+ if [[ "$BASE" == "Layout.astro" ]] || [[ "$BASE" == "design.astro" ]]; then
29
+ emit_signal "hook:design-check:skip" 0 "file=$BASE allowlisted"
30
+ exit 0
31
+ fi
32
+
33
+ PALETTE='zinc|slate|gray|neutral|stone|red|orange|amber|yellow|lime|green|emerald|teal|cyan|sky|blue|indigo|violet|purple|fuchsia|pink|rose'
34
+ PROPS='bg|text|border|ring|from|to|via|fill|stroke|placeholder|caret|accent|decoration|divide|outline|shadow'
35
+ CLASS_RE="(${PROPS})-(${PALETTE})-[0-9]+"
36
+ LITERAL_RE='#[0-9a-fA-F]{3}([0-9a-fA-F]{3})?([0-9a-fA-F]{2})?\b|\b(hsl|rgb|hsla|rgba)\('
37
+
38
+ # Off-scale escape hatches: arbitrary spacing/sizing/radius/typography values.
39
+ # Spacing scale (design.md): 4/8/12/16/20/24/32. Radius: sm/md/lg (6/10/16).
40
+ # `text-[var(--size)]` is fine — only literal pixel/rem/em values get flagged.
41
+ # Width/height/size deliberately omitted — layout math (h-[calc(100vh-73px)],
42
+ # w-[380px] for a fixed widget) legitimately needs values off the 4-step scale.
43
+ ARBITRARY_PROPS='p|px|py|pt|pr|pb|pl|m|mx|my|mt|mr|mb|ml|gap|gap-x|gap-y|space-x|space-y|rounded|rounded-t|rounded-b|rounded-l|rounded-r|rounded-tl|rounded-tr|rounded-bl|rounded-br|text|leading|tracking'
44
+ ARBITRARY_RE="(^|[[:space:]\"'])(${ARBITRARY_PROPS})-\[[^]]+\]"
45
+
46
+ VIOLATIONS=$(
47
+ {
48
+ grep -nE "$CLASS_RE" "$FILE" 2>/dev/null
49
+ grep -nE "$LITERAL_RE" "$FILE" 2>/dev/null
50
+ grep -nE "$ARBITRARY_RE" "$FILE" 2>/dev/null | grep -v 'var(--'
51
+ } | head -20
52
+ )
53
+
54
+ if [[ -z "$VIOLATIONS" ]]; then
55
+ emit_signal "hook:design-check:ok" 1 "file=$BASE"
56
+ exit 0
57
+ fi
58
+
59
+ COUNT=$(echo "$VIOLATIONS" | wc -l | tr -d ' ')
60
+ emit_signal "hook:design-check:warn" -1 "file=$BASE violations=$COUNT"
61
+
62
+ cat >&2 <<EOF
63
+ ✗ Design system violation in $BASE — $COUNT line(s) escape the token / scale system.
64
+
65
+ $VIOLATIONS
66
+
67
+ The design system is 6 tokens: background, foreground, font, primary, secondary, tertiary.
68
+ Plus invariants: white, black, transparent.
69
+
70
+ Replace banned utilities:
71
+ bg-zinc-*, bg-indigo-*, etc. → bg-{background|foreground|primary|secondary|tertiary}
72
+ text-zinc-*, text-emerald-* → text-{font|primary|tertiary} (use /60, /40 for muted)
73
+ border-zinc-* → border-font/10
74
+ Hex literals (#fff, #abc) → use a token
75
+ hsl()/rgb() in source → only allowed in Layout.astro
76
+ p-[13px], m-[7px], gap-[5px] → snap to 4/8/12/16/20/24/32 (p-1, p-2, p-3, p-4, ...)
77
+ rounded-[7px] → rounded-md (10px) / rounded-lg (16px) / use --radius-*
78
+ text-[15px], leading-[18px] → Tailwind text-sm/base/lg or text-[var(--size-N)]
79
+
80
+ Spec: design.md · Rule: .claude/rules/design.md · Showcase: /design
81
+ EOF
82
+
83
+ exit 2
@@ -0,0 +1,32 @@
1
+ #!/bin/bash
2
+ # POST-EDIT CHECK — The deterministic sandwich around every file change
3
+ #
4
+ # Runs after Write/Edit. Checks the touched file with biome.
5
+ # Non-blocking (exit 0) — reports issues as warnings, doesn't halt.
6
+ # The W4 verify step does the blocking check.
7
+ # Emits hook:post-edit:{ok,warn} signal per Rule 1 (see .claude/skills/signal.md).
8
+
9
+ # shellcheck source=lib/signal.sh
10
+ source "$CLAUDE_PROJECT_DIR/.claude/hooks/lib/signal.sh"
11
+
12
+ FILE=$(echo "$1" | jq -r '.tool_input.file_path // .tool_input.path // empty' 2>/dev/null)
13
+
14
+ # Only check .ts/.tsx/.js/.jsx files
15
+ if [[ -z "$FILE" ]] || [[ ! "$FILE" =~ \.(ts|tsx|js|jsx)$ ]]; then
16
+ exit 0
17
+ fi
18
+
19
+ # Quick biome lint on the single file (non-blocking)
20
+ RESULT=$(cd "$CLAUDE_PROJECT_DIR" && bun biome check "$FILE" 2>&1)
21
+ EXIT=$?
22
+
23
+ BASE=$(basename "$FILE")
24
+ if [ $EXIT -ne 0 ]; then
25
+ ISSUES=$(echo "$RESULT" | grep -c "━━━" 2>/dev/null || echo "?")
26
+ echo "biome: ${ISSUES} issues in $BASE" >&2
27
+ emit_signal "hook:post-edit:warn" -0.5 "file=$BASE issues=$ISSUES"
28
+ else
29
+ emit_signal "hook:post-edit:ok" 1 "file=$BASE"
30
+ fi
31
+
32
+ exit 0 # Never block — just inform
@@ -0,0 +1,51 @@
1
+ #!/bin/bash
2
+ # SESSION-END VERIFY — Report regressions introduced during session
3
+ #
4
+ # Runs on Stop (session end). Non-blocking — just reports.
5
+ # Lets human see what might have broken without blocking the session.
6
+ # Emits hook:session-end:{ok,warn} signal per Rule 1 (see .claude/skills/signal.md).
7
+
8
+ # shellcheck source=lib/signal.sh
9
+ source "$CLAUDE_PROJECT_DIR/.claude/hooks/lib/signal.sh"
10
+
11
+ cd "$CLAUDE_PROJECT_DIR" || exit 0
12
+
13
+ echo ""
14
+ echo "┌─────────────────────────────────────────────────────────────┐"
15
+ echo "│ SESSION-END VERIFICATION │"
16
+ echo "└─────────────────────────────────────────────────────────────┘"
17
+ echo ""
18
+
19
+ # Run only biome (fast, ~0.3s). Skip tsc/vitest to avoid blocking session exit.
20
+ RESULT=$(bun biome check . 2>&1)
21
+ EXIT=$?
22
+
23
+ if [ $EXIT -eq 0 ]; then
24
+ echo "✓ All checks passed. Baseline is green."
25
+ echo ""
26
+ emit_signal "hook:session-end:ok" 1 "biome=clean"
27
+ exit 0
28
+ fi
29
+
30
+ # Extract test counts
31
+ PASSED=$(echo "$RESULT" | grep -o '[0-9]* pass' | head -1 | awk '{print $1}')
32
+ FAILED=$(echo "$RESULT" | grep -o '[0-9]* fail' | head -1 | awk '{print $1}')
33
+ ERRORS=$(echo "$RESULT" | grep -c "error TS" || echo "0")
34
+
35
+ echo "⚠ Issues detected:"
36
+ if [ -n "$FAILED" ] && [ "$FAILED" -gt 0 ]; then
37
+ echo " · Tests: $FAILED failed (was passing before?)"
38
+ fi
39
+ if [ "$ERRORS" -gt 0 ]; then
40
+ echo " · TypeScript: $ERRORS type errors"
41
+ fi
42
+
43
+ echo ""
44
+ echo "Next session should start with: bun run verify"
45
+ echo ""
46
+
47
+ # Mild warn — baseline dirty but session continuing
48
+ emit_signal "hook:session-end:warn" -0.5 "failed=${FAILED:-0} errors=${ERRORS:-0}"
49
+
50
+ # Non-blocking — always exit 0
51
+ exit 0
@@ -0,0 +1,88 @@
1
+ #!/usr/bin/env bash
2
+ # SESSION-START — surface unaddressed proposals + module-specific context.
3
+ #
4
+ # Closes the loop with stop-reflect.sh: stop emits proposals into
5
+ # .claude/improvements.queue.md; this surfaces them at next session start so
6
+ # they don't rot. Article calls this the self-improving harness pattern.
7
+ #
8
+ # Also prints the relevant skill list for the most recently touched module,
9
+ # so dev context loads dynamically rather than every-session.
10
+ #
11
+ # Output is brief — every line costs context.
12
+
13
+ # shellcheck source=lib/signal.sh
14
+ source "$CLAUDE_PROJECT_DIR/.claude/hooks/lib/signal.sh"
15
+
16
+ cd "$CLAUDE_PROJECT_DIR" || exit 0
17
+
18
+ QUEUE="$CLAUDE_PROJECT_DIR/.claude/improvements.queue.md"
19
+ PROPOSAL_COUNT=0
20
+
21
+ # Count unaddressed proposals (every "-" bullet under a session header)
22
+ if [ -f "$QUEUE" ]; then
23
+ PROPOSAL_COUNT=$(grep -c '^- \*\*' "$QUEUE" 2>/dev/null || echo 0)
24
+ fi
25
+
26
+ # Detect most-recently touched module — use mtime on a stable per-module marker.
27
+ # Falls back silently if nothing identifiable.
28
+ HOT_MODULE=""
29
+ HOT_HINT=""
30
+
31
+ # Parallel arrays — bash 3.2 compatible (macOS default).
32
+ MODULES=(
33
+ "one.ie/web"
34
+ "agents"
35
+ "api"
36
+ "packages/sdk"
37
+ "packages/mcp"
38
+ "packages/cli"
39
+ "sync"
40
+ "backup"
41
+ )
42
+ HINTS=(
43
+ "/astro · /react19 · /shadcn · /dev · /build"
44
+ "/cloudflare · /ai-sdk · /signal"
45
+ "/cloudflare · /typedb · /signal"
46
+ "/typecheck · /signal (substrate verbs)"
47
+ "/typecheck · MCP server"
48
+ "/typecheck · CLI bins"
49
+ "/cloudflare (Scheduled Worker)"
50
+ "/cloudflare (Scheduled Worker)"
51
+ )
52
+
53
+ NEWEST_TS=0
54
+ i=0
55
+ while [ $i -lt ${#MODULES[@]} ]; do
56
+ module="${MODULES[$i]}"
57
+ if [ -d "$module" ]; then
58
+ TS=$(find "$module" -type f \( -name '*.ts' -o -name '*.tsx' -o -name '*.astro' -o -name '*.md' \) \
59
+ -not -path '*/node_modules/*' -not -path '*/dist/*' -not -path '*/.wrangler/*' \
60
+ 2>/dev/null | head -200 | xargs -I{} stat -f '%m' {} 2>/dev/null | sort -rn | head -1)
61
+ if [ -n "$TS" ] && [ "$TS" -gt "$NEWEST_TS" ]; then
62
+ NEWEST_TS=$TS
63
+ HOT_MODULE=$module
64
+ HOT_HINT="${HINTS[$i]}"
65
+ fi
66
+ fi
67
+ i=$((i + 1))
68
+ done
69
+
70
+ # Render — keep it short. Only print if there's something to say.
71
+ OUT=""
72
+
73
+ if [ "$PROPOSAL_COUNT" -gt 0 ]; then
74
+ OUT+="📋 ${PROPOSAL_COUNT} unaddressed proposal(s) in .claude/improvements.queue.md\n"
75
+ fi
76
+
77
+ if [ -n "$HOT_MODULE" ]; then
78
+ OUT+="🔥 hot module: \`${HOT_MODULE}\` — relevant skills: ${HOT_HINT}\n"
79
+ fi
80
+
81
+ if [ -n "$OUT" ]; then
82
+ echo ""
83
+ printf "%b" "$OUT"
84
+ echo ""
85
+ fi
86
+
87
+ emit_signal "hook:session-start:ok" 1 "proposals=$PROPOSAL_COUNT hot=$HOT_MODULE"
88
+ exit 0
@@ -0,0 +1,95 @@
1
+ #!/usr/bin/env bash
2
+ # STOP-REFLECT — capture session learnings while context is fresh.
3
+ #
4
+ # Runs on Stop alongside session-end-verify. Non-blocking.
5
+ # Scans the session's git delta for signals of new conventions and appends
6
+ # proposals to .claude/improvements.queue.md for batch review.
7
+ #
8
+ # Heuristics (intentionally conservative — false positives = noise):
9
+ # 1. CLAUDE.md / rules / skills changed → record the change as a candidate
10
+ # convention for upstream propagation.
11
+ # 2. Code touched but no doc touched in same family → flag for doc-drift.
12
+ # 3. New file created under .claude/skills/ or .claude/rules/ → flag as new
13
+ # primitive (likely deserves a one-line README mention).
14
+ #
15
+ # Per Rule 1 (closed loop) — emits hook:stop-reflect:{ok,warn} with counts.
16
+ # shellcheck source=lib/signal.sh
17
+ source "$CLAUDE_PROJECT_DIR/.claude/hooks/lib/signal.sh"
18
+
19
+ cd "$CLAUDE_PROJECT_DIR" || exit 0
20
+
21
+ QUEUE="$CLAUDE_PROJECT_DIR/.claude/improvements.queue.md"
22
+ mkdir -p "$(dirname "$QUEUE")"
23
+
24
+ # What changed this session? Use unstaged + staged + untracked.
25
+ CHANGED=$(git status --porcelain 2>/dev/null | awk '{print $2}')
26
+ [ -z "$CHANGED" ] && { emit_signal "hook:stop-reflect:ok" 1 "changed=0"; exit 0; }
27
+
28
+ # Header for this session's block (idempotent — appends if file exists)
29
+ TS=$(date -u +%Y-%m-%dT%H:%M:%SZ)
30
+ PROPOSALS=0
31
+
32
+ # Buffer proposals; only write if non-empty
33
+ BUF=""
34
+
35
+ # (1) Convention-bearing files
36
+ CONVENTION_HITS=$(echo "$CHANGED" | grep -E '(CLAUDE\.md$|\.claude/rules/|\.claude/skills/|\.claude/agents/)' || true)
37
+ if [ -n "$CONVENTION_HITS" ]; then
38
+ while IFS= read -r f; do
39
+ BUF+="- **convention** \`$f\` — review: did this change introduce a rule the broader team should know? If yes, surface it in root \`CLAUDE.md\` or the relevant skill.\n"
40
+ PROPOSALS=$((PROPOSALS + 1))
41
+ done <<< "$CONVENTION_HITS"
42
+ fi
43
+
44
+ # (2) New primitives (untracked files in .claude/)
45
+ NEW_PRIMITIVES=$(echo "$CHANGED" | grep -E '^\.claude/(skills|rules|agents|hooks)/[^/]+\.(md|sh)$' | while read -r f; do
46
+ git ls-files --error-unmatch "$f" >/dev/null 2>&1 || echo "$f"
47
+ done)
48
+ if [ -n "$NEW_PRIMITIVES" ]; then
49
+ while IFS= read -r f; do
50
+ BUF+="- **new primitive** \`$f\` — add a one-line entry to the index (\`.claude/CLAUDE.md\` or root README skills table).\n"
51
+ PROPOSALS=$((PROPOSALS + 1))
52
+ done <<< "$NEW_PRIMITIVES"
53
+ fi
54
+
55
+ # (3) Doc drift — code touched in a family but no doc touched
56
+ # Family map: agents/src ↔ agents/CLAUDE.md, packages/sdk/src ↔ packages/sdk/CLAUDE.md,
57
+ # one.ie/web/src ↔ one.ie/web/CLAUDE.md
58
+ check_drift() {
59
+ local code_glob="$1"; local doc_file="$2"
60
+ local code_changed
61
+ code_changed=$(echo "$CHANGED" | grep -E "$code_glob" || true)
62
+ if [ -n "$code_changed" ] && ! echo "$CHANGED" | grep -qF "$doc_file"; then
63
+ local count
64
+ count=$(echo "$code_changed" | wc -l | awk '{print $1}')
65
+ if [ "$count" -ge 3 ]; then
66
+ BUF+="- **doc-drift** \`$doc_file\` not updated despite $count file changes under \`$code_glob\`. Review.\n"
67
+ PROPOSALS=$((PROPOSALS + 1))
68
+ fi
69
+ fi
70
+ }
71
+ check_drift '^agents/src/' 'agents/CLAUDE.md'
72
+ check_drift '^packages/sdk/src/' 'packages/sdk/CLAUDE.md'
73
+ check_drift '^one\.ie/web/src/' 'one.ie/web/CLAUDE.md'
74
+ check_drift '^api/src/' 'api/CLAUDE.md'
75
+
76
+ # Only write the file if we found something worth reviewing
77
+ if [ "$PROPOSALS" -gt 0 ]; then
78
+ {
79
+ echo ""
80
+ echo "## $TS"
81
+ printf "%b" "$BUF"
82
+ } >> "$QUEUE"
83
+
84
+ echo ""
85
+ echo "┌─────────────────────────────────────────────────────────────┐"
86
+ echo "│ STOP-REFLECT: $PROPOSALS proposal(s) queued │"
87
+ echo "│ Review: .claude/improvements.queue.md │"
88
+ echo "└─────────────────────────────────────────────────────────────┘"
89
+
90
+ emit_signal "hook:stop-reflect:warn" 0.5 "proposals=$PROPOSALS"
91
+ else
92
+ emit_signal "hook:stop-reflect:ok" 1 "changed=$(echo "$CHANGED" | wc -l | awk '{print $1}') proposals=0"
93
+ fi
94
+
95
+ exit 0
@@ -0,0 +1,46 @@
1
+ #!/bin/bash
2
+ # SYNC-TODO-DOCS — Keep TODO.md + todo.json in sync with TODO-*.md edits.
3
+ #
4
+ # Fires after Write/Edit on any docs/TODO*.md file.
5
+ # Best-effort, non-blocking: pings POST /api/tasks/sync on the dev server
6
+ # if it's running (full pipeline: KV → TypeDB → regenerate TODO.md + todo.json).
7
+ # If the dev server isn't up, we silently skip — the sandwich's deterministic
8
+ # PRE check fails closed without breaking the edit flow.
9
+ #
10
+ # The dev server does all the real work; this hook is just a trigger.
11
+ # To run the full sync manually: `/sync` (or `POST /api/tasks/sync`).
12
+ # Emits hook:todo-sync:{ok,dissolved} signal per Rule 1 (see .claude/skills/signal.md).
13
+
14
+ # shellcheck source=lib/signal.sh
15
+ source "$CLAUDE_PROJECT_DIR/.claude/hooks/lib/signal.sh"
16
+
17
+ FILE=$(echo "$1" | jq -r '.tool_input.file_path // .tool_input.path // empty' 2>/dev/null)
18
+
19
+ # Only fire for TODO markdown files under docs/
20
+ case "$FILE" in
21
+ */docs/TODO*.md) ;;
22
+ *) exit 0 ;;
23
+ esac
24
+
25
+ # Don't recurse: if the edit IS TODO.md itself, it was likely regenerated. Skip.
26
+ case "$FILE" in
27
+ */docs/TODO.md) exit 0 ;;
28
+ esac
29
+
30
+ # Best-effort POST with a 2s cap so edits stay snappy.
31
+ # Silently swallow all output — this is a background sync, not a gate.
32
+ DEV_URL="${ONE_DEV_URL:-http://localhost:4321}"
33
+ RESP=$(curl -s -o /dev/null -w "%{http_code}" \
34
+ --max-time 2 \
35
+ -X POST "${DEV_URL}/api/tasks/sync" 2>/dev/null)
36
+
37
+ BASE=$(basename "$FILE")
38
+ if [ "$RESP" = "200" ]; then
39
+ echo "todo-sync: regenerated TODO.md + todo.json from $BASE" >&2
40
+ emit_signal "hook:todo-sync:ok" 1 "file=$BASE"
41
+ else
42
+ # Server down/timeout/error → dissolved (missing unit semantics, mild warn)
43
+ emit_signal "hook:todo-sync:dissolved" -0.5 "file=$BASE http=$RESP"
44
+ fi
45
+
46
+ exit 0
@@ -0,0 +1,52 @@
1
+ #!/bin/bash
2
+ # TASK-COMPLETE VERIFY — The W4 Gate
3
+ #
4
+ # Runs on TaskCompleted. Blocks task mark if tests regress.
5
+ # This is the gate between signal and pheromone — prevents corruption.
6
+ #
7
+ # Exit 0 = task marked (all checks pass)
8
+ # Exit 1 = task blocked (tests regressed or type errors detected)
9
+ # Emits hook:w4-verify:{ok,fail} signal per Rule 1 (see .claude/skills/signal.md).
10
+
11
+ # shellcheck source=lib/signal.sh
12
+ source "$CLAUDE_PROJECT_DIR/.claude/hooks/lib/signal.sh"
13
+
14
+ cd "$CLAUDE_PROJECT_DIR" || exit 1
15
+
16
+ # Run the full verify suite
17
+ RESULT=$(bun run verify 2>&1)
18
+ EXIT=$?
19
+
20
+ # Extract test counts (used in both branches for the signal payload)
21
+ PASSED=$(echo "$RESULT" | grep -o '[0-9]* pass' | head -1 | awk '{print $1}')
22
+ FAILED=$(echo "$RESULT" | grep -o '[0-9]* fail' | head -1 | awk '{print $1}')
23
+ ERRORS=$(echo "$RESULT" | grep -c "error TS" || echo "0")
24
+
25
+ if [ $EXIT -eq 0 ]; then
26
+ # All checks passed — mark with deterministic receipts (Rule 3)
27
+ emit_signal "hook:w4-verify:ok" 1 "passed=${PASSED:-0} failed=0 errors=0"
28
+ exit 0
29
+ fi
30
+
31
+ # Verify failed — extract details
32
+ echo "W4 GATE BLOCKED: Verification failed" >&2
33
+ echo "" >&2
34
+
35
+ if [ -n "$FAILED" ] && [ "$FAILED" -gt 0 ]; then
36
+ echo "Tests: $FAILED failed" >&2
37
+ fi
38
+ if [ "$ERRORS" -gt 0 ]; then
39
+ echo "TypeScript: $ERRORS type errors" >&2
40
+ fi
41
+
42
+ # Show last 15 lines of output for context
43
+ echo "" >&2
44
+ echo "Last output:" >&2
45
+ echo "$RESULT" | tail -15 >&2
46
+ echo "" >&2
47
+ echo "Fix baseline before marking task done." >&2
48
+
49
+ # Full warn — tests actually regressed, agent produced nothing usable
50
+ emit_signal "hook:w4-verify:fail" -1 "passed=${PASSED:-0} failed=${FAILED:-0} errors=${ERRORS:-0}"
51
+
52
+ exit 1
@@ -0,0 +1,48 @@
1
+ #!/usr/bin/env bash
2
+ # Tool-call signal emitter — PostToolUse hook.
3
+ # Emits tool:<ToolName>:{ok,fail} with +1/-1 weight. Receiver + weight
4
+ # semantics: .claude/skills/signal.md.
5
+
6
+ # shellcheck source=lib/signal.sh
7
+ source "$CLAUDE_PROJECT_DIR/.claude/hooks/lib/signal.sh"
8
+
9
+ PAYLOAD="$1"
10
+ [[ -z "$PAYLOAD" ]] && exit 0
11
+
12
+ TOOL=$(echo "$PAYLOAD" | jq -r '.tool_name // empty' 2>/dev/null)
13
+ [[ -z "$TOOL" ]] && exit 0
14
+
15
+ # Receiver must match ^[a-zA-Z0-9:_-]+$ (validateUid in signal.ts).
16
+ # MCP tools (mcp__server__tool) contain underscores — still safe.
17
+ SAFE_TOOL="${TOOL//[^a-zA-Z0-9_-]/}"
18
+ [[ -z "$SAFE_TOOL" ]] && exit 0
19
+
20
+ # Outcome: error field → fail; Bash non-zero or interrupted → fail; else ok
21
+ ERROR=$(echo "$PAYLOAD" | jq -r '.tool_response.error // empty' 2>/dev/null)
22
+ OUTCOME="ok"
23
+ WEIGHT=1
24
+
25
+ if [[ -n "$ERROR" ]]; then
26
+ OUTCOME="fail"; WEIGHT=-1
27
+ elif [[ "$TOOL" == "Bash" ]]; then
28
+ EXIT_CODE=$(echo "$PAYLOAD" | jq -r '.tool_response.exit_code // .tool_response.returncode // 0' 2>/dev/null)
29
+ INTERRUPTED=$(echo "$PAYLOAD" | jq -r '.tool_response.interrupted // false' 2>/dev/null)
30
+ [[ "$EXIT_CODE" != "0" || "$INTERRUPTED" == "true" ]] && { OUTCOME="fail"; WEIGHT=-1; }
31
+ fi
32
+
33
+ # Human-greppable crumb per tool family. Not structured — don't serialize objects.
34
+ DATA=""
35
+ case "$TOOL" in
36
+ Write|Edit|MultiEdit|Read|NotebookEdit)
37
+ FILE=$(echo "$PAYLOAD" | jq -r '.tool_input.file_path // .tool_input.notebook_path // .tool_input.path // empty' 2>/dev/null)
38
+ [[ -n "$FILE" ]] && DATA="file=$(basename "$FILE")" ;;
39
+ Bash)
40
+ CMD=$(echo "$PAYLOAD" | jq -r '.tool_input.command // empty' 2>/dev/null | head -c 60 | tr '\n' ' ')
41
+ [[ -n "$CMD" ]] && DATA="cmd=$CMD" ;;
42
+ Grep|Glob)
43
+ PATTERN=$(echo "$PAYLOAD" | jq -r '.tool_input.pattern // empty' 2>/dev/null | head -c 40)
44
+ [[ -n "$PATTERN" ]] && DATA="pattern=$PATTERN" ;;
45
+ esac
46
+
47
+ emit_signal "tool:${SAFE_TOOL}:${OUTCOME}" "$WEIGHT" "$DATA"
48
+ exit 0
package/package.json ADDED
@@ -0,0 +1,33 @@
1
+ {
2
+ "name": "@oneie/claude",
3
+ "version": "0.1.0",
4
+ "description": "ONE Claude Code plugin — /do lifecycle, W1-W4 BUILD engine, ONE substrate via MCP, signals, and rules",
5
+ "files": [
6
+ ".claude-plugin",
7
+ ".mcp.json",
8
+ "commands",
9
+ "agents",
10
+ "skills",
11
+ "rules",
12
+ "hooks",
13
+ "scripts",
14
+ "README.md"
15
+ ],
16
+ "peerDependencies": {
17
+ "@oneie/sdk": ">=0.3.0",
18
+ "@oneie/mcp": ">=0.3.0",
19
+ "@oneie/cli": ">=0.2.0"
20
+ },
21
+ "peerDependenciesMeta": {
22
+ "@oneie/sdk": { "optional": true },
23
+ "@oneie/mcp": { "optional": true },
24
+ "@oneie/cli": { "optional": true }
25
+ },
26
+ "keywords": ["claude-code", "plugin", "do", "lifecycle", "mcp", "signals", "typedb", "agents", "one"],
27
+ "license": "SEE LICENSE IN https://one.ie/free-license",
28
+ "repository": {
29
+ "type": "git",
30
+ "url": "git+https://github.com/one-ie/claude.git"
31
+ },
32
+ "homepage": "https://github.com/one-ie/claude"
33
+ }
package/rules/api.md ADDED
@@ -0,0 +1,50 @@
1
+ ---
2
+ paths:
3
+ - "one.ie/web/src/pages/api/**/*.ts"
4
+ - "one.ie/web/src/hooks/**/*.ts"
5
+ ---
6
+
7
+ # API Rules
8
+
9
+ Apply to `src/pages/api/**/*.ts`, `src/hooks/**/*.ts`
10
+
11
+ ## The contract: no new endpoints for product features
12
+
13
+ Four universal endpoints cover all product work. Before adding a new file to `src/pages/api/`, name which of these four cannot handle it and why.
14
+
15
+ | Endpoint | Use for |
16
+ |---|---|
17
+ | `POST /api/signal/:receiver` | Fire-and-forget — trigger a workflow, record an event |
18
+ | `POST /api/ask/:receiver` | Synchronous — get a result back (30s max) |
19
+ | `POST /api/mark/:edge` / `/api/warn/:edge` | Pheromone on any path (`source>target` URL-encoded) |
20
+ | `GET/PUT /api/settings?scope=X` | Any new setting — add a new `scope` value, not a new file |
21
+
22
+ ## When a new endpoint IS justified
23
+
24
+ | Pattern | Why |
25
+ |---|---|
26
+ | Inbound webhook from external service | External system calls a fixed URL — cannot invert |
27
+ | OAuth / redirect flow | Browser redirect URI must be a stable route |
28
+ | Multi-step auth ceremony | Stateful challenge exchange (WebAuthn) |
29
+ | Binary or streaming response | SSE stream, image gen, file upload, CSV export |
30
+ | Resource-bound read/write (`/api/<resource>/:id`) | RESTful read/append against a specific persisted entity — e.g. `GET/POST /api/threads/:tid` for chat-thread history and owner inbox replies. The id is a stable handle (not a one-shot signal), and the same path participates in CDN cache + access control |
31
+ | `/api/export/<dim>` family | Bulk read of a substrate dimension shaped for the inbox/CRM — already established (`actors`, `groups`, `skills`, `highways`, `conversations`). New dimensions extend the family rather than create ad-hoc routes |
32
+
33
+ If the use case does not match one of these four patterns, route through `signal`, `ask`, `mark/warn`, or `settings?scope=`.
34
+
35
+ ## Edge format for mark/warn
36
+
37
+ ```ts
38
+ const edge = encodeURIComponent(`${source}>${target}`)
39
+ fetch(`/api/mark/${edge}`, { method: 'POST', body: JSON.stringify({ strength }) })
40
+ ```
41
+
42
+ The `>` separator works as a fallback to `→`. Always pass `{ strength }` in the body.
43
+
44
+ ## State from highways, not localStorage
45
+
46
+ Active state (saved, archived, completed) belongs on the pheromone path, not in `localStorage`. Poll `/api/export/highways?from=source&limit=200` and classify by strength/resistance thresholds. This keeps state honest across devices.
47
+
48
+ ## No stubs
49
+
50
+ Do not create a no-op endpoint (one that always returns `[]` or `{ok:true}`) as a placeholder. If the backend isn't ready, the caller handles the empty response from `signal`/`ask`.