codebakers 3.3.0 → 4.1.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/index.js +1165 -1421
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -6,9 +6,10 @@ import {
6
6
  } from "./chunk-HOWR3YTF.js";
7
7
 
8
8
  // src/index.ts
9
- import * as p8 from "@clack/prompts";
9
+ import * as p7 from "@clack/prompts";
10
10
  import ora3 from "ora";
11
- import fs18 from "fs-extra";
11
+ import fs17 from "fs-extra";
12
+ import path18 from "path";
12
13
 
13
14
  // src/core/config.ts
14
15
  import Conf from "conf";
@@ -749,7 +750,7 @@ import Anthropic from "@anthropic-ai/sdk";
749
750
  var CORE_PATTERNS = `
750
751
  ## RULES (You MUST follow these)
751
752
 
752
- ### BANNED (never do these)
753
+ ### BANNED (never do these) - 15 Rules
753
754
  - \u274C \`any\` type \u2192 use proper TypeScript types
754
755
  - \u274C \`@ts-ignore\` or \`@ts-nocheck\` \u2192 fix the actual type error
755
756
  - \u274C \`console.log\` \u2192 remove or use proper logger
@@ -760,226 +761,468 @@ var CORE_PATTERNS = `
760
761
  - \u274C \`throw new Error('Not implemented')\` \u2192 implement it
761
762
  - \u274C \`debugger\` statements \u2192 remove before commit
762
763
  - \u274C hardcoded secrets/keys \u2192 use environment variables
763
-
764
- ### REQUIRED (always do these)
765
- - \u2705 Buttons: must have \`disabled={isLoading}\` and loading indicator
766
- - \u2705 Forms: must have Zod validation schema
767
- - \u2705 Forms: must show field-level error messages
768
- - \u2705 Forms: must disable submit while submitting
769
- - \u2705 Async operations: must have try/catch with error handling
770
- - \u2705 Async operations: must have loading state
771
- - \u2705 Lists: must have loading state (skeleton/spinner)
772
- - \u2705 Lists: must have empty state
773
- - \u2705 Lists: must have error state with retry
774
- - \u2705 API routes: must check authentication
775
- - \u2705 API routes: must validate input with Zod
776
- - \u2705 API routes: must return proper HTTP status codes
777
- - \u2705 All exports: must have TypeScript types
778
-
779
- ### PATTERNS (use these structures)
780
-
781
- #### Button with Loading
782
- \`\`\`tsx
783
- <Button
784
- onClick={handleAction}
785
- disabled={isLoading}
786
- >
787
- {isLoading ? (
788
- <>
789
- <Loader2 className="mr-2 h-4 w-4 animate-spin" />
790
- Loading...
791
- </>
792
- ) : (
793
- 'Action'
794
- )}
795
- </Button>
796
- \`\`\`
797
-
798
- #### Form with Validation
799
- \`\`\`tsx
800
- const schema = z.object({
801
- email: z.string().email('Invalid email'),
802
- password: z.string().min(8, 'Password must be at least 8 characters'),
803
- });
804
-
805
- type FormData = z.infer<typeof schema>;
806
-
807
- function MyForm() {
808
- const [isLoading, setIsLoading] = useState(false);
809
- const { register, handleSubmit, formState: { errors } } = useForm<FormData>({
810
- resolver: zodResolver(schema),
811
- });
812
-
813
- const onSubmit = async (data: FormData) => {
814
- setIsLoading(true);
815
- try {
816
- await submitData(data);
817
- toast.success('Success!');
818
- } catch (error) {
819
- toast.error(error instanceof Error ? error.message : 'Failed');
820
- } finally {
821
- setIsLoading(false);
822
- }
823
- };
824
-
825
- return (
826
- <form onSubmit={handleSubmit(onSubmit)}>
827
- <input {...register('email')} />
828
- {errors.email && <p className="text-red-500">{errors.email.message}</p>}
829
-
830
- <Button type="submit" disabled={isLoading}>
831
- {isLoading ? 'Submitting...' : 'Submit'}
832
- </Button>
833
- </form>
834
- );
835
- }
836
- \`\`\`
837
-
838
- #### Async Data Fetching
839
- \`\`\`tsx
840
- function MyComponent() {
841
- const [data, setData] = useState<DataType | null>(null);
842
- const [isLoading, setIsLoading] = useState(true);
843
- const [error, setError] = useState<string | null>(null);
844
-
845
- useEffect(() => {
846
- async function fetchData() {
847
- try {
848
- const response = await fetch('/api/data');
849
- if (!response.ok) throw new Error('Failed to fetch');
850
- const result = await response.json();
851
- setData(result);
852
- } catch (err) {
853
- setError(err instanceof Error ? err.message : 'Failed to load');
854
- } finally {
855
- setIsLoading(false);
856
- }
857
- }
858
- fetchData();
859
- }, []);
860
-
861
- if (isLoading) return <Skeleton />;
862
- if (error) return <ErrorState message={error} onRetry={() => window.location.reload()} />;
863
- if (!data) return <EmptyState message="No data found" />;
864
-
865
- return <DataDisplay data={data} />;
866
- }
867
- \`\`\`
868
-
869
- #### API Route
870
- \`\`\`tsx
871
- import { NextRequest, NextResponse } from 'next/server';
872
- import { z } from 'zod';
873
- import { createClient } from '@/lib/supabase/server';
874
-
875
- const requestSchema = z.object({
876
- name: z.string().min(1),
877
- email: z.string().email(),
878
- });
879
-
880
- export async function POST(request: NextRequest) {
881
- try {
882
- // 1. Auth check
883
- const supabase = createClient();
884
- const { data: { user }, error: authError } = await supabase.auth.getUser();
885
-
886
- if (authError || !user) {
887
- return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
888
- }
889
-
890
- // 2. Validate input
891
- const body = await request.json();
892
- const result = requestSchema.safeParse(body);
893
-
894
- if (!result.success) {
895
- return NextResponse.json(
896
- { error: 'Validation failed', details: result.error.flatten() },
897
- { status: 400 }
898
- );
899
- }
900
-
901
- // 3. Business logic
902
- const data = result.data;
903
- // ... do something
904
-
905
- // 4. Return success
906
- return NextResponse.json({ data }, { status: 201 });
907
-
908
- } catch (error) {
909
- console.error('[API] Error:', error);
910
- return NextResponse.json({ error: 'Internal server error' }, { status: 500 });
911
- }
912
- }
913
- \`\`\`
764
+ - \u274C \`innerHTML\` or \`dangerouslySetInnerHTML\` without sanitization
765
+ - \u274C \`document.write\` \u2192 use React/DOM methods
766
+ - \u274C synchronous \`localStorage\` in render \u2192 use useEffect
767
+ - \u274C \`var\` keyword \u2192 use const/let
768
+ - \u274C nested ternaries \u2192 use if/else or early returns
769
+
770
+ ### REACT PATTERNS - 12 Rules
771
+ - \u2705 Components must have TypeScript props interface
772
+ - \u2705 useState must have explicit type: \`useState<Type>()\`
773
+ - \u2705 useEffect must have dependency array
774
+ - \u2705 useEffect cleanup for subscriptions/timers
775
+ - \u2705 useMemo/useCallback for expensive computations
776
+ - \u2705 Keys in lists must be stable IDs (not index)
777
+ - \u2705 Error boundaries for component trees
778
+ - \u2705 Suspense boundaries for lazy components
779
+ - \u2705 forwardRef for components accepting refs
780
+ - \u2705 displayName for HOCs and forwardRef
781
+ - \u2705 Prop drilling > 2 levels \u2192 use Context
782
+ - \u2705 Custom hooks for reusable stateful logic
783
+
784
+ ### FORM PATTERNS - 8 Rules
785
+ - \u2705 Forms must have Zod validation schema
786
+ - \u2705 Forms must show field-level error messages
787
+ - \u2705 Forms must disable submit while submitting
788
+ - \u2705 Forms must show loading state on submit button
789
+ - \u2705 Forms must handle server errors gracefully
790
+ - \u2705 Forms must prevent double-submission
791
+ - \u2705 File inputs must validate file type/size
792
+ - \u2705 Forms must have accessible labels
793
+
794
+ ### ASYNC/DATA PATTERNS - 10 Rules
795
+ - \u2705 Async operations must have try/catch
796
+ - \u2705 Async operations must have loading state
797
+ - \u2705 Lists must have loading state (skeleton/spinner)
798
+ - \u2705 Lists must have empty state
799
+ - \u2705 Lists must have error state with retry
800
+ - \u2705 Fetch must check response.ok
801
+ - \u2705 Fetch must have timeout handling
802
+ - \u2705 Pagination for lists > 20 items
803
+ - \u2705 Optimistic updates for better UX
804
+ - \u2705 Debounce for search/filter inputs
805
+
806
+ ### API ROUTE PATTERNS - 8 Rules
807
+ - \u2705 API routes must check authentication
808
+ - \u2705 API routes must validate input with Zod
809
+ - \u2705 API routes must return proper HTTP status codes
810
+ - \u2705 API routes must log errors
811
+ - \u2705 API routes must sanitize user input
812
+ - \u2705 API routes must rate limit sensitive endpoints
813
+ - \u2705 API routes must validate content-type
814
+ - \u2705 API routes must handle CORS properly
815
+
816
+ ### SECURITY PATTERNS - 8 Rules
817
+ - \u2705 Sanitize all user input before display
818
+ - \u2705 Use parameterized queries (no SQL injection)
819
+ - \u2705 CSRF tokens for state-changing operations
820
+ - \u2705 Secure cookies (httpOnly, secure, sameSite)
821
+ - \u2705 Validate redirect URLs
822
+ - \u2705 Use Content-Security-Policy headers
823
+ - \u2705 Hash passwords with bcrypt/argon2
824
+ - \u2705 Rate limit authentication endpoints
825
+
826
+ ### ACCESSIBILITY PATTERNS - 6 Rules
827
+ - \u2705 Images must have alt text
828
+ - \u2705 Interactive elements must be keyboard accessible
829
+ - \u2705 Form inputs must have labels
830
+ - \u2705 Color contrast must meet WCAG AA
831
+ - \u2705 Focus indicators must be visible
832
+ - \u2705 ARIA labels for icon-only buttons
833
+
834
+ ### TYPESCRIPT PATTERNS - 5 Rules
835
+ - \u2705 All exports must have explicit types
836
+ - \u2705 Prefer interfaces over type aliases for objects
837
+ - \u2705 Use discriminated unions for state
838
+ - \u2705 Avoid type assertions (as) where possible
839
+ - \u2705 Use strict mode
840
+
841
+ ### TESTING PATTERNS - 4 Rules
842
+ - \u2705 Test files must be co-located with source
843
+ - \u2705 Test user behavior, not implementation
844
+ - \u2705 Mock external dependencies
845
+ - \u2705 Use data-testid for test selectors
914
846
  `;
