claude-code-starter 0.9.0 → 0.11.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 (2) hide show
  1. package/dist/cli.js +779 -3138
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -533,3055 +533,111 @@ function summarizeTechStack(stack) {
533
533
  // src/generator.ts
534
534
  import fs2 from "fs";
535
535
  import path2 from "path";
536
- function generateArtifacts(projectInfo) {
537
- const artifacts = [];
538
- const { techStack, rootDir } = projectInfo;
539
- artifacts.push(generateSettings(techStack));
540
- artifacts.push(...generateSkills(techStack));
541
- artifacts.push(...generateAgents(techStack));
542
- artifacts.push(...generateRules(techStack));
543
- artifacts.push(...generateCommands());
544
- for (const artifact of artifacts) {
545
- const fullPath = path2.join(rootDir, artifact.path);
546
- artifact.isNew = !fs2.existsSync(fullPath);
547
- }
548
- const summary = {
549
- created: artifacts.filter((a) => a.isNew).length,
550
- updated: artifacts.filter((a) => !a.isNew).length,
551
- skipped: 0
552
- };
553
- return { artifacts, summary };
554
- }
555
- function writeArtifacts(artifacts, rootDir, force) {
556
- const created = [];
557
- const updated = [];
558
- const skipped = [];
559
- for (const artifact of artifacts) {
560
- const fullPath = path2.join(rootDir, artifact.path);
561
- const dir = path2.dirname(fullPath);
562
- fs2.mkdirSync(dir, { recursive: true });
563
- const exists = fs2.existsSync(fullPath);
564
- if (exists && !force) {
565
- const shouldPreserve = artifact.path.includes("state/task.md");
566
- if (shouldPreserve) {
567
- skipped.push(artifact.path);
568
- continue;
569
- }
570
- }
571
- fs2.writeFileSync(fullPath, artifact.content);
572
- if (exists) {
573
- updated.push(artifact.path);
574
- } else {
575
- created.push(artifact.path);
576
- }
536
+ function ensureDirectories(rootDir) {
537
+ const dirs = [
538
+ ".claude",
539
+ ".claude/skills",
540
+ ".claude/agents",
541
+ ".claude/rules",
542
+ ".claude/commands",
543
+ ".claude/state"
544
+ ];
545
+ for (const dir of dirs) {
546
+ fs2.mkdirSync(path2.join(rootDir, dir), { recursive: true });
577
547
  }
578
- return { created, updated, skipped };
579
548
  }
580
549
  function generateSettings(stack) {
581
- const permissions = ["Read(**)", "Edit(**)", "Write(.claude/**)", "Bash(git:*)"];
582
- const pkgManagers = ["npm", "yarn", "pnpm", "bun", "npx"];
583
- for (const pm of pkgManagers) {
584
- permissions.push(`Bash(${pm}:*)`);
585
- }
586
- if (stack.languages.includes("typescript") || stack.languages.includes("javascript")) {
587
- permissions.push("Bash(node:*)", "Bash(tsc:*)");
588
- }
589
- if (stack.languages.includes("python")) {
590
- permissions.push(
591
- "Bash(python:*)",
592
- "Bash(pip:*)",
593
- "Bash(poetry:*)",
594
- "Bash(pytest:*)",
595
- "Bash(uvicorn:*)"
596
- );
597
- }
598
- if (stack.languages.includes("go")) {
599
- permissions.push("Bash(go:*)");
600
- }
601
- if (stack.languages.includes("rust")) {
602
- permissions.push("Bash(cargo:*)", "Bash(rustc:*)");
603
- }
604
- if (stack.languages.includes("ruby")) {
605
- permissions.push("Bash(ruby:*)", "Bash(bundle:*)", "Bash(rails:*)", "Bash(rake:*)");
606
- }
607
- if (stack.testingFramework) {
608
- const testCommands = {
609
- jest: ["jest:*"],
610
- vitest: ["vitest:*"],
611
- playwright: ["playwright:*"],
612
- cypress: ["cypress:*"],
613
- pytest: ["pytest:*"],
614
- rspec: ["rspec:*"]
615
- };
616
- const cmds = testCommands[stack.testingFramework];
617
- if (cmds) {
618
- permissions.push(...cmds.map((c) => `Bash(${c})`));
619
- }
620
- }
621
- if (stack.linter) {
622
- permissions.push(`Bash(${stack.linter}:*)`);
623
- }
624
- if (stack.formatter) {
625
- permissions.push(`Bash(${stack.formatter}:*)`);
626
- }
627
- permissions.push(
628
- "Bash(ls:*)",
629
- "Bash(mkdir:*)",
630
- "Bash(cat:*)",
631
- "Bash(echo:*)",
632
- "Bash(grep:*)",
633
- "Bash(find:*)"
634
- );
635
- if (stack.hasDocker) {
636
- permissions.push("Bash(docker:*)", "Bash(docker-compose:*)");
637
- }
638
- const settings = {
639
- $schema: "https://json.schemastore.org/claude-code-settings.json",
640
- permissions: {
641
- allow: [...new Set(permissions)]
642
- // Deduplicate
643
- }
644
- };
645
- return {
646
- type: "settings",
647
- path: ".claude/settings.json",
648
- content: JSON.stringify(settings, null, 2),
649
- isNew: true
650
- };
651
- }
652
- function generateSkills(stack) {
653
- const artifacts = [];
654
- artifacts.push(generatePatternDiscoverySkill());
655
- artifacts.push(generateSystematicDebuggingSkill());
656
- artifacts.push(generateTestingMethodologySkill(stack));
657
- artifacts.push(generateIterativeDevelopmentSkill(stack));
658
- artifacts.push(generateCommitHygieneSkill());
659
- artifacts.push(generateCodeDeduplicationSkill());
660
- artifacts.push(generateSimplicityRulesSkill());
661
- artifacts.push(generateSecuritySkill(stack));
662
- if (stack.frameworks.includes("nextjs")) {
663
- artifacts.push(generateNextJsSkill());
664
- }
665
- if (stack.frameworks.includes("react") && !stack.frameworks.includes("nextjs")) {
666
- artifacts.push(generateReactSkill());
667
- }
668
- if (stack.frameworks.includes("fastapi")) {
669
- artifacts.push(generateFastAPISkill());
670
- }
671
- if (stack.frameworks.includes("nestjs")) {
672
- artifacts.push(generateNestJSSkill());
673
- }
674
- if (stack.frameworks.includes("swiftui")) {
675
- artifacts.push(generateSwiftUISkill());
676
- }
677
- if (stack.frameworks.includes("uikit")) {
678
- artifacts.push(generateUIKitSkill());
679
- }
680
- if (stack.frameworks.includes("vapor")) {
681
- artifacts.push(generateVaporSkill());
682
- }
683
- if (stack.frameworks.includes("jetpack-compose")) {
684
- artifacts.push(generateJetpackComposeSkill());
685
- }
686
- if (stack.frameworks.includes("android-views")) {
687
- artifacts.push(generateAndroidViewsSkill());
688
- }
689
- return artifacts;
690
- }
691
- function generatePatternDiscoverySkill() {
692
- return {
693
- type: "skill",
694
- path: ".claude/skills/pattern-discovery.md",
695
- content: `---
696
- name: pattern-discovery
697
- description: Analyze existing codebase to discover and document patterns
698
- globs:
699
- - "src/**/*"
700
- - "lib/**/*"
701
- - "app/**/*"
702
- - "components/**/*"
703
- - "pages/**/*"
704
- - "api/**/*"
705
- - "services/**/*"
706
- ---
707
-
708
- # Pattern Discovery
709
-
710
- When starting work on a project, analyze the existing code to understand its patterns.
711
-
712
- ## Discovery Process
713
-
714
- ### 1. Check for Existing Documentation
715
-
716
- \`\`\`
717
- Look for:
718
- - README.md, CONTRIBUTING.md
719
- - docs/ folder
720
- - Code comments and JSDoc/TSDoc
721
- - .editorconfig, .prettierrc, eslint config
722
- \`\`\`
723
-
724
- ### 2. Analyze Project Structure
725
-
726
- \`\`\`
727
- Questions to answer:
728
- - How are files organized? (by feature, by type, flat?)
729
- - Where does business logic live?
730
- - Where are tests located?
731
- - How are configs managed?
732
- \`\`\`
733
-
734
- ### 3. Detect Code Patterns
735
-
736
- \`\`\`
737
- Look at 3-5 similar files to find:
738
- - Naming conventions (camelCase, snake_case, PascalCase)
739
- - Import organization (grouped? sorted? relative vs absolute?)
740
- - Export style (named, default, barrel files?)
741
- - Error handling approach
742
- - Logging patterns
743
- \`\`\`
744
-
745
- ### 4. Identify Architecture
746
-
747
- \`\`\`
748
- Common patterns to detect:
749
- - MVC / MVVM / Clean Architecture
750
- - Repository pattern
751
- - Service layer
752
- - Dependency injection
753
- - Event-driven
754
- - Functional vs OOP
755
- \`\`\`
756
-
757
- ## When No Code Exists
758
-
759
- If starting a new project:
760
-
761
- 1. Ask about preferred patterns
762
- 2. Check package.json/config files for framework hints
763
- 3. Use sensible defaults for detected stack
764
- 4. Document decisions in \`.claude/state/task.md\`
765
-
766
- ## Important
767
-
768
- - **Match existing patterns** - don't impose new ones
769
- - **When in doubt, check similar files** in the codebase
770
- - **Document as you discover** - note patterns in task state
771
- - **Ask if unclear** - better to ask than assume
772
- `,
773
- isNew: true
774
- };
775
- }
776
- function generateSystematicDebuggingSkill() {
777
- return {
778
- type: "skill",
779
- path: ".claude/skills/systematic-debugging.md",
780
- content: `---
781
- name: systematic-debugging
782
- description: Methodical approach to finding and fixing bugs
783
- globs:
784
- - "**/*.ts"
785
- - "**/*.tsx"
786
- - "**/*.js"
787
- - "**/*.jsx"
788
- - "**/*.py"
789
- - "**/*.go"
790
- - "**/*.rs"
791
- ---
792
-
793
- # Systematic Debugging
794
-
795
- A 4-phase methodology for finding and fixing bugs efficiently.
796
-
797
- ## Phase 1: Reproduce
798
-
799
- Before fixing, confirm you can reproduce the bug.
800
-
801
- \`\`\`
802
- 1. Get exact steps to reproduce
803
- 2. Identify expected vs actual behavior
804
- 3. Note any error messages verbatim
805
- 4. Check if it's consistent or intermittent
806
- \`\`\`
807
-
808
- ## Phase 2: Locate
809
-
810
- Narrow down where the bug occurs.
811
-
812
- \`\`\`
813
- Techniques:
814
- - Binary search through code flow
815
- - Add logging at key points
816
- - Check recent changes (git log, git diff)
817
- - Review stack traces carefully
818
- - Use debugger breakpoints
819
- \`\`\`
820
-
821
- ## Phase 3: Diagnose
822
-
823
- Understand WHY the bug happens.
824
-
825
- \`\`\`
826
- Questions:
827
- - What assumptions are being violated?
828
- - What state is unexpected?
829
- - Is this a logic error, data error, or timing issue?
830
- - Are there edge cases not handled?
831
- \`\`\`
832
-
833
- ## Phase 4: Fix
834
-
835
- Apply the minimal correct fix.
836
-
837
- \`\`\`
838
- Guidelines:
839
- - Fix the root cause, not symptoms
840
- - Make the smallest change that fixes the issue
841
- - Add a test that would have caught this bug
842
- - Check for similar bugs elsewhere
843
- - Update documentation if needed
844
- \`\`\`
845
-
846
- ## Quick Reference
847
-
848
- | Symptom | Check First |
849
- |---------|-------------|
850
- | TypeError | Null/undefined values, type mismatches |
851
- | Off-by-one | Loop bounds, array indices |
852
- | Race condition | Async operations, shared state |
853
- | Memory leak | Event listeners, subscriptions, closures |
854
- | Infinite loop | Exit conditions, recursive calls |
855
- `,
856
- isNew: true
857
- };
858
- }
859
- function generateTestingMethodologySkill(stack) {
860
- const testingFramework = stack.testingFramework || "generic";
861
- const examples = getTestingExamples(stack);
862
- return {
863
- type: "skill",
864
- path: ".claude/skills/testing-methodology.md",
865
- content: `---
866
- name: testing-methodology
867
- description: Testing patterns and best practices for this project
868
- globs:
869
- - "**/*.test.*"
870
- - "**/*.spec.*"
871
- - "**/test/**"
872
- - "**/tests/**"
873
- - "**/__tests__/**"
874
- ---
875
-
876
- # Testing Methodology
877
-
878
- ## Testing Framework
879
-
880
- This project uses: **${testingFramework}**
881
-
882
- ## The AAA Pattern
883
-
884
- Structure every test with:
885
-
886
- \`\`\`
887
- Arrange - Set up test data and conditions
888
- Act - Execute the code being tested
889
- Assert - Verify the expected outcome
890
- \`\`\`
891
-
892
- ## What to Test
893
-
894
- ### Must Test
895
- - Core business logic
896
- - Edge cases and boundaries
897
- - Error handling paths
898
- - Public API contracts
899
-
900
- ### Consider Testing
901
- - Integration points
902
- - Complex conditional logic
903
- - State transitions
904
-
905
- ### Skip Testing
906
- - Framework internals
907
- - Simple getters/setters
908
- - Configuration constants
909
-
910
- ## Example Patterns
911
-
912
- ${examples}
913
-
914
- ## Test Naming
915
-
916
- \`\`\`
917
- Format: [unit]_[scenario]_[expected result]
918
-
919
- Examples:
920
- - calculateTotal_withEmptyCart_returnsZero
921
- - userService_createUser_savesToDatabase
922
- - parseDate_invalidFormat_throwsError
923
- \`\`\`
924
-
925
- ## Mocking Guidelines
926
-
927
- 1. **Mock external dependencies** - APIs, databases, file system
928
- 2. **Don't mock what you own** - Prefer real implementations for your code
929
- 3. **Keep mocks simple** - Complex mocks often indicate design issues
930
- 4. **Reset mocks between tests** - Avoid state leakage
931
-
932
- ## Coverage Philosophy
933
-
934
- - Aim for **80%+ coverage** on critical paths
935
- - Don't chase 100% - it often leads to brittle tests
936
- - Focus on **behavior coverage**, not line coverage
937
- `,
938
- isNew: true
939
- };
940
- }
941
- function getTestingExamples(stack) {
942
- if (stack.testingFramework === "vitest" || stack.testingFramework === "jest") {
943
- return `
944
- \`\`\`typescript
945
- import { describe, it, expect } from '${stack.testingFramework}';
946
-
947
- describe('UserService', () => {
948
- it('should create user with valid data', async () => {
949
- // Arrange
950
- const userData = { name: 'Test', email: 'test@example.com' };
951
-
952
- // Act
953
- const user = await userService.create(userData);
954
-
955
- // Assert
956
- expect(user.id).toBeDefined();
957
- expect(user.name).toBe('Test');
958
- });
959
-
960
- it('should throw on invalid email', async () => {
961
- // Arrange
962
- const userData = { name: 'Test', email: 'invalid' };
963
-
964
- // Act & Assert
965
- await expect(userService.create(userData)).rejects.toThrow('Invalid email');
966
- });
967
- });
968
- \`\`\``;
969
- }
970
- if (stack.testingFramework === "pytest") {
971
- return `
972
- \`\`\`python
973
- import pytest
974
- from myapp.services import UserService
975
-
976
- class TestUserService:
977
- def test_create_user_with_valid_data(self, db_session):
978
- # Arrange
979
- user_data = {"name": "Test", "email": "test@example.com"}
980
- service = UserService(db_session)
981
-
982
- # Act
983
- user = service.create(user_data)
984
-
985
- # Assert
986
- assert user.id is not None
987
- assert user.name == "Test"
988
-
989
- def test_create_user_invalid_email_raises(self, db_session):
990
- # Arrange
991
- user_data = {"name": "Test", "email": "invalid"}
992
- service = UserService(db_session)
993
-
994
- # Act & Assert
995
- with pytest.raises(ValueError, match="Invalid email"):
996
- service.create(user_data)
997
- \`\`\``;
998
- }
999
- if (stack.testingFramework === "go-test") {
1000
- return `
1001
- \`\`\`go
1002
- func TestUserService_Create(t *testing.T) {
1003
- t.Run("creates user with valid data", func(t *testing.T) {
1004
- // Arrange
1005
- svc := NewUserService(mockDB)
1006
- userData := UserInput{Name: "Test", Email: "test@example.com"}
1007
-
1008
- // Act
1009
- user, err := svc.Create(userData)
1010
-
1011
- // Assert
1012
- assert.NoError(t, err)
1013
- assert.NotEmpty(t, user.ID)
1014
- assert.Equal(t, "Test", user.Name)
1015
- })
1016
-
1017
- t.Run("returns error on invalid email", func(t *testing.T) {
1018
- // Arrange
1019
- svc := NewUserService(mockDB)
1020
- userData := UserInput{Name: "Test", Email: "invalid"}
1021
-
1022
- // Act
1023
- _, err := svc.Create(userData)
1024
-
1025
- // Assert
1026
- assert.ErrorContains(t, err, "invalid email")
1027
- })
1028
- }
1029
- \`\`\``;
1030
- }
1031
- return `
1032
- \`\`\`
1033
- // Add examples for your testing framework here
1034
- describe('Component', () => {
1035
- it('should behave correctly', () => {
1036
- // Arrange - set up test conditions
1037
- // Act - execute the code
1038
- // Assert - verify results
1039
- });
1040
- });
1041
- \`\`\``;
1042
- }
1043
- function generateNextJsSkill() {
1044
- return {
1045
- type: "skill",
1046
- path: ".claude/skills/nextjs-patterns.md",
1047
- content: `---
1048
- name: nextjs-patterns
1049
- description: Next.js App Router patterns and best practices
1050
- globs:
1051
- - "app/**/*"
1052
- - "src/app/**/*"
1053
- - "components/**/*"
1054
- ---
1055
-
1056
- # Next.js Patterns (App Router)
1057
-
1058
- ## File Conventions
1059
-
1060
- | File | Purpose |
1061
- |------|---------|
1062
- | \`page.tsx\` | Route UI |
1063
- | \`layout.tsx\` | Shared layout wrapper |
1064
- | \`loading.tsx\` | Loading UI (Suspense) |
1065
- | \`error.tsx\` | Error boundary |
1066
- | \`not-found.tsx\` | 404 page |
1067
- | \`route.ts\` | API endpoint |
1068
-
1069
- ## Server vs Client Components
1070
-
1071
- \`\`\`tsx
1072
- // Server Component (default) - runs on server only
1073
- export default function ServerComponent() {
1074
- // Can use: async/await, direct DB access, server-only code
1075
- // Cannot use: useState, useEffect, browser APIs
1076
- return <div>Server rendered</div>;
1077
- }
1078
-
1079
- // Client Component - runs on client
1080
- 'use client';
1081
- export default function ClientComponent() {
1082
- // Can use: hooks, event handlers, browser APIs
1083
- const [state, setState] = useState();
1084
- return <button onClick={() => setState(...)}>Click</button>;
1085
- }
1086
- \`\`\`
1087
-
1088
- ## Data Fetching
1089
-
1090
- \`\`\`tsx
1091
- // Server Component - fetch directly
1092
- async function ProductPage({ params }: { params: { id: string } }) {
1093
- const product = await db.product.findUnique({ where: { id: params.id } });
1094
- return <ProductDetails product={product} />;
1095
- }
1096
-
1097
- // With caching
1098
- const getData = cache(async (id: string) => {
1099
- return await db.find(id);
1100
- });
1101
- \`\`\`
1102
-
1103
- ## Server Actions
1104
-
1105
- \`\`\`tsx
1106
- // actions.ts
1107
- 'use server';
1108
-
1109
- export async function createPost(formData: FormData) {
1110
- const title = formData.get('title');
1111
- await db.post.create({ data: { title } });
1112
- revalidatePath('/posts');
1113
- }
1114
-
1115
- // In component
1116
- <form action={createPost}>
1117
- <input name="title" />
1118
- <button type="submit">Create</button>
1119
- </form>
1120
- \`\`\`
1121
-
1122
- ## Route Handlers
1123
-
1124
- \`\`\`tsx
1125
- // app/api/users/route.ts
1126
- import { NextResponse } from 'next/server';
1127
-
1128
- export async function GET() {
1129
- const users = await db.user.findMany();
1130
- return NextResponse.json(users);
1131
- }
1132
-
1133
- export async function POST(request: Request) {
1134
- const body = await request.json();
1135
- const user = await db.user.create({ data: body });
1136
- return NextResponse.json(user, { status: 201 });
1137
- }
1138
- \`\`\`
1139
-
1140
- ## Patterns to Follow
1141
-
1142
- 1. **Default to Server Components** - Only use 'use client' when needed
1143
- 2. **Colocate related files** - Keep components near their routes
1144
- 3. **Use route groups** - \`(auth)/login\` for organization without URL impact
1145
- 4. **Parallel routes** - \`@modal/\` for simultaneous rendering
1146
- 5. **Intercepting routes** - \`(.)/photo\` for modal patterns
1147
- `,
1148
- isNew: true
1149
- };
1150
- }
1151
- function generateReactSkill() {
1152
- return {
1153
- type: "skill",
1154
- path: ".claude/skills/react-components.md",
1155
- content: `---
1156
- name: react-components
1157
- description: React component patterns and best practices
1158
- globs:
1159
- - "src/components/**/*"
1160
- - "components/**/*"
1161
- - "**/*.tsx"
1162
- - "**/*.jsx"
1163
- ---
1164
-
1165
- # React Component Patterns
1166
-
1167
- ## Component Structure
1168
-
1169
- \`\`\`tsx
1170
- // Standard component structure
1171
- import { useState, useCallback } from 'react';
1172
- import type { ComponentProps } from './types';
1173
-
1174
- interface Props {
1175
- title: string;
1176
- onAction?: () => void;
1177
- children?: React.ReactNode;
1178
- }
1179
-
1180
- export function MyComponent({ title, onAction, children }: Props) {
1181
- const [state, setState] = useState(false);
1182
-
1183
- const handleClick = useCallback(() => {
1184
- setState(true);
1185
- onAction?.();
1186
- }, [onAction]);
1187
-
1188
- return (
1189
- <div>
1190
- <h1>{title}</h1>
1191
- <button onClick={handleClick}>Action</button>
1192
- {children}
1193
- </div>
1194
- );
1195
- }
1196
- \`\`\`
1197
-
1198
- ## Hooks Patterns
1199
-
1200
- \`\`\`tsx
1201
- // Custom hook for data fetching
1202
- function useUser(id: string) {
1203
- const [user, setUser] = useState<User | null>(null);
1204
- const [loading, setLoading] = useState(true);
1205
- const [error, setError] = useState<Error | null>(null);
1206
-
1207
- useEffect(() => {
1208
- fetchUser(id)
1209
- .then(setUser)
1210
- .catch(setError)
1211
- .finally(() => setLoading(false));
1212
- }, [id]);
1213
-
1214
- return { user, loading, error };
1215
- }
1216
- \`\`\`
1217
-
1218
- ## State Management
1219
-
1220
- \`\`\`tsx
1221
- // Use reducer for complex state
1222
- const [state, dispatch] = useReducer(reducer, initialState);
1223
-
1224
- // Use context for shared state
1225
- const ThemeContext = createContext<Theme>('light');
1226
- export const useTheme = () => useContext(ThemeContext);
1227
- \`\`\`
1228
-
1229
- ## Performance
1230
-
1231
- 1. **Memoize expensive calculations**: \`useMemo\`
1232
- 2. **Memoize callbacks**: \`useCallback\`
1233
- 3. **Memoize components**: \`React.memo\`
1234
- 4. **Avoid inline objects/arrays in props**
1235
-
1236
- ## Testing
1237
-
1238
- \`\`\`tsx
1239
- import { render, screen, fireEvent } from '@testing-library/react';
1240
-
1241
- test('button triggers action', () => {
1242
- const onAction = vi.fn();
1243
- render(<MyComponent title="Test" onAction={onAction} />);
1244
-
1245
- fireEvent.click(screen.getByRole('button'));
1246
-
1247
- expect(onAction).toHaveBeenCalled();
1248
- });
1249
- \`\`\`
1250
- `,
1251
- isNew: true
1252
- };
1253
- }
1254
- function generateFastAPISkill() {
1255
- return {
1256
- type: "skill",
1257
- path: ".claude/skills/fastapi-patterns.md",
1258
- content: `---
1259
- name: fastapi-patterns
1260
- description: FastAPI endpoint patterns and best practices
1261
- globs:
1262
- - "app/**/*.py"
1263
- - "src/**/*.py"
1264
- - "api/**/*.py"
1265
- - "routers/**/*.py"
1266
- ---
1267
-
1268
- # FastAPI Patterns
1269
-
1270
- ## Router Structure
1271
-
1272
- \`\`\`python
1273
- # routers/users.py
1274
- from fastapi import APIRouter, Depends, HTTPException
1275
- from sqlalchemy.orm import Session
1276
-
1277
- from app.database import get_db
1278
- from app.schemas import UserCreate, UserResponse
1279
- from app.services import UserService
1280
-
1281
- router = APIRouter(prefix="/users", tags=["users"])
1282
-
1283
- @router.get("/", response_model=list[UserResponse])
1284
- async def list_users(
1285
- skip: int = 0,
1286
- limit: int = 100,
1287
- db: Session = Depends(get_db)
1288
- ):
1289
- service = UserService(db)
1290
- return service.get_all(skip=skip, limit=limit)
1291
-
1292
- @router.post("/", response_model=UserResponse, status_code=201)
1293
- async def create_user(
1294
- user: UserCreate,
1295
- db: Session = Depends(get_db)
1296
- ):
1297
- service = UserService(db)
1298
- return service.create(user)
1299
- \`\`\`
1300
-
1301
- ## Dependency Injection
1302
-
1303
- \`\`\`python
1304
- # Dependencies
1305
- def get_current_user(token: str = Depends(oauth2_scheme)) -> User:
1306
- user = decode_token(token)
1307
- if not user:
1308
- raise HTTPException(status_code=401, detail="Invalid token")
1309
- return user
1310
-
1311
- def require_admin(user: User = Depends(get_current_user)) -> User:
1312
- if not user.is_admin:
1313
- raise HTTPException(status_code=403, detail="Admin required")
1314
- return user
1315
-
1316
- # Usage
1317
- @router.delete("/{id}")
1318
- async def delete_user(id: int, admin: User = Depends(require_admin)):
1319
- ...
1320
- \`\`\`
1321
-
1322
- ## Pydantic Schemas
1323
-
1324
- \`\`\`python
1325
- from pydantic import BaseModel, EmailStr, Field
1326
-
1327
- class UserBase(BaseModel):
1328
- email: EmailStr
1329
- name: str = Field(..., min_length=1, max_length=100)
1330
-
1331
- class UserCreate(UserBase):
1332
- password: str = Field(..., min_length=8)
1333
-
1334
- class UserResponse(UserBase):
1335
- id: int
1336
- created_at: datetime
1337
-
1338
- class Config:
1339
- from_attributes = True # For ORM mode
1340
- \`\`\`
1341
-
1342
- ## Error Handling
1343
-
1344
- \`\`\`python
1345
- from fastapi import HTTPException
1346
- from fastapi.responses import JSONResponse
1347
-
1348
- # Custom exception
1349
- class NotFoundError(Exception):
1350
- def __init__(self, resource: str, id: int):
1351
- self.resource = resource
1352
- self.id = id
1353
-
1354
- # Exception handler
1355
- @app.exception_handler(NotFoundError)
1356
- async def not_found_handler(request, exc: NotFoundError):
1357
- return JSONResponse(
1358
- status_code=404,
1359
- content={"error": f"{exc.resource} {exc.id} not found"}
1360
- )
1361
- \`\`\`
1362
-
1363
- ## Testing
1364
-
1365
- \`\`\`python
1366
- from fastapi.testclient import TestClient
1367
-
1368
- def test_create_user(client: TestClient):
1369
- response = client.post("/users/", json={
1370
- "email": "test@example.com",
1371
- "name": "Test",
1372
- "password": "password123"
1373
- })
1374
- assert response.status_code == 201
1375
- assert response.json()["email"] == "test@example.com"
1376
- \`\`\`
1377
- `,
1378
- isNew: true
1379
- };
1380
- }
1381
- function generateNestJSSkill() {
1382
- return {
1383
- type: "skill",
1384
- path: ".claude/skills/nestjs-patterns.md",
1385
- content: `---
1386
- name: nestjs-patterns
1387
- description: NestJS module patterns and best practices
1388
- globs:
1389
- - "src/**/*.ts"
1390
- - "**/*.module.ts"
1391
- - "**/*.controller.ts"
1392
- - "**/*.service.ts"
1393
- ---
1394
-
1395
- # NestJS Patterns
1396
-
1397
- ## Module Structure
1398
-
1399
- \`\`\`typescript
1400
- // users/users.module.ts
1401
- import { Module } from '@nestjs/common';
1402
- import { TypeOrmModule } from '@nestjs/typeorm';
1403
- import { UsersController } from './users.controller';
1404
- import { UsersService } from './users.service';
1405
- import { User } from './entities/user.entity';
1406
-
1407
- @Module({
1408
- imports: [TypeOrmModule.forFeature([User])],
1409
- controllers: [UsersController],
1410
- providers: [UsersService],
1411
- exports: [UsersService],
1412
- })
1413
- export class UsersModule {}
1414
- \`\`\`
1415
-
1416
- ## Controller Pattern
1417
-
1418
- \`\`\`typescript
1419
- // users/users.controller.ts
1420
- import { Controller, Get, Post, Body, Param, UseGuards } from '@nestjs/common';
1421
- import { UsersService } from './users.service';
1422
- import { CreateUserDto } from './dto/create-user.dto';
1423
- import { JwtAuthGuard } from '../auth/jwt-auth.guard';
1424
-
1425
- @Controller('users')
1426
- export class UsersController {
1427
- constructor(private readonly usersService: UsersService) {}
1428
-
1429
- @Get()
1430
- findAll() {
1431
- return this.usersService.findAll();
1432
- }
1433
-
1434
- @Get(':id')
1435
- findOne(@Param('id') id: string) {
1436
- return this.usersService.findOne(+id);
1437
- }
1438
-
1439
- @Post()
1440
- @UseGuards(JwtAuthGuard)
1441
- create(@Body() createUserDto: CreateUserDto) {
1442
- return this.usersService.create(createUserDto);
1443
- }
1444
- }
1445
- \`\`\`
1446
-
1447
- ## Service Pattern
1448
-
1449
- \`\`\`typescript
1450
- // users/users.service.ts
1451
- import { Injectable, NotFoundException } from '@nestjs/common';
1452
- import { InjectRepository } from '@nestjs/typeorm';
1453
- import { Repository } from 'typeorm';
1454
- import { User } from './entities/user.entity';
1455
-
1456
- @Injectable()
1457
- export class UsersService {
1458
- constructor(
1459
- @InjectRepository(User)
1460
- private usersRepository: Repository<User>,
1461
- ) {}
1462
-
1463
- findAll(): Promise<User[]> {
1464
- return this.usersRepository.find();
1465
- }
1466
-
1467
- async findOne(id: number): Promise<User> {
1468
- const user = await this.usersRepository.findOne({ where: { id } });
1469
- if (!user) {
1470
- throw new NotFoundException(\`User #\${id} not found\`);
1471
- }
1472
- return user;
1473
- }
1474
- }
1475
- \`\`\`
1476
-
1477
- ## DTO Validation
1478
-
1479
- \`\`\`typescript
1480
- // dto/create-user.dto.ts
1481
- import { IsEmail, IsString, MinLength } from 'class-validator';
1482
-
1483
- export class CreateUserDto {
1484
- @IsEmail()
1485
- email: string;
1486
-
1487
- @IsString()
1488
- @MinLength(1)
1489
- name: string;
1490
-
1491
- @IsString()
1492
- @MinLength(8)
1493
- password: string;
1494
- }
1495
- \`\`\`
1496
-
1497
- ## Testing
1498
-
1499
- \`\`\`typescript
1500
- describe('UsersService', () => {
1501
- let service: UsersService;
1502
- let repository: MockType<Repository<User>>;
1503
-
1504
- beforeEach(async () => {
1505
- const module = await Test.createTestingModule({
1506
- providers: [
1507
- UsersService,
1508
- { provide: getRepositoryToken(User), useFactory: repositoryMockFactory },
1509
- ],
1510
- }).compile();
1511
-
1512
- service = module.get(UsersService);
1513
- repository = module.get(getRepositoryToken(User));
1514
- });
1515
-
1516
- it('should find all users', async () => {
1517
- const users = [{ id: 1, name: 'Test' }];
1518
- repository.find.mockReturnValue(users);
1519
-
1520
- expect(await service.findAll()).toEqual(users);
1521
- });
1522
- });
1523
- \`\`\`
1524
- `,
1525
- isNew: true
1526
- };
1527
- }
1528
- function generateSwiftUISkill() {
1529
- return {
1530
- type: "skill",
1531
- path: ".claude/skills/swiftui-patterns.md",
1532
- content: `---
1533
- name: swiftui-patterns
1534
- description: SwiftUI declarative UI patterns and best practices
1535
- globs:
1536
- - "**/*.swift"
1537
- ---
1538
-
1539
- # SwiftUI Patterns
1540
-
1541
- ## View Structure
1542
-
1543
- \`\`\`swift
1544
- import SwiftUI
1545
-
1546
- struct ContentView: View {
1547
- @State private var count = 0
1548
- @StateObject private var viewModel = ContentViewModel()
1549
-
1550
- var body: some View {
1551
- VStack(spacing: 16) {
1552
- Text("Count: \\(count)")
1553
- .font(.title)
1554
-
1555
- Button("Increment") {
1556
- count += 1
1557
- }
1558
- .buttonStyle(.borderedProminent)
1559
- }
1560
- .padding()
1561
- }
1562
- }
1563
-
1564
- #Preview {
1565
- ContentView()
1566
- }
1567
- \`\`\`
1568
-
1569
- ## Property Wrappers
1570
-
1571
- | Wrapper | Use Case |
1572
- |---------|----------|
1573
- | \`@State\` | Simple value types owned by view |
1574
- | \`@Binding\` | Two-way connection to parent's state |
1575
- | \`@StateObject\` | Reference type owned by view (create once) |
1576
- | \`@ObservedObject\` | Reference type passed from parent |
1577
- | \`@EnvironmentObject\` | Shared data through view hierarchy |
1578
- | \`@Environment\` | System environment values |
1579
-
1580
- ## MVVM Pattern
1581
-
1582
- \`\`\`swift
1583
- // ViewModel
1584
- @MainActor
1585
- class UserViewModel: ObservableObject {
1586
- @Published var users: [User] = []
1587
- @Published var isLoading = false
1588
- @Published var error: Error?
1589
-
1590
- private let service: UserService
1591
-
1592
- init(service: UserService = .shared) {
1593
- self.service = service
1594
- }
1595
-
1596
- func fetchUsers() async {
1597
- isLoading = true
1598
- defer { isLoading = false }
1599
-
1600
- do {
1601
- users = try await service.getUsers()
1602
- } catch {
1603
- self.error = error
1604
- }
1605
- }
1606
- }
1607
-
1608
- // View
1609
- struct UsersView: View {
1610
- @StateObject private var viewModel = UserViewModel()
1611
-
1612
- var body: some View {
1613
- List(viewModel.users) { user in
1614
- UserRow(user: user)
1615
- }
1616
- .overlay {
1617
- if viewModel.isLoading {
1618
- ProgressView()
1619
- }
1620
- }
1621
- .task {
1622
- await viewModel.fetchUsers()
1623
- }
1624
- }
1625
- }
1626
- \`\`\`
1627
-
1628
- ## Navigation (iOS 16+)
1629
-
1630
- \`\`\`swift
1631
- struct AppNavigation: View {
1632
- @State private var path = NavigationPath()
1633
-
1634
- var body: some View {
1635
- NavigationStack(path: $path) {
1636
- HomeView()
1637
- .navigationDestination(for: User.self) { user in
1638
- UserDetailView(user: user)
1639
- }
1640
- .navigationDestination(for: Settings.self) { settings in
1641
- SettingsView(settings: settings)
1642
- }
1643
- }
1644
- }
1645
- }
1646
- \`\`\`
1647
-
1648
- ## Async/Await Patterns
1649
-
1650
- \`\`\`swift
1651
- // Task modifier for view lifecycle
1652
- .task {
1653
- await loadData()
1654
- }
1655
-
1656
- // Task with cancellation
1657
- .task(id: searchText) {
1658
- try? await Task.sleep(for: .milliseconds(300))
1659
- await search(searchText)
1660
- }
1661
-
1662
- // Refreshable
1663
- .refreshable {
1664
- await viewModel.refresh()
1665
- }
1666
- \`\`\`
1667
-
1668
- ## Best Practices
1669
-
1670
- 1. **Keep views small** - Extract subviews for reusability
1671
- 2. **Use \`@MainActor\`** - For ViewModels updating UI
1672
- 3. **Prefer value types** - Structs over classes when possible
1673
- 4. **Use \`.task\`** - Instead of \`.onAppear\` for async work
1674
- 5. **Preview extensively** - Use #Preview for rapid iteration
1675
- `,
1676
- isNew: true
1677
- };
1678
- }
1679
- function generateUIKitSkill() {
1680
- return {
1681
- type: "skill",
1682
- path: ".claude/skills/uikit-patterns.md",
1683
- content: `---
1684
- name: uikit-patterns
1685
- description: UIKit view controller patterns and best practices
1686
- globs:
1687
- - "**/*.swift"
1688
- ---
1689
-
1690
- # UIKit Patterns
1691
-
1692
- ## View Controller Structure
1693
-
1694
- \`\`\`swift
1695
- import UIKit
1696
-
1697
- class UserViewController: UIViewController {
1698
- // MARK: - Properties
1699
- private let viewModel: UserViewModel
1700
- private var cancellables = Set<AnyCancellable>()
1701
-
1702
- // MARK: - UI Components
1703
- private lazy var tableView: UITableView = {
1704
- let table = UITableView()
1705
- table.translatesAutoresizingMaskIntoConstraints = false
1706
- table.delegate = self
1707
- table.dataSource = self
1708
- table.register(UserCell.self, forCellReuseIdentifier: UserCell.identifier)
1709
- return table
1710
- }()
1711
-
1712
- // MARK: - Lifecycle
1713
- init(viewModel: UserViewModel) {
1714
- self.viewModel = viewModel
1715
- super.init(nibName: nil, bundle: nil)
1716
- }
1717
-
1718
- required init?(coder: NSCoder) {
1719
- fatalError("init(coder:) has not been implemented")
1720
- }
1721
-
1722
- override func viewDidLoad() {
1723
- super.viewDidLoad()
1724
- setupUI()
1725
- setupBindings()
1726
- viewModel.fetchUsers()
1727
- }
1728
-
1729
- // MARK: - Setup
1730
- private func setupUI() {
1731
- view.backgroundColor = .systemBackground
1732
- view.addSubview(tableView)
1733
-
1734
- NSLayoutConstraint.activate([
1735
- tableView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
1736
- tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
1737
- tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
1738
- tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor)
1739
- ])
1740
- }
1741
-
1742
- private func setupBindings() {
1743
- viewModel.$users
1744
- .receive(on: DispatchQueue.main)
1745
- .sink { [weak self] _ in
1746
- self?.tableView.reloadData()
1747
- }
1748
- .store(in: &cancellables)
1749
- }
1750
- }
1751
- \`\`\`
1752
-
1753
- ## Coordinator Pattern
1754
-
1755
- \`\`\`swift
1756
- protocol Coordinator: AnyObject {
1757
- var childCoordinators: [Coordinator] { get set }
1758
- var navigationController: UINavigationController { get set }
1759
- func start()
1760
- }
1761
-
1762
- class AppCoordinator: Coordinator {
1763
- var childCoordinators: [Coordinator] = []
1764
- var navigationController: UINavigationController
1765
-
1766
- init(navigationController: UINavigationController) {
1767
- self.navigationController = navigationController
1768
- }
1769
-
1770
- func start() {
1771
- let vc = HomeViewController()
1772
- vc.coordinator = self
1773
- navigationController.pushViewController(vc, animated: false)
1774
- }
1775
-
1776
- func showUserDetail(_ user: User) {
1777
- let vc = UserDetailViewController(user: user)
1778
- navigationController.pushViewController(vc, animated: true)
1779
- }
1780
- }
1781
- \`\`\`
1782
-
1783
- ## Table View Cell
1784
-
1785
- \`\`\`swift
1786
- class UserCell: UITableViewCell {
1787
- static let identifier = "UserCell"
1788
-
1789
- private let nameLabel: UILabel = {
1790
- let label = UILabel()
1791
- label.font = .preferredFont(forTextStyle: .headline)
1792
- label.translatesAutoresizingMaskIntoConstraints = false
1793
- return label
1794
- }()
1795
-
1796
- override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
1797
- super.init(style: style, reuseIdentifier: reuseIdentifier)
1798
- setupUI()
1799
- }
1800
-
1801
- required init?(coder: NSCoder) {
1802
- fatalError("init(coder:) has not been implemented")
1803
- }
1804
-
1805
- private func setupUI() {
1806
- contentView.addSubview(nameLabel)
1807
- NSLayoutConstraint.activate([
1808
- nameLabel.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 16),
1809
- nameLabel.centerYAnchor.constraint(equalTo: contentView.centerYAnchor)
1810
- ])
1811
- }
1812
-
1813
- func configure(with user: User) {
1814
- nameLabel.text = user.name
1815
- }
1816
- }
1817
- \`\`\`
1818
-
1819
- ## Best Practices
1820
-
1821
- 1. **Use Auto Layout** - Programmatic constraints over Storyboards
1822
- 2. **MARK comments** - Organize code sections
1823
- 3. **Coordinator pattern** - For navigation logic
1824
- 4. **Dependency injection** - Pass dependencies via init
1825
- 5. **Combine for bindings** - Reactive updates from ViewModel
1826
- `,
1827
- isNew: true
1828
- };
1829
- }
1830
- function generateVaporSkill() {
1831
- return {
1832
- type: "skill",
1833
- path: ".claude/skills/vapor-patterns.md",
1834
- content: `---
1835
- name: vapor-patterns
1836
- description: Vapor server-side Swift patterns
1837
- globs:
1838
- - "**/*.swift"
1839
- - "Package.swift"
1840
- ---
1841
-
1842
- # Vapor Patterns
1843
-
1844
- ## Route Structure
1845
-
1846
- \`\`\`swift
1847
- import Vapor
1848
-
1849
- func routes(_ app: Application) throws {
1850
- // Basic routes
1851
- app.get { req in
1852
- "Hello, world!"
1853
- }
1854
-
1855
- // Route groups
1856
- let api = app.grouped("api", "v1")
1857
-
1858
- // Controller registration
1859
- try api.register(collection: UserController())
1860
- }
1861
- \`\`\`
1862
-
1863
- ## Controller Pattern
1864
-
1865
- \`\`\`swift
1866
- import Vapor
1867
-
1868
- struct UserController: RouteCollection {
1869
- func boot(routes: RoutesBuilder) throws {
1870
- let users = routes.grouped("users")
1871
-
1872
- users.get(use: index)
1873
- users.post(use: create)
1874
- users.group(":userID") { user in
1875
- user.get(use: show)
1876
- user.put(use: update)
1877
- user.delete(use: delete)
1878
- }
1879
- }
1880
-
1881
- // GET /users
1882
- func index(req: Request) async throws -> [UserDTO] {
1883
- try await User.query(on: req.db).all().map { $0.toDTO() }
1884
- }
1885
-
1886
- // POST /users
1887
- func create(req: Request) async throws -> UserDTO {
1888
- let input = try req.content.decode(CreateUserInput.self)
1889
- let user = User(name: input.name, email: input.email)
1890
- try await user.save(on: req.db)
1891
- return user.toDTO()
1892
- }
1893
-
1894
- // GET /users/:userID
1895
- func show(req: Request) async throws -> UserDTO {
1896
- guard let user = try await User.find(req.parameters.get("userID"), on: req.db) else {
1897
- throw Abort(.notFound)
1898
- }
1899
- return user.toDTO()
1900
- }
1901
- }
1902
- \`\`\`
1903
-
1904
- ## Fluent Models
1905
-
1906
- \`\`\`swift
1907
- import Fluent
1908
- import Vapor
1909
-
1910
- final class User: Model, Content {
1911
- static let schema = "users"
1912
-
1913
- @ID(key: .id)
1914
- var id: UUID?
1915
-
1916
- @Field(key: "name")
1917
- var name: String
1918
-
1919
- @Field(key: "email")
1920
- var email: String
1921
-
1922
- @Timestamp(key: "created_at", on: .create)
1923
- var createdAt: Date?
1924
-
1925
- @Children(for: \\.$user)
1926
- var posts: [Post]
1927
-
1928
- init() {}
1929
-
1930
- init(id: UUID? = nil, name: String, email: String) {
1931
- self.id = id
1932
- self.name = name
1933
- self.email = email
1934
- }
1935
- }
1936
-
1937
- // Migration
1938
- struct CreateUser: AsyncMigration {
1939
- func prepare(on database: Database) async throws {
1940
- try await database.schema("users")
1941
- .id()
1942
- .field("name", .string, .required)
1943
- .field("email", .string, .required)
1944
- .field("created_at", .datetime)
1945
- .unique(on: "email")
1946
- .create()
1947
- }
1948
-
1949
- func revert(on database: Database) async throws {
1950
- try await database.schema("users").delete()
1951
- }
1952
- }
1953
- \`\`\`
1954
-
1955
- ## DTOs and Validation
1956
-
1957
- \`\`\`swift
1958
- struct CreateUserInput: Content, Validatable {
1959
- var name: String
1960
- var email: String
1961
-
1962
- static func validations(_ validations: inout Validations) {
1963
- validations.add("name", as: String.self, is: !.empty)
1964
- validations.add("email", as: String.self, is: .email)
1965
- }
1966
- }
1967
-
1968
- struct UserDTO: Content {
1969
- var id: UUID?
1970
- var name: String
1971
- var email: String
1972
- }
1973
-
1974
- extension User {
1975
- func toDTO() -> UserDTO {
1976
- UserDTO(id: id, name: name, email: email)
1977
- }
1978
- }
1979
- \`\`\`
1980
-
1981
- ## Middleware
1982
-
1983
- \`\`\`swift
1984
- struct AuthMiddleware: AsyncMiddleware {
1985
- func respond(to request: Request, chainingTo next: AsyncResponder) async throws -> Response {
1986
- guard let token = request.headers.bearerAuthorization?.token else {
1987
- throw Abort(.unauthorized)
1988
- }
1989
-
1990
- // Validate token
1991
- let user = try await validateToken(token, on: request)
1992
- request.auth.login(user)
1993
-
1994
- return try await next.respond(to: request)
1995
- }
1996
- }
1997
- \`\`\`
1998
-
1999
- ## Testing
2000
-
2001
- \`\`\`swift
2002
- @testable import App
2003
- import XCTVapor
2004
-
2005
- final class UserTests: XCTestCase {
2006
- var app: Application!
2007
-
2008
- override func setUp() async throws {
2009
- app = Application(.testing)
2010
- try configure(app)
2011
- try await app.autoMigrate()
2012
- }
2013
-
2014
- override func tearDown() async throws {
2015
- try await app.autoRevert()
2016
- app.shutdown()
2017
- }
2018
-
2019
- func testCreateUser() async throws {
2020
- try app.test(.POST, "api/v1/users", beforeRequest: { req in
2021
- try req.content.encode(CreateUserInput(name: "Test", email: "test@example.com"))
2022
- }, afterResponse: { res in
2023
- XCTAssertEqual(res.status, .ok)
2024
- let user = try res.content.decode(UserDTO.self)
2025
- XCTAssertEqual(user.name, "Test")
2026
- })
2027
- }
2028
- }
2029
- \`\`\`
2030
- `,
2031
- isNew: true
2032
- };
2033
- }
2034
- function generateJetpackComposeSkill() {
2035
- return {
2036
- type: "skill",
2037
- path: ".claude/skills/compose-patterns.md",
2038
- content: `---
2039
- name: compose-patterns
2040
- description: Jetpack Compose UI patterns and best practices
2041
- globs:
2042
- - "**/*.kt"
2043
- ---
2044
-
2045
- # Jetpack Compose Patterns
2046
-
2047
- ## Composable Structure
2048
-
2049
- \`\`\`kotlin
2050
- @Composable
2051
- fun UserScreen(
2052
- viewModel: UserViewModel = hiltViewModel()
2053
- ) {
2054
- val uiState by viewModel.uiState.collectAsStateWithLifecycle()
2055
-
2056
- UserScreenContent(
2057
- uiState = uiState,
2058
- onRefresh = viewModel::refresh,
2059
- onUserClick = viewModel::selectUser
2060
- )
2061
- }
2062
-
2063
- @Composable
2064
- private fun UserScreenContent(
2065
- uiState: UserUiState,
2066
- onRefresh: () -> Unit,
2067
- onUserClick: (User) -> Unit
2068
- ) {
2069
- Scaffold(
2070
- topBar = {
2071
- TopAppBar(title = { Text("Users") })
2072
- }
2073
- ) { padding ->
2074
- when (uiState) {
2075
- is UserUiState.Loading -> LoadingIndicator()
2076
- is UserUiState.Success -> UserList(
2077
- users = uiState.users,
2078
- onUserClick = onUserClick,
2079
- modifier = Modifier.padding(padding)
2080
- )
2081
- is UserUiState.Error -> ErrorMessage(uiState.message)
2082
- }
2083
- }
2084
- }
2085
- \`\`\`
2086
-
2087
- ## State Management
2088
-
2089
- \`\`\`kotlin
2090
- // UI State
2091
- sealed interface UserUiState {
2092
- object Loading : UserUiState
2093
- data class Success(val users: List<User>) : UserUiState
2094
- data class Error(val message: String) : UserUiState
2095
- }
2096
-
2097
- // ViewModel
2098
- @HiltViewModel
2099
- class UserViewModel @Inject constructor(
2100
- private val repository: UserRepository
2101
- ) : ViewModel() {
2102
-
2103
- private val _uiState = MutableStateFlow<UserUiState>(UserUiState.Loading)
2104
- val uiState: StateFlow<UserUiState> = _uiState.asStateFlow()
2105
-
2106
- init {
2107
- loadUsers()
2108
- }
2109
-
2110
- fun refresh() {
2111
- loadUsers()
2112
- }
2113
-
2114
- private fun loadUsers() {
2115
- viewModelScope.launch {
2116
- _uiState.value = UserUiState.Loading
2117
- repository.getUsers()
2118
- .onSuccess { users ->
2119
- _uiState.value = UserUiState.Success(users)
2120
- }
2121
- .onFailure { error ->
2122
- _uiState.value = UserUiState.Error(error.message ?: "Unknown error")
2123
- }
2124
- }
2125
- }
2126
- }
2127
- \`\`\`
2128
-
2129
- ## Reusable Components
2130
-
2131
- \`\`\`kotlin
2132
- @Composable
2133
- fun UserCard(
2134
- user: User,
2135
- onClick: () -> Unit,
2136
- modifier: Modifier = Modifier
2137
- ) {
2138
- Card(
2139
- onClick = onClick,
2140
- modifier = modifier.fillMaxWidth()
2141
- ) {
2142
- Row(
2143
- modifier = Modifier.padding(16.dp),
2144
- verticalAlignment = Alignment.CenterVertically
2145
- ) {
2146
- AsyncImage(
2147
- model = user.avatarUrl,
2148
- contentDescription = null,
2149
- modifier = Modifier
2150
- .size(48.dp)
2151
- .clip(CircleShape)
2152
- )
2153
- Spacer(modifier = Modifier.width(16.dp))
2154
- Column {
2155
- Text(
2156
- text = user.name,
2157
- style = MaterialTheme.typography.titleMedium
2158
- )
2159
- Text(
2160
- text = user.email,
2161
- style = MaterialTheme.typography.bodySmall
2162
- )
2163
- }
2164
- }
2165
- }
2166
- }
2167
- \`\`\`
2168
-
2169
- ## Navigation
2170
-
2171
- \`\`\`kotlin
2172
- @Composable
2173
- fun AppNavigation() {
2174
- val navController = rememberNavController()
2175
-
2176
- NavHost(
2177
- navController = navController,
2178
- startDestination = "home"
2179
- ) {
2180
- composable("home") {
2181
- HomeScreen(
2182
- onUserClick = { userId ->
2183
- navController.navigate("user/$userId")
2184
- }
2185
- )
2186
- }
2187
- composable(
2188
- route = "user/{userId}",
2189
- arguments = listOf(navArgument("userId") { type = NavType.StringType })
2190
- ) { backStackEntry ->
2191
- val userId = backStackEntry.arguments?.getString("userId")
2192
- UserDetailScreen(userId = userId)
2193
- }
2194
- }
2195
- }
2196
- \`\`\`
2197
-
2198
- ## Theming
2199
-
2200
- \`\`\`kotlin
2201
- @Composable
2202
- fun AppTheme(
2203
- darkTheme: Boolean = isSystemInDarkTheme(),
2204
- content: @Composable () -> Unit
2205
- ) {
2206
- val colorScheme = if (darkTheme) {
2207
- darkColorScheme(
2208
- primary = Purple80,
2209
- secondary = PurpleGrey80
2210
- )
2211
- } else {
2212
- lightColorScheme(
2213
- primary = Purple40,
2214
- secondary = PurpleGrey40
2215
- )
2216
- }
2217
-
2218
- MaterialTheme(
2219
- colorScheme = colorScheme,
2220
- typography = Typography,
2221
- content = content
2222
- )
2223
- }
2224
- \`\`\`
2225
-
2226
- ## Best Practices
2227
-
2228
- 1. **Stateless composables** - Pass state down, events up
2229
- 2. **Remember wisely** - Use \`remember\` for expensive calculations
2230
- 3. **Lifecycle-aware collection** - Use \`collectAsStateWithLifecycle()\`
2231
- 4. **Modifier parameter** - Always accept Modifier as last parameter
2232
- 5. **Preview annotations** - Add @Preview for rapid iteration
2233
- `,
2234
- isNew: true
2235
- };
2236
- }
2237
- function generateAndroidViewsSkill() {
2238
- return {
2239
- type: "skill",
2240
- path: ".claude/skills/android-views-patterns.md",
2241
- content: `---
2242
- name: android-views-patterns
2243
- description: Android XML views and traditional patterns
2244
- globs:
2245
- - "**/*.kt"
2246
- - "**/*.xml"
2247
- ---
2248
-
2249
- # Android Views Patterns
2250
-
2251
- ## Activity Structure
2252
-
2253
- \`\`\`kotlin
2254
- class MainActivity : AppCompatActivity() {
2255
-
2256
- private lateinit var binding: ActivityMainBinding
2257
- private val viewModel: MainViewModel by viewModels()
2258
-
2259
- override fun onCreate(savedInstanceState: Bundle?) {
2260
- super.onCreate(savedInstanceState)
2261
- binding = ActivityMainBinding.inflate(layoutInflater)
2262
- setContentView(binding.root)
2263
-
2264
- setupUI()
2265
- observeViewModel()
2266
- }
2267
-
2268
- private fun setupUI() {
2269
- binding.recyclerView.apply {
2270
- layoutManager = LinearLayoutManager(this@MainActivity)
2271
- adapter = userAdapter
2272
- }
2273
-
2274
- binding.swipeRefresh.setOnRefreshListener {
2275
- viewModel.refresh()
2276
- }
2277
- }
2278
-
2279
- private fun observeViewModel() {
2280
- lifecycleScope.launch {
2281
- repeatOnLifecycle(Lifecycle.State.STARTED) {
2282
- viewModel.uiState.collect { state ->
2283
- updateUI(state)
2284
- }
2285
- }
2286
- }
2287
- }
2288
-
2289
- private fun updateUI(state: MainUiState) {
2290
- binding.swipeRefresh.isRefreshing = state.isLoading
2291
- userAdapter.submitList(state.users)
2292
- binding.errorText.isVisible = state.error != null
2293
- binding.errorText.text = state.error
2294
- }
2295
- }
2296
- \`\`\`
2297
-
2298
- ## Fragment Pattern
2299
-
2300
- \`\`\`kotlin
2301
- class UserFragment : Fragment(R.layout.fragment_user) {
2302
-
2303
- private var _binding: FragmentUserBinding? = null
2304
- private val binding get() = _binding!!
2305
-
2306
- private val viewModel: UserViewModel by viewModels()
2307
- private val args: UserFragmentArgs by navArgs()
2308
-
2309
- override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
2310
- super.onViewCreated(view, savedInstanceState)
2311
- _binding = FragmentUserBinding.bind(view)
2312
-
2313
- setupUI()
2314
- observeViewModel()
2315
- viewModel.loadUser(args.userId)
2316
- }
2317
-
2318
- override fun onDestroyView() {
2319
- super.onDestroyView()
2320
- _binding = null
2321
- }
2322
- }
2323
- \`\`\`
2324
-
2325
- ## RecyclerView Adapter
2326
-
2327
- \`\`\`kotlin
2328
- class UserAdapter(
2329
- private val onItemClick: (User) -> Unit
2330
- ) : ListAdapter<User, UserAdapter.ViewHolder>(UserDiffCallback()) {
2331
-
2332
- override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
2333
- val binding = ItemUserBinding.inflate(
2334
- LayoutInflater.from(parent.context),
2335
- parent,
2336
- false
2337
- )
2338
- return ViewHolder(binding)
2339
- }
2340
-
2341
- override fun onBindViewHolder(holder: ViewHolder, position: Int) {
2342
- holder.bind(getItem(position))
2343
- }
2344
-
2345
- inner class ViewHolder(
2346
- private val binding: ItemUserBinding
2347
- ) : RecyclerView.ViewHolder(binding.root) {
2348
-
2349
- init {
2350
- binding.root.setOnClickListener {
2351
- onItemClick(getItem(adapterPosition))
2352
- }
2353
- }
2354
-
2355
- fun bind(user: User) {
2356
- binding.nameText.text = user.name
2357
- binding.emailText.text = user.email
2358
- Glide.with(binding.avatar)
2359
- .load(user.avatarUrl)
2360
- .circleCrop()
2361
- .into(binding.avatar)
2362
- }
2363
- }
2364
-
2365
- class UserDiffCallback : DiffUtil.ItemCallback<User>() {
2366
- override fun areItemsTheSame(oldItem: User, newItem: User) =
2367
- oldItem.id == newItem.id
2368
-
2369
- override fun areContentsTheSame(oldItem: User, newItem: User) =
2370
- oldItem == newItem
2371
- }
2372
- }
2373
- \`\`\`
2374
-
2375
- ## XML Layout
2376
-
2377
- \`\`\`xml
2378
- <?xml version="1.0" encoding="utf-8"?>
2379
- <androidx.constraintlayout.widget.ConstraintLayout
2380
- xmlns:android="http://schemas.android.com/apk/res/android"
2381
- xmlns:app="http://schemas.android.com/apk/res-auto"
2382
- android:layout_width="match_parent"
2383
- android:layout_height="match_parent">
2384
-
2385
- <com.google.android.material.appbar.MaterialToolbar
2386
- android:id="@+id/toolbar"
2387
- android:layout_width="0dp"
2388
- android:layout_height="?attr/actionBarSize"
2389
- app:title="Users"
2390
- app:layout_constraintTop_toTopOf="parent"
2391
- app:layout_constraintStart_toStartOf="parent"
2392
- app:layout_constraintEnd_toEndOf="parent" />
2393
-
2394
- <androidx.swiperefreshlayout.widget.SwipeRefreshLayout
2395
- android:id="@+id/swipeRefresh"
2396
- android:layout_width="0dp"
2397
- android:layout_height="0dp"
2398
- app:layout_constraintTop_toBottomOf="@id/toolbar"
2399
- app:layout_constraintBottom_toBottomOf="parent"
2400
- app:layout_constraintStart_toStartOf="parent"
2401
- app:layout_constraintEnd_toEndOf="parent">
2402
-
2403
- <androidx.recyclerview.widget.RecyclerView
2404
- android:id="@+id/recyclerView"
2405
- android:layout_width="match_parent"
2406
- android:layout_height="match_parent" />
2407
-
2408
- </androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
2409
-
2410
- </androidx.constraintlayout.widget.ConstraintLayout>
2411
- \`\`\`
2412
-
2413
- ## ViewModel with Repository
2414
-
2415
- \`\`\`kotlin
2416
- @HiltViewModel
2417
- class UserViewModel @Inject constructor(
2418
- private val repository: UserRepository,
2419
- private val savedStateHandle: SavedStateHandle
2420
- ) : ViewModel() {
2421
-
2422
- private val _uiState = MutableStateFlow(UserUiState())
2423
- val uiState: StateFlow<UserUiState> = _uiState.asStateFlow()
2424
-
2425
- fun loadUsers() {
2426
- viewModelScope.launch {
2427
- _uiState.update { it.copy(isLoading = true) }
2428
- try {
2429
- val users = repository.getUsers()
2430
- _uiState.update { it.copy(users = users, isLoading = false) }
2431
- } catch (e: Exception) {
2432
- _uiState.update { it.copy(error = e.message, isLoading = false) }
2433
- }
2434
- }
2435
- }
2436
- }
2437
-
2438
- data class UserUiState(
2439
- val users: List<User> = emptyList(),
2440
- val isLoading: Boolean = false,
2441
- val error: String? = null
2442
- )
2443
- \`\`\`
2444
-
2445
- ## Best Practices
2446
-
2447
- 1. **View Binding** - Use over findViewById or synthetic imports
2448
- 2. **Lifecycle awareness** - Collect flows in repeatOnLifecycle
2449
- 3. **ListAdapter** - For efficient RecyclerView updates
2450
- 4. **Navigation Component** - For fragment navigation
2451
- 5. **Clean up bindings** - Set to null in onDestroyView
2452
- `,
2453
- isNew: true
2454
- };
2455
- }
2456
- function generateIterativeDevelopmentSkill(stack) {
2457
- const testCmd = getTestCommand(stack);
2458
- const lintCmd = getLintCommand(stack);
2459
- return {
2460
- type: "skill",
2461
- path: ".claude/skills/iterative-development.md",
2462
- content: `---
2463
- name: iterative-development
2464
- description: TDD-driven iterative loops until tests pass
2465
- globs:
2466
- - "**/*.ts"
2467
- - "**/*.tsx"
2468
- - "**/*.js"
2469
- - "**/*.py"
2470
- - "**/*.go"
2471
- ---
2472
-
2473
- # Iterative Development (TDD Loops)
2474
-
2475
- Self-referential development loops where you iterate until completion criteria are met.
2476
-
2477
- ## Core Philosophy
2478
-
2479
- \`\`\`
2480
- \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510
2481
- \u2502 ITERATION > PERFECTION \u2502
2482
- \u2502 Don't aim for perfect on first try. \u2502
2483
- \u2502 Let the loop refine the work. \u2502
2484
- \u251C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524
2485
- \u2502 FAILURES ARE DATA \u2502
2486
- \u2502 Failed tests, lint errors, type mismatches are signals. \u2502
2487
- \u2502 Use them to guide the next iteration. \u2502
2488
- \u251C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524
2489
- \u2502 CLEAR COMPLETION CRITERIA \u2502
2490
- \u2502 Define exactly what "done" looks like. \u2502
2491
- \u2502 Tests passing. Coverage met. Lint clean. \u2502
2492
- \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518
2493
- \`\`\`
2494
-
2495
- ## TDD Workflow (Mandatory)
2496
-
2497
- Every implementation task MUST follow this workflow:
2498
-
2499
- ### 1. RED: Write Tests First
2500
- \`\`\`bash
2501
- # Write tests based on requirements
2502
- # Run tests - they MUST FAIL
2503
- ${testCmd}
2504
- \`\`\`
2505
-
2506
- ### 2. GREEN: Implement Feature
2507
- \`\`\`bash
2508
- # Write minimum code to pass tests
2509
- # Run tests - they MUST PASS
2510
- ${testCmd}
2511
- \`\`\`
2512
-
2513
- ### 3. VALIDATE: Quality Gates
2514
- \`\`\`bash
2515
- # Full quality check
2516
- ${lintCmd ? `${lintCmd} && ` : ""}${testCmd}
2517
- \`\`\`
2518
-
2519
- ## Completion Criteria Template
2520
-
2521
- For any implementation task, define:
2522
-
2523
- \`\`\`markdown
2524
- ### Completion Criteria
2525
- - [ ] All tests passing
2526
- - [ ] Coverage >= 80% (on new code)
2527
- - [ ] Lint clean (no errors)
2528
- - [ ] Type check passing
2529
- \`\`\`
2530
-
2531
- ## When to Use This Workflow
2532
-
2533
- | Task Type | Use TDD Loop? |
2534
- |-----------|---------------|
2535
- | New feature | \u2705 Always |
2536
- | Bug fix | \u2705 Always (write test that reproduces bug first) |
2537
- | Refactoring | \u2705 Always (existing tests must stay green) |
2538
- | Spike/exploration | \u274C Skip (but document findings) |
2539
- | Documentation | \u274C Skip |
2540
-
2541
- ## Anti-Patterns
2542
-
2543
- - \u274C Writing code before tests
2544
- - \u274C Skipping the RED phase (tests that never fail are useless)
2545
- - \u274C Moving on when tests fail
2546
- - \u274C Large batches (prefer small, focused iterations)
2547
- `,
2548
- isNew: true
2549
- };
2550
- }
2551
- function generateCommitHygieneSkill() {
2552
- return {
2553
- type: "skill",
2554
- path: ".claude/skills/commit-hygiene.md",
2555
- content: `---
2556
- name: commit-hygiene
2557
- description: Atomic commits, PR size limits, commit thresholds
2558
- globs:
2559
- - "**/*"
2560
- ---
2561
-
2562
- # Commit Hygiene
2563
-
2564
- Keep commits atomic, PRs reviewable, and git history clean.
2565
-
2566
- ## Size Thresholds
2567
-
2568
- | Metric | \u{1F7E2} Good | \u{1F7E1} Warning | \u{1F534} Commit Now |
2569
- |--------|---------|------------|---------------|
2570
- | Files changed | 1-5 | 6-10 | > 10 |
2571
- | Lines added | < 150 | 150-300 | > 300 |
2572
- | Total changes | < 250 | 250-400 | > 400 |
2573
-
2574
- **Research shows:** PRs > 400 lines have 40%+ defect rates vs 15% for smaller changes.
2575
-
2576
- ## When to Commit
2577
-
2578
- ### Commit Triggers (Any = Commit)
2579
-
2580
- | Trigger | Action |
2581
- |---------|--------|
2582
- | Test passes | Just got a test green \u2192 commit |
2583
- | Feature complete | Finished a function \u2192 commit |
2584
- | Refactor done | Renamed across files \u2192 commit |
2585
- | Bug fixed | Fixed the issue \u2192 commit |
2586
- | Threshold hit | > 5 files or > 200 lines \u2192 commit |
2587
-
2588
- ### Commit Immediately If
2589
-
2590
- - \u2705 Tests are passing after being red
2591
- - \u2705 You're about to make a "big change"
2592
- - \u2705 You've been coding for 30+ minutes
2593
- - \u2705 You're about to try something risky
2594
- - \u2705 The current state is "working"
2595
-
2596
- ## Atomic Commit Patterns
2597
-
2598
- ### Good Commits \u2705
2599
-
2600
- \`\`\`
2601
- "Add email validation to signup form"
2602
- - 3 files: validator.ts, signup.tsx, signup.test.ts
2603
- - 120 lines changed
2604
- - Single purpose: email validation
2605
-
2606
- "Fix null pointer in user lookup"
2607
- - 2 files: userService.ts, userService.test.ts
2608
- - 25 lines changed
2609
- - Single purpose: fix one bug
2610
- \`\`\`
2611
-
2612
- ### Bad Commits \u274C
2613
-
2614
- \`\`\`
2615
- "Add authentication, fix bugs, update styles"
2616
- - 25 files changed, 800 lines
2617
- - Multiple unrelated purposes
2618
-
2619
- "WIP" / "Updates" / "Fix stuff"
2620
- - Unknown scope, no clear purpose
2621
- \`\`\`
2622
-
2623
- ## Quick Status Check
2624
-
2625
- Run frequently to check current state:
2626
-
2627
- \`\`\`bash
2628
- # See what's changed
2629
- git status --short
2630
-
2631
- # Count changes
2632
- git diff --shortstat
2633
-
2634
- # Full summary
2635
- git diff --stat HEAD
2636
- \`\`\`
2637
-
2638
- ## PR Size Rules
2639
-
2640
- | PR Size | Review Time | Quality |
2641
- |---------|-------------|---------|
2642
- | < 200 lines | < 30 min | High confidence |
2643
- | 200-400 lines | 30-60 min | Good confidence |
2644
- | 400-1000 lines | 1-2 hours | Declining quality |
2645
- | > 1000 lines | Often skipped | Rubber-stamped |
2646
-
2647
- **Best practice:** If a PR will be > 400 lines, split into stacked PRs.
2648
- `,
2649
- isNew: true
2650
- };
2651
- }
2652
- function generateCodeDeduplicationSkill() {
2653
- return {
2654
- type: "skill",
2655
- path: ".claude/skills/code-deduplication.md",
2656
- content: `---
2657
- name: code-deduplication
2658
- description: Prevent semantic code duplication with capability index
2659
- globs:
2660
- - "**/*.ts"
2661
- - "**/*.tsx"
2662
- - "**/*.js"
2663
- - "**/*.py"
2664
- ---
2665
-
2666
- # Code Deduplication
2667
-
2668
- Prevent semantic duplication by maintaining awareness of existing capabilities.
2669
-
2670
- ## Core Principle
2671
-
2672
- \`\`\`
2673
- \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510
2674
- \u2502 CHECK BEFORE YOU WRITE \u2502
2675
- \u2502 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 \u2502
2676
- \u2502 AI doesn't copy/paste - it reimplements. \u2502
2677
- \u2502 The problem isn't duplicate code, it's duplicate PURPOSE. \u2502
2678
- \u2502 \u2502
2679
- \u2502 Before writing ANY new function: \u2502
2680
- \u2502 1. Search codebase for similar functionality \u2502
2681
- \u2502 2. Check utils/, helpers/, lib/ for existing implementations \u2502
2682
- \u2502 3. Extend existing code if possible \u2502
2683
- \u2502 4. Only create new if nothing suitable exists \u2502
2684
- \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518
2685
- \`\`\`
2686
-
2687
- ## Before Writing New Code
2688
-
2689
- ### Search Checklist
2690
-
2691
- 1. **Search by purpose**: "format date", "validate email", "fetch user"
2692
- 2. **Search common locations**:
2693
- - \`src/utils/\` or \`lib/\`
2694
- - \`src/helpers/\`
2695
- - \`src/common/\`
2696
- - \`src/shared/\`
2697
- 3. **Search by function signature**: Similar inputs/outputs
2698
-
2699
- ### Common Duplicate Candidates
2700
-
2701
- | Category | Look For |
2702
- |----------|----------|
2703
- | Date/Time | formatDate, parseDate, isExpired, addDays |
2704
- | Validation | isEmail, isPhone, isURL, isUUID |
2705
- | Strings | slugify, truncate, capitalize, pluralize |
2706
- | API | fetchUser, createItem, handleError |
2707
- | Auth | validateToken, requireAuth, getCurrentUser |
2708
-
2709
- ## If Similar Code Exists
2710
-
2711
- ### Option 1: Reuse directly
2712
- \`\`\`typescript
2713
- // Import and use existing function
2714
- import { formatDate } from '@/utils/dates';
2715
- \`\`\`
2716
-
2717
- ### Option 2: Extend with options
2718
- \`\`\`typescript
2719
- // Add optional parameter to existing function
2720
- export function formatDate(
2721
- date: Date,
2722
- format: string = 'short',
2723
- locale?: string // NEW: added locale support
2724
- ): string { ... }
2725
- \`\`\`
2726
-
2727
- ### Option 3: Compose from existing
2728
- \`\`\`typescript
2729
- // Build on existing utilities
2730
- export function formatDateRange(start: Date, end: Date) {
2731
- return \`\${formatDate(start)} - \${formatDate(end)}\`;
2732
- }
2733
- \`\`\`
2734
-
2735
- ## File Header Pattern
2736
-
2737
- Document what each file provides:
2738
-
2739
- \`\`\`typescript
2740
- /**
2741
- * @file User validation utilities
2742
- * @description Email, phone, and identity validation functions.
2743
- *
2744
- * Key exports:
2745
- * - isEmail(email) - Validates email format
2746
- * - isPhone(phone, country?) - Validates phone with country
2747
- * - isValidUsername(username) - Checks username rules
2748
- */
2749
- \`\`\`
2750
-
2751
- ## Anti-Patterns
2752
-
2753
- - \u274C Writing date formatter without checking utils/
2754
- - \u274C Creating new API client when one exists
2755
- - \u274C Duplicating validation logic across files
2756
- - \u274C Copy-pasting functions between files
2757
- - \u274C "I'll refactor later" (you won't)
2758
- `,
2759
- isNew: true
2760
- };
2761
- }
2762
- function generateSimplicityRulesSkill() {
2763
- return {
2764
- type: "skill",
2765
- path: ".claude/skills/simplicity-rules.md",
2766
- content: `---
2767
- name: simplicity-rules
2768
- description: Enforced code complexity constraints
2769
- globs:
2770
- - "**/*.ts"
2771
- - "**/*.tsx"
2772
- - "**/*.js"
2773
- - "**/*.py"
2774
- - "**/*.go"
2775
- ---
2776
-
2777
- # Simplicity Rules
2778
-
2779
- Complexity is the enemy. Every line of code is a liability.
2780
-
2781
- ## Enforced Limits
2782
-
2783
- **CRITICAL: These limits are non-negotiable. Check and enforce for EVERY file.**
2784
-
2785
- ### Function Level
2786
-
2787
- | Constraint | Limit | Action if Exceeded |
2788
- |------------|-------|-------------------|
2789
- | Lines per function | 20 max | Decompose immediately |
2790
- | Parameters | 3 max | Use options object |
2791
- | Nesting levels | 2 max | Flatten with early returns |
2792
-
2793
- ### File Level
2794
-
2795
- | Constraint | Limit | Action if Exceeded |
2796
- |------------|-------|-------------------|
2797
- | Lines per file | 200 max | Split by responsibility |
2798
- | Functions per file | 10 max | Split into modules |
2799
-
2800
- ### Module Level
2801
-
2802
- | Constraint | Limit | Reason |
2803
- |------------|-------|--------|
2804
- | Directory nesting | 3 levels max | Flat is better |
2805
- | Circular deps | 0 | Never acceptable |
2806
-
2807
- ## Enforcement Protocol
2808
-
2809
- **Before completing ANY file:**
2810
-
2811
- \`\`\`
2812
- 1. Count total lines \u2192 if > 200, STOP and split
2813
- 2. Count functions \u2192 if > 10, STOP and split
2814
- 3. Check function length \u2192 if any > 20 lines, decompose
2815
- 4. Check parameters \u2192 if any > 3, refactor to options object
2816
- \`\`\`
2817
-
2818
- ## Violation Response
2819
-
2820
- When limits are exceeded:
2821
-
2822
- \`\`\`
2823
- \u26A0\uFE0F FILE SIZE VIOLATION DETECTED
2824
-
2825
- [filename] has [X] lines (limit: 200)
2826
-
2827
- Splitting into:
2828
- - [filename-a].ts - [responsibility A]
2829
- - [filename-b].ts - [responsibility B]
2830
- \`\`\`
2831
-
2832
- **Never defer refactoring.** Fix violations immediately.
2833
-
2834
- ## Decomposition Patterns
2835
-
2836
- ### Long Function \u2192 Multiple Functions
2837
-
2838
- \`\`\`typescript
2839
- // BEFORE: 40 lines
2840
- function processOrder(order) {
2841
- // validate... 10 lines
2842
- // calculate totals... 15 lines
2843
- // apply discounts... 10 lines
2844
- // save... 5 lines
2845
- }
2846
-
2847
- // AFTER: 4 functions, each < 15 lines
2848
- function processOrder(order) {
2849
- validateOrder(order);
2850
- const totals = calculateTotals(order);
2851
- const finalPrice = applyDiscounts(totals, order.coupons);
2852
- return saveOrder({ ...order, finalPrice });
2853
- }
2854
- \`\`\`
2855
-
2856
- ### Many Parameters \u2192 Options Object
2857
-
2858
- \`\`\`typescript
2859
- // BEFORE: 6 parameters
2860
- function createUser(name, email, password, role, team, settings) { }
2861
-
2862
- // AFTER: 1 options object
2863
- interface CreateUserOptions {
2864
- name: string;
2865
- email: string;
2866
- password: string;
2867
- role?: string;
2868
- team?: string;
2869
- settings?: UserSettings;
2870
- }
2871
- function createUser(options: CreateUserOptions) { }
2872
- \`\`\`
2873
-
2874
- ### Deep Nesting \u2192 Early Returns
2875
-
2876
- \`\`\`typescript
2877
- // BEFORE: 4 levels deep
2878
- function process(data) {
2879
- if (data) {
2880
- if (data.valid) {
2881
- if (data.items) {
2882
- for (const item of data.items) {
2883
- // actual logic here
2884
- }
2885
- }
2886
- }
2887
- }
2888
- }
2889
-
2890
- // AFTER: 1 level deep
2891
- function process(data) {
2892
- if (!data?.valid || !data.items) return;
2893
-
2894
- for (const item of data.items) {
2895
- // actual logic here
2896
- }
2897
- }
2898
- \`\`\`
2899
-
2900
- ## Anti-Patterns
2901
-
2902
- - \u274C God objects/files (do everything)
2903
- - \u274C "Just one more line" (compound violations)
2904
- - \u274C "I'll split it later" (you won't)
2905
- - \u274C Deep inheritance hierarchies
2906
- - \u274C Complex conditionals without extraction
2907
- `,
2908
- isNew: true
2909
- };
2910
- }
2911
- function generateSecuritySkill(stack) {
2912
- const isJS = stack.languages.includes("typescript") || stack.languages.includes("javascript");
2913
- const isPython = stack.languages.includes("python");
2914
- return {
2915
- type: "skill",
2916
- path: ".claude/skills/security.md",
2917
- content: `---
2918
- name: security
2919
- description: Security best practices, secrets management, OWASP patterns
2920
- globs:
2921
- - "**/*"
2922
- ---
2923
-
2924
- # Security Best Practices
2925
-
2926
- Security is not optional. Every project must pass security checks.
2927
-
2928
- ## Required .gitignore Entries
2929
-
2930
- **NEVER commit these:**
2931
-
2932
- \`\`\`gitignore
2933
- # Environment files
2934
- .env
2935
- .env.*
2936
- !.env.example
2937
-
2938
- # Secrets and credentials
2939
- *.pem
2940
- *.key
2941
- *.p12
2942
- credentials.json
2943
- secrets.json
2944
- *-credentials.json
2945
- service-account*.json
2946
-
2947
- # IDE secrets
2948
- .idea/
2949
- .vscode/settings.json
2950
- \`\`\`
2951
-
2952
- ## Environment Variables
2953
-
2954
- ### Create .env.example
2955
-
2956
- Document required vars without values:
2957
-
2958
- \`\`\`bash
2959
- # Server-side only (never expose to client)
2960
- DATABASE_URL=
2961
- API_SECRET_KEY=
2962
- ANTHROPIC_API_KEY=
2963
-
2964
- # Client-side safe (public, non-sensitive)
2965
- ${isJS ? "VITE_API_URL=\nNEXT_PUBLIC_SITE_URL=" : "API_BASE_URL="}
2966
- \`\`\`
2967
-
2968
- ${isJS ? `### Frontend Exposure Rules
2969
-
2970
- | Framework | Client-Exposed Prefix | Server-Only |
2971
- |-----------|----------------------|-------------|
2972
- | Vite | \`VITE_*\` | No prefix |
2973
- | Next.js | \`NEXT_PUBLIC_*\` | No prefix |
2974
- | CRA | \`REACT_APP_*\` | N/A |
2975
-
2976
- **CRITICAL:** Never put secrets in client-exposed env vars!
2977
-
2978
- \`\`\`typescript
2979
- // \u274C WRONG - Secret exposed to browser
2980
- const key = import.meta.env.VITE_API_SECRET;
2981
-
2982
- // \u2705 CORRECT - Secret stays server-side
2983
- const key = process.env.API_SECRET; // in API route only
2984
- \`\`\`
2985
- ` : ""}
2986
-
2987
- ### Validate at Startup
2988
-
2989
- ${isJS ? `\`\`\`typescript
2990
- import { z } from 'zod';
2991
-
2992
- const envSchema = z.object({
2993
- DATABASE_URL: z.string().url(),
2994
- API_SECRET_KEY: z.string().min(32),
2995
- NODE_ENV: z.enum(['development', 'production', 'test']),
2996
- });
2997
-
2998
- export const env = envSchema.parse(process.env);
2999
- \`\`\`` : ""}
3000
- ${isPython ? `\`\`\`python
3001
- from pydantic_settings import BaseSettings
3002
-
3003
- class Settings(BaseSettings):
3004
- database_url: str
3005
- api_secret_key: str
3006
- environment: str = "development"
3007
-
3008
- class Config:
3009
- env_file = ".env"
3010
-
3011
- settings = Settings()
3012
- \`\`\`` : ""}
3013
-
3014
- ## OWASP Top 10 Checklist
3015
-
3016
- | Vulnerability | Prevention |
3017
- |---------------|------------|
3018
- | Injection (SQL, NoSQL, Command) | Parameterized queries, input validation |
3019
- | Broken Auth | Secure session management, MFA |
3020
- | Sensitive Data Exposure | Encryption at rest and in transit |
3021
- | XXE | Disable external entity processing |
3022
- | Broken Access Control | Verify permissions on every request |
3023
- | Security Misconfiguration | Secure defaults, minimal permissions |
3024
- | XSS | Output encoding, CSP headers |
3025
- | Insecure Deserialization | Validate all serialized data |
3026
- | Using Vulnerable Components | Keep dependencies updated |
3027
- | Insufficient Logging | Log security events, monitor |
3028
-
3029
- ## Input Validation
3030
-
3031
- \`\`\`
3032
- RULE: Never trust user input. Validate everything.
3033
-
3034
- - Validate type, length, format, range
3035
- - Sanitize before storage
3036
- - Encode before output
3037
- - Use allowlists over denylists
3038
- \`\`\`
3039
-
3040
- ## Secrets Detection
3041
-
3042
- Before committing, check for:
3043
-
3044
- - API keys (usually 32+ chars, specific patterns)
3045
- - Passwords in code
3046
- - Connection strings with credentials
3047
- - Private keys (BEGIN RSA/EC/PRIVATE KEY)
3048
- - Tokens (jwt, bearer, oauth)
3049
-
3050
- ## Security Review Checklist
3051
-
3052
- Before PR merge:
3053
-
3054
- - [ ] No secrets in code or config
3055
- - [ ] Input validation on all user data
3056
- - [ ] Output encoding where displayed
3057
- - [ ] Authentication checked on protected routes
3058
- - [ ] Authorization verified for resources
3059
- - [ ] Dependencies scanned for vulnerabilities
3060
- - [ ] Error messages don't leak internals
3061
- `,
3062
- isNew: true
3063
- };
3064
- }
3065
- function generateAgents(stack) {
3066
- const artifacts = [];
3067
- artifacts.push(generateCodeReviewerAgent(stack));
3068
- artifacts.push(generateTestWriterAgent(stack));
3069
- return artifacts;
3070
- }
3071
- function generateCodeReviewerAgent(stack) {
3072
- const lintCommand = getLintCommand(stack);
3073
- return {
3074
- type: "agent",
3075
- path: ".claude/agents/code-reviewer.md",
3076
- content: `---
3077
- name: code-reviewer
3078
- description: Reviews code for quality, security issues, and best practices
3079
- tools: Read, Grep, Glob${lintCommand ? `, Bash(${lintCommand})` : ""}
3080
- disallowedTools: Write, Edit
3081
- model: sonnet
3082
- ---
3083
-
3084
- You are a senior code reviewer with expertise in security and performance.
3085
-
3086
- ## Code Style Reference
3087
-
3088
- Read these files to understand project conventions:
3089
- ${stack.linter === "eslint" ? "- `eslint.config.js` or `.eslintrc.*`" : ""}
3090
- ${stack.formatter === "prettier" ? "- `.prettierrc`" : ""}
3091
- ${stack.languages.includes("typescript") ? "- `tsconfig.json`" : ""}
3092
- ${stack.languages.includes("python") ? "- `pyproject.toml` or `setup.cfg`" : ""}
3093
-
3094
- ${lintCommand ? `Run \`${lintCommand}\` to check violations programmatically.` : ""}
3095
-
3096
- ## Review Process
3097
-
3098
- 1. Run \`git diff\` to identify changed files
3099
- 2. Analyze each change for:
3100
- - Security vulnerabilities (OWASP Top 10)
3101
- - Performance issues
3102
- - Code style violations
3103
- - Missing error handling
3104
- - Test coverage gaps
3105
-
3106
- ## Output Format
3107
-
3108
- For each finding:
3109
-
3110
- - **Critical**: Must fix before merge
3111
- - **Warning**: Should address
3112
- - **Suggestion**: Consider improving
3113
-
3114
- Include file:line references for each issue.
3115
- `,
3116
- isNew: true
3117
- };
3118
- }
3119
- function generateTestWriterAgent(stack) {
3120
- const testCommand = getTestCommand(stack);
3121
- return {
3122
- type: "agent",
3123
- path: ".claude/agents/test-writer.md",
3124
- content: `---
3125
- name: test-writer
3126
- description: Generates comprehensive tests for code
3127
- tools: Read, Grep, Glob, Write, Edit, Bash(${testCommand})
3128
- model: sonnet
3129
- ---
3130
-
3131
- You are a testing expert who writes thorough, maintainable tests.
3132
-
3133
- ## Testing Framework
3134
-
3135
- This project uses: **${stack.testingFramework || "unknown"}**
3136
-
3137
- ## Your Process
3138
-
3139
- 1. Read the code to be tested
3140
- 2. Identify test cases:
3141
- - Happy path scenarios
3142
- - Edge cases
3143
- - Error conditions
3144
- - Boundary values
3145
- 3. Write tests following project patterns
3146
- 4. Run tests to verify they pass
3147
-
3148
- ## Test Structure
3149
-
3150
- Follow the AAA pattern:
3151
- - **Arrange**: Set up test data
3152
- - **Act**: Execute the code
3153
- - **Assert**: Verify results
3154
-
3155
- ## Guidelines
3156
-
3157
- - One assertion focus per test
3158
- - Descriptive test names
3159
- - Mock external dependencies
3160
- - Don't test implementation details
3161
- - Aim for behavior coverage
3162
-
3163
- ## Run Tests
3164
-
3165
- \`\`\`bash
3166
- ${testCommand}
3167
- \`\`\`
3168
- `,
3169
- isNew: true
3170
- };
3171
- }
3172
- function getLintCommand(stack) {
3173
- switch (stack.linter) {
3174
- case "eslint":
3175
- return `${stack.packageManager === "bun" ? "bun" : "npx"} eslint .`;
3176
- case "biome":
3177
- return `${stack.packageManager === "bun" ? "bun" : "npx"} biome check .`;
3178
- case "ruff":
3179
- return "ruff check .";
3180
- default:
3181
- return "";
3182
- }
3183
- }
3184
- function getTestCommand(stack) {
3185
- switch (stack.testingFramework) {
3186
- case "vitest":
3187
- return `${stack.packageManager || "npm"} ${stack.packageManager === "npm" ? "run " : ""}test`;
3188
- case "jest":
3189
- return `${stack.packageManager || "npm"} ${stack.packageManager === "npm" ? "run " : ""}test`;
3190
- case "bun-test":
3191
- return "bun test";
3192
- case "pytest":
3193
- return "pytest";
3194
- case "go-test":
3195
- return "go test ./...";
3196
- case "rust-test":
3197
- return "cargo test";
3198
- default:
3199
- return `${stack.packageManager || "npm"} test`;
3200
- }
3201
- }
3202
- function generateRules(stack) {
3203
- const artifacts = [];
3204
- if (stack.languages.includes("typescript")) {
3205
- artifacts.push(generateTypeScriptRules());
3206
- }
3207
- if (stack.languages.includes("python")) {
3208
- artifacts.push(generatePythonRules());
3209
- }
3210
- artifacts.push(generateCodeStyleRule(stack));
3211
- return artifacts;
3212
- }
3213
- function generateTypeScriptRules() {
3214
- return {
3215
- type: "rule",
3216
- path: ".claude/rules/typescript.md",
3217
- content: `---
3218
- paths:
3219
- - "**/*.ts"
3220
- - "**/*.tsx"
3221
- ---
3222
-
3223
- # TypeScript Rules
3224
-
3225
- ## Type Safety
3226
-
3227
- - Avoid \`any\` - use \`unknown\` and narrow types
3228
- - Prefer interfaces for objects, types for unions/intersections
3229
- - Use strict mode (\`strict: true\` in tsconfig)
3230
- - Enable \`noUncheckedIndexedAccess\` for safer array access
3231
-
3232
- ## Patterns
3233
-
3234
- \`\`\`typescript
3235
- // Prefer
3236
- const user: User | undefined = users.find(u => u.id === id);
3237
- if (user) { /* use user */ }
3238
-
3239
- // Avoid
3240
- const user = users.find(u => u.id === id) as User;
3241
- \`\`\`
3242
-
3243
- ## Naming
3244
-
3245
- - Interfaces: PascalCase (e.g., \`UserProfile\`)
3246
- - Types: PascalCase (e.g., \`ApiResponse\`)
3247
- - Functions: camelCase (e.g., \`getUserById\`)
3248
- - Constants: SCREAMING_SNAKE_CASE for true constants
3249
-
3250
- ## Imports
3251
-
3252
- - Group imports: external, internal, relative
3253
- - Use path aliases when configured
3254
- - Prefer named exports over default exports
3255
- `,
3256
- isNew: true
3257
- };
3258
- }
3259
- function generatePythonRules() {
3260
- return {
3261
- type: "rule",
3262
- path: ".claude/rules/python.md",
3263
- content: `---
3264
- paths:
3265
- - "**/*.py"
3266
- ---
3267
-
3268
- # Python Rules
3269
-
3270
- ## Style
3271
-
3272
- - Follow PEP 8
3273
- - Use type hints for function signatures
3274
- - Docstrings for public functions (Google style)
3275
- - Max line length: 88 (Black default)
3276
-
3277
- ## Patterns
3278
-
3279
- \`\`\`python
3280
- # Prefer
3281
- def get_user(user_id: int) -> User | None:
3282
- """Fetch user by ID.
3283
-
3284
- Args:
3285
- user_id: The user's unique identifier.
3286
-
3287
- Returns:
3288
- User object if found, None otherwise.
3289
- """
3290
- return db.query(User).filter(User.id == user_id).first()
3291
-
3292
- # Avoid
3293
- def get_user(id):
3294
- return db.query(User).filter(User.id == id).first()
3295
- \`\`\`
3296
-
3297
- ## Naming
3298
-
3299
- - Functions/variables: snake_case
3300
- - Classes: PascalCase
3301
- - Constants: SCREAMING_SNAKE_CASE
3302
- - Private: _leading_underscore
3303
-
3304
- ## Imports
3305
-
3306
- \`\`\`python
3307
- # Standard library
3308
- import os
3309
- from pathlib import Path
3310
-
3311
- # Third-party
3312
- from fastapi import FastAPI
3313
- from pydantic import BaseModel
3314
-
3315
- # Local
3316
- from app.models import User
3317
- from app.services import UserService
3318
- \`\`\`
3319
- `,
3320
- isNew: true
3321
- };
3322
- }
3323
- function generateCodeStyleRule(stack) {
3324
- return {
3325
- type: "rule",
3326
- path: ".claude/rules/code-style.md",
3327
- content: `# Code Style
3328
-
3329
- ## General Principles
3330
-
3331
- 1. **Clarity over cleverness** - Code is read more than written
3332
- 2. **Consistency** - Match existing patterns in the codebase
3333
- 3. **Simplicity** - Prefer simple solutions over complex ones
3334
-
3335
- ## Formatting
3336
-
3337
- ${stack.formatter ? `This project uses **${stack.formatter}** for formatting. Run it before committing.` : "Format code consistently with the existing codebase."}
3338
-
3339
- ${stack.linter ? `This project uses **${stack.linter}** for linting. Fix all warnings.` : ""}
3340
-
3341
- ## Comments
3342
-
3343
- - Write self-documenting code first
3344
- - Comment the "why", not the "what"
3345
- - Keep comments up to date with code changes
3346
- - Use TODO/FIXME with context
3347
-
3348
- ## Error Handling
3349
-
3350
- - Handle errors at appropriate boundaries
3351
- - Provide meaningful error messages
3352
- - Log errors with context
3353
- - Don't swallow errors silently
3354
-
3355
- ## Git Commits
3356
-
3357
- - Write clear, concise commit messages
3358
- - Use conventional commits format when applicable
3359
- - Keep commits focused and atomic
3360
- `,
3361
- isNew: true
3362
- };
3363
- }
3364
- function generateCommands() {
3365
- return [
3366
- generateTaskCommand(),
3367
- generateStatusCommand(),
3368
- generateDoneCommand(),
3369
- generateAnalyzeCommand(),
3370
- generateCodeReviewCommand()
3371
- ];
3372
- }
3373
- function generateTaskCommand() {
3374
- return {
3375
- type: "command",
3376
- path: ".claude/commands/task.md",
3377
- content: `---
3378
- allowed-tools: Read, Write, Edit, Glob, Grep
3379
- argument-hint: [task description]
3380
- description: Start or switch to a new task
3381
- ---
3382
-
3383
- # Start Task
3384
-
3385
- ## Current State
3386
- !cat .claude/state/task.md 2>/dev/null || echo "No existing task"
3387
-
3388
- ## Your Task
3389
-
3390
- Start or switch to the task: **$ARGUMENTS**
3391
-
3392
- 1. Read current state from \`.claude/state/task.md\`
3393
- 2. If switching tasks, summarize previous progress
3394
- 3. Update \`.claude/state/task.md\` with:
3395
- - Status: In Progress
3396
- - Task description
3397
- - Initial context/understanding
3398
- - Planned next steps
3399
-
3400
- 4. Begin working on the task
3401
- `,
3402
- isNew: true
3403
- };
3404
- }
3405
- function generateStatusCommand() {
3406
- return {
3407
- type: "command",
3408
- path: ".claude/commands/status.md",
3409
- content: `---
3410
- allowed-tools: Read, Glob
3411
- description: Show current task and session state
3412
- ---
3413
-
3414
- # Status Check
3415
-
3416
- ## Current Task State
3417
- !cat .claude/state/task.md 2>/dev/null || echo "No task in progress"
3418
-
3419
- ## Your Response
3420
-
3421
- Provide a concise status update:
3422
-
3423
- 1. **Current Task**: What are you working on?
3424
- 2. **Progress**: What's been completed?
3425
- 3. **Blockers**: Any issues or questions?
3426
- 4. **Next Steps**: What's coming up?
3427
-
3428
- Keep it brief - this is a quick check-in.
3429
- `,
3430
- isNew: true
3431
- };
3432
- }
3433
- function generateDoneCommand() {
3434
- return {
3435
- type: "command",
3436
- path: ".claude/commands/done.md",
3437
- content: `---
3438
- allowed-tools: Read, Write, Edit, Glob, Bash(git diff), Bash(git status)
3439
- description: Mark current task complete
3440
- ---
3441
-
3442
- # Complete Task
3443
-
3444
- ## Current State
3445
- !cat .claude/state/task.md
3446
-
3447
- ## Completion Checklist
3448
-
3449
- Before marking complete, verify:
3450
-
3451
- 1. [ ] All requirements met
3452
- 2. [ ] Tests pass (if applicable)
3453
- 3. [ ] No linting errors
3454
- 4. [ ] Code reviewed for quality
3455
-
3456
- ## Your Task
3457
-
3458
- 1. Run final checks (tests, lint)
3459
- 2. Update \`.claude/state/task.md\`:
3460
- - Status: **Completed**
3461
- - Summary of what was done
3462
- - Files changed
3463
- - Any follow-up items
3464
-
3465
- 3. Show git status/diff for review
3466
- `,
3467
- isNew: true
550
+ const permissions = ["Read(**)", "Edit(**)", "Write(.claude/**)", "Bash(git:*)"];
551
+ const pkgManagers = ["npm", "yarn", "pnpm", "bun", "npx"];
552
+ for (const pm of pkgManagers) {
553
+ permissions.push(`Bash(${pm}:*)`);
554
+ }
555
+ if (stack.languages.includes("typescript") || stack.languages.includes("javascript")) {
556
+ permissions.push("Bash(node:*)", "Bash(tsc:*)");
557
+ }
558
+ if (stack.languages.includes("python")) {
559
+ permissions.push(
560
+ "Bash(python:*)",
561
+ "Bash(pip:*)",
562
+ "Bash(poetry:*)",
563
+ "Bash(pytest:*)",
564
+ "Bash(uvicorn:*)"
565
+ );
566
+ }
567
+ if (stack.languages.includes("go")) {
568
+ permissions.push("Bash(go:*)");
569
+ }
570
+ if (stack.languages.includes("rust")) {
571
+ permissions.push("Bash(cargo:*)", "Bash(rustc:*)");
572
+ }
573
+ if (stack.languages.includes("ruby")) {
574
+ permissions.push("Bash(ruby:*)", "Bash(bundle:*)", "Bash(rails:*)", "Bash(rake:*)");
575
+ }
576
+ if (stack.testingFramework) {
577
+ const testCommands = {
578
+ jest: ["jest:*"],
579
+ vitest: ["vitest:*"],
580
+ playwright: ["playwright:*"],
581
+ cypress: ["cypress:*"],
582
+ pytest: ["pytest:*"],
583
+ rspec: ["rspec:*"]
584
+ };
585
+ const cmds = testCommands[stack.testingFramework];
586
+ if (cmds) {
587
+ permissions.push(...cmds.map((c) => `Bash(${c})`));
588
+ }
589
+ }
590
+ if (stack.linter) {
591
+ permissions.push(`Bash(${stack.linter}:*)`);
592
+ }
593
+ if (stack.formatter) {
594
+ permissions.push(`Bash(${stack.formatter}:*)`);
595
+ }
596
+ permissions.push(
597
+ "Bash(ls:*)",
598
+ "Bash(mkdir:*)",
599
+ "Bash(cat:*)",
600
+ "Bash(echo:*)",
601
+ "Bash(grep:*)",
602
+ "Bash(find:*)"
603
+ );
604
+ if (stack.hasDocker) {
605
+ permissions.push("Bash(docker:*)", "Bash(docker-compose:*)");
606
+ }
607
+ const settings = {
608
+ $schema: "https://json.schemastore.org/claude-code-settings.json",
609
+ permissions: {
610
+ allow: [...new Set(permissions)]
611
+ // Deduplicate
612
+ }
3468
613
  };
3469
- }
3470
- function generateAnalyzeCommand() {
3471
614
  return {
3472
- type: "command",
3473
- path: ".claude/commands/analyze.md",
3474
- content: `---
3475
- allowed-tools: Read, Glob, Grep
3476
- argument-hint: [area to analyze]
3477
- description: Deep analysis of a specific area
3478
- ---
3479
-
3480
- # Analyze: $ARGUMENTS
3481
-
3482
- ## Analysis Scope
3483
-
3484
- Perform deep analysis of: **$ARGUMENTS**
3485
-
3486
- ## Process
3487
-
3488
- 1. **Locate relevant files** using Glob and Grep
3489
- 2. **Read and understand** the code structure
3490
- 3. **Identify patterns** and conventions
3491
- 4. **Document findings** with file:line references
3492
-
3493
- ## Output Format
3494
-
3495
- ### Overview
3496
- Brief description of what this area does.
3497
-
3498
- ### Key Files
3499
- - \`path/to/file.ts:10\` - Purpose
3500
-
3501
- ### Patterns Found
3502
- - Pattern 1: Description
3503
- - Pattern 2: Description
3504
-
3505
- ### Dependencies
3506
- What this area depends on and what depends on it.
3507
-
3508
- ### Recommendations
3509
- Any improvements or concerns noted.
3510
- `,
3511
- isNew: true
615
+ path: ".claude/settings.json",
616
+ content: JSON.stringify(settings, null, 2)
3512
617
  };
3513
618
  }
3514
- function generateCodeReviewCommand() {
3515
- return {
3516
- type: "command",
3517
- path: ".claude/commands/code-review.md",
3518
- content: `---
3519
- allowed-tools: Read, Glob, Grep, Bash(git diff), Bash(git status), Bash(git log)
3520
- description: Review code changes for quality, security, and best practices
3521
- ---
3522
-
3523
- # Code Review
3524
-
3525
- ## Changes to Review
3526
-
3527
- !git diff --stat HEAD~1 2>/dev/null || git diff --stat
3528
-
3529
- ## Review Process
3530
-
3531
- Analyze all changes for:
3532
-
3533
- ### 1. Security (Critical)
3534
- - [ ] No secrets/credentials in code
3535
- - [ ] Input validation present
3536
- - [ ] Output encoding where needed
3537
- - [ ] Auth/authz checks on protected routes
3538
-
3539
- ### 2. Quality
3540
- - [ ] Functions \u2264 20 lines
3541
- - [ ] Files \u2264 200 lines
3542
- - [ ] No code duplication
3543
- - [ ] Clear naming
3544
- - [ ] Proper error handling
3545
-
3546
- ### 3. Testing
3547
- - [ ] Tests exist for new code
3548
- - [ ] Edge cases covered
3549
- - [ ] Tests are meaningful (not just for coverage)
3550
-
3551
- ### 4. Style
3552
- - [ ] Matches existing patterns
3553
- - [ ] Consistent formatting
3554
- - [ ] No commented-out code
3555
-
3556
- ## Output Format
3557
-
3558
- For each finding, include file:line reference:
3559
-
3560
- ### Critical (Must Fix)
3561
- Issues that block merge
3562
-
3563
- ### Warning (Should Fix)
3564
- Issues that should be addressed
3565
-
3566
- ### Suggestion (Consider)
3567
- Optional improvements
3568
-
3569
- ## Summary
3570
-
3571
- Provide:
3572
- 1. Overall assessment (Ready / Changes Needed / Not Ready)
3573
- 2. Count of findings by severity
3574
- 3. Top priorities to address
3575
- `,
3576
- isNew: true
3577
- };
619
+ function writeSettings(rootDir, stack) {
620
+ const { path: settingsPath, content } = generateSettings(stack);
621
+ const fullPath = path2.join(rootDir, settingsPath);
622
+ const dir = path2.dirname(fullPath);
623
+ fs2.mkdirSync(dir, { recursive: true });
624
+ fs2.writeFileSync(fullPath, content);
3578
625
  }
3579
626
 
3580
627
  // src/prompt.ts
3581
628
  function getAnalysisPrompt(projectInfo) {
3582
629
  const context = buildContextSection(projectInfo);
630
+ const templateVars = buildTemplateVariables(projectInfo);
3583
631
  return `${ANALYSIS_PROMPT}
3584
632
 
633
+ ${SKILLS_PROMPT}
634
+
635
+ ${AGENTS_PROMPT}
636
+
637
+ ${RULES_PROMPT}
638
+
639
+ ${COMMANDS_PROMPT}
640
+
3585
641
  ---
3586
642
 
3587
643
  ## Pre-detected Context
@@ -3591,6 +647,10 @@ Use this as a starting point - verify and expand on it during your analysis.
3591
647
 
3592
648
  ${context}
3593
649
 
650
+ ### Template Variables (use these in generated files)
651
+
652
+ ${templateVars}
653
+
3594
654
  ---
3595
655
 
3596
656
  ## Execute Now
@@ -3600,9 +660,14 @@ ${context}
3600
660
  3. Execute Phase 2 - generate the CLAUDE.md using only discovered information
3601
661
  4. Execute Phase 3 - verify quality before writing
3602
662
  5. Use the Write tool to create \`.claude/CLAUDE.md\` with the final content
3603
- 6. Output a brief summary of what was generated and any gaps found
663
+ 6. Execute Phase 4 - generate ALL skill files
664
+ 7. Execute Phase 5 - generate agent files
665
+ 8. Execute Phase 6 - generate rule files
666
+ 9. Execute Phase 7 - generate command files
667
+ 10. Output a brief summary of what was generated and any gaps found
3604
668
 
3605
- Do NOT output the full CLAUDE.md to stdout. Write it to disk using the Write tool.`;
669
+ Do NOT output file contents to stdout. Write all files to disk using the Write tool.
670
+ Generate ALL files in a single pass \u2014 do not stop after CLAUDE.md.`;
3606
671
  }
3607
672
  function buildContextSection(projectInfo) {
3608
673
  const { name, description, techStack, fileCount } = projectInfo;
@@ -3654,11 +719,88 @@ function buildContextSection(projectInfo) {
3654
719
  }
3655
720
  return lines.join("\n");
3656
721
  }
722
+ function buildTemplateVariables(projectInfo) {
723
+ const { techStack } = projectInfo;
724
+ const vars = [];
725
+ vars.push(`- **detected_languages**: ${techStack.languages.join(", ") || "none detected"}`);
726
+ vars.push(`- **detected_frameworks**: ${techStack.frameworks.join(", ") || "none detected"}`);
727
+ vars.push(`- **detected_testing_framework**: ${techStack.testingFramework || "none detected"}`);
728
+ vars.push(`- **test_command**: ${getTestCommand(techStack)}`);
729
+ vars.push(`- **lint_command**: ${getLintCommand(techStack)}`);
730
+ vars.push(`- **detected_linter**: ${techStack.linter || "none detected"}`);
731
+ vars.push(`- **detected_formatter**: ${techStack.formatter || "none detected"}`);
732
+ vars.push(`- **source_glob_patterns**: ${getSourceGlobs(techStack)}`);
733
+ return vars.join("\n");
734
+ }
735
+ function getTestCommand(stack) {
736
+ if (stack.testingFramework) {
737
+ const commands = {
738
+ jest: "npx jest",
739
+ vitest: "npx vitest",
740
+ "bun-test": "bun test",
741
+ pytest: "pytest",
742
+ "go-test": "go test ./...",
743
+ "rust-test": "cargo test",
744
+ rspec: "bundle exec rspec",
745
+ junit: "mvn test",
746
+ mocha: "npx mocha",
747
+ playwright: "npx playwright test",
748
+ cypress: "npx cypress run",
749
+ unittest: "python -m unittest discover"
750
+ };
751
+ return commands[stack.testingFramework] || stack.testingFramework;
752
+ }
753
+ if (stack.packageManager === "bun") return "bun test";
754
+ if (stack.packageManager === "cargo") return "cargo test";
755
+ if (stack.packageManager === "go") return "go test ./...";
756
+ if (stack.packageManager) return `${stack.packageManager} test`;
757
+ return "npm test";
758
+ }
759
+ function getLintCommand(stack) {
760
+ if (stack.linter) {
761
+ const commands = {
762
+ eslint: "npx eslint .",
763
+ biome: "npx biome check .",
764
+ pylint: "pylint",
765
+ flake8: "flake8",
766
+ ruff: "ruff check .",
767
+ "golangci-lint": "golangci-lint run",
768
+ clippy: "cargo clippy",
769
+ rubocop: "bundle exec rubocop"
770
+ };
771
+ return commands[stack.linter] || stack.linter;
772
+ }
773
+ return "no linter detected";
774
+ }
775
+ function getSourceGlobs(stack) {
776
+ const globs = [];
777
+ for (const lang of stack.languages) {
778
+ const langGlobs = {
779
+ typescript: ["**/*.ts", "**/*.tsx"],
780
+ javascript: ["**/*.js", "**/*.jsx"],
781
+ python: ["**/*.py"],
782
+ go: ["**/*.go"],
783
+ rust: ["**/*.rs"],
784
+ java: ["**/*.java"],
785
+ ruby: ["**/*.rb"],
786
+ csharp: ["**/*.cs"],
787
+ swift: ["**/*.swift"],
788
+ kotlin: ["**/*.kt", "**/*.kts"],
789
+ php: ["**/*.php"],
790
+ cpp: ["**/*.cpp", "**/*.hpp", "**/*.h"]
791
+ };
792
+ const patterns = langGlobs[lang];
793
+ if (patterns) {
794
+ globs.push(...patterns);
795
+ }
796
+ }
797
+ return globs.length > 0 ? globs.join(", ") : "**/*";
798
+ }
3657
799
  var ANALYSIS_PROMPT = `You are a senior software architect performing a comprehensive codebase analysis.
3658
- Your goal is to generate a professional \`.claude/CLAUDE.md\` file that gives Claude
800
+ Your goal is to generate ALL \`.claude/\` configuration files that give Claude
3659
801
  complete context to work effectively in this project.
3660
802
 
3661
- **This is NOT a generic template.** Every section must contain information specific to THIS
803
+ **This is NOT a generic template.** Every file must contain information specific to THIS
3662
804
  project, discovered through actual file reading and analysis. If you cannot determine
3663
805
  something, omit that section entirely - do not fill in generic boilerplate.
3664
806
 
@@ -3667,7 +809,7 @@ something, omit that section entirely - do not fill in generic boilerplate.
3667
809
  ## Phase 1: Discovery (Read Before You Write)
3668
810
 
3669
811
  Perform these analysis steps IN ORDER. Do not skip any step. Do not start writing
3670
- the CLAUDE.md until all discovery is complete.
812
+ any files until all discovery is complete.
3671
813
 
3672
814
  ### 1.1 Project Identity
3673
815
 
@@ -3901,6 +1043,264 @@ Before writing the CLAUDE.md, verify:
3901
1043
  5. **Keep it maintainable.** Don't include metrics that go stale quickly.
3902
1044
 
3903
1045
  6. **Respect existing CLAUDE.md.** If one exists, read it first and preserve manually-added sections.`;
1046
+ var SKILLS_PROMPT = `---
1047
+
1048
+ ## Phase 4: Generate Skills
1049
+
1050
+ Write each skill file to \`.claude/skills/\` using the Write tool. Every skill must have
1051
+ YAML frontmatter with \`name\`, \`description\`, and optionally \`globs\` for file matching.
1052
+
1053
+ **Tailor ALL skills to this specific project** \u2014 use the actual test command, lint command,
1054
+ file patterns, and conventions discovered during Phase 1.
1055
+
1056
+ ### 4.1 Core Skills (ALWAYS generate all 8)
1057
+
1058
+ **\`.claude/skills/pattern-discovery.md\`**
1059
+ - Name: pattern-discovery
1060
+ - Description: Analyze codebase to discover and document patterns
1061
+ - Content: How to search for patterns in THIS project's structure. Include the actual source directories, key file patterns, and import conventions found.
1062
+
1063
+ **\`.claude/skills/systematic-debugging.md\`**
1064
+ - Name: systematic-debugging
1065
+ - Description: 4-phase debugging methodology \u2014 Reproduce, Locate, Diagnose, Fix
1066
+ - Content: Tailor reproduction steps to the project's actual test runner and dev server commands. Include how to use the project's logging/debugging setup.
1067
+
1068
+ **\`.claude/skills/testing-methodology.md\`**
1069
+ - Name: testing-methodology
1070
+ - Description: AAA testing pattern with project-specific framework syntax
1071
+ - Content: Use the project's actual testing framework syntax. Include real examples of test patterns found in the codebase (describe/it blocks, pytest fixtures, etc.). Reference the actual test command. Include mocking/stubbing patterns specific to the stack.
1072
+
1073
+ **\`.claude/skills/iterative-development.md\`**
1074
+ - Name: iterative-development
1075
+ - Description: TDD workflow with project-specific test and lint commands
1076
+ - Content: The TDD loop using the actual test command and lint command. Include the project's verification steps (typecheck, build, etc.).
1077
+
1078
+ **\`.claude/skills/commit-hygiene.md\`**
1079
+ - Name: commit-hygiene
1080
+ - Description: Atomic commits, conventional format, size thresholds
1081
+ - Content: Size thresholds (\xB1300 lines per commit), when-to-commit triggers, conventional commit format. If the project uses commitlint or similar, reference its config.
1082
+
1083
+ **\`.claude/skills/code-deduplication.md\`**
1084
+ - Name: code-deduplication
1085
+ - Description: Check-before-write principle and search checklist
1086
+ - Content: Search existing code before writing new code. Include project-specific glob patterns for source files. Reference the actual directory structure for where to look.
1087
+
1088
+ **\`.claude/skills/simplicity-rules.md\`**
1089
+ - Name: simplicity-rules
1090
+ - Description: Function and file size limits, decomposition patterns
1091
+ - Content: Function length limits (40 lines), file limits (300 lines), cyclomatic complexity. Decomposition patterns appropriate for the project's architecture style.
1092
+
1093
+ **\`.claude/skills/security.md\`**
1094
+ - Name: security
1095
+ - Description: Security patterns and secrets management for this stack
1096
+ - Content: .gitignore entries appropriate for the detected stack. Environment variable handling patterns. OWASP checklist items relevant to the detected framework. Include actual secrets patterns to watch for (API keys, database URLs, etc.).
1097
+
1098
+ ### 4.2 Framework-Specific Skills (ONLY if detected)
1099
+
1100
+ Generate the matching skill ONLY if the framework was detected in the tech stack:
1101
+
1102
+ - **Next.js detected** \u2192 Write \`.claude/skills/nextjs-patterns.md\` \u2014 App Router patterns, Server/Client Components, data fetching (fetch, server actions), middleware, image optimization, caching strategies. Use patterns from the actual codebase.
1103
+
1104
+ - **React (without Next.js) detected** \u2192 Write \`.claude/skills/react-components.md\` \u2014 Hooks patterns, component composition, state management (whatever is used), performance (memo, useMemo, useCallback), error boundaries.
1105
+
1106
+ - **FastAPI detected** \u2192 Write \`.claude/skills/fastapi-patterns.md\` \u2014 Router organization, dependency injection, Pydantic models, async/await patterns, middleware, exception handlers.
1107
+
1108
+ - **NestJS detected** \u2192 Write \`.claude/skills/nestjs-patterns.md\` \u2014 Module structure, controllers, services, decorators, pipes, guards, interceptors, custom providers.
1109
+
1110
+ - **SwiftUI detected** \u2192 Write \`.claude/skills/swiftui-patterns.md\` \u2014 Property wrappers (@State, @Binding, @StateObject, @EnvironmentObject), MVVM, navigation (NavigationStack/NavigationSplitView), previews, accessibility.
1111
+
1112
+ - **UIKit detected** \u2192 Write \`.claude/skills/uikit-patterns.md\` \u2014 View controller lifecycle, Auto Layout (programmatic and storyboard), delegates/datasources, MVC, coordinator pattern.
1113
+
1114
+ - **Vapor detected** \u2192 Write \`.claude/skills/vapor-patterns.md\` \u2014 Routes, middleware, Fluent ORM, async controllers, content negotiation, validation.
1115
+
1116
+ - **Jetpack Compose detected** \u2192 Write \`.claude/skills/compose-patterns.md\` \u2014 @Composable functions, remember/rememberSaveable, ViewModel integration, navigation, side effects (LaunchedEffect, DisposableEffect), theming.
1117
+
1118
+ - **Android Views detected** \u2192 Write \`.claude/skills/android-views-patterns.md\` \u2014 Activities, Fragments, XML layouts, ViewBinding, RecyclerView, lifecycle awareness.
1119
+
1120
+ - **Vue/Nuxt detected** \u2192 Write \`.claude/skills/vue-patterns.md\` \u2014 Composition API (ref, reactive, computed), composables, Pinia stores, routing, auto-imports.
1121
+
1122
+ - **Django detected** \u2192 Write \`.claude/skills/django-patterns.md\` \u2014 Models, views (class-based and function-based), serializers, middleware, admin customization, signals.
1123
+
1124
+ - **Rails detected** \u2192 Write \`.claude/skills/rails-patterns.md\` \u2014 MVC, ActiveRecord, concerns, service objects, jobs, mailers, strong parameters.
1125
+
1126
+ - **Spring detected** \u2192 Write \`.claude/skills/spring-patterns.md\` \u2014 Beans, controllers, services, repositories, AOP, dependency injection, configuration properties.`;
1127
+ var AGENTS_PROMPT = `---
1128
+
1129
+ ## Phase 5: Generate Agents
1130
+
1131
+ Write 2 agent files to \`.claude/agents/\`.
1132
+
1133
+ ### \`.claude/agents/code-reviewer.md\`
1134
+
1135
+ YAML frontmatter:
1136
+ \`\`\`yaml
1137
+ ---
1138
+ name: code-reviewer
1139
+ description: Reviews code for quality, security issues, and best practices
1140
+ tools:
1141
+ - Read
1142
+ - Grep
1143
+ - Glob
1144
+ - "Bash({lint_command})"
1145
+ disallowed_tools:
1146
+ - Write
1147
+ - Edit
1148
+ model: sonnet
1149
+ ---
1150
+ \`\`\`
1151
+
1152
+ Body content \u2014 instructions for the code reviewer agent:
1153
+ - Check naming conventions match project patterns
1154
+ - Verify error handling follows project style
1155
+ - Look for security issues (injection, XSS, auth bypass, secrets exposure)
1156
+ - Verify test coverage for changed code
1157
+ - Check import organization
1158
+ - Flag code duplication
1159
+ - Use the project's actual linter for automated checks
1160
+
1161
+ ### \`.claude/agents/test-writer.md\`
1162
+
1163
+ YAML frontmatter:
1164
+ \`\`\`yaml
1165
+ ---
1166
+ name: test-writer
1167
+ description: Generates comprehensive tests for code
1168
+ tools:
1169
+ - Read
1170
+ - Grep
1171
+ - Glob
1172
+ - Write
1173
+ - Edit
1174
+ - "Bash({test_command})"
1175
+ model: sonnet
1176
+ ---
1177
+ \`\`\`
1178
+
1179
+ Body content \u2014 instructions for the test writer agent:
1180
+ - Follow the AAA pattern (Arrange, Act, Assert)
1181
+ - Use the project's actual testing framework and syntax
1182
+ - Follow existing test file naming conventions
1183
+ - Include edge cases: empty inputs, nulls, errors, boundaries
1184
+ - Mock external dependencies following project patterns
1185
+ - Run tests after writing to verify they pass`;
1186
+ var RULES_PROMPT = `---
1187
+
1188
+ ## Phase 6: Generate Rules
1189
+
1190
+ Write rule files to \`.claude/rules/\`. Each rule file needs YAML frontmatter.
1191
+
1192
+ ### Always Generate:
1193
+
1194
+ **\`.claude/rules/code-style.md\`** (no \`paths\` \u2014 applies to all files)
1195
+
1196
+ Content based on what was discovered in Phase 1:
1197
+ - Which formatter/linter to use and how (include actual commands)
1198
+ - Comment style: "why" not "what", keep comments current
1199
+ - Error handling patterns specific to this project
1200
+ - Git commit message conventions (conventional commits if commitlint is configured)
1201
+ - Import ordering conventions found in the codebase
1202
+
1203
+ ### Conditional Rules (generate ONLY if the language was detected):
1204
+
1205
+ **TypeScript detected** \u2192 Write \`.claude/rules/typescript.md\`
1206
+ \`\`\`yaml
1207
+ ---
1208
+ paths: ["**/*.ts", "**/*.tsx"]
1209
+ ---
1210
+ \`\`\`
1211
+ Content: strict mode settings, type annotation preferences (interface vs type), import style (type imports), null handling, generic patterns found in the codebase.
1212
+
1213
+ **Python detected** \u2192 Write \`.claude/rules/python.md\`
1214
+ \`\`\`yaml
1215
+ ---
1216
+ paths: ["**/*.py"]
1217
+ ---
1218
+ \`\`\`
1219
+ Content: type hint style, docstring format (Google/NumPy/Sphinx), import ordering (isort), virtual environment conventions, Python version requirements.
1220
+
1221
+ **Swift detected** \u2192 Write \`.claude/rules/swift.md\`
1222
+ \`\`\`yaml
1223
+ ---
1224
+ paths: ["**/*.swift"]
1225
+ ---
1226
+ \`\`\`
1227
+ Content: access control patterns, optional handling, protocol-oriented patterns, SwiftLint rules if configured.
1228
+
1229
+ **Go detected** \u2192 Write \`.claude/rules/go.md\`
1230
+ \`\`\`yaml
1231
+ ---
1232
+ paths: ["**/*.go"]
1233
+ ---
1234
+ \`\`\`
1235
+ Content: error handling patterns (wrap errors), interface placement, package naming, go fmt/vet/lint conventions.
1236
+
1237
+ **Rust detected** \u2192 Write \`.claude/rules/rust.md\`
1238
+ \`\`\`yaml
1239
+ ---
1240
+ paths: ["**/*.rs"]
1241
+ ---
1242
+ \`\`\`
1243
+ Content: ownership/borrowing patterns, error handling (Result/Option, thiserror/anyhow), trait patterns, clippy lints.`;
1244
+ var COMMANDS_PROMPT = `---
1245
+
1246
+ ## Phase 7: Generate Commands
1247
+
1248
+ Write 5 command files to \`.claude/commands/\`. Each needs YAML frontmatter with
1249
+ \`allowed-tools\`, \`description\`, and optionally \`argument-hint\`.
1250
+
1251
+ ### \`.claude/commands/task.md\`
1252
+ \`\`\`yaml
1253
+ ---
1254
+ allowed-tools: ["Read", "Write", "Edit", "Glob"]
1255
+ description: "Start or switch to a new task"
1256
+ argument-hint: "<task description>"
1257
+ ---
1258
+ \`\`\`
1259
+ Body: Instructions to read current \`.claude/state/task.md\`, update status to "In Progress",
1260
+ record the task description and timestamp. If starting a new task, archive the previous one.
1261
+
1262
+ ### \`.claude/commands/status.md\`
1263
+ \`\`\`yaml
1264
+ ---
1265
+ allowed-tools: ["Read", "Glob", "Grep", "Bash(git status)", "Bash(git diff --stat)"]
1266
+ description: "Show current task and session state"
1267
+ ---
1268
+ \`\`\`
1269
+ Body: Read \`.claude/state/task.md\`, show git status, list recently modified files,
1270
+ summarize current state in a concise format.
1271
+
1272
+ ### \`.claude/commands/done.md\`
1273
+ \`\`\`yaml
1274
+ ---
1275
+ allowed-tools: ["Read", "Write", "Edit", "Glob", "Grep", "Bash(git:*)", "Bash({test_command})", "Bash({lint_command})"]
1276
+ description: "Mark current task complete"
1277
+ ---
1278
+ \`\`\`
1279
+ Body: Run tests and lint checks. If they pass, update \`.claude/state/task.md\`
1280
+ status to "Done". Show a summary of what was accomplished. Suggest next steps.
1281
+
1282
+ ### \`.claude/commands/analyze.md\`
1283
+ \`\`\`yaml
1284
+ ---
1285
+ allowed-tools: ["Read", "Glob", "Grep"]
1286
+ description: "Deep analysis of a specific area"
1287
+ argument-hint: "<area or file path>"
1288
+ ---
1289
+ \`\`\`
1290
+ Body: Perform thorough analysis of the specified area. Read relevant files,
1291
+ trace data flow, identify patterns, document findings. Output a structured report.
1292
+
1293
+ ### \`.claude/commands/code-review.md\`
1294
+ \`\`\`yaml
1295
+ ---
1296
+ allowed-tools: ["Read", "Glob", "Grep", "Bash(git diff)", "Bash(git diff --cached)", "Bash({lint_command})"]
1297
+ description: "Review code changes for quality and security"
1298
+ ---
1299
+ \`\`\`
1300
+ Body: Review staged and unstaged changes. Check for: naming consistency,
1301
+ error handling, security issues, test coverage, import organization,
1302
+ code duplication. Run the project's linter. Provide a summary with
1303
+ severity levels (critical, warning, suggestion).`;
3904
1304
 
3905
1305
  // src/cli.ts
3906
1306
  var __dirname2 = path3.dirname(fileURLToPath(import.meta.url));
@@ -3938,11 +1338,12 @@ ${pc.bold("OPTIONS")}
3938
1338
  ${pc.bold("WHAT IT DOES")}
3939
1339
  1. Analyzes your repository's tech stack
3940
1340
  2. Launches Claude CLI to deeply analyze your codebase
3941
- 3. Generates tailored Claude Code configurations:
1341
+ 3. Generates all .claude/ configuration files:
3942
1342
  - CLAUDE.md with project-specific instructions (via Claude analysis)
3943
- - Skills for your frameworks (Next.js, FastAPI, etc.)
1343
+ - Skills for your frameworks and workflows
3944
1344
  - Agents for code review and testing
3945
1345
  - Rules matching your code style
1346
+ - Commands for task management
3946
1347
 
3947
1348
  ${pc.bold("REQUIREMENTS")}
3948
1349
  Claude CLI must be installed: https://claude.ai/download
@@ -3995,70 +1396,277 @@ function showTechStack(projectInfo, verbose) {
3995
1396
  }
3996
1397
  console.log();
3997
1398
  }
1399
+ var frameworkChoices = {
1400
+ typescript: [
1401
+ { title: "Next.js", value: "nextjs" },
1402
+ { title: "React", value: "react" },
1403
+ { title: "Vue", value: "vue" },
1404
+ { title: "Svelte", value: "svelte" },
1405
+ { title: "Express", value: "express" },
1406
+ { title: "NestJS", value: "nestjs" },
1407
+ { title: "Fastify", value: "fastify" },
1408
+ { title: "Hono", value: "hono" },
1409
+ { title: "Astro", value: "astro" },
1410
+ { title: "None / Other", value: null }
1411
+ ],
1412
+ javascript: [
1413
+ { title: "Next.js", value: "nextjs" },
1414
+ { title: "React", value: "react" },
1415
+ { title: "Vue", value: "vue" },
1416
+ { title: "Svelte", value: "svelte" },
1417
+ { title: "Express", value: "express" },
1418
+ { title: "NestJS", value: "nestjs" },
1419
+ { title: "Fastify", value: "fastify" },
1420
+ { title: "Hono", value: "hono" },
1421
+ { title: "Astro", value: "astro" },
1422
+ { title: "None / Other", value: null }
1423
+ ],
1424
+ python: [
1425
+ { title: "FastAPI", value: "fastapi" },
1426
+ { title: "Django", value: "django" },
1427
+ { title: "Flask", value: "flask" },
1428
+ { title: "None / Other", value: null }
1429
+ ],
1430
+ go: [
1431
+ { title: "Gin", value: "gin" },
1432
+ { title: "Echo", value: "echo" },
1433
+ { title: "Fiber", value: "fiber" },
1434
+ { title: "None / Other", value: null }
1435
+ ],
1436
+ swift: [
1437
+ { title: "SwiftUI", value: "swiftui" },
1438
+ { title: "UIKit", value: "uikit" },
1439
+ { title: "Vapor", value: "vapor" },
1440
+ { title: "None / Other", value: null }
1441
+ ],
1442
+ kotlin: [
1443
+ { title: "Jetpack Compose", value: "jetpack-compose" },
1444
+ { title: "Android Views", value: "android-views" },
1445
+ { title: "Spring", value: "spring" },
1446
+ { title: "None / Other", value: null }
1447
+ ],
1448
+ java: [
1449
+ { title: "Spring", value: "spring" },
1450
+ { title: "Quarkus", value: "quarkus" },
1451
+ { title: "None / Other", value: null }
1452
+ ],
1453
+ ruby: [
1454
+ { title: "Rails", value: "rails" },
1455
+ { title: "Sinatra", value: "sinatra" },
1456
+ { title: "None / Other", value: null }
1457
+ ],
1458
+ rust: [
1459
+ { title: "Actix", value: "actix" },
1460
+ { title: "Axum", value: "axum" },
1461
+ { title: "Rocket", value: "rocket" },
1462
+ { title: "None / Other", value: null }
1463
+ ]
1464
+ };
1465
+ var defaultFrameworkChoices = [{ title: "None / Other", value: null }];
3998
1466
  async function promptNewProject(args) {
3999
1467
  if (!args.interactive) {
4000
1468
  return null;
4001
1469
  }
4002
1470
  console.log(pc.yellow("New project detected - let's set it up!"));
4003
1471
  console.log();
4004
- const response = await prompts([
4005
- {
4006
- type: "text",
4007
- name: "description",
4008
- message: "What are you building?",
4009
- initial: "A new project"
4010
- },
4011
- {
4012
- type: "select",
4013
- name: "primaryLanguage",
4014
- message: "Primary language?",
4015
- choices: [
4016
- { title: "TypeScript", value: "typescript" },
4017
- { title: "JavaScript", value: "javascript" },
4018
- { title: "Python", value: "python" },
4019
- { title: "Go", value: "go" },
4020
- { title: "Rust", value: "rust" },
4021
- { title: "Other", value: null }
4022
- ]
4023
- },
4024
- {
4025
- type: (prev) => prev === "typescript" || prev === "javascript" ? "select" : null,
4026
- name: "framework",
4027
- message: "Framework?",
4028
- choices: [
4029
- { title: "Next.js", value: "nextjs" },
4030
- { title: "React", value: "react" },
4031
- { title: "Vue", value: "vue" },
4032
- { title: "Svelte", value: "svelte" },
4033
- { title: "Express", value: "express" },
4034
- { title: "NestJS", value: "nestjs" },
4035
- { title: "Hono", value: "hono" },
4036
- { title: "None / Other", value: null }
4037
- ]
4038
- },
4039
- {
4040
- type: (_, values) => values.primaryLanguage === "python" ? "select" : null,
4041
- name: "framework",
4042
- message: "Framework?",
4043
- choices: [
4044
- { title: "FastAPI", value: "fastapi" },
4045
- { title: "Django", value: "django" },
4046
- { title: "Flask", value: "flask" },
4047
- { title: "None / Other", value: null }
4048
- ]
4049
- }
4050
- ]);
4051
- if (!response.description) {
1472
+ const descResponse = await prompts({
1473
+ type: "text",
1474
+ name: "description",
1475
+ message: "What are you building?",
1476
+ initial: "A new project"
1477
+ });
1478
+ if (!descResponse.description) {
4052
1479
  return null;
4053
1480
  }
1481
+ const langResponse = await prompts({
1482
+ type: "select",
1483
+ name: "primaryLanguage",
1484
+ message: "Primary language?",
1485
+ choices: [
1486
+ { title: "TypeScript", value: "typescript" },
1487
+ { title: "JavaScript", value: "javascript" },
1488
+ { title: "Python", value: "python" },
1489
+ { title: "Go", value: "go" },
1490
+ { title: "Rust", value: "rust" },
1491
+ { title: "Swift", value: "swift" },
1492
+ { title: "Kotlin", value: "kotlin" },
1493
+ { title: "Java", value: "java" },
1494
+ { title: "Ruby", value: "ruby" },
1495
+ { title: "C#", value: "csharp" },
1496
+ { title: "PHP", value: "php" },
1497
+ { title: "C++", value: "cpp" }
1498
+ ]
1499
+ });
1500
+ const lang = langResponse.primaryLanguage || "typescript";
1501
+ const fwChoices = frameworkChoices[lang] || defaultFrameworkChoices;
1502
+ const fwResponse = await prompts({
1503
+ type: "select",
1504
+ name: "framework",
1505
+ message: "Framework?",
1506
+ choices: fwChoices
1507
+ });
1508
+ const pmChoices = getPackageManagerChoices(lang);
1509
+ const pmResponse = await prompts({
1510
+ type: "select",
1511
+ name: "packageManager",
1512
+ message: "Package manager?",
1513
+ choices: pmChoices
1514
+ });
1515
+ const testChoices = getTestingFrameworkChoices(lang);
1516
+ const testResponse = await prompts({
1517
+ type: "select",
1518
+ name: "testingFramework",
1519
+ message: "Testing framework?",
1520
+ choices: testChoices
1521
+ });
1522
+ const lintChoices = getLinterFormatterChoices(lang);
1523
+ const lintResponse = await prompts({
1524
+ type: "select",
1525
+ name: "linter",
1526
+ message: "Linter/Formatter?",
1527
+ choices: lintChoices
1528
+ });
1529
+ const typeResponse = await prompts({
1530
+ type: "select",
1531
+ name: "projectType",
1532
+ message: "Project type?",
1533
+ choices: [
1534
+ { title: "Web App", value: "Web App" },
1535
+ { title: "API / Backend", value: "API/Backend" },
1536
+ { title: "CLI Tool", value: "CLI Tool" },
1537
+ { title: "Library / Package", value: "Library/Package" },
1538
+ { title: "Mobile App", value: "Mobile App" },
1539
+ { title: "Desktop App", value: "Desktop App" },
1540
+ { title: "Monorepo", value: "Monorepo" },
1541
+ { title: "Other", value: "Other" }
1542
+ ]
1543
+ });
4054
1544
  return {
4055
- description: response.description,
4056
- primaryLanguage: response.primaryLanguage || "typescript",
4057
- framework: response.framework || null,
1545
+ description: descResponse.description,
1546
+ primaryLanguage: langResponse.primaryLanguage || "typescript",
1547
+ framework: fwResponse.framework || null,
4058
1548
  includeTests: true,
4059
- includeLinting: true
1549
+ includeLinting: true,
1550
+ packageManager: pmResponse.packageManager || null,
1551
+ testingFramework: testResponse.testingFramework || null,
1552
+ linter: lintResponse.linter || null,
1553
+ formatter: lintResponse.linter || null,
1554
+ // Use same as linter for simplicity
1555
+ projectType: typeResponse.projectType || "Other"
4060
1556
  };
4061
1557
  }
1558
+ function getPackageManagerChoices(lang) {
1559
+ if (lang === "typescript" || lang === "javascript") {
1560
+ return [
1561
+ { title: "npm", value: "npm" },
1562
+ { title: "yarn", value: "yarn" },
1563
+ { title: "pnpm", value: "pnpm" },
1564
+ { title: "bun", value: "bun" }
1565
+ ];
1566
+ }
1567
+ if (lang === "python") {
1568
+ return [
1569
+ { title: "pip", value: "pip" },
1570
+ { title: "poetry", value: "poetry" }
1571
+ ];
1572
+ }
1573
+ if (lang === "rust") {
1574
+ return [{ title: "cargo", value: "cargo" }];
1575
+ }
1576
+ if (lang === "go") {
1577
+ return [{ title: "go modules", value: "go" }];
1578
+ }
1579
+ if (lang === "ruby") {
1580
+ return [{ title: "bundler", value: "bundler" }];
1581
+ }
1582
+ if (lang === "java" || lang === "kotlin") {
1583
+ return [
1584
+ { title: "Maven", value: "maven" },
1585
+ { title: "Gradle", value: "gradle" }
1586
+ ];
1587
+ }
1588
+ return [{ title: "None / Default", value: null }];
1589
+ }
1590
+ function getTestingFrameworkChoices(lang) {
1591
+ if (lang === "typescript" || lang === "javascript") {
1592
+ return [
1593
+ { title: "Vitest", value: "vitest" },
1594
+ { title: "Jest", value: "jest" },
1595
+ { title: "Bun Test", value: "bun-test" },
1596
+ { title: "Playwright", value: "playwright" },
1597
+ { title: "None / I'll set it up later", value: null }
1598
+ ];
1599
+ }
1600
+ if (lang === "python") {
1601
+ return [
1602
+ { title: "pytest", value: "pytest" },
1603
+ { title: "unittest", value: "unittest" },
1604
+ { title: "None / I'll set it up later", value: null }
1605
+ ];
1606
+ }
1607
+ if (lang === "go") {
1608
+ return [
1609
+ { title: "go test", value: "go-test" },
1610
+ { title: "None / I'll set it up later", value: null }
1611
+ ];
1612
+ }
1613
+ if (lang === "rust") {
1614
+ return [
1615
+ { title: "cargo test", value: "rust-test" },
1616
+ { title: "None / I'll set it up later", value: null }
1617
+ ];
1618
+ }
1619
+ if (lang === "ruby") {
1620
+ return [
1621
+ { title: "RSpec", value: "rspec" },
1622
+ { title: "None / I'll set it up later", value: null }
1623
+ ];
1624
+ }
1625
+ if (lang === "java" || lang === "kotlin") {
1626
+ return [
1627
+ { title: "JUnit", value: "junit" },
1628
+ { title: "None / I'll set it up later", value: null }
1629
+ ];
1630
+ }
1631
+ return [{ title: "None / I'll set it up later", value: null }];
1632
+ }
1633
+ function getLinterFormatterChoices(lang) {
1634
+ if (lang === "typescript" || lang === "javascript") {
1635
+ return [
1636
+ { title: "Biome", value: "biome" },
1637
+ { title: "ESLint + Prettier", value: "eslint" },
1638
+ { title: "ESLint", value: "eslint" },
1639
+ { title: "None", value: null }
1640
+ ];
1641
+ }
1642
+ if (lang === "python") {
1643
+ return [
1644
+ { title: "Ruff", value: "ruff" },
1645
+ { title: "Flake8 + Black", value: "flake8" },
1646
+ { title: "Pylint", value: "pylint" },
1647
+ { title: "None", value: null }
1648
+ ];
1649
+ }
1650
+ if (lang === "go") {
1651
+ return [
1652
+ { title: "golangci-lint", value: "golangci-lint" },
1653
+ { title: "None", value: null }
1654
+ ];
1655
+ }
1656
+ if (lang === "rust") {
1657
+ return [
1658
+ { title: "Clippy", value: "clippy" },
1659
+ { title: "None", value: null }
1660
+ ];
1661
+ }
1662
+ if (lang === "ruby") {
1663
+ return [
1664
+ { title: "RuboCop", value: "rubocop" },
1665
+ { title: "None", value: null }
1666
+ ];
1667
+ }
1668
+ return [{ title: "None", value: null }];
1669
+ }
4062
1670
  function createTaskFile(projectInfo, preferences) {
4063
1671
  const taskPath = path3.join(projectInfo.rootDir, ".claude", "state", "task.md");
4064
1672
  fs3.mkdirSync(path3.dirname(taskPath), { recursive: true });
@@ -4189,25 +1797,33 @@ function runClaudeAnalysis(projectDir, projectInfo) {
4189
1797
  return new Promise((resolve) => {
4190
1798
  const prompt = getAnalysisPrompt(projectInfo);
4191
1799
  console.log(pc.cyan("Launching Claude for deep project analysis..."));
4192
- console.log(pc.gray("Claude will read your codebase and generate a comprehensive CLAUDE.md"));
1800
+ console.log(
1801
+ pc.gray("Claude will read your codebase and generate all .claude/ configuration files")
1802
+ );
4193
1803
  console.log();
4194
1804
  const child = spawn(
4195
1805
  "claude",
4196
1806
  [
4197
1807
  "-p",
4198
- prompt,
1808
+ "--dangerously-skip-permissions",
4199
1809
  "--allowedTools",
4200
1810
  "Read",
1811
+ "--allowedTools",
4201
1812
  "Glob",
1813
+ "--allowedTools",
4202
1814
  "Grep",
4203
- `Write(.claude/**)`,
4204
- `Edit(.claude/**)`
1815
+ "--allowedTools",
1816
+ "Write",
1817
+ "--allowedTools",
1818
+ "Edit"
4205
1819
  ],
4206
1820
  {
4207
1821
  cwd: projectDir,
4208
- stdio: ["ignore", "inherit", "inherit"]
1822
+ stdio: ["pipe", "inherit", "inherit"]
4209
1823
  }
4210
1824
  );
1825
+ child.stdin.write(prompt);
1826
+ child.stdin.end();
4211
1827
  child.on("error", (err) => {
4212
1828
  console.error(pc.red(`Failed to launch Claude CLI: ${err.message}`));
4213
1829
  resolve(false);
@@ -4224,6 +1840,24 @@ function runClaudeAnalysis(projectDir, projectInfo) {
4224
1840
  });
4225
1841
  });
4226
1842
  }
1843
+ function getGeneratedFiles(projectDir) {
1844
+ const claudeDir = path3.join(projectDir, ".claude");
1845
+ const files = [];
1846
+ function walk(dir) {
1847
+ if (!fs3.existsSync(dir)) return;
1848
+ const entries = fs3.readdirSync(dir, { withFileTypes: true });
1849
+ for (const entry of entries) {
1850
+ const fullPath = path3.join(dir, entry.name);
1851
+ if (entry.isDirectory()) {
1852
+ walk(fullPath);
1853
+ } else {
1854
+ files.push(path3.relative(projectDir, fullPath));
1855
+ }
1856
+ }
1857
+ }
1858
+ walk(claudeDir);
1859
+ return files;
1860
+ }
4227
1861
  async function main() {
4228
1862
  const args = parseArgs(process.argv.slice(2));
4229
1863
  if (args.help) {
@@ -4249,6 +1883,18 @@ async function main() {
4249
1883
  projectInfo.techStack.primaryFramework = preferences.framework;
4250
1884
  projectInfo.techStack.frameworks = [preferences.framework];
4251
1885
  }
1886
+ if (preferences.packageManager) {
1887
+ projectInfo.techStack.packageManager = preferences.packageManager;
1888
+ }
1889
+ if (preferences.testingFramework) {
1890
+ projectInfo.techStack.testingFramework = preferences.testingFramework;
1891
+ }
1892
+ if (preferences.linter) {
1893
+ projectInfo.techStack.linter = preferences.linter;
1894
+ }
1895
+ if (preferences.formatter) {
1896
+ projectInfo.techStack.formatter = preferences.formatter;
1897
+ }
4252
1898
  projectInfo.description = preferences.description;
4253
1899
  }
4254
1900
  } else {
@@ -4277,28 +1923,12 @@ async function main() {
4277
1923
  console.error(pc.gray("Install it from: https://claude.ai/download"));
4278
1924
  process.exit(1);
4279
1925
  }
4280
- console.log(pc.gray("Generating supporting configuration..."));
1926
+ console.log(pc.gray("Setting up .claude/ directory structure..."));
4281
1927
  console.log();
4282
- const result = generateArtifacts(projectInfo);
4283
- const { created, updated, skipped } = writeArtifacts(result.artifacts, projectDir, args.force);
4284
- if (created.length > 0) {
4285
- console.log(pc.green("Created:"));
4286
- for (const file of created) {
4287
- console.log(pc.green(` + ${file}`));
4288
- }
4289
- }
4290
- if (updated.length > 0) {
4291
- console.log(pc.blue("Updated:"));
4292
- for (const file of updated) {
4293
- console.log(pc.blue(` ~ ${file}`));
4294
- }
4295
- }
4296
- if (skipped.length > 0 && args.verbose) {
4297
- console.log(pc.gray("Preserved:"));
4298
- for (const file of skipped) {
4299
- console.log(pc.gray(` - ${file}`));
4300
- }
4301
- }
1928
+ writeSettings(projectDir, projectInfo.techStack);
1929
+ ensureDirectories(projectDir);
1930
+ console.log(pc.green("Created:"));
1931
+ console.log(pc.green(" + .claude/settings.json"));
4302
1932
  console.log();
4303
1933
  createTaskFile(projectInfo, preferences);
4304
1934
  const success = await runClaudeAnalysis(projectDir, projectInfo);
@@ -4306,44 +1936,55 @@ async function main() {
4306
1936
  console.error(pc.red("Claude analysis failed. Please try again."));
4307
1937
  process.exit(1);
4308
1938
  }
4309
- const totalFiles = created.length + updated.length + 1;
1939
+ const generatedFiles = getGeneratedFiles(projectDir);
4310
1940
  console.log();
4311
- console.log(pc.green(`Done! (${totalFiles} files)`));
1941
+ console.log(pc.green(`Done! (${generatedFiles.length} files)`));
4312
1942
  console.log();
4313
1943
  console.log(pc.bold("Generated for your stack:"));
4314
- console.log(pc.cyan(" CLAUDE.md (deep analysis by Claude)"));
4315
- const skills = result.artifacts.filter((a) => a.type === "skill");
4316
- const agents = result.artifacts.filter((a) => a.type === "agent");
4317
- const rules = result.artifacts.filter((a) => a.type === "rule");
1944
+ const skills = generatedFiles.filter((f) => f.includes("/skills/"));
1945
+ const agents = generatedFiles.filter((f) => f.includes("/agents/"));
1946
+ const rules = generatedFiles.filter((f) => f.includes("/rules/"));
1947
+ const commands = generatedFiles.filter((f) => f.includes("/commands/"));
1948
+ if (generatedFiles.some((f) => f.endsWith("CLAUDE.md"))) {
1949
+ console.log(pc.cyan(" CLAUDE.md (deep analysis by Claude)"));
1950
+ }
4318
1951
  if (skills.length > 0) {
4319
1952
  console.log(
4320
- ` ${skills.length} skills (${skills.map((s) => path3.basename(s.path, ".md")).join(", ")})`
1953
+ ` ${skills.length} skills (${skills.map((s) => path3.basename(s, ".md")).join(", ")})`
4321
1954
  );
4322
1955
  }
4323
1956
  if (agents.length > 0) {
4324
1957
  console.log(
4325
- ` ${agents.length} agents (${agents.map((a) => path3.basename(a.path, ".md")).join(", ")})`
1958
+ ` ${agents.length} agents (${agents.map((a) => path3.basename(a, ".md")).join(", ")})`
4326
1959
  );
4327
1960
  }
4328
1961
  if (rules.length > 0) {
4329
1962
  console.log(` ${rules.length} rules`);
4330
1963
  }
1964
+ if (commands.length > 0) {
1965
+ console.log(` ${commands.length} commands`);
1966
+ }
4331
1967
  console.log();
4332
1968
  console.log(`${pc.cyan("Next step:")} Run ${pc.bold("claude")} to start working!`);
4333
1969
  console.log();
4334
1970
  console.log(
4335
- pc.gray("Your CLAUDE.md was generated by deep analysis - review it with: cat .claude/CLAUDE.md")
1971
+ pc.gray(
1972
+ "Your .claude/ files were generated by deep analysis - review them with: ls -la .claude/"
1973
+ )
4336
1974
  );
4337
1975
  }
4338
- var isMain = fs3.realpathSync(process.argv[1]) === fileURLToPath(import.meta.url);
4339
- if (isMain) {
4340
- main().catch((err) => {
4341
- console.error(pc.red("Error:"), err.message);
4342
- if (process.env.DEBUG) {
4343
- console.error(err.stack);
4344
- }
4345
- process.exit(1);
4346
- });
1976
+ try {
1977
+ const isMain = process.argv[1] && fs3.realpathSync(process.argv[1]) === fileURLToPath(import.meta.url);
1978
+ if (isMain) {
1979
+ main().catch((err) => {
1980
+ console.error(pc.red("Error:"), err.message);
1981
+ if (process.env.DEBUG) {
1982
+ console.error(err.stack);
1983
+ }
1984
+ process.exit(1);
1985
+ });
1986
+ }
1987
+ } catch {
4347
1988
  }
4348
1989
  export {
4349
1990
  checkClaudeCli,