claude-think 0.3.2 → 0.4.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.
package/CHANGELOG.md CHANGED
@@ -5,6 +5,25 @@ 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.4.0] - 2025-02-05
9
+
10
+ ### Added
11
+ - **Multi-profile support** - switch between different configurations (work, personal, etc.)
12
+ - `think profile list` - list all profiles with active indicator
13
+ - `think profile use <name>` - switch to a profile (auto-syncs)
14
+ - `think profile create <name> [--from <profile>]` - create new profile
15
+ - `think profile delete <name>` - delete with confirmation
16
+ - TUI profile switcher (press `P`) - switch profiles without leaving the app
17
+ - Active profile shown in TUI header
18
+ - **Inline edit/delete in Memory section**
19
+ - Arrow keys to select items
20
+ - `Enter` to edit inline
21
+ - `d` to delete with confirmation
22
+ - `e` still opens $EDITOR for bulk edits
23
+ ### Changed
24
+ - Directory structure now uses `~/.think/profiles/<name>/` for each profile
25
+ - Sync command shows which profile is being synced
26
+
8
27
  ## [0.3.2] - 2025-02-05
9
28
 
10
29
  ### Added
package/README.md CHANGED
@@ -46,10 +46,24 @@ claude
46
46
 
47
47
  ## How it works
48
48
 
49
- 1. Your preferences live in `~/.think/` (markdown files)
50
- 2. `think sync` generates `~/.claude/CLAUDE.md`
49
+ 1. Your preferences live in `~/.think/profiles/<name>/` (markdown files)
50
+ 2. `think sync` generates `~/.claude/CLAUDE.md` from active profile
51
51
  3. Claude Code auto-loads CLAUDE.md at session start
52
52
 
53
+ ## Profiles
54
+
55
+ Switch between different configurations for work, personal projects, or clients:
56
+
57
+ ```bash
58
+ think profile list # See all profiles
59
+ think profile create work # Create new profile
60
+ think profile create client --from work # Copy from existing
61
+ think profile use work # Switch (auto-syncs)
62
+ think profile delete old # Remove a profile
63
+ ```
64
+
65
+ Press `P` in the TUI to switch profiles interactively.
66
+
53
67
  ## Commands
54
68
 
55
69
  | Command | Description |
@@ -61,13 +75,35 @@ claude
61
75
  | `think status` | Show current status |
62
76
  | `think learn "..."` | Add a learning |
63
77
  | `think review` | Review Claude's suggestions |
64
- | `think profile` | Edit your profile |
78
+ | `think profile list` | List all profiles |
79
+ | `think profile use <name>` | Switch to a profile |
80
+ | `think profile create <name>` | Create new profile |
81
+ | `think profile delete <name>` | Delete a profile |
82
+ | `think profile edit` | Edit profile.md |
65
83
  | `think edit <file>` | Edit any config file |
66
84
  | `think allow "cmd"` | Pre-approve a command |
67
85
  | `think tree` | Preview project file tree |
68
86
  | `think project learn` | Generate CLAUDE.md for current project |
69
87
  | `think help` | Show all commands |
70
88
 
89
+ ## TUI
90
+
91
+ Run `think` to launch the fullscreen TUI:
92
+
93
+ | Key | Action |
94
+ |-----|--------|
95
+ | `Tab` | Switch sections |
96
+ | `↑↓` / `jk` | Navigate / scroll |
97
+ | `Enter` | Edit selected item |
98
+ | `d` | Delete selected item |
99
+ | `a` | Quick actions (sync, learn, search) |
100
+ | `p` | Preview CLAUDE.md |
101
+ | `P` | Switch profile |
102
+ | `/` | Search all files |
103
+ | `e` | Open in $EDITOR |
104
+ | `?` | Help |
105
+ | `q` | Quit |
106
+
71
107
  ## What you can configure
72
108
 
73
109
  - **Profile** - Communication style, preferences
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-think",
3
- "version": "0.3.2",
3
+ "version": "0.4.0",
4
4
  "description": "Personal context manager for Claude - manage your preferences, patterns, and memory",
5
5
  "author": "Amit Feldman",
6
6
  "license": "MIT",
@@ -1,7 +1,8 @@
1
1
  import { existsSync } from "fs";
2
2
  import { spawn } from "child_process";
