mirror of
https://github.com/catlog22/Claude-Code-Workflow.git
synced 2026-02-05 01:50:27 +08:00
- Enhanced 14 commands with flexible parameter support - Standardized argument formats across all commands - Added English parameter descriptions for clarity - Maintained backward compatibility Commands optimized: - analyze-with-file: Added --depth, --max-iterations - brainstorm-with-file: Added --perspectives, --max-ideas, --focus - debug-with-file: Added --scope, --focus, --depth - issue-execute: Unified format, added --skip-tests, --skip-build, --dry-run - lite-plan-a/b/c: Added depth and execution control flags - execute: Added --parallel, --filter, --skip-tests - brainstorm-to-cycle: Unified to --session format, added --launch - lite-fix: Added --hotfix, --severity, --scope - clean: Added --focus, --target, --confirm - lite-execute: Unified --plan format, added execution control - compact: Added --description, --tags, --force - issue-new: Complete flexible parameter support Unchanged (already optimal): - issue-plan, issue-discover, issue-queue, issue-discover-by-prompt
12 KiB
12 KiB
description, argument-hint
| description | argument-hint |
|---|---|
| Intelligent code cleanup with mainline detection, stale artifact discovery, and safe execution. Supports targeted cleanup and confirmation. | [--dry-run] [--focus=<area>] [--target=sessions|documents|dead-code] [--confirm] |
Workflow Clean Command
Overview
Evidence-based intelligent cleanup command. Systematically identifies stale artifacts through mainline analysis, discovers drift, and safely removes unused sessions, documents, and dead code.
Core workflow: Detect Mainline → Discover Drift → Confirm → Stage → Execute
Target Cleanup
Focus area: $FOCUS (or entire project if not specified) Mode: $ARGUMENTS
--dry-run: Preview cleanup without executing--focus: Focus area (module or path)--target: Cleanup target (sessions|documents|dead-code)--confirm: Skip confirmation, execute directly
Execution Process
Phase 0: Initialization
├─ Parse arguments (--dry-run, FOCUS)
├─ Setup session folder
└─ Initialize utility functions
Phase 1: Mainline Detection
├─ Analyze git history (30 days)
├─ Identify core modules (high commit frequency)
├─ Map active vs stale branches
└─ Build mainline profile
Phase 2: Drift Discovery (Subagent)
├─ spawn_agent with cli-explore-agent role
├─ Scan workflow sessions for orphaned artifacts
├─ Identify documents drifted from mainline
├─ Detect dead code and unused exports
└─ Generate cleanup manifest
Phase 3: Confirmation
├─ Validate manifest schema
├─ Display cleanup summary by category
├─ AskUser: Select categories and risk level
└─ Dry-run exit if --dry-run
Phase 4: Execution
├─ Validate paths (security check)
├─ Stage deletion (move to .trash)
├─ Update manifests
├─ Permanent deletion
└─ Report results
Implementation
Phase 0: Initialization
const getUtc8ISOString = () => new Date(Date.now() + 8 * 60 * 60 * 1000).toISOString()
// Parse arguments
const args = "$ARGUMENTS"
const isDryRun = args.includes('--dry-run')
const focusMatch = args.match(/FOCUS="([^"]+)"/)
const focusArea = focusMatch ? focusMatch[1] : "$FOCUS" !== "$" + "FOCUS" ? "$FOCUS" : null
// Session setup
const dateStr = getUtc8ISOString().substring(0, 10)
const sessionId = `clean-${dateStr}`
const sessionFolder = `.workflow/.clean/${sessionId}`
const trashFolder = `${sessionFolder}/.trash`
const projectRoot = process.cwd()
bash(`mkdir -p ${sessionFolder}`)
bash(`mkdir -p ${trashFolder}`)
// Utility functions
function fileExists(p) {
try { return bash(`test -f "${p}" && echo "yes"`).includes('yes') } catch { return false }
}
function dirExists(p) {
try { return bash(`test -d "${p}" && echo "yes"`).includes('yes') } catch { return false }
}
function validatePath(targetPath) {
if (targetPath.includes('..')) return { valid: false, reason: 'Path traversal' }
const allowed = ['.workflow/', '.claude/rules/tech/', 'src/']
const dangerous = [/^\//, /^C:\\Windows/i, /node_modules/, /\.git$/]
if (!allowed.some(p => targetPath.startsWith(p))) {
return { valid: false, reason: 'Outside allowed directories' }
}
if (dangerous.some(p => p.test(targetPath))) {
return { valid: false, reason: 'Dangerous pattern' }
}
return { valid: true }
}
Phase 1: Mainline Detection
// Check git repository
const isGitRepo = bash('git rev-parse --git-dir 2>/dev/null && echo "yes"').includes('yes')
let mainlineProfile = {
coreModules: [],
activeFiles: [],
activeBranches: [],
staleThreshold: { sessions: 7, branches: 30, documents: 14 },
isGitRepo,
timestamp: getUtc8ISOString()
}
if (isGitRepo) {
// Commit frequency by directory (last 30 days)
const freq = bash('git log --since="30 days ago" --name-only --pretty=format: | grep -v "^$" | cut -d/ -f1-2 | sort | uniq -c | sort -rn | head -20')
// Parse core modules (>5 commits)
mainlineProfile.coreModules = freq.trim().split('\n')
.map(l => l.trim().match(/^(\d+)\s+(.+)$/))
.filter(m => m && parseInt(m[1]) >= 5)
.map(m => m[2])
// Recent branches
const branches = bash('git for-each-ref --sort=-committerdate refs/heads/ --format="%(refname:short)" | head -10')
mainlineProfile.activeBranches = branches.trim().split('\n').filter(Boolean)
}
Write(`${sessionFolder}/mainline-profile.json`, JSON.stringify(mainlineProfile, null, 2))
Phase 2: Drift Discovery
let exploreAgent = null
try {
// Launch cli-explore-agent
exploreAgent = spawn_agent({
message: `
## TASK ASSIGNMENT
### MANDATORY FIRST STEPS
1. Read: ~/.codex/agents/cli-explore-agent.md
2. Read: .workflow/project-tech.json (if exists)
## Task Objective
Discover stale artifacts for cleanup.
## Context
- Session: ${sessionFolder}
- Focus: ${focusArea || 'entire project'}
## Discovery Categories
### 1. Stale Workflow Sessions
Scan: .workflow/active/WFS-*, .workflow/archives/WFS-*, .workflow/.lite-plan/*, .workflow/.debug/DBG-*
Criteria: No modification >7 days + no related git commits
### 2. Drifted Documents
Scan: .claude/rules/tech/*, .workflow/.scratchpad/*
Criteria: >30% broken references to non-existent files
### 3. Dead Code
Scan: Unused exports, orphan files (not imported anywhere)
Criteria: No importers in import graph
## Output
Write to: ${sessionFolder}/cleanup-manifest.json
Format:
{
"generated_at": "ISO",
"discoveries": {
"stale_sessions": [{ "path": "...", "age_days": N, "reason": "...", "risk": "low|medium|high" }],
"drifted_documents": [{ "path": "...", "drift_percentage": N, "reason": "...", "risk": "..." }],
"dead_code": [{ "path": "...", "type": "orphan_file", "reason": "...", "risk": "..." }]
},
"summary": { "total_items": N, "by_category": {...}, "by_risk": {...} }
}
`
})
// Wait with timeout handling
let result = wait({ ids: [exploreAgent], timeout_ms: 600000 })
if (result.timed_out) {
send_input({ id: exploreAgent, message: 'Complete now and write cleanup-manifest.json.' })
result = wait({ ids: [exploreAgent], timeout_ms: 300000 })
if (result.timed_out) throw new Error('Agent timeout')
}
if (!fileExists(`${sessionFolder}/cleanup-manifest.json`)) {
throw new Error('Manifest not generated')
}
} finally {
if (exploreAgent) close_agent({ id: exploreAgent })
}
Phase 3: Confirmation
// Load and validate manifest
const manifest = JSON.parse(Read(`${sessionFolder}/cleanup-manifest.json`))
// Display summary
console.log(`
## Cleanup Discovery Report
| Category | Count | Risk |
|----------|-------|------|
| Sessions | ${manifest.summary.by_category.stale_sessions} | ${getRiskSummary('sessions')} |
| Documents | ${manifest.summary.by_category.drifted_documents} | ${getRiskSummary('documents')} |
| Dead Code | ${manifest.summary.by_category.dead_code} | ${getRiskSummary('code')} |
**Total**: ${manifest.summary.total_items} items
`)
// Dry-run exit
if (isDryRun) {
console.log(`
**Dry-run mode**: No changes made.
Manifest: ${sessionFolder}/cleanup-manifest.json
`)
return
}
// User confirmation
const selection = AskUser({
questions: [
{
question: "Which categories to clean?",
header: "Categories",
multiSelect: true,
options: [
{ label: "Sessions", description: `${manifest.summary.by_category.stale_sessions} stale sessions` },
{ label: "Documents", description: `${manifest.summary.by_category.drifted_documents} drifted docs` },
{ label: "Dead Code", description: `${manifest.summary.by_category.dead_code} unused files` }
]
},
{
question: "Risk level?",
header: "Risk",
options: [
{ label: "Low only", description: "Safest (Recommended)" },
{ label: "Low + Medium", description: "Includes likely unused" },
{ label: "All", description: "Aggressive" }
]
}
]
})
Phase 4: Execution
const riskFilter = {
'Low only': ['low'],
'Low + Medium': ['low', 'medium'],
'All': ['low', 'medium', 'high']
}[selection.risk]
// Collect items to clean
const items = []
if (selection.categories.includes('Sessions')) {
items.push(...manifest.discoveries.stale_sessions.filter(s => riskFilter.includes(s.risk)))
}
if (selection.categories.includes('Documents')) {
items.push(...manifest.discoveries.drifted_documents.filter(d => riskFilter.includes(d.risk)))
}
if (selection.categories.includes('Dead Code')) {
items.push(...manifest.discoveries.dead_code.filter(c => riskFilter.includes(c.risk)))
}
const results = { staged: [], deleted: [], failed: [], skipped: [] }
// Validate and stage
for (const item of items) {
const validation = validatePath(item.path)
if (!validation.valid) {
results.skipped.push({ path: item.path, reason: validation.reason })
continue
}
if (!fileExists(item.path) && !dirExists(item.path)) {
results.skipped.push({ path: item.path, reason: 'Not found' })
continue
}
try {
const trashTarget = `${trashFolder}/${item.path.replace(/\//g, '_')}`
bash(`mv "${item.path}" "${trashTarget}"`)
results.staged.push({ path: item.path, trashPath: trashTarget })
} catch (e) {
results.failed.push({ path: item.path, error: e.message })
}
}
// Permanent deletion
for (const staged of results.staged) {
try {
bash(`rm -rf "${staged.trashPath}"`)
results.deleted.push(staged.path)
} catch (e) {
console.error(`Failed: ${staged.path}`)
}
}
// Cleanup empty trash
bash(`rmdir "${trashFolder}" 2>/dev/null || true`)
// Report
console.log(`
## Cleanup Complete
**Deleted**: ${results.deleted.length}
**Failed**: ${results.failed.length}
**Skipped**: ${results.skipped.length}
### Deleted
${results.deleted.map(p => `- ${p}`).join('\n') || '(none)'}
${results.failed.length > 0 ? `### Failed\n${results.failed.map(f => `- ${f.path}: ${f.error}`).join('\n')}` : ''}
Report: ${sessionFolder}/cleanup-report.json
`)
Write(`${sessionFolder}/cleanup-report.json`, JSON.stringify({
timestamp: getUtc8ISOString(),
results,
summary: {
deleted: results.deleted.length,
failed: results.failed.length,
skipped: results.skipped.length
}
}, null, 2))
Session Folder
.workflow/.clean/clean-{YYYY-MM-DD}/
├── mainline-profile.json # Git history analysis
├── cleanup-manifest.json # Discovery results
├── cleanup-report.json # Execution results
└── .trash/ # Staging area (temporary)
Risk Levels
| Risk | Description | Examples |
|---|---|---|
| Low | Safe to delete | Empty sessions, scratchpad files |
| Medium | Likely unused | Orphan files, old archives |
| High | May have dependencies | Files with some imports |
Security Features
| Feature | Protection |
|---|---|
| Path Validation | Whitelist directories, reject traversal |
| Staged Deletion | Move to .trash before permanent delete |
| Dangerous Patterns | Block system dirs, node_modules, .git |
Iteration Flow
First Call (/prompts:clean):
├─ Detect mainline from git history
├─ Discover stale artifacts via subagent
├─ Display summary, await user selection
└─ Execute cleanup with staging
Dry-Run (/prompts:clean --dry-run):
├─ All phases except execution
└─ Manifest saved for review
Focused (/prompts:clean FOCUS="auth"):
└─ Discovery limited to specified area
Error Handling
| Situation | Action |
|---|---|
| No git repo | Use file timestamps only |
| Agent timeout | Retry once with prompt, then abort |
| Path validation fail | Skip item, report reason |
| Manifest parse error | Abort with error |
| Empty discovery | Report "codebase is clean" |
Now execute cleanup workflow with focus: $FOCUS