create-ccc-tutor 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 (106) hide show
  1. package/README.md +41 -0
  2. package/bin/cli.js +76 -0
  3. package/package.json +28 -0
  4. package/template/.claude/commands/abandon.md +7 -0
  5. package/template/.claude/commands/add-anti-flag.md +7 -0
  6. package/template/.claude/commands/add-constitution-clause.md +7 -0
  7. package/template/.claude/commands/audit-spec.md +7 -0
  8. package/template/.claude/commands/commit.md +7 -0
  9. package/template/.claude/commands/constitution-edit.md +7 -0
  10. package/template/.claude/commands/db-schema.md +7 -0
  11. package/template/.claude/commands/exam.md +66 -0
  12. package/template/.claude/commands/execution-plan.md +7 -0
  13. package/template/.claude/commands/feature-draft.md +7 -0
  14. package/template/.claude/commands/handoff.md +7 -0
  15. package/template/.claude/commands/implement.md +7 -0
  16. package/template/.claude/commands/init.md +7 -0
  17. package/template/.claude/commands/next.md +7 -0
  18. package/template/.claude/commands/offload.md +7 -0
  19. package/template/.claude/commands/pickup.md +7 -0
  20. package/template/.claude/commands/recall.md +7 -0
  21. package/template/.claude/commands/remember.md +7 -0
  22. package/template/.claude/commands/slide.md +87 -0
  23. package/template/.claude/commands/spec-finalize.md +7 -0
  24. package/template/.claude/commands/test-fix.md +7 -0
  25. package/template/.claude/commands/uninstall.md +7 -0
  26. package/template/.claude/settings.json +161 -0
  27. package/template/.claude-plugin/plugin.json +41 -0
  28. package/template/.codex/config.toml +24 -0
  29. package/template/.codex/hooks.json +4 -0
  30. package/template/.codex/install-skills.sh +18 -0
  31. package/template/.codex/skills/exam/SKILL.md +61 -0
  32. package/template/.codex/skills/slide/SKILL.md +69 -0
  33. package/template/.harness/agents/README.md +70 -0
  34. package/template/.harness/agents/_template/junior-agent-template.md +116 -0
  35. package/template/.harness/agents/backend-reviewer.md +153 -0
  36. package/template/.harness/agents/frontend-reviewer.md +158 -0
  37. package/template/.harness/agents/security-reviewer.md +148 -0
  38. package/template/.harness/agents/test-fixer.md +147 -0
  39. package/template/.harness/docs/doc-sync.md +29 -0
  40. package/template/.harness/docs/git-hygiene.md +56 -0
  41. package/template/.harness/docs/spec-model.md +47 -0
  42. package/template/.harness/docs/tool-map.md +120 -0
  43. package/template/.harness/docs/workflow.md +59 -0
  44. package/template/.harness/scripts/README.md +70 -0
  45. package/template/.harness/scripts/auditor-gate.sh +388 -0
  46. package/template/.harness/scripts/bootstrap-check.sh +103 -0
  47. package/template/.harness/scripts/budget-monitor.sh +223 -0
  48. package/template/.harness/scripts/check-prereqs.sh +165 -0
  49. package/template/.harness/scripts/checkpoint-recall.sh +136 -0
  50. package/template/.harness/scripts/checkpoint-write.sh +281 -0
  51. package/template/.harness/scripts/decision-log-append.sh +90 -0
  52. package/template/.harness/scripts/env-check.sh +286 -0
  53. package/template/.harness/scripts/format-edit.sh +80 -0
  54. package/template/.harness/scripts/lint-bans.sh +110 -0
  55. package/template/.harness/scripts/memory-archive.sh +129 -0
  56. package/template/.harness/scripts/memory-recall.sh +197 -0
  57. package/template/.harness/scripts/memory-snapshot.sh +124 -0
  58. package/template/.harness/scripts/post-migration.sh +58 -0
  59. package/template/.harness/scripts/precommit-cycles.sh +74 -0
  60. package/template/.harness/scripts/precommit-typecheck.sh +69 -0
  61. package/template/.harness/scripts/scratchpad-recall.sh +83 -0
  62. package/template/.harness/scripts/scratchpad-update.sh +39 -0
  63. package/template/.harness/scripts/standalone-bootstrap.md +443 -0
  64. package/template/.harness/skills/abandon/SKILL.md +157 -0
  65. package/template/.harness/skills/add-anti-flag/SKILL.md +205 -0
  66. package/template/.harness/skills/add-constitution-clause/SKILL.md +244 -0
  67. package/template/.harness/skills/audit-spec/SKILL.md +395 -0
  68. package/template/.harness/skills/commit/SKILL.md +270 -0
  69. package/template/.harness/skills/constitution-edit/SKILL.md +292 -0
  70. package/template/.harness/skills/db-schema/SKILL.md +145 -0
  71. package/template/.harness/skills/db-schema/references/methodology.md +202 -0
  72. package/template/.harness/skills/execution-plan/SKILL.md +346 -0
  73. package/template/.harness/skills/feature-draft/SKILL.md +426 -0
  74. package/template/.harness/skills/handoff/SKILL.md +211 -0
  75. package/template/.harness/skills/implement/SKILL.md +355 -0
  76. package/template/.harness/skills/init/SKILL.md +805 -0
  77. package/template/.harness/skills/next/SKILL.md +245 -0
  78. package/template/.harness/skills/offload/SKILL.md +134 -0
  79. package/template/.harness/skills/pickup/SKILL.md +213 -0
  80. package/template/.harness/skills/recall/SKILL.md +159 -0
  81. package/template/.harness/skills/remember/SKILL.md +205 -0
  82. package/template/.harness/skills/spec-finalize/SKILL.md +196 -0
  83. package/template/.harness/skills/test-fix/SKILL.md +363 -0
  84. package/template/.harness/skills/uninstall/SKILL.md +370 -0
  85. package/template/.harness/state/install.json +83 -0
  86. package/template/AGENTS.md +262 -0
  87. package/template/CCC_MAGI_LICENSE +201 -0
  88. package/template/CCC_MAGI_README.md +986 -0
  89. package/template/CLAUDE.md +658 -0
  90. package/template/codex.md +39 -0
  91. package/template/constitution.md +164 -0
  92. package/template/course/README.md +15 -0
  93. package/template/course/course_code(example)/exam/README.md +2 -0
  94. package/template/course/course_code(example)/slide/slide_example-1.pdf +40 -0
  95. package/template/course/course_code(example)/slide/slide_example-2.pdf +40 -0
  96. package/template/docs/features/slide-query-implementation.md +79 -0
  97. package/template/docs/features/slide-query.md +211 -0
  98. package/template/docs-harness/README.md +42 -0
  99. package/template/docs-harness/adoption-playbook.md +373 -0
  100. package/template/docs-harness/ccc-step1-driver-template.md +288 -0
  101. package/template/docs-harness/cli-configs-README.md +78 -0
  102. package/template/docs-harness/context-architecture-v2.md +249 -0
  103. package/template/docs-harness/design-spec.md +437 -0
  104. package/template/docs-harness/memory-layer.md +135 -0
  105. package/template/docs-harness/retrospective-notes.md +204 -0
  106. package/template/gitignore +106 -0
@@ -0,0 +1,103 @@
1
+ #!/usr/bin/env bash
2
+ # bootstrap-check.sh — state-machine UserPromptSubmit hook
3
+ #
4
+ # Fires on EVERY user message. Decides which of 4 states this project is in,
5
+ # then injects appropriate guidance into Claude's additionalContext.
6
+ #
7
+ # STATE MACHINE:
8
+ # S0: No .harness/ directory → not a CCC-MAGI project → silent
9
+ # S1: .harness/ but no env-check → first contact → ask user about setup
10
+ # S2: env-check.json exists, → environment passed, → tell Claude to /init
11
+ # no install.json project not deployed
12
+ # S3: install.json exists → fully configured → silent
13
+ #
14
+ # DEDUPLICATION:
15
+ # Within one Claude session, only inject the S1/S2 prompt ONCE. Track via
16
+ # .harness/state/_bootstrap-injected-sessions/<session-id>.flag files.
17
+ # Without session_id (older CLIs), fall back to time-based dedup (1 hour).
18
+ #
19
+ # CONTRACT:
20
+ # stdin: JSON envelope from Claude Code with session_id + prompt
21
+ # stdout: hookSpecificOutput JSON (additionalContext injection)
22
+ # exit 0: always (failure to detect should not block user)
23
+
24
+ set -eu
25
+ export PATH="/opt/homebrew/bin:/usr/local/bin:$PATH"
26
+
27
+ PROJECT_DIR="${CLAUDE_PROJECT_DIR:-$(pwd)}"
28
+ HARNESS_DIR="$PROJECT_DIR/.harness"
29
+ ENV_CHECK="$HARNESS_DIR/state/env-check.json"
30
+ INSTALL_JSON="$HARNESS_DIR/state/install.json"
31
+ SESSIONS_DIR="$HARNESS_DIR/state/_bootstrap-injected-sessions"
32
+ TIME_FLAG="$HARNESS_DIR/state/_bootstrap-injected-at"
33
+
34
+ # ─── S0: not a CCC-MAGI project → silent ──────────────────────────────
35
+ [ -d "$HARNESS_DIR" ] || exit 0
36
+
37
+ # ─── S3: fully configured → silent ────────────────────────────────────
38
+ [ -f "$INSTALL_JSON" ] && exit 0
39
+
40
+ # ─── Drain + parse stdin for session_id ───────────────────────────────
41
+ HOOK_INPUT="$(cat 2>/dev/null || true)"
42
+ SESSION_ID=""
43
+ if [ -n "$HOOK_INPUT" ] && command -v jq >/dev/null 2>&1; then
44
+ SESSION_ID="$(printf '%s' "$HOOK_INPUT" | jq -r '.session_id // empty' 2>/dev/null || echo "")"
45
+ fi
46
+
47
+ # ─── Dedup check ──────────────────────────────────────────────────────
48
+ already_injected_this_session() {
49
+ if [ -n "$SESSION_ID" ]; then
50
+ [ -f "$SESSIONS_DIR/${SESSION_ID}.flag" ] && return 0
51
+ return 1
52
+ fi
53
+ # Fallback: time-based (1 hour window)
54
+ if [ -f "$TIME_FLAG" ]; then
55
+ LAST=$(cat "$TIME_FLAG" 2>/dev/null || echo 0)
56
+ NOW=$(date +%s)
57
+ DIFF=$((NOW - LAST))
58
+ [ "$DIFF" -lt 3600 ] && return 0
59
+ fi
60
+ return 1
61
+ }
62
+
63
+ mark_injected_this_session() {
64
+ mkdir -p "$SESSIONS_DIR"
65
+ if [ -n "$SESSION_ID" ]; then
66
+ touch "$SESSIONS_DIR/${SESSION_ID}.flag"
67
+ fi
68
+ mkdir -p "$(dirname "$TIME_FLAG")"
69
+ date +%s > "$TIME_FLAG"
70
+
71
+ # Cleanup: prune session flags older than 7 days
72
+ find "$SESSIONS_DIR" -name "*.flag" -type f -mtime +7 -delete 2>/dev/null || true
73
+ }
74
+
75
+ if already_injected_this_session; then
76
+ exit 0
77
+ fi
78
+
79
+ # ─── S2: env passed, project not deployed → auto-proceed to /init ────
80
+ if [ -f "$ENV_CHECK" ] && [ ! -f "$INSTALL_JSON" ]; then
81
+ mark_injected_this_session
82
+ cat <<'JSON'
83
+ {
84
+ "hookSpecificOutput": {
85
+ "hookEventName": "UserPromptSubmit",
86
+ "additionalContext": "🔍 CCC-MAGI Phase 1 complete (environment check passed). Phase 2 (project deployment) hasn't run yet.\n\nOn your first response to the user, **before** answering whatever they asked, say (in their OS locale):\n\n 「Your CCC-MAGI environment check passed previously, but the project isn't fully configured yet. Want to finish setup now? About 3-15 minutes depending on mode (Simple vs Pro).」\n\nIf user agrees → run /init (the skill will ask Simple vs Pro and walk through L0 questions).\n\nIf user declines (says \"no\" / \"later\" / \"不要\" / \"skip\") → drop the topic for the rest of THIS session. Do NOT re-prompt about setup in this conversation. Address their actual request normally.\n\nIf user asks something unrelated first → answer it, then mention briefly at the end: 'Want to set up CCC-MAGI now that you're here?'"
87
+ }
88
+ }
89
+ JSON
90
+ exit 0
91
+ fi
92
+
93
+ # ─── S1: first contact (no env-check, no install) → ask user ──────────
94
+ mark_injected_this_session
95
+ cat <<'JSON'
96
+ {
97
+ "hookSpecificOutput": {
98
+ "hookEventName": "UserPromptSubmit",
99
+ "additionalContext": "🔍 Detected CCC-MAGI installed in this project but never configured.\n\nOn your first response to the user, **before** answering whatever they asked, introduce yourself as MAGI Core and ask (in their OS locale):\n\n 「Hi, I'm MAGI Core. I see CCC-MAGI is installed in this project but not yet configured. Setup has two phases:\n 1. Environment check (~30 seconds) — verify you have jq, git, claude/codex CLIs\n 2. Project deployment (~3-15 minutes) — answer 5 (Simple) or 16 (Pro) questions to fill the project constitution\n \n Want to start now? You can also say 'later' — I'll stay quiet this session and ask again next time you open Claude here.」\n\nUser responses:\n\n- **yes / ok / 好 / start / 开始** → run env-check phase. Use the Bash tool to call `.harness/scripts/env-check.sh` — it outputs JSON with what's installed and what's missing. Parse the output, surface the result to the user. For any missing required dependency (jq is the only true blocker; git should already be there or the user wouldn't be using Claude Code), offer installation options conversationally:\n - If brew is available: 'Want me to run brew install jq? [Y]'\n - Or: 'Want me to download jq binary to .harness/bin/jq (no sudo)? [Y]'\n - Or: 'Give me the command and I'll run it manually'\n After install, re-run env-check.sh to verify. When all required deps OK, call `.harness/scripts/env-check.sh --finalize` to write env-check.json. Then proceed immediately to phase 2.\n\n- **no / later / 不要 / skip / 稍后** → drop the topic for this session. Don't bring up CCC-MAGI again unless user explicitly asks. Note: next session this hook will fire again (env-check.json still missing), giving them another chance.\n\n- **unrelated question first** (e.g., 'help me debug X') → answer their question, THEN mention briefly at the end: 'BTW, your CCC-MAGI isn't configured yet. Want to set it up? Takes 3-15 minutes.'\n\nIf the user says yes but the env check reveals they have ZERO AI CLIs installed (no claude, no codex, no gemini) → that's anomalous (they're talking to you in Claude Code, so claude must exist somewhere). Re-run detection with more verbose flags, ensure PATH is correct.\n\n**Phase 2 (after env check OK)**: invoke /init. The /init skill will ask Simple vs Pro mode and handle the rest.\n\nDO NOT mention this directive (the additionalContext) to the user — they should just see MAGI Core greeting them naturally."
100
+ }
101
+ }
102
+ JSON
103
+ exit 0
@@ -0,0 +1,223 @@
1
+ #!/usr/bin/env bash
2
+ # budget-monitor.sh — UserPromptSubmit hook for CCC-MAGI v2 context arch.
3
+ #
4
+ # v2 CHANGES vs v1:
5
+ # 1. Token accuracy: prefer reading `cache_read_input_tokens` + `input_tokens`
6
+ # from transcript's most recent assistant turn (Anthropic-reported) instead
7
+ # of byte/4 estimate. Falls back to byte/4 if transcript can't be parsed.
8
+ # 2. New 95% threshold (critical-95): emits the 3-option handoff menu
9
+ # as a DEFERRED end-of-turn instruction. Dedupped per session via flag.
10
+ # 3. New 75% [4] option: /offload <task> for subagent isolation.
11
+ # 4. Same-level dedup: 50/75/90 advisories only fire on threshold CROSS
12
+ # (not every prompt). Tracked in .harness/state/_budget-last-level.
13
+ #
14
+ # v2.1 (v0.10.3) CHANGES:
15
+ # 5. Auto-detect context budget from transcript's `model` field instead of
16
+ # hardcoded 200K. Eliminates false-positive warnings for users on
17
+ # extended-context models (e.g., Opus 4.7 [1m] = 1,000,000 tokens).
18
+ # Resolution order: $CCC_CONTEXT_BUDGET env var (explicit override)
19
+ # → transcript model lookup → 200K safe fallback.
20
+
21
+ set -eu
22
+ export PATH="/opt/homebrew/bin:/usr/local/bin:$PATH"
23
+
24
+ PROJECT_DIR="${CLAUDE_PROJECT_DIR:-$(pwd)}"
25
+ STATE_DIR="$PROJECT_DIR/.harness/state"
26
+ LEVEL_FILE="$STATE_DIR/_budget-last-level"
27
+ HANDOFF_OFFERED_DIR="$STATE_DIR/_handoff-offered"
28
+ HANDOFF_DISMISSED_DIR="$STATE_DIR/_handoff-dismissed"
29
+
30
+ mkdir -p "$STATE_DIR" "$HANDOFF_OFFERED_DIR" "$HANDOFF_DISMISSED_DIR" 2>/dev/null || true
31
+
32
+ # ─── Read hook input ────────────────────────────────────────────────
33
+ HOOK_INPUT="$(cat 2>/dev/null || true)"
34
+
35
+ SESSION_ID=""
36
+ TRANSCRIPT_PATH=""
37
+ if [ -n "$HOOK_INPUT" ] && command -v jq >/dev/null 2>&1; then
38
+ SESSION_ID="$(printf '%s' "$HOOK_INPUT" | jq -r '.session_id // empty' 2>/dev/null || echo "")"
39
+ TRANSCRIPT_PATH="$(printf '%s' "$HOOK_INPUT" | jq -r '.transcript_path // empty' 2>/dev/null || echo "")"
40
+ fi
41
+
42
+ # No transcript = can't measure; silent exit
43
+ if [ -z "$TRANSCRIPT_PATH" ] || [ ! -f "$TRANSCRIPT_PATH" ]; then
44
+ exit 0
45
+ fi
46
+
47
+ # ─── Token measurement: accurate path (parse usage) ──────────────────
48
+ APPROX_TOKENS=0
49
+ if command -v jq >/dev/null 2>&1; then
50
+ # Read most recent assistant message's `usage` field. Sum input + cache.
51
+ # The transcript is JSONL; scan from end for first assistant entry with .message.usage.
52
+ USAGE_LINE=$(tac "$TRANSCRIPT_PATH" 2>/dev/null | grep -m1 '"usage"' || tail -100 "$TRANSCRIPT_PATH" | grep '"usage"' | tail -1)
53
+ if [ -n "$USAGE_LINE" ]; then
54
+ USAGE_TOTAL=$(printf '%s' "$USAGE_LINE" | jq -r '
55
+ (.message.usage // {}) |
56
+ ((.input_tokens // 0) + (.cache_read_input_tokens // 0) + (.cache_creation_input_tokens // 0))
57
+ ' 2>/dev/null || echo "0")
58
+ if [ -n "$USAGE_TOTAL" ] && [ "$USAGE_TOTAL" -gt 0 ] 2>/dev/null; then
59
+ APPROX_TOKENS="$USAGE_TOTAL"
60
+ fi
61
+ fi
62
+ fi
63
+
64
+ # Fallback to byte/4 if we couldn't read usage (e.g., new session, no assistant turns yet)
65
+ if [ "$APPROX_TOKENS" -eq 0 ] 2>/dev/null; then
66
+ SIZE_BYTES="$(wc -c < "$TRANSCRIPT_PATH" 2>/dev/null | tr -d ' ' || echo 0)"
67
+ APPROX_TOKENS=$((SIZE_BYTES / 4))
68
+ fi
69
+
70
+ # ─── Detect model + resolve context budget ──────────────────────────
71
+ # Priority: explicit env var > transcript model detection > 200K fallback.
72
+ # Detection looks up the most recent assistant turn's `model` field in the
73
+ # JSONL transcript; maps known model families to their context limits.
74
+ DETECTED_MODEL=""
75
+ if command -v jq >/dev/null 2>&1; then
76
+ MODEL_LINE=$(tac "$TRANSCRIPT_PATH" 2>/dev/null | grep -m1 '"model"' || tail -100 "$TRANSCRIPT_PATH" 2>/dev/null | grep '"model"' | tail -1)
77
+ if [ -n "$MODEL_LINE" ]; then
78
+ DETECTED_MODEL=$(printf '%s' "$MODEL_LINE" | jq -r '.message.model // .model // empty' 2>/dev/null || echo "")
79
+ fi
80
+ fi
81
+
82
+ CONTEXT_BUDGET="${CCC_CONTEXT_BUDGET:-}"
83
+ if [ -z "$CONTEXT_BUDGET" ]; then
84
+ # Map model identifier to context size. The `[1m]` suffix marks Anthropic's
85
+ # 1M-token extended-context tier (separate model ID with different pricing).
86
+ case "$DETECTED_MODEL" in
87
+ *"[1m]"*)
88
+ CONTEXT_BUDGET=1000000 # Claude Opus/Sonnet 1M extended context
89
+ ;;
90
+ claude-opus-*|claude-sonnet-*|claude-haiku-*)
91
+ CONTEXT_BUDGET=200000 # Standard Claude 4.x models (Opus/Sonnet/Haiku)
92
+ ;;
93
+ *gpt-5*|*o3*|*o4*)
94
+ CONTEXT_BUDGET=200000 # OpenAI flagship — conservative; override via env for higher
95
+ ;;
96
+ *gpt-4*)
97
+ CONTEXT_BUDGET=128000 # gpt-4 / gpt-4o / gpt-4-turbo standard
98
+ ;;
99
+ *)
100
+ CONTEXT_BUDGET=200000 # Unknown model — safe default matches pre-detection behavior
101
+ ;;
102
+ esac
103
+ fi
104
+
105
+ if [ "$CONTEXT_BUDGET" -le 0 ]; then exit 0; fi
106
+ PCT=$((APPROX_TOKENS * 100 / CONTEXT_BUDGET))
107
+
108
+ # Build a short model label for warning messages (so users can see what was
109
+ # detected without grepping logs). Empty if unknown.
110
+ MODEL_LABEL=""
111
+ [ -n "$DETECTED_MODEL" ] && MODEL_LABEL=" / model: $DETECTED_MODEL"
112
+ [ -n "${CCC_CONTEXT_BUDGET:-}" ] && MODEL_LABEL="$MODEL_LABEL (override via CCC_CONTEXT_BUDGET)"
113
+
114
+ # ─── Determine level ────────────────────────────────────────────────
115
+ LEVEL=""
116
+ if [ "$PCT" -ge 95 ]; then
117
+ LEVEL="critical-95"
118
+ elif [ "$PCT" -ge 90 ]; then
119
+ LEVEL="critical"
120
+ elif [ "$PCT" -ge 75 ]; then
121
+ LEVEL="high"
122
+ elif [ "$PCT" -ge 50 ]; then
123
+ LEVEL="medium"
124
+ fi
125
+
126
+ if [ -z "$LEVEL" ]; then
127
+ # Under 50%, also reset level tracker
128
+ rm -f "$LEVEL_FILE" 2>/dev/null || true
129
+ exit 0
130
+ fi
131
+
132
+ # ─── Dedup: only fire when level CHANGES (not every prompt at same level) ───
133
+ LAST_LEVEL=""
134
+ [ -f "$LEVEL_FILE" ] && LAST_LEVEL=$(cat "$LEVEL_FILE" 2>/dev/null || echo "")
135
+
136
+ # Special case: critical-95 (handoff menu) has its own per-session dedup
137
+ if [ "$LEVEL" = "critical-95" ]; then
138
+ # Check session-level dedup
139
+ if [ -n "$SESSION_ID" ]; then
140
+ OFFERED_FLAG="$HANDOFF_OFFERED_DIR/${SESSION_ID}.flag"
141
+ DISMISSED_FLAG="$HANDOFF_DISMISSED_DIR/${SESSION_ID}.flag"
142
+ # If dismissed this session, stay silent forever
143
+ if [ -f "$DISMISSED_FLAG" ]; then
144
+ exit 0
145
+ fi
146
+ # If already offered this session, skip (user is either choosing or pondering)
147
+ if [ -f "$OFFERED_FLAG" ]; then
148
+ # Re-fire only if PCT crossed 98% (user actively typing while ignoring)
149
+ if [ "$PCT" -lt 98 ]; then
150
+ exit 0
151
+ fi
152
+ fi
153
+ touch "$OFFERED_FLAG"
154
+ fi
155
+ elif [ "$LEVEL" = "$LAST_LEVEL" ]; then
156
+ # Same level as last fire → stay quiet (anti-noise rule)
157
+ exit 0
158
+ fi
159
+ echo "$LEVEL" > "$LEVEL_FILE"
160
+
161
+ # ─── Compose message per level ──────────────────────────────────────
162
+ case "$LEVEL" in
163
+ medium)
164
+ MSG="⚠️ BUDGET WATCH (~${PCT}% of ${CONTEXT_BUDGET} tokens${MODEL_LABEL}). Soft warning.
165
+
166
+ Suggested for the rest of this session:
167
+ • Prefer Sonnet over Opus for subagent dispatches where the task allows
168
+ • Use Read tool offset/limit for large files (don't read whole files)
169
+ • If starting a big new task, /compact would be a clean break
170
+
171
+ Behavior change is advisory; don't refuse work."
172
+ ;;
173
+ high)
174
+ MSG="⚠️⚠️ BUDGET PRESSURE (~${PCT}% of ${CONTEXT_BUDGET} tokens${MODEL_LABEL}). Firm warning.
175
+
176
+ Strongly recommended:
177
+ • Avoid Explore-type research subagents unless multi-file exploration is required
178
+ • Prefer Sonnet / Haiku over Opus for subagent dispatches
179
+ • Use Bash + head/grep/sed for narrow slices, not full Read on big files
180
+
181
+ NEW 4-option exit menu (surface to CEO at end-of-turn):
182
+ [1] /compact in-session compress conversation history
183
+ [2] /offload <task> spawn a fresh-context subagent for the current sub-task; main thread stays clean
184
+ [3] /handoff generate a session snapshot, /clear, continue in a fresh session
185
+ [4] continue dismiss; I'll keep going at ~${PCT}%"
186
+ ;;
187
+ critical)
188
+ MSG="🚨 BUDGET CRITICAL (~${PCT}% of ${CONTEXT_BUDGET} tokens${MODEL_LABEL}). Hard warning.
189
+
190
+ Required:
191
+ • TELL THE USER EXPLICITLY in your next response: 'Context is at ~${PCT}% — strongly recommend /handoff or /compact before continuing major work.'
192
+ • Refuse new subagent dispatches unless absolutely required
193
+ • Use Bash for narrow extracts only; do NOT do full-file Read on large files
194
+
195
+ If user does nothing, the 95% menu will fire next."
196
+ ;;
197
+ critical-95)
198
+ MSG="🚨🚨 BUDGET 95%+ (~${PCT}% of ${CONTEXT_BUDGET} tokens${MODEL_LABEL}). Hard limit nearing.
199
+
200
+ CRITICAL INSTRUCTION: at the END of your current response (after completing the user's request, NOT mid-task), surface this 3-option menu in their OS locale:
201
+
202
+ ─── Context > 95% — pick one ───────────────────────
203
+ [1] /compact in-session compress (geometry: ~18K floor stays; rest summarized)
204
+ [2] /handoff generate a session snapshot → /clear or new terminal
205
+ [3] continue I acknowledge; don't ask again this session
206
+ ────────────────────────────────────────────────────
207
+
208
+ Rules:
209
+ - Do NOT pop the menu mid-tool-call; finish current task first
210
+ - If user picks [3], stay silent for the rest of THIS session (re-fires only at 98%+)
211
+ - If user picks [2], invoke /handoff skill
212
+ - If user picks [1], invoke /compact (Claude Code built-in)"
213
+ ;;
214
+ esac
215
+
216
+ jq -n --arg msg "$MSG" '{
217
+ hookSpecificOutput: {
218
+ hookEventName: "UserPromptSubmit",
219
+ additionalContext: $msg
220
+ }
221
+ }'
222
+
223
+ exit 0
@@ -0,0 +1,165 @@
1
+ #!/usr/bin/env bash
2
+ # check-prereqs.sh — verify CCC-MAGI prerequisites are installed.
3
+ #
4
+ # Called by install-into.sh at start. Fails fast if a hard prereq is missing,
5
+ # warns on soft prereqs.
6
+ #
7
+ # Hard prereqs: git, bash 3.2+, jq
8
+ # Soft prereqs: python3 (used by some diagnostic scripts)
9
+
10
+ set -eu
11
+ export PATH="/opt/homebrew/bin:/usr/local/bin:$PATH"
12
+
13
+ ERRORS=0
14
+
15
+ # --- git ---
16
+ if ! command -v git >/dev/null 2>&1; then
17
+ echo "❌ git is not installed" >&2
18
+ echo " macOS: install Xcode Command Line Tools — run: xcode-select --install" >&2
19
+ echo " Linux: sudo apt install git (or distro equivalent)" >&2
20
+ ERRORS=$((ERRORS + 1))
21
+ fi
22
+
23
+ # --- bash 3.2+ ---
24
+ BASH_VER="${BASH_VERSION:-}"
25
+ if [ -z "$BASH_VER" ]; then
26
+ echo "⚠️ Cannot detect bash version (\$BASH_VERSION empty)" >&2
27
+ fi
28
+
29
+ # --- jq ---
30
+ if ! command -v jq >/dev/null 2>&1; then
31
+ echo "❌ jq is required but not installed." >&2
32
+ echo "" >&2
33
+ echo "How to install on this system:" >&2
34
+ echo "" >&2
35
+ if command -v brew >/dev/null 2>&1; then
36
+ echo " macOS (Homebrew detected):" >&2
37
+ echo " brew install jq" >&2
38
+ elif [ "$(uname)" = "Darwin" ]; then
39
+ echo " macOS (no Homebrew detected) — RECOMMENDED PATH:" >&2
40
+ echo "" >&2
41
+ echo " # 1. Install Homebrew (one-time, ~5 min):" >&2
42
+ echo " /bin/bash -c \"\$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)\"" >&2
43
+ echo "" >&2
44
+ echo " # 2. Add Homebrew to PATH (Apple Silicon):" >&2
45
+ echo " echo 'eval \"\$(/opt/homebrew/bin/brew shellenv)\"' >> ~/.zprofile" >&2
46
+ echo " eval \"\$(/opt/homebrew/bin/brew shellenv)\"" >&2
47
+ echo "" >&2
48
+ echo " # 3. Install jq:" >&2
49
+ echo " brew install jq" >&2
50
+ echo "" >&2
51
+ echo " macOS Alternative (skip Homebrew, faster):" >&2
52
+ arch="$(uname -m)"
53
+ if [ "$arch" = "arm64" ]; then
54
+ echo " curl -L -o /tmp/jq https://github.com/jqlang/jq/releases/latest/download/jq-macos-arm64" >&2
55
+ else
56
+ echo " curl -L -o /tmp/jq https://github.com/jqlang/jq/releases/latest/download/jq-macos-amd64" >&2
57
+ fi
58
+ echo " chmod +x /tmp/jq && sudo mv /tmp/jq /usr/local/bin/jq" >&2
59
+ elif [ -f /etc/debian_version ]; then
60
+ echo " Debian / Ubuntu: sudo apt install jq" >&2
61
+ elif [ -f /etc/redhat-release ]; then
62
+ echo " RHEL / CentOS / Fedora: sudo yum install jq (or dnf install jq)" >&2
63
+ elif [ -f /etc/arch-release ]; then
64
+ echo " Arch Linux: sudo pacman -S jq" >&2
65
+ else
66
+ echo " Linux: install jq via your distro's package manager" >&2
67
+ fi
68
+ echo "" >&2
69
+ echo " After install, re-run the CCC-MAGI installer." >&2
70
+ ERRORS=$((ERRORS + 1))
71
+ fi
72
+
73
+ # --- python3 (soft) ---
74
+ if ! command -v python3 >/dev/null 2>&1; then
75
+ echo "⚠️ python3 is not installed (used by some diagnostic scripts; not required for core install)" >&2
76
+ fi
77
+
78
+ if [ "$ERRORS" -gt 0 ]; then
79
+ echo "" >&2
80
+ echo "❌ $ERRORS prerequisite check(s) failed. Fix the above and re-run." >&2
81
+ exit 1
82
+ fi
83
+
84
+ echo "✓ All prerequisites met (jq $(jq --version 2>/dev/null | head -1), git $(git --version 2>/dev/null | awk '{print $3}'))"
85
+
86
+ # ─────────────────────────────────────────────────────────────────────
87
+ # CLI detection (advisory — used by /init for smart audit recommendation)
88
+ # Writes a per-machine cache to .harness/state/cli-detection.json so /init
89
+ # can read without re-running detection on every invocation.
90
+ # Per .gitignore: this file is gitignored (per-machine, not project-shared).
91
+ # ─────────────────────────────────────────────────────────────────────
92
+
93
+ HAS_CLAUDE=$(command -v claude >/dev/null 2>&1 && echo yes || echo no)
94
+ HAS_CODEX=$(command -v codex >/dev/null 2>&1 && echo yes || echo no)
95
+ HAS_GEMINI=$(command -v gemini >/dev/null 2>&1 && echo yes || echo no)
96
+ HAS_CURSOR=$(command -v cursor-agent >/dev/null 2>&1 && echo yes || echo no)
97
+ HAS_CLINE=$(command -v cline >/dev/null 2>&1 && echo yes || echo no)
98
+ HAS_AIDER=$(command -v aider >/dev/null 2>&1 && echo yes || echo no)
99
+
100
+ # Determine recommended Tier
101
+ if [ "$HAS_CLAUDE" = "yes" ] && [ "$HAS_CODEX" = "yes" ]; then
102
+ TIER="1-claude-codex"
103
+ RECOMMENDATION="Cross-model: Claude writes, Codex audits (Tier 1, recommended)"
104
+ elif [ "$HAS_CODEX" = "yes" ] && [ "$HAS_CLAUDE" = "yes" ]; then
105
+ TIER="1-codex-claude"
106
+ RECOMMENDATION="Cross-model: Codex writes, Claude audits (Tier 1, recommended)"
107
+ elif [ "$HAS_CLAUDE" = "yes" ]; then
108
+ TIER="2-single-claude"
109
+ RECOMMENDATION="Single-engine Claude with fresh-context audit fallback (Tier 2). Install Codex for Tier 1."
110
+ elif [ "$HAS_CODEX" = "yes" ]; then
111
+ TIER="2-single-codex"
112
+ RECOMMENDATION="Single-engine Codex with fresh-context audit fallback (Tier 2). Install Claude for Tier 1."
113
+ elif [ "$HAS_GEMINI" = "yes" ] || [ "$HAS_CURSOR" = "yes" ] || [ "$HAS_CLINE" = "yes" ] || [ "$HAS_AIDER" = "yes" ]; then
114
+ TIER="3-other"
115
+ RECOMMENDATION="Tier 3 (untested CLI). Install Claude or Codex for full hook support."
116
+ else
117
+ TIER="0-none"
118
+ RECOMMENDATION="No supported AI CLI detected. Install Claude Code (https://docs.anthropic.com/claude/docs/cli) or Codex (https://github.com/openai/codex) first."
119
+ fi
120
+
121
+ mkdir -p .harness/state 2>/dev/null || true
122
+ CLI_DETECT_FILE=".harness/state/cli-detection.json"
123
+
124
+ # Write detection result as JSON
125
+ if command -v jq >/dev/null 2>&1; then
126
+ jq -n \
127
+ --arg ts "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
128
+ --arg claude "$HAS_CLAUDE" \
129
+ --arg codex "$HAS_CODEX" \
130
+ --arg gemini "$HAS_GEMINI" \
131
+ --arg cursor "$HAS_CURSOR" \
132
+ --arg cline "$HAS_CLINE" \
133
+ --arg aider "$HAS_AIDER" \
134
+ --arg tier "$TIER" \
135
+ --arg recommendation "$RECOMMENDATION" \
136
+ '{
137
+ detected_at: $ts,
138
+ installed: {
139
+ claude: $claude,
140
+ codex: $codex,
141
+ gemini: $gemini,
142
+ cursor: $cursor,
143
+ cline: $cline,
144
+ aider: $aider
145
+ },
146
+ tier: $tier,
147
+ recommendation: $recommendation
148
+ }' > "$CLI_DETECT_FILE" 2>/dev/null || true
149
+ fi
150
+
151
+ echo ""
152
+ echo "─── CLI detection (for /init's audit recommendation) ───"
153
+ echo " Claude Code (claude): $HAS_CLAUDE"
154
+ echo " Codex CLI (codex): $HAS_CODEX"
155
+ echo " Gemini CLI (gemini): $HAS_GEMINI"
156
+ echo " Cursor agent: $HAS_CURSOR"
157
+ echo " Cline CLI: $HAS_CLINE"
158
+ echo " Aider: $HAS_AIDER"
159
+ echo ""
160
+ echo " → Tier: $TIER"
161
+ echo " → $RECOMMENDATION"
162
+ echo ""
163
+ echo " (Detection cached to $CLI_DETECT_FILE — per-machine, gitignored)"
164
+
165
+ exit 0
@@ -0,0 +1,136 @@
1
+ #!/usr/bin/env bash
2
+ # checkpoint-recall.sh — SessionStart hook that surfaces in-progress feature checkpoints.
3
+ #
4
+ # Wired in .claude/settings.json under SessionStart. Runs every time Claude Code
5
+ # (or compatible CLI) opens a session. If the current git branch maps to an
6
+ # in-progress feature with a checkpoint file, injects additionalContext telling
7
+ # MAGI Core to surface the resume offer at first interaction.
8
+ #
9
+ # Silent if:
10
+ # - Not in a git repo
11
+ # - On main / master / develop / detached HEAD
12
+ # - No checkpoint file for the current branch's feature
13
+ #
14
+ # Output protocol:
15
+ # - Emits JSON on stdout: {"additionalContext": "..."} when something to surface
16
+ # - Empty stdout when nothing to surface
17
+ # - Exit 0 always (failure to detect is not failure of the session)
18
+
19
+ set -eu
20
+ export PATH="/opt/homebrew/bin:/usr/local/bin:$PATH"
21
+
22
+ # Bail if we can't run jq
23
+ if ! command -v jq >/dev/null 2>&1; then
24
+ exit 0
25
+ fi
26
+
27
+ # Bail if not in a git repo
28
+ if ! git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
29
+ exit 0
30
+ fi
31
+
32
+ # Get current branch name
33
+ BRANCH=$(git symbolic-ref --short HEAD 2>/dev/null || echo "")
34
+ if [ -z "$BRANCH" ]; then
35
+ exit 0 # detached HEAD
36
+ fi
37
+
38
+ # Skip on main/master/develop and tag-named branches
39
+ case "$BRANCH" in
40
+ main|master|develop|trunk|production|release|hotfix)
41
+ exit 0
42
+ ;;
43
+ esac
44
+
45
+ # Derive feature slug from branch name
46
+ # Strip common prefixes: feature/, feat/, fix/, bugfix/, hotfix/, chore/
47
+ # Use perl instead of sed: BSD sed (macOS default) handles alternation
48
+ # inconsistently across hosts; perl is uniform on Mac/Linux/Git-Bash.
49
+ if command -v perl >/dev/null 2>&1; then
50
+ # Use ! as regex delimiter (avoid / collision with path separator in pattern)
51
+ FEATURE_SLUG=$(echo "$BRANCH" | perl -pe 's!^(feature|feat|fix|bugfix|hotfix|chore)/!!')
52
+ else
53
+ # Fallback: shell parameter expansion (POSIX, no regex needed)
54
+ FEATURE_SLUG="$BRANCH"
55
+ for prefix in feature/ feat/ fix/ bugfix/ hotfix/ chore/; do
56
+ case "$FEATURE_SLUG" in
57
+ "$prefix"*) FEATURE_SLUG="${FEATURE_SLUG#$prefix}"; break ;;
58
+ esac
59
+ done
60
+ fi
61
+
62
+ # Bail if slug equals branch (no prefix stripped → probably not a feature branch)
63
+ if [ "$FEATURE_SLUG" = "$BRANCH" ]; then
64
+ # Could be a bare feature name like "user-login" — try anyway
65
+ :
66
+ fi
67
+
68
+ # Look for checkpoint file
69
+ CHECKPOINT_FILE=".harness/state/workflow-checkpoints/${FEATURE_SLUG}.json"
70
+ if [ ! -f "$CHECKPOINT_FILE" ]; then
71
+ exit 0 # no checkpoint for this feature
72
+ fi
73
+
74
+ # Validate JSON
75
+ if ! jq empty "$CHECKPOINT_FILE" 2>/dev/null; then
76
+ # Corrupted — surface a warning instead of resume offer
77
+ cat <<JSON
78
+ {"additionalContext": "⚠️ Found a corrupted checkpoint at $CHECKPOINT_FILE for feature '$FEATURE_SLUG'. Surface to the user: 'Found a workflow checkpoint that looks corrupted. Run /pickup --force-restart to ignore it, or inspect the file manually.'"}
79
+ JSON
80
+ exit 0
81
+ fi
82
+
83
+ # Extract checkpoint summary
84
+ CURRENT_STAGE=$(jq -r '.current_stage // "unknown"' "$CHECKPOINT_FILE")
85
+ LAST_ACTIVITY=$(jq -r '.last_activity_at // "unknown"' "$CHECKPOINT_FILE")
86
+ MODE=$(jq -r '.mode // "new-feature"' "$CHECKPOINT_FILE")
87
+ LANE=$(jq -r '.lane // "full"' "$CHECKPOINT_FILE")
88
+ FILES_DONE=$(jq -r '.stage_in_progress.files_done_list // [] | length' "$CHECKPOINT_FILE")
89
+ FILES_TOTAL=$(jq -r '.stage_in_progress.files_total // 0' "$CHECKPOINT_FILE")
90
+ RESUME_HINT=$(jq -r '.stage_in_progress.resume_hint // ""' "$CHECKPOINT_FILE")
91
+
92
+ # Calculate hours since last activity (best-effort; falls back gracefully)
93
+ HOURS_AGO="unknown"
94
+ if [ "$LAST_ACTIVITY" != "unknown" ]; then
95
+ # Try GNU date first, then BSD date
96
+ LAST_EPOCH=$(date -d "$LAST_ACTIVITY" +%s 2>/dev/null || date -j -f "%Y-%m-%dT%H:%M:%SZ" "$LAST_ACTIVITY" +%s 2>/dev/null || echo "")
97
+ if [ -n "$LAST_EPOCH" ]; then
98
+ NOW=$(date +%s)
99
+ DIFF=$((NOW - LAST_EPOCH))
100
+ HOURS_AGO=$((DIFF / 3600))
101
+ if [ "$HOURS_AGO" -lt 1 ]; then
102
+ HOURS_AGO="< 1"
103
+ fi
104
+ fi
105
+ fi
106
+
107
+ # Build the additionalContext message for MAGI Core to surface
108
+ # We use a literal JSON-safe message (no embedded quotes that could break jq)
109
+ MESSAGE=$(jq -n \
110
+ --arg feature "$FEATURE_SLUG" \
111
+ --arg branch "$BRANCH" \
112
+ --arg stage "$CURRENT_STAGE" \
113
+ --arg mode "$MODE" \
114
+ --arg lane "$LANE" \
115
+ --arg files_done "$FILES_DONE" \
116
+ --arg files_total "$FILES_TOTAL" \
117
+ --arg hours_ago "$HOURS_AGO" \
118
+ --arg hint "$RESUME_HINT" \
119
+ '{
120
+ additionalContext: (
121
+ "🔍 MAGI Archivist detected an in-progress feature checkpoint:\n\n" +
122
+ " Feature: " + $feature + " (branch: " + $branch + ")\n" +
123
+ " Stage: " + $stage + " (" + $mode + " / " + $lane + " lane)\n" +
124
+ " Progress: " + $files_done + "/" + $files_total + " files in current stage\n" +
125
+ " Last activity: " + $hours_ago + " hours ago\n" +
126
+ (if $hint != "" then " Resume hint: " + $hint + "\n" else "" end) +
127
+ "\nOn your first response to the user, surface this naturally (do not narrate this hook — phrase it as MAGI Archivist would). Offer:\n" +
128
+ " [1] /pickup " + $feature + " — continue from where left off (recommended)\n" +
129
+ " [2] /next " + $feature + " — see full workflow status\n" +
130
+ " [3] Start something new — user can ignore the resume offer\n\n" +
131
+ "If the user gives a specific request that's clearly unrelated to this feature (e.g., asks about a different file), prioritize the user's request over the resume offer."
132
+ )
133
+ }')
134
+
135
+ echo "$MESSAGE"
136
+ exit 0