claude-think 0.1.2 → 0.1.4

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.
@@ -19,7 +19,11 @@
19
19
  "Bash(git commit -m \"$\\(cat <<''EOF''\nv0.1.1: Fix plugin registration\n\n- Use correct .claude-plugin/ directory structure\n- Auto-register plugin in Claude settings.json on sync\n\nCo-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>\nEOF\n\\)\")",
20
20
  "Bash(bun src/cli/index.ts setup:*)",
21
21
  "Bash(printf:*)",
22
- "Bash(npm publish:*)"
22
+ "Bash(npm publish:*)",
23
+ "Bash(bun install:*)",
24
+ "Bash(bun:*)",
25
+ "Bash(git commit -m \"$\\(cat <<''EOF''\nv0.1.3: Fix --version to read from package.json\n\nCo-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>\nEOF\n\\)\")",
26
+ "Bash(git commit -m \"$\\(cat <<''EOF''\nv0.1.4: Improved setup wizard UX with @clack/prompts\n\n- Arrow key navigation and space to toggle checkboxes\n- Multi-select for frontend, CSS, database, infrastructure\n- Cleaner visual design with spinners and hints\n- Better cancel handling\n\nCo-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>\nEOF\n\\)\")"
23
27
  ]
24
28
  }
25
29
  }
package/CHANGELOG.md CHANGED
@@ -5,6 +5,19 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.1.4] - 2025-02-05
9
+
10
+ ### Changed
11
+ - Setup wizard now uses @clack/prompts for better UX
12
+ - Arrow key navigation and space to toggle checkboxes
13
+ - Multi-select for frontend, CSS, database, infrastructure (can pick multiple)
14
+ - Cleaner visual design with spinners and hints
15
+
16
+ ## [0.1.3] - 2025-02-05
17
+
18
+ ### Fixed
19
+ - `--version` now reads from package.json instead of hardcoded value
20
+
8
21
  ## [0.1.2] - 2025-02-05
9
22
 
10
23
  ### Added
package/bun.lock CHANGED
@@ -5,6 +5,7 @@
5
5
  "": {
6
6
  "name": "think",
7
7
  "dependencies": {
8
+ "@clack/prompts": "^1.0.0",
8
9
  "chalk": "^5.6.2",
9
10
  "commander": "^14.0.3",
10
11
  "gray-matter": "^4.0.3",
@@ -25,6 +26,10 @@
25
26
  "packages": {
26
27
  "@alcalzone/ansi-tokenize": ["@alcalzone/ansi-tokenize@0.2.4", "", { "dependencies": { "ansi-styles": "^6.2.1", "is-fullwidth-code-point": "^5.0.0" } }, "sha512-HTgrrTgZ9Jgeo6Z3oqbQ7lifOVvRR14vaDuBGPPUxk9Thm+vObaO4QfYYYWw4Zo5CWQDBEfsinFA6Gre+AqwNQ=="],
27
28
 
29
+ "@clack/core": ["@clack/core@1.0.0", "", { "dependencies": { "picocolors": "^1.0.0", "sisteransi": "^1.0.5" } }, "sha512-Orf9Ltr5NeiEuVJS8Rk2XTw3IxNC2Bic3ash7GgYeA8LJ/zmSNpSQ/m5UAhe03lA6KFgklzZ5KTHs4OAMA/SAQ=="],
30
+
31
+ "@clack/prompts": ["@clack/prompts@1.0.0", "", { "dependencies": { "@clack/core": "1.0.0", "picocolors": "^1.0.0", "sisteransi": "^1.0.5" } }, "sha512-rWPXg9UaCFqErJVQ+MecOaWsozjaxol4yjnmYcGNipAWzdaWa2x+VJmKfGq7L0APwBohQOYdHC+9RO4qRXej+A=="],
32
+
28
33
  "@types/bun": ["@types/bun@1.3.8", "", { "dependencies": { "bun-types": "1.3.8" } }, "sha512-3LvWJ2q5GerAXYxO2mffLTqOzEu5qnhEAlh48Vnu8WQfnmSwbgagjGZV6BoHKJztENYEDn6QmVd949W4uESRJA=="],
29
34
 
30
35
  "@types/node": ["@types/node@25.2.1", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-CPrnr8voK8vC6eEtyRzvMpgp3VyVRhgclonE7qYi6P9sXwYb59ucfrnmFBTaP0yUi8Gk4yZg/LlTJULGxvTNsg=="],
@@ -97,6 +102,8 @@
97
102
 
98
103
  "patch-console": ["patch-console@2.0.0", "", {}, "sha512-0YNdUceMdaQwoKce1gatDScmMo5pu/tfABfnzEqeG0gtTmd7mh/WcwgUjtAeOU7N8nFFlbQBnFK2gXW5fGvmMA=="],
99
104
 
105
+ "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="],
106
+
100
107
  "react": ["react@19.2.4", "", {}, "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ=="],
101
108
 
102
109
  "react-reconciler": ["react-reconciler@0.33.0", "", { "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { "react": "^19.2.0" } }, "sha512-KetWRytFv1epdpJc3J4G75I4WrplZE5jOL7Yq0p34+OVOKF4Se7WrdIdVC45XsSSmUTlht2FM/fM1FZb1mfQeA=="],
@@ -109,6 +116,8 @@
109
116
 
110
117
  "signal-exit": ["signal-exit@3.0.7", "", {}, "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="],
111
118
 
119
+ "sisteransi": ["sisteransi@1.0.5", "", {}, "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg=="],
120
+
112
121
  "slice-ansi": ["slice-ansi@7.1.2", "", { "dependencies": { "ansi-styles": "^6.2.1", "is-fullwidth-code-point": "^5.0.0" } }, "sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w=="],
113
122
 
114
123
  "sprintf-js": ["sprintf-js@1.0.3", "", {}, "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g=="],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-think",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "description": "Personal context manager for Claude - manage your preferences, patterns, and memory",
5
5
  "author": "Amit Feldman",
6
6
  "license": "MIT",
@@ -27,6 +27,7 @@
27
27
  "typescript": "^5"
28
28
  },
29
29
  "dependencies": {
30
+ "@clack/prompts": "^1.0.0",
30
31
  "chalk": "^5.6.2",
31
32
  "commander": "^14.0.3",
32
33
  "gray-matter": "^4.0.3",
@@ -1,29 +1,11 @@
1
- import { writeFile, readFile } from "fs/promises";
1
+ import { writeFile } from "fs/promises";
2
2
  import { existsSync } from "fs";
3
- import * as readline from "readline";
3
+ import * as p from "@clack/prompts";
4
4
  import chalk from "chalk";
5
5
  import { CONFIG, thinkPath } from "../../core/config";
6
6
  import { printBanner } from "../../core/banner";
7
7
  import { syncCommand } from "./sync";
8
8
 
9
- interface SetupAnswers {
10
- name: string;
11
- style: "direct" | "conversational" | "detailed";
12
- packageManager: "bun" | "pnpm" | "npm" | "yarn";
13
- languages: string[];
14
- backend: string;
15
- frontend: string;
16
- css: string;
17
- database: string;
18
- infrastructure: string;
19
- monorepo: string;
20
- testing: string[];
21
- linting: string[];
22
- validation: string[];
23
- editor: string;
24
- avoidAll: boolean;
25
- }
26
-
27
9
  /**
28
10
  * Interactive profile setup wizard
29
11
  */
@@ -34,289 +16,293 @@ export async function setupCommand(): Promise<void> {
34
16
  }
35
17
 
36
18
  printBanner();
37
- console.log(chalk.bold("Profile Setup\n"));
38
- console.log(chalk.dim("Let's configure your preferences.\n"));
39
-
40
- const rl = readline.createInterface({
41
- input: process.stdin,
42
- output: process.stdout,
43
- });
44
19
 
45
- const question = (prompt: string): Promise<string> => {
46
- return new Promise((resolve) => {
47
- rl.question(prompt, resolve);
48
- });
49
- };
20
+ p.intro(chalk.bgCyan(chalk.black(" think setup ")));
50
21
 
51
- const select = async (
52
- prompt: string,
53
- options: { key: string; label: string }[]
54
- ): Promise<string> => {
55
- console.log(chalk.cyan(prompt));
56
- options.forEach((opt, i) => {
57
- console.log(` ${chalk.green(i + 1)}) ${opt.label}`);
22
+ const name = await p.text({
23
+ message: "What's your name?",
24
+ placeholder: "Your name",
25
+ });
26
+ if (p.isCancel(name)) return handleCancel();
27
+
28
+ const style = await p.select({
29
+ message: "What communication style do you prefer?",
30
+ options: [
31
+ { value: "direct", label: "Direct & minimal", hint: "no fluff, just answers" },
32
+ { value: "conversational", label: "Conversational", hint: "friendly but efficient" },
33
+ { value: "detailed", label: "Detailed", hint: "thorough explanations" },
34
+ ],
35
+ });
36
+ if (p.isCancel(style)) return handleCancel();
37
+
38
+ const packageManager = await p.select({
39
+ message: "Preferred package manager?",
40
+ options: [
41
+ { value: "bun", label: "Bun" },
42
+ { value: "pnpm", label: "pnpm" },
43
+ { value: "npm", label: "npm" },
44
+ { value: "yarn", label: "yarn" },
45
+ ],
46
+ });
47
+ if (p.isCancel(packageManager)) return handleCancel();
48
+
49
+ let bunFeatures: string[] = [];
50
+ if (packageManager === "bun") {
51
+ const features = await p.multiselect({
52
+ message: "Bun features you use?",
53
+ options: [
54
+ { value: "catalog", label: "Dependency catalog" },
55
+ { value: "workspaces", label: "Bun workspaces" },
56
+ { value: "macros", label: "Bun macros" },
57
+ { value: "shell", label: "Bun shell ($``)" },
58
+ { value: "sqlite", label: "bun:sqlite" },
59
+ { value: "test", label: "bun test" },
60
+ ],
61
+ required: false,
58
62
  });
59
- const answer = await question(chalk.dim("Enter number: "));
60
- const idx = parseInt(answer) - 1;
61
- if (idx >= 0 && idx < options.length) {
62
- return options[idx].key;
63
- }
64
- return options[0].key;
65
- };
63
+ if (p.isCancel(features)) return handleCancel();
64
+ bunFeatures = features as string[];
65
+ }
66
66
 
67
- const multiSelect = async (
68
- prompt: string,
69
- options: { key: string; label: string }[]
70
- ): Promise<string[]> => {
71
- console.log(chalk.cyan(prompt));
72
- options.forEach((opt, i) => {
73
- console.log(` ${chalk.green(i + 1)}) ${opt.label}`);
74
- });
75
- const answer = await question(chalk.dim("Enter numbers (comma-separated): "));
76
- const indices = answer.split(",").map((s) => parseInt(s.trim()) - 1);
77
- return indices
78
- .filter((i) => i >= 0 && i < options.length)
79
- .map((i) => options[i].key);
67
+ const languages = await p.multiselect({
68
+ message: "Primary programming languages?",
69
+ options: [
70
+ { value: "TypeScript", label: "TypeScript" },
71
+ { value: "JavaScript", label: "JavaScript" },
72
+ { value: "Python", label: "Python" },
73
+ { value: "Ruby", label: "Ruby" },
74
+ { value: "Rust", label: "Rust" },
75
+ { value: "Go", label: "Go" },
76
+ { value: "Java", label: "Java" },
77
+ { value: "C#", label: "C#" },
78
+ { value: "PHP", label: "PHP" },
79
+ { value: "Elixir", label: "Elixir" },
80
+ ],
81
+ required: true,
82
+ });
83
+ if (p.isCancel(languages)) return handleCancel();
84
+
85
+ const backend = await p.select({
86
+ message: "Backend framework?",
87
+ options: [
88
+ { value: "none", label: "None / Custom" },
89
+ { value: "Rails", label: "Ruby on Rails" },
90
+ { value: "Django", label: "Django" },
91
+ { value: "FastAPI", label: "FastAPI" },
92
+ { value: "Express", label: "Express.js" },
93
+ { value: "Hono", label: "Hono" },
94
+ { value: "Phoenix", label: "Phoenix (Elixir)" },
95
+ { value: "Spring", label: "Spring Boot" },
96
+ { value: "ASP.NET", label: "ASP.NET Core" },
97
+ { value: "Laravel", label: "Laravel" },
98
+ ],
99
+ });
100
+ if (p.isCancel(backend)) return handleCancel();
101
+
102
+ const frontend = await p.multiselect({
103
+ message: "Frontend frameworks?",
104
+ options: [
105
+ { value: "React", label: "React" },
106
+ { value: "Vue", label: "Vue.js" },
107
+ { value: "Svelte", label: "Svelte" },
108
+ { value: "Angular", label: "Angular" },
109
+ { value: "Solid", label: "SolidJS" },
110
+ { value: "HTMX", label: "HTMX" },
111
+ ],
112
+ required: false,
113
+ });
114
+ if (p.isCancel(frontend)) return handleCancel();
115
+
116
+ const css = await p.multiselect({
117
+ message: "CSS / UI frameworks?",
118
+ options: [
119
+ { value: "Tailwind", label: "Tailwind CSS" },
120
+ { value: "Vuetify", label: "Vuetify" },
121
+ { value: "Bootstrap", label: "Bootstrap" },
122
+ { value: "Material UI", label: "Material UI" },
123
+ { value: "shadcn/ui", label: "shadcn/ui" },
124
+ { value: "CSS Modules", label: "CSS Modules" },
125
+ { value: "styled-components", label: "styled-components" },
126
+ ],
127
+ required: false,
128
+ });
129
+ if (p.isCancel(css)) return handleCancel();
130
+
131
+ const database = await p.multiselect({
132
+ message: "Databases?",
133
+ options: [
134
+ { value: "PostgreSQL", label: "PostgreSQL" },
135
+ { value: "MySQL", label: "MySQL" },
136
+ { value: "MongoDB", label: "MongoDB" },
137
+ { value: "SQLite", label: "SQLite" },
138
+ { value: "Redis", label: "Redis" },
139
+ { value: "Supabase", label: "Supabase" },
140
+ { value: "Firebase", label: "Firebase" },
141
+ ],
142
+ required: false,
143
+ });
144
+ if (p.isCancel(database)) return handleCancel();
145
+
146
+ const orm = await p.multiselect({
147
+ message: "ORM / database tools?",
148
+ options: [
149
+ { value: "Prisma", label: "Prisma" },
150
+ { value: "Drizzle", label: "Drizzle" },
151
+ { value: "TypeORM", label: "TypeORM" },
152
+ { value: "Kysely", label: "Kysely" },
153
+ { value: "Sequelize", label: "Sequelize" },
154
+ { value: "Mongoose", label: "Mongoose" },
155
+ { value: "ActiveRecord", label: "ActiveRecord (Rails)" },
156
+ { value: "SQLAlchemy", label: "SQLAlchemy" },
157
+ { value: "Django ORM", label: "Django ORM" },
158
+ ],
159
+ required: false,
160
+ });
161
+ if (p.isCancel(orm)) return handleCancel();
162
+
163
+ const auth = await p.multiselect({
164
+ message: "Authentication?",
165
+ options: [
166
+ { value: "better-auth", label: "better-auth" },
167
+ { value: "Auth.js", label: "Auth.js (NextAuth)" },
168
+ { value: "Lucia", label: "Lucia" },
169
+ { value: "Clerk", label: "Clerk" },
170
+ { value: "Supabase Auth", label: "Supabase Auth" },
171
+ { value: "Firebase Auth", label: "Firebase Auth" },
172
+ { value: "Passport.js", label: "Passport.js" },
173
+ { value: "Devise", label: "Devise (Rails)" },
174
+ ],
175
+ required: false,
176
+ });
177
+ if (p.isCancel(auth)) return handleCancel();
178
+
179
+ const infrastructure = await p.multiselect({
180
+ message: "Infrastructure / deployment?",
181
+ options: [
182
+ { value: "Docker Compose", label: "Docker + Compose" },
183
+ { value: "Docker", label: "Docker only" },
184
+ { value: "Kubernetes", label: "Kubernetes" },
185
+ { value: "Fly.io", label: "Fly.io" },
186
+ { value: "Vercel", label: "Vercel" },
187
+ { value: "Railway", label: "Railway" },
188
+ { value: "Render", label: "Render" },
189
+ ],
190
+ required: false,
191
+ });
192
+ if (p.isCancel(infrastructure)) return handleCancel();
193
+
194
+ const monorepo = await p.select({
195
+ message: "Monorepo tooling?",
196
+ options: [
197
+ { value: "none", label: "None / Single repo" },
198
+ { value: "Turborepo", label: "Turborepo" },
199
+ { value: "Bun workspaces", label: "Bun workspaces" },
200
+ { value: "Nx", label: "Nx" },
201
+ { value: "pnpm workspaces", label: "pnpm workspaces" },
202
+ { value: "Lerna", label: "Lerna" },
203
+ ],
204
+ });
205
+ if (p.isCancel(monorepo)) return handleCancel();
206
+
207
+ const testing = await p.multiselect({
208
+ message: "Testing frameworks?",
209
+ options: [
210
+ { value: "bun test", label: "bun test" },
211
+ { value: "Vitest", label: "Vitest" },
212
+ { value: "Jest", label: "Jest" },
213
+ { value: "RSpec", label: "RSpec" },
214
+ { value: "pytest", label: "pytest" },
215
+ { value: "Playwright", label: "Playwright (E2E)" },
216
+ { value: "Cypress", label: "Cypress (E2E)" },
217
+ ],
218
+ required: false,
219
+ });
220
+ if (p.isCancel(testing)) return handleCancel();
221
+
222
+ const linting = await p.multiselect({
223
+ message: "Linting & formatting?",
224
+ options: [
225
+ { value: "Biome", label: "Biome" },
226
+ { value: "ESLint", label: "ESLint" },
227
+ { value: "Prettier", label: "Prettier" },
228
+ { value: "oxlint", label: "oxlint" },
229
+ { value: "Rubocop", label: "Rubocop" },
230
+ { value: "Ruff", label: "Ruff (Python)" },
231
+ { value: "rustfmt", label: "rustfmt" },
232
+ { value: "gofmt", label: "gofmt" },
233
+ ],
234
+ required: false,
235
+ });
236
+ if (p.isCancel(linting)) return handleCancel();
237
+
238
+ const validation = await p.multiselect({
239
+ message: "Validation & schema libraries?",
240
+ options: [
241
+ { value: "Zod", label: "Zod" },
242
+ { value: "Yup", label: "Yup" },
243
+ { value: "Valibot", label: "Valibot" },
244
+ { value: "ArkType", label: "ArkType" },
245
+ { value: "io-ts", label: "io-ts" },
246
+ { value: "TypeBox", label: "TypeBox" },
247
+ { value: "Pydantic", label: "Pydantic" },
248
+ { value: "Joi", label: "Joi" },
249
+ ],
250
+ required: false,
251
+ });
252
+ if (p.isCancel(validation)) return handleCancel();
253
+
254
+ const editor = await p.select({
255
+ message: "Primary editor?",
256
+ options: [
257
+ { value: "Zed", label: "Zed" },
258
+ { value: "VS Code", label: "VS Code" },
259
+ { value: "Cursor", label: "Cursor" },
260
+ { value: "Neovim", label: "Neovim" },
261
+ { value: "WebStorm", label: "WebStorm" },
262
+ ],
263
+ });
264
+ if (p.isCancel(editor)) return handleCancel();
265
+
266
+ const avoidAnswer = await p.select({
267
+ message: "What should Claude avoid?",
268
+ options: [
269
+ { value: "all", label: "All", hint: "over-engineering, verbose explanations, extra features" },
270
+ { value: "some", label: "Just over-engineering" },
271
+ { value: "none", label: "No specific restrictions" },
272
+ ],
273
+ });
274
+ if (p.isCancel(avoidAnswer)) return handleCancel();
275
+
276
+ const s = p.spinner();
277
+ s.start("Generating profile");
278
+
279
+ // Generate profile
280
+ const styleDescriptions: Record<string, string[]> = {
281
+ direct: [
282
+ "Be direct and minimal - no fluff, just answers and code",
283
+ "Skip lengthy reasoning unless asked",
284
+ "Don't explain obvious things",
285
+ ],
286
+ conversational: [
287
+ "Be friendly but efficient",
288
+ "Brief explanations when helpful",
289
+ "Keep a conversational tone",
290
+ ],
291
+ detailed: [
292
+ "Provide thorough explanations",
293
+ "Include context and reasoning",
294
+ "Explain trade-offs and alternatives",
295
+ ],
80
296
  };
81
297
 
82
- try {
83
- // Name
84
- const name = await question(chalk.cyan("What's your name? "));
85
- console.log();
86
-
87
- // Communication style
88
- const style = await select("What communication style do you prefer?", [
89
- { key: "direct", label: "Direct & minimal - no fluff, just answers" },
90
- { key: "conversational", label: "Conversational - friendly but efficient" },
91
- { key: "detailed", label: "Detailed - thorough explanations" },
92
- ]);
93
- console.log();
94
-
95
- // Package manager
96
- const packageManager = await select("Preferred package manager?", [
97
- { key: "bun", label: "bun" },
98
- { key: "pnpm", label: "pnpm" },
99
- { key: "npm", label: "npm" },
100
- { key: "yarn", label: "yarn" },
101
- ]);
102
- console.log();
103
-
104
- // Bun-specific features
105
- let bunFeatures: string[] = [];
106
- if (packageManager === "bun") {
107
- bunFeatures = await multiSelect("Bun features you use?", [
108
- { key: "catalog", label: "Dependency catalog" },
109
- { key: "workspaces", label: "Bun workspaces" },
110
- { key: "macros", label: "Bun macros" },
111
- { key: "shell", label: "Bun shell ($``)" },
112
- { key: "sqlite", label: "bun:sqlite" },
113
- { key: "test", label: "bun test" },
114
- ]);
115
- console.log();
116
- }
117
-
118
- // Languages
119
- const languages = await multiSelect("Primary programming languages?", [
120
- { key: "TypeScript", label: "TypeScript" },
121
- { key: "JavaScript", label: "JavaScript" },
122
- { key: "Python", label: "Python" },
123
- { key: "Ruby", label: "Ruby" },
124
- { key: "Rust", label: "Rust" },
125
- { key: "Go", label: "Go" },
126
- { key: "Java", label: "Java" },
127
- { key: "C#", label: "C#" },
128
- { key: "PHP", label: "PHP" },
129
- { key: "Elixir", label: "Elixir" },
130
- ]);
131
- console.log();
132
-
133
- // Backend framework
134
- const backend = await select("Backend framework?", [
135
- { key: "none", label: "None / Custom" },
136
- { key: "Rails", label: "Ruby on Rails" },
137
- { key: "Django", label: "Django" },
138
- { key: "FastAPI", label: "FastAPI" },
139
- { key: "Express", label: "Express.js" },
140
- { key: "Hono", label: "Hono" },
141
- { key: "Phoenix", label: "Phoenix (Elixir)" },
142
- { key: "Spring", label: "Spring Boot" },
143
- { key: "ASP.NET", label: "ASP.NET Core" },
144
- { key: "Laravel", label: "Laravel" },
145
- ]);
146
- console.log();
147
-
148
- // Frontend framework
149
- const frontend = await select("Frontend framework?", [
150
- { key: "none", label: "None / Backend only" },
151
- { key: "React", label: "React" },
152
- { key: "Vue", label: "Vue.js" },
153
- { key: "Svelte", label: "Svelte" },
154
- { key: "Angular", label: "Angular" },
155
- { key: "Solid", label: "SolidJS" },
156
- { key: "HTMX", label: "HTMX" },
157
- ]);
158
- console.log();
159
-
160
- // CSS/UI framework
161
- const css = await select("CSS / UI framework?", [
162
- { key: "none", label: "Plain CSS / None" },
163
- { key: "Tailwind", label: "Tailwind CSS" },
164
- { key: "Vuetify", label: "Vuetify" },
165
- { key: "Bootstrap", label: "Bootstrap" },
166
- { key: "Material UI", label: "Material UI" },
167
- { key: "shadcn/ui", label: "shadcn/ui" },
168
- { key: "CSS Modules", label: "CSS Modules" },
169
- { key: "styled-components", label: "styled-components" },
170
- ]);
171
- console.log();
172
-
173
- // Database
174
- const database = await select("Primary database?", [
175
- { key: "none", label: "None" },
176
- { key: "PostgreSQL", label: "PostgreSQL" },
177
- { key: "MySQL", label: "MySQL" },
178
- { key: "MongoDB", label: "MongoDB" },
179
- { key: "SQLite", label: "SQLite" },
180
- { key: "Redis", label: "Redis" },
181
- { key: "Supabase", label: "Supabase" },
182
- { key: "Firebase", label: "Firebase" },
183
- ]);
184
- console.log();
185
-
186
- // ORM / Database tools
187
- const orm = await multiSelect("ORM / database tools?", [
188
- { key: "Prisma", label: "Prisma" },
189
- { key: "Drizzle", label: "Drizzle" },
190
- { key: "TypeORM", label: "TypeORM" },
191
- { key: "Kysely", label: "Kysely" },
192
- { key: "Sequelize", label: "Sequelize" },
193
- { key: "Mongoose", label: "Mongoose" },
194
- { key: "ActiveRecord", label: "ActiveRecord (Rails)" },
195
- { key: "SQLAlchemy", label: "SQLAlchemy" },
196
- { key: "Django ORM", label: "Django ORM" },
197
- ]);
198
- console.log();
199
-
200
- // Auth
201
- const auth = await multiSelect("Authentication?", [
202
- { key: "better-auth", label: "better-auth" },
203
- { key: "Auth.js", label: "Auth.js (NextAuth)" },
204
- { key: "Lucia", label: "Lucia" },
205
- { key: "Clerk", label: "Clerk" },
206
- { key: "Supabase Auth", label: "Supabase Auth" },
207
- { key: "Firebase Auth", label: "Firebase Auth" },
208
- { key: "Passport.js", label: "Passport.js" },
209
- { key: "Devise", label: "Devise (Rails)" },
210
- ]);
211
- console.log();
212
-
213
- // Infrastructure
214
- const infrastructure = await select("Infrastructure / containerization?", [
215
- { key: "none", label: "None" },
216
- { key: "Docker Compose", label: "Docker + Compose" },
217
- { key: "Docker", label: "Docker only" },
218
- { key: "Kubernetes", label: "Kubernetes" },
219
- { key: "Fly.io", label: "Fly.io" },
220
- { key: "Vercel", label: "Vercel" },
221
- { key: "Railway", label: "Railway" },
222
- ]);
223
- console.log();
224
-
225
- // Monorepo
226
- const monorepo = await select("Monorepo tooling?", [
227
- { key: "none", label: "None / Single repo" },
228
- { key: "Turborepo", label: "Turborepo" },
229
- { key: "Bun workspaces", label: "Bun workspaces" },
230
- { key: "Nx", label: "Nx" },
231
- { key: "pnpm workspaces", label: "pnpm workspaces" },
232
- { key: "Lerna", label: "Lerna" },
233
- ]);
234
- console.log();
235
-
236
- // Testing
237
- const testing = await multiSelect("Testing frameworks?", [
238
- { key: "bun test", label: "bun test" },
239
- { key: "Vitest", label: "Vitest" },
240
- { key: "Jest", label: "Jest" },
241
- { key: "RSpec", label: "RSpec" },
242
- { key: "pytest", label: "pytest" },
243
- { key: "Playwright", label: "Playwright (E2E)" },
244
- { key: "Cypress", label: "Cypress (E2E)" },
245
- ]);
246
- console.log();
247
-
248
- // Linting/Formatting
249
- const linting = await multiSelect("Linting & formatting?", [
250
- { key: "Biome", label: "Biome" },
251
- { key: "ESLint", label: "ESLint" },
252
- { key: "Prettier", label: "Prettier" },
253
- { key: "oxlint", label: "oxlint" },
254
- { key: "Rubocop", label: "Rubocop" },
255
- { key: "Ruff", label: "Ruff (Python)" },
256
- { key: "rustfmt", label: "rustfmt" },
257
- { key: "gofmt", label: "gofmt" },
258
- ]);
259
- console.log();
260
-
261
- // Validation/Schema
262
- const validation = await multiSelect("Validation & schema libraries?", [
263
- { key: "Zod", label: "Zod" },
264
- { key: "Yup", label: "Yup" },
265
- { key: "Valibot", label: "Valibot" },
266
- { key: "ArkType", label: "ArkType" },
267
- { key: "io-ts", label: "io-ts" },
268
- { key: "TypeBox", label: "TypeBox" },
269
- { key: "Pydantic", label: "Pydantic" },
270
- { key: "Joi", label: "Joi" },
271
- ]);
272
- console.log();
273
-
274
- // Editor
275
- const editor = await select("Primary editor?", [
276
- { key: "Zed", label: "Zed" },
277
- { key: "VS Code", label: "VS Code" },
278
- { key: "Cursor", label: "Cursor" },
279
- { key: "Neovim", label: "Neovim" },
280
- ]);
281
- console.log();
282
-
283
- // Avoid patterns
284
- const avoidAnswer = await select("What should Claude avoid?", [
285
- { key: "all", label: "All: over-engineering, verbose explanations, extra features" },
286
- { key: "some", label: "Just over-engineering" },
287
- { key: "none", label: "No specific restrictions" },
288
- ]);
289
- console.log();
290
-
291
- rl.close();
292
-
293
- // Generate profile
294
- const styleDescriptions: Record<string, string[]> = {
295
- direct: [
296
- "Be direct and minimal - no fluff, just answers and code",
297
- "Skip lengthy reasoning unless asked",
298
- "Don't explain obvious things",
299
- ],
300
- conversational: [
301
- "Be friendly but efficient",
302
- "Brief explanations when helpful",
303
- "Keep a conversational tone",
304
- ],
305
- detailed: [
306
- "Provide thorough explanations",
307
- "Include context and reasoning",
308
- "Explain trade-offs and alternatives",
309
- ],
310
- };
311
-
312
- const profileContent = `---
298
+ const profileContent = `---
313
299
  name: ${name}
314
300
  style: ${style}
315
301
  ---
316
302
 
317
303
  # Communication Preferences
318
304
 
319
- ${styleDescriptions[style].map((s) => `- ${s}`).join("\n")}
305
+ ${styleDescriptions[style as string].map((s) => `- ${s}`).join("\n")}
320
306
  - No emojis unless explicitly requested
321
307
  - Show code when it's clearer than explanation
322
308
 
@@ -326,136 +312,127 @@ ${styleDescriptions[style].map((s) => `- ${s}`).join("\n")}
326
312
  - Match existing code patterns in the project
327
313
  `;
328
314
 
329
- await writeFile(thinkPath(CONFIG.files.profile), profileContent);
330
-
331
- // Generate tools preferences
332
- const toolsSections: string[] = ["# Tool Preferences"];
333
-
334
- // Runtime & Package Manager
335
- const pmAlternatives = ["npm", "pnpm", "yarn", "Node.js"].filter(
336
- (p) => p.toLowerCase() !== packageManager.toLowerCase()
337
- );
338
- const pmSection = [`- Use ${packageManager === "bun" ? "Bun" : packageManager}${pmAlternatives.length ? ` (not ${pmAlternatives.join(", ")})` : ""}`];
339
-
340
- if (bunFeatures.length > 0) {
341
- if (bunFeatures.includes("catalog")) pmSection.push("- Use Bun dependency catalog for shared deps");
342
- if (bunFeatures.includes("workspaces")) pmSection.push("- Use Bun workspaces for monorepos");
343
- if (bunFeatures.includes("macros")) pmSection.push("- Use Bun macros for compile-time code");
344
- if (bunFeatures.includes("shell")) pmSection.push("- Use Bun shell ($``) for shell commands");
345
- if (bunFeatures.includes("sqlite")) pmSection.push("- Use bun:sqlite for embedded database");
346
- if (bunFeatures.includes("test")) pmSection.push("- Use `bun test` for testing");
347
- } else {
348
- pmSection.push(`- Use \`${packageManager} test\` for testing`);
349
- }
315
+ await writeFile(thinkPath(CONFIG.files.profile), profileContent);
316
+
317
+ // Generate tools preferences
318
+ const toolsSections: string[] = ["# Tool Preferences"];
319
+
320
+ // Runtime & Package Manager
321
+ const pmAlternatives = ["npm", "pnpm", "yarn", "Node.js"].filter(
322
+ (pm) => pm.toLowerCase() !== (packageManager as string).toLowerCase()
323
+ );
324
+ const pmSection = [`- Use ${packageManager === "bun" ? "Bun" : packageManager}${pmAlternatives.length ? ` (not ${pmAlternatives.join(", ")})` : ""}`];
325
+
326
+ if (bunFeatures.length > 0) {
327
+ if (bunFeatures.includes("catalog")) pmSection.push("- Use Bun dependency catalog for shared deps");
328
+ if (bunFeatures.includes("workspaces")) pmSection.push("- Use Bun workspaces for monorepos");
329
+ if (bunFeatures.includes("macros")) pmSection.push("- Use Bun macros for compile-time code");
330
+ if (bunFeatures.includes("shell")) pmSection.push("- Use Bun shell ($``) for shell commands");
331
+ if (bunFeatures.includes("sqlite")) pmSection.push("- Use bun:sqlite for embedded database");
332
+ if (bunFeatures.includes("test")) pmSection.push("- Use `bun test` for testing");
333
+ } else {
334
+ pmSection.push(`- Use \`${packageManager} test\` for testing`);
335
+ }
350
336
 
351
- toolsSections.push(`
337
+ toolsSections.push(`
352
338
  ## Runtime & Package Manager
353
339
  ${pmSection.join("\n")}`);
354
340
 
355
- // Languages
356
- toolsSections.push(`
341
+ // Languages
342
+ toolsSections.push(`
357
343
  ## Languages
358
- ${languages.map((l) => `- ${l}`).join("\n")}`);
344
+ ${(languages as string[]).map((l) => `- ${l}`).join("\n")}`);
359
345
 
360
- // Backend
361
- if (backend !== "none") {
362
- toolsSections.push(`
346
+ // Backend
347
+ if (backend !== "none") {
348
+ toolsSections.push(`
363
349
  ## Backend
364
350
  - ${backend}`);
365
- }
351
+ }
366
352
 
367
- // Frontend
368
- if (frontend !== "none") {
369
- const frontendAlternatives = ["Next.js", "Vue", "Svelte", "Angular"].filter(
370
- (f) => f.toLowerCase() !== frontend.toLowerCase()
371
- );
372
- toolsSections.push(`
353
+ // Frontend
354
+ if ((frontend as string[]).length > 0) {
355
+ toolsSections.push(`
373
356
  ## Frontend
374
- - ${frontend}${frontendAlternatives.length ? ` (not ${frontendAlternatives.slice(0, 2).join(", ")})` : ""}
357
+ ${(frontend as string[]).map((f) => `- ${f}`).join("\n")}
375
358
  - Prefer functional components with hooks`);
359
+ }
376
360
 
377
- // CSS
378
- if (css !== "none") {
379
- toolsSections.push(`- ${css} for styling`);
380
- }
381
- }
361
+ // CSS
362
+ if ((css as string[]).length > 0) {
363
+ toolsSections.push(`
364
+ ## CSS / UI
365
+ ${(css as string[]).map((c) => `- ${c}`).join("\n")}`);
366
+ }
382
367
 
383
- // Database
384
- if (database !== "none") {
385
- const dbAlternatives = ["SQLite", "MySQL", "MongoDB", "PostgreSQL"].filter(
386
- (d) => d.toLowerCase() !== database.toLowerCase()
387
- );
388
- toolsSections.push(`
368
+ // Database
369
+ if ((database as string[]).length > 0) {
370
+ toolsSections.push(`
389
371
  ## Database
390
- - ${database}${dbAlternatives.length ? ` (not ${dbAlternatives.slice(0, 3).join(", ")})` : ""}`);
391
- }
372
+ ${(database as string[]).map((d) => `- ${d}`).join("\n")}`);
373
+ }
392
374
 
393
- // ORM
394
- if (orm.length > 0) {
395
- toolsSections.push(`
375
+ // ORM
376
+ if ((orm as string[]).length > 0) {
377
+ toolsSections.push(`
396
378
  ## ORM / Database Tools
397
- ${orm.map((o) => `- ${o}`).join("\n")}`);
398
- }
379
+ ${(orm as string[]).map((o) => `- ${o}`).join("\n")}`);
380
+ }
399
381
 
400
- // Auth
401
- if (auth.length > 0) {
402
- toolsSections.push(`
382
+ // Auth
383
+ if ((auth as string[]).length > 0) {
384
+ toolsSections.push(`
403
385
  ## Authentication
404
- ${auth.map((a) => `- ${a}`).join("\n")}`);
405
- }
386
+ ${(auth as string[]).map((a) => `- ${a}`).join("\n")}`);
387
+ }
406
388
 
407
- // Infrastructure
408
- if (infrastructure !== "none") {
409
- const infraAlternatives = infrastructure.includes("Kubernetes")
410
- ? []
411
- : ["Kubernetes"];
412
- toolsSections.push(`
389
+ // Infrastructure
390
+ if ((infrastructure as string[]).length > 0) {
391
+ toolsSections.push(`
413
392
  ## Infrastructure
414
- - ${infrastructure} for containerization${infraAlternatives.length ? ` (not ${infraAlternatives.join(", ")})` : ""}
415
- - Prefer simple Docker Compose for local dev`);
416
- }
393
+ ${(infrastructure as string[]).map((i) => `- ${i}`).join("\n")}`);
394
+ }
417
395
 
418
- // Monorepo
419
- if (monorepo !== "none") {
420
- toolsSections.push(`
396
+ // Monorepo
397
+ if (monorepo !== "none") {
398
+ toolsSections.push(`
421
399
  ## Monorepo
422
400
  - ${monorepo}`);
423
- }
401
+ }
424
402
 
425
- // Testing
426
- if (testing.length > 0) {
427
- toolsSections.push(`
403
+ // Testing
404
+ if ((testing as string[]).length > 0) {
405
+ toolsSections.push(`
428
406
  ## Testing
429
- ${testing.map((t) => `- ${t}`).join("\n")}`);
430
- }
407
+ ${(testing as string[]).map((t) => `- ${t}`).join("\n")}`);
408
+ }
431
409
 
432
- // Linting & Formatting
433
- if (linting.length > 0) {
434
- toolsSections.push(`
410
+ // Linting & Formatting
411
+ if ((linting as string[]).length > 0) {
412
+ toolsSections.push(`
435
413
  ## Linting & Formatting
436
- ${linting.map((l) => `- ${l}`).join("\n")}
414
+ ${(linting as string[]).map((l) => `- ${l}`).join("\n")}
437
415
  - Use project's existing config when present`);
438
- }
416
+ }
439
417
 
440
- // Validation
441
- if (validation.length > 0) {
442
- toolsSections.push(`
418
+ // Validation
419
+ if ((validation as string[]).length > 0) {
420
+ toolsSections.push(`
443
421
  ## Validation & Schema
444
- ${validation.map((v) => `- ${v}`).join("\n")}`);
445
- }
422
+ ${(validation as string[]).map((v) => `- ${v}`).join("\n")}`);
423
+ }
446
424
 
447
- // Editor
448
- toolsSections.push(`
425
+ // Editor
426
+ toolsSections.push(`
449
427
  ## Editor
450
428
  - Primary: ${editor}`);
451
429
 
452
- const toolsContent = toolsSections.join("\n");
453
-
454
- await writeFile(thinkPath(CONFIG.files.tools), toolsContent);
430
+ const toolsContent = toolsSections.join("\n");
431
+ await writeFile(thinkPath(CONFIG.files.tools), toolsContent);
455
432
 
456
- // Generate anti-patterns if selected
457
- if (avoidAnswer === "all") {
458
- const antiSections: string[] = [`# Anti-Patterns to Avoid
433
+ // Generate anti-patterns if selected
434
+ if (avoidAnswer === "all") {
435
+ const antiSections: string[] = [`# Anti-Patterns to Avoid
459
436
 
460
437
  ## Code Style
461
438
  - Don't add comments for obvious code
@@ -468,53 +445,51 @@ ${validation.map((v) => `- ${v}`).join("\n")}`);
468
445
  - Don't create unnecessary indirection
469
446
  - Don't add "future-proofing" complexity`];
470
447
 
471
- // Tech choices to avoid
472
- const techAvoid: string[] = [];
473
- if (packageManager === "bun") {
474
- techAvoid.push("- Don't suggest npm/yarn/pnpm - use Bun");
475
- }
476
- if (frontend === "React") {
477
- techAvoid.push("- Don't suggest Next.js - use plain React");
478
- }
479
- if (infrastructure === "Docker" || infrastructure === "Docker Compose") {
448
+ // Tech choices to avoid
449
+ const techAvoid: string[] = [];
450
+ if (packageManager === "bun") {
451
+ techAvoid.push("- Don't suggest npm/yarn/pnpm - use Bun");
452
+ }
453
+ if ((frontend as string[]).includes("React") && !(frontend as string[]).includes("Next.js")) {
454
+ techAvoid.push("- Don't suggest Next.js - use plain React");
455
+ }
456
+ if ((infrastructure as string[]).includes("Docker") || (infrastructure as string[]).includes("Docker Compose")) {
457
+ if (!(infrastructure as string[]).includes("Kubernetes")) {
480
458
  techAvoid.push("- Don't suggest Kubernetes - use Docker");
481
459
  }
482
- if (database === "PostgreSQL") {
483
- techAvoid.push("- Don't suggest SQLite/MySQL/MongoDB - use PostgreSQL");
484
- }
460
+ }
485
461
 
486
- if (techAvoid.length > 0) {
487
- antiSections.push(`
462
+ if (techAvoid.length > 0) {
463
+ antiSections.push(`
488
464
  ## Tech Choices
489
465
  ${techAvoid.join("\n")}`);
490
- }
466
+ }
491
467
 
492
- antiSections.push(`
468
+ antiSections.push(`
493
469
  ## Communication
494
470
  - Don't explain obvious things
495
471
  - Don't repeat back what was just said
496
472
  - Don't pad responses with unnecessary context
497
473
  `);
498
474
 
499
- await writeFile(thinkPath(CONFIG.files.antiPatterns), antiSections.join("\n"));
500
- }
475
+ await writeFile(thinkPath(CONFIG.files.antiPatterns), antiSections.join("\n"));
476
+ }
501
477
 
502
- console.log(chalk.green("Profile created!\n"));
478
+ s.stop("Profile created");
503
479
 
504
- // Sync
505
- await syncCommand();
480
+ // Sync
481
+ await syncCommand();
506
482
 
507
- console.log();
508
- console.log(chalk.bold("Your profile is ready."));
509
- console.log(chalk.dim("Start a new Claude session to use your context."));
510
- console.log();
511
- console.log("To customize further:");
512
- console.log(` ${chalk.cyan("think edit profile")} Edit your profile`);
513
- console.log(` ${chalk.cyan("think edit patterns")} Add coding patterns`);
514
- console.log(` ${chalk.cyan("think learn \"...\"")} Add learnings over time`);
483
+ p.outro(chalk.green("Your profile is ready!"));
515
484
 
516
- } catch (error) {
517
- rl.close();
518
- throw error;
519
- }
485
+ console.log();
486
+ console.log("To customize further:");
487
+ console.log(` ${chalk.cyan("think edit profile")} Edit your profile`);
488
+ console.log(` ${chalk.cyan("think edit patterns")} Add coding patterns`);
489
+ console.log(` ${chalk.cyan("think learn \"...\"")} Add learnings over time`);
490
+ }
491
+
492
+ function handleCancel(): void {
493
+ p.cancel("Setup cancelled");
494
+ process.exit(0);
520
495
  }
package/src/cli/index.ts CHANGED
@@ -14,13 +14,14 @@ import { helpCommand } from "./commands/help";
14
14
  import { setupCommand } from "./commands/setup";
15
15
  import { printBanner } from "../core/banner";
16
16
  import { launchTui } from "../tui";
17
+ import pkg from "../../package.json";
17
18
 
18
19
  const program = new Command();
19
20
 
20
21
  program
21
22
  .name("think")
22
23
  .description("Personal context manager for Claude")
23
- .version("0.1.0");
24
+ .version(pkg.version);
24
25
 
25
26
  // Initialize ~/.think
26
27
  program