mirror of
https://github.com/Donchitos/Claude-Code-Game-Studios.git
synced 2026-10-16 09:58:36 +00:00
Release 1.1.0 — one config file, and a process weight you choose
`project.yaml` at the repo root is now the single source of truth for a project's configuration, replacing `production/stage.txt`, `production/review-mode.txt` and `.claude/docs/technical-preferences.md` (all three still read as a fallback; nothing is force-deleted). **`modes.rigor` — one question instead of six.** `minimal`, `standard` or `full` fronts six knobs: `modes.workflow`, `docs.density`, `qa.level`, `modes.story_granularity`, `modes.review_mode` and `team.size`. It defaults to `minimal`, which is a change from 1.0's behaviour: the heavier tier was measured costing several times more to reach working code without producing a better result, and returning nothing when a fresh developer inherited the project. Pin `modes.rigor: standard` to keep 1.0's process level. Also in this release: - `/settings` to view or change any setting, and `project.local.yaml` for per-developer preferences that should not be committed. - `system_overrides` to hold one system to a higher standard than the rest of the project, and `testing.strict` to set per test type whether missing evidence blocks a story or warns. - A story that changes anything the player sees is not closed until the game has been launched, observed, and a screenshot retained in `production/qa/evidence/`. A parse check is not a run. This holds at every rigor level, including `minimal`, where automated tests are waived but the look is not. - The code root is engine-specific and enforced as such: Godot `src/`, Unity `Assets/`, Unreal `Source/<Module>/`. Two of the three will not build a project laid out any other way. - Migration tooling for existing projects: `.claude/scripts/migrate-v1-config.sh` converts a 1.0 layout, non-destructively by default, with a report to review before `--finalize` deletes anything. - Ask-before-write holds regardless of personal automation settings. Upgrading from 1.0 or earlier: see UPGRADING.md. Full list of changes: CHANGELOG.md. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016xvzG3i7pWQg4LjyDwWMXd
This commit is contained in:
co-authored by
Claude Opus 5
parent
984023ddac
commit
d05699707f
+100
-12
@@ -1,4 +1,32 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# --- work from the project root ----------------------------------------------
|
||||
# Every path below is repo-relative, so a hook invoked with a working directory
|
||||
# that is not the repo root would silently read and write the WRONG TREE --
|
||||
# returning a near-empty result instead of the session-recovery block, and
|
||||
# creating stray trees such as docs/production/session-logs/ on write.
|
||||
#
|
||||
# PRECEDENCE IS LOAD-BEARING. A cwd that IS a project root carries real
|
||||
# information and must win: a caller sitting inside another project means that
|
||||
# project, not this one. Resolving to the script's own location first would
|
||||
# override them. So, in order:
|
||||
# 1. cwd holds project.yaml -> cwd (a project root)
|
||||
# 2. cwd holds .claude/ -> cwd (a project root not yet configured)
|
||||
# 3. CLAUDE_PROJECT_DIR -> that (populated in the hook environment)
|
||||
# 4. this script's location -> <root>/.claude/hooks/../.. by construction
|
||||
# Rule 4 always works and needs no environment at all; rules 1-2 stop it from
|
||||
# overriding a caller that legitimately means somewhere else.
|
||||
#
|
||||
# NOT an upward search: that resolves a nested project to its parent's config.
|
||||
if [ -f "project.yaml" ] || [ -d ".claude" ]; then
|
||||
CCGS_ROOT="$PWD"
|
||||
elif [ -n "${CLAUDE_PROJECT_DIR:-}" ] && [ -d "${CLAUDE_PROJECT_DIR}" ]; then
|
||||
CCGS_ROOT="$CLAUDE_PROJECT_DIR"
|
||||
else
|
||||
CCGS_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." 2>/dev/null && pwd)"
|
||||
fi
|
||||
[ -n "$CCGS_ROOT" ] && cd "$CCGS_ROOT" 2>/dev/null || true
|
||||
|
||||
# Claude Code Game Studios — Status Line
|
||||
# Receives JSON on stdin, outputs a single-line status.
|
||||
#
|
||||
@@ -12,9 +40,9 @@ if command -v jq &>/dev/null; then
|
||||
used_pct=$(echo "$input" | jq -r '.context_window.used_percentage // empty')
|
||||
cwd=$(echo "$input" | jq -r '.workspace.current_dir // .cwd // ""')
|
||||
else
|
||||
model=$(echo "$input" | grep -oE '"display_name"\s*:\s*"[^"]*"' | head -1 | sed 's/.*: *"//;s/"//')
|
||||
used_pct=$(echo "$input" | grep -oE '"used_percentage"\s*:\s*[0-9]+' | head -1 | sed 's/.*: *//')
|
||||
cwd=$(echo "$input" | grep -oE '"current_dir"\s*:\s*"[^"]*"' | head -1 | sed 's/.*: *"//;s/"//')
|
||||
model=$(echo "$input" | grep -oE '"display_name"[[:space:]]*:[[:space:]]*"[^"]*"' | head -1 | sed 's/.*: *"//;s/"//')
|
||||
used_pct=$(echo "$input" | grep -oE '"used_percentage"[[:space:]]*:[[:space:]]*[0-9]+' | head -1 | sed 's/.*: *//')
|
||||
cwd=$(echo "$input" | grep -oE '"current_dir"[[:space:]]*:[[:space:]]*"[^"]*"' | head -1 | sed 's/.*: *"//;s/"//')
|
||||
[ -z "$model" ] && model="Unknown"
|
||||
fi
|
||||
|
||||
@@ -30,14 +58,23 @@ else
|
||||
fi
|
||||
|
||||
# --- Production stage ---
|
||||
# Priority 1: Explicit stage from stage.txt
|
||||
stage_file="$cwd/production/stage.txt"
|
||||
# Priority 1: project.stage from project.yaml
|
||||
stage=""
|
||||
if [ -f "$stage_file" ]; then
|
||||
stage=$(head -1 "$stage_file" | tr -d '\r\n')
|
||||
project_yaml="$cwd/project.yaml"
|
||||
yaml_helper="$cwd/.claude/hooks/yaml-helper.sh"
|
||||
if [ -f "$project_yaml" ] && [ -f "$yaml_helper" ]; then
|
||||
source "$yaml_helper"
|
||||
stage=$(get_yaml_key "$project_yaml" project.stage 2>/dev/null)
|
||||
fi
|
||||
# Priority 2: legacy stage.txt fallback
|
||||
if [ -z "$stage" ]; then
|
||||
stage_file="$cwd/production/stage.txt"
|
||||
if [ -f "$stage_file" ]; then
|
||||
stage=$(head -1 "$stage_file" | tr -d '\r\n')
|
||||
fi
|
||||
fi
|
||||
|
||||
# Priority 2: Auto-detect from artifacts
|
||||
# Priority 3: Auto-detect from artifacts
|
||||
if [ -z "$stage" ]; then
|
||||
concept_file="$cwd/design/gdd/game-concept.md"
|
||||
systems_file="$cwd/design/gdd/systems-index.md"
|
||||
@@ -51,9 +88,21 @@ if [ -z "$stage" ]; then
|
||||
[ -f "$concept_file" ] && has_concept=true
|
||||
[ -f "$systems_file" ] && has_systems=true
|
||||
|
||||
# Check if engine is configured (not placeholder)
|
||||
if [ -f "$tech_prefs" ]; then
|
||||
engine_line=$(grep -m1 '^\*\*Engine\*\*:' "$tech_prefs" 2>/dev/null || true)
|
||||
# Check if engine is configured (project.yaml first, fall back to technical-preferences.md)
|
||||
if [ -f "$project_yaml" ] && [ -f "$yaml_helper" ]; then
|
||||
# yaml-helper may have been sourced above for stage; sourcing again is idempotent
|
||||
source "$yaml_helper"
|
||||
engine_name=$(get_yaml_key "$project_yaml" engine.name 2>/dev/null)
|
||||
[ -n "$engine_name" ] && engine_configured=true
|
||||
fi
|
||||
if [ "$engine_configured" = false ] && [ -f "$tech_prefs" ]; then
|
||||
# Leading whitespace tolerated, matching detect-gaps.sh and the migrator.
|
||||
# This is the THIRD copy of "is the engine configured in
|
||||
# technical-preferences.md" in the tree, and it was the last one still
|
||||
# anchored to column 0 -- an indented bullet read as unconfigured here while
|
||||
# the other two read it as configured, so the auto-detect ladder below
|
||||
# dropped the project to an earlier stage than the rest of the system saw.
|
||||
engine_line=$(grep -m1 -E '^[[:space:]]*-[[:space:]]+\*\*Engine\*\*:' "$tech_prefs" 2>/dev/null || true)
|
||||
if [ -n "$engine_line" ] && ! echo "$engine_line" | grep -q "TO BE CONFIGURED"; then
|
||||
engine_configured=true
|
||||
fi
|
||||
@@ -86,6 +135,45 @@ if [ -z "$stage" ]; then
|
||||
fi
|
||||
fi
|
||||
|
||||
# --- Process posture (modes.rigor) ---
|
||||
# Locked to project.yaml (not locally overridable) with a plain terminal default
|
||||
# of 'standard', so a direct get_yaml_key read + default is exact. Deliberately
|
||||
# NOT resolve_setting: that assumes PWD is the project root, unsafe here since the
|
||||
# status line works from an absolute $cwd and never cd's.
|
||||
#
|
||||
# The 'standard' default applies ONLY when nothing contradicts it. If `rigor` is
|
||||
# unset but a knob it fronts is set explicitly, the project's real process weight
|
||||
# is whatever that knob says, and printing 'standard' actively misreports it —
|
||||
# migration is the common case, writing `modes.review_mode` and no `modes.rigor`,
|
||||
# so every v1.0 upgrader running full director reviews read 'Production · standard'.
|
||||
# Suppress instead of guessing, matching the unconfigured-project behaviour: no
|
||||
# config, no claim. Suppression is correct rather than lossy — the fronted knobs
|
||||
# disagree with each other in this state, so there is no single honest posture.
|
||||
rigor=""
|
||||
if [ -f "$project_yaml" ] && [ -f "$yaml_helper" ]; then
|
||||
source "$yaml_helper"
|
||||
rigor=$(get_yaml_key "$project_yaml" modes.rigor 2>/dev/null)
|
||||
if [ -z "$rigor" ]; then
|
||||
rigor="standard"
|
||||
# Cheap pre-filter first. This hook runs every turn, and the common case
|
||||
# (nothing fronted set) must not cost a get_yaml_key subprocess per key.
|
||||
# The grep is a deliberate SUPERSET — it matches the leaf names anywhere at
|
||||
# depth, so a false positive only costs the precise checks below, while a
|
||||
# miss is impossible. Never let it decide on its own: a bare `size:` under
|
||||
# some unrelated block would suppress the posture with no reason.
|
||||
_fronted='^[[:space:]]+(review_mode|workflow|density|level|story_granularity|size):[[:space:]]*[^[:space:]#]'
|
||||
for _f in "$project_yaml" "$cwd/project.local.yaml"; do
|
||||
[ -f "$_f" ] || continue
|
||||
grep -qE "$_fronted" "$_f" 2>/dev/null || continue
|
||||
for _k in modes.review_mode modes.workflow docs.density qa.level \
|
||||
modes.story_granularity team.size; do
|
||||
if [ -n "$(get_yaml_key "$_f" "$_k" 2>/dev/null)" ]; then rigor=""; break; fi
|
||||
done
|
||||
[ -z "$rigor" ] && break
|
||||
done
|
||||
fi
|
||||
fi
|
||||
|
||||
# --- Epic/Feature/Task breadcrumb (Production+ only) ---
|
||||
breadcrumb=""
|
||||
if [ "$stage" = "Production" ] || [ "$stage" = "Polish" ] || [ "$stage" = "Release" ]; then
|
||||
@@ -118,4 +206,4 @@ if [ "$stage" = "Production" ] || [ "$stage" = "Polish" ] || [ "$stage" = "Relea
|
||||
fi
|
||||
|
||||
# --- Assemble ---
|
||||
printf "%s" "${ctx_label} | ${model} | ${stage}${breadcrumb}"
|
||||
printf "%s" "${ctx_label} | ${model} | ${stage}${rigor:+ · $rigor}${breadcrumb}"
|
||||
|
||||
Reference in New Issue
Block a user