@friedbotstudio/create-baseline 0.8.0 → 0.8.1
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/obj/template/.claude/hooks/git_commit_guard.mjs +2 -2
- package/obj/template/.claude/manifest.json +8 -8
- package/obj/template/.claude/project.json +1 -1
- package/obj/template/.claude/skills/audit-baseline/audit.sh +25 -8
- package/obj/template/.claude/skills/changelog/changelog.mjs +1 -1
- package/obj/template/.claude/skills/changelog/tests/consent-expired_test.sh +4 -4
- package/obj/template/docs/init/seed.md +1 -1
- package/package.json +1 -1
- package/src/project.template.json +1 -1
- package/src/seed.template.md +1 -1
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
// --abbrev-ref HEAD`. Detached HEAD ("HEAD") → DENY explicitly.
|
|
11
11
|
// - On a branch matched by project.json → git.protected_branches
|
|
12
12
|
// (or when that key is null/absent → every branch protected),
|
|
13
|
-
// commits require fresh commit_consent (
|
|
13
|
+
// commits require fresh commit_consent (900s) and pushes require
|
|
14
14
|
// fresh push_consent (300s).
|
|
15
15
|
// - When git.branch_pattern is set and the current branch does NOT
|
|
16
16
|
// match the regex, commits are denied with the pattern surfaced.
|
|
@@ -179,7 +179,7 @@ function handleBash(cmd) {
|
|
|
179
179
|
|
|
180
180
|
// Protected — require the matching consent token.
|
|
181
181
|
if (isCommit) {
|
|
182
|
-
validateConsentToken(`${STATE_DIR}/commit_consent`, '.consent.commit_ttl_seconds',
|
|
182
|
+
validateConsentToken(`${STATE_DIR}/commit_consent`, '.consent.commit_ttl_seconds', 900, 'Git Commit Guard', '/grant-commit');
|
|
183
183
|
} else {
|
|
184
184
|
validateConsentToken(`${STATE_DIR}/push_consent`, '.consent.push_ttl_seconds', 300, 'Git Commit Guard', '/grant-push');
|
|
185
185
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"manifest_version": 3,
|
|
3
|
-
"generated_at": "2026-05-
|
|
3
|
+
"generated_at": "2026-05-22T15:13:25.707Z",
|
|
4
4
|
"files": {
|
|
5
5
|
".claude/agents/swarm-worker.md": {
|
|
6
6
|
"sha256": "1735a220f268c9765cb22e0567b728803f2edd7776cbde51dd017a9f062ae41f",
|
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
"tier": "MECHANICAL"
|
|
56
56
|
},
|
|
57
57
|
".claude/hooks/git_commit_guard.mjs": {
|
|
58
|
-
"sha256": "
|
|
58
|
+
"sha256": "dbea846f2e11a68054a902ee37ac2f11383fdfc9abff33fa2bdec0f4c373f610",
|
|
59
59
|
"tier": "MECHANICAL"
|
|
60
60
|
},
|
|
61
61
|
".claude/hooks/harness_continuation.sh": {
|
|
@@ -203,7 +203,7 @@
|
|
|
203
203
|
"tier": "MECHANICAL"
|
|
204
204
|
},
|
|
205
205
|
".claude/project.json": {
|
|
206
|
-
"sha256": "
|
|
206
|
+
"sha256": "752fe71917e2d0c38e756427af8ca0a01efe096cc633dced2f870edfc878c495",
|
|
207
207
|
"tier": "NEVER_TOUCH"
|
|
208
208
|
},
|
|
209
209
|
".claude/schemas/workflow-track.v1.json": {
|
|
@@ -227,7 +227,7 @@
|
|
|
227
227
|
"tier": "MECHANICAL"
|
|
228
228
|
},
|
|
229
229
|
".claude/skills/audit-baseline/audit.sh": {
|
|
230
|
-
"sha256": "
|
|
230
|
+
"sha256": "68011b46a05473237b9d1ce3d6c9c6d760b460e3416a749b2738504fbde6ab4f",
|
|
231
231
|
"tier": "MECHANICAL"
|
|
232
232
|
},
|
|
233
233
|
".claude/skills/audit-baseline/tests/fixtures/_pending_opener_only.md": {
|
|
@@ -267,7 +267,7 @@
|
|
|
267
267
|
"tier": "MECHANICAL"
|
|
268
268
|
},
|
|
269
269
|
".claude/skills/changelog/changelog.mjs": {
|
|
270
|
-
"sha256": "
|
|
270
|
+
"sha256": "a00e9aae5d660424588a37774d13d44625012506e128cf51bb9198b9c9c518dd",
|
|
271
271
|
"tier": "MECHANICAL"
|
|
272
272
|
},
|
|
273
273
|
".claude/skills/changelog/classifier.mjs": {
|
|
@@ -279,7 +279,7 @@
|
|
|
279
279
|
"tier": "MECHANICAL"
|
|
280
280
|
},
|
|
281
281
|
".claude/skills/changelog/tests/consent-expired_test.sh": {
|
|
282
|
-
"sha256": "
|
|
282
|
+
"sha256": "da950772c5f6938c81b7c2667d1807b0da838e1f8ea00d64970d2251692b7b87",
|
|
283
283
|
"tier": "MECHANICAL"
|
|
284
284
|
},
|
|
285
285
|
".claude/skills/changelog/tests/golden-path_test.sh": {
|
|
@@ -843,7 +843,7 @@
|
|
|
843
843
|
"tier": "SEMANTIC"
|
|
844
844
|
},
|
|
845
845
|
"docs/init/seed.md": {
|
|
846
|
-
"sha256": "
|
|
846
|
+
"sha256": "d1c33da767f4311d1f1c5efc98157b5c09a0b80331d7992f0ca66ec48a6ee08d",
|
|
847
847
|
"tier": "SEMANTIC"
|
|
848
848
|
}
|
|
849
849
|
},
|
|
@@ -889,5 +889,5 @@
|
|
|
889
889
|
"verify": "baseline"
|
|
890
890
|
}
|
|
891
891
|
},
|
|
892
|
-
"build_id": "gha-
|
|
892
|
+
"build_id": "gha-26295896640"
|
|
893
893
|
}
|
|
@@ -224,13 +224,17 @@ check_names("agents names match seed §4.2", EXPECTED_AGENTS, add_agents,
|
|
|
224
224
|
# Skills canonical set comes from manifest.owners.skills (built by
|
|
225
225
|
# scripts/build-manifest.mjs at release time). Falls back to disk_baseline_skills
|
|
226
226
|
# when the manifest is missing (e.g., first audit before initial build).
|
|
227
|
+
# Unlike hooks/agents/commands, the skills `disk_*` is `disk_baseline_skills`
|
|
228
|
+
# (filtered to `owner: baseline`), so `add_skills` (project additions, which are
|
|
229
|
+
# user/third-party by design) cannot be passed in here — they'd false-FAIL.
|
|
230
|
+
# Per CLAUDE.md Article XI #5.
|
|
227
231
|
_manifest_for_skills = load_manifest()
|
|
228
232
|
if _manifest_for_skills is None:
|
|
229
233
|
_canonical_skills = disk_baseline_skills
|
|
230
234
|
else:
|
|
231
235
|
_canonical_skills = set((_manifest_for_skills.get("owners") or {}).get("skills", {}).keys()) \
|
|
232
236
|
or disk_baseline_skills
|
|
233
|
-
check_names("skills names match seed §4.3", _canonical_skills,
|
|
237
|
+
check_names("skills names match seed §4.3", _canonical_skills, set(), disk_baseline_skills)
|
|
234
238
|
check_names("commands names match seed §4.4", EXPECTED_COMMANDS, set(), disk_commands)
|
|
235
239
|
|
|
236
240
|
# ---------- skill ownership (per-file hash drift + frontmatter validation) ----------
|
|
@@ -349,11 +353,23 @@ else:
|
|
|
349
353
|
# The src/ tree mirrors the canonical paths with a `.template` suffix so
|
|
350
354
|
# `npx @friedbotstudio/create-baseline` can discover and overlay deterministically.
|
|
351
355
|
src_dir = root / "src"
|
|
356
|
+
# Consumer-mode detection: the CLI overlay ships .claude/manifest.json into
|
|
357
|
+
# every consumer project; src/ exists only in the baseline-dev repo as the
|
|
358
|
+
# overlay source. Manifest present + src/ absent ⇒ legitimate consumer install
|
|
359
|
+
# ⇒ skip the src/ checks instead of false-FAIL'ing. Per CLAUDE.md Appendix A.
|
|
360
|
+
consumer_manifest = (root / ".claude" / "manifest.json").is_file()
|
|
352
361
|
if not src_dir.is_dir():
|
|
353
|
-
|
|
362
|
+
if consumer_manifest:
|
|
363
|
+
add("src templates: directory", "PASS",
|
|
364
|
+
"consumer install (manifest present, src/ absent) — src/ checks skipped")
|
|
365
|
+
else:
|
|
366
|
+
add("src templates: directory", "FAIL", "missing src/")
|
|
367
|
+
_SKIP_SRC = True
|
|
354
368
|
else:
|
|
355
369
|
add("src templates: directory", "PASS", "")
|
|
370
|
+
_SKIP_SRC = False
|
|
356
371
|
|
|
372
|
+
if not _SKIP_SRC:
|
|
357
373
|
# CLAUDE.template.md — must exist and read as constitution-voice (or, in
|
|
358
374
|
# the pre-Stage-2 transitional shape, at least the user-voice lede). The
|
|
359
375
|
# test below tolerates either: dogfood-leak fails hard; constitutional
|
|
@@ -630,12 +646,13 @@ else:
|
|
|
630
646
|
add("CLAUDE.md: Article X.2 present", "FAIL",
|
|
631
647
|
"missing `### X.2 Design-task routing` heading — Article X.2 is the structural seam between design-ui and impeccable")
|
|
632
648
|
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
"src
|
|
649
|
+
if not _SKIP_SRC:
|
|
650
|
+
template_claude = read_text("src/CLAUDE.template.md")
|
|
651
|
+
if "### X.2 Design-task routing" in template_claude:
|
|
652
|
+
add("src/CLAUDE.template.md: Article X.2 mirrors", "PASS", "")
|
|
653
|
+
else:
|
|
654
|
+
add("src/CLAUDE.template.md: Article X.2 mirrors", "FAIL",
|
|
655
|
+
"src template does not contain Article X.2 — template-drift will fail")
|
|
639
656
|
|
|
640
657
|
design_ui_skill = read_text(".claude/skills/design-ui/SKILL.md")
|
|
641
658
|
if re.search(r'^description:.*orchestrat', design_ui_skill, re.MULTILINE | re.IGNORECASE):
|
|
@@ -20,7 +20,7 @@ import { previewProjectedVersion } from './version-preview.mjs';
|
|
|
20
20
|
import { writeState } from './state-writer.mjs';
|
|
21
21
|
import { appendUnderUnreleased } from './unreleased-writer.mjs';
|
|
22
22
|
|
|
23
|
-
const TTL_SECONDS =
|
|
23
|
+
const TTL_SECONDS = 900;
|
|
24
24
|
|
|
25
25
|
function parseCli() {
|
|
26
26
|
const { values } = parseArgs({
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
2
|
# Fixture-based integration test for AC-010: when commit_consent token is
|
|
3
|
-
# stale (older than consent.commit_ttl_seconds, default
|
|
3
|
+
# stale (older than consent.commit_ttl_seconds, default 900s), the changelog
|
|
4
4
|
# skill exits non-zero with "consent expired" stderr, does NOT modify
|
|
5
5
|
# CHANGELOG.md, and does NOT write the state file.
|
|
6
6
|
#
|
|
@@ -16,7 +16,7 @@ PASS=0; FAIL=0; FAILED=()
|
|
|
16
16
|
|
|
17
17
|
fail() { echo " FAIL: $*"; return 1; }
|
|
18
18
|
|
|
19
|
-
# Seed a tempdir with a stale commit_consent (epoch = now -
|
|
19
|
+
# Seed a tempdir with a stale commit_consent (epoch = now - 910s).
|
|
20
20
|
seed_stale_consent_project() {
|
|
21
21
|
local proj="$1" slug="$2"
|
|
22
22
|
mkdir -p "$proj/.claude/state"
|
|
@@ -29,8 +29,8 @@ seed_stale_consent_project() {
|
|
|
29
29
|
echo "stale test" > thing.txt
|
|
30
30
|
git add thing.txt
|
|
31
31
|
git commit -q -m "feat: stale consent path"
|
|
32
|
-
# Stale token: epoch in the past, beyond
|
|
33
|
-
local stale_epoch; stale_epoch=$(( $(date +%s) -
|
|
32
|
+
# Stale token: epoch in the past, beyond 900s default TTL.
|
|
33
|
+
local stale_epoch; stale_epoch=$(( $(date +%s) - 910 ))
|
|
34
34
|
echo "$stale_epoch" > "$proj/.claude/state/commit_consent"
|
|
35
35
|
echo "stale" >> "$proj/.claude/state/commit_consent"
|
|
36
36
|
cat > "$proj/.claude/state/workflow.json" <<EOF
|
|
@@ -531,7 +531,7 @@ Seed-level requirement: no stale workflow artifacts in the working tree after co
|
|
|
531
531
|
- `artifacts.required_sections.{intake,brd,spec,rca}` — the canonical section lists.
|
|
532
532
|
- `artifacts.required_diagrams.spec` — the six kinds (§4.7).
|
|
533
533
|
- `swarm.max_parallel`, `swarm.isolation: "auto"`, `swarm.min_tasks_worth_swarming: 3`, `swarm.refuse_dirty_tree: true`, `swarm.exempt_path_prefixes`, `swarm.enforced_path_prefixes`.
|
|
534
|
-
- `consent.commit_ttl_seconds:
|
|
534
|
+
- `consent.commit_ttl_seconds: 900`.
|
|
535
535
|
- `additions.{agents,skills,hooks,mcp_servers,swarm_worker_skills}` — names of every project-adopted addition the recommender emitted (just identifiers, no `command`/`why`/`tokens` payload). `additions.agents` stays empty in this baseline — the recommender does not propose new subagent types. `additions.swarm_worker_skills` lists stack-specific skills the `swarm-worker` template should preload via the `{{SKILLS}}` token at re-render time. `audit.sh` reads this manifest and unions each set with the baseline `EXPECTED_*` sets when checking names; counts are reframed as `"<total> = <baseline> + <project>"` so legitimate additions don't fail drift detection. Default state is five empty arrays.
|
|
536
536
|
- Flip `configured: true`.
|
|
537
537
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@friedbotstudio/create-baseline",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.1",
|
|
4
4
|
"description": "Node CLI scaffolder that materializes the Claude Code baseline (hooks, skills, commands, MCP servers, governance docs) into a target project, with branded interactive install / upgrade / doctor flows. Run via `npx @friedbotstudio/create-baseline <target>`.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
package/src/seed.template.md
CHANGED
|
@@ -531,7 +531,7 @@ Seed-level requirement: no stale workflow artifacts in the working tree after co
|
|
|
531
531
|
- `artifacts.required_sections.{intake,brd,spec,rca}` — the canonical section lists.
|
|
532
532
|
- `artifacts.required_diagrams.spec` — the six kinds (§4.7).
|
|
533
533
|
- `swarm.max_parallel`, `swarm.isolation: "auto"`, `swarm.min_tasks_worth_swarming: 3`, `swarm.refuse_dirty_tree: true`, `swarm.exempt_path_prefixes`, `swarm.enforced_path_prefixes`.
|
|
534
|
-
- `consent.commit_ttl_seconds:
|
|
534
|
+
- `consent.commit_ttl_seconds: 900`.
|
|
535
535
|
- `additions.{agents,skills,hooks,mcp_servers,swarm_worker_skills}` — names of every project-adopted addition the recommender emitted (just identifiers, no `command`/`why`/`tokens` payload). `additions.agents` stays empty in this baseline — the recommender does not propose new subagent types. `additions.swarm_worker_skills` lists stack-specific skills the `swarm-worker` template should preload via the `{{SKILLS}}` token at re-render time. `audit.sh` reads this manifest and unions each set with the baseline `EXPECTED_*` sets when checking names; counts are reframed as `"<total> = <baseline> + <project>"` so legitimate additions don't fail drift detection. Default state is five empty arrays.
|
|
536
536
|
- Flip `configured: true`.
|
|
537
537
|
|