@kennethsolomon/shipkit 1.0.0

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.
Files changed (117) hide show
  1. package/README.md +321 -0
  2. package/bin/shipkit.js +146 -0
  3. package/commands/sk/brainstorm.md +63 -0
  4. package/commands/sk/branch.md +35 -0
  5. package/commands/sk/config.md +96 -0
  6. package/commands/sk/execute-plan.md +85 -0
  7. package/commands/sk/features.md +238 -0
  8. package/commands/sk/finish-feature.md +154 -0
  9. package/commands/sk/help.md +103 -0
  10. package/commands/sk/hotfix.md +61 -0
  11. package/commands/sk/plan.md +30 -0
  12. package/commands/sk/release.md +72 -0
  13. package/commands/sk/security-check.md +188 -0
  14. package/commands/sk/set-profile.md +71 -0
  15. package/commands/sk/status.md +25 -0
  16. package/commands/sk/update-task.md +35 -0
  17. package/commands/sk/write-plan.md +72 -0
  18. package/package.json +23 -0
  19. package/skills/sk:accessibility/LICENSE.txt +177 -0
  20. package/skills/sk:accessibility/SKILL.md +150 -0
  21. package/skills/sk:api-design/LICENSE.txt +177 -0
  22. package/skills/sk:api-design/SKILL.md +158 -0
  23. package/skills/sk:brainstorming/SKILL.md +124 -0
  24. package/skills/sk:debug/SKILL.md +252 -0
  25. package/skills/sk:debug/debug_conductor.py +177 -0
  26. package/skills/sk:debug/lib/__init__.py +1 -0
  27. package/skills/sk:debug/lib/bug_gatherer.py +55 -0
  28. package/skills/sk:debug/lib/context_reader.py +139 -0
  29. package/skills/sk:debug/lib/findings_writer.py +76 -0
  30. package/skills/sk:debug/lib/lessons_writer.py +165 -0
  31. package/skills/sk:debug/lib/step_runner.py +326 -0
  32. package/skills/sk:features/SKILL.md +238 -0
  33. package/skills/sk:frontend-design/LICENSE.txt +177 -0
  34. package/skills/sk:frontend-design/SKILL.md +191 -0
  35. package/skills/sk:laravel-init/SKILL.md +37 -0
  36. package/skills/sk:laravel-new/SKILL.md +68 -0
  37. package/skills/sk:lint/SKILL.md +113 -0
  38. package/skills/sk:perf/LICENSE.txt +177 -0
  39. package/skills/sk:perf/SKILL.md +188 -0
  40. package/skills/sk:release/SKILL.md +113 -0
  41. package/skills/sk:release/references/android-checklist.md +269 -0
  42. package/skills/sk:release/references/ios-checklist.md +339 -0
  43. package/skills/sk:release/release.sh +378 -0
  44. package/skills/sk:review/SKILL.md +346 -0
  45. package/skills/sk:review/references/security-checklist.md +223 -0
  46. package/skills/sk:schema-migrate/SKILL.md +125 -0
  47. package/skills/sk:schema-migrate/orms/drizzle.md +546 -0
  48. package/skills/sk:schema-migrate/orms/laravel.md +367 -0
  49. package/skills/sk:schema-migrate/orms/prisma.md +357 -0
  50. package/skills/sk:schema-migrate/orms/rails.md +351 -0
  51. package/skills/sk:schema-migrate/orms/sqlalchemy.md +385 -0
  52. package/skills/sk:schema-migrate/references/detection.md +110 -0
  53. package/skills/sk:setup-claude/SKILL.md +365 -0
  54. package/skills/sk:setup-claude/references/detection.md +6 -0
  55. package/skills/sk:setup-claude/references/templates.md +11 -0
  56. package/skills/sk:setup-claude/scripts/apply_setup_claude.py +443 -0
  57. package/skills/sk:setup-claude/scripts/detect_arch_changes.py +437 -0
  58. package/skills/sk:setup-claude/templates/.claude/docs/arch-changelog-guide.md.template +6 -0
  59. package/skills/sk:setup-claude/templates/.claude/docs/changelog-guide.md.template +12 -0
  60. package/skills/sk:setup-claude/templates/CHANGELOG.md.template +21 -0
  61. package/skills/sk:setup-claude/templates/CLAUDE.md.template +299 -0
  62. package/skills/sk:setup-claude/templates/arch-changelog-guide.md.template +3 -0
  63. package/skills/sk:setup-claude/templates/changelog-guide.md.template +3 -0
  64. package/skills/sk:setup-claude/templates/commands/brainstorm.md.template +74 -0
  65. package/skills/sk:setup-claude/templates/commands/execute-plan.md.template +57 -0
  66. package/skills/sk:setup-claude/templates/commands/features.md.template +238 -0
  67. package/skills/sk:setup-claude/templates/commands/finish-feature.md.template +155 -0
  68. package/skills/sk:setup-claude/templates/commands/plan.md.template +30 -0
  69. package/skills/sk:setup-claude/templates/commands/re-setup.md.template +38 -0
  70. package/skills/sk:setup-claude/templates/commands/release.md.template +74 -0
  71. package/skills/sk:setup-claude/templates/commands/security-check.md.template +172 -0
  72. package/skills/sk:setup-claude/templates/commands/status.md.template +17 -0
  73. package/skills/sk:setup-claude/templates/commands/write-plan.md.template +34 -0
  74. package/skills/sk:setup-claude/templates/finish-feature.md.template +3 -0
  75. package/skills/sk:setup-claude/templates/plan.md.template +3 -0
  76. package/skills/sk:setup-claude/templates/status.md.template +3 -0
  77. package/skills/sk:setup-claude/templates/tasks/findings.md.template +19 -0
  78. package/skills/sk:setup-claude/templates/tasks/lessons.md.template +26 -0
  79. package/skills/sk:setup-claude/templates/tasks/progress.md.template +20 -0
  80. package/skills/sk:setup-claude/templates/tasks/security-findings.md.template +5 -0
  81. package/skills/sk:setup-claude/templates/tasks/todo.md.template +26 -0
  82. package/skills/sk:setup-claude/templates/tasks/workflow-status.md.template +31 -0
  83. package/skills/sk:setup-claude/templates/tasks-findings.md.template +3 -0
  84. package/skills/sk:setup-claude/templates/tasks-lessons.md.template +3 -0
  85. package/skills/sk:setup-claude/templates/tasks-progress.md.template +3 -0
  86. package/skills/sk:setup-claude/templates/tasks-todo.md.template +3 -0
  87. package/skills/sk:setup-claude/tests/test_apply_setup_claude.py +193 -0
  88. package/skills/sk:setup-optimizer/SKILL.md +184 -0
  89. package/skills/sk:setup-optimizer/lib/__init__.py +24 -0
  90. package/skills/sk:setup-optimizer/lib/detect.py +205 -0
  91. package/skills/sk:setup-optimizer/lib/discover.py +221 -0
  92. package/skills/sk:setup-optimizer/lib/enrich.py +163 -0
  93. package/skills/sk:setup-optimizer/lib/merge.py +277 -0
  94. package/skills/sk:setup-optimizer/lib/sidecar.py +129 -0
  95. package/skills/sk:setup-optimizer/optimize_claude.py +174 -0
  96. package/skills/sk:setup-optimizer/templates/CLAUDE.md.template +105 -0
  97. package/skills/sk:skill-creator/LICENSE.txt +202 -0
  98. package/skills/sk:skill-creator/SKILL.md +479 -0
  99. package/skills/sk:skill-creator/agents/analyzer.md +274 -0
  100. package/skills/sk:skill-creator/agents/comparator.md +202 -0
  101. package/skills/sk:skill-creator/agents/grader.md +223 -0
  102. package/skills/sk:skill-creator/assets/eval_review.html +146 -0
  103. package/skills/sk:skill-creator/eval-viewer/generate_review.py +471 -0
  104. package/skills/sk:skill-creator/eval-viewer/viewer.html +1325 -0
  105. package/skills/sk:skill-creator/references/schemas.md +430 -0
  106. package/skills/sk:skill-creator/scripts/aggregate_benchmark.py +401 -0
  107. package/skills/sk:skill-creator/scripts/generate_report.py +326 -0
  108. package/skills/sk:skill-creator/scripts/improve_description.py +248 -0
  109. package/skills/sk:skill-creator/scripts/package_skill.py +136 -0
  110. package/skills/sk:skill-creator/scripts/quick_validate.py +103 -0
  111. package/skills/sk:skill-creator/scripts/run_eval.py +310 -0
  112. package/skills/sk:skill-creator/scripts/run_loop.py +332 -0
  113. package/skills/sk:skill-creator/scripts/utils.py +47 -0
  114. package/skills/sk:smart-commit/SKILL.md +175 -0
  115. package/skills/sk:test/SKILL.md +171 -0
  116. package/skills/sk:write-tests/SKILL.md +195 -0
  117. package/skills/sk:write-tests/references/patterns.md +209 -0
