UNRELEASED DEVELOPMENT SNAPSHOT · READ-ONLY EXCERPT
bin/li-scaffold
This is the cited technical source at local revision 275a354, inspected on 22 September 2026. It differs from the public release. This reference publishes only the cited sections, not an installable release or the development branch.
Line numbers refer to the complete recorded file. Uncited lines are omitted. Full-file SHA-256: a6d33762361fc17da2e88daa659d3ea18f65c57230ed59b2efd1ba19d376feab.
1
#!/usr/bin/env bash2
# li-scaffold — copy scaffolding/01-foundation/* into target repo with repo-specific rendering3
#4
# Usage:5
# li-scaffold init [--target <path>] [--name <repo-name>] [--pack <pack>] [--mode <type>]6
# li-scaffold check [--target <path>]7
#8
# Defaults:9
# --target: current working directory10
# --name: basename of target11
# --pack: _default (neutral)12
# --mode: internal-tool14
set -euo pipefail16
# Prefer the adapter-supplied installed bundle. Executable-relative discovery… omitted uncited lines …
149
cmd_init() {150
# Validate the complete Copilot target before writing any scaffold files.151
if [ "$COPILOT" = 1 ]; then152
bash "$SCRIPT_DIR/li-copilot" init --target "$TARGET"153
return154
fi155
check_source156
echo "li-scaffold: initializing $TARGET"157
echo " name=$NAME pack=$PACK mode=$MODE voice=$VOICE_TIER compliance=$COMPLIANCE_LEVEL"158
echo ""160
cd "$TARGET"162
# CLAUDE.md from template (basic substitution)163
if [ -f "CLAUDE.md" ]; then164
echo " ⚠ CLAUDE.md exists — skipping (use --force to overwrite, future feature)"165
else166
if [ -f "$SCAFFOLDING_SRC/CLAUDE.md.template" ]; then167
# Pure-bash literal substitution — NOT sed (battletest K1: GNU sed's `s///e`168
# executes shell, so a repo dir / --name containing `x/;e <cmd>` was169
# operator-priv RCE). Parameter expansion replaces literally — the value is170
# never interpreted as a program or regex.171
_render="$(cat "$SCAFFOLDING_SRC/CLAUDE.md.template")"172
_render="${_render//\{\{REPO_NAME\}\}/$NAME}"173
_render="${_render//\{\{PACK\}\}/$PACK}"174
_render="${_render//\{\{MODE\}\}/$MODE}"175
_render="${_render//\{\{VOICE_TIER\}\}/$VOICE_TIER}"176
printf '%s\n' "$_render" > CLAUDE.md177
echo " ✓ CLAUDE.md (rendered)"178
else179
echo " ⚠ CLAUDE.md.template not found — skipping"180
fi181
fi183
# Both entry files carry the complete shared session protocol. Existing project184
# entries remain user-owned; the Copilot installer supports managed block updates.185
if [ ! -f "AGENTS.md" ] && [ -f "$SCAFFOLDING_SRC/AGENTS.md.template" ]; then186
_render="$(cat "$SCAFFOLDING_SRC/AGENTS.md.template")"187
_render="${_render//\{\{REPO_NAME\}\}/$NAME}"188
_render="${_render//\{\{PACK\}\}/$PACK}"189
_render="${_render//\{\{MODE\}\}/$MODE}"190
_render="${_render//\{\{VOICE_TIER\}\}/$VOICE_TIER}"191
printf '%s\n' "$_render" > AGENTS.md192
echo " ✓ AGENTS.md (full session protocol)"193
fi195
# .claude/rules/ — path-scoped rules (Claude Code loads paths:-scoped rules196
# natively; the session-digest hook lists them as an index for other CLIs)197
mkdir -p .claude/rules198
if [ ! -f ".claude/rules/README.md" ]; then199
cat > .claude/rules/README.md <<'RULESEOF'200
# Path-scoped rules202
One rule per file. Claude Code loads PROJECT-level rules (`./.claude/rules/`) conditioned on203
`paths:` frontmatter — globs MUST be quoted (`"src/api/**"`, unquoted globs fail silently) and204
user-level (`~/.claude/rules/`) path-scoping is broken as of mid-2026. Other CLIs see the rule205
titles in the session digest and read on demand.207
```markdown208
---209
paths:210
- "src/api/**"211
---212
The API layer never throws raw exceptions across the boundary — wrap in ApiError.213
```215
Promote a lesson here when it is path-specific (cheaper than loading it every session).216
RULESEOF217
echo " ✓ .claude/rules/README.md"218
fi220
# Copy static templates221
for f in CORE-PRINCIPLES.md SESSION-PROTOCOL.md EVOLUTION.md EVOLUTION-LOG.md TEMPLATE-skill.md TEMPLATE-agent.md; do222
if [ -f "$SCAFFOLDING_SRC/$f" ] && [ ! -f "$f" ]; then223
cp "$SCAFFOLDING_SRC/$f" .224
echo " ✓ $f"225
fi226
done228
# .claude/memory/ + .claude/plans/ + .claude/decisions/ (v5 layout, ADR-0005)229
mkdir -p .claude/memory .claude/plans .claude/decisions230
for f in lessons.md working-state.md personas.md personas-example.md; do231
if [ -f "$SCAFFOLDING_SRC/.claude/memory/$f" ] && [ ! -f ".claude/memory/$f" ]; then232
cp "$SCAFFOLDING_SRC/.claude/memory/$f" .claude/memory/233
echo " ✓ .claude/memory/$f"234
fi235
done236
if [ -f "$SCAFFOLDING_SRC/.claude/plans/todo.md" ] && [ ! -f ".claude/plans/todo.md" ]; then237
cp "$SCAFFOLDING_SRC/.claude/plans/todo.md" .claude/plans/238
echo " ✓ .claude/plans/todo.md"239
fi240
for f in "$SCAFFOLDING_SRC"/.claude/decisions/*.md; do241
[ -f "$f" ] || continue242
name=$(basename "$f")243
if [ ! -f ".claude/decisions/$name" ]; then244
cp "$f" .claude/decisions/245
echo " ✓ .claude/decisions/$name"246
fi247
done249
# Layout stamping (marker, .gitignore, MEMORY.md seed, auto-memory pointer) —250
# one implementation: the idempotent migration tool no-ops the moves on a251
# fresh repo and stamps the rest.252
if git rev-parse --show-toplevel >/dev/null 2>&1; then253
bash "$SCRIPT_DIR/li-migrate-claude-home" --repo . | sed 's/^/ /'254
else255
echo " ⚠ not a git repo — skipped layout marker/auto-memory stamping (run li-migrate-claude-home after git init)"256
fi258
# .claude/ — note: no .claude/agents/ is scaffolded. The subagent fleet ships with the259
# plugin; a repo-local agent would shadow the fleet's same-named one (2026-06-14, ADR-0015).260
if [ -f "$SCAFFOLDING_SRC/.claude/SUBAGENT-GUIDE.md" ] && [ ! -f ".claude/SUBAGENT-GUIDE.md" ]; then261
cp "$SCAFFOLDING_SRC/.claude/SUBAGENT-GUIDE.md" .claude/262
echo " ✓ .claude/SUBAGENT-GUIDE.md"263
fi265
# Swarm templates are repo-local seeds, not agent definitions. They let a fresh266
# scaffold create durable briefs and evidence without shadowing the plugin fleet.267
mkdir -p .claude/templates/swarm268
for f in "$SCAFFOLDING_SRC"/templates/swarm/*; do269
[ -f "$f" ] || continue270
name=$(basename "$f")271
if [ ! -f ".claude/templates/swarm/$name" ]; then272
cp "$f" ".claude/templates/swarm/$name"273
echo " ✓ .claude/templates/swarm/$name"274
fi275
done… omitted uncited lines …
279
echo ""280
echo "Next steps:"281
echo " 1. Review CLAUDE.md and customize project-specific sections."282
echo " 2. Edit .claude/memory/personas.md with project-specific personas."283
echo " 3. Install Lintel plugin for your CLI:"284
echo " Claude Code: /plugin marketplace add jokerman89/lintel"285
echo " /plugin install li@jokerman-lintel"286
echo " Codex: /plugins → search li → Install"287
echo " Cursor: /add-plugin li"288
echo " Gemini: gemini extensions install https://github.com/jokerman89/lintel"289
echo " 4. Run /li:welcome in your CLI — detects your CLI, shows its honest tier, runs a dry-run cycle."290
echo " 5. git add -A && git commit -m 'chore: scaffold Lintel base'"291
echo ""292
}294
case "$CMD" in295
init) cmd_init ;;296
check) cmd_check ;;297
help|--help|-h) help ;;298
*) help; exit 2 ;;299
esac