@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,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`.
|