@@ -0,0 +1,3 @@
1
+ <!-- Deprecated template path. -->
2
+ <!-- Source of truth: templates/tasks/lessons.md.template -->
3
+
@@ -0,0 +1,3 @@
1
+ <!-- Deprecated template path. -->
2
+ <!-- Source of truth: templates/tasks/progress.md.template -->
3
+
@@ -0,0 +1,3 @@
1
+ <!-- Deprecated template path. -->
2
+ <!-- Source of truth: templates/tasks/todo.md.template -->
3
+
@@ -0,0 +1,193 @@
1
+ import contextlib
2
+ import importlib.util
3
+ import io
4
+ import json
5
+ import sys
6
+ import tempfile
7
+ import unittest
8
+ from pathlib import Path
9
+
10
+
11
+ def _load_apply_module():
12
+ skill_root = Path(__file__).resolve().parents[1]
13
+ script_path = skill_root / "scripts" / "apply_setup_claude.py"
14
+ spec = importlib.util.spec_from_file_location("apply_setup_claude", script_path)
15
+ assert spec and spec.loader
16
+ module = importlib.util.module_from_spec(spec)
17
+ sys.modules[spec.name] = module
18
+ spec.loader.exec_module(module)
19
+ return module
20
+
21
+
22
+ class TestApplySetupClaude(unittest.TestCase):
23
+ def test_print_detection_exits_without_writes(self):
24
+ mod = _load_apply_module()
25
+ skill_root = Path(__file__).resolve().parents[1]
26
+
27
+ with tempfile.TemporaryDirectory() as td:
28
+ repo_root = Path(td)
29
+ (repo_root / "package.json").write_text(
30
+ json.dumps(
31
+ {
32
+ "name": "demo",
33
+ "description": "Demo repo",
34
+ "scripts": {"dev": "next dev", "test": "vitest"},
35
+ "dependencies": {"next": "1.0.0", "react": "1.0.0"},
36
+ "devDependencies": {"vitest": "1.0.0"},
37
+ }
38
+ ),
39
+ encoding="utf-8",
40
+ )
41
+
42
+ buf = io.StringIO()
43
+ with contextlib.redirect_stdout(buf):
44
+ rc = mod.main(["apply_setup_claude.py", str(repo_root), "--print-detection"])
45
+ self.assertEqual(rc, 0)
46
+ data = json.loads(buf.getvalue().strip())
47
+ self.assertEqual(data["project_name"], "demo")
48
+ self.assertEqual(data["framework"], "Next.js (App Router)")
49
+
50
+ self.assertFalse((repo_root / "tasks").exists())
51
+ self.assertFalse((repo_root / ".claude").exists())
52
+
53
+ def test_dry_run_does_not_write_files_or_dirs(self):
54
+ mod = _load_apply_module()
55
+ skill_root = Path(__file__).resolve().parents[1]
56
+
57
+ with tempfile.TemporaryDirectory() as td:
58
+ repo_root = Path(td)
59
+ (repo_root / "package.json").write_text(json.dumps({"name": "demo"}), encoding="utf-8")
60
+
61
+ buf = io.StringIO()
62
+ with contextlib.redirect_stdout(buf):
63
+ rc = mod.apply(
64
+ repo_root,
65
+ skill_root,
66
+ update_generated=False,
67
+ dry_run=True,
68
+ detection=mod.detect(repo_root),
69
+ )
70
+ self.assertEqual(rc, 0)
71
+
72
+ self.assertFalse((repo_root / "tasks").exists())
73
+ self.assertFalse((repo_root / ".claude").exists())
74
+
75
+ def test_marker_guarded_updates(self):
76
+ mod = _load_apply_module()
77
+ skill_root = Path(__file__).resolve().parents[1]
78
+
79
+ with tempfile.TemporaryDirectory() as td:
80
+ repo_root = Path(td)
81
+ (repo_root / "package.json").write_text(
82
+ json.dumps({"name": "demo", "scripts": {"dev": "node dev"}}),
83
+ encoding="utf-8",
84
+ )
85
+ detection = mod.detect(repo_root)
86
+
87
+ # Create an agent-modified file (no marker): should NOT be overwritten.
88
+ no_marker_path = repo_root / ".claude" / "commands" / "status.md"
89
+ no_marker_path.parent.mkdir(parents=True, exist_ok=True)
90
+ no_marker_path.write_text("# custom status\n", encoding="utf-8")
91
+
92
+ # Create a generated file with marker but wrong content: should be updated.
93
+ marker_path = repo_root / ".claude" / "commands" / "plan.md"
94
+ marker_path.write_text(f"{mod.GENERATED_MARKER}\n# old\n", encoding="utf-8")
95
+
96
+ buf = io.StringIO()
97
+ with contextlib.redirect_stdout(buf):
98
+ rc = mod.apply(
99
+ repo_root,
100
+ skill_root,
101
+ update_generated=True,
102
+ dry_run=False,
103
+ detection=detection,
104
+ )
105
+ self.assertEqual(rc, 0)
106
+
107
+ self.assertEqual(no_marker_path.read_text(encoding="utf-8"), "# custom status\n")
108
+
109
+ tpl = (skill_root / "templates" / "commands" / "plan.md.template").read_text(encoding="utf-8")
110
+ expected = mod.render_template(tpl, detection)
111
+ self.assertEqual(marker_path.read_text(encoding="utf-8"), expected)
112
+
113
+ def test_workflow_status_created_on_fresh_setup(self):
114
+ mod = _load_apply_module()
115
+ skill_root = Path(__file__).resolve().parents[1]
116
+
117
+ with tempfile.TemporaryDirectory() as td:
118
+ repo_root = Path(td)
119
+ (repo_root / "package.json").write_text(json.dumps({"name": "demo"}), encoding="utf-8")
120
+
121
+ buf = io.StringIO()
122
+ with contextlib.redirect_stdout(buf):
123
+ rc = mod.apply(
124
+ repo_root,
125
+ skill_root,
126
+ update_generated=False,
127
+ dry_run=False,
128
+ detection=mod.detect(repo_root),
129
+ )
130
+ self.assertEqual(rc, 0)
131
+
132
+ wf_path = repo_root / "tasks" / "workflow-status.md"
133
+ self.assertTrue(wf_path.exists())
134
+ content = wf_path.read_text(encoding="utf-8")
135
+ self.assertIn(">> next <<", content)
136
+ self.assertIn("/brainstorm", content)
137
+ self.assertIn("/finish-feature", content)
138
+ self.assertIn("/release", content)
139
+ # All 14 steps present
140
+ self.assertIn("| 14 |", content)
141
+
142
+ def test_workflow_status_not_overwritten_on_rerun(self):
143
+ mod = _load_apply_module()
144
+ skill_root = Path(__file__).resolve().parents[1]
145
+
146
+ with tempfile.TemporaryDirectory() as td:
147
+ repo_root = Path(td)
148
+ (repo_root / "package.json").write_text(json.dumps({"name": "demo"}), encoding="utf-8")
149
+
150
+ # First run creates the file
151
+ buf = io.StringIO()
152
+ with contextlib.redirect_stdout(buf):
153
+ mod.apply(repo_root, skill_root, update_generated=False, dry_run=False, detection=mod.detect(repo_root))
154
+
155
+ # Simulate user progress by modifying the file
156
+ wf_path = repo_root / "tasks" / "workflow-status.md"
157
+ custom_content = wf_path.read_text(encoding="utf-8").replace(">> next <<", "done")
158
+ wf_path.write_text(custom_content, encoding="utf-8")
159
+
160
+ # Second run should NOT overwrite
161
+ buf = io.StringIO()
162
+ with contextlib.redirect_stdout(buf):
163
+ mod.apply(repo_root, skill_root, update_generated=False, dry_run=False, detection=mod.detect(repo_root))
164
+
165
+ self.assertEqual(wf_path.read_text(encoding="utf-8"), custom_content)
166
+
167
+ def test_existing_custom_claude_md_writes_sidecar(self):
168
+ mod = _load_apply_module()
169
+ skill_root = Path(__file__).resolve().parents[1]
170
+
171
+ with tempfile.TemporaryDirectory() as td:
172
+ repo_root = Path(td)
173
+ (repo_root / "package.json").write_text(json.dumps({"name": "demo"}), encoding="utf-8")
174
+ (repo_root / "CLAUDE.md").write_text("# Custom\n", encoding="utf-8")
175
+
176
+ buf = io.StringIO()
177
+ with contextlib.redirect_stdout(buf):
178
+ rc = mod.apply(
179
+ repo_root,
180
+ skill_root,
181
+ update_generated=False,
182
+ dry_run=False,
183
+ detection=mod.detect(repo_root),
184
+ )
185
+ self.assertEqual(rc, 0)
186
+
187
+ self.assertEqual((repo_root / "CLAUDE.md").read_text(encoding="utf-8"), "# Custom\n")
188
+ self.assertTrue((repo_root / "CLAUDE.setup-claude.md").exists())
189
+ self.assertIn("Notes:", buf.getvalue())
190
+
191
+
192
+ if __name__ == "__main__":
193
+ unittest.main()
@@ -0,0 +1,184 @@
1
+ ---
2
+ name: sk:setup-optimizer
3
+ description: "Diagnose, update workflow, enrich, and maintain CLAUDE.md. The single command to keep any CLAUDE.md current."
4
+ triggers:
5
+ - optimize claude
6
+ - optimize setup
7
+ - enrich claude
8
+ - maintain claude
9
+ - doctor claude
10
+ - check claude
11
+ - diagnose claude
12
+ - refresh claude
13
+ - update claude
14
+ - re-setup
15
+ allowed-tools:
16
+ - Bash
17
+ - Read
18
+ - Write
19
+ ---
20
+
21
+ ## Overview
22
+
23
+ The single command to keep your CLAUDE.md current. Diagnoses problems, updates the workflow to the latest version, scans your codebase, and enriches with project context — all while preserving your customizations.
24
+
25
+ ### What It Does
26
+
27
+ 1. **Diagnoses** — finds missing sections, stale info, inconsistencies, and gaps
28
+ 2. **Updates workflow** — refreshes the workflow section to the latest template version
29
+ 3. **Discovers** — scans project structure, docs, and workflows
30
+ 4. **Enriches** — merges discoveries into CLAUDE.md while preserving your edits
31
+
32
+ ## Usage
33
+
34
+ ```bash
35
+ /sk:setup-optimizer
36
+ ```
37
+
38
+ ### Step 0: Diagnose
39
+
40
+ Before making any changes, runs a diagnostic pass on the existing CLAUDE.md:
41
+
42
+ - **Missing sections** — checks for essential sections (Workflow, Sub-Agent Patterns, Project Memory, Lessons Capture, Testing, Commands, etc.)
43
+ - **Stale content** — detects outdated info (stale model/route counts, removed dependencies, old command names like `/laravel-lint` instead of `/sk:lint`)
44
+ - **Inconsistencies** — compares documented vs actual project state (directories, scripts, workflows)
45
+ - **Section completeness** — flags sections that exist but are empty or have only placeholder text
46
+ - **Outdated workflow** — checks if the workflow matches the current 24-step TDD flow with hard gates
47
+
48
+ Reports findings before proceeding. If issues are found, they inform subsequent steps.
49
+
50
+ ### Step 1: Update Workflow
51
+
52
+ If the workflow section is outdated or missing, replace it with the latest version:
53
+
54
+ **Current workflow (24 steps, TDD with hard gates):**
55
+ ```
56
+ Read → Explore → Design → Accessibility → Plan → Branch → Migrate → Write Tests → Implement → Lint → Verify Tests → Security → Performance → Review → Finish
57
+ ```
58
+
59
+ **What gets updated:**
60
+ - Workflow table (24 steps with correct commands: `/sk:write-tests`, `/sk:lint`, `/sk:test`, `/sk:accessibility`, `/sk:perf`)
61
+ - Step details (TDD red/green/verify descriptions)
62
+ - Tracker rules (hard gates at 12, 14, 16, 20; optional steps 4, 5, 7, 18, 24)
63
+ - Step completion summary rule (NON-NEGOTIABLE)
64
+ - Bug fix flow section
65
+ - Sub-Agent Patterns section (if missing)
66
+ - Project Memory section (if missing)
67
+ - Lessons Capture section (if missing)
68
+ - Testing TDD section (if missing)
69
+ - 3-Strike Protocol (if missing)
70
+
71
+ **What gets preserved:**
72
+ - Everything marked with `<!-- LOCK -->` is never touched
73
+ - Project-specific content below the workflow (conventions, models, routes, architecture)
74
+ - Stack section, Build & Run section
75
+ - Any section with `<!-- EDITED -->` marker
76
+
77
+ **How it works:**
78
+ 1. Read the latest workflow template from `~/.claude/skills/sk:setup-claude/templates/CLAUDE.md.template`
79
+ 2. Compare with the current CLAUDE.md workflow section
80
+ 3. If different, replace the workflow section (between `## Workflow` and the next `##` that isn't a workflow subsection)
81
+ 4. Insert missing sections (Sub-Agent Patterns, Project Memory, etc.) in their correct positions
82
+ 5. Preserve all `<!-- LOCK -->` and project-specific sections
83
+
84
+ ### Step 2: Scan & Enrich
85
+
86
+ After workflow update, proceeds with codebase discovery and enrichment:
87
+
88
+ 1. Scans project for directories, docs, and workflows
89
+ 2. Reads your existing CLAUDE.md
90
+ 3. Intelligently merges discoveries with your content (prioritizing diagnosed gaps)
91
+ 4. Preserves any user customizations
92
+ 5. Updates CLAUDE.md with comprehensive context
93
+
94
+ ## What Gets Discovered
95
+
96
+ ### Directories
97
+ Auto-documents: src/, tests/, docs/, public/, scripts/, config/, migrations/, and more (intelligently excludes node_modules/, vendor/, etc.)
98
+
99
+ ### Documentation
100
+ Finds and links: README.md, CONTRIBUTING.md, CHANGELOG.md, docs/*.md, .github/CONTRIBUTING.md, and more
101
+
102
+ ### Workflows
103
+ Detects: Makefile targets, npm/yarn scripts, GitHub Actions workflows
104
+
105
+ ## Smart Features
106
+
107
+ ### 1. User Customization Preservation
108
+
109
+ **Dual Detection:**
110
+ - Compares content to detect user edits
111
+ - Looks for `<!-- EDITED -->` markers
112
+ - Automatically preserves customized sections
113
+
114
+ **Auto-Locking:**
115
+ - `Important Context` section - auto-locked if has content
116
+ - `Known Issues` section - auto-locked
117
+ - Any section with `<!-- LOCK -->` comment - permanently locked
118
+
119
+ **Result:** Run multiple times during development without losing work!
120
+
121
+ ### 2. Intelligent Merging
122
+
123
+ When updating CLAUDE.md:
124
+ - ✅ Keeps user customizations intact
125
+ - ✅ Updates auto-generated discovery sections
126
+ - ✅ Adds newly discovered items
127
+ - ✅ Reports what was preserved
128
+
129
+ ### 3. Flexible Line Count
130
+
131
+ - **Target:** Stay under 200 lines when possible
132
+ - **Philosophy:** Comprehensive context > artificial line limit
133
+ - **Strategy:** Link to docs instead of inlining if extensive
134
+
135
+ ## Maintenance Workflow
136
+
137
+ ### During Development
138
+
139
+ ```bash
140
+ # After adding new test directory
141
+ mkdir tests
142
+
143
+ # Run optimizer to discover it
144
+ /optimize-claude
145
+
146
+ # CLAUDE.md now documents tests/ automatically!
147
+ ```
148
+
149
+ ### When Customizing
150
+
151
+ ```bash
152
+ # Edit Important Context with custom notes
153
+ vim CLAUDE.md
154
+
155
+ # The edit is automatically preserved on next run
156
+ /optimize-claude
157
+
158
+ # ✅ Your notes stay intact!
159
+ ```
160
+
161
+ ## Configuration
162
+
163
+ ### Smart Defaults (Auto-Locked)
164
+ - **Important Context** - Your project decisions
165
+ - **Known Issues** - Problems/limitations
166
+ - Any section with `<!-- LOCK -->` comment
167
+
168
+ ### Explicit Locking
169
+
170
+ ```markdown
171
+ ## My Custom Section
172
+ <!-- LOCK -->
173
+ This content will never be regenerated.
174
+ ```
175
+
176
+ ## When to Use
177
+
178
+ ✅ **Use `/optimize-claude` when:**
179
+ - You've added new directories to your project
180
+ - You've created documentation files
181
+ - You want to refresh project context
182
+ - Monthly maintenance of CLAUDE.md
183
+
184
+ ✅ **Safe to run multiple times during development** - Your customizations are always preserved!
@@ -0,0 +1,24 @@
1
+ """Claude setup tools library."""
2
+
3
+ from .detect import ProjectConfig, detect_project
4
+ from .sidecar import (
5
+ has_marker,
6
+ is_custom_file,
7
+ get_target_file,
8
+ add_marker,
9
+ format_output,
10
+ count_lines,
11
+ extract_sections,
12
+ )
13
+
14
+ __all__ = [
15
+ "ProjectConfig",
16
+ "detect_project",
17
+ "has_marker",
18
+ "is_custom_file",
19
+ "get_target_file",
20
+ "add_marker",
21
+ "format_output",
22
+ "count_lines",
23
+ "extract_sections",
24
+ ]
@@ -0,0 +1,205 @@
1
+ """Auto-detection logic for project configuration."""
2
+
3
+ import json
4
+ from dataclasses import dataclass, field
5
+ from pathlib import Path
6
+ from typing import Optional
7
+
8
+
9
+ @dataclass
10
+ class ProjectConfig:
11
+ """Detected project configuration."""
12
+
13
+ language: str = "Unknown"
14
+ framework: str = "None"
15
+ database: str = "None"
16
+ ui: str = "None"
17
+ testing: str = "None"
18
+ description: str = ""
19
+ dev_command: str = ""
20
+ build_command: str = ""
21
+ test_command: str = ""
22
+ lint_command: str = ""
23
+ project_name: str = ""
24
+ project_dir: str = ""
25
+
26
+
27
+ def detect_project(root_path: Path) -> ProjectConfig:
28
+ """Auto-detect project configuration from files.
29
+
30
+ Args:
31
+ root_path: Root directory of the project
32
+
33
+ Returns:
34
+ ProjectConfig with detected values
35
+ """
36
+ config = ProjectConfig()
37
+ config.project_dir = root_path.name
38
+ config.project_name = root_path.name
39
+
40
+ # Check package.json (Node.js)
41
+ package_json = root_path / "package.json"
42
+ if package_json.exists():
43
+ return _detect_nodejs(root_path, config)
44
+
45
+ # Check pyproject.toml or setup.py (Python)
46
+ if (root_path / "pyproject.toml").exists() or (root_path / "setup.py").exists():
47
+ return _detect_python(root_path, config)
48
+
49
+ # Check go.mod (Go)
50
+ if (root_path / "go.mod").exists():
51
+ return _detect_go(root_path, config)
52
+
53
+ # Check Cargo.toml (Rust)
54
+ if (root_path / "Cargo.toml").exists():
55
+ return _detect_rust(root_path, config)
56
+
57
+ # Default: Generic project
58
+ config.language = "JavaScript/TypeScript"
59
+ config.dev_command = "npm run dev"
60
+ config.build_command = "npm run build"
61
+ config.test_command = "npm test"
62
+ config.lint_command = "npm run lint"
63
+
64
+ return config
65
+
66
+
67
+ def _detect_nodejs(root_path: Path, config: ProjectConfig) -> ProjectConfig:
68
+ """Detect Node.js/JavaScript project details."""
69
+ config.language = "JavaScript/TypeScript"
70
+
71
+ try:
72
+ with open(root_path / "package.json") as f:
73
+ pkg = json.load(f)
74
+
75
+ # Extract project info
76
+ config.project_name = pkg.get("name", config.project_name)
77
+ config.description = pkg.get("description", "")
78
+
79
+ # Detect framework
80
+ deps = {**pkg.get("dependencies", {}), **pkg.get("devDependencies", {})}
81
+
82
+ if "react" in deps or "next" in deps:
83
+ config.framework = "React" if "react" in deps else "Next.js"
84
+ config.ui = "React"
85
+ elif "vue" in deps or "nuxt" in deps:
86
+ config.framework = "Vue" if "vue" in deps else "Nuxt"
87
+ config.ui = "Vue"
88
+ elif "angular" in deps:
89
+ config.framework = "Angular"
90
+ config.ui = "Angular"
91
+ elif "svelte" in deps:
92
+ config.framework = "Svelte"
93
+ config.ui = "Svelte"
94
+
95
+ # Detect UI framework
96
+ if "tailwindcss" in deps:
97
+ config.ui = "Tailwind CSS"
98
+ elif "styled-components" in deps:
99
+ config.ui = "styled-components"
100
+ elif "sass" in deps or "node-sass" in deps:
101
+ config.ui = "Sass"
102
+
103
+ # Detect database
104
+ if "prisma" in deps:
105
+ config.database = "Prisma"
106
+ elif "drizzle-orm" in deps:
107
+ config.database = "Drizzle"
108
+ elif "mongoose" in deps:
109
+ config.database = "MongoDB (Mongoose)"
110
+ elif "sequelize" in deps:
111
+ config.database = "PostgreSQL (Sequelize)"
112
+ elif "typeorm" in deps:
113
+ config.database = "TypeORM"
114
+
115
+ # Detect testing
116
+ if "vitest" in deps:
117
+ config.testing = "Vitest"
118
+ elif "jest" in deps:
119
+ config.testing = "Jest"
120
+ elif "mocha" in deps:
121
+ config.testing = "Mocha"
122
+
123
+ # Extract commands from package.json scripts
124
+ scripts = pkg.get("scripts", {})
125
+ config.dev_command = f"npm run {_find_script(scripts, ['dev', 'start'])}" if _find_script(scripts, ['dev', 'start']) else "npm run dev"
126
+ config.build_command = f"npm run {_find_script(scripts, ['build'])}" if _find_script(scripts, ['build']) else "npm run build"
127
+ config.test_command = f"npm test"
128
+ config.lint_command = f"npm run {_find_script(scripts, ['lint'])}" if _find_script(scripts, ['lint']) else "npm run lint"
129
+
130
+ except Exception as e:
131
+ print(f"Warning: Could not fully parse package.json: {e}")
132
+
133
+ return config
134
+
135
+
136
+ def _detect_python(root_path: Path, config: ProjectConfig) -> ProjectConfig:
137
+ """Detect Python project details."""
138
+ config.language = "Python"
139
+
140
+ # Try pyproject.toml first
141
+ pyproject = root_path / "pyproject.toml"
142
+ if pyproject.exists():
143
+ try:
144
+ content = pyproject.read_text()
145
+
146
+ # Simple detection without external dependency
147
+ if "django" in content:
148
+ config.framework = "Django"
149
+ elif "fastapi" in content:
150
+ config.framework = "FastAPI"
151
+ elif "flask" in content:
152
+ config.framework = "Flask"
153
+ elif "starlette" in content:
154
+ config.framework = "Starlette"
155
+
156
+ if "sqlalchemy" in content or "alembic" in content:
157
+ config.database = "SQLAlchemy"
158
+ elif "django" in content:
159
+ config.database = "Django ORM"
160
+
161
+ if "pytest" in content:
162
+ config.testing = "pytest"
163
+ except Exception as e:
164
+ print(f"Warning: Could not parse pyproject.toml: {e}")
165
+
166
+ # Default Python commands
167
+ config.dev_command = "python -m uvicorn main:app --reload"
168
+ config.build_command = "pip install -e ."
169
+ config.test_command = "pytest"
170
+ config.lint_command = "pylint ."
171
+ config.description = "Python project"
172
+
173
+ return config
174
+
175
+
176
+ def _detect_go(root_path: Path, config: ProjectConfig) -> ProjectConfig:
177
+ """Detect Go project details."""
178
+ config.language = "Go"
179
+ config.dev_command = "go run ."
180
+ config.build_command = "go build -o bin/app ."
181
+ config.test_command = "go test ./..."
182
+ config.lint_command = "golangci-lint run"
183
+ config.description = "Go project"
184
+
185
+ return config
186
+
187
+
188
+ def _detect_rust(root_path: Path, config: ProjectConfig) -> ProjectConfig:
189
+ """Detect Rust project details."""
190
+ config.language = "Rust"
191
+ config.dev_command = "cargo run"
192
+ config.build_command = "cargo build --release"
193
+ config.test_command = "cargo test"
194
+ config.lint_command = "cargo clippy"
195
+ config.description = "Rust project"
196
+
197
+ return config
198
+
199
+
200
+ def _find_script(scripts: dict, candidates: list) -> Optional[str]:
201
+ """Find first matching script from candidates."""
202
+ for candidate in candidates:
203
+ if candidate in scripts:
204
+ return candidate
205
+ return None