Source index · Technical reference · Home

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 bash
2# li-scaffold — copy scaffolding/01-foundation/* into target repo with repo-specific rendering
3#
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 directory
10# --name: basename of target
11# --pack: _default (neutral)
12# --mode: internal-tool
14set -euo pipefail
16# Prefer the adapter-supplied installed bundle. Executable-relative discovery
… omitted uncited lines …
149cmd_init() {
150 # Validate the complete Copilot target before writing any scaffold files.
151 if [ "$COPILOT" = 1 ]; then
152 bash "$SCRIPT_DIR/li-copilot" init --target "$TARGET"
153 return
154 fi
155 check_source
156 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" ]; then
164 echo " ⚠ CLAUDE.md exists — skipping (use --force to overwrite, future feature)"
165 else
166 if [ -f "$SCAFFOLDING_SRC/CLAUDE.md.template" ]; then
167 # 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>` was
169 # operator-priv RCE). Parameter expansion replaces literally — the value is
170 # 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.md
177 echo " ✓ CLAUDE.md (rendered)"
178 else
179 echo " ⚠ CLAUDE.md.template not found — skipping"
180 fi
181 fi
183 # Both entry files carry the complete shared session protocol. Existing project
184 # entries remain user-owned; the Copilot installer supports managed block updates.
185 if [ ! -f "AGENTS.md" ] && [ -f "$SCAFFOLDING_SRC/AGENTS.md.template" ]; then
186 _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.md
192 echo " ✓ AGENTS.md (full session protocol)"
193 fi
195 # .claude/rules/ — path-scoped rules (Claude Code loads paths:-scoped rules
196 # natively; the session-digest hook lists them as an index for other CLIs)
197 mkdir -p .claude/rules
198 if [ ! -f ".claude/rules/README.md" ]; then
199 cat > .claude/rules/README.md <<'RULESEOF'
200# Path-scoped rules
202One rule per file. Claude Code loads PROJECT-level rules (`./.claude/rules/`) conditioned on
203`paths:` frontmatter — globs MUST be quoted (`"src/api/**"`, unquoted globs fail silently) and
204user-level (`~/.claude/rules/`) path-scoping is broken as of mid-2026. Other CLIs see the rule
205titles in the session digest and read on demand.
207```markdown
208---
209paths:
210 - "src/api/**"
211---
212The API layer never throws raw exceptions across the boundary — wrap in ApiError.
213```
215Promote a lesson here when it is path-specific (cheaper than loading it every session).
216RULESEOF
217 echo " ✓ .claude/rules/README.md"
218 fi
220 # Copy static templates
221 for f in CORE-PRINCIPLES.md SESSION-PROTOCOL.md EVOLUTION.md EVOLUTION-LOG.md TEMPLATE-skill.md TEMPLATE-agent.md; do
222 if [ -f "$SCAFFOLDING_SRC/$f" ] && [ ! -f "$f" ]; then
223 cp "$SCAFFOLDING_SRC/$f" .
224 echo " ✓ $f"
225 fi
226 done
228 # .claude/memory/ + .claude/plans/ + .claude/decisions/ (v5 layout, ADR-0005)
229 mkdir -p .claude/memory .claude/plans .claude/decisions
230 for f in lessons.md working-state.md personas.md personas-example.md; do
231 if [ -f "$SCAFFOLDING_SRC/.claude/memory/$f" ] && [ ! -f ".claude/memory/$f" ]; then
232 cp "$SCAFFOLDING_SRC/.claude/memory/$f" .claude/memory/
233 echo " ✓ .claude/memory/$f"
234 fi
235 done
236 if [ -f "$SCAFFOLDING_SRC/.claude/plans/todo.md" ] && [ ! -f ".claude/plans/todo.md" ]; then
237 cp "$SCAFFOLDING_SRC/.claude/plans/todo.md" .claude/plans/
238 echo " ✓ .claude/plans/todo.md"
239 fi
240 for f in "$SCAFFOLDING_SRC"/.claude/decisions/*.md; do
241 [ -f "$f" ] || continue
242 name=$(basename "$f")
243 if [ ! -f ".claude/decisions/$name" ]; then
244 cp "$f" .claude/decisions/
245 echo " ✓ .claude/decisions/$name"
246 fi
247 done
249 # Layout stamping (marker, .gitignore, MEMORY.md seed, auto-memory pointer) —
250 # one implementation: the idempotent migration tool no-ops the moves on a
251 # fresh repo and stamps the rest.
252 if git rev-parse --show-toplevel >/dev/null 2>&1; then
253 bash "$SCRIPT_DIR/li-migrate-claude-home" --repo . | sed 's/^/ /'
254 else
255 echo " ⚠ not a git repo — skipped layout marker/auto-memory stamping (run li-migrate-claude-home after git init)"
256 fi
258 # .claude/ — note: no .claude/agents/ is scaffolded. The subagent fleet ships with the
259 # 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" ]; then
261 cp "$SCAFFOLDING_SRC/.claude/SUBAGENT-GUIDE.md" .claude/
262 echo " ✓ .claude/SUBAGENT-GUIDE.md"
263 fi
265 # Swarm templates are repo-local seeds, not agent definitions. They let a fresh
266 # scaffold create durable briefs and evidence without shadowing the plugin fleet.
267 mkdir -p .claude/templates/swarm
268 for f in "$SCAFFOLDING_SRC"/templates/swarm/*; do
269 [ -f "$f" ] || continue
270 name=$(basename "$f")
271 if [ ! -f ".claude/templates/swarm/$name" ]; then
272 cp "$f" ".claude/templates/swarm/$name"
273 echo " ✓ .claude/templates/swarm/$name"
274 fi
275 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 ""
294case "$CMD" in
295 init) cmd_init ;;
296 check) cmd_check ;;
297 help|--help|-h) help ;;
298 *) help; exit 2 ;;
299esac