Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ User runs /setup
| Module | Purpose |
|--------|---------|
| `skills/readiness/SKILL.md` | Harness Readiness Report |
| `skills/setup/SKILL.md` | setup skill definition |
| `skills/setup/SKILL.md` | Setup Report |
| `skills/setup/scripts/generate-claude-md.js` | Generate tailored CLAUDE.md files for a project from templates. |
| `skills/setup/scripts/init-project.js` | Project scaffolding script for Node/TypeScript projects. |
| `skills/setup/scripts/install-enforcement.js` | Copies enforcement tooling into a target project. |
Expand Down
65 changes: 50 additions & 15 deletions skills/setup/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -184,19 +184,54 @@ If any check fails, fix the issue immediately (e.g., `chmod +x` a hook, regenera

---

## Phase 7: Summary

After all phases complete and verification passes, output a summary that includes:

1. **What was installed** — list every file created or modified
2. **Verification results** — confirm all 6 checks passed
3. **Key commands** for the stack:
- How to run tests
- How to run the linter
- How to make a commit (hooks fire automatically)
4. **TDD reminder**: "Write tests first. Red (failing test) → Green (passing) → Refactor. Never write implementation before a test exists."
5. **Suggested next steps**:
- Fill in the `[bracketed placeholders]` in CLAUDE.md
- Review and expand the Architecture section
- Add the first real feature with a test
## Phase 7: Write Setup Report

Write `.claude/setup-report.md` with YAML frontmatter summarizing everything that was done. This is a persistent state artifact that documents what the skill installed and what passed verification.

```markdown
---
generated: YYYY-MM-DD
stack: node-typescript (or python, go, rust, cpp)
framework: express (or fastapi, gin, none, etc.)
project_name: myapp
status: complete (or partial if any verification failed)
files_created:
- package.json
- CLAUDE.md
- .claude/settings.json
- scripts/check-secrets.js
- (list every file created)
files_modified:
- (list any existing files that were changed)
verification:
hooks_executable: true
enforcement_scripts: true
claude_md_sections: true
agent_config_valid: true
auto_doc_pipeline: true
linter_clean: true (or skipped)
---

# Setup Report

## What Was Installed
(list every file, grouped by category)

## Verification Results
(pass/fail for each of the 6 checks from Phase 6)

## Key Commands
(stack-appropriate commands for test, lint, commit)

## Next Steps
- Fill in the `[bracketed placeholders]` in CLAUDE.md
- Review and expand the Architecture section
- Add the first real feature with a test
```

After writing the report, also output a human-readable summary to the conversation that includes:

1. **Status** — complete or partial
2. **Key commands** for the stack
3. **TDD reminder**: "Write tests first. Red → Green → Refactor."
- Make the first commit to initialize git history
47 changes: 11 additions & 36 deletions tests/evals/run-evals.sh
Original file line number Diff line number Diff line change
Expand Up @@ -54,44 +54,14 @@ fi
command -v claude &>/dev/null || { echo -e "${RED}Error: 'claude' CLI not found.${NC}"; exit 1; }
command -v jq &>/dev/null || { echo -e "${RED}Error: 'jq' not found.${NC}"; exit 1; }

# Validate marketplace.json schema before running evals
MARKETPLACE="$PLUGIN_DIR/.claude-plugin/marketplace.json"
if [ -f "$MARKETPLACE" ]; then
echo -e "${BLUE}Validating marketplace.json schema...${NC}"
SCHEMA_ERRORS=""

# owner must be an object, not a string
OWNER_TYPE=$(jq -r '.owner | type' "$MARKETPLACE")
if [ "$OWNER_TYPE" != "object" ]; then
SCHEMA_ERRORS="${SCHEMA_ERRORS}\n - owner must be an object (got $OWNER_TYPE)"
fi

# reject unrecognized root-level keys
BAD_KEYS=$(jq -r 'keys[] | select(. != "$schema" and . != "name" and . != "owner" and . != "plugins" and . != "metadata")' "$MARKETPLACE" || true)
if [ -n "$BAD_KEYS" ]; then
SCHEMA_ERRORS="${SCHEMA_ERRORS}\n - unrecognized root-level keys: $BAD_KEYS"
fi

# each plugin source must be an object, not a string
STRING_SOURCES=$(jq -r '.plugins[]? | select(.source | type == "string") | .name' "$MARKETPLACE" || true)
if [ -n "$STRING_SOURCES" ]; then
SCHEMA_ERRORS="${SCHEMA_ERRORS}\n - plugin source must be an object, not a string (in: $STRING_SOURCES)"
fi

if [ -n "$SCHEMA_ERRORS" ]; then
echo -e "${RED}Error: marketplace.json has schema errors:${SCHEMA_ERRORS}${NC}"
# Validate plugin manifest
if command -v claude &>/dev/null && [ -f "$PLUGIN_DIR/.claude-plugin/marketplace.json" ]; then
echo -e "${BLUE}Validating plugin manifest...${NC}"
if ! claude plugin validate "$PLUGIN_DIR" 2>&1; then
echo -e "${RED}Error: claude plugin validate failed${NC}"
exit 1
fi

# Run claude plugin validate if available (authoritative check)
if command -v claude &>/dev/null; then
if ! claude plugin validate "$PLUGIN_DIR" 2>&1; then
echo -e "${RED}Error: claude plugin validate failed${NC}"
exit 1
fi
fi

echo -e "${GREEN} marketplace.json schema OK${NC}"
echo -e "${GREEN} manifest OK${NC}"
echo ""
fi

Expand Down Expand Up @@ -211,6 +181,11 @@ run_test_case() {
for d in .claude scripts docs src tests .husky .git/hooks; do
[ -d "$tmp_dir/$d" ] && { mkdir -p "$result_dir/$d"; cp -r "$tmp_dir/$d/." "$result_dir/$d/" 2>/dev/null || true; }
done
if [ -f "$tmp_dir/.claude/setup-report.md" ]; then
echo -e " ${GREEN}✓ Setup report created${NC}"
else
echo -e " ${RED}✗ Setup report NOT created${NC}"
fi
echo -e " ${GREEN}✓ Setup artifacts captured${NC}"
fi

Expand Down
77 changes: 33 additions & 44 deletions tests/evals/setup-grader.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,25 @@ function isExecutable(p) {
}
}

// ─── 0. Setup report ───
const reportPath = path.join(fixtureDir, '.claude/setup-report.md');
let reportFrontmatter = {};
check('Setup report created', pathExists(reportPath));
if (pathExists(reportPath)) {
const fmMatch = fs.readFileSync(reportPath, 'utf8').match(/^---\n([\s\S]*?)\n---/);
if (fmMatch) {
check('Report has YAML frontmatter', true);
for (const line of fmMatch[1].split('\n')) {
const kv = line.match(/^(\w[\w_-]*)\s*:\s*(.+)$/);
if (kv) reportFrontmatter[kv[1]] = kv[2].trim();
}
check('Report has stack', !!reportFrontmatter.stack, reportFrontmatter.stack || 'Missing');
check('Report status complete', reportFrontmatter.status === 'complete', reportFrontmatter.status || 'Missing');
} else {
check('Report has YAML frontmatter', false, 'No --- delimiters');
}
}

// ─── 1. Required files must exist ───
for (const filePath of expected.files_must_exist || []) {
const fullPath = path.join(fixtureDir, filePath);
Expand Down Expand Up @@ -169,52 +188,22 @@ if (expected.rules_have_globs_frontmatter) {

// ─── 8. Auto-documentation pipeline ───
if (expected.auto_doc_pipeline) {
// Check generate-docs scripts exist
const genDocsPath = path.join(fixtureDir, 'scripts/generate-docs.js');
const genHelpersPath = path.join(fixtureDir, 'scripts/generate-docs-helpers.js');
check(
'Auto-doc: generate-docs.js exists',
pathExists(genDocsPath),
pathExists(genDocsPath) ? '' : 'Not found',
);
check(
'Auto-doc: generate-docs-helpers.js exists',
pathExists(genHelpersPath),
pathExists(genHelpersPath) ? '' : 'Not found',
);
const gdPath = path.join(fixtureDir, 'scripts/generate-docs.js');
const ghPath = path.join(fixtureDir, 'scripts/generate-docs-helpers.js');
check('Auto-doc: generate-docs.js exists', pathExists(gdPath), pathExists(gdPath) ? '' : 'Not found');
check('Auto-doc: helpers exists', pathExists(ghPath), pathExists(ghPath) ? '' : 'Not found');

// Check pre-commit hook references generate-docs (check both locations)
const gitHookPath = path.join(fixtureDir, '.git/hooks/pre-commit');
const huskyHookPath = path.join(fixtureDir, '.husky/pre-commit');
const preCommitPath = pathExists(gitHookPath) ? gitHookPath : pathExists(huskyHookPath) ? huskyHookPath : null;
if (preCommitPath) {
const hookContent = fs.readFileSync(preCommitPath, 'utf8');
const callsGenDocs = /generate-docs/i.test(hookContent);
check(
'Auto-doc: pre-commit hook runs generate-docs',
callsGenDocs,
callsGenDocs ? '' : 'Hook does not reference generate-docs',
);
} else {
check('Auto-doc: pre-commit hook runs generate-docs', false, 'No pre-commit hook found');
}
const gitHook = path.join(fixtureDir, '.git/hooks/pre-commit');
const huskyHook = path.join(fixtureDir, '.husky/pre-commit');
const hookFile = pathExists(gitHook) ? gitHook : pathExists(huskyHook) ? huskyHook : null;
const hookOk = hookFile && /generate-docs/i.test(fs.readFileSync(hookFile, 'utf8'));
check('Auto-doc: hook runs generate-docs', hookOk, hookOk ? '' : 'Not found or missing reference');

// Check CLAUDE.md has AUTO markers
const claudePath = path.join(fixtureDir, 'CLAUDE.md');
if (pathExists(claudePath)) {
const claudeContent = fs.readFileSync(claudePath, 'utf8');
const hasAutoTree = /<!--\s*AUTO:tree\s*-->/.test(claudeContent);
const hasAutoModules = /<!--\s*AUTO:modules\s*-->/.test(claudeContent);
check(
'Auto-doc: CLAUDE.md has AUTO:tree marker',
hasAutoTree,
hasAutoTree ? '' : 'Missing <!-- AUTO:tree --> marker',
);
check(
'Auto-doc: CLAUDE.md has AUTO:modules marker',
hasAutoModules,
hasAutoModules ? '' : 'Missing <!-- AUTO:modules --> marker',
);
const cPath = path.join(fixtureDir, 'CLAUDE.md');
if (pathExists(cPath)) {
const c = fs.readFileSync(cPath, 'utf8');
check('Auto-doc: AUTO:tree marker', /<!--\s*AUTO:tree\s*-->/.test(c));
check('Auto-doc: AUTO:modules marker', /<!--\s*AUTO:modules\s*-->/.test(c));
} else {
check('Auto-doc: CLAUDE.md has AUTO markers', false, 'CLAUDE.md not found');
}
Expand Down
Loading