claude-code-modes 0.2.8 → 0.2.10
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/README.md +27 -2
- package/package.json +1 -1
- package/prompts/base/doing-tasks.md +1 -0
- package/prompts/base/env.md +2 -2
- package/prompts/base/session-guidance.md +1 -1
- package/prompts/chill/core.md +3 -1
- package/prompts/chill/env.md +1 -1
- package/src/build-info.ts +1 -1
- package/src/cli.ts +12 -0
- package/src/embedded-prompts.ts +8 -5
- package/src/env.ts +10 -1
- package/src/types.ts +2 -0
- package/src/update.ts +467 -0
- package/src/usage.ts +12 -1
package/README.md
CHANGED
|
@@ -27,6 +27,20 @@ Requires [Bun](https://bun.sh/) and [Claude Code](https://docs.anthropic.com/en/
|
|
|
27
27
|
|
|
28
28
|
> **Migrating from an older git clone?** If you previously used `./claude-mode` directly from the repo, that bash wrapper has been removed. Run `bun link` in the repo to put `claude-mode` on your PATH via the package.json bin entry, or switch to the binary install above.
|
|
29
29
|
|
|
30
|
+
## Updating
|
|
31
|
+
|
|
32
|
+
If you installed the binary via `install.sh`:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
claude-mode update # update to the latest release
|
|
36
|
+
claude-mode update --check # check without installing
|
|
37
|
+
claude-mode update 0.2.5 # pin a specific version (downgrade or reinstall)
|
|
38
|
+
claude-mode update --force # reinstall same version (repair a corrupt binary)
|
|
39
|
+
claude-mode update --dry-run # show what would happen
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
`update` only works on binaries built from the upstream repo. If you're running from a `git clone` checkout (`bun link`), use `git pull && bun install` instead. If you're running a fork build, update via your fork's release process.
|
|
43
|
+
|
|
30
44
|
## Usage
|
|
31
45
|
|
|
32
46
|
Pick a preset that matches your task:
|
|
@@ -40,6 +54,7 @@ claude-mode explore # Read-only — understand code without changing it
|
|
|
40
54
|
claude-mode debug # Investigation-first debugging (chill base)
|
|
41
55
|
claude-mode methodical # Step-by-step precision (chill base)
|
|
42
56
|
claude-mode director # Delegate to sub-agents, orchestrate and verify (chill base)
|
|
57
|
+
claude-mode partner # Pair-of-equals: terse, test-first, decisive on craft (chill base)
|
|
43
58
|
claude-mode none # Strip all behavioral opinions, use your own CLAUDE.md
|
|
44
59
|
```
|
|
45
60
|
|
|
@@ -53,6 +68,7 @@ claude-mode none # Strip all behavioral opinions, use your own CLAUDE.md
|
|
|
53
68
|
| `debug` | collaborative | pragmatic | narrow | Find root causes — evidence-first, ask for guidance when stuck |
|
|
54
69
|
| `methodical` | surgical | architect | narrow | Step-by-step craftsmanship — follow instructions, stop when done |
|
|
55
70
|
| `director` | collaborative | architect | unrestricted | Orchestrate sub-agents — delegate implementation, verify results |
|
|
71
|
+
| `partner` | partner | pragmatic | adjacent | Pair-of-equals collaboration — decisive on craft, test-first, terse by default |
|
|
56
72
|
| `none` | — | — | — | Strip all behavioral instructions, use your own |
|
|
57
73
|
|
|
58
74
|
### Alternative base: chill
|
|
@@ -93,10 +109,10 @@ prompts/
|
|
|
93
109
|
base/ Standard base (derived from upstream Claude Code)
|
|
94
110
|
chill/ Alternative base (emotion-research-informed, leaner)
|
|
95
111
|
axis/ Behavioral prompts organized by three axes
|
|
96
|
-
modifiers/ Behavioral layers (bold, debug, methodical, director, readonly, context-pacing)
|
|
112
|
+
modifiers/ Behavioral layers (bold, debug, methodical, director, readonly, context-pacing, speak-plain, tdd)
|
|
97
113
|
```
|
|
98
114
|
|
|
99
|
-
Each base has a `base.json` manifest — a flat JSON array declaring fragment order with `"axes"` and `"modifiers"` as reserved insertion points. The standard base is validated against Claude Code **v2.1.
|
|
115
|
+
Each base has a `base.json` manifest — a flat JSON array declaring fragment order with `"axes"` and `"modifiers"` as reserved insertion points. The standard base is validated against Claude Code **v2.1.133**.
|
|
100
116
|
|
|
101
117
|
The behavioral layer is composed from three independent axes — **agency** (how much initiative), **quality** (what code standard), and **scope** (how far beyond the request). Presets are just named combinations of these three values.
|
|
102
118
|
|
|
@@ -159,6 +175,14 @@ Check the installed version:
|
|
|
159
175
|
claude-mode --version
|
|
160
176
|
```
|
|
161
177
|
|
|
178
|
+
Release builds print a single line (`claude-mode 0.2.8`). Builds compiled from a git checkout — forks, dev builds — also print build provenance (`repo`, `branch`, `commit`, plus `(dirty)` if the worktree had uncommitted changes), so a binary built from a fork is unmistakably identifiable.
|
|
179
|
+
|
|
180
|
+
`--version` is standalone-only — combining it with any other flag exits with a non-zero status. To pass `--version` through to Claude Code itself, use the `--` escape hatch:
|
|
181
|
+
|
|
182
|
+
```bash
|
|
183
|
+
claude-mode -- --version # Forwards --version to claude
|
|
184
|
+
```
|
|
185
|
+
|
|
162
186
|
## Config file
|
|
163
187
|
|
|
164
188
|
Create a `.claude-mode.json` in your project root to define reusable custom modifiers, axis values, and presets. Manage it with the CLI or edit directly.
|
|
@@ -236,6 +260,7 @@ claude-mode config remove-preset <name> # Remove custom preset
|
|
|
236
260
|
- **autonomous** — Makes decisions, creates files, restructures without asking
|
|
237
261
|
- **collaborative** — Explains reasoning, checks in at decision points
|
|
238
262
|
- **surgical** — Executes exactly what was asked, nothing more
|
|
263
|
+
- **partner** — Pair of equals: commits decisively on execution choices (naming, structure, idiom), defers to the user on direction (priorities, scope, what counts as done), keeps mental models in sync
|
|
239
264
|
|
|
240
265
|
**Quality** — What code standard should it target?
|
|
241
266
|
- **architect** — Proper abstractions, error handling, forward-thinking structure
|
package/package.json
CHANGED
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
- Don't explain WHAT the code does, since well-named identifiers already do that. Don't reference the current task, fix, or callers ("used by X", "added for the Y flow", "handles the case from issue #123"), since those belong in the PR description and rot as the codebase evolves.
|
|
12
12
|
- For UI or frontend changes, start the dev server and use the feature in a browser before reporting the task as complete. Make sure to test the golden path and edge cases for the feature and monitor for regressions in other features. Type checking and test suites verify code correctness, not feature correctness - if you can't test the UI, say so explicitly rather than claiming success.
|
|
13
13
|
- Don't use feature flags or backwards-compatibility shims when you can just change the code.
|
|
14
|
+
- When reporting results, be accurate about what you verified vs. what you assumed. Distinguish between what you confirmed (ran a command, read a file) and what you believe but did not check. Do not assert assumptions as facts.
|
|
14
15
|
- If the user asks for help or wants to give feedback inform them of the following:
|
|
15
16
|
- /help: Get help with using Claude Code
|
|
16
17
|
- To give feedback, users should report the issue at https://github.com/anthropics/claude-code/issues
|
package/prompts/base/env.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Environment
|
|
2
|
-
You have been invoked in the following environment:
|
|
3
|
-
- Primary working directory: {{CWD}}
|
|
2
|
+
You have been invoked in the following environment:
|
|
3
|
+
- Primary working directory: {{CWD}}{{WORKTREE_NOTICE}}
|
|
4
4
|
- Is a git repository: {{IS_GIT}}
|
|
5
5
|
- Platform: {{PLATFORM}}
|
|
6
6
|
- Shell: {{SHELL}}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
# Session-specific guidance
|
|
2
2
|
- If the user needs to run a shell command themselves (an interactive login like `gcloud auth login`, or something requiring their own credentials), suggest they type `! <command>` — the `!` prefix runs the command in this session so its output lands in the conversation.
|
|
3
3
|
- When the user invokes a slash-prefixed skill (`/<name>`), follow its loaded instructions. Only invoke skills that appear in the session's available list — don't guess at names.
|
|
4
|
-
-
|
|
4
|
+
- Use sub-agents to keep the main context lean. Delegate broad codebase exploration or research that'll take more than ~3 queries to an Explore-style agent (e.g. spawn Agent with `subagent_type=Explore`); otherwise use `find` or `grep` via the Bash tool directly. Don't duplicate searches a delegated agent is already doing.
|
|
5
5
|
- If the user asks about "ultrareview" or how to run it, explain that /ultrareview launches a multi-agent cloud review of the current branch (or /ultrareview <PR#> for a GitHub PR). It is user-triggered and billed; you cannot launch it yourself. It needs a git repository (offer to "git init" if not in one); the no-arg form bundles the local branch and does not need a GitHub remote.
|
package/prompts/chill/core.md
CHANGED
|
@@ -25,6 +25,8 @@ Read code before changing it. Understand what exists before proposing modificati
|
|
|
25
25
|
|
|
26
26
|
When something fails, that's normal — it's information, not a setback. Read the error, check your assumptions, try a focused fix. Most bugs have a straightforward cause once you look at them calmly.
|
|
27
27
|
|
|
28
|
+
When reporting results, be accurate about what you verified vs. what you assumed. Distinguish what you confirmed (ran a command, read a file) from what you believe but didn't check. Don't assert assumptions as facts.
|
|
29
|
+
|
|
28
30
|
Write secure code. Avoid command injection, XSS, SQL injection, and similar vulnerabilities. If you spot insecure code you wrote, fix it. Use linters and skills to assist you as needed.
|
|
29
31
|
|
|
30
32
|
For UI or frontend changes, start the dev server and test in a browser before reporting done. Test the golden path and edge cases, monitor for regressions. Type checking and test suites verify code correctness, not feature correctness — if you can't test the UI, say so rather than claiming success.
|
|
@@ -71,7 +73,7 @@ In code: default to no comments. Skip multi-paragraph docstrings and comment blo
|
|
|
71
73
|
|
|
72
74
|
If a tool denial is confusing, ask the user why. If you need them to run an interactive command, suggest `! <command>` in the prompt.
|
|
73
75
|
|
|
74
|
-
Using bash operations will require user input, which will slow our efforts. Prefer your specialized agents, like Read, instead of grep. Or Edit, instead of sed or awk. For broader exploration (more than ~3 queries), spawn
|
|
76
|
+
Using bash operations will require user input, which will slow our efforts. Prefer your specialized agents, like Read, instead of grep. Or Edit, instead of sed or awk. For broader exploration (more than ~3 queries), spawn Agent with `subagent_type=Explore`; otherwise use `find` or `grep` via Bash directly.
|
|
75
77
|
|
|
76
78
|
Slash commands (e.g., /commit) invoke skills — use the Skill tool for those listed as user-invocable.
|
|
77
79
|
|
package/prompts/chill/env.md
CHANGED
package/src/build-info.ts
CHANGED
package/src/cli.ts
CHANGED
|
@@ -8,6 +8,7 @@ import { assemblePrompt, writeTempPrompt } from "./assemble.js";
|
|
|
8
8
|
import { detectEnv, buildTemplateVars } from "./env.js";
|
|
9
9
|
import { runConfigCommand } from "./config-cli.js";
|
|
10
10
|
import { runInspectCommand } from "./inspect.js";
|
|
11
|
+
import { runUpdateCommand } from "./update.js";
|
|
11
12
|
import { printUsage } from "./usage.js";
|
|
12
13
|
import { formatVersion } from "./version.js";
|
|
13
14
|
|
|
@@ -63,6 +64,17 @@ async function main(): Promise<void> {
|
|
|
63
64
|
process.exit(0);
|
|
64
65
|
}
|
|
65
66
|
|
|
67
|
+
// Update subcommand routing
|
|
68
|
+
if (argv[0] === "update") {
|
|
69
|
+
try {
|
|
70
|
+
await runUpdateCommand(argv.slice(1));
|
|
71
|
+
} catch (err) {
|
|
72
|
+
process.stderr.write(`Error: ${(err as Error).message}\n`);
|
|
73
|
+
process.exit(1);
|
|
74
|
+
}
|
|
75
|
+
process.exit(0);
|
|
76
|
+
}
|
|
77
|
+
|
|
66
78
|
let parsed;
|
|
67
79
|
try {
|
|
68
80
|
parsed = parseCliArgs(argv);
|
package/src/embedded-prompts.ts
CHANGED
|
@@ -30,6 +30,7 @@ IMPORTANT: You must NEVER generate or guess URLs for the user unless you are con
|
|
|
30
30
|
- Don't explain WHAT the code does, since well-named identifiers already do that. Don't reference the current task, fix, or callers ("used by X", "added for the Y flow", "handles the case from issue #123"), since those belong in the PR description and rot as the codebase evolves.
|
|
31
31
|
- For UI or frontend changes, start the dev server and use the feature in a browser before reporting the task as complete. Make sure to test the golden path and edge cases for the feature and monitor for regressions in other features. Type checking and test suites verify code correctness, not feature correctness - if you can't test the UI, say so explicitly rather than claiming success.
|
|
32
32
|
- Don't use feature flags or backwards-compatibility shims when you can just change the code.
|
|
33
|
+
- When reporting results, be accurate about what you verified vs. what you assumed. Distinguish between what you confirmed (ran a command, read a file) and what you believe but did not check. Do not assert assumptions as facts.
|
|
33
34
|
- If the user asks for help or wants to give feedback inform them of the following:
|
|
34
35
|
- /help: Get help with using Claude Code
|
|
35
36
|
- To give feedback, users should report the issue at https://github.com/anthropics/claude-code/issues
|
|
@@ -71,12 +72,12 @@ In code: default to writing no comments. Never write multi-paragraph docstrings
|
|
|
71
72
|
"base/session-guidance.md": `# Session-specific guidance
|
|
72
73
|
- If the user needs to run a shell command themselves (an interactive login like \`gcloud auth login\`, or something requiring their own credentials), suggest they type \`! <command>\` — the \`!\` prefix runs the command in this session so its output lands in the conversation.
|
|
73
74
|
- When the user invokes a slash-prefixed skill (\`/<name>\`), follow its loaded instructions. Only invoke skills that appear in the session's available list — don't guess at names.
|
|
74
|
-
-
|
|
75
|
+
- Use sub-agents to keep the main context lean. Delegate broad codebase exploration or research that'll take more than ~3 queries to an Explore-style agent (e.g. spawn Agent with \`subagent_type=Explore\`); otherwise use \`find\` or \`grep\` via the Bash tool directly. Don't duplicate searches a delegated agent is already doing.
|
|
75
76
|
- If the user asks about "ultrareview" or how to run it, explain that /ultrareview launches a multi-agent cloud review of the current branch (or /ultrareview <PR#> for a GitHub PR). It is user-triggered and billed; you cannot launch it yourself. It needs a git repository (offer to "git init" if not in one); the no-arg form bundles the local branch and does not need a GitHub remote.
|
|
76
77
|
`,
|
|
77
78
|
"base/env.md": `# Environment
|
|
78
|
-
You have been invoked in the following environment:
|
|
79
|
-
- Primary working directory: {{CWD}}
|
|
79
|
+
You have been invoked in the following environment:
|
|
80
|
+
- Primary working directory: {{CWD}}{{WORKTREE_NOTICE}}
|
|
80
81
|
- Is a git repository: {{IS_GIT}}
|
|
81
82
|
- Platform: {{PLATFORM}}
|
|
82
83
|
- Shell: {{SHELL}}
|
|
@@ -141,6 +142,8 @@ Read code before changing it. Understand what exists before proposing modificati
|
|
|
141
142
|
|
|
142
143
|
When something fails, that's normal — it's information, not a setback. Read the error, check your assumptions, try a focused fix. Most bugs have a straightforward cause once you look at them calmly.
|
|
143
144
|
|
|
145
|
+
When reporting results, be accurate about what you verified vs. what you assumed. Distinguish what you confirmed (ran a command, read a file) from what you believe but didn't check. Don't assert assumptions as facts.
|
|
146
|
+
|
|
144
147
|
Write secure code. Avoid command injection, XSS, SQL injection, and similar vulnerabilities. If you spot insecure code you wrote, fix it. Use linters and skills to assist you as needed.
|
|
145
148
|
|
|
146
149
|
For UI or frontend changes, start the dev server and test in a browser before reporting done. Test the golden path and edge cases, monitor for regressions. Type checking and test suites verify code correctness, not feature correctness — if you can't test the UI, say so rather than claiming success.
|
|
@@ -187,7 +190,7 @@ In code: default to no comments. Skip multi-paragraph docstrings and comment blo
|
|
|
187
190
|
|
|
188
191
|
If a tool denial is confusing, ask the user why. If you need them to run an interactive command, suggest \`! <command>\` in the prompt.
|
|
189
192
|
|
|
190
|
-
Using bash operations will require user input, which will slow our efforts. Prefer your specialized agents, like Read, instead of grep. Or Edit, instead of sed or awk. For broader exploration (more than ~3 queries), spawn
|
|
193
|
+
Using bash operations will require user input, which will slow our efforts. Prefer your specialized agents, like Read, instead of grep. Or Edit, instead of sed or awk. For broader exploration (more than ~3 queries), spawn Agent with \`subagent_type=Explore\`; otherwise use \`find\` or \`grep\` via Bash directly.
|
|
191
194
|
|
|
192
195
|
Slash commands (e.g., /commit) invoke skills — use the Skill tool for those listed as user-invocable.
|
|
193
196
|
|
|
@@ -230,7 +233,7 @@ Read will work better than cat or grep. Using pgrep and echo for process monitor
|
|
|
230
233
|
Reserve Bash for commands that genuinely need shell execution.
|
|
231
234
|
`,
|
|
232
235
|
"chill/env.md": `# Environment
|
|
233
|
-
- Working directory: {{CWD}}
|
|
236
|
+
- Working directory: {{CWD}}{{WORKTREE_NOTICE}}
|
|
234
237
|
- Git repo: {{IS_GIT}}
|
|
235
238
|
- Platform: {{PLATFORM}}
|
|
236
239
|
- Shell: {{SHELL}}
|
package/src/env.ts
CHANGED
|
@@ -22,11 +22,15 @@ export function detectEnv(): EnvInfo {
|
|
|
22
22
|
const cwd = process.cwd();
|
|
23
23
|
const isGit = exec("git rev-parse --is-inside-work-tree") === "true";
|
|
24
24
|
|
|
25
|
+
let isWorktree = false;
|
|
25
26
|
let gitBranch: string | null = null;
|
|
26
27
|
let gitStatus: string | null = null;
|
|
27
28
|
let gitLog: string | null = null;
|
|
28
29
|
|
|
29
30
|
if (isGit) {
|
|
31
|
+
const gitDir = exec("git rev-parse --git-dir");
|
|
32
|
+
const commonDir = exec("git rev-parse --git-common-dir");
|
|
33
|
+
isWorktree = gitDir !== null && commonDir !== null && gitDir !== commonDir;
|
|
30
34
|
gitBranch = exec("git branch --show-current");
|
|
31
35
|
gitStatus = exec("git status --short");
|
|
32
36
|
gitLog = exec("git log --oneline -5");
|
|
@@ -36,7 +40,7 @@ export function detectEnv(): EnvInfo {
|
|
|
36
40
|
const shell = basename(process.env.SHELL || "bash");
|
|
37
41
|
const osVersion = exec("uname -sr") ?? "unknown";
|
|
38
42
|
|
|
39
|
-
return { cwd, isGit, gitBranch, gitStatus, gitLog, platform, shell, osVersion };
|
|
43
|
+
return { cwd, isGit, isWorktree, gitBranch, gitStatus, gitLog, platform, shell, osVersion };
|
|
40
44
|
}
|
|
41
45
|
|
|
42
46
|
// Hardcoded model info — update when Claude Code updates
|
|
@@ -58,6 +62,10 @@ export function buildTemplateVars(env: EnvInfo): TemplateVars {
|
|
|
58
62
|
gitStatusBlock = parts.join("\n");
|
|
59
63
|
}
|
|
60
64
|
|
|
65
|
+
const worktreeNotice = env.isWorktree
|
|
66
|
+
? "\n - This is a git worktree — an isolated copy of the repository. Run all commands from this directory. Do NOT `cd` to the original repository root."
|
|
67
|
+
: "";
|
|
68
|
+
|
|
61
69
|
return {
|
|
62
70
|
CWD: env.cwd,
|
|
63
71
|
IS_GIT: env.isGit ? "true" : "false",
|
|
@@ -68,5 +76,6 @@ export function buildTemplateVars(env: EnvInfo): TemplateVars {
|
|
|
68
76
|
MODEL_ID,
|
|
69
77
|
KNOWLEDGE_CUTOFF,
|
|
70
78
|
GIT_STATUS: gitStatusBlock,
|
|
79
|
+
WORKTREE_NOTICE: worktreeNotice,
|
|
71
80
|
};
|
|
72
81
|
}
|
package/src/types.ts
CHANGED
|
@@ -71,6 +71,7 @@ export interface ModeConfig {
|
|
|
71
71
|
export interface EnvInfo {
|
|
72
72
|
cwd: string;
|
|
73
73
|
isGit: boolean;
|
|
74
|
+
isWorktree: boolean;
|
|
74
75
|
gitBranch: string | null;
|
|
75
76
|
gitStatus: string | null;
|
|
76
77
|
gitLog: string | null;
|
|
@@ -90,6 +91,7 @@ export interface TemplateVars {
|
|
|
90
91
|
MODEL_ID: string;
|
|
91
92
|
KNOWLEDGE_CUTOFF: string;
|
|
92
93
|
GIT_STATUS: string;
|
|
94
|
+
WORKTREE_NOTICE: string;
|
|
93
95
|
}
|
|
94
96
|
|
|
95
97
|
export interface AssembleOptions {
|
package/src/update.ts
ADDED
|
@@ -0,0 +1,467 @@
|
|
|
1
|
+
import { writeFileSync, chmodSync, renameSync } from "node:fs";
|
|
2
|
+
import { execSync } from "node:child_process";
|
|
3
|
+
import { createHash } from "node:crypto";
|
|
4
|
+
import { basename } from "node:path";
|
|
5
|
+
import { VERSION } from "./version.js";
|
|
6
|
+
import { BUILD_INFO, type BuildInfo } from "./build-info.js";
|
|
7
|
+
|
|
8
|
+
// ----------------------------------------------------------------------
|
|
9
|
+
// Constants
|
|
10
|
+
// ----------------------------------------------------------------------
|
|
11
|
+
|
|
12
|
+
/** Upstream repo slug — also referenced by install.sh and the release workflow. */
|
|
13
|
+
const UPSTREAM_REPO = "nklisch/claude-code-modes";
|
|
14
|
+
|
|
15
|
+
/** Normalized form of the upstream remote URL captured by build-info. */
|
|
16
|
+
const UPSTREAM_REPO_URL = "https://github.com/nklisch/claude-code-modes.git";
|
|
17
|
+
|
|
18
|
+
const USER_AGENT = `claude-mode/${VERSION}`;
|
|
19
|
+
|
|
20
|
+
const GITHUB_API_BASE = "https://api.github.com";
|
|
21
|
+
|
|
22
|
+
/** Basenames of interpreter runtimes — signals we're running from source. */
|
|
23
|
+
const SOURCE_EXEC_NAMES = new Set(["bun", "bun-debug", "node"]);
|
|
24
|
+
|
|
25
|
+
// ----------------------------------------------------------------------
|
|
26
|
+
// Public types
|
|
27
|
+
// ----------------------------------------------------------------------
|
|
28
|
+
|
|
29
|
+
export interface UpdateOptions {
|
|
30
|
+
check: boolean;
|
|
31
|
+
force: boolean;
|
|
32
|
+
dryRun: boolean;
|
|
33
|
+
/** Positional version: "0.2.5" or "v0.2.5"; null for "latest". */
|
|
34
|
+
targetTag: string | null;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export type InstallClassification =
|
|
38
|
+
| { kind: "source"; reason: string }
|
|
39
|
+
| { kind: "dirty"; reason: string }
|
|
40
|
+
| { kind: "fork"; reason: string }
|
|
41
|
+
| { kind: "release"; binaryPath: string };
|
|
42
|
+
|
|
43
|
+
export interface ReleaseAsset {
|
|
44
|
+
name: string;
|
|
45
|
+
url: string;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export interface ReleaseInfo {
|
|
49
|
+
/** "v0.2.8" — exact tag from GitHub. */
|
|
50
|
+
tag: string;
|
|
51
|
+
/** "0.2.8" — tag with leading "v" stripped, comparable to package.json. */
|
|
52
|
+
version: string;
|
|
53
|
+
assets: ReleaseAsset[];
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export interface PlatformArtifact {
|
|
57
|
+
binary: ReleaseAsset;
|
|
58
|
+
checksums: ReleaseAsset;
|
|
59
|
+
/** "claude-mode-linux-x64" etc. */
|
|
60
|
+
artifactName: string;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export type UpdateAction = "install" | "no-op" | "downgrade" | "reinstall";
|
|
64
|
+
|
|
65
|
+
export interface UpdatePlan {
|
|
66
|
+
current: string;
|
|
67
|
+
target: string;
|
|
68
|
+
tag: string;
|
|
69
|
+
artifact: PlatformArtifact;
|
|
70
|
+
binaryPath: string;
|
|
71
|
+
action: UpdateAction;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/** Injectable transport — default uses global fetch; tests inject a fake. */
|
|
75
|
+
export interface UpdateTransport {
|
|
76
|
+
fetchJson(url: string): Promise<unknown>;
|
|
77
|
+
fetchBytes(url: string): Promise<Uint8Array>;
|
|
78
|
+
fetchText(url: string): Promise<string>;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// ----------------------------------------------------------------------
|
|
82
|
+
// Default transport
|
|
83
|
+
// ----------------------------------------------------------------------
|
|
84
|
+
|
|
85
|
+
async function apiFetch(url: string): Promise<Response> {
|
|
86
|
+
const res = await fetch(url, {
|
|
87
|
+
redirect: "follow",
|
|
88
|
+
headers: { "User-Agent": USER_AGENT },
|
|
89
|
+
});
|
|
90
|
+
if (!res.ok) {
|
|
91
|
+
throw new Error(`HTTP ${res.status} fetching ${url}`);
|
|
92
|
+
}
|
|
93
|
+
return res;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export const defaultTransport: UpdateTransport = {
|
|
97
|
+
async fetchJson(url: string): Promise<unknown> {
|
|
98
|
+
const res = await apiFetch(url);
|
|
99
|
+
return res.json();
|
|
100
|
+
},
|
|
101
|
+
async fetchBytes(url: string): Promise<Uint8Array> {
|
|
102
|
+
const res = await apiFetch(url);
|
|
103
|
+
const buf = await res.arrayBuffer();
|
|
104
|
+
return new Uint8Array(buf);
|
|
105
|
+
},
|
|
106
|
+
async fetchText(url: string): Promise<string> {
|
|
107
|
+
const res = await apiFetch(url);
|
|
108
|
+
return res.text();
|
|
109
|
+
},
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
// ----------------------------------------------------------------------
|
|
113
|
+
// Public API — pure helpers
|
|
114
|
+
// ----------------------------------------------------------------------
|
|
115
|
+
|
|
116
|
+
/** Parses argv (after "update" has been stripped). Throws on unknown flags. */
|
|
117
|
+
export function parseUpdateArgs(argv: string[]): UpdateOptions {
|
|
118
|
+
const opts: UpdateOptions = { check: false, force: false, dryRun: false, targetTag: null };
|
|
119
|
+
|
|
120
|
+
for (const arg of argv) {
|
|
121
|
+
if (arg === "--check") {
|
|
122
|
+
opts.check = true;
|
|
123
|
+
} else if (arg === "--force") {
|
|
124
|
+
opts.force = true;
|
|
125
|
+
} else if (arg === "--dry-run") {
|
|
126
|
+
opts.dryRun = true;
|
|
127
|
+
} else if (arg.startsWith("--")) {
|
|
128
|
+
throw new Error(`Unknown flag: ${arg}`);
|
|
129
|
+
} else {
|
|
130
|
+
// Positional argument (version tag)
|
|
131
|
+
if (opts.targetTag !== null) {
|
|
132
|
+
throw new Error(`Unexpected argument: ${arg}`);
|
|
133
|
+
}
|
|
134
|
+
opts.targetTag = arg;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return opts;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/** Determines whether the running binary is safe to self-update. Pure. */
|
|
142
|
+
export function classifyInstall(
|
|
143
|
+
execPath: string = process.execPath,
|
|
144
|
+
buildInfo: BuildInfo = BUILD_INFO,
|
|
145
|
+
): InstallClassification {
|
|
146
|
+
// 1. Runtime interpreter → source mode
|
|
147
|
+
if (SOURCE_EXEC_NAMES.has(basename(execPath))) {
|
|
148
|
+
return {
|
|
149
|
+
kind: "source",
|
|
150
|
+
reason: `Running as ${basename(execPath)} runtime (source mode)`,
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// 2. Dirty working tree → dev build
|
|
155
|
+
if (buildInfo.dirty === true) {
|
|
156
|
+
return {
|
|
157
|
+
kind: "dirty",
|
|
158
|
+
reason: "Binary was built from a dirty working tree",
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// 3. Non-upstream repo → fork build
|
|
163
|
+
if (buildInfo.repo && buildInfo.repo !== UPSTREAM_REPO_URL) {
|
|
164
|
+
return {
|
|
165
|
+
kind: "fork",
|
|
166
|
+
reason: `Binary was built from a fork: ${buildInfo.repo}`,
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// 4. Otherwise → safe to update
|
|
171
|
+
return { kind: "release", binaryPath: execPath };
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/** Maps process.platform/process.arch → release artifact name. Throws on unsupported. */
|
|
175
|
+
export function detectArtifactName(
|
|
176
|
+
platformVal: string = process.platform,
|
|
177
|
+
archVal: string = process.arch,
|
|
178
|
+
): string {
|
|
179
|
+
const SUPPORTED_PLATFORMS = ["linux", "darwin"] as const;
|
|
180
|
+
const SUPPORTED_ARCHES = ["x64", "arm64"] as const;
|
|
181
|
+
|
|
182
|
+
type SupportedPlatform = (typeof SUPPORTED_PLATFORMS)[number];
|
|
183
|
+
type SupportedArch = (typeof SUPPORTED_ARCHES)[number];
|
|
184
|
+
|
|
185
|
+
if (!(SUPPORTED_PLATFORMS as readonly string[]).includes(platformVal)) {
|
|
186
|
+
throw new Error(
|
|
187
|
+
`Unsupported platform: "${platformVal}". ` +
|
|
188
|
+
`claude-mode update only supports: ${SUPPORTED_PLATFORMS.join(", ")}.`,
|
|
189
|
+
);
|
|
190
|
+
}
|
|
191
|
+
if (!(SUPPORTED_ARCHES as readonly string[]).includes(archVal)) {
|
|
192
|
+
throw new Error(
|
|
193
|
+
`Unsupported architecture: "${archVal}". ` +
|
|
194
|
+
`claude-mode update only supports: ${SUPPORTED_ARCHES.join(", ")}.`,
|
|
195
|
+
);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
const platform = platformVal as SupportedPlatform;
|
|
199
|
+
const arch = archVal as SupportedArch;
|
|
200
|
+
return `claude-mode-${platform}-${arch}`;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/** Hits GET /repos/{repo}/releases/latest. */
|
|
204
|
+
export async function fetchLatestRelease(transport: UpdateTransport): Promise<ReleaseInfo> {
|
|
205
|
+
const url = `${GITHUB_API_BASE}/repos/${UPSTREAM_REPO}/releases/latest`;
|
|
206
|
+
const data = await transport.fetchJson(url);
|
|
207
|
+
return parseReleaseJson(data);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/** Hits GET /repos/{repo}/releases/tags/v{tag}. Accepts "0.2.5" or "v0.2.5". */
|
|
211
|
+
export async function fetchReleaseByTag(
|
|
212
|
+
tag: string,
|
|
213
|
+
transport: UpdateTransport,
|
|
214
|
+
): Promise<ReleaseInfo> {
|
|
215
|
+
const normalizedTag = tag.startsWith("v") ? tag : `v${tag}`;
|
|
216
|
+
const url = `${GITHUB_API_BASE}/repos/${UPSTREAM_REPO}/releases/tags/${normalizedTag}`;
|
|
217
|
+
try {
|
|
218
|
+
const data = await transport.fetchJson(url);
|
|
219
|
+
return parseReleaseJson(data);
|
|
220
|
+
} catch (err) {
|
|
221
|
+
throw new Error(`Could not find release ${normalizedTag}: ${(err as Error).message}`);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/** Picks the binary asset and checksums.txt from a release. Throws if either is missing. */
|
|
226
|
+
export function selectArtifact(release: ReleaseInfo, artifactName: string): PlatformArtifact {
|
|
227
|
+
const binary = release.assets.find((a) => a.name === artifactName);
|
|
228
|
+
const checksums = release.assets.find((a) => a.name === "checksums.txt");
|
|
229
|
+
|
|
230
|
+
if (!binary) {
|
|
231
|
+
const available = release.assets.map((a) => a.name).join(", ");
|
|
232
|
+
throw new Error(
|
|
233
|
+
`Could not find binary "${artifactName}" in release ${release.tag}. ` +
|
|
234
|
+
`Available assets: ${available}`,
|
|
235
|
+
);
|
|
236
|
+
}
|
|
237
|
+
if (!checksums) {
|
|
238
|
+
throw new Error(
|
|
239
|
+
`Could not find "checksums.txt" in release ${release.tag}. ` +
|
|
240
|
+
`Available assets: ${release.assets.map((a) => a.name).join(", ")}`,
|
|
241
|
+
);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
return { binary, checksums, artifactName };
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/** Reads "<sha256> <filename>" lines and returns the hash for artifactName. */
|
|
248
|
+
export function parseChecksum(text: string, artifactName: string): string {
|
|
249
|
+
const lines = text.split("\n");
|
|
250
|
+
for (const line of lines) {
|
|
251
|
+
const match = line.match(/^([0-9a-f]{64})\s+\*?(.+)$/i);
|
|
252
|
+
if (match && match[2].trim() === artifactName) {
|
|
253
|
+
return match[1].toLowerCase();
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
throw new Error(`Could not find checksum for "${artifactName}" in checksums.txt`);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/** Returns lowercase hex SHA-256 of data. */
|
|
260
|
+
export function computeSha256(data: Uint8Array): string {
|
|
261
|
+
return createHash("sha256").update(data).digest("hex").toLowerCase();
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/** Throws Error with expected/actual hashes if they don't match. */
|
|
265
|
+
export function verifyChecksum(data: Uint8Array, expected: string): void {
|
|
266
|
+
const actual = computeSha256(data);
|
|
267
|
+
if (actual !== expected.toLowerCase()) {
|
|
268
|
+
throw new Error(
|
|
269
|
+
`Checksum mismatch!\n` +
|
|
270
|
+
` Expected: ${expected.toLowerCase()}\n` +
|
|
271
|
+
` Actual: ${actual}`,
|
|
272
|
+
);
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/** Numeric compare of "X.Y.Z" strings. -1 / 0 / 1. */
|
|
277
|
+
export function compareSemver(a: string, b: string): number {
|
|
278
|
+
const parseVer = (v: string): number[] =>
|
|
279
|
+
v.replace(/^v/, "").split(".").map((n) => parseInt(n, 10));
|
|
280
|
+
|
|
281
|
+
const aParts = parseVer(a);
|
|
282
|
+
const bParts = parseVer(b);
|
|
283
|
+
|
|
284
|
+
for (let i = 0; i < 3; i++) {
|
|
285
|
+
const diff = (aParts[i] ?? 0) - (bParts[i] ?? 0);
|
|
286
|
+
if (diff < 0) return -1;
|
|
287
|
+
if (diff > 0) return 1;
|
|
288
|
+
}
|
|
289
|
+
return 0;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/** Picks the action given current, target, and --force. */
|
|
293
|
+
export function computeAction(
|
|
294
|
+
current: string,
|
|
295
|
+
target: string,
|
|
296
|
+
force: boolean,
|
|
297
|
+
): UpdateAction {
|
|
298
|
+
if (force) return "reinstall";
|
|
299
|
+
const cmp = compareSemver(current, target);
|
|
300
|
+
if (cmp === 0) return "no-op";
|
|
301
|
+
if (cmp < 0) return "install";
|
|
302
|
+
return "downgrade";
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* Atomic install: write to "<binaryPath>.new", chmod 0755, drop macOS
|
|
307
|
+
* quarantine xattr (best-effort), rename over binaryPath.
|
|
308
|
+
*/
|
|
309
|
+
export function installBinary(
|
|
310
|
+
data: Uint8Array,
|
|
311
|
+
target: { binaryPath: string; isDarwin: boolean },
|
|
312
|
+
): void {
|
|
313
|
+
const stagingPath = `${target.binaryPath}.new`;
|
|
314
|
+
writeFileSync(stagingPath, data);
|
|
315
|
+
chmodSync(stagingPath, 0o755);
|
|
316
|
+
|
|
317
|
+
if (target.isDarwin) {
|
|
318
|
+
try {
|
|
319
|
+
execSync(`xattr -d com.apple.quarantine "${stagingPath}" 2>/dev/null`, { stdio: "ignore" });
|
|
320
|
+
} catch {
|
|
321
|
+
// Best-effort — missing xattr attribute is normal
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
renameSync(stagingPath, target.binaryPath);
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
// ----------------------------------------------------------------------
|
|
329
|
+
// Orchestrator internals (unexported)
|
|
330
|
+
// ----------------------------------------------------------------------
|
|
331
|
+
|
|
332
|
+
function guidanceFor(kind: "source" | "dirty" | "fork"): string {
|
|
333
|
+
if (kind === "source") {
|
|
334
|
+
return "To update, use: git pull && bun install";
|
|
335
|
+
}
|
|
336
|
+
if (kind === "dirty") {
|
|
337
|
+
return "Commit or stash your changes, build a clean binary, then run update.";
|
|
338
|
+
}
|
|
339
|
+
// fork
|
|
340
|
+
return "Update via your fork's release process.";
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
function printDryRunPlan(plan: UpdatePlan): void {
|
|
344
|
+
process.stdout.write(`Dry-run plan:\n`);
|
|
345
|
+
process.stdout.write(` Action: ${plan.action}\n`);
|
|
346
|
+
process.stdout.write(` Current: ${plan.current}\n`);
|
|
347
|
+
process.stdout.write(` Target: ${plan.target} (${plan.tag})\n`);
|
|
348
|
+
process.stdout.write(` Binary URL: ${plan.artifact.binary.url}\n`);
|
|
349
|
+
process.stdout.write(` Checksums URL: ${plan.artifact.checksums.url}\n`);
|
|
350
|
+
process.stdout.write(` Artifact: ${plan.artifact.artifactName}\n`);
|
|
351
|
+
process.stdout.write(` Install path: ${plan.binaryPath}\n`);
|
|
352
|
+
process.stdout.write(` (No changes written)\n`);
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
async function executePlan(plan: UpdatePlan, transport: UpdateTransport): Promise<void> {
|
|
356
|
+
// Fetch binary and checksums in parallel
|
|
357
|
+
const [binaryData, checksumsText] = await Promise.all([
|
|
358
|
+
transport.fetchBytes(plan.artifact.binary.url),
|
|
359
|
+
transport.fetchText(plan.artifact.checksums.url),
|
|
360
|
+
]);
|
|
361
|
+
|
|
362
|
+
const expectedHash = parseChecksum(checksumsText, plan.artifact.artifactName);
|
|
363
|
+
verifyChecksum(binaryData, expectedHash);
|
|
364
|
+
|
|
365
|
+
installBinary(binaryData, {
|
|
366
|
+
binaryPath: plan.binaryPath,
|
|
367
|
+
isDarwin: process.platform === "darwin",
|
|
368
|
+
});
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
/** Parses a raw GitHub release API response into a ReleaseInfo. */
|
|
372
|
+
function parseReleaseJson(data: unknown): ReleaseInfo {
|
|
373
|
+
const raw = data as Record<string, unknown>;
|
|
374
|
+
const tag = raw["tag_name"] as string;
|
|
375
|
+
const version = tag.startsWith("v") ? tag.slice(1) : tag;
|
|
376
|
+
const rawAssets = (raw["assets"] as Record<string, unknown>[]) ?? [];
|
|
377
|
+
const assets: ReleaseAsset[] = rawAssets.map((a) => ({
|
|
378
|
+
name: a["name"] as string,
|
|
379
|
+
url: a["browser_download_url"] as string,
|
|
380
|
+
}));
|
|
381
|
+
return { tag, version, assets };
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
// ----------------------------------------------------------------------
|
|
385
|
+
// Orchestrator (public entry point)
|
|
386
|
+
// ----------------------------------------------------------------------
|
|
387
|
+
|
|
388
|
+
/**
|
|
389
|
+
* Orchestrates the whole update operation. argv excludes the leading
|
|
390
|
+
* "update" subcommand. Throws on any failure; cli.ts converts to stderr+exit.
|
|
391
|
+
*/
|
|
392
|
+
export async function runUpdateCommand(
|
|
393
|
+
argv: string[],
|
|
394
|
+
transport: UpdateTransport = defaultTransport,
|
|
395
|
+
): Promise<void> {
|
|
396
|
+
// 1. Parse args
|
|
397
|
+
const opts = parseUpdateArgs(argv);
|
|
398
|
+
|
|
399
|
+
// 2. Classify install — refuse if not a release build
|
|
400
|
+
const classification = classifyInstall();
|
|
401
|
+
if (classification.kind !== "release") {
|
|
402
|
+
const guidance = guidanceFor(classification.kind);
|
|
403
|
+
throw new Error(
|
|
404
|
+
`Cannot self-update: ${classification.reason}.\n${guidance}`,
|
|
405
|
+
);
|
|
406
|
+
}
|
|
407
|
+
const binaryPath = classification.binaryPath;
|
|
408
|
+
|
|
409
|
+
// 3. Detect artifact name for this platform
|
|
410
|
+
const artifactName = detectArtifactName();
|
|
411
|
+
|
|
412
|
+
// 4. Fetch the target release
|
|
413
|
+
process.stdout.write("Checking for updates...\n");
|
|
414
|
+
const release = opts.targetTag
|
|
415
|
+
? await fetchReleaseByTag(opts.targetTag, transport)
|
|
416
|
+
: await fetchLatestRelease(transport);
|
|
417
|
+
|
|
418
|
+
// 5. Compute action
|
|
419
|
+
const action = computeAction(VERSION, release.version, opts.force);
|
|
420
|
+
|
|
421
|
+
// 6. Print status
|
|
422
|
+
process.stdout.write(`Current: ${VERSION}\n`);
|
|
423
|
+
process.stdout.write(`Latest: ${release.version}\n`);
|
|
424
|
+
|
|
425
|
+
// 7. --check: print human-readable status and return
|
|
426
|
+
if (opts.check) {
|
|
427
|
+
if (action === "no-op") {
|
|
428
|
+
process.stdout.write("Up to date.\n");
|
|
429
|
+
} else if (action === "install") {
|
|
430
|
+
process.stdout.write(`Update available: ${VERSION} -> ${release.version}\n`);
|
|
431
|
+
} else if (action === "downgrade") {
|
|
432
|
+
process.stdout.write(`Older version available: ${release.version} (current: ${VERSION})\n`);
|
|
433
|
+
} else {
|
|
434
|
+
// reinstall (--force + --check)
|
|
435
|
+
process.stdout.write(`Will reinstall: ${release.version}\n`);
|
|
436
|
+
}
|
|
437
|
+
return;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
// 8. no-op without --force
|
|
441
|
+
if (action === "no-op") {
|
|
442
|
+
process.stdout.write("Already up to date.\n");
|
|
443
|
+
return;
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
// 9. Select the platform artifact
|
|
447
|
+
const artifact = selectArtifact(release, artifactName);
|
|
448
|
+
|
|
449
|
+
const plan: UpdatePlan = {
|
|
450
|
+
current: VERSION,
|
|
451
|
+
target: release.version,
|
|
452
|
+
tag: release.tag,
|
|
453
|
+
artifact,
|
|
454
|
+
binaryPath,
|
|
455
|
+
action,
|
|
456
|
+
};
|
|
457
|
+
|
|
458
|
+
// 10. --dry-run: print the plan and return
|
|
459
|
+
if (opts.dryRun) {
|
|
460
|
+
printDryRunPlan(plan);
|
|
461
|
+
return;
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
// 11. Execute
|
|
465
|
+
await executePlan(plan, transport);
|
|
466
|
+
process.stdout.write(`Installed claude-mode ${plan.target} at ${binaryPath}\n`);
|
|
467
|
+
}
|
package/src/usage.ts
CHANGED
|
@@ -4,6 +4,13 @@ export function printUsage(): void {
|
|
|
4
4
|
Subcommands:
|
|
5
5
|
config Manage configuration
|
|
6
6
|
inspect [--print] Show prompt assembly plan with provenance and warnings
|
|
7
|
+
update [version] Update the installed binary from GitHub Releases
|
|
8
|
+
|
|
9
|
+
Update flags (passed after the "update" subcommand):
|
|
10
|
+
--check Check for updates without installing
|
|
11
|
+
--force Reinstall the same version (repair a corrupted install)
|
|
12
|
+
--dry-run Show what would happen without writing anything
|
|
13
|
+
<version> Install a specific release tag (e.g. "0.2.5"); default is latest
|
|
7
14
|
|
|
8
15
|
Info:
|
|
9
16
|
--version Print claude-mode version and exit
|
|
@@ -58,7 +65,11 @@ Examples:
|
|
|
58
65
|
claude-mode director # delegate to sub-agents
|
|
59
66
|
claude-mode partner # equal-pair: speak plainly, TDD by default
|
|
60
67
|
claude-mode create --modifier bold # confident, idiomatic code
|
|
61
|
-
claude-mode create -- --verbose --model sonnet
|
|
68
|
+
claude-mode create -- --verbose --model sonnet
|
|
69
|
+
claude-mode update # update to the latest release
|
|
70
|
+
claude-mode update --check # check for updates without installing
|
|
71
|
+
claude-mode update 0.2.5 # pin to a specific version (downgrade or reinstall)
|
|
72
|
+
claude-mode update --force # reinstall the same version (repair)`;
|
|
62
73
|
|
|
63
74
|
process.stdout.write(usage + "\n");
|
|
64
75
|
}
|