915
847
  var BANNED_PATTERNS = [
848
+ // === BANNED PATTERNS (15) ===
916
849
  {
917
850
  id: "no-any",
918
851
  regex: /:\s*any\b|<any>|as\s+any/g,
919
852
  rule: "No `any` type",
920
853
  fix: "Use proper TypeScript types",
921
- severity: "error"
854
+ severity: "error",
855
+ category: "banned"
922
856
  },
923
857
  {
924
858
  id: "no-ts-ignore",
925
859
  regex: /@ts-ignore|@ts-nocheck/g,
926
860
  rule: "No @ts-ignore",
927
861
  fix: "Fix the actual type error",
928
- severity: "error"
862
+ severity: "error",
863
+ category: "banned"
929
864
  },
930
865
  {
931
866
  id: "no-console",
932
867
  regex: /console\.(log|debug|info)\(/g,
933
868
  rule: "No console.log",
934
869
  fix: "Remove or use proper logger",
935
- severity: "error"
870
+ severity: "warning",
871
+ category: "banned"
936
872
  },
937
873
  {
938
874
  id: "no-todo",
939
875
  regex: /\/\/\s*(TODO|FIXME|HACK|XXX):/gi,
940
876
  rule: "No TODO/FIXME",
941
877
  fix: "Complete the implementation now",
942
- severity: "error"
878
+ severity: "error",
879
+ category: "banned"
943
880
  },
944
881
  {
945
882
  id: "no-eval",
946
883
  regex: /\beval\s*\(/g,
947
884
  rule: "No eval()",
948
885
  fix: "Use safer alternatives",
949
- severity: "error"
886
+ severity: "error",
887
+ category: "security"
950
888
  },
951
889
  {
952
890
  id: "no-empty-handler",
953
891
  regex: /on\w+\s*=\s*\{\s*\(\)\s*=>\s*\{\s*\}\s*\}/g,
954
892
  rule: "No empty event handlers",
955
893
  fix: "Implement the handler",
956
- severity: "error"
894
+ severity: "error",
895
+ category: "banned"
957
896
  },
958
897
  {
959
898
  id: "no-debugger",
960
899
  regex: /\bdebugger\b/g,
961
900
  rule: "No debugger statements",
962
901
  fix: "Remove debugger",
963
- severity: "error"
902
+ severity: "error",
903
+ category: "banned"
964
904
  },
965
905
  {
966
906
  id: "no-placeholder",
967
907
  regex: /\/\/\s*\.\.\.|\/\*\s*\.\.\.\s*\*\//g,
968
908
  rule: "No placeholder comments",
969
909
  fix: "Write actual code",
970
- severity: "error"
910
+ severity: "error",
911
+ category: "banned"
971
912
  },
972
913
  {
973
914
  id: "no-not-implemented",
974
915
  regex: /throw\s+new\s+Error\s*\(\s*['"`]not\s+implemented/gi,
975
916
  rule: 'No "not implemented" errors',
976
917
  fix: "Implement the function",
977
- severity: "error"
918
+ severity: "error",
919
+ category: "banned"
920
+ },
921
+ {
922
+ id: "no-var",
923
+ regex: /\bvar\s+\w+/g,
924
+ rule: "No var keyword",
925
+ fix: "Use const or let",
926
+ severity: "error",
927
+ category: "banned"
928
+ },
929
+ {
930
+ id: "no-innerhtml",
931
+ regex: /\.innerHTML\s*=|dangerouslySetInnerHTML/g,
932
+ rule: "No innerHTML without sanitization",
933
+ fix: "Sanitize content or use React methods",
934
+ severity: "error",
935
+ category: "security"
936
+ },
937
+ {
938
+ id: "no-document-write",
939
+ regex: /document\.write\s*\(/g,
940
+ rule: "No document.write",
941
+ fix: "Use DOM methods or React",
942
+ severity: "error",
943
+ category: "banned"
944
+ },
945
+ {
946
+ id: "no-hardcoded-secret",
947
+ regex: /(api[_-]?key|secret|password|token)\s*[:=]\s*['"`][^'"`]{8,}/gi,
948
+ rule: "No hardcoded secrets",
949
+ fix: "Use environment variables",
950
+ severity: "error",
951
+ category: "security"
952
+ },
953
+ {
954
+ id: "no-nested-ternary",
955
+ regex: /\?\s*[^:]+\?\s*[^:]+:/g,
956
+ rule: "No nested ternaries",
957
+ fix: "Use if/else or early returns",
958
+ severity: "warning",
959
+ category: "banned"
960
+ },
961
+ {
962
+ id: "no-alert",
963
+ regex: /\balert\s*\(|\bconfirm\s*\(|\bprompt\s*\(/g,
964
+ rule: "No browser alerts",
965
+ fix: "Use proper UI components",
966
+ severity: "warning",
967
+ category: "banned"
968
+ },
969
+ // === REACT PATTERNS (12) ===
970
+ {
971
+ id: "react-no-index-key",
972
+ regex: /key\s*=\s*\{?\s*(index|i|idx)\s*\}?/g,
973
+ rule: "No array index as key",
974
+ fix: "Use stable unique ID",
975
+ severity: "warning",
976
+ category: "react"
977
+ },
978
+ {
979
+ id: "react-missing-deps",
980
+ regex: /useEffect\s*\(\s*\(\)\s*=>\s*\{[^}]*\}\s*\)/g,
981
+ rule: "useEffect missing dependency array",
982
+ fix: "Add dependency array []",
983
+ severity: "error",
984
+ category: "react"
985
+ },
986
+ {
987
+ id: "react-setstate-in-render",
988
+ regex: /function\s+\w+\s*\([^)]*\)\s*\{[^}]*\bset\w+\s*\([^)]+\)[^}]*return\s*\(/gs,
989
+ rule: "setState called during render",
990
+ fix: "Move to useEffect or event handler",
991
+ severity: "error",
992
+ category: "react"
993
+ },
994
+ // === SECURITY PATTERNS (8) ===
995
+ {
996
+ id: "security-sql-injection",
997
+ regex: /\$\{[^}]+\}.*(?:SELECT|INSERT|UPDATE|DELETE|FROM|WHERE)/gi,
998
+ rule: "Potential SQL injection",
999
+ fix: "Use parameterized queries",
1000
+ severity: "error",
1001
+ category: "security"
1002
+ },
1003
+ {
1004
+ id: "security-open-redirect",
1005
+ regex: /redirect\s*\(\s*(?:req\.|request\.)/gi,
1006
+ rule: "Potential open redirect",
1007
+ fix: "Validate redirect URL",
1008
+ severity: "error",
1009
+ category: "security"
1010
+ },
1011
+ {
1012
+ id: "security-exec",
1013
+ regex: /\bexec\s*\(|\bexecSync\s*\(|\bspawn\s*\(/g,
1014
+ rule: "Command execution detected",
1015
+ fix: "Sanitize input or avoid shell commands",
1016
+ severity: "warning",
1017
+ category: "security"
1018
+ },
1019
+ // === ACCESSIBILITY PATTERNS (6) ===
1020
+ {
1021
+ id: "a11y-img-alt",
1022
+ regex: /<img[^>]+(?!alt\s*=)[^>]*>/gi,
1023
+ rule: "Image missing alt attribute",
1024
+ fix: 'Add alt="description"',
1025
+ severity: "warning",
1026
+ category: "a11y"
1027
+ },
1028
+ {
1029
+ id: "a11y-button-type",
1030
+ regex: /<button(?![^>]*type\s*=)[^>]*>/gi,
1031
+ rule: "Button missing type attribute",
1032
+ fix: 'Add type="button" or type="submit"',
1033
+ severity: "warning",
1034
+ category: "a11y"
1035
+ },
1036
+ {
1037
+ id: "a11y-anchor-blank",
1038
+ regex: /target\s*=\s*['"]_blank['"](?![^>]*rel\s*=)/gi,
1039
+ rule: "External link missing rel attribute",
1040
+ fix: 'Add rel="noopener noreferrer"',
1041
+ severity: "warning",
1042
+ category: "a11y"
1043
+ },
1044
+ // === ASYNC PATTERNS (10) ===
1045
+ {
1046
+ id: "async-no-await",
1047
+ regex: /async\s+(?:function|\([^)]*\)\s*=>)[^{]*\{(?:(?!await)[^}])*\}/g,
1048
+ rule: "Async function without await",
1049
+ fix: "Add await or remove async",
1050
+ severity: "warning",
1051
+ category: "async"
1052
+ },
1053
+ {
1054
+ id: "async-promise-no-catch",
1055
+ regex: /\.then\s*\([^)]+\)(?!\s*\.catch)/g,
1056
+ rule: "Promise without catch",
1057
+ fix: "Add .catch() handler",
1058
+ severity: "warning",
1059
+ category: "async"
1060
+ },
1061
+ // === TYPESCRIPT PATTERNS (5) ===
1062
+ {
1063
+ id: "ts-non-null-assertion",
1064
+ regex: /\w+!/g,
1065
+ rule: "Non-null assertion used",
1066
+ fix: "Use optional chaining or null check",
1067
+ severity: "warning",
1068
+ category: "typescript"
1069
+ },
1070
+ {
1071
+ id: "ts-explicit-any-return",
1072
+ regex: /\):\s*any\s*\{/g,
1073
+ rule: "Function returns any",
1074
+ fix: "Add explicit return type",
1075
+ severity: "error",
1076
+ category: "typescript"
1077
+ },
1078
+ {
1079
+ id: "ts-object-type",
1080
+ regex: /:\s*object\b/g,
1081
+ rule: "Generic object type used",
1082
+ fix: "Use specific interface or Record<K,V>",
1083
+ severity: "warning",
1084
+ category: "typescript"
1085
+ },
1086
+ // === MORE REACT PATTERNS ===
1087
+ {
1088
+ id: "react-no-bind-in-render",
1089
+ regex: /onClick\s*=\s*\{[^}]*\.bind\s*\(/g,
1090
+ rule: "No .bind() in render",
1091
+ fix: "Use arrow function or useCallback",
1092
+ severity: "warning",
1093
+ category: "react"
1094
+ },
1095
+ {
1096
+ id: "react-no-arrow-in-render",
1097
+ regex: /onClick\s*=\s*\{\s*\(\)\s*=>\s*\w+\([^)]*\)\s*\}/g,
1098
+ rule: "Arrow function in render creates new function",
1099
+ fix: "Extract handler or use useCallback",
1100
+ severity: "warning",
1101
+ category: "react"
1102
+ },
1103
+ {
1104
+ id: "react-missing-error-boundary",
1105
+ regex: /throw\s+new\s+Error\s*\(/g,
1106
+ rule: "Throwing error without boundary",
1107
+ fix: "Wrap in Error Boundary",
1108
+ severity: "warning",
1109
+ category: "react"
1110
+ },
1111
+ {
1112
+ id: "react-direct-dom",
1113
+ regex: /document\.getElementById|document\.querySelector|document\.getElement/g,
1114
+ rule: "Direct DOM manipulation in React",
1115
+ fix: "Use refs or React state",
1116
+ severity: "warning",
1117
+ category: "react"
1118
+ },
1119
+ // === MORE SECURITY PATTERNS ===
1120
+ {
1121
+ id: "security-jwt-secret",
1122
+ regex: /jwt\.sign\s*\([^)]*['"`][^'"`]{8,}['"`]/gi,
1123
+ rule: "Hardcoded JWT secret",
1124
+ fix: "Use environment variable",
1125
+ severity: "error",
1126
+ category: "security"
1127
+ },
1128
+ {
1129
+ id: "security-md5",
1130
+ regex: /\bmd5\s*\(|createHash\s*\(\s*['"]md5['"]/gi,
1131
+ rule: "MD5 is insecure for passwords",
1132
+ fix: "Use bcrypt or argon2",
1133
+ severity: "error",
1134
+ category: "security"
1135
+ },
1136
+ {
1137
+ id: "security-sha1",
1138
+ regex: /createHash\s*\(\s*['"]sha1['"]/gi,
1139
+ rule: "SHA1 is deprecated",
1140
+ fix: "Use SHA256 or bcrypt",
1141
+ severity: "warning",
1142
+ category: "security"
1143
+ },
1144
+ {
1145
+ id: "security-cors-star",
1146
+ regex: /Access-Control-Allow-Origin['":\s]+\*/gi,
1147
+ rule: "CORS allows all origins",
1148
+ fix: "Specify allowed origins",
1149
+ severity: "warning",
1150
+ category: "security"
1151
+ },
1152
+ // === MORE ASYNC PATTERNS ===
1153
+ {
1154
+ id: "async-floating-promise",
1155
+ regex: /(?<!await\s)(?<!return\s)\w+\s*\.\s*then\s*\(/g,
1156
+ rule: "Floating promise (not awaited)",
1157
+ fix: "Add await or handle promise",
1158
+ severity: "warning",
1159
+ category: "async"
1160
+ },
1161
+ {
1162
+ id: "async-foreach-async",
1163
+ regex: /\.forEach\s*\(\s*async/g,
1164
+ rule: "async in forEach does not wait",
1165
+ fix: "Use for...of or Promise.all with map",
1166
+ severity: "error",
1167
+ category: "async"
1168
+ },
1169
+ // === MORE ACCESSIBILITY PATTERNS ===
1170
+ {
1171
+ id: "a11y-no-autofocus",
1172
+ regex: /autoFocus|autofocus/g,
1173
+ rule: "Autofocus can confuse screen readers",
1174
+ fix: "Remove or use carefully",
1175
+ severity: "warning",
1176
+ category: "a11y"
1177
+ },
1178
+ {
1179
+ id: "a11y-positive-tabindex",
1180
+ regex: /tabIndex\s*=\s*\{?\s*[1-9]/g,
1181
+ rule: "Positive tabindex disrupts focus order",
1182
+ fix: "Use tabIndex={0} or tabIndex={-1}",
1183
+ severity: "warning",
1184
+ category: "a11y"
1185
+ },
1186
+ {
1187
+ id: "a11y-onclick-no-keyboard",
1188
+ regex: /onClick\s*=\s*\{[^}]+\}(?![^>]*onKeyDown|onKeyPress|onKeyUp)/g,
1189
+ rule: "Click handler without keyboard support",
1190
+ fix: "Add onKeyDown for Enter/Space",
1191
+ severity: "warning",
1192
+ category: "a11y"
1193
+ },
1194
+ // === FORM PATTERNS ===
1195
+ {
1196
+ id: "form-no-autocomplete",
1197
+ regex: /<input[^>]+type\s*=\s*['"]password['"][^>]*(?!autocomplete)/gi,
1198
+ rule: "Password field missing autocomplete",
1199
+ fix: 'Add autocomplete="current-password" or "new-password"',
1200
+ severity: "warning",
1201
+ category: "form"
1202
+ },
1203
+ // === PERFORMANCE PATTERNS ===
1204
+ {
1205
+ id: "perf-console-in-loop",
1206
+ regex: /(?:for|while|\.forEach|\.map)\s*\([^)]*\)\s*\{[^}]*console\./g,
1207
+ rule: "Console in loop impacts performance",
1208
+ fix: "Remove or move outside loop",
1209
+ severity: "warning",
1210
+ category: "banned"
1211
+ },
1212
+ {
1213
+ id: "perf-await-in-loop",
1214
+ regex: /(?:for|while)\s*\([^)]*\)\s*\{[^}]*await\s/g,
1215
+ rule: "await in loop is sequential",
1216
+ fix: "Use Promise.all for parallel execution",
1217
+ severity: "warning",
1218
+ category: "async"
978
1219
  }
979
1220
  ];
980
1221
  var REQUIRED_CHECKS = [
1222
+ // === BUTTON CHECKS ===
981
1223
  {
982
1224
  id: "button-loading",
1225
+ category: "form",
983
1226
  check: (code) => {
984
1227
  const hasButton = /<Button|<button/i.test(code);
985
1228
  if (!hasButton) return { pass: true };
@@ -987,17 +1230,31 @@ var REQUIRED_CHECKS = [
987
1230
  return { pass: hasDisabled, rule: "Buttons must have disabled={isLoading}", fix: "Add loading state" };
988
1231
  }
989
1232
  },
1233
+ // === FORM CHECKS ===
990
1234
  {
991
1235
  id: "form-validation",
1236
+ category: "form",
992
1237
  check: (code) => {
993
1238
  const hasForm = /<form|useForm|handleSubmit/i.test(code);
994
1239
  if (!hasForm) return { pass: true };
995
- const hasValidation = /zod|z\.|zodResolver|schema/i.test(code);
996
- return { pass: hasValidation, rule: "Forms must have Zod validation", fix: "Add Zod schema" };
1240
+ const hasValidation = /zod|z\.|zodResolver|schema|yup|validate/i.test(code);
1241
+ return { pass: hasValidation, rule: "Forms must have validation", fix: "Add Zod/Yup schema" };
1242
+ }
1243
+ },
1244
+ {
1245
+ id: "form-error-display",
1246
+ category: "form",
1247
+ check: (code) => {
1248
+ const hasForm = /<form|useForm/i.test(code);
1249
+ if (!hasForm) return { pass: true };
1250
+ const hasErrors = /errors\.|error\s*&&|formState.*errors|\.message/i.test(code);
1251
+ return { pass: hasErrors, rule: "Forms must display errors", fix: "Add error messages" };
997
1252
  }
998
1253
  },
1254
+ // === ASYNC CHECKS ===
999
1255
  {
1000
1256
  id: "async-error-handling",
1257
+ category: "async",
1001
1258
  check: (code) => {
1002
1259
  const hasAsync = /async\s+|await\s+/i.test(code);
1003
1260
  if (!hasAsync) return { pass: true };
@@ -1005,14 +1262,102 @@ var REQUIRED_CHECKS = [
1005
1262
  return { pass: hasTryCatch, rule: "Async operations must have try/catch", fix: "Add error handling" };
1006
1263
  }
1007
1264
  },
1265
+ {
1266
+ id: "async-loading-state",
1267
+ category: "async",
1268
+ check: (code) => {
1269
+ const hasAsync = /async\s+|await\s+|\.then\s*\(/i.test(code);
1270
+ if (!hasAsync) return { pass: true };
1271
+ const hasLoading = /loading|isLoading|pending|fetching|submitting/i.test(code);
1272
+ return { pass: hasLoading, rule: "Async operations must have loading state", fix: "Add isLoading state" };
1273
+ }
1274
+ },
1275
+ // === API ROUTE CHECKS ===
1008
1276
  {
1009
1277
  id: "api-auth-check",
1278
+ category: "api",
1010
1279
  check: (code) => {
1011
1280
  const isApiRoute = /export\s+(async\s+)?function\s+(GET|POST|PUT|DELETE|PATCH)/i.test(code);
1012
1281
  if (!isApiRoute) return { pass: true };
1013
- const hasAuth = /getUser|getSession|auth\(\)|authenticate|unauthorized|401/i.test(code);
1282
+ const hasAuth = /getUser|getSession|auth\(\)|authenticate|unauthorized|401|getServerSession/i.test(code);
1014
1283
  return { pass: hasAuth, rule: "API routes must check authentication", fix: "Add auth check" };
1015
1284
  }
1285
+ },
1286
+ {
1287
+ id: "api-input-validation",
1288
+ category: "api",
1289
+ check: (code) => {
1290
+ const isApiRoute = /export\s+(async\s+)?function\s+(POST|PUT|PATCH)/i.test(code);
1291
+ if (!isApiRoute) return { pass: true };
1292
+ const hasValidation = /safeParse|parse\(|validate|schema|zod|z\./i.test(code);
1293
+ return { pass: hasValidation, rule: "API routes must validate input", fix: "Add Zod validation" };
1294
+ }
1295
+ },
1296
+ {
1297
+ id: "api-status-codes",
1298
+ category: "api",
1299
+ check: (code) => {
1300
+ const isApiRoute = /export\s+(async\s+)?function\s+(GET|POST|PUT|DELETE|PATCH)/i.test(code);
1301
+ if (!isApiRoute) return { pass: true };
1302
+ const hasStatusCodes = /status:\s*\d{3}|\{\s*status:\s*\d/i.test(code);
1303
+ return { pass: hasStatusCodes, rule: "API routes must return proper status codes", fix: "Add status codes" };
1304
+ }
1305
+ },
1306
+ // === LIST/DATA CHECKS ===
1307
+ {
1308
+ id: "list-loading-state",
1309
+ category: "async",
1310
+ check: (code) => {
1311
+ const hasList = /\.map\s*\(|Array\.from|forEach/i.test(code);
1312
+ const hasFetch = /fetch|useQuery|useSWR|axios/i.test(code);
1313
+ if (!hasList || !hasFetch) return { pass: true };
1314
+ const hasLoading = /Skeleton|Spinner|Loading|isLoading|loading/i.test(code);
1315
+ return { pass: hasLoading, rule: "Lists must have loading state", fix: "Add skeleton/spinner" };
1316
+ }
1317
+ },
1318
+ {
1319
+ id: "list-empty-state",
1320
+ category: "async",
1321
+ check: (code) => {
1322
+ const hasList = /\.map\s*\(/i.test(code);
1323
+ const hasFetch = /fetch|useQuery|useSWR|axios/i.test(code);
1324
+ if (!hasList || !hasFetch) return { pass: true };
1325
+ const hasEmpty = /length\s*===?\s*0|!.*\.length|empty|no\s+\w+\s+found/i.test(code);
1326
+ return { pass: hasEmpty, rule: "Lists must have empty state", fix: "Add empty state UI" };
1327
+ }
1328
+ },
1329
+ // === SECURITY CHECKS ===
1330
+ {
1331
+ id: "security-env-secrets",
1332
+ category: "security",
1333
+ check: (code) => {
1334
+ const hasSecret = /api[_-]?key|secret|password|token/i.test(code);
1335
+ if (!hasSecret) return { pass: true };
1336
+ const usesEnv = /process\.env|import\.meta\.env/i.test(code);
1337
+ return { pass: usesEnv, rule: "Secrets must use environment variables", fix: "Move to .env" };
1338
+ }
1339
+ },
1340
+ // === TYPESCRIPT CHECKS ===
1341
+ {
1342
+ id: "ts-export-types",
1343
+ category: "typescript",
1344
+ check: (code) => {
1345
+ const hasExport = /export\s+(const|function|class)\s+\w+/i.test(code);
1346
+ if (!hasExport) return { pass: true };
1347
+ const hasTypes = /:\s*\w+|<\w+>|interface\s+|type\s+/i.test(code);
1348
+ return { pass: hasTypes, rule: "Exports must have TypeScript types", fix: "Add type annotations" };
1349
+ }
1350
+ },
1351
+ // === ACCESSIBILITY CHECKS ===
1352
+ {
1353
+ id: "a11y-form-labels",
1354
+ category: "a11y",
1355
+ check: (code) => {
1356
+ const hasInput = /<input|<select|<textarea/i.test(code);
1357
+ if (!hasInput) return { pass: true };
1358
+ const hasLabel = /<label|aria-label|aria-labelledby|id=.*htmlFor/i.test(code);
1359
+ return { pass: hasLabel, rule: "Form inputs must have labels", fix: "Add <label> or aria-label" };
1360
+ }
1016
1361
  }
1017
1362
  ];
1018
1363
  function validateCode(code) {
@@ -1029,7 +1374,8 @@ function validateCode(code) {
1029
1374
  line: lineNumber,
1030
1375
  code: match[0],
1031
1376
  fix: pattern.fix,
1032
- severity: pattern.severity
1377
+ severity: pattern.severity,
1378
+ category: pattern.category
1033
1379
  });
1034
1380
  }
1035
1381
  }
@@ -1039,7 +1385,8 @@ function validateCode(code) {
1039
1385
  violations.push({
1040
1386
  rule: result.rule,
1041
1387
  fix: result.fix || "See pattern documentation",
1042
- severity: "error"
1388
+ severity: "error",
1389
+ category: check.category
1043
1390
  });
1044
1391
  }
1045
1392
  }
@@ -1285,7 +1632,7 @@ ${originalRequest}`;
1285
1632
  }
1286
1633
  const pathMatch = request.match(/['"`]([^'"`]+)['"`]/g);
1287
1634
  if (pathMatch) {
1288
- keywords.push(...pathMatch.map((p9) => p9.replace(/['"`]/g, "")));
1635
+ keywords.push(...pathMatch.map((p8) => p8.replace(/['"`]/g, "")));
1289
1636
  }
1290
1637
  return keywords;
1291
1638
  }
@@ -1499,11 +1846,11 @@ Format each as a single line. Be brief.`;
1499
1846
  } else if (line.toLowerCase().includes("suggestion") || line.toLowerCase().includes("improvement")) {
1500
1847
  currentSection = "suggestions";
1501
1848
  } else if (line.startsWith("-") || line.startsWith("\u2022") || /^\d+\./.test(line)) {
1502
- const text6 = line.replace(/^[-•\d.]\s*/, "").trim();
1503
- if (text6 && currentSection === "issues") {
1504
- issues.push(text6);
1505
- } else if (text6 && currentSection === "suggestions") {
1506
- suggestions.push(text6);
1849
+ const text5 = line.replace(/^[-•\d.]\s*/, "").trim();
1850
+ if (text5 && currentSection === "issues") {
1851
+ issues.push(text5);
1852
+ } else if (text5 && currentSection === "suggestions") {
1853
+ suggestions.push(text5);
1507
1854
  }
1508
1855
  }
1509
1856
  }
@@ -1572,8 +1919,8 @@ Format each as a single line. Be brief.`;
1572
1919
  if (this.ai) {
1573
1920
  onProgress?.("Running AI deep analysis...", 60);
1574
1921
  const filesForAI = await Promise.all(
1575
- filePaths.slice(0, 50).map(async (p9) => {
1576
- const f = await this.loadFile(p9);
1922
+ filePaths.slice(0, 50).map(async (p8) => {
1923
+ const f = await this.loadFile(p8);
1577
1924
  return f ? { path: f.path, content: f.content } : null;
1578
1925
  })
1579
1926
  );
@@ -1730,57 +2077,67 @@ import * as p from "@clack/prompts";
1730
2077
  import chalk from "chalk";
1731
2078
  var colors = {
1732
2079
  primary: chalk.hex("#7fff00"),
1733
- // Lime green (accent)
2080
+ // Lime green (brand)
1734
2081
  secondary: chalk.hex("#888888"),
1735
2082
  // Gray text
1736
2083
  muted: chalk.hex("#555555"),
1737
2084
  // Dimmed text
1738
- success: chalk.hex("#27ae60"),
1739
- // Green
1740
- warning: chalk.hex("#f39c12"),
1741
- // Orange
2085
+ success: chalk.hex("#2ecc71"),
2086
+ // Bright green
2087
+ warning: chalk.hex("#f1c40f"),
2088
+ // Yellow
1742
2089
  error: chalk.hex("#e74c3c"),
1743
2090
  // Red
1744
2091
  info: chalk.hex("#3498db"),
1745
2092
  // Blue
1746
- white: chalk.hex("#eeeeee"),
1747
- dim: chalk.hex("#666666")
2093
+ white: chalk.hex("#ffffff"),
2094
+ dim: chalk.hex("#666666"),
2095
+ accent: chalk.hex("#9b59b6")
2096
+ // Purple for highlights
2097
+ };
2098
+ var box = {
2099
+ topLeft: "\u256D",
2100
+ topRight: "\u256E",
2101
+ bottomLeft: "\u2570",
2102
+ bottomRight: "\u256F",
2103
+ horizontal: "\u2500",
2104
+ vertical: "\u2502",
2105
+ teeRight: "\u251C",
2106
+ teeLeft: "\u2524"
1748
2107
  };
1749
2108
  var sym = {
1750
- check: chalk.green("\u2713"),
1751
- cross: chalk.red("\u2717"),
1752
- box: chalk.dim("\u2610"),
1753
- bullet: chalk.dim("\u2022"),
1754
- arrow: chalk.dim("\u2192"),
1755
- ellipsis: "\u2026",
1756
- spinner: "\u27F3"
2109
+ check: colors.success("\u2713"),
2110
+ cross: colors.error("\u2717"),
2111
+ bullet: colors.dim("\u2022"),
2112
+ arrow: colors.primary("\u2192"),
2113
+ arrowRight: "\u203A",
2114
+ spinner: "\u27F3",
2115
+ star: "\u2605",
2116
+ lightning: "\u26A1",
2117
+ folder: "\u{1F4C1}",
2118
+ file: "\u{1F4C4}",
2119
+ gear: "\u2699",
2120
+ rocket: "\u{1F680}",
2121
+ sparkles: "\u2728",
2122
+ warning: "\u26A0",
2123
+ info: "\u2139"
1757
2124
  };
1758
- function showHeader() {
1759
- console.clear();
1760
- console.log("");
1761
- console.log(colors.muted(" \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"));
1762
- console.log(colors.muted(" \u2502") + colors.white(" \u{1F4E6} C O D E B A K E R S ") + colors.muted("\u2502"));
1763
- console.log(colors.muted(" \u2502") + colors.dim(" ") + colors.muted("\u2502"));
1764
- console.log(colors.muted(" \u2502") + colors.secondary(" AI Dev Team That Follows The Rules ") + colors.muted("\u2502"));
1765
- console.log(colors.muted(" \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"));
1766
- console.log("");
1767
- }
1768
2125
  function showSuccess(message) {
1769
- console.log(` ${sym.check} ${colors.white(message)}`);
2126
+ console.log(` ${sym.check} ${colors.success(message)}`);
1770
2127
  }
1771
2128
  function showError(message) {
1772
2129
  console.log(` ${sym.cross} ${colors.error(message)}`);
1773
2130
  }
1774
2131
  function showWarning(message) {
1775
- console.log(` ${colors.warning("\u26A0")} ${colors.warning(message)}`);
2132
+ console.log(` ${colors.warning(sym.warning)} ${colors.warning(message)}`);
1776
2133
  }
1777
2134
  function showInfo(message) {
1778
- console.log(colors.muted(` \u2139\uFE0F ${message}`));
2135
+ console.log(` ${colors.info(sym.info)} ${colors.info(message)}`);
1779
2136
  }
1780
2137
  function divider() {
1781
- console.log(colors.muted(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
2138
+ console.log(colors.muted(` ${box.horizontal.repeat(50)}`));
1782
2139
  }
1783
- var promptSymbol = colors.primary("\u276F");
2140
+ var promptSymbol = colors.primary("\u203A");
1784
2141
 
1785
2142
  // src/core/nlp.ts
1786
2143
  var INTENT_PATTERNS = [
@@ -2503,12 +2860,12 @@ var NLPInterpreter = class {
2503
2860
  deploy: "This will deploy your code. Continue?"
2504
2861
  };
2505
2862
  console.log("");
2506
- const confirm8 = await p.confirm({
2863
+ const confirm7 = await p.confirm({
2507
2864
  message: colors.warning(messages[intent.type] || "Are you sure?"),
2508
2865
  initialValue: intent.type !== "deploy"
2509
2866
  // Default no for deploy
2510
2867
  });
2511
- return !p.isCancel(confirm8) && confirm8 === true;
2868
+ return !p.isCancel(confirm7) && confirm7 === true;
2512
2869
  }
2513
2870
  };
2514
2871
  var nlp = new NLPInterpreter();
@@ -2736,22 +3093,22 @@ Please provide ONLY the corrected file content with the path. No explanation nee
2736
3093
  // HELPERS
2737
3094
  // ============================================================================
2738
3095
  async findMissingPackages(packages) {
2739
- const fs19 = await import("fs-extra");
3096
+ const fs18 = await import("fs-extra");
2740
3097
  const path19 = await import("path");
2741
3098
  const pkgPath = path19.join(this.scanner["projectPath"], "package.json");
2742
- if (!await fs19.pathExists(pkgPath)) return packages;
2743
- const pkg = await fs19.readJson(pkgPath);
3099
+ if (!await fs18.pathExists(pkgPath)) return packages;
3100
+ const pkg = await fs18.readJson(pkgPath);
2744
3101
  const installed = {
2745
3102
  ...pkg.dependencies,
2746
3103
  ...pkg.devDependencies
2747
3104
  };
2748
- return packages.filter((p9) => !installed[p9]);
3105
+ return packages.filter((p8) => !installed[p8]);
2749
3106
  }
2750
- spin(text6) {
3107
+ spin(text5) {
2751
3108
  if (this.spinner) {
2752
- this.spinner.text = text6 + " (ESC to cancel)";
3109
+ this.spinner.text = text5 + " (ESC to cancel)";
2753
3110
  } else {
2754
- this.spinner = ora(text6 + " (ESC to cancel)").start();
3111
+ this.spinner = ora(text5 + " (ESC to cancel)").start();
2755
3112
  }
2756
3113
  }
2757
3114
  stop() {
@@ -4410,574 +4767,46 @@ function getModeManager() {
4410
4767
 
4411
4768
  // src/core/plan-executor.ts
4412
4769
  import Anthropic3 from "@anthropic-ai/sdk";
4413
- var PlanExecutor = class {
4770
+
4771
+ // src/core/question-executor.ts
4772
+ import * as p2 from "@clack/prompts";
4773
+ import Anthropic4 from "@anthropic-ai/sdk";
4774
+
4775
+ // src/core/agent-executor.ts
4776
+ import ora2 from "ora";
4777
+
4778
+ // src/core/input-handler.ts
4779
+ import fs9 from "fs-extra";
4780
+ import path9 from "path";
4781
+
4782
+ // src/core/prd-parser.ts
4783
+ import Anthropic5 from "@anthropic-ai/sdk";
4784
+ var PRDParser = class {
4414
4785
  anthropic;
4415
4786
  memory;
4416
- scanner;
4417
- constructor(config, memory, scanner) {
4787
+ constructor(config, memory) {
4418
4788
  const apiKey = config.getAnthropicKey();
4419
4789
  if (!apiKey) throw new Error("API key not configured");
4420
- this.anthropic = new Anthropic3({ apiKey });
4790
+ this.anthropic = new Anthropic5({ apiKey });
4421
4791
  this.memory = memory;
4422
- this.scanner = scanner;
4423
4792
  }
4424
- async createPlan(request) {
4425
- const projectContext = await this.scanner.getContextString([]);
4426
- const prompt = `You are planning a coding task. DO NOT write any code yet - just create a plan.
4427
-
4428
- PROJECT CONTEXT:
4429
- ${projectContext}
4430
-
4431
- USER REQUEST:
4432
- ${request}
4433
-
4434
- Create a detailed plan. Respond with ONLY valid JSON:
4435
- {
4436
- "description": "Brief summary of what will be built",
4437
- "filesToCreate": ["src/path/file.ts", ...],
4438
- "filesToModify": ["src/existing/file.ts", ...],
4439
- "packagesToInstall": ["package-name", ...],
4440
- "commandsToRun": ["npm install", "npm run build", ...],
4441
- "estimatedTime": "2-3 minutes"
4442
- }
4443
-
4444
- Be specific about file paths. List ALL files that will be created or modified.`;
4445
- const response = await this.anthropic.messages.create({
4446
- model: "claude-sonnet-4-20250514",
4447
- max_tokens: 2048,
4448
- messages: [{ role: "user", content: prompt }]
4449
- });
4450
- const content = response.content[0]?.type === "text" ? response.content[0].text : "";
4451
- try {
4452
- const jsonStr = content.replace(/```json\n?|\n?```/g, "").trim();
4453
- return JSON.parse(jsonStr);
4454
- } catch {
4455
- return {
4456
- description: "Unable to parse plan",
4457
- filesToCreate: [],
4458
- filesToModify: [],
4459
- packagesToInstall: [],
4460
- commandsToRun: [],
4461
- estimatedTime: "Unknown"
4462
- };
4793
+ // ============================================================================
4794
+ // PARSE PRD
4795
+ // ============================================================================
4796
+ async parse(prdContent) {
4797
+ const chunks = this.chunkContent(prdContent, 5e4);
4798
+ let analysis;
4799
+ if (chunks.length === 1) {
4800
+ analysis = await this.analyzeChunk(chunks[0], true);
4801
+ } else {
4802
+ analysis = await this.analyzeMultipleChunks(chunks);
4463
4803
  }
4804
+ analysis.phases = this.createPhases(analysis.features);
4805
+ return analysis;
4464
4806
  }
4465
- formatPlan(plan) {
4466
- let output = "\n";
4467
- output += colors.white(" \u{1F4CB} PLAN") + colors.muted(" (not executed yet)") + "\n\n";
4468
- output += colors.muted(" " + plan.description) + "\n\n";
4469
- if (plan.filesToCreate.length > 0) {
4470
- output += colors.white(" Files to create:\n");
4471
- for (const file of plan.filesToCreate) {
4472
- output += colors.success(` + ${file}`) + "\n";
4473
- }
4474
- output += "\n";
4475
- }
4476
- if (plan.filesToModify.length > 0) {
4477
- output += colors.white(" Files to modify:\n");
4478
- for (const file of plan.filesToModify) {
4479
- output += colors.warning(` ~ ${file}`) + "\n";
4480
- }
4481
- output += "\n";
4482
- }
4483
- if (plan.packagesToInstall.length > 0) {
4484
- output += colors.white(" Packages to install:\n");
4485
- for (const pkg of plan.packagesToInstall) {
4486
- output += colors.muted(` + ${pkg}`) + "\n";
4487
- }
4488
- output += "\n";
4489
- }
4490
- if (plan.commandsToRun.length > 0) {
4491
- output += colors.white(" Commands to run:\n");
4492
- for (const cmd of plan.commandsToRun) {
4493
- output += colors.muted(` $ ${cmd}`) + "\n";
4494
- }
4495
- output += "\n";
4496
- }
4497
- output += colors.muted(` Estimated time: ${plan.estimatedTime}`) + "\n";
4498
- return output;
4499
- }
4500
- };
4501
-
4502
- // src/core/question-executor.ts
4503
- import * as p2 from "@clack/prompts";
4504
- import Anthropic4 from "@anthropic-ai/sdk";
4505
- var QuestionExecutor = class {
4506
- anthropic;
4507
- memory;
4508
- scanner;
4509
- constructor(config, memory, scanner) {
4510
- const apiKey = config.getAnthropicKey();
4511
- if (!apiKey) throw new Error("API key not configured");
4512
- this.anthropic = new Anthropic4({ apiKey });
4513
- this.memory = memory;
4514
- this.scanner = scanner;
4515
- }
4516
- async generateQuestions(request) {
4517
- const projectContext = await this.scanner.getContextString([]);
4518
- const memoryContext = this.memory.getFullContext();
4519
- const prompt = `You are about to build something. Before starting, identify what clarifying questions would help you build exactly what the user wants.
4520
-
4521
- PROJECT CONTEXT:
4522
- ${projectContext}
4523
-
4524
- MEMORY (what you already know):
4525
- ${memoryContext}
4526
-
4527
- USER REQUEST:
4528
- ${request}
4529
-
4530
- Generate 2-4 clarifying questions. Only ask questions where the answer would significantly change what you build. Skip questions if the answer is obvious from context or memory.
4531
-
4532
- Respond with ONLY valid JSON:
4533
- {
4534
- "questions": [
4535
- {
4536
- "id": "auth_type",
4537
- "text": "What type of authentication do you need?",
4538
- "type": "select",
4539
- "options": [
4540
- { "value": "email", "label": "Email/password only" },
4541
- { "value": "oauth", "label": "OAuth (Google, GitHub)" },
4542
- { "value": "both", "label": "Both email and OAuth" }
4543
- ]
4544
- },
4545
- {
4546
- "id": "need_reset",
4547
- "text": "Include password reset flow?",
4548
- "type": "confirm",
4549
- "default": "yes"
4550
- }
4551
- ]
4552
- }
4553
-
4554
- Question types:
4555
- - "select": Multiple choice (provide options)
4556
- - "confirm": Yes/no question
4557
- - "text": Free text input
4558
-
4559
- Only ask ESSENTIAL questions. If you can make a reasonable assumption, do so.`;
4560
- const response = await this.anthropic.messages.create({
4561
- model: "claude-sonnet-4-20250514",
4562
- max_tokens: 2048,
4563
- messages: [{ role: "user", content: prompt }]
4564
- });
4565
- const content = response.content[0]?.type === "text" ? response.content[0].text : "";
4566
- try {
4567
- const jsonStr = content.replace(/```json\n?|\n?```/g, "").trim();
4568
- const parsed = JSON.parse(jsonStr);
4569
- return parsed.questions || [];
4570
- } catch {
4571
- return [];
4572
- }
4573
- }
4574
- async askQuestions(questions) {
4575
- const answers = {};
4576
- if (questions.length === 0) {
4577
- console.log(colors.muted("\n No clarification needed - proceeding with build.\n"));
4578
- return { questions: [], answers: {} };
4579
- }
4580
- console.log("\n" + colors.secondary(" \u{1F914} A few questions first:") + "\n");
4581
- for (const question of questions) {
4582
- let answer;
4583
- switch (question.type) {
4584
- case "select":
4585
- answer = await p2.select({
4586
- message: question.text,
4587
- options: question.options?.map((opt) => ({
4588
- value: opt.value,
4589
- label: opt.label
4590
- })) || []
4591
- });
4592
- break;
4593
- case "confirm":
4594
- answer = await p2.confirm({
4595
- message: question.text,
4596
- initialValue: question.default === "yes"
4597
- });
4598
- break;
4599
- case "text":
4600
- default:
4601
- answer = await p2.text({
4602
- message: question.text,
4603
- placeholder: question.default
4604
- });
4605
- break;
4606
- }
4607
- if (p2.isCancel(answer)) {
4608
- return { questions, answers };
4609
- }
4610
- answers[question.id] = String(answer);
4611
- }
4612
- return { questions, answers };
4613
- }
4614
- formatAnswersForPrompt(result) {
4615
- if (Object.keys(result.answers).length === 0) {
4616
- return "";
4617
- }
4618
- let context = "\n\nUSER PREFERENCES (from clarifying questions):\n";
4619
- for (const question of result.questions) {
4620
- const answer = result.answers[question.id];
4621
- if (answer) {
4622
- context += `- ${question.text}: ${answer}
4623
- `;
4624
- }
4625
- }
4626
- return context;
4627
- }
4628
- showSummary(result) {
4629
- if (Object.keys(result.answers).length === 0) return;
4630
- console.log("\n" + colors.muted(" Got it! Building with:"));
4631
- for (const question of result.questions) {
4632
- const answer = result.answers[question.id];
4633
- if (answer) {
4634
- let displayAnswer = answer;
4635
- if (question.type === "select" && question.options) {
4636
- const option = question.options.find((o) => o.value === answer);
4637
- if (option) displayAnswer = option.label;
4638
- } else if (question.type === "confirm") {
4639
- displayAnswer = answer === "true" ? "Yes" : "No";
4640
- }
4641
- console.log(colors.muted(` \u2022 ${displayAnswer}`));
4642
- }
4643
- }
4644
- console.log("");
4645
- }
4646
- };
4647
-
4648
- // src/core/agent-executor.ts
4649
- import ora2 from "ora";
4650
- var AgentExecutor = class {
4651
- ai;
4652
- config;
4653
- memory;
4654
- scanner;
4655
- editor;
4656
- spinner = null;
4657
- paused = false;
4658
- cancelled = false;
4659
- constructor(config, ai, memory, scanner, editor) {
4660
- this.config = config;
4661
- this.ai = ai;
4662
- this.memory = memory;
4663
- this.scanner = scanner;
4664
- this.editor = editor;
4665
- }
4666
- // ============================================================================
4667
- // MAIN EXECUTION
4668
- // ============================================================================
4669
- async execute(request, options = {}) {
4670
- const startTime = Date.now();
4671
- this.paused = false;
4672
- this.cancelled = false;
4673
- const result = {
4674
- success: false,
4675
- filesCreated: [],
4676
- filesModified: [],
4677
- packagesInstalled: [],
4678
- commandsRun: [],
4679
- errors: [],
4680
- duration: 0
4681
- };
4682
- const checkCancelled = () => {
4683
- if (options.checkCancelled?.() || this.cancelled) {
4684
- return true;
4685
- }
4686
- return false;
4687
- };
4688
- console.log("");
4689
- console.log(colors.primary(" \u{1F916} AGENT MODE") + colors.muted(" - Running autonomously"));
4690
- console.log(colors.muted(" Press ESC to pause at any checkpoint"));
4691
- console.log("");
4692
- try {
4693
- const isComplex = this.isComplexRequest(request);
4694
- if (isComplex) {
4695
- return await this.executeParallel(request, options, checkCancelled);
4696
- }
4697
- const orchestrator = new Orchestrator(this.ai, this.editor, this.scanner, this.memory);
4698
- this.spin("Generating code...");
4699
- const genResult = await this.ai.generateStream(
4700
- request,
4701
- {
4702
- onToken: (token) => {
4703
- }
4704
- },
4705
- checkCancelled
4706
- );
4707
- if (checkCancelled()) {
4708
- this.stop();
4709
- return { ...result, cancelled: true, duration: Date.now() - startTime };
4710
- }
4711
- if (this.paused) {
4712
- this.stop();
4713
- return { ...result, paused: true, duration: Date.now() - startTime };
4714
- }
4715
- this.spin("Applying changes...");
4716
- this.editor.parseResponse(genResult.content);
4717
- const changes = this.editor.getPendingChanges();
4718
- for (const change of changes) {
4719
- if (checkCancelled()) {
4720
- this.stop();
4721
- return { ...result, cancelled: true, duration: Date.now() - startTime };
4722
- }
4723
- options.onFile?.(change.path, change.type === "create" ? "create" : "modify");
4724
- if (change.type === "create") {
4725
- result.filesCreated.push(change.path);
4726
- } else {
4727
- result.filesModified.push(change.path);
4728
- }
4729
- this.logStep(`${change.type === "create" ? "Created" : "Modified"} ${change.path}`);
4730
- }
4731
- await this.editor.apply();
4732
- const allContent = changes.map((c) => c.content || "").join("\n");
4733
- const packages = this.detectPackages(allContent);
4734
- if (packages.length > 0 && !checkCancelled()) {
4735
- this.spin("Installing packages...");
4736
- const { Terminal: Terminal2 } = await import("./terminal-6ZQVP6R7.js");
4737
- const terminal = new Terminal2();
4738
- for (const pkg of packages) {
4739
- this.logStep(`Installing ${pkg}...`);
4740
- }
4741
- const installResult = await terminal.npmInstall(packages);
4742
- if (installResult.success) {
4743
- result.packagesInstalled = packages;
4744
- result.commandsRun.push(`npm install ${packages.join(" ")}`);
4745
- }
4746
- }
4747
- if (!checkCancelled()) {
4748
- this.spin("Type checking...");
4749
- const { Terminal: Terminal2 } = await import("./terminal-6ZQVP6R7.js");
4750
- const terminal = new Terminal2();
4751
- const typeResult = await terminal.typecheck();
4752
- result.commandsRun.push("tsc --noEmit");
4753
- if (!typeResult.success) {
4754
- this.logStep("Fixing type errors...");
4755
- }
4756
- }
4757
- this.stop();
4758
- result.success = result.errors.length === 0;
4759
- result.duration = Date.now() - startTime;
4760
- this.showSummary(result);
4761
- for (const file of [...result.filesCreated, ...result.filesModified]) {
4762
- this.memory.addModifiedFile(file);
4763
- }
4764
- await this.memory.save();
4765
- return result;
4766
- } catch (error) {
4767
- this.stop();
4768
- result.errors.push(error instanceof Error ? error.message : "Unknown error");
4769
- result.duration = Date.now() - startTime;
4770
- return result;
4771
- }
4772
- }
4773
- // ============================================================================
4774
- // PARALLEL EXECUTION
4775
- // ============================================================================
4776
- async executeParallel(request, options, checkCancelled) {
4777
- const startTime = Date.now();
4778
- const result = {
4779
- success: false,
4780
- filesCreated: [],
4781
- filesModified: [],
4782
- packagesInstalled: [],
4783
- commandsRun: [],
4784
- errors: [],
4785
- duration: 0
4786
- };
4787
- console.log(colors.muted(" Using parallel agents for faster build..."));
4788
- console.log("");
4789
- const parallelSystem = new ParallelAgentSystem(
4790
- this.config,
4791
- this.memory,
4792
- this.scanner,
4793
- this.editor
4794
- );
4795
- const parallelResult = await parallelSystem.build(
4796
- request,
4797
- {
4798
- onTaskPlan: (tasks) => {
4799
- console.log(colors.muted(` \u{1F4CB} ${tasks.length} tasks planned`));
4800
- },
4801
- onAgentStart: (taskId, name) => {
4802
- console.log(colors.muted(` \u25CF [${name}] Starting...`));
4803
- },
4804
- onAgentComplete: (taskId, agentResult) => {
4805
- if (agentResult.success) {
4806
- console.log(colors.success(` \u2713 [${taskId}] Complete (${agentResult.files.length} files)`));
4807
- } else {
4808
- console.log(colors.error(` \u2717 [${taskId}] Failed`));
4809
- }
4810
- },
4811
- onMergeStart: () => {
4812
- console.log(colors.muted(" \u{1F504} Merging results..."));
4813
- },
4814
- onMergeComplete: (conflicts) => {
4815
- if (conflicts.length === 0) {
4816
- console.log(colors.success(" \u2713 No conflicts"));
4817
- }
4818
- }
4819
- },
4820
- checkCancelled
4821
- );
4822
- if (parallelResult.cancelled) {
4823
- return { ...result, cancelled: true, duration: Date.now() - startTime };
4824
- }
4825
- const changes = this.editor.getPendingChanges();
4826
- await this.editor.apply();
4827
- for (const change of changes) {
4828
- if (change.type === "create") {
4829
- result.filesCreated.push(change.path);
4830
- } else {
4831
- result.filesModified.push(change.path);
4832
- }
4833
- }
4834
- result.success = parallelResult.success;
4835
- result.duration = Date.now() - startTime;
4836
- this.showSummary(result);
4837
- return result;
4838
- }
4839
- // ============================================================================
4840
- // HELPERS
4841
- // ============================================================================
4842
- isComplexRequest(request) {
4843
- const lower = request.toLowerCase();
4844
- const complexIndicators = [
4845
- " and ",
4846
- " with ",
4847
- ", ",
4848
- " + ",
4849
- "full",
4850
- "complete",
4851
- "entire",
4852
- "saas",
4853
- "dashboard",
4854
- "auth",
4855
- "billing",
4856
- "payment"
4857
- ];
4858
- let score = 0;
4859
- for (const indicator of complexIndicators) {
4860
- if (lower.includes(indicator)) score++;
4861
- }
4862
- return score >= 3;
4863
- }
4864
- detectPackages(content) {
4865
- const packages = /* @__PURE__ */ new Set();
4866
- const importRegex = /import\s+.*?from\s+['"]([^'"./][^'"]*)['"]/g;
4867
- let match;
4868
- while ((match = importRegex.exec(content)) !== null) {
4869
- let pkg = match[1];
4870
- if (pkg.startsWith("@")) {
4871
- pkg = pkg.split("/").slice(0, 2).join("/");
4872
- } else {
4873
- pkg = pkg.split("/")[0];
4874
- }
4875
- const builtins = ["fs", "path", "http", "https", "crypto", "stream", "events", "os", "util", "url"];
4876
- if (!builtins.includes(pkg)) {
4877
- packages.add(pkg);
4878
- }
4879
- }
4880
- return Array.from(packages);
4881
- }
4882
- spin(text6) {
4883
- if (this.spinner) {
4884
- this.spinner.text = text6;
4885
- } else {
4886
- this.spinner = ora2(text6).start();
4887
- }
4888
- }
4889
- stop() {
4890
- if (this.spinner) {
4891
- this.spinner.stop();
4892
- this.spinner = null;
4893
- }
4894
- }
4895
- logStep(message) {
4896
- this.stop();
4897
- console.log(colors.muted(` \u25CF ${message}`));
4898
- }
4899
- showSummary(result) {
4900
- console.log("");
4901
- console.log(" " + "\u2550".repeat(50));
4902
- if (result.success) {
4903
- console.log(colors.success(" \u{1F389} AUTONOMOUS BUILD COMPLETE"));
4904
- } else if (result.cancelled) {
4905
- console.log(colors.warning(" \u26A0\uFE0F BUILD CANCELLED"));
4906
- } else if (result.paused) {
4907
- console.log(colors.warning(" \u23F8\uFE0F BUILD PAUSED"));
4908
- } else {
4909
- console.log(colors.error(" \u274C BUILD FAILED"));
4910
- }
4911
- console.log("");
4912
- if (result.filesCreated.length > 0) {
4913
- console.log(colors.muted(` Created: ${result.filesCreated.length} files`));
4914
- }
4915
- if (result.filesModified.length > 0) {
4916
- console.log(colors.muted(` Modified: ${result.filesModified.length} files`));
4917
- }
4918
- if (result.packagesInstalled.length > 0) {
4919
- console.log(colors.muted(` Installed: ${result.packagesInstalled.join(", ")}`));
4920
- }
4921
- const seconds = (result.duration / 1e3).toFixed(1);
4922
- console.log(colors.muted(` Time: ${seconds}s`));
4923
- if (result.errors.length > 0) {
4924
- console.log("");
4925
- console.log(colors.error(" Errors:"));
4926
- for (const error of result.errors) {
4927
- console.log(colors.error(` \u2022 ${error}`));
4928
- }
4929
- }
4930
- console.log("");
4931
- console.log(colors.muted(" All changes saved. Use /undo to rollback."));
4932
- console.log(" " + "\u2550".repeat(50));
4933
- console.log("");
4934
- }
4935
- // ============================================================================
4936
- // CONTROL
4937
- // ============================================================================
4938
- pause() {
4939
- this.paused = true;
4940
- }
4941
- cancel() {
4942
- this.cancelled = true;
4943
- }
4944
- resume() {
4945
- this.paused = false;
4946
- }
4947
- };
4948
-
4949
- // src/core/input-handler.ts
4950
- import fs9 from "fs-extra";
4951
- import path9 from "path";
4952
-
4953
- // src/core/prd-parser.ts
4954
- import Anthropic5 from "@anthropic-ai/sdk";
4955
- var PRDParser = class {
4956
- anthropic;
4957
- memory;
4958
- constructor(config, memory) {
4959
- const apiKey = config.getAnthropicKey();
4960
- if (!apiKey) throw new Error("API key not configured");
4961
- this.anthropic = new Anthropic5({ apiKey });
4962
- this.memory = memory;
4963
- }
4964
- // ============================================================================
4965
- // PARSE PRD
4966
- // ============================================================================
4967
- async parse(prdContent) {
4968
- const chunks = this.chunkContent(prdContent, 5e4);
4969
- let analysis;
4970
- if (chunks.length === 1) {
4971
- analysis = await this.analyzeChunk(chunks[0], true);
4972
- } else {
4973
- analysis = await this.analyzeMultipleChunks(chunks);
4974
- }
4975
- analysis.phases = this.createPhases(analysis.features);
4976
- return analysis;
4977
- }
4978
- chunkContent(content, maxSize) {
4979
- if (content.length <= maxSize) {
4980
- return [content];
4807
+ chunkContent(content, maxSize) {
4808
+ if (content.length <= maxSize) {
4809
+ return [content];
4981
4810
  }
4982
4811
  const chunks = [];
4983
4812
  let remaining = content;
@@ -5188,7 +5017,7 @@ ${chunks[i]}`,
5188
5017
  projectName: analysis.projectName,
5189
5018
  totalFeatures: analysis.features.length,
5190
5019
  completedFeatures: analysis.features.filter((f) => f.status === "complete").length,
5191
- currentPhase: analysis.phases.find((p9) => p9.status === "in_progress")?.id || analysis.phases[0]?.id || "",
5020
+ currentPhase: analysis.phases.find((p8) => p8.status === "in_progress")?.id || analysis.phases[0]?.id || "",
5192
5021
  currentFeature: analysis.features.find((f) => f.status === "in_progress")?.id || "",
5193
5022
  features: analysis.features.map((f) => ({ id: f.id, status: f.status })),
5194
5023
  startedAt: Date.now(),
@@ -6684,8 +6513,8 @@ var SmartGitManager = class {
6684
6513
  const pr = await this.createPR(phaseBranch, baseBranch);
6685
6514
  return pr;
6686
6515
  }
6687
- slugify(text6) {
6688
- return text6.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "").slice(0, 30);
6516
+ slugify(text5) {
6517
+ return text5.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "").slice(0, 30);
6689
6518
  }
6690
6519
  // ============================================================================
6691
6520
  // SMART COMMITS
@@ -6964,7 +6793,7 @@ var ProjectBuilder = class {
6964
6793
  totalDuration: 0
6965
6794
  };
6966
6795
  try {
6967
- const phaseNames = spec.buildPhases.map((p9) => p9.name.replace(/Phase \d+:\s*/i, ""));
6796
+ const phaseNames = spec.buildPhases.map((p8) => p8.name.replace(/Phase \d+:\s*/i, ""));
6968
6797
  const strategy = await this.git.createProjectStrategy(spec.name, phaseNames);
6969
6798
  console.log(this.git.formatStrategy(strategy));
6970
6799
  const proceed = await p4.confirm({
@@ -7671,7 +7500,7 @@ Return ONLY the fixed code, no explanations. Keep all other code exactly the sam
7671
7500
  return this.patterns;
7672
7501
  }
7673
7502
  enablePattern(id) {
7674
- const pattern = this.patterns.find((p9) => p9.id === id);
7503
+ const pattern = this.patterns.find((p8) => p8.id === id);
7675
7504
  if (pattern) {
7676
7505
  pattern.enabled = true;
7677
7506
  return true;
@@ -7679,7 +7508,7 @@ Return ONLY the fixed code, no explanations. Keep all other code exactly the sam
7679
7508
  return false;
7680
7509
  }
7681
7510
  disablePattern(id) {
7682
- const pattern = this.patterns.find((p9) => p9.id === id);
7511
+ const pattern = this.patterns.find((p8) => p8.id === id);
7683
7512
  if (pattern) {
7684
7513
  pattern.enabled = false;
7685
7514
  return true;
@@ -7738,8 +7567,8 @@ Return ONLY the fixed code, no explanations. Keep all other code exactly the sam
7738
7567
  formatPatterns() {
7739
7568
  let output = "\n";
7740
7569
  output += colors.white(" \u{1F4CB} Pattern Rules") + "\n\n";
7741
- const enabled = this.patterns.filter((p9) => p9.enabled);
7742
- const disabled = this.patterns.filter((p9) => !p9.enabled);
7570
+ const enabled = this.patterns.filter((p8) => p8.enabled);
7571
+ const disabled = this.patterns.filter((p8) => !p8.enabled);
7743
7572
  output += colors.success(" ENABLED") + "\n";
7744
7573
  for (const pattern of enabled) {
7745
7574
  const severityIcon = pattern.severity === "critical" ? "\u{1F534}" : pattern.severity === "warning" ? "\u{1F7E1}" : "\u{1F7E2}";
@@ -7764,7 +7593,7 @@ Return ONLY the fixed code, no explanations. Keep all other code exactly the sam
7764
7593
  return grouped;
7765
7594
  }
7766
7595
  getPatternName(id) {
7767
- const pattern = this.patterns.find((p9) => p9.id === id);
7596
+ const pattern = this.patterns.find((p8) => p8.id === id);
7768
7597
  return pattern?.name || id;
7769
7598
  }
7770
7599
  // ============================================================================
@@ -7970,7 +7799,7 @@ var SmartSuggestions = class {
7970
7799
  /platform\s*(that|which|for)/,
7971
7800
  /saas\s*(for|that|which)/
7972
7801
  ];
7973
- return ideaPatterns.some((p9) => p9.test(lower));
7802
+ return ideaPatterns.some((p8) => p8.test(lower));
7974
7803
  }
7975
7804
  looksLikeComplexRequest(input) {
7976
7805
  const lower = input.toLowerCase();
@@ -8000,7 +7829,7 @@ var SmartSuggestions = class {
8000
7829
  /clean\s*up/,
8001
7830
  /improve\s*(the\s*)?(code|quality)/
8002
7831
  ];
8003
- return patterns.some((p9) => p9.test(lower));
7832
+ return patterns.some((p8) => p8.test(lower));
8004
7833
  }
8005
7834
  // ============================================================================
8006
7835
  // DISPLAY
@@ -8863,7 +8692,7 @@ ${issues.map((i) => ` \u2022 ${i}`).join("\n")}` : "\n\nEverything looks good!"
8863
8692
  /^(ugh|argh|damn|shit|fuck)/i
8864
8693
  // Frustration words
8865
8694
  ];
8866
- return frustrationPatterns.some((p9) => p9.test(input));
8695
+ return frustrationPatterns.some((p8) => p8.test(input));
8867
8696
  }
8868
8697
  // ============================================================================
8869
8698
  // RATE LIMITING (Don't be annoying)
@@ -9125,8 +8954,8 @@ ${chunk.content.slice(0, 2e3)}`
9125
8954
  chunk.embedding = this.simpleEmbed(chunk.content + " " + chunk.summary);
9126
8955
  }
9127
8956
  }
9128
- simpleEmbed(text6) {
9129
- const words = text6.toLowerCase().split(/\W+/).filter((w) => w.length > 2);
8957
+ simpleEmbed(text5) {
8958
+ const words = text5.toLowerCase().split(/\W+/).filter((w) => w.length > 2);
9130
8959
  const embedding = new Array(256).fill(0);
9131
8960
  for (const word of words) {
9132
8961
  const hash = this.hashString(word);
@@ -9587,7 +9416,7 @@ ${featureList}`
9587
9416
  "Redux": ["@reduxjs/toolkit", "redux"]
9588
9417
  };
9589
9418
  for (const [name, packages] of Object.entries(categories)) {
9590
- if (packages.some((p9) => deps.has(p9))) {
9419
+ if (packages.some((p8) => deps.has(p8))) {
9591
9420
  stack.push(name);
9592
9421
  }
9593
9422
  }
@@ -9719,279 +9548,11 @@ ${featureList}`
9719
9548
  }
9720
9549
  };
9721
9550
 
9722
- // src/core/onboarding.ts
9723
- import * as p6 from "@clack/prompts";
9724
- import fs16 from "fs-extra";
9725
- import path16 from "path";
9726
- function showWelcomeScreen() {
9727
- console.clear();
9728
- console.log("");
9729
- console.log(colors.primary(" \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"));
9730
- console.log(colors.primary(" \u2502 \u2502"));
9731
- console.log(colors.primary(" \u2502") + colors.white(" Welcome to CodeBakers! ") + colors.primary("\u2502"));
9732
- console.log(colors.primary(" \u2502") + colors.muted(" AI Dev Team That Follows The Rules ") + colors.primary("\u2502"));
9733
- console.log(colors.primary(" \u2502 \u2502"));
9734
- console.log(colors.primary(" \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"));
9735
- console.log("");
9736
- console.log(colors.white(" Let's get you set up in 2 quick steps:"));
9737
- console.log("");
9738
- console.log(colors.muted(" 1. Connect your account (or use API key)"));
9739
- console.log(colors.muted(" 2. Initialize your project"));
9740
- console.log("");
9741
- console.log(colors.dim(" Press ESC at any time to go back"));
9742
- console.log("");
9743
- }
9744
- async function showAuthScreen() {
9745
- console.clear();
9746
- console.log("");
9747
- console.log(colors.white(" \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"));
9748
- console.log(colors.white(" \u2502") + colors.primary(" Step 1 of 2: ") + colors.white("Connect Your Account ") + colors.white("\u2502"));
9749
- console.log(colors.white(" \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"));
9750
- console.log("");
9751
- const choice = await p6.select({
9752
- message: "How would you like to connect?",
9753
- options: [
9754
- {
9755
- value: "browser",
9756
- label: "\u{1F310} Login with browser",
9757
- hint: "Google or GitHub - recommended"
9758
- },
9759
- {
9760
- value: "code",
9761
- label: "\u{1F511} Enter admin/beta code",
9762
- hint: "If you have a special code"
9763
- },
9764
- {
9765
- value: "apikey",
9766
- label: "\u{1F527} Use my own API key",
9767
- hint: "No account needed, limited features"
9768
- },
9769
- {
9770
- value: "skip",
9771
- label: "\u23ED\uFE0F Skip for now",
9772
- hint: "You can login later with /login"
9773
- }
9774
- ]
9775
- });
9776
- if (p6.isCancel(choice)) {
9777
- return "back";
9778
- }
9779
- return choice;
9780
- }
9781
- async function showApiKeyScreen(config) {
9782
- console.clear();
9783
- console.log("");
9784
- console.log(colors.white(" \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"));
9785
- console.log(colors.white(" \u2502") + colors.primary(" Step 1 of 2: ") + colors.white("Enter Your API Key ") + colors.white("\u2502"));
9786
- console.log(colors.white(" \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"));
9787
- console.log("");
9788
- console.log(colors.muted(" Get your API key from: https://console.anthropic.com/"));
9789
- console.log("");
9790
- const apiKey = await p6.text({
9791
- message: "Anthropic API Key:",
9792
- placeholder: "sk-ant-api...",
9793
- validate: (value) => {
9794
- if (!value) return "API key is required";
9795
- if (!value.startsWith("sk-ant-")) return "API key should start with sk-ant-";
9796
- return void 0;
9797
- }
9798
- });
9799
- if (p6.isCancel(apiKey)) {
9800
- return "back";
9801
- }
9802
- config.setAnthropicKey(apiKey);
9803
- console.log("");
9804
- console.log(colors.success(" \u2713 API key saved"));
9805
- await sleep(500);
9806
- return "done";
9807
- }
9808
- async function showProjectScreen(memory) {
9809
- console.clear();
9810
- console.log("");
9811
- console.log(colors.white(" \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"));
9812
- console.log(colors.white(" \u2502") + colors.primary(" Step 2 of 2: ") + colors.white("Initialize Your Project ") + colors.white("\u2502"));
9813
- console.log(colors.white(" \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"));
9814
- console.log("");
9815
- const cwd = process.cwd();
9816
- const dirName = path16.basename(cwd);
9817
- const hasPackageJson = await fs16.pathExists(path16.join(cwd, "package.json"));
9818
- if (hasPackageJson) {
9819
- console.log(colors.muted(` \u{1F4C1} Detected project: ${dirName}`));
9820
- console.log("");
9821
- } else {
9822
- console.log(colors.muted(` \u{1F4C1} Current folder: ${dirName}`));
9823
- console.log("");
9824
- }
9825
- const action = await p6.select({
9826
- message: "What would you like to do?",
9827
- options: [
9828
- {
9829
- value: "init",
9830
- label: "\u2728 Initialize this folder as a CB project",
9831
- hint: "CB will remember context about this project"
9832
- },
9833
- {
9834
- value: "skip",
9835
- label: "\u23ED\uFE0F Skip for now",
9836
- hint: "You can initialize later with /init"
9837
- }
9838
- ]
9839
- });
9840
- if (p6.isCancel(action)) {
9841
- return "back";
9842
- }
9843
- if (action === "skip") {
9844
- return "skip";
9845
- }
9846
- const projectName = await p6.text({
9847
- message: "Project name:",
9848
- initialValue: dirName,
9849
- placeholder: dirName
9850
- });
9851
- if (p6.isCancel(projectName)) {
9852
- return "back";
9853
- }
9854
- const description = await p6.text({
9855
- message: "Brief description (optional):",
9856
- placeholder: "A web app that..."
9857
- });
9858
- if (p6.isCancel(description)) {
9859
- return "back";
9860
- }
9861
- await memory.init();
9862
- memory.setIdentity(projectName, description || "");
9863
- console.log("");
9864
- console.log(colors.success(` \u2713 Project "${projectName}" initialized`));
9865
- console.log(colors.muted(" CB will now remember context about this project"));
9866
- await sleep(500);
9867
- return "done";
9868
- }
9869
- function showCompleteScreen() {
9870
- console.clear();
9871
- console.log("");
9872
- console.log(colors.primary(" \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"));
9873
- console.log(colors.primary(" \u2502") + colors.white(" ") + colors.primary("\u2502"));
9874
- console.log(colors.primary(" \u2502") + colors.success(" \u2713 You're all set! ") + colors.primary("\u2502"));
9875
- console.log(colors.primary(" \u2502") + colors.white(" ") + colors.primary("\u2502"));
9876
- console.log(colors.primary(" \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"));
9877
- console.log("");
9878
- console.log(colors.white(" Quick start:"));
9879
- console.log("");
9880
- console.log(colors.muted(" Just type what you want:"));
9881
- console.log(colors.white(' "Add a login page"'));
9882
- console.log(colors.white(' "Fix the bug in navbar"'));
9883
- console.log(colors.white(' "Create an API for users"'));
9884
- console.log("");
9885
- console.log(colors.muted(" Or use commands:"));
9886
- console.log(colors.dim(" /help ") + colors.muted("Show all commands"));
9887
- console.log(colors.dim(" /audit ") + colors.muted("Check code quality"));
9888
- console.log(colors.dim(" /spec ") + colors.muted("Generate a product spec"));
9889
- console.log("");
9890
- }
9891
- async function runOnboarding(config, memory) {
9892
- let currentStep = "welcome";
9893
- const hasApiKey = config.isConfigured();
9894
- const hasAuth = auth.isLoggedIn();
9895
- const hasProject = memory.getIdentity().name !== "";
9896
- if (hasAuth || hasApiKey) {
9897
- if (hasProject) {
9898
- return true;
9899
- }
9900
- currentStep = "project";
9901
- }
9902
- while (true) {
9903
- switch (currentStep) {
9904
- case "welcome": {
9905
- showWelcomeScreen();
9906
- const proceed = await p6.confirm({
9907
- message: "Ready to begin?",
9908
- initialValue: true
9909
- });
9910
- if (p6.isCancel(proceed) || !proceed) {
9911
- return false;
9912
- }
9913
- currentStep = "auth";
9914
- break;
9915
- }
9916
- case "auth": {
9917
- const authChoice = await showAuthScreen();
9918
- if (authChoice === "back") {
9919
- currentStep = "welcome";
9920
- break;
9921
- }
9922
- if (authChoice === "skip") {
9923
- if (!config.isConfigured()) {
9924
- console.log("");
9925
- console.log(colors.warning(" \u26A0 You need either an account or API key to use CB"));
9926
- console.log("");
9927
- await sleep(1500);
9928
- break;
9929
- }
9930
- currentStep = "project";
9931
- break;
9932
- }
9933
- if (authChoice === "apikey") {
9934
- currentStep = "api_key";
9935
- break;
9936
- }
9937
- if (authChoice === "browser" || authChoice === "code") {
9938
- const success = authChoice === "browser" ? await auth.loginWithBrowser() : await auth.loginWithCode();
9939
- if (success) {
9940
- currentStep = "project";
9941
- }
9942
- break;
9943
- }
9944
- break;
9945
- }
9946
- case "api_key": {
9947
- const result = await showApiKeyScreen(config);
9948
- if (result === "back") {
9949
- currentStep = "auth";
9950
- break;
9951
- }
9952
- currentStep = "project";
9953
- break;
9954
- }
9955
- case "project": {
9956
- const result = await showProjectScreen(memory);
9957
- if (result === "back") {
9958
- if (!config.isConfigured() && !auth.isLoggedIn()) {
9959
- currentStep = "auth";
9960
- }
9961
- break;
9962
- }
9963
- currentStep = "complete";
9964
- break;
9965
- }
9966
- case "complete": {
9967
- showCompleteScreen();
9968
- const ready = await p6.confirm({
9969
- message: "Start using CodeBakers?",
9970
- initialValue: true
9971
- });
9972
- if (p6.isCancel(ready)) {
9973
- currentStep = "project";
9974
- break;
9975
- }
9976
- return true;
9977
- }
9978
- }
9979
- }
9980
- }
9981
- function needsOnboarding(config, memory) {
9982
- const hasApiKey = config.isConfigured();
9983
- const hasAuth = auth.isLoggedIn();
9984
- return !hasApiKey && !hasAuth;
9985
- }
9986
- function sleep(ms) {
9987
- return new Promise((resolve) => setTimeout(resolve, ms));
9988
- }
9989
-
9990
9551
  // src/utils/folder-picker.ts
9991
9552
  import { execSync } from "child_process";
9992
- import path17 from "path";
9553
+ import path16 from "path";
9993
9554
  import os2 from "os";
9994
- import * as p7 from "@clack/prompts";
9555
+ import * as p6 from "@clack/prompts";
9995
9556
  async function pickFolder(message = "Select a folder") {
9996
9557
  const platform = os2.platform();
9997
9558
  try {
@@ -10084,24 +9645,24 @@ async function manualFolderEntry(message) {
10084
9645
  const commonPaths = [
10085
9646
  { label: "\u{1F4C1} Current folder", value: process.cwd() },
10086
9647
  { label: "\u{1F3E0} Home", value: homeDir },
10087
- { label: "\u{1F4C2} Desktop", value: path17.join(homeDir, "Desktop") },
10088
- { label: "\u{1F4C2} Documents", value: path17.join(homeDir, "Documents") },
10089
- { label: "\u{1F4C2} Downloads", value: path17.join(homeDir, "Downloads") },
9648
+ { label: "\u{1F4C2} Desktop", value: path16.join(homeDir, "Desktop") },
9649
+ { label: "\u{1F4C2} Documents", value: path16.join(homeDir, "Documents") },
9650
+ { label: "\u{1F4C2} Downloads", value: path16.join(homeDir, "Downloads") },
10090
9651
  { label: "\u270F\uFE0F Type custom path", value: "__custom__" }
10091
9652
  ];
10092
- const choice = await p7.select({
9653
+ const choice = await p6.select({
10093
9654
  message,
10094
- options: commonPaths.map((p9) => ({
10095
- value: p9.value,
10096
- label: p9.label,
10097
- hint: p9.value !== "__custom__" ? p9.value : void 0
9655
+ options: commonPaths.map((p8) => ({
9656
+ value: p8.value,
9657
+ label: p8.label,
9658
+ hint: p8.value !== "__custom__" ? p8.value : void 0
10098
9659
  }))
10099
9660
  });
10100
- if (p7.isCancel(choice)) {
9661
+ if (p6.isCancel(choice)) {
10101
9662
  return null;
10102
9663
  }
10103
9664
  if (choice === "__custom__") {
10104
- const customPath = await p7.text({
9665
+ const customPath = await p6.text({
10105
9666
  message: "Enter folder path:",
10106
9667
  placeholder: "/path/to/folder",
10107
9668
  validate: (value) => {
@@ -10109,7 +9670,7 @@ async function manualFolderEntry(message) {
10109
9670
  return void 0;
10110
9671
  }
10111
9672
  });
10112
- if (p7.isCancel(customPath)) {
9673
+ if (p6.isCancel(customPath)) {
10113
9674
  return null;
10114
9675
  }
10115
9676
  let cleanPath = customPath.trim();
@@ -10122,8 +9683,8 @@ async function manualFolderEntry(message) {
10122
9683
 
10123
9684
  // src/services/git.ts
10124
9685
  import simpleGit from "simple-git";
10125
- import fs17 from "fs-extra";
10126
- import path18 from "path";
9686
+ import fs16 from "fs-extra";
9687
+ import path17 from "path";
10127
9688
  var GitService = class {
10128
9689
  git;
10129
9690
  projectPath;
@@ -10132,7 +9693,7 @@ var GitService = class {
10132
9693
  this.git = simpleGit(projectPath);
10133
9694
  }
10134
9695
  async isRepo() {
10135
- return fs17.pathExists(path18.join(this.projectPath, ".git"));
9696
+ return fs16.pathExists(path17.join(this.projectPath, ".git"));
10136
9697
  }
10137
9698
  async init() {
10138
9699
  await this.git.init();
@@ -10243,7 +9804,7 @@ var VercelService = class {
10243
9804
  };
10244
9805
 
10245
9806
  // src/index.ts
10246
- var VERSION = "3.3.0";
9807
+ var VERSION = "4.1.0";
10247
9808
  async function main() {
10248
9809
  const config = new Config();
10249
9810
  const args = process.argv.slice(2);
@@ -10255,76 +9816,106 @@ async function main() {
10255
9816
  showHelp();
10256
9817
  return;
10257
9818
  }
10258
- if (args[0] === "setup") {
10259
- await runSetup(config);
10260
- return;
10261
- }
10262
9819
  if (args[0] === "login") {
10263
9820
  await auth.login();
10264
9821
  return;
10265
9822
  }
9823
+ if (args[0] === "logout") {
9824
+ auth.logout();
9825
+ return;
9826
+ }
10266
9827
  const memory = new Memory();
10267
9828
  await memory.init();
10268
- if (needsOnboarding(config, memory)) {
10269
- const completed = await runOnboarding(config, memory);
10270
- if (!completed) {
10271
- console.log("");
10272
- console.log(colors.muted(" Run `cb` again when you're ready."));
10273
- console.log("");
10274
- return;
10275
- }
10276
- }
10277
- showHeader();
10278
- console.log(colors.muted(` v${VERSION}`));
10279
- console.log("");
10280
9829
  const scanner = new ProjectScanner();
10281
9830
  const structure = await scanner.detectProject();
10282
9831
  const editor = new FileEditor();
10283
9832
  const git = new GitService();
10284
- const identity = memory.getIdentity();
10285
- if (identity.name) {
10286
- console.log(colors.white(` \u{1F4C1} ${identity.name}`) + colors.muted(` (${structure.framework || "Project"})`));
10287
- } else {
10288
- console.log(colors.white(` \u{1F4C1} ${structure.name}`) + colors.muted(` (${structure.framework || "Unknown"})`));
10289
- console.log(colors.dim(" Run /init to enable project memory"));
9833
+ if (!config.isConfigured() && !auth.isLoggedIn()) {
9834
+ console.log("");
9835
+ console.log(colors.white(" Welcome to CodeBakers"));
9836
+ console.log("");
9837
+ console.log(colors.muted(" You need an API key to continue."));
9838
+ console.log("");
9839
+ const apiKey = await p7.text({
9840
+ message: "Anthropic API Key:",
9841
+ placeholder: "sk-ant-api..."
9842
+ });
9843
+ if (p7.isCancel(apiKey) || !apiKey) {
9844
+ console.log("");
9845
+ console.log(colors.muted(" Get your key at: https://console.anthropic.com"));
9846
+ console.log(colors.muted(" Then run: cb"));
9847
+ console.log("");
9848
+ return;
9849
+ }
9850
+ config.setAnthropicKey(apiKey);
9851
+ console.log(colors.success(" \u2713 API key saved"));
9852
+ console.log("");
10290
9853
  }
10291
- console.log("");
10292
9854
  let ai;
10293
9855
  try {
10294
9856
  ai = new AIEngine(config, memory, scanner);
10295
9857
  } catch (error) {
10296
- showError(error instanceof Error ? error.message : "Failed to initialize AI");
9858
+ console.log("");
9859
+ console.log(colors.error(` \u2717 ${error instanceof Error ? error.message : "Failed to start"}`));
9860
+ console.log("");
9861
+ console.log(colors.muted(" Check your API key with: cb login"));
9862
+ console.log("");
10297
9863
  return;
10298
9864
  }
10299
9865
  const nlp2 = new NLPInterpreter();
10300
9866
  const modeManager = getModeManager();
10301
9867
  const suggestions = new SmartSuggestions(memory, scanner);
10302
9868
  const proactive = new ProactiveAssistant(memory, scanner, config);
10303
- console.log(colors.muted(" Just tell me what you need:"));
10304
- console.log(colors.dim(' "Add a login page" \u2022 "Fix the navbar" \u2022 /help'));
9869
+ console.clear();
9870
+ console.log("");
9871
+ console.log(colors.muted(" \u256D\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256E"));
9872
+ console.log(colors.muted(" \u2502") + colors.primary(" \u2B21 C O D E B A K E R S ") + colors.muted("\u2502"));
9873
+ console.log(colors.muted(" \u2502") + colors.dim(` v${VERSION} \u2022 58 patterns \u2022 auto-parallel `) + colors.muted("\u2502"));
9874
+ console.log(colors.muted(" \u2570\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256F"));
10305
9875
  console.log("");
9876
+ console.log(colors.muted(" \u{1F4C1} ") + colors.white(structure.name) + (structure.framework ? colors.dim(` (${structure.framework})`) : ""));
9877
+ console.log("");
9878
+ console.log(colors.dim(" Just tell me what you want to build or do."));
9879
+ console.log(colors.dim(" Type /help to see examples."));
9880
+ console.log("");
9881
+ const terminalHeight = process.stdout.rows || 24;
9882
+ const paddingLines = Math.max(0, terminalHeight - 14);
9883
+ console.log("\n".repeat(paddingLines));
10306
9884
  let lastAction;
10307
9885
  let running = true;
10308
9886
  while (running) {
10309
- const modePrompt = modeManager.getPrompt();
10310
- const input = await p8.text({
10311
- message: modePrompt,
10312
- placeholder: "What would you like to build? (Shift+Tab to change mode)"
9887
+ const input = await p7.text({
9888
+ message: colors.primary(">"),
9889
+ placeholder: 'e.g. "Add a login page" or "Check my code"'
10313
9890
  });
10314
- if (p8.isCancel(input)) {
9891
+ if (p7.isCancel(input)) {
10315
9892
  running = false;
10316
- showInfo("Goodbye! \u{1F44B}");
9893
+ console.log("");
9894
+ console.log(colors.muted(" Goodbye! \u{1F44B}"));
9895
+ console.log("");
10317
9896
  break;
10318
9897
  }
10319
9898
  const cmd = input.trim();
10320
9899
  if (!cmd) continue;
10321
- const suggestion = await suggestions.checkForSuggestions(cmd, lastAction);
10322
- if (suggestion) {
10323
- console.log(suggestions.formatSuggestion(suggestion));
9900
+ const smartSuggestion = await suggestions.checkForSuggestions(cmd, lastAction);
9901
+ if (smartSuggestion) {
9902
+ console.log(suggestions.formatSuggestion(smartSuggestion));
10324
9903
  }
10325
9904
  const result = await nlp2.interpret(cmd);
10326
9905
  if (result.type === "unclear") {
10327
- showWarning("I didn't understand that. Try rephrasing or type /help for commands.");
9906
+ console.log("");
9907
+ console.log(colors.muted(" I'm not sure what you want to do."));
9908
+ console.log("");
9909
+ const cmdSuggestions = getSuggestions(cmd);
9910
+ if (cmdSuggestions.length > 0) {
9911
+ console.log(colors.muted(" Did you mean:"));
9912
+ cmdSuggestions.forEach((s) => {
9913
+ console.log(colors.dim(` \u2022 ${s}`));
9914
+ });
9915
+ console.log("");
9916
+ }
9917
+ console.log(colors.dim(" Type /help for a list of commands"));
9918
+ console.log("");
10328
9919
  continue;
10329
9920
  }
10330
9921
  if (["undo", "clear", "deploy"].includes(result.type)) {
@@ -10335,7 +9926,9 @@ async function main() {
10335
9926
  switch (result.type) {
10336
9927
  case "quit":
10337
9928
  running = false;
10338
- showInfo("Goodbye! \u{1F44B}");
9929
+ console.log("");
9930
+ console.log(colors.muted(" Goodbye! \u{1F44B}"));
9931
+ console.log("");
10339
9932
  break;
10340
9933
  case "help":
10341
9934
  showCommandHelp();
@@ -10368,9 +9961,13 @@ async function main() {
10368
9961
  await memory.save();
10369
9962
  showSuccess(`Decision recorded: ${result.params.content}`);
10370
9963
  } else {
10371
- showInfo("What decision would you like to record?");
10372
- const decision = await p8.text({ message: "Decision:" });
10373
- if (!p8.isCancel(decision) && decision) {
9964
+ const decision = await p7.text({
9965
+ message: "What decision would you like to record?",
9966
+ placeholder: "e.g., Use Supabase for auth"
9967
+ });
9968
+ if (p7.isCancel(decision)) {
9969
+ console.log(colors.muted(" Cancelled"));
9970
+ } else if (decision) {
10374
9971
  memory.addDecision(decision);
10375
9972
  await memory.save();
10376
9973
  showSuccess(`Decision recorded: ${decision}`);
@@ -10383,9 +9980,13 @@ async function main() {
10383
9980
  await memory.save();
10384
9981
  showSuccess(`Rule added: ${result.params.content}`);
10385
9982
  } else {
10386
- showInfo("What rule would you like to add?");
10387
- const rule = await p8.text({ message: "Rule:" });
10388
- if (!p8.isCancel(rule) && rule) {
9983
+ const rule = await p7.text({
9984
+ message: "What rule would you like to add?",
9985
+ placeholder: "e.g., Always use TypeScript strict mode"
9986
+ });
9987
+ if (p7.isCancel(rule)) {
9988
+ console.log(colors.muted(" Cancelled"));
9989
+ } else if (rule) {
10389
9990
  memory.addCustomRule(rule);
10390
9991
  await memory.save();
10391
9992
  showSuccess(`Rule added: ${rule}`);
@@ -10401,8 +10002,10 @@ async function main() {
10401
10002
  if (result.params?.command) {
10402
10003
  await handleRunCommand(result.params.command, ctx);
10403
10004
  } else {
10404
- showInfo("Usage: /run <command>");
10405
- showInfo("Example: /run npm install lodash");
10005
+ console.log("");
10006
+ console.log(colors.muted(" Usage: /run <command>"));
10007
+ console.log(colors.dim(" Example: /run npm install lodash"));
10008
+ console.log("");
10406
10009
  }
10407
10010
  break;
10408
10011
  case "create":
@@ -10459,67 +10062,166 @@ async function main() {
10459
10062
  const detected = await inputHandler.detectAndProcess(cmd);
10460
10063
  if (detected.type !== "text") {
10461
10064
  console.log(inputHandler.formatDetection(detected));
10462
- const proceed = await p8.confirm({
10065
+ const proceed = await p7.confirm({
10463
10066
  message: `Process this ${detected.type}?`,
10464
10067
  initialValue: true
10465
10068
  });
10466
- if (!proceed || p8.isCancel(proceed)) break;
10069
+ if (!proceed || p7.isCancel(proceed)) {
10070
+ console.log(colors.muted(" Cancelled"));
10071
+ break;
10072
+ }
10467
10073
  const enhancedCmd = inputHandler.enhancePrompt(detected, cmd);
10468
- await handleGeneration(enhancedCmd, ctx);
10074
+ await handleAutomaticBuild(enhancedCmd, ctx);
10469
10075
  break;
10470
10076
  }
10471
- const currentMode = getModeManager().getMode();
10472
- if (currentMode === "plan") {
10473
- await handlePlanMode(cmd, ctx);
10474
- } else if (currentMode === "question") {
10475
- await handleQuestionMode(cmd, ctx);
10476
- } else if (currentMode === "agent") {
10477
- await handleAgentMode(cmd, ctx);
10478
- } else {
10479
- const isComplex = detectComplexRequest(cmd);
10480
- if (isComplex) {
10481
- console.log("");
10482
- console.log(colors.secondary(" \u{1F4A1} This looks like a complex request."));
10483
- console.log(colors.muted(" Parallel build could be ~3x faster."));
10484
- console.log("");
10485
- const useParallel = await p8.confirm({
10486
- message: "Use parallel build with 3 agents?",
10487
- initialValue: true
10488
- });
10489
- if (useParallel && !p8.isCancel(useParallel)) {
10490
- await handleParallelBuild(cmd, ctx);
10491
- break;
10492
- }
10493
- }
10494
- const templateManager = new TemplateManager();
10495
- const suggestedTemplate = templateManager.suggestTemplate(cmd);
10496
- if (suggestedTemplate) {
10497
- console.log("");
10498
- console.log(colors.secondary(` \u{1F4A1} I recommend the "${suggestedTemplate.name}" template.`));
10499
- console.log(colors.muted(` ${suggestedTemplate.description}`));
10500
- console.log("");
10501
- const useTemplate = await p8.confirm({
10502
- message: "Install this template first?",
10503
- initialValue: true
10504
- });
10505
- if (useTemplate && !p8.isCancel(useTemplate)) {
10506
- await handleInstallTemplate(suggestedTemplate.id, ctx);
10507
- showSuccess("Template installed! Now customizing...");
10508
- }
10509
- }
10510
- await handleGeneration(cmd, ctx);
10511
- const filesCreated = editor.getPendingChanges().length;
10512
- const followUp = await proactive.onAfterAction(cmd, filesCreated);
10513
- if (followUp) {
10514
- console.log(proactive.formatAction(followUp));
10515
- proactive.markSuggestionShown();
10516
- }
10517
- }
10077
+ await handleAutomaticBuild(cmd, ctx);
10078
+ lastAction = cmd;
10079
+ break;
10518
10080
  break;
10519
10081
  }
10520
10082
  proactive.updateLastActivity();
10521
10083
  }
10522
10084
  }
10085
+ async function handleAutomaticBuild(request, ctx) {
10086
+ const { config, memory, scanner, editor, ai } = ctx;
10087
+ memory.addMessage("user", request);
10088
+ memory.setActiveTask(request);
10089
+ await memory.createSnapshot(`Before: ${request.slice(0, 50)}`);
10090
+ const tasks = analyzeAndSplitTasks(request);
10091
+ if (tasks.length > 1) {
10092
+ console.log("");
10093
+ console.log(colors.muted(" \u256D\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256E"));
10094
+ console.log(colors.muted(" \u2502") + colors.primary(" \u26A1 Parallel Build ") + colors.muted("\u2502"));
10095
+ console.log(colors.muted(" \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\u2524"));
10096
+ tasks.forEach((task, i) => {
10097
+ const taskText = task.slice(0, 43);
10098
+ console.log(colors.muted(" \u2502") + colors.dim(` ${i + 1}. ${taskText}`.padEnd(47)) + colors.muted("\u2502"));
10099
+ });
10100
+ console.log(colors.muted(" \u2570\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256F"));
10101
+ console.log("");
10102
+ const hasParallel = await auth.checkFeature(FEATURES.parallel);
10103
+ if (hasParallel) {
10104
+ await runParallelBuild(tasks, ctx);
10105
+ } else {
10106
+ console.log(colors.dim(" Running sequentially (parallel requires Pro)..."));
10107
+ console.log("");
10108
+ for (const task of tasks) {
10109
+ await handleGeneration(task, ctx);
10110
+ }
10111
+ }
10112
+ } else {
10113
+ await handleGeneration(request, ctx);
10114
+ }
10115
+ }
10116
+ function analyzeAndSplitTasks(request) {
10117
+ const lowered = request.toLowerCase();
10118
+ const multiIndicators = [
10119
+ /\band\b/i,
10120
+ // "login and dashboard"
10121
+ /\+/,
10122
+ // "login + dashboard"
10123
+ /,\s*(?:and\s+)?/,
10124
+ // "login, dashboard, settings"
10125
+ /\bthen\b/i,
10126
+ // "login then dashboard"
10127
+ /\balso\b/i,
10128
+ // "also add settings"
10129
+ /\bplus\b/i
10130
+ // "plus a settings page"
10131
+ ];
10132
+ const hasMultiIndicator = multiIndicators.some((r) => r.test(request));
10133
+ if (!hasMultiIndicator) {
10134
+ return [request];
10135
+ }
10136
+ let tasks = request.split(/(?:\band\b|\+|,\s*(?:and\s+)?|\bthen\b|\balso\b|\bplus\b)/i).map((t) => t.trim()).filter((t) => t.length > 3);
10137
+ if (tasks.length <= 1) {
10138
+ return [request];
10139
+ }
10140
+ if (tasks.length > 4) {
10141
+ const first3 = tasks.slice(0, 3);
10142
+ const rest = tasks.slice(3).join(" and ");
10143
+ tasks = [...first3, rest];
10144
+ }
10145
+ return tasks;
10146
+ }
10147
+ async function runParallelBuild(tasks, ctx) {
10148
+ const { config, memory, scanner, editor } = ctx;
10149
+ if (!await auth.checkFeature(FEATURES.parallel)) {
10150
+ return;
10151
+ }
10152
+ const parallelSystem = new ParallelAgentSystem(config, memory, scanner, editor);
10153
+ let cancelled = false;
10154
+ const checkCancelled = () => cancelled;
10155
+ const onKeypress = (key) => {
10156
+ const str = key.toString();
10157
+ if (str === "\x1B" || str === "\x1B") {
10158
+ cancelled = true;
10159
+ parallelSystem.cancel();
10160
+ }
10161
+ };
10162
+ if (process.stdin.isTTY) {
10163
+ process.stdin.setRawMode(true);
10164
+ }
10165
+ process.stdin.resume();
10166
+ process.stdin.on("data", onKeypress);
10167
+ console.log(colors.muted(" Press ESC to cancel"));
10168
+ console.log("");
10169
+ try {
10170
+ const startTime = Date.now();
10171
+ const combinedRequest = tasks.join(" AND ");
10172
+ const result = await parallelSystem.build(
10173
+ combinedRequest,
10174
+ {
10175
+ onTaskPlan: (plannedTasks) => {
10176
+ console.log(colors.muted(` Planned ${plannedTasks.length} parallel tasks`));
10177
+ },
10178
+ onAgentStart: (taskId, name) => {
10179
+ console.log(colors.dim(` \u2192 Starting: ${name}`));
10180
+ },
10181
+ onAgentComplete: (taskId, agentResult) => {
10182
+ const status = agentResult.success ? colors.success("\u2713") : colors.error("\u2717");
10183
+ console.log(` ${status} ${taskId}: ${agentResult.files.length} files`);
10184
+ }
10185
+ },
10186
+ checkCancelled
10187
+ );
10188
+ process.stdin.removeListener("data", onKeypress);
10189
+ if (process.stdin.isTTY) {
10190
+ process.stdin.setRawMode(false);
10191
+ }
10192
+ const duration = ((Date.now() - startTime) / 1e3).toFixed(1);
10193
+ console.log("");
10194
+ if (result.cancelled) {
10195
+ console.log(colors.muted(" Cancelled"));
10196
+ } else if (result.success) {
10197
+ console.log(colors.success(` \u2713 Completed in ${duration}s`));
10198
+ console.log(colors.muted(` Files: ${result.totalFiles}`));
10199
+ if (result.mergeConflicts.length > 0) {
10200
+ console.log(colors.warning(` Conflicts: ${result.mergeConflicts.length}`));
10201
+ }
10202
+ } else {
10203
+ console.log(colors.error(" Build failed"));
10204
+ result.results.forEach((r) => {
10205
+ if (!r.success && r.errors.length > 0) {
10206
+ r.errors.forEach((e) => console.log(colors.dim(` \u2022 ${e}`)));
10207
+ }
10208
+ });
10209
+ }
10210
+ console.log("");
10211
+ } catch (error) {
10212
+ process.stdin.removeListener("data", onKeypress);
10213
+ if (process.stdin.isTTY) {
10214
+ process.stdin.setRawMode(false);
10215
+ }
10216
+ if (cancelled) {
10217
+ console.log("");
10218
+ console.log(colors.muted(" Cancelled"));
10219
+ console.log("");
10220
+ return;
10221
+ }
10222
+ showError(error instanceof Error ? error.message : "Parallel build failed");
10223
+ }
10224
+ }
10523
10225
  async function handleGeneration(request, ctx) {
10524
10226
  const { memory, editor, ai, scanner } = ctx;
10525
10227
  memory.addMessage("user", request);
@@ -10559,31 +10261,46 @@ async function handleGeneration(request, ctx) {
10559
10261
  return;
10560
10262
  }
10561
10263
  console.log("");
10264
+ console.log(colors.muted(" \u256D\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256E"));
10562
10265
  if (result.filesCreated.length > 0) {
10563
- console.log(colors.success(` \u2713 Created ${result.filesCreated.length} file(s)`));
10564
- for (const f of result.filesCreated) {
10565
- console.log(colors.muted(` + ${f}`));
10266
+ console.log(colors.muted(" \u2502") + colors.success(` \u2713 Created ${result.filesCreated.length} file(s)`.padEnd(47)) + colors.muted("\u2502"));
10267
+ for (const f of result.filesCreated.slice(0, 5)) {
10268
+ console.log(colors.muted(" \u2502") + colors.dim(` + ${f}`.padEnd(47)) + colors.muted("\u2502"));
10269
+ }
10270
+ if (result.filesCreated.length > 5) {
10271
+ console.log(colors.muted(" \u2502") + colors.dim(` ... and ${result.filesCreated.length - 5} more`.padEnd(47)) + colors.muted("\u2502"));
10566
10272
  }
10567
10273
  }
10568
10274
  if (result.filesModified.length > 0) {
10569
- console.log(colors.success(` \u2713 Modified ${result.filesModified.length} file(s)`));
10570
- for (const f of result.filesModified) {
10571
- console.log(colors.muted(` ~ ${f}`));
10275
+ console.log(colors.muted(" \u2502") + colors.warning(` ~ Modified ${result.filesModified.length} file(s)`.padEnd(47)) + colors.muted("\u2502"));
10276
+ for (const f of result.filesModified.slice(0, 5)) {
10277
+ console.log(colors.muted(" \u2502") + colors.dim(` ~ ${f}`.padEnd(47)) + colors.muted("\u2502"));
10278
+ }
10279
+ if (result.filesModified.length > 5) {
10280
+ console.log(colors.muted(" \u2502") + colors.dim(` ... and ${result.filesModified.length - 5} more`.padEnd(47)) + colors.muted("\u2502"));
10572
10281
  }
10573
10282
  }
10574
10283
  if (result.commandsRun.length > 0) {
10575
- console.log(colors.muted(` \u26A1 Ran: ${result.commandsRun.join(", ")}`));
10284
+ console.log(colors.muted(" \u2502") + colors.dim(` \u26A1 Ran: ${result.commandsRun.join(", ")}`.padEnd(47)) + colors.muted("\u2502"));
10576
10285
  }
10577
10286
  if (result.errors.length > 0) {
10578
- console.log("");
10579
- console.log(colors.error(` \u274C ${result.errors.length} error(s):`));
10580
- for (const e of result.errors.slice(0, 5)) {
10581
- console.log(colors.error(` \u2022 ${e}`));
10287
+ console.log(colors.muted(" \u2502") + colors.error(` \u2717 ${result.errors.length} error(s)`.padEnd(47)) + colors.muted("\u2502"));
10288
+ for (const e of result.errors.slice(0, 3)) {
10289
+ console.log(colors.muted(" \u2502") + colors.dim(` ${e.slice(0, 43)}`.padEnd(47)) + colors.muted("\u2502"));
10582
10290
  }
10583
10291
  }
10292
+ console.log(colors.muted(" \u2570\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256F"));
10584
10293
  if (result.success) {
10585
10294
  console.log("");
10586
- showSuccess("Done!");
10295
+ console.log(colors.success(" \u2713 Done!"));
10296
+ const totalFiles = result.filesCreated.length + result.filesModified.length;
10297
+ if (totalFiles > 0) {
10298
+ console.log("");
10299
+ console.log(colors.dim(" What's next?"));
10300
+ console.log(colors.dim(' \u2022 "Check my code for issues"'));
10301
+ console.log(colors.dim(' \u2022 "Generate tests"'));
10302
+ console.log(colors.dim(' \u2022 "Commit my changes"'));
10303
+ }
10587
10304
  }
10588
10305
  console.log("");
10589
10306
  memory.addMessage("assistant", `Created: ${result.filesCreated.join(", ")}. Modified: ${result.filesModified.join(", ")}`);
@@ -10641,7 +10358,7 @@ async function handleCreate(name, ctx) {
10641
10358
  const { config } = ctx;
10642
10359
  let projectName = name;
10643
10360
  if (!projectName) {
10644
- const nameInput = await p8.text({
10361
+ const nameInput = await p7.text({
10645
10362
  message: "Project name:",
10646
10363
  placeholder: "my-awesome-app",
10647
10364
  validate: (value) => {
@@ -10649,23 +10366,23 @@ async function handleCreate(name, ctx) {
10649
10366
  if (!/^[a-z0-9-]+$/.test(value)) return "Use lowercase letters, numbers, and hyphens only";
10650
10367
  }
10651
10368
  });
10652
- if (p8.isCancel(nameInput)) return;
10369
+ if (p7.isCancel(nameInput)) return;
10653
10370
  projectName = nameInput;
10654
10371
  }
10655
10372
  console.log("");
10656
- const github = await p8.confirm({
10373
+ const github = await p7.confirm({
10657
10374
  message: "Create GitHub repository?",
10658
10375
  initialValue: !!config.getGithubToken()
10659
10376
  });
10660
- const supabase = await p8.confirm({
10377
+ const supabase = await p7.confirm({
10661
10378
  message: "Create Supabase project?",
10662
10379
  initialValue: !!config.getSupabaseToken()
10663
10380
  });
10664
- const vercel = await p8.confirm({
10381
+ const vercel = await p7.confirm({
10665
10382
  message: "Deploy to Vercel?",
10666
10383
  initialValue: !!config.getVercelToken()
10667
10384
  });
10668
- if (p8.isCancel(github) || p8.isCancel(supabase) || p8.isCancel(vercel)) return;
10385
+ if (p7.isCancel(github) || p7.isCancel(supabase) || p7.isCancel(vercel)) return;
10669
10386
  const creator = new ProjectCreator(config);
10670
10387
  const spinner = ora3("Creating project...").start();
10671
10388
  const result = await creator.create(
@@ -10726,14 +10443,17 @@ async function handleInstallTemplate(templateId, ctx) {
10726
10443
  let id = templateId;
10727
10444
  if (!id) {
10728
10445
  const templates = templateManager.listTemplates();
10729
- const choice = await p8.select({
10446
+ const choice = await p7.select({
10730
10447
  message: "Choose a template:",
10731
10448
  options: templates.map((t) => ({
10732
10449
  value: t.id,
10733
10450
  label: `${t.name} - ${t.description}`
10734
10451
  }))
10735
10452
  });
10736
- if (p8.isCancel(choice)) return;
10453
+ if (p7.isCancel(choice)) {
10454
+ console.log(colors.muted(" Cancelled"));
10455
+ return;
10456
+ }
10737
10457
  id = choice;
10738
10458
  }
10739
10459
  const spinner = ora3(`Installing ${id}...`).start();
@@ -10809,11 +10529,11 @@ async function handleParallelBuild(request, ctx) {
10809
10529
  console.log(colors.muted(` + ${change.path}`));
10810
10530
  }
10811
10531
  console.log("");
10812
- const apply = await p8.confirm({
10532
+ const apply = await p7.confirm({
10813
10533
  message: "Apply all files?",
10814
10534
  initialValue: true
10815
10535
  });
10816
- if (apply && !p8.isCancel(apply)) {
10536
+ if (apply && !p7.isCancel(apply)) {
10817
10537
  const applied = await editor.apply();
10818
10538
  showSuccess(`Applied ${applied.length} files`);
10819
10539
  for (const file of applied) {
@@ -10839,124 +10559,18 @@ async function handleParallelBuild(request, ctx) {
10839
10559
  }
10840
10560
  console.log("");
10841
10561
  }
10842
- function detectComplexRequest(request) {
10843
- const lower = request.toLowerCase();
10844
- const features = [
10845
- "auth",
10846
- "login",
10847
- "signup",
10848
- "authentication",
10849
- "dashboard",
10850
- "admin",
10851
- "settings",
10852
- "profile",
10853
- "billing",
10854
- "payment",
10855
- "stripe",
10856
- "subscription",
10857
- "analytics",
10858
- "charts",
10859
- "users",
10860
- "user management",
10861
- "api",
10862
- "backend",
10863
- "database",
10864
- "crud"
10865
- ];
10866
- let featureCount = 0;
10867
- for (const feature of features) {
10868
- if (lower.includes(feature)) {
10869
- featureCount++;
10870
- }
10871
- }
10872
- const hasMultiple = lower.includes(" and ") || lower.includes(" with ") || lower.includes(", ") || lower.includes(" + ");
10873
- const scopeKeywords = ["full", "complete", "entire", "whole", "saas", "app", "application", "system"];
10874
- const hasScope = scopeKeywords.some((k) => lower.includes(k));
10875
- return featureCount >= 3 || featureCount >= 2 && hasMultiple || hasScope && featureCount >= 2;
10876
- }
10877
- async function handlePlanMode(request, ctx) {
10878
- const { config, memory, scanner } = ctx;
10879
- console.log("");
10880
- console.log(colors.primary(" \u{1F4CB} PLAN MODE") + colors.muted(" - Showing what will happen"));
10881
- console.log("");
10882
- const planExecutor = new PlanExecutor(config, memory, scanner);
10883
- const spinner = ora3("Creating plan...").start();
10884
- try {
10885
- const plan = await planExecutor.createPlan(request);
10886
- spinner.stop();
10887
- console.log(planExecutor.formatPlan(plan));
10888
- const execute = await p8.confirm({
10889
- message: "Execute this plan?",
10890
- initialValue: true
10891
- });
10892
- if (execute && !p8.isCancel(execute)) {
10893
- await handleGeneration(request, ctx);
10894
- }
10895
- } catch (error) {
10896
- spinner.stop();
10897
- showError(error instanceof Error ? error.message : "Plan failed");
10898
- }
10899
- }
10900
- async function handleQuestionMode(request, ctx) {
10901
- const { config, memory, scanner } = ctx;
10902
- const questionExecutor = new QuestionExecutor(config, memory, scanner);
10903
- try {
10904
- const questions = await questionExecutor.generateQuestions(request);
10905
- const result = await questionExecutor.askQuestions(questions);
10906
- if (Object.keys(result.answers).length > 0) {
10907
- questionExecutor.showSummary(result);
10908
- const enhancedRequest = request + questionExecutor.formatAnswersForPrompt(result);
10909
- await handleGeneration(enhancedRequest, ctx);
10910
- } else {
10911
- await handleGeneration(request, ctx);
10912
- }
10913
- } catch (error) {
10914
- showError(error instanceof Error ? error.message : "Question mode failed");
10915
- }
10916
- }
10917
- async function handleAgentMode(request, ctx) {
10918
- const { config, ai, memory, scanner, editor } = ctx;
10919
- await memory.createSnapshot(`Before agent: ${request.slice(0, 50)}`);
10920
- let cancelled = false;
10921
- const checkCancelled = () => cancelled;
10922
- const onKeypress = (key) => {
10923
- const str = key.toString();
10924
- if (str === "\x1B" || str === "\x1B") {
10925
- cancelled = true;
10926
- }
10927
- };
10928
- if (process.stdin.isTTY) {
10929
- process.stdin.setRawMode(true);
10930
- }
10931
- process.stdin.resume();
10932
- process.stdin.on("data", onKeypress);
10933
- const agentExecutor = new AgentExecutor(config, ai, memory, scanner, editor);
10934
- try {
10935
- const result = await agentExecutor.execute(request, { checkCancelled });
10936
- process.stdin.removeListener("data", onKeypress);
10937
- if (process.stdin.isTTY) {
10938
- process.stdin.setRawMode(false);
10939
- }
10940
- if (result.cancelled) {
10941
- showWarning("Cancelled.");
10942
- }
10943
- } catch (error) {
10944
- process.stdin.removeListener("data", onKeypress);
10945
- if (process.stdin.isTTY) {
10946
- process.stdin.setRawMode(false);
10947
- }
10948
- showError(error instanceof Error ? error.message : "Agent mode failed");
10949
- }
10950
- }
10951
10562
  async function handleTestGeneration(filePath, ctx) {
10952
10563
  const { config, scanner, editor } = ctx;
10953
10564
  const testGen = new TestGenerator(config, scanner);
10954
10565
  if (!filePath) {
10955
- const file = await p8.text({
10566
+ const file = await p7.text({
10956
10567
  message: "File to generate tests for:",
10957
10568
  placeholder: "src/components/Button.tsx"
10958
10569
  });
10959
- if (p8.isCancel(file) || !file) return;
10570
+ if (p7.isCancel(file) || !file) {
10571
+ console.log(colors.muted(" Cancelled"));
10572
+ return;
10573
+ }
10960
10574
  filePath = file;
10961
10575
  }
10962
10576
  const spinner = ora3(`Generating tests for ${filePath}...`).start();
@@ -10973,13 +10587,17 @@ async function handleTestGeneration(filePath, ctx) {
10973
10587
  console.log(colors.muted(" " + test.content.split("\n").slice(0, 10).join("\n ")));
10974
10588
  console.log(colors.muted(" ..."));
10975
10589
  console.log("");
10976
- const save = await p8.confirm({
10590
+ const save = await p7.confirm({
10977
10591
  message: `Save to ${test.testPath}?`,
10978
10592
  initialValue: true
10979
10593
  });
10980
- if (save && !p8.isCancel(save)) {
10594
+ if (p7.isCancel(save)) {
10595
+ console.log(colors.muted(" Test not saved"));
10596
+ } else if (save) {
10981
10597
  await testGen.writeTest(test);
10982
10598
  showSuccess(`Test saved to ${test.testPath}`);
10599
+ } else {
10600
+ console.log(colors.muted(" Test not saved"));
10983
10601
  }
10984
10602
  } catch (error) {
10985
10603
  spinner.stop();
@@ -11015,11 +10633,11 @@ async function handleLivePreview(ctx) {
11015
10633
  const { scanner } = ctx;
11016
10634
  const preview = new LivePreview(scanner);
11017
10635
  if (preview.isRunning()) {
11018
- const stop = await p8.confirm({
10636
+ const stop = await p7.confirm({
11019
10637
  message: "Preview is running. Stop it?",
11020
10638
  initialValue: false
11021
10639
  });
11022
- if (stop && !p8.isCancel(stop)) {
10640
+ if (stop && !p7.isCancel(stop)) {
11023
10641
  await preview.stop();
11024
10642
  showSuccess("Preview stopped");
11025
10643
  } else {
@@ -11047,28 +10665,38 @@ async function handleSpecGeneration(input, ctx) {
11047
10665
  return;
11048
10666
  }
11049
10667
  const { config, memory, scanner, editor, ai } = ctx;
10668
+ console.log("");
10669
+ console.log(colors.white(" \u{1F4DD} Spec Generator"));
10670
+ console.log(colors.muted(" \u2500".repeat(35)));
10671
+ console.log("");
11050
10672
  let description = input.replace(/^\/spec\s*/i, "").replace(/^\/prd\s*/i, "").replace(/^\/plan\s*/i, "").trim();
11051
10673
  if (!description) {
11052
- const desc = await p8.text({
10674
+ const desc = await p7.text({
11053
10675
  message: "What do you want to build?",
11054
10676
  placeholder: "An Uber for dog walkers"
11055
10677
  });
11056
- if (p8.isCancel(desc) || !desc) return;
10678
+ if (p7.isCancel(desc) || !desc) {
10679
+ console.log(colors.muted(" Spec generation cancelled"));
10680
+ console.log("");
10681
+ return;
10682
+ }
11057
10683
  description = desc;
11058
10684
  }
11059
10685
  const specGen = new SpecGenerator(config);
11060
10686
  try {
11061
10687
  const spec = await specGen.generate(description);
11062
10688
  if (!spec) {
11063
- showInfo("Spec generation cancelled");
10689
+ console.log("");
10690
+ console.log(colors.muted(" Spec generation cancelled"));
10691
+ console.log("");
11064
10692
  return;
11065
10693
  }
11066
10694
  console.log(specGen.formatSpec(spec));
11067
- const buildNow = await p8.confirm({
10695
+ const buildNow = await p7.confirm({
11068
10696
  message: "Build from this spec?",
11069
10697
  initialValue: true
11070
10698
  });
11071
- if (buildNow && !p8.isCancel(buildNow)) {
10699
+ if (buildNow && !p7.isCancel(buildNow)) {
11072
10700
  const builder = new ProjectBuilder(config, memory, scanner, editor, ai);
11073
10701
  let cancelled = false;
11074
10702
  const checkCancelled = () => cancelled;
@@ -11195,30 +10823,47 @@ async function handleAudit(args, ctx) {
11195
10823
  const auditor = new CodebaseAuditor(config);
11196
10824
  const git = new SmartGitManager(config);
11197
10825
  const subcommand = args?.split(" ")[0] || "";
10826
+ console.log("");
10827
+ console.log(colors.white(" \u{1F50D} Codebase Audit"));
10828
+ console.log(colors.muted(" \u2500".repeat(35)));
11198
10829
  switch (subcommand) {
11199
- case "fix": {
10830
+ case "fix":
10831
+ case "--fix": {
10832
+ console.log("");
11200
10833
  const spinner = ora3("Scanning codebase...").start();
11201
10834
  const report = await auditor.audit((file, current, total) => {
11202
10835
  spinner.text = `Scanning ${current}/${total}: ${file}`;
11203
10836
  });
11204
- spinner.stop();
10837
+ spinner.succeed("Scan complete");
11205
10838
  console.log(auditor.formatReport(report));
11206
10839
  if (report.totalIssues === 0) {
11207
- showSuccess("No issues found!");
10840
+ console.log("");
10841
+ showSuccess("No issues found! Your code is clean.");
10842
+ console.log("");
10843
+ console.log(colors.muted(" What you can do next:"));
10844
+ console.log(colors.dim(" \u2022 /review Get a full codebase review"));
10845
+ console.log(colors.dim(" \u2022 /spec Generate a product spec"));
10846
+ console.log("");
11208
10847
  return;
11209
10848
  }
11210
10849
  const fixType = args?.includes("critical") ? "critical" : args?.includes("all") ? "all" : null;
11211
10850
  let issuesToFix = report.critical;
11212
10851
  if (!fixType) {
11213
- const choice = await p8.select({
10852
+ const choice = await p7.select({
11214
10853
  message: "What would you like to fix?",
11215
10854
  options: [
11216
10855
  { value: "critical", label: `Critical issues only (${report.critical.length})` },
11217
10856
  { value: "warnings", label: `Critical + Warnings (${report.critical.length + report.warnings.length})` },
11218
- { value: "all", label: `Everything (${report.totalIssues})` }
10857
+ { value: "all", label: `Everything (${report.totalIssues})` },
10858
+ { value: "cancel", label: "\u2190 Back", hint: "Fix later" }
11219
10859
  ]
11220
10860
  });
11221
- if (p8.isCancel(choice)) return;
10861
+ if (p7.isCancel(choice) || choice === "cancel") {
10862
+ console.log("");
10863
+ console.log(colors.muted(" Auto-fix cancelled. You can run /audit fix anytime."));
10864
+ console.log("");
10865
+ return;
10866
+ }
11222
10867
  if (choice === "warnings") {
11223
10868
  issuesToFix = [...report.critical, ...report.warnings];
11224
10869
  } else if (choice === "all") {
@@ -11249,11 +10894,11 @@ async function handleAudit(args, ctx) {
11249
10894
  const files = result.changes.map((c) => c.file);
11250
10895
  await git.commitFiles(files, "fix: resolve codebase audit issues");
11251
10896
  console.log("");
11252
- const push = await p8.confirm({
10897
+ const push = await p7.confirm({
11253
10898
  message: "Push and create PR?",
11254
10899
  initialValue: true
11255
10900
  });
11256
- if (push && !p8.isCancel(push)) {
10901
+ if (push && !p7.isCancel(push)) {
11257
10902
  await git.push();
11258
10903
  const pr = await git.createPR(branchName, await git.getMainBranch(), "Fix: Codebase Audit Issues");
11259
10904
  if (pr?.url) {
@@ -11271,21 +10916,29 @@ async function handleAudit(args, ctx) {
11271
10916
  spinner.stop();
11272
10917
  const markdown = auditor.exportMarkdown(report);
11273
10918
  const reportPath = "AUDIT_REPORT.md";
11274
- await fs18.writeFile(reportPath, markdown);
10919
+ await fs17.writeFile(reportPath, markdown);
11275
10920
  console.log(auditor.formatReport(report));
11276
10921
  showSuccess(`Report saved to ${reportPath}`);
11277
10922
  break;
11278
10923
  }
11279
10924
  default: {
10925
+ console.log("");
11280
10926
  const spinner = ora3("Scanning codebase...").start();
11281
10927
  const report = await auditor.audit((file, current, total) => {
11282
10928
  spinner.text = `Scanning ${current}/${total}: ${file}`;
11283
10929
  });
11284
- spinner.stop();
10930
+ spinner.succeed("Scan complete");
11285
10931
  console.log(auditor.formatReport(report));
11286
- if (report.totalIssues > 0) {
11287
- console.log(colors.muted(" Use /audit fix to auto-fix issues"));
11288
- console.log(colors.muted(" Use /audit report to export markdown"));
10932
+ if (report.totalIssues === 0) {
10933
+ console.log("");
10934
+ showSuccess("No issues found! Your code is clean.");
10935
+ console.log("");
10936
+ } else {
10937
+ console.log("");
10938
+ console.log(colors.muted(" What you can do:"));
10939
+ console.log(colors.dim(" /audit fix Auto-fix issues"));
10940
+ console.log(colors.dim(" /audit report Export markdown report"));
10941
+ console.log("");
11289
10942
  }
11290
10943
  }
11291
10944
  }
@@ -11375,7 +11028,7 @@ async function handleFolderChange() {
11375
11028
  console.log(colors.muted(" Cancelled"));
11376
11029
  return;
11377
11030
  }
11378
- if (!await fs18.pathExists(folder)) {
11031
+ if (!await fs17.pathExists(folder)) {
11379
11032
  showError(`Folder not found: ${folder}`);
11380
11033
  return;
11381
11034
  }
@@ -11393,17 +11046,29 @@ async function initProject(ctx) {
11393
11046
  const { memory, scanner } = ctx;
11394
11047
  const structure = await scanner.detectProject();
11395
11048
  console.log("");
11396
- const name = await p8.text({
11049
+ console.log(colors.white(" \u{1F4C1} Initialize Project"));
11050
+ console.log(colors.muted(" \u2500".repeat(35)));
11051
+ console.log("");
11052
+ const name = await p7.text({
11397
11053
  message: "Project name:",
11398
- initialValue: structure.name
11054
+ initialValue: structure.name,
11055
+ placeholder: structure.name
11399
11056
  });
11400
- if (p8.isCancel(name)) return;
11401
- const description = await p8.text({
11402
- message: "Description:",
11057
+ if (p7.isCancel(name)) {
11058
+ console.log(colors.muted(" Cancelled"));
11059
+ console.log("");
11060
+ return;
11061
+ }
11062
+ const description = await p7.text({
11063
+ message: "Description (optional):",
11403
11064
  placeholder: "A brief description of your project"
11404
11065
  });
11405
- if (p8.isCancel(description)) return;
11406
- const stack = await p8.text({
11066
+ if (p7.isCancel(description)) {
11067
+ console.log(colors.muted(" Cancelled"));
11068
+ console.log("");
11069
+ return;
11070
+ }
11071
+ const stack = await p7.text({
11407
11072
  message: "Tech stack (comma-separated):",
11408
11073
  initialValue: [
11409
11074
  structure.framework,
@@ -11412,7 +11077,11 @@ async function initProject(ctx) {
11412
11077
  "Tailwind"
11413
11078
  ].filter(Boolean).join(", ")
11414
11079
  });
11415
- if (p8.isCancel(stack)) return;
11080
+ if (p7.isCancel(stack)) {
11081
+ console.log(colors.muted(" Cancelled"));
11082
+ console.log("");
11083
+ return;
11084
+ }
11416
11085
  memory.setIdentity(
11417
11086
  name,
11418
11087
  description,
@@ -11455,11 +11124,11 @@ async function undoChanges(ctx, steps) {
11455
11124
  const snapshot = snapshots[targetIndex];
11456
11125
  console.log("");
11457
11126
  console.log(colors.white(` Rolling back to: ${snapshot.description}`));
11458
- const confirm8 = await p8.confirm({
11127
+ const confirm7 = await p7.confirm({
11459
11128
  message: "Proceed with rollback?",
11460
11129
  initialValue: false
11461
11130
  });
11462
- if (confirm8 && !p8.isCancel(confirm8)) {
11131
+ if (confirm7 && !p7.isCancel(confirm7)) {
11463
11132
  const restored = await memory.rollback(snapshot.id);
11464
11133
  showSuccess(`Restored ${restored.length} file${restored.length > 1 ? "s" : ""}`);
11465
11134
  }
@@ -11467,11 +11136,11 @@ async function undoChanges(ctx, steps) {
11467
11136
  async function commitChanges(ctx, message) {
11468
11137
  const { git, memory } = ctx;
11469
11138
  if (!await git.isRepo()) {
11470
- const init = await p8.confirm({
11139
+ const init = await p7.confirm({
11471
11140
  message: "No git repo found. Initialize one?",
11472
11141
  initialValue: true
11473
11142
  });
11474
- if (init && !p8.isCancel(init)) {
11143
+ if (init && !p7.isCancel(init)) {
11475
11144
  await git.init();
11476
11145
  showSuccess("Git repository initialized");
11477
11146
  } else {
@@ -11496,13 +11165,13 @@ async function deployProject(ctx) {
11496
11165
  showError("Vercel CLI not installed. Run: npm i -g vercel");
11497
11166
  return;
11498
11167
  }
11499
- const prod = await p8.confirm({
11168
+ const prod = await p7.confirm({
11500
11169
  message: "Deploy to production?",
11501
11170
  initialValue: false
11502
11171
  });
11503
11172
  const spinner = ora3("Deploying...").start();
11504
11173
  try {
11505
- const result = await vercel.deploy(process.cwd(), prod && !p8.isCancel(prod));
11174
+ const result = await vercel.deploy(process.cwd(), prod && !p7.isCancel(prod));
11506
11175
  spinner.stop();
11507
11176
  showSuccess(`Deployed: ${result.url}`);
11508
11177
  } catch (error) {
@@ -11511,78 +11180,108 @@ async function deployProject(ctx) {
11511
11180
  }
11512
11181
  }
11513
11182
  async function reviewCodebase(ctx) {
11514
- const { ai } = ctx;
11515
- const includeAI = await p8.confirm({
11183
+ const { ai, memory } = ctx;
11184
+ console.log("");
11185
+ console.log(colors.white(" \u{1F4CA} Codebase Review"));
11186
+ console.log(colors.muted(" \u2500".repeat(35)));
11187
+ console.log("");
11188
+ const includeAI = await p7.confirm({
11516
11189
  message: "Include AI deep analysis? (slower but more thorough)",
11517
11190
  initialValue: true
11518
11191
  });
11519
- if (p8.isCancel(includeAI)) return;
11192
+ if (p7.isCancel(includeAI)) {
11193
+ console.log(colors.muted(" Review cancelled"));
11194
+ console.log("");
11195
+ return;
11196
+ }
11520
11197
  const analyzer = new CodebaseAnalyzer(
11521
11198
  process.cwd(),
11522
11199
  includeAI ? ai : void 0
11523
11200
  );
11524
- const spinner = ora3("Starting codebase review...").start();
11201
+ console.log("");
11202
+ const spinner = ora3({
11203
+ text: "Starting codebase review...",
11204
+ spinner: "dots"
11205
+ }).start();
11525
11206
  try {
11526
11207
  const report = await analyzer.fullReview((step, progress) => {
11527
11208
  spinner.text = `${step} (${Math.round(progress)}%)`;
11528
11209
  });
11529
- spinner.stop();
11210
+ spinner.succeed("Review complete!");
11211
+ console.log("");
11530
11212
  const formatted = analyzer.formatReport(report);
11531
11213
  console.log(formatted);
11532
- if (report.byCategory.patterns.length > 0) {
11533
- const fix = await p8.confirm({
11534
- message: `Fix ${report.byCategory.patterns.length} pattern violations automatically?`,
11535
- initialValue: false
11214
+ console.log(colors.muted(" \u2500".repeat(35)));
11215
+ console.log("");
11216
+ const hasIssues = report.summary.totalViolations > 0 || report.summary.totalIssues > 0;
11217
+ if (!hasIssues) {
11218
+ console.log(colors.success(" \u2713 Your codebase looks healthy!"));
11219
+ console.log("");
11220
+ console.log(colors.muted(" What you can do next:"));
11221
+ console.log(colors.dim(" \u2022 /audit Run a deeper audit with auto-fix"));
11222
+ console.log(colors.dim(" \u2022 /spec Generate a product spec"));
11223
+ console.log(colors.dim(" \u2022 /git Check your git status"));
11224
+ console.log("");
11225
+ } else {
11226
+ console.log(colors.warning(` Found ${report.summary.totalViolations + report.summary.totalIssues} issues to address`));
11227
+ console.log("");
11228
+ const action = await p7.select({
11229
+ message: "What would you like to do?",
11230
+ options: [
11231
+ {
11232
+ value: "fix",
11233
+ label: "\u{1F527} Auto-fix pattern violations",
11234
+ hint: `Fix ${report.byCategory.patterns.length} violations`
11235
+ },
11236
+ {
11237
+ value: "audit",
11238
+ label: "\u{1F50D} Run detailed audit",
11239
+ hint: "Get line-by-line fixes"
11240
+ },
11241
+ {
11242
+ value: "export",
11243
+ label: "\u{1F4C4} Export report",
11244
+ hint: "Save to file"
11245
+ },
11246
+ {
11247
+ value: "skip",
11248
+ label: "\u2190 Back to main",
11249
+ hint: "Address later"
11250
+ }
11251
+ ]
11536
11252
  });
11537
- if (fix && !p8.isCancel(fix)) {
11538
- showInfo("Use `/fix` command to auto-fix pattern violations (coming soon)");
11253
+ if (p7.isCancel(action) || action === "skip") {
11254
+ console.log("");
11255
+ console.log(colors.muted(" You can run /audit anytime to fix issues"));
11256
+ console.log("");
11257
+ return;
11258
+ }
11259
+ if (action === "fix") {
11260
+ console.log("");
11261
+ console.log(colors.muted(" Starting auto-fix..."));
11262
+ await handleAudit("--fix", ctx);
11263
+ } else if (action === "audit") {
11264
+ await handleAudit("", ctx);
11265
+ } else if (action === "export") {
11266
+ const reportPath = path18.join(process.cwd(), "codebase-review.md");
11267
+ await fs17.writeFile(reportPath, formatted.replace(/\x1b\[[0-9;]*m/g, ""));
11268
+ console.log("");
11269
+ showSuccess(`Report saved to ${reportPath}`);
11539
11270
  }
11540
11271
  }
11272
+ memory.addDecision(`Codebase reviewed: ${report.summary.healthScore}/100 health score, ${report.summary.totalViolations} violations`);
11273
+ await memory.save();
11541
11274
  } catch (error) {
11542
- spinner.stop();
11543
- showError(error instanceof Error ? error.message : "Review failed");
11544
- }
11545
- }
11546
- async function runSetup(config) {
11547
- console.log("");
11548
- console.log(colors.white(" Setup CodeBakers CLI"));
11549
- divider();
11550
- const apiKey = await p8.text({
11551
- message: "Anthropic API key:",
11552
- placeholder: "sk-ant-...",
11553
- validate: (value) => {
11554
- if (!value) return "Required";
11555
- if (!value.startsWith("sk-ant-")) return "Invalid format";
11556
- }
11557
- });
11558
- if (p8.isCancel(apiKey)) return;
11559
- config.setAnthropicKey(apiKey);
11560
- const setupGithub = await p8.confirm({
11561
- message: "Set up GitHub token? (optional)",
11562
- initialValue: false
11563
- });
11564
- if (setupGithub && !p8.isCancel(setupGithub)) {
11565
- const token = await p8.text({
11566
- message: "GitHub token:",
11567
- placeholder: "ghp_..."
11568
- });
11569
- if (!p8.isCancel(token) && token) {
11570
- config.setGithubToken(token);
11571
- }
11572
- }
11573
- const setupVercel = await p8.confirm({
11574
- message: "Set up Vercel token? (optional)",
11575
- initialValue: false
11576
- });
11577
- if (setupVercel && !p8.isCancel(setupVercel)) {
11578
- const token = await p8.text({
11579
- message: "Vercel token:"
11580
- });
11581
- if (!p8.isCancel(token) && token) {
11582
- config.setVercelToken(token);
11583
- }
11275
+ spinner.fail("Review failed");
11276
+ console.log("");
11277
+ showError(error instanceof Error ? error.message : "Unknown error");
11278
+ console.log("");
11279
+ console.log(colors.muted(" Try:"));
11280
+ console.log(colors.dim(" \u2022 Check if you're in a project directory"));
11281
+ console.log(colors.dim(" \u2022 Run /status to verify your setup"));
11282
+ console.log(colors.dim(' \u2022 Run without AI: answer "no" to deep analysis'));
11283
+ console.log("");
11584
11284
  }
11585
- showSuccess("Setup complete!");
11586
11285
  }
11587
11286
  function showHelp() {
11588
11287
  console.log(`
@@ -11602,33 +11301,78 @@ function showHelp() {
11602
11301
  `);
11603
11302
  }
11604
11303
  function showCommandHelp() {
11605
- console.log(`
11606
- ${colors.white("Modes (Shift+Tab to cycle):")}
11607
- [PLAN] Shows what will happen, then asks to execute
11608
- [QUESTION] Asks clarifying questions first
11609
- [AGENT] Runs autonomously without asking
11610
-
11611
- ${colors.white("Commands:")}
11612
- /spec <idea> \u{1F4C4} Generate spec + build from it
11613
- /parallel <req> \u{1F680} Parallel build (3x faster)
11614
- /create <name> One-click: GitHub + Supabase + Vercel
11615
- /templates List available templates
11616
- /install <id> Install a template
11617
- /test [file] Generate tests
11618
- /pr [title] Create pull request
11619
- /preview Start dev server
11620
- /review Codebase review
11621
- /run <cmd> Terminal command
11622
- /status Project status
11623
- /undo Rollback changes
11624
- /commit Git commit
11625
- /deploy Vercel deploy
11626
- /quit Exit
11627
-
11628
- ${colors.white("Examples:")}
11629
- /spec An Uber for dog walkers
11630
- /parallel Build auth + dashboard
11631
- ./design.png \u2192 Build from image
11632
- `);
11304
+ console.log("");
11305
+ console.log(colors.muted(" \u256D\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256E"));
11306
+ console.log(colors.muted(" \u2502") + colors.white(" Just tell me what you want ") + colors.muted("\u2502"));
11307
+ console.log(colors.muted(" \u2570\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256F"));
11308
+ console.log("");
11309
+ console.log(colors.primary(" Build & Create"));
11310
+ console.log(colors.dim(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
11311
+ console.log(colors.white(' "Add a login page with Google auth"'));
11312
+ console.log(colors.white(' "Build login, dashboard, and settings"'));
11313
+ console.log(colors.white(' "I want to build an Uber for dog walking"'));
11314
+ console.log("");
11315
+ console.log(colors.primary(" Review & Fix"));
11316
+ console.log(colors.dim(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
11317
+ console.log(colors.white(' "Check my code for issues"'));
11318
+ console.log(colors.white(` "What's wrong with this project?"`));
11319
+ console.log(colors.white(' "Fix the problems you find"'));
11320
+ console.log("");
11321
+ console.log(colors.primary(" Git & Deploy"));
11322
+ console.log(colors.dim(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
11323
+ console.log(colors.white(' "Commit my changes"'));
11324
+ console.log(colors.white(' "Push to GitHub"'));
11325
+ console.log(colors.white(' "Deploy to Vercel"'));
11326
+ console.log("");
11327
+ console.log(colors.primary(" Other"));
11328
+ console.log(colors.dim(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
11329
+ console.log(colors.white(' "Generate tests"') + colors.dim(' "Start dev server"'));
11330
+ console.log(colors.white(' "Create a PR"') + colors.dim(' "Undo that"'));
11331
+ console.log("");
11332
+ console.log(colors.dim(" Shortcuts: /help /quit /status /login"));
11333
+ console.log("");
11334
+ }
11335
+ function getSuggestions(input) {
11336
+ const lowered = input.toLowerCase();
11337
+ const suggestions = [];
11338
+ const mappings = {
11339
+ "reveiw": ["/review - Review your codebase"],
11340
+ "reviw": ["/review - Review your codebase"],
11341
+ "aduit": ["/audit - Audit code quality"],
11342
+ "auidt": ["/audit - Audit code quality"],
11343
+ "hlep": ["/help - Show all commands"],
11344
+ "helo": ["/help - Show all commands"],
11345
+ "init": ["/init - Initialize project"],
11346
+ "start": ["/preview - Start dev server", "/init - Initialize project"],
11347
+ "run": ["/run <command> - Run a terminal command"],
11348
+ "fix": ["/audit fix - Auto-fix issues"],
11349
+ "check": ["/audit - Check code quality", "/review - Review codebase"],
11350
+ "test": ["/test - Generate tests"],
11351
+ "build": ['"Build a login page" - Describe what you want'],
11352
+ "make": ['"Make a navbar" - Describe what you want'],
11353
+ "create": ["/create - Create new project", '"Create a dashboard" - Describe it'],
11354
+ "git": ["/git status - Check git", "/git commit - Commit changes"],
11355
+ "commit": ["/commit - Commit changes", '/git commit -m "message"'],
11356
+ "push": ["/git push - Push to remote"],
11357
+ "status": ["/status - Project status", "/git status - Git status"],
11358
+ "login": ["/login - Login to CodeBakers"],
11359
+ "logout": ["/logout - Logout"]
11360
+ };
11361
+ for (const [key, vals] of Object.entries(mappings)) {
11362
+ if (lowered.includes(key) || key.includes(lowered)) {
11363
+ suggestions.push(...vals);
11364
+ }
11365
+ }
11366
+ if (suggestions.length === 0) {
11367
+ if (lowered.length < 3) {
11368
+ suggestions.push("/help - Show all commands");
11369
+ } else {
11370
+ suggestions.push(
11371
+ '"' + input + '" - Try describing what you want to build',
11372
+ "/help - Show all commands"
11373
+ );
11374
+ }
11375
+ }
11376
+ return [...new Set(suggestions)].slice(0, 4);
11633
11377
  }
11634
11378
  main().catch(console.error);