3
3
  import chalk from "chalk";
4
- import { CONFIG, thinkPath } from "../../core/config";
4
+ import { CONFIG, thinkPath, profilePath } from "../../core/config";
5
+ import { isPathWithin } from "../../core/security";
5
6
 
6
7
  /**
7
8
  * Open any ~/.think file in $EDITOR
@@ -30,6 +31,12 @@ export async function editCommand(file: string): Promise<void> {
30
31
  const resolvedFile = shortcuts[file] || file;
31
32
  const filePath = thinkPath(resolvedFile);
32
33
 
34
+ // Security: Validate path stays within the profile directory
35
+ if (!isPathWithin(profilePath(), filePath)) {
36
+ console.log(chalk.red(`Invalid path: "${file}" is outside the profile directory`));
37
+ process.exit(1);
38
+ }
39
+
33
40
  if (!existsSync(filePath)) {
34
41
  console.log(chalk.red(`File not found: ${filePath}`));
35
42
  console.log();
@@ -25,7 +25,7 @@ export async function helpCommand(): Promise<void> {
25
25
  { cmd: "think allow <cmd>", desc: "Add command to allowed list" },
26
26
  { cmd: "", desc: "" },
27
27
  { cmd: "think tree", desc: "Preview file tree for current directory" },
28
- { cmd: "think project init", desc: "Create .think.yaml for project" },
28
+ { cmd: "think project learn", desc: "Generate CLAUDE.md for project" },
29
29
  ];
30
30
 
31
31
  for (const { cmd, desc } of commands) {
@@ -57,8 +57,7 @@ export async function helpCommand(): Promise<void> {
57
57
  console.log();
58
58
  console.log(chalk.bold("Files:\n"));
59
59
  console.log(` ${chalk.dim("Source:")} ~/.think/`);
60
- console.log(` ${chalk.dim("Plugin:")} ~/.claude/plugins/think/`);
61
- console.log(` ${chalk.dim("Project:")} .think.yaml (optional)`);
60
+ console.log(` ${chalk.dim("Output:")} ~/.claude/CLAUDE.md`);
62
61
 
63
62
  console.log();
64
63
  }
@@ -1,8 +1,9 @@
1
- import { mkdir, copyFile, readdir } from "fs/promises";
1
+ import { mkdir, copyFile } from "fs/promises";
2
2
  import { existsSync } from "fs";
3
3
  import { join, dirname } from "path";
4
4
  import chalk from "chalk";
5
- import { CONFIG, thinkPath } from "../../core/config";
5
+ import { CONFIG, profileFilePath } from "../../core/config";
6
+ import { ensureProfilesStructure } from "../../core/profiles";
6
7
 
7
8
  const TEMPLATE_DIR = join(dirname(dirname(dirname(__dirname))), "src", "templates");
8
9
 
@@ -13,31 +14,35 @@ export async function initCommand(options: { force?: boolean }): Promise<void> {
13
14
  const { force } = options;
14
15
 
15
16
  // Check if already initialized
16
- if (existsSync(CONFIG.thinkDir) && !force) {
17
- console.log(chalk.yellow(`~/.think already exists. Use --force to reinitialize.`));
17
+ if (existsSync(CONFIG.profilesDir) && !force) {
18
+ console.log(chalk.yellow(`~/.think already initialized. Use --force to reinitialize.`));
18
19
  return;
19
20
  }
20
21
 
21
22
  console.log(chalk.blue("Initializing ~/.think..."));
22
23
 
23
- // Create directory structure
24
+ // Ensure base structure exists
25
+ await mkdir(CONFIG.thinkDir, { recursive: true });
26
+ ensureProfilesStructure();
27
+
28
+ // Create directory structure in default profile
29
+ const profileRoot = profileFilePath(CONFIG.defaultProfile);
24
30
  const dirs = [
25
- CONFIG.thinkDir,
26
- thinkPath(CONFIG.dirs.preferences),
27
- thinkPath(CONFIG.dirs.permissions),
28
- thinkPath(CONFIG.dirs.skills),
29
- thinkPath(CONFIG.dirs.agents),
30
- thinkPath(CONFIG.dirs.memory),
31
- thinkPath(CONFIG.dirs.automation),
32
- thinkPath(CONFIG.dirs.templates),
33
- thinkPath(CONFIG.dirs.projects),
31
+ profileRoot,
32
+ join(profileRoot, CONFIG.dirs.preferences),
33
+ join(profileRoot, CONFIG.dirs.permissions),
34
+ join(profileRoot, CONFIG.dirs.skills),
35
+ join(profileRoot, CONFIG.dirs.agents),
36
+ join(profileRoot, CONFIG.dirs.memory),
37
+ join(profileRoot, CONFIG.dirs.automation),
38
+ join(profileRoot, CONFIG.dirs.templates),
34
39
  ];
35
40
 
36
41
  for (const dir of dirs) {
37
42
  await mkdir(dir, { recursive: true });
38
43
  }
39
44
 
40
- // Copy templates
45
+ // Copy templates to default profile
41
46
  const templateMap: Record<string, string> = {
42
47
  "profile.md": CONFIG.files.profile,
43
48
  "tools.md": CONFIG.files.tools,
@@ -55,7 +60,7 @@ export async function initCommand(options: { force?: boolean }): Promise<void> {
55
60
 
56
61
  for (const [template, dest] of Object.entries(templateMap)) {
57
62
  const srcPath = join(TEMPLATE_DIR, template);
58
- const destPath = thinkPath(dest);
63
+ const destPath = profileFilePath(CONFIG.defaultProfile, dest);
59
64
 
60
65
  // Only copy if destination doesn't exist (unless force)
61
66
  if (!existsSync(destPath) || force) {
@@ -70,7 +75,9 @@ export async function initCommand(options: { force?: boolean }): Promise<void> {
70
75
  console.log(chalk.green("Done! Your ~/.think directory is ready."));
71
76
  console.log();
72
77
  console.log("Next steps:");
73
- console.log(` 1. Edit ${chalk.cyan("~/.think/profile.md")} with your preferences`);
74
- console.log(` 2. Run ${chalk.cyan("think sync")} to generate the Claude plugin`);
78
+ console.log(` 1. Run ${chalk.cyan("think setup")} to configure your profile`);
79
+ console.log(` 2. Run ${chalk.cyan("think sync")} to generate CLAUDE.md`);
75
80
  console.log(` 3. Start using Claude with your personal context!`);
81
+ console.log();
82
+ console.log(chalk.gray(`Profile: default (at ~/.think/profiles/default/)`));
76
83
  }
@@ -0,0 +1,142 @@
1
+ import chalk from "chalk";
2
+ import * as p from "@clack/prompts";
3
+ import {
4
+ listProfiles,
5
+ switchProfile,
6
+ createProfile,
7
+ deleteProfile,
8
+ profileExists,
9
+ } from "../../core/profiles";
10
+ import { getActiveProfile } from "../../core/config";
11
+ import { syncCommand } from "../commands/sync";
12
+
13
+ /**
14
+ * List all profiles, showing which is active with a checkmark
15
+ */
16
+ export async function profileListCommand(): Promise<void> {
17
+ const profiles = listProfiles();
18
+
19
+ if (profiles.length === 0) {
20
+ console.log(chalk.yellow("No profiles found. Run `think init` first."));
21
+ return;
22
+ }
23
+
24
+ console.log("Profiles:");
25
+ for (const profile of profiles) {
26
+ if (profile.isActive) {
27
+ console.log(` ${chalk.green("\u2713")} ${chalk.cyan(profile.name)} (active)`);
28
+ } else {
29
+ console.log(` ${chalk.cyan(profile.name)}`);
30
+ }
31
+ }
32
+ }
33
+
34
+ /**
35
+ * Switch to a profile, then run sync
36
+ */
37
+ export async function profileUseCommand(name: string): Promise<void> {
38
+ if (!profileExists(name)) {
39
+ console.log(chalk.red(`Profile "${name}" does not exist.`));
40
+ console.log();
41
+ console.log("Available profiles:");
42
+ const profiles = listProfiles();
43
+ for (const profile of profiles) {
44
+ console.log(` - ${chalk.cyan(profile.name)}`);
45
+ }
46
+ process.exit(1);
47
+ }
48
+
49
+ const currentProfile = getActiveProfile();
50
+ if (currentProfile === name) {
51
+ console.log(chalk.yellow(`Already using profile "${name}".`));
52
+ return;
53
+ }
54
+
55
+ try {
56
+ switchProfile(name);
57
+ console.log(chalk.green(`Switched to profile "${chalk.cyan(name)}".`));
58
+ console.log();
59
+ await syncCommand();
60
+ } catch (error) {
61
+ console.error(chalk.red(`Failed to switch profile: ${error}`));
62
+ process.exit(1);
63
+ }
64
+ }
65
+
66
+ /**
67
+ * Create a new profile, optionally copying from an existing one
68
+ */
69
+ export async function profileCreateCommand(
70
+ name: string,
71
+ options: { from?: string }
72
+ ): Promise<void> {
73
+ if (profileExists(name)) {
74
+ console.log(chalk.red(`Profile "${name}" already exists.`));
75
+ process.exit(1);
76
+ }
77
+
78
+ if (options.from && !profileExists(options.from)) {
79
+ console.log(chalk.red(`Source profile "${options.from}" does not exist.`));
80
+ process.exit(1);
81
+ }
82
+
83
+ try {
84
+ createProfile(name, options.from);
85
+
86
+ if (options.from) {
87
+ console.log(
88
+ chalk.green(
89
+ `Created profile "${chalk.cyan(name)}" from "${chalk.cyan(options.from)}".`
90
+ )
91
+ );
92
+ } else {
93
+ console.log(chalk.green(`Created profile "${chalk.cyan(name)}".`));
94
+ }
95
+
96
+ console.log();
97
+ console.log(`Run ${chalk.cyan(`think profile use ${name}`)} to switch to it.`);
98
+ } catch (error) {
99
+ console.error(chalk.red(`Failed to create profile: ${error}`));
100
+ process.exit(1);
101
+ }
102
+ }
103
+
104
+ /**
105
+ * Delete a profile with confirmation
106
+ */
107
+ export async function profileDeleteCommand(name: string): Promise<void> {
108
+ if (!profileExists(name)) {
109
+ console.log(chalk.red(`Profile "${name}" does not exist.`));
110
+ process.exit(1);
111
+ }
112
+
113
+ const activeProfile = getActiveProfile();
114
+ const isActive = activeProfile === name;
115
+
116
+ const confirmMessage = isActive
117
+ ? `Are you sure you want to delete the active profile "${name}"? This will switch to the default profile.`
118
+ : `Are you sure you want to delete profile "${name}"?`;
119
+
120
+ const confirmed = await p.confirm({
121
+ message: confirmMessage,
122
+ });
123
+
124
+ if (p.isCancel(confirmed) || !confirmed) {
125
+ console.log(chalk.yellow("Deletion cancelled."));
126
+ return;
127
+ }
128
+
129
+ try {
130
+ deleteProfile(name);
131
+ console.log(chalk.green(`Deleted profile "${chalk.cyan(name)}".`));
132
+
133
+ if (isActive) {
134
+ console.log();
135
+ console.log(`Switched to default profile.`);
136
+ await syncCommand();
137
+ }
138
+ } catch (error) {
139
+ console.error(chalk.red(`Failed to delete profile: ${error}`));
140
+ process.exit(1);
141
+ }
142
+ }
@@ -1,6 +1,6 @@
1
- import { existsSync } from "fs";
2
- import { writeFile, readFile, readdir, stat } from "fs/promises";
3
- import { join, basename } from "path";
1
+ import { existsSync, readFileSync } from "fs";
2
+ import { writeFile, readFile, readdir } from "fs/promises";
3
+ import { join } from "path";
4
4
  import chalk from "chalk";
5
5
  import { detectProject, ProjectType } from "../../core/project-detect";
6
6
 
@@ -180,7 +180,7 @@ function findEntryPoints(dir: string, type: ProjectType): string[] {
180
180
  const pkgPath = join(dir, "package.json");
181
181
  if (existsSync(pkgPath)) {
182
182
  try {
183
- const pkg = JSON.parse(require("fs").readFileSync(pkgPath, "utf-8"));
183
+ const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
184
184
  if (pkg.bin) {
185
185
  const bins = typeof pkg.bin === "string" ? [pkg.bin] : Object.values(pkg.bin);
186
186
  for (const bin of bins as string[]) {
@@ -300,16 +300,16 @@ export async function setupCommand(): Promise<void> {
300
300
  });
301
301
  if (p.isCancel(infrastructure)) return handleCancel();
302
302
 
303
- const monorepo = await p.select({
303
+ const monorepo = await p.multiselect({
304
304
  message: "Monorepo tooling?",
305
305
  options: [
306
- { value: "none", label: "None / Single repo" },
307
306
  { value: "Turborepo", label: "Turborepo" },
308
307
  { value: "Bun workspaces", label: "Bun workspaces" },
309
308
  { value: "Nx", label: "Nx" },
310
309
  { value: "pnpm workspaces", label: "pnpm workspaces" },
311
310
  { value: "Lerna", label: "Lerna" },
312
311
  ],
312
+ required: false,
313
313
  });
314
314
  if (p.isCancel(monorepo)) return handleCancel();
315
315
 
@@ -615,10 +615,10 @@ ${(infrastructure as string[]).map((i) => `- ${i}`).join("\n")}`);
615
615
  }
616
616
 
617
617
  // Monorepo
618
- if (monorepo !== "none") {
618
+ if ((monorepo as string[]).length > 0) {
619
619
  toolsSections.push(`
620
620
  ## Monorepo
621
- - ${monorepo}`);
621
+ ${(monorepo as string[]).map((m) => `- ${m}`).join("\n")}`);
622
622
  }
623
623
 
624
624
  // Testing
@@ -1,7 +1,7 @@
1
1
  import { existsSync } from "fs";
2
- import { readFile, readdir } from "fs/promises";
2
+ import { readFile, readdir, stat } from "fs/promises";
3
3
  import chalk from "chalk";
4
- import { CONFIG, thinkPath, pluginPath } from "../../core/config";
4
+ import { CONFIG, thinkPath, getActiveProfile } from "../../core/config";
5
5
  import { extractLearnings } from "../../core/dedup";
6
6
  import { printBanner } from "../../core/banner";
7
7
 
@@ -19,11 +19,17 @@ export async function statusCommand(): Promise<void> {
19
19
 
20
20
  console.log(chalk.green("✓ ~/.think initialized"));
21
21
 
22
- // Check plugin
23
- if (existsSync(CONFIG.pluginDir)) {
24
- console.log(chalk.green("✓ Plugin generated at ~/.claude/plugins/think"));
22
+ // Show active profile
23
+ const profile = getActiveProfile();
24
+ console.log(`Profile: ${chalk.cyan(profile)}`);
25
+
26
+ // Check CLAUDE.md and show token estimate
27
+ if (existsSync(CONFIG.claudeMdPath)) {
28
+ const content = await readFile(CONFIG.claudeMdPath, "utf-8");
29
+ const tokens = Math.ceil(content.length / 4);
30
+ console.log(chalk.green(`✓ CLAUDE.md generated (~${formatTokens(tokens)} tokens)`));
25
31
  } else {
26
- console.log(chalk.yellow("○ Plugin not generated. Run `think sync`"));
32
+ console.log(chalk.yellow("○ CLAUDE.md not generated. Run `think sync`"));
27
33
  }
28
34
 
29
35
  console.log();
@@ -64,3 +70,9 @@ export async function statusCommand(): Promise<void> {
64
70
 
65
71
  console.log();
66
72
  }
73
+
74
+ function formatTokens(tokens: number): string {
75
+ if (tokens < 1000) return tokens.toString();
76
+ if (tokens < 10000) return `${(tokens / 1000).toFixed(1)}k`;
77
+ return `${Math.round(tokens / 1000)}k`;
78
+ }
@@ -1,6 +1,7 @@
1
1
  import { existsSync } from "fs";
2
+ import { readFile } from "fs/promises";
2
3
  import chalk from "chalk";
3
- import { CONFIG } from "../../core/config";
4
+ import { CONFIG, getActiveProfile } from "../../core/config";
4
5
  import { generatePlugin } from "../../core/generator";
5
6
 
6
7
  /**
@@ -13,14 +14,20 @@ export async function syncCommand(): Promise<void> {
13
14
  process.exit(1);
14
15
  }
15
16
 
16
- console.log(chalk.blue("Syncing ~/.think to ~/.claude/CLAUDE.md..."));
17
+ const profile = getActiveProfile();
18
+ console.log(chalk.blue(`Syncing profile "${chalk.cyan(profile)}" to ~/.claude/CLAUDE.md...`));
17
19
 
18
20
  try {
19
21
  await generatePlugin();
20
22
 
23
+ // Calculate token estimate
24
+ const content = await readFile(CONFIG.claudeMdPath, "utf-8");
25
+ const tokens = Math.ceil(content.length / 4);
26
+
21
27
  console.log(chalk.green("Done!"));
22
28
  console.log();
23
29
  console.log(`Generated: ${chalk.cyan(CONFIG.claudeMdPath)}`);
30
+ console.log(`Size: ~${chalk.magenta(formatTokens(tokens))} tokens`);
24
31
  console.log();
25
32
  console.log("Claude will automatically load your context in new sessions.");
26
33
  } catch (error) {
@@ -28,3 +35,9 @@ export async function syncCommand(): Promise<void> {
28
35
  process.exit(1);
29
36
  }
30
37
  }
38
+
39
+ function formatTokens(tokens: number): string {
40
+ if (tokens < 1000) return tokens.toString();
41
+ if (tokens < 10000) return `${(tokens / 1000).toFixed(1)}k`;
42
+ return `${Math.round(tokens / 1000)}k`;
43
+ }
package/src/cli/index.ts CHANGED
@@ -5,11 +5,16 @@ import { syncCommand } from "./commands/sync";
5
5
  import { learnCommand } from "./commands/learn";
6
6
  import { statusCommand } from "./commands/status";
7
7
  import { profileCommand } from "./commands/profile";
8
+ import {
9
+ profileListCommand,
10
+ profileUseCommand,
11
+ profileCreateCommand,
12
+ profileDeleteCommand,
13
+ } from "./commands/profile-commands";
8
14
  import { editCommand } from "./commands/edit";
9
15
  import { allowCommand } from "./commands/allow";
10
16
  import { reviewCommand } from "./commands/review";
11
17
  import { treeCommand } from "./commands/tree";
12
- import { projectInitCommand } from "./commands/project";
13
18
  import { projectLearnCommand } from "./commands/project-learn";
14
19
  import { helpCommand } from "./commands/help";
15
20
  import { setupCommand } from "./commands/setup";
@@ -50,12 +55,40 @@ program
50
55
  .description("Show current think status")
51
56
  .action(statusCommand);
52
57
 
53
- // Edit profile
54
- program
58
+ // Profile management commands
59
+ const profileCmd = program
55
60
  .command("profile")
61
+ .description("Manage profiles");
62
+
63
+ profileCmd
64
+ .command("list")
65
+ .description("List all profiles")
66
+ .action(profileListCommand);
67
+
68
+ profileCmd
69
+ .command("use <name>")
70
+ .description("Switch to a profile")
71
+ .action(profileUseCommand);
72
+
73
+ profileCmd
74
+ .command("create <name>")
75
+ .description("Create a new profile")
76
+ .option("--from <profile>", "Copy from existing profile")
77
+ .action(profileCreateCommand);
78
+
79
+ profileCmd
80
+ .command("delete <name>")
81
+ .description("Delete a profile")
82
+ .action(profileDeleteCommand);
83
+
84
+ profileCmd
85
+ .command("edit")
56
86
  .description("Open profile.md in $EDITOR")
57
87
  .action(profileCommand);
58
88
 
89
+ // Also allow `think profile` with no subcommand to edit
90
+ profileCmd.action(profileCommand);
91
+
59
92
  // Edit any file
60
93
  program
61
94
  .command("edit <file>")
@@ -81,20 +114,11 @@ program
81
114
  .description("Preview file tree for current directory")
82
115
  .action(treeCommand);
83
116
 
84
- // Project commands
85
- const projectCmd = program
117
+ // Project command - generate project CLAUDE.md
118
+ program
86
119
  .command("project")
87
- .description("Project-specific commands");
88
-
89
- projectCmd
90
- .command("init")
91
- .description("Initialize .think.yaml for current project")
92
- .option("-f, --force", "Overwrite existing config")
93
- .action(projectInitCommand);
94
-
95
- projectCmd
96
- .command("learn")
97
- .description("Generate CLAUDE.md with project structure")
120
+ .description("Generate CLAUDE.md for current project")
121
+ .alias("project learn")
98
122
  .option("-f, --force", "Overwrite existing CLAUDE.md")
99
123
  .action(projectLearnCommand);
100
124