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.
- package/README.md +41 -0
- package/bin/cli.js +76 -0
- package/package.json +28 -0
- package/template/.claude/commands/abandon.md +7 -0
- package/template/.claude/commands/add-anti-flag.md +7 -0
- package/template/.claude/commands/add-constitution-clause.md +7 -0
- package/template/.claude/commands/audit-spec.md +7 -0
- package/template/.claude/commands/commit.md +7 -0
- package/template/.claude/commands/constitution-edit.md +7 -0
- package/template/.claude/commands/db-schema.md +7 -0
- package/template/.claude/commands/exam.md +66 -0
- package/template/.claude/commands/execution-plan.md +7 -0
- package/template/.claude/commands/feature-draft.md +7 -0
- package/template/.claude/commands/handoff.md +7 -0
- package/template/.claude/commands/implement.md +7 -0
- package/template/.claude/commands/init.md +7 -0
- package/template/.claude/commands/next.md +7 -0
- package/template/.claude/commands/offload.md +7 -0
- package/template/.claude/commands/pickup.md +7 -0
- package/template/.claude/commands/recall.md +7 -0
- package/template/.claude/commands/remember.md +7 -0
- package/template/.claude/commands/slide.md +87 -0
- package/template/.claude/commands/spec-finalize.md +7 -0
- package/template/.claude/commands/test-fix.md +7 -0
- package/template/.claude/commands/uninstall.md +7 -0
- package/template/.claude/settings.json +161 -0
- package/template/.claude-plugin/plugin.json +41 -0
- package/template/.codex/config.toml +24 -0
- package/template/.codex/hooks.json +4 -0
- package/template/.codex/install-skills.sh +18 -0
- package/template/.codex/skills/exam/SKILL.md +61 -0
- package/template/.codex/skills/slide/SKILL.md +69 -0
- package/template/.harness/agents/README.md +70 -0
- package/template/.harness/agents/_template/junior-agent-template.md +116 -0
- package/template/.harness/agents/backend-reviewer.md +153 -0
- package/template/.harness/agents/frontend-reviewer.md +158 -0
- package/template/.harness/agents/security-reviewer.md +148 -0
- package/template/.harness/agents/test-fixer.md +147 -0
- package/template/.harness/docs/doc-sync.md +29 -0
- package/template/.harness/docs/git-hygiene.md +56 -0
- package/template/.harness/docs/spec-model.md +47 -0
- package/template/.harness/docs/tool-map.md +120 -0
- package/template/.harness/docs/workflow.md +59 -0
- package/template/.harness/scripts/README.md +70 -0
- package/template/.harness/scripts/auditor-gate.sh +388 -0
- package/template/.harness/scripts/bootstrap-check.sh +103 -0
- package/template/.harness/scripts/budget-monitor.sh +223 -0
- package/template/.harness/scripts/check-prereqs.sh +165 -0
- package/template/.harness/scripts/checkpoint-recall.sh +136 -0
- package/template/.harness/scripts/checkpoint-write.sh +281 -0
- package/template/.harness/scripts/decision-log-append.sh +90 -0
- package/template/.harness/scripts/env-check.sh +286 -0
- package/template/.harness/scripts/format-edit.sh +80 -0
- package/template/.harness/scripts/lint-bans.sh +110 -0
- package/template/.harness/scripts/memory-archive.sh +129 -0
- package/template/.harness/scripts/memory-recall.sh +197 -0
- package/template/.harness/scripts/memory-snapshot.sh +124 -0
- package/template/.harness/scripts/post-migration.sh +58 -0
- package/template/.harness/scripts/precommit-cycles.sh +74 -0
- package/template/.harness/scripts/precommit-typecheck.sh +69 -0
- package/template/.harness/scripts/scratchpad-recall.sh +83 -0
- package/template/.harness/scripts/scratchpad-update.sh +39 -0
- package/template/.harness/scripts/standalone-bootstrap.md +443 -0
- package/template/.harness/skills/abandon/SKILL.md +157 -0
- package/template/.harness/skills/add-anti-flag/SKILL.md +205 -0
- package/template/.harness/skills/add-constitution-clause/SKILL.md +244 -0
- package/template/.harness/skills/audit-spec/SKILL.md +395 -0
- package/template/.harness/skills/commit/SKILL.md +270 -0
- package/template/.harness/skills/constitution-edit/SKILL.md +292 -0
- package/template/.harness/skills/db-schema/SKILL.md +145 -0
- package/template/.harness/skills/db-schema/references/methodology.md +202 -0
- package/template/.harness/skills/execution-plan/SKILL.md +346 -0
- package/template/.harness/skills/feature-draft/SKILL.md +426 -0
- package/template/.harness/skills/handoff/SKILL.md +211 -0
- package/template/.harness/skills/implement/SKILL.md +355 -0
- package/template/.harness/skills/init/SKILL.md +805 -0
- package/template/.harness/skills/next/SKILL.md +245 -0
- package/template/.harness/skills/offload/SKILL.md +134 -0
- package/template/.harness/skills/pickup/SKILL.md +213 -0
- package/template/.harness/skills/recall/SKILL.md +159 -0
- package/template/.harness/skills/remember/SKILL.md +205 -0
- package/template/.harness/skills/spec-finalize/SKILL.md +196 -0
- package/template/.harness/skills/test-fix/SKILL.md +363 -0
- package/template/.harness/skills/uninstall/SKILL.md +370 -0
- package/template/.harness/state/install.json +83 -0
- package/template/AGENTS.md +262 -0
- package/template/CCC_MAGI_LICENSE +201 -0
- package/template/CCC_MAGI_README.md +986 -0
- package/template/CLAUDE.md +658 -0
- package/template/codex.md +39 -0
- package/template/constitution.md +164 -0
- package/template/course/README.md +15 -0
- package/template/course/course_code(example)/exam/README.md +2 -0
- package/template/course/course_code(example)/slide/slide_example-1.pdf +40 -0
- package/template/course/course_code(example)/slide/slide_example-2.pdf +40 -0
- package/template/docs/features/slide-query-implementation.md +79 -0
- package/template/docs/features/slide-query.md +211 -0
- package/template/docs-harness/README.md +42 -0
- package/template/docs-harness/adoption-playbook.md +373 -0
- package/template/docs-harness/ccc-step1-driver-template.md +288 -0
- package/template/docs-harness/cli-configs-README.md +78 -0
- package/template/docs-harness/context-architecture-v2.md +249 -0
- package/template/docs-harness/design-spec.md +437 -0
- package/template/docs-harness/memory-layer.md +135 -0
- package/template/docs-harness/retrospective-notes.md +204 -0
- package/template/gitignore +106 -0
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# checkpoint-write.sh — atomic writer for .harness/state/workflow-checkpoints/<feature>.json
|
|
3
|
+
#
|
|
4
|
+
# Called by every stage skill at its successful completion. Centralizes the
|
|
5
|
+
# checkpoint schema (single source of truth — change schema here, all skills
|
|
6
|
+
# inherit it). Per Phase 5 (Session resume) of the v0.9.0 release.
|
|
7
|
+
#
|
|
8
|
+
# USAGE:
|
|
9
|
+
# checkpoint-write.sh --feature <slug> [options]
|
|
10
|
+
#
|
|
11
|
+
# OPTIONS:
|
|
12
|
+
# --feature <slug> Required. Feature slug (matches branch name suffix).
|
|
13
|
+
# --branch <name> Override detected branch (default: current git branch).
|
|
14
|
+
# --mode <new-feature|audit> Workflow mode (default: new-feature).
|
|
15
|
+
# --lane <full|stability-fix|trivial> Workflow lane (default: full).
|
|
16
|
+
# --stage <N> Set current_stage to N.
|
|
17
|
+
# --stage-complete <N> Mark stage N as complete (append to stages_completed).
|
|
18
|
+
# --stage-skip <N> --skip-reason <text> Mark stage N as skipped with reason.
|
|
19
|
+
# --artifact-spec <path> Record spec artifact path (computes sha256).
|
|
20
|
+
# --artifact-implementation <path> Record implementation-doc artifact path.
|
|
21
|
+
# --artifact-plan <path> Record execution-plan artifact path.
|
|
22
|
+
# --artifact-schema <path> Record schema artifact path.
|
|
23
|
+
# --append-audit '<json>' Append one audit entry. JSON: {"stage":N,"verdict":"...","risk":N,"at":"<iso>"}
|
|
24
|
+
# --stage-in-progress '<json>' Replace stage_in_progress block. JSON: {"stage_number":N,"files_total":N,...}
|
|
25
|
+
# --file-done <path> Append one path to stage_in_progress.files_done_list (idempotent: dedupe).
|
|
26
|
+
# --append-decision '<json>' Append one decision entry. JSON: {"at":"...","stage":N,"by":"CEO","decision":"..."}
|
|
27
|
+
# --archive Move checkpoint to _archived/<feature>-<timestamp>.json (used by /commit).
|
|
28
|
+
# --create-if-missing Create a minimal checkpoint if file doesn't exist (used by /feature-draft as first stage).
|
|
29
|
+
#
|
|
30
|
+
# OUTPUT:
|
|
31
|
+
# - On success: prints "✓ checkpoint updated: <path>" to stdout, exits 0.
|
|
32
|
+
# - On error: prints error to stderr, exits 1.
|
|
33
|
+
#
|
|
34
|
+
# DESIGN NOTES:
|
|
35
|
+
# - Atomic write: tmp + rename (won't leave half-written checkpoint).
|
|
36
|
+
# - Idempotent: rerunning the same call doesn't duplicate stages/files.
|
|
37
|
+
# - Schema versioning: bumps schema_version if needed, refuses to write older schema over newer.
|
|
38
|
+
# - jq is required.
|
|
39
|
+
|
|
40
|
+
set -euo pipefail
|
|
41
|
+
export PATH="/opt/homebrew/bin:/usr/local/bin:$PATH"
|
|
42
|
+
|
|
43
|
+
if ! command -v jq >/dev/null 2>&1; then
|
|
44
|
+
echo "checkpoint-write.sh requires jq. Install with: brew install jq" >&2
|
|
45
|
+
exit 1
|
|
46
|
+
fi
|
|
47
|
+
|
|
48
|
+
# ─── Defaults / state ──────────────────────────────────────────────────
|
|
49
|
+
SCHEMA_VERSION=1
|
|
50
|
+
FEATURE=""
|
|
51
|
+
BRANCH=""
|
|
52
|
+
MODE=""
|
|
53
|
+
LANE=""
|
|
54
|
+
SET_STAGE=""
|
|
55
|
+
COMPLETE_STAGE=""
|
|
56
|
+
SKIP_STAGE=""
|
|
57
|
+
SKIP_REASON=""
|
|
58
|
+
ARTIFACT_SPEC=""
|
|
59
|
+
ARTIFACT_IMPL=""
|
|
60
|
+
ARTIFACT_PLAN=""
|
|
61
|
+
ARTIFACT_SCHEMA=""
|
|
62
|
+
APPEND_AUDIT=""
|
|
63
|
+
STAGE_IN_PROGRESS=""
|
|
64
|
+
FILE_DONE=""
|
|
65
|
+
APPEND_DECISION=""
|
|
66
|
+
ARCHIVE=false
|
|
67
|
+
CREATE_IF_MISSING=false
|
|
68
|
+
|
|
69
|
+
# ─── Parse args ────────────────────────────────────────────────────────
|
|
70
|
+
while [ "$#" -gt 0 ]; do
|
|
71
|
+
case "$1" in
|
|
72
|
+
--feature) FEATURE="$2"; shift 2 ;;
|
|
73
|
+
--branch) BRANCH="$2"; shift 2 ;;
|
|
74
|
+
--mode) MODE="$2"; shift 2 ;;
|
|
75
|
+
--lane) LANE="$2"; shift 2 ;;
|
|
76
|
+
--stage) SET_STAGE="$2"; shift 2 ;;
|
|
77
|
+
--stage-complete) COMPLETE_STAGE="$2"; shift 2 ;;
|
|
78
|
+
--stage-skip) SKIP_STAGE="$2"; shift 2 ;;
|
|
79
|
+
--skip-reason) SKIP_REASON="$2"; shift 2 ;;
|
|
80
|
+
--artifact-spec) ARTIFACT_SPEC="$2"; shift 2 ;;
|
|
81
|
+
--artifact-implementation) ARTIFACT_IMPL="$2"; shift 2 ;;
|
|
82
|
+
--artifact-plan) ARTIFACT_PLAN="$2"; shift 2 ;;
|
|
83
|
+
--artifact-schema) ARTIFACT_SCHEMA="$2"; shift 2 ;;
|
|
84
|
+
--append-audit) APPEND_AUDIT="$2"; shift 2 ;;
|
|
85
|
+
--stage-in-progress) STAGE_IN_PROGRESS="$2"; shift 2 ;;
|
|
86
|
+
--file-done) FILE_DONE="$2"; shift 2 ;;
|
|
87
|
+
--append-decision) APPEND_DECISION="$2"; shift 2 ;;
|
|
88
|
+
--archive) ARCHIVE=true; shift ;;
|
|
89
|
+
--create-if-missing) CREATE_IF_MISSING=true; shift ;;
|
|
90
|
+
*)
|
|
91
|
+
echo "Unknown arg: $1" >&2
|
|
92
|
+
exit 1
|
|
93
|
+
;;
|
|
94
|
+
esac
|
|
95
|
+
done
|
|
96
|
+
|
|
97
|
+
if [ -z "$FEATURE" ]; then
|
|
98
|
+
echo "checkpoint-write.sh: --feature <slug> required" >&2
|
|
99
|
+
exit 1
|
|
100
|
+
fi
|
|
101
|
+
|
|
102
|
+
CHECKPOINT_DIR=".harness/state/workflow-checkpoints"
|
|
103
|
+
CHECKPOINT_FILE="$CHECKPOINT_DIR/${FEATURE}.json"
|
|
104
|
+
mkdir -p "$CHECKPOINT_DIR"
|
|
105
|
+
|
|
106
|
+
NOW=$(date -u +%Y-%m-%dT%H:%M:%SZ)
|
|
107
|
+
|
|
108
|
+
# Detect branch if not provided
|
|
109
|
+
if [ -z "$BRANCH" ]; then
|
|
110
|
+
BRANCH=$(git symbolic-ref --short HEAD 2>/dev/null || echo "(no branch)")
|
|
111
|
+
fi
|
|
112
|
+
|
|
113
|
+
# ─── Archive mode (used by /commit at Stage 8) ─────────────────────────
|
|
114
|
+
if [ "$ARCHIVE" = "true" ]; then
|
|
115
|
+
if [ ! -f "$CHECKPOINT_FILE" ]; then
|
|
116
|
+
echo "✓ checkpoint not found for $FEATURE (already archived or never created); no-op"
|
|
117
|
+
exit 0
|
|
118
|
+
fi
|
|
119
|
+
ARCHIVE_DIR="$CHECKPOINT_DIR/_archived"
|
|
120
|
+
mkdir -p "$ARCHIVE_DIR"
|
|
121
|
+
ARCHIVED_FILE="$ARCHIVE_DIR/${FEATURE}-$(date -u +%Y%m%dT%H%M%SZ).json"
|
|
122
|
+
mv "$CHECKPOINT_FILE" "$ARCHIVED_FILE"
|
|
123
|
+
echo "✓ checkpoint archived: $ARCHIVED_FILE"
|
|
124
|
+
exit 0
|
|
125
|
+
fi
|
|
126
|
+
|
|
127
|
+
# ─── Create-if-missing (initial checkpoint from /feature-draft Stage 1) ───
|
|
128
|
+
if [ ! -f "$CHECKPOINT_FILE" ]; then
|
|
129
|
+
if [ "$CREATE_IF_MISSING" = "true" ]; then
|
|
130
|
+
INIT_MODE="${MODE:-new-feature}"
|
|
131
|
+
INIT_LANE="${LANE:-full}"
|
|
132
|
+
INIT_STAGE="${SET_STAGE:-1}"
|
|
133
|
+
jq -n \
|
|
134
|
+
--argjson schema_version "$SCHEMA_VERSION" \
|
|
135
|
+
--arg feature "$FEATURE" \
|
|
136
|
+
--arg branch "$BRANCH" \
|
|
137
|
+
--arg started_at "$NOW" \
|
|
138
|
+
--arg last_at "$NOW" \
|
|
139
|
+
--arg mode "$INIT_MODE" \
|
|
140
|
+
--arg lane "$INIT_LANE" \
|
|
141
|
+
--argjson stage "$INIT_STAGE" \
|
|
142
|
+
'{
|
|
143
|
+
schema_version: $schema_version,
|
|
144
|
+
feature: $feature,
|
|
145
|
+
feature_slug: $feature,
|
|
146
|
+
branch: $branch,
|
|
147
|
+
started_at: $started_at,
|
|
148
|
+
last_activity_at: $last_at,
|
|
149
|
+
mode: $mode,
|
|
150
|
+
lane: $lane,
|
|
151
|
+
current_stage: $stage,
|
|
152
|
+
stages_completed: [],
|
|
153
|
+
stages_skipped: [],
|
|
154
|
+
stages_skipped_reasons: {},
|
|
155
|
+
artifacts: { spec: null, implementation: null, plan: null, schema: null },
|
|
156
|
+
audits: [],
|
|
157
|
+
stage_in_progress: null,
|
|
158
|
+
decisions: [],
|
|
159
|
+
session_chain: []
|
|
160
|
+
}' > "$CHECKPOINT_FILE"
|
|
161
|
+
# Fall through to apply any additional updates from this call
|
|
162
|
+
else
|
|
163
|
+
echo "checkpoint-write.sh: $CHECKPOINT_FILE does not exist. Use --create-if-missing to bootstrap." >&2
|
|
164
|
+
exit 1
|
|
165
|
+
fi
|
|
166
|
+
fi
|
|
167
|
+
|
|
168
|
+
# ─── Atomic update via tmp + rename ────────────────────────────────────
|
|
169
|
+
TMP_FILE=$(mktemp "${CHECKPOINT_FILE}.tmp.XXXXXX")
|
|
170
|
+
trap 'rm -f "$TMP_FILE"' EXIT
|
|
171
|
+
|
|
172
|
+
# Build a jq filter chain that applies all requested mutations
|
|
173
|
+
JQ_FILTER='. | .last_activity_at = $now'
|
|
174
|
+
|
|
175
|
+
# Mode / lane updates
|
|
176
|
+
[ -n "$MODE" ] && JQ_FILTER="$JQ_FILTER | .mode = \$mode"
|
|
177
|
+
[ -n "$LANE" ] && JQ_FILTER="$JQ_FILTER | .lane = \$lane"
|
|
178
|
+
[ -n "$BRANCH" ] && JQ_FILTER="$JQ_FILTER | .branch = \$branch"
|
|
179
|
+
|
|
180
|
+
# Stage current
|
|
181
|
+
[ -n "$SET_STAGE" ] && JQ_FILTER="$JQ_FILTER | .current_stage = (\$set_stage | tonumber)"
|
|
182
|
+
|
|
183
|
+
# Complete stage (dedupe via unique)
|
|
184
|
+
[ -n "$COMPLETE_STAGE" ] && JQ_FILTER="$JQ_FILTER | .stages_completed = ((.stages_completed + [(\$complete_stage | tonumber)]) | unique)"
|
|
185
|
+
|
|
186
|
+
# Skip stage
|
|
187
|
+
if [ -n "$SKIP_STAGE" ]; then
|
|
188
|
+
JQ_FILTER="$JQ_FILTER | .stages_skipped = ((.stages_skipped + [(\$skip_stage | tonumber)]) | unique)"
|
|
189
|
+
if [ -n "$SKIP_REASON" ]; then
|
|
190
|
+
JQ_FILTER="$JQ_FILTER | .stages_skipped_reasons[\$skip_stage] = \$skip_reason"
|
|
191
|
+
fi
|
|
192
|
+
fi
|
|
193
|
+
|
|
194
|
+
# Portable sha256 (Mac uses shasum, Linux/Git-Bash use sha256sum)
|
|
195
|
+
portable_sha256() {
|
|
196
|
+
local path="$1"
|
|
197
|
+
if command -v sha256sum >/dev/null 2>&1; then
|
|
198
|
+
sha256sum "$path" 2>/dev/null | awk '{print $1}' | head -c 16
|
|
199
|
+
elif command -v shasum >/dev/null 2>&1; then
|
|
200
|
+
shasum -a 256 "$path" 2>/dev/null | awk '{print $1}' | head -c 16
|
|
201
|
+
elif command -v openssl >/dev/null 2>&1; then
|
|
202
|
+
openssl dgst -sha256 "$path" 2>/dev/null | awk '{print $NF}' | head -c 16
|
|
203
|
+
else
|
|
204
|
+
echo ""
|
|
205
|
+
fi
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
# Artifact paths (compute sha256 if file exists)
|
|
209
|
+
compute_artifact_block() {
|
|
210
|
+
local path="$1"
|
|
211
|
+
if [ -f "$path" ]; then
|
|
212
|
+
local sha
|
|
213
|
+
sha=$(portable_sha256 "$path")
|
|
214
|
+
jq -n --arg p "$path" --arg s "$sha" '{path: $p, sha256: $s, exists: true}'
|
|
215
|
+
else
|
|
216
|
+
jq -n --arg p "$path" '{path: $p, sha256: null, exists: false}'
|
|
217
|
+
fi
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
if [ -n "$ARTIFACT_SPEC" ]; then
|
|
221
|
+
ART_SPEC_JSON=$(compute_artifact_block "$ARTIFACT_SPEC")
|
|
222
|
+
JQ_FILTER="$JQ_FILTER | .artifacts.spec = \$art_spec"
|
|
223
|
+
fi
|
|
224
|
+
if [ -n "$ARTIFACT_IMPL" ]; then
|
|
225
|
+
ART_IMPL_JSON=$(compute_artifact_block "$ARTIFACT_IMPL")
|
|
226
|
+
JQ_FILTER="$JQ_FILTER | .artifacts.implementation = \$art_impl"
|
|
227
|
+
fi
|
|
228
|
+
if [ -n "$ARTIFACT_PLAN" ]; then
|
|
229
|
+
ART_PLAN_JSON=$(compute_artifact_block "$ARTIFACT_PLAN")
|
|
230
|
+
JQ_FILTER="$JQ_FILTER | .artifacts.plan = \$art_plan"
|
|
231
|
+
fi
|
|
232
|
+
if [ -n "$ARTIFACT_SCHEMA" ]; then
|
|
233
|
+
ART_SCHEMA_JSON=$(compute_artifact_block "$ARTIFACT_SCHEMA")
|
|
234
|
+
JQ_FILTER="$JQ_FILTER | .artifacts.schema = \$art_schema"
|
|
235
|
+
fi
|
|
236
|
+
|
|
237
|
+
# Append audit entry
|
|
238
|
+
if [ -n "$APPEND_AUDIT" ]; then
|
|
239
|
+
JQ_FILTER="$JQ_FILTER | .audits = (.audits + [\$append_audit])"
|
|
240
|
+
fi
|
|
241
|
+
|
|
242
|
+
# Replace stage_in_progress (full block)
|
|
243
|
+
if [ -n "$STAGE_IN_PROGRESS" ]; then
|
|
244
|
+
JQ_FILTER="$JQ_FILTER | .stage_in_progress = \$stage_in_progress"
|
|
245
|
+
fi
|
|
246
|
+
|
|
247
|
+
# Append single file to files_done_list (idempotent dedupe)
|
|
248
|
+
if [ -n "$FILE_DONE" ]; then
|
|
249
|
+
JQ_FILTER="$JQ_FILTER | .stage_in_progress = (.stage_in_progress // {stage_number: .current_stage, files_total: 0, files_done_list: [], files_remaining_list: [], last_action: null, resume_hint: null}) | .stage_in_progress.files_done_list = ((.stage_in_progress.files_done_list + [\$file_done]) | unique) | .stage_in_progress.last_action = (\"Wrote \" + \$file_done)"
|
|
250
|
+
fi
|
|
251
|
+
|
|
252
|
+
# Append decision entry
|
|
253
|
+
if [ -n "$APPEND_DECISION" ]; then
|
|
254
|
+
JQ_FILTER="$JQ_FILTER | .decisions = (.decisions + [\$append_decision])"
|
|
255
|
+
fi
|
|
256
|
+
|
|
257
|
+
# Build the jq invocation with all required --arg / --argjson
|
|
258
|
+
JQ_ARGS=(--arg now "$NOW")
|
|
259
|
+
|
|
260
|
+
[ -n "$MODE" ] && JQ_ARGS+=(--arg mode "$MODE")
|
|
261
|
+
[ -n "$LANE" ] && JQ_ARGS+=(--arg lane "$LANE")
|
|
262
|
+
[ -n "$BRANCH" ] && JQ_ARGS+=(--arg branch "$BRANCH")
|
|
263
|
+
[ -n "$SET_STAGE" ] && JQ_ARGS+=(--arg set_stage "$SET_STAGE")
|
|
264
|
+
[ -n "$COMPLETE_STAGE" ] && JQ_ARGS+=(--arg complete_stage "$COMPLETE_STAGE")
|
|
265
|
+
[ -n "$SKIP_STAGE" ] && JQ_ARGS+=(--arg skip_stage "$SKIP_STAGE")
|
|
266
|
+
[ -n "$SKIP_REASON" ] && JQ_ARGS+=(--arg skip_reason "$SKIP_REASON")
|
|
267
|
+
[ -n "$FILE_DONE" ] && JQ_ARGS+=(--arg file_done "$FILE_DONE")
|
|
268
|
+
|
|
269
|
+
[ -n "$ARTIFACT_SPEC" ] && JQ_ARGS+=(--argjson art_spec "$ART_SPEC_JSON")
|
|
270
|
+
[ -n "$ARTIFACT_IMPL" ] && JQ_ARGS+=(--argjson art_impl "$ART_IMPL_JSON")
|
|
271
|
+
[ -n "$ARTIFACT_PLAN" ] && JQ_ARGS+=(--argjson art_plan "$ART_PLAN_JSON")
|
|
272
|
+
[ -n "$ARTIFACT_SCHEMA" ] && JQ_ARGS+=(--argjson art_schema "$ART_SCHEMA_JSON")
|
|
273
|
+
[ -n "$APPEND_AUDIT" ] && JQ_ARGS+=(--argjson append_audit "$APPEND_AUDIT")
|
|
274
|
+
[ -n "$STAGE_IN_PROGRESS" ] && JQ_ARGS+=(--argjson stage_in_progress "$STAGE_IN_PROGRESS")
|
|
275
|
+
[ -n "$APPEND_DECISION" ] && JQ_ARGS+=(--argjson append_decision "$APPEND_DECISION")
|
|
276
|
+
|
|
277
|
+
jq "${JQ_ARGS[@]}" "$JQ_FILTER" "$CHECKPOINT_FILE" > "$TMP_FILE"
|
|
278
|
+
mv "$TMP_FILE" "$CHECKPOINT_FILE"
|
|
279
|
+
trap - EXIT
|
|
280
|
+
|
|
281
|
+
echo "✓ checkpoint updated: $CHECKPOINT_FILE"
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# decision-log-append.sh — appends a human-readable row to .harness/memory/decision-log.md
|
|
3
|
+
#
|
|
4
|
+
# Per BMAD-style decision-log pattern (the v0.9.0 "memory layer upgrade").
|
|
5
|
+
# Each call appends one line; file is gitignored (per-developer record).
|
|
6
|
+
#
|
|
7
|
+
# USAGE:
|
|
8
|
+
# decision-log-append.sh \
|
|
9
|
+
# --feature <slug> \
|
|
10
|
+
# --stage <N> \
|
|
11
|
+
# --by <CEO|MAGI-Core|MAGI-Verdict|MAGI-Planner|MAGI-Programmer|MAGI-Tester|MAGI-Reviewer|MAGI-Archivist|system> \
|
|
12
|
+
# --decision "<short prose>"
|
|
13
|
+
#
|
|
14
|
+
# OPTIONS:
|
|
15
|
+
# --feature <slug> Required. Feature slug.
|
|
16
|
+
# --stage <N> Required. Stage number (1-9) or word ("init", "audit", "config").
|
|
17
|
+
# --by <actor> Required. Who made the decision.
|
|
18
|
+
# --decision "<text>" Required. One-line description. ≤120 chars recommended.
|
|
19
|
+
# --evidence "<text>" Optional. Brief evidence / link / commit / verdict file.
|
|
20
|
+
#
|
|
21
|
+
# OUTPUT:
|
|
22
|
+
# - On success: prints "✓ decision logged" to stdout, exits 0.
|
|
23
|
+
# - On error: prints error to stderr, exits 1.
|
|
24
|
+
|
|
25
|
+
set -euo pipefail
|
|
26
|
+
export PATH="/opt/homebrew/bin:/usr/local/bin:$PATH"
|
|
27
|
+
|
|
28
|
+
FEATURE=""
|
|
29
|
+
STAGE=""
|
|
30
|
+
BY=""
|
|
31
|
+
DECISION=""
|
|
32
|
+
EVIDENCE=""
|
|
33
|
+
|
|
34
|
+
while [ "$#" -gt 0 ]; do
|
|
35
|
+
case "$1" in
|
|
36
|
+
--feature) FEATURE="$2"; shift 2 ;;
|
|
37
|
+
--stage) STAGE="$2"; shift 2 ;;
|
|
38
|
+
--by) BY="$2"; shift 2 ;;
|
|
39
|
+
--decision) DECISION="$2"; shift 2 ;;
|
|
40
|
+
--evidence) EVIDENCE="$2"; shift 2 ;;
|
|
41
|
+
*)
|
|
42
|
+
echo "Unknown arg: $1" >&2
|
|
43
|
+
exit 1
|
|
44
|
+
;;
|
|
45
|
+
esac
|
|
46
|
+
done
|
|
47
|
+
|
|
48
|
+
if [ -z "$FEATURE" ] || [ -z "$STAGE" ] || [ -z "$BY" ] || [ -z "$DECISION" ]; then
|
|
49
|
+
echo "decision-log-append.sh: --feature, --stage, --by, --decision all required" >&2
|
|
50
|
+
exit 1
|
|
51
|
+
fi
|
|
52
|
+
|
|
53
|
+
LOG_FILE=".harness/memory/decision-log.md"
|
|
54
|
+
mkdir -p "$(dirname "$LOG_FILE")"
|
|
55
|
+
|
|
56
|
+
# Initialize file with header if missing
|
|
57
|
+
if [ ! -f "$LOG_FILE" ]; then
|
|
58
|
+
cat > "$LOG_FILE" <<'HEADER'
|
|
59
|
+
# Decision Log
|
|
60
|
+
|
|
61
|
+
> Per-developer chronological log of workflow decisions. Each row captures
|
|
62
|
+
> WHO decided WHAT at WHICH stage of WHICH feature. Maintained automatically
|
|
63
|
+
> by stage skills + MAGI Archivist. Gitignored — your private project diary.
|
|
64
|
+
>
|
|
65
|
+
> Use cases:
|
|
66
|
+
> - Release-note source ("why we chose Redis over Memcached")
|
|
67
|
+
> - Onboarding new teammates (paste relevant rows into Slack/Wiki)
|
|
68
|
+
> - Post-mortem after incidents ("what did MAGI Verdict flag, did CEO accept?")
|
|
69
|
+
> - Quarterly review of personal decision quality
|
|
70
|
+
|
|
71
|
+
| Timestamp (UTC) | Feature | Stage | Actor | Decision | Evidence |
|
|
72
|
+
|-----------------|---------|-------|-------|----------|----------|
|
|
73
|
+
HEADER
|
|
74
|
+
fi
|
|
75
|
+
|
|
76
|
+
NOW=$(date -u +%Y-%m-%dT%H:%M:%SZ)
|
|
77
|
+
|
|
78
|
+
# Escape pipe characters in text fields so they don't break the markdown table
|
|
79
|
+
escape_pipes() {
|
|
80
|
+
echo "$1" | sed 's/|/\\|/g'
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
DECISION_ESC=$(escape_pipes "$DECISION")
|
|
84
|
+
EVIDENCE_ESC=$(escape_pipes "${EVIDENCE:-—}")
|
|
85
|
+
|
|
86
|
+
printf "| %s | %s | %s | %s | %s | %s |\n" \
|
|
87
|
+
"$NOW" "$FEATURE" "$STAGE" "$BY" "$DECISION_ESC" "$EVIDENCE_ESC" \
|
|
88
|
+
>> "$LOG_FILE"
|
|
89
|
+
|
|
90
|
+
echo "✓ decision logged to $LOG_FILE"
|
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# env-check.sh — environment / dependency detector for CCC-MAGI
|
|
3
|
+
#
|
|
4
|
+
# Called by MAGI Core (via Bash tool) during the bootstrap S1 phase to detect
|
|
5
|
+
# what's installed. Outputs structured JSON that MAGI Core parses to guide
|
|
6
|
+
# the user through installation of any missing dependencies conversationally.
|
|
7
|
+
#
|
|
8
|
+
# USAGE:
|
|
9
|
+
# env-check.sh # Detect + output JSON to stdout, exit 0
|
|
10
|
+
# env-check.sh --finalize # Detect; if all required deps OK, write env-check.json. Exit 0 on success, 1 if blockers remain.
|
|
11
|
+
# env-check.sh --install-jq-vendored # Download jq binary to .harness/bin/jq (no sudo needed)
|
|
12
|
+
#
|
|
13
|
+
# OUTPUT JSON SCHEMA:
|
|
14
|
+
# {
|
|
15
|
+
# "detected_at": "<iso>",
|
|
16
|
+
# "platform": "darwin|linux|windows-wsl|windows-git-bash|unknown",
|
|
17
|
+
# "shell": "bash <version>",
|
|
18
|
+
# "required": {
|
|
19
|
+
# "git": {"installed": true, "version": "2.39.5"},
|
|
20
|
+
# "jq": {"installed": false, "install_hints": [...]}
|
|
21
|
+
# },
|
|
22
|
+
# "ai_clis": {
|
|
23
|
+
# "claude": {"installed": true, "path": "..."},
|
|
24
|
+
# "codex": {"installed": true, "path": "..."},
|
|
25
|
+
# "gemini": {"installed": false}
|
|
26
|
+
# },
|
|
27
|
+
# "tier": "1-claude-codex|1-codex-claude|2-single-claude|2-single-codex|3-other|0-none",
|
|
28
|
+
# "recommendation": "<human-readable>",
|
|
29
|
+
# "all_required_ok": true|false,
|
|
30
|
+
# "blockers": ["jq"]
|
|
31
|
+
# }
|
|
32
|
+
|
|
33
|
+
set -eu
|
|
34
|
+
export PATH="/opt/homebrew/bin:/usr/local/bin:.harness/bin:$PATH"
|
|
35
|
+
|
|
36
|
+
MODE="${1:-detect}"
|
|
37
|
+
|
|
38
|
+
# ─── Platform detection ────────────────────────────────────────────────
|
|
39
|
+
detect_platform() {
|
|
40
|
+
case "$(uname -s)" in
|
|
41
|
+
Darwin*) echo "darwin" ;;
|
|
42
|
+
Linux*)
|
|
43
|
+
# WSL?
|
|
44
|
+
if grep -qi microsoft /proc/version 2>/dev/null || [ -n "${WSL_DISTRO_NAME:-}" ]; then
|
|
45
|
+
echo "windows-wsl"
|
|
46
|
+
else
|
|
47
|
+
echo "linux"
|
|
48
|
+
fi
|
|
49
|
+
;;
|
|
50
|
+
MINGW*|MSYS*|CYGWIN*) echo "windows-git-bash" ;;
|
|
51
|
+
*) echo "unknown" ;;
|
|
52
|
+
esac
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
PLATFORM=$(detect_platform)
|
|
56
|
+
|
|
57
|
+
# ─── Helpers ───────────────────────────────────────────────────────────
|
|
58
|
+
detect_tool() {
|
|
59
|
+
local name="$1"
|
|
60
|
+
if command -v "$name" >/dev/null 2>&1; then
|
|
61
|
+
local path version
|
|
62
|
+
path="$(command -v "$name")"
|
|
63
|
+
version="$("$name" --version 2>/dev/null | head -1 | sed 's/"/\\"/g' || echo "unknown")"
|
|
64
|
+
printf '{"installed":true,"path":"%s","version":"%s"}' "$path" "$version"
|
|
65
|
+
else
|
|
66
|
+
printf '{"installed":false}'
|
|
67
|
+
fi
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
# Install hints for jq, per platform
|
|
71
|
+
jq_install_hints() {
|
|
72
|
+
case "$PLATFORM" in
|
|
73
|
+
darwin)
|
|
74
|
+
if command -v brew >/dev/null 2>&1; then
|
|
75
|
+
cat <<'EOF'
|
|
76
|
+
[
|
|
77
|
+
{"method":"brew","cmd":"brew install jq","sudo":false,"speed":"~10s"},
|
|
78
|
+
{"method":"vendored","cmd":".harness/scripts/env-check.sh --install-jq-vendored","sudo":false,"speed":"~5s"}
|
|
79
|
+
]
|
|
80
|
+
EOF
|
|
81
|
+
else
|
|
82
|
+
cat <<'EOF'
|
|
83
|
+
[
|
|
84
|
+
{"method":"brew","cmd":"/bin/bash -c \"$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)\" && brew install jq","sudo":false,"speed":"~5min"},
|
|
85
|
+
{"method":"vendored","cmd":".harness/scripts/env-check.sh --install-jq-vendored","sudo":false,"speed":"~5s","recommended":true}
|
|
86
|
+
]
|
|
87
|
+
EOF
|
|
88
|
+
fi
|
|
89
|
+
;;
|
|
90
|
+
linux|windows-wsl)
|
|
91
|
+
if [ -f /etc/debian_version ]; then
|
|
92
|
+
cat <<'EOF'
|
|
93
|
+
[
|
|
94
|
+
{"method":"apt","cmd":"sudo apt install -y jq","sudo":true,"speed":"~10s"},
|
|
95
|
+
{"method":"vendored","cmd":".harness/scripts/env-check.sh --install-jq-vendored","sudo":false,"speed":"~5s"}
|
|
96
|
+
]
|
|
97
|
+
EOF
|
|
98
|
+
elif [ -f /etc/redhat-release ]; then
|
|
99
|
+
cat <<'EOF'
|
|
100
|
+
[
|
|
101
|
+
{"method":"yum","cmd":"sudo yum install -y jq","sudo":true,"speed":"~15s"},
|
|
102
|
+
{"method":"vendored","cmd":".harness/scripts/env-check.sh --install-jq-vendored","sudo":false,"speed":"~5s"}
|
|
103
|
+
]
|
|
104
|
+
EOF
|
|
105
|
+
elif [ -f /etc/arch-release ]; then
|
|
106
|
+
cat <<'EOF'
|
|
107
|
+
[
|
|
108
|
+
{"method":"pacman","cmd":"sudo pacman -S --noconfirm jq","sudo":true,"speed":"~10s"},
|
|
109
|
+
{"method":"vendored","cmd":".harness/scripts/env-check.sh --install-jq-vendored","sudo":false,"speed":"~5s"}
|
|
110
|
+
]
|
|
111
|
+
EOF
|
|
112
|
+
else
|
|
113
|
+
cat <<'EOF'
|
|
114
|
+
[
|
|
115
|
+
{"method":"vendored","cmd":".harness/scripts/env-check.sh --install-jq-vendored","sudo":false,"speed":"~5s","recommended":true}
|
|
116
|
+
]
|
|
117
|
+
EOF
|
|
118
|
+
fi
|
|
119
|
+
;;
|
|
120
|
+
windows-git-bash)
|
|
121
|
+
cat <<'EOF'
|
|
122
|
+
[
|
|
123
|
+
{"method":"scoop","cmd":"scoop install jq","sudo":false,"speed":"~15s","precondition":"requires Scoop package manager"},
|
|
124
|
+
{"method":"chocolatey","cmd":"choco install jq -y","sudo":true,"speed":"~30s","precondition":"requires Chocolatey package manager"},
|
|
125
|
+
{"method":"vendored","cmd":".harness/scripts/env-check.sh --install-jq-vendored","sudo":false,"speed":"~5s","recommended":true}
|
|
126
|
+
]
|
|
127
|
+
EOF
|
|
128
|
+
;;
|
|
129
|
+
*)
|
|
130
|
+
echo '[{"method":"manual","cmd":"Install jq from https://jqlang.github.io/jq/download/","sudo":false}]'
|
|
131
|
+
;;
|
|
132
|
+
esac
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
# ─── Vendored jq install ───────────────────────────────────────────────
|
|
136
|
+
install_jq_vendored() {
|
|
137
|
+
local arch url
|
|
138
|
+
arch="$(uname -m)"
|
|
139
|
+
case "$PLATFORM-$arch" in
|
|
140
|
+
darwin-arm64) url="https://github.com/jqlang/jq/releases/latest/download/jq-macos-arm64" ;;
|
|
141
|
+
darwin-x86_64) url="https://github.com/jqlang/jq/releases/latest/download/jq-macos-amd64" ;;
|
|
142
|
+
linux-x86_64|windows-wsl-x86_64) url="https://github.com/jqlang/jq/releases/latest/download/jq-linux-amd64" ;;
|
|
143
|
+
linux-aarch64|windows-wsl-aarch64) url="https://github.com/jqlang/jq/releases/latest/download/jq-linux-arm64" ;;
|
|
144
|
+
windows-git-bash-*) url="https://github.com/jqlang/jq/releases/latest/download/jq-windows-amd64.exe" ;;
|
|
145
|
+
*) echo "Unsupported platform/arch combo: $PLATFORM-$arch" >&2; exit 1 ;;
|
|
146
|
+
esac
|
|
147
|
+
|
|
148
|
+
mkdir -p .harness/bin
|
|
149
|
+
TARGET=".harness/bin/jq"
|
|
150
|
+
[ "$PLATFORM" = "windows-git-bash" ] && TARGET=".harness/bin/jq.exe"
|
|
151
|
+
|
|
152
|
+
echo "Downloading jq from: $url"
|
|
153
|
+
if command -v curl >/dev/null 2>&1; then
|
|
154
|
+
curl -fsSL "$url" -o "$TARGET" || { echo "Download failed"; exit 1; }
|
|
155
|
+
elif command -v wget >/dev/null 2>&1; then
|
|
156
|
+
wget -q "$url" -O "$TARGET" || { echo "Download failed"; exit 1; }
|
|
157
|
+
else
|
|
158
|
+
echo "Neither curl nor wget found" >&2
|
|
159
|
+
exit 1
|
|
160
|
+
fi
|
|
161
|
+
chmod +x "$TARGET" 2>/dev/null || true
|
|
162
|
+
|
|
163
|
+
echo "✓ jq installed to $TARGET"
|
|
164
|
+
echo " Verify: $TARGET --version"
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
# ─── Mode dispatch ─────────────────────────────────────────────────────
|
|
168
|
+
case "$MODE" in
|
|
169
|
+
--install-jq-vendored)
|
|
170
|
+
install_jq_vendored
|
|
171
|
+
exit 0
|
|
172
|
+
;;
|
|
173
|
+
detect|--finalize)
|
|
174
|
+
;;
|
|
175
|
+
*)
|
|
176
|
+
echo "Unknown mode: $MODE" >&2
|
|
177
|
+
echo "Usage: $0 [--finalize | --install-jq-vendored]" >&2
|
|
178
|
+
exit 1
|
|
179
|
+
;;
|
|
180
|
+
esac
|
|
181
|
+
|
|
182
|
+
# ─── Build detection JSON ──────────────────────────────────────────────
|
|
183
|
+
GIT_INFO=$(detect_tool git)
|
|
184
|
+
JQ_INFO=$(detect_tool jq)
|
|
185
|
+
CLAUDE_INFO=$(detect_tool claude)
|
|
186
|
+
CODEX_INFO=$(detect_tool codex)
|
|
187
|
+
GEMINI_INFO=$(detect_tool gemini)
|
|
188
|
+
|
|
189
|
+
# Bash version (just for info)
|
|
190
|
+
BASH_VER="${BASH_VERSION:-unknown}"
|
|
191
|
+
SHELL_DESC="bash $BASH_VER"
|
|
192
|
+
|
|
193
|
+
# Determine tier
|
|
194
|
+
HAS_CLAUDE=$(printf '%s' "$CLAUDE_INFO" | grep -q '"installed":true' && echo yes || echo no)
|
|
195
|
+
HAS_CODEX=$(printf '%s' "$CODEX_INFO" | grep -q '"installed":true' && echo yes || echo no)
|
|
196
|
+
HAS_GEMINI=$(printf '%s' "$GEMINI_INFO" | grep -q '"installed":true' && echo yes || echo no)
|
|
197
|
+
|
|
198
|
+
if [ "$HAS_CLAUDE" = "yes" ] && [ "$HAS_CODEX" = "yes" ]; then
|
|
199
|
+
TIER="1-claude-codex"
|
|
200
|
+
RECOMMENDATION="Tier 1 ideal: Claude writes code, Codex audits. Cross-model bias cancellation active."
|
|
201
|
+
elif [ "$HAS_CLAUDE" = "yes" ]; then
|
|
202
|
+
TIER="2-single-claude"
|
|
203
|
+
RECOMMENDATION="Tier 2: Claude only. Audit will use fresh-context Claude (same model, weaker bias cancellation). Install Codex for Tier 1."
|
|
204
|
+
elif [ "$HAS_CODEX" = "yes" ]; then
|
|
205
|
+
TIER="2-single-codex"
|
|
206
|
+
RECOMMENDATION="Tier 2: Codex only. Audit will use fresh-context Codex. Install Claude for Tier 1."
|
|
207
|
+
elif [ "$HAS_GEMINI" = "yes" ]; then
|
|
208
|
+
TIER="3-other"
|
|
209
|
+
RECOMMENDATION="Tier 3: Gemini detected but untested. Install Claude or Codex for Tier 1 support."
|
|
210
|
+
else
|
|
211
|
+
TIER="0-none"
|
|
212
|
+
RECOMMENDATION="No supported AI CLI detected. This is unusual — you must have at least one (Claude/Codex) to be talking to MAGI Core right now. Check PATH."
|
|
213
|
+
fi
|
|
214
|
+
|
|
215
|
+
# Determine blockers (jq is the only true required blocker)
|
|
216
|
+
JQ_OK=$(printf '%s' "$JQ_INFO" | grep -q '"installed":true' && echo yes || echo no)
|
|
217
|
+
GIT_OK=$(printf '%s' "$GIT_INFO" | grep -q '"installed":true' && echo yes || echo no)
|
|
218
|
+
|
|
219
|
+
BLOCKERS="["
|
|
220
|
+
ALL_OK="true"
|
|
221
|
+
if [ "$GIT_OK" = "no" ]; then
|
|
222
|
+
BLOCKERS="${BLOCKERS}\"git\","
|
|
223
|
+
ALL_OK="false"
|
|
224
|
+
fi
|
|
225
|
+
if [ "$JQ_OK" = "no" ]; then
|
|
226
|
+
BLOCKERS="${BLOCKERS}\"jq\","
|
|
227
|
+
ALL_OK="false"
|
|
228
|
+
fi
|
|
229
|
+
BLOCKERS="${BLOCKERS%,}]" # strip trailing comma
|
|
230
|
+
|
|
231
|
+
NOW=$(date -u +%Y-%m-%dT%H:%M:%SZ)
|
|
232
|
+
|
|
233
|
+
# Build JQ install hints if needed
|
|
234
|
+
JQ_HINTS="[]"
|
|
235
|
+
if [ "$JQ_OK" = "no" ]; then
|
|
236
|
+
JQ_HINTS=$(jq_install_hints)
|
|
237
|
+
fi
|
|
238
|
+
|
|
239
|
+
# ─── Assemble output JSON ──────────────────────────────────────────────
|
|
240
|
+
OUTPUT=$(cat <<EOF
|
|
241
|
+
{
|
|
242
|
+
"detected_at": "$NOW",
|
|
243
|
+
"platform": "$PLATFORM",
|
|
244
|
+
"shell": "$SHELL_DESC",
|
|
245
|
+
"required": {
|
|
246
|
+
"git": $GIT_INFO,
|
|
247
|
+
"jq": $JQ_INFO
|
|
248
|
+
},
|
|
249
|
+
"jq_install_hints": $JQ_HINTS,
|
|
250
|
+
"ai_clis": {
|
|
251
|
+
"claude": $CLAUDE_INFO,
|
|
252
|
+
"codex": $CODEX_INFO,
|
|
253
|
+
"gemini": $GEMINI_INFO
|
|
254
|
+
},
|
|
255
|
+
"tier": "$TIER",
|
|
256
|
+
"recommendation": "$RECOMMENDATION",
|
|
257
|
+
"all_required_ok": $ALL_OK,
|
|
258
|
+
"blockers": $BLOCKERS
|
|
259
|
+
}
|
|
260
|
+
EOF
|
|
261
|
+
)
|
|
262
|
+
|
|
263
|
+
# Pretty-print if jq available (recursive bootstrap problem: if jq isn't installed yet, we just output raw)
|
|
264
|
+
if command -v jq >/dev/null 2>&1; then
|
|
265
|
+
echo "$OUTPUT" | jq .
|
|
266
|
+
else
|
|
267
|
+
echo "$OUTPUT"
|
|
268
|
+
fi
|
|
269
|
+
|
|
270
|
+
# ─── Finalize mode: write env-check.json if all required deps OK ──────
|
|
271
|
+
if [ "$MODE" = "--finalize" ]; then
|
|
272
|
+
if [ "$ALL_OK" != "true" ]; then
|
|
273
|
+
echo "" >&2
|
|
274
|
+
echo "✗ Cannot finalize — blockers remain: $BLOCKERS" >&2
|
|
275
|
+
echo " Install the missing dependency, then re-run env-check.sh --finalize" >&2
|
|
276
|
+
exit 1
|
|
277
|
+
fi
|
|
278
|
+
|
|
279
|
+
mkdir -p .harness/state
|
|
280
|
+
echo "$OUTPUT" > .harness/state/env-check.json
|
|
281
|
+
echo "" >&2
|
|
282
|
+
echo "✓ Environment check finalized: .harness/state/env-check.json" >&2
|
|
283
|
+
echo " Phase 1 complete. Next: project deployment via /init" >&2
|
|
284
|
+
fi
|
|
285
|
+
|
|
286
|
+
exit 0
|