claude-code-modes 0.4.0 → 0.4.1

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 CHANGED
@@ -147,11 +147,10 @@ prompts/
147
147
  chill/ Alternative base (emotion-research-informed, leaner)
148
148
  flow/ Alternative base (chill's calm + restored engagement)
149
149
  axis/ Behavioral prompts organized by three axes
150
- style/ Writing styles (declaudified)
151
150
  modifiers/ Behavioral layers (bold, debug, methodical, director, readonly, context-pacing, speak-plain, tdd, muse, flow, playful)
152
151
  ```
153
152
 
154
- 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.198**.
153
+ 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.217**.
155
154
 
156
155
  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.
157
156
 
@@ -186,15 +185,6 @@ claude-mode create --quality ./team-quality.md # Use a custom quality fragme
186
185
  claude-mode create --quality team-standard # Resolve from config
187
186
  ```
188
187
 
189
- Set a writing style — a single fragment that shapes how Claude writes to you (answers, summaries, commit messages), independent of the behavioral axes:
190
-
191
- ```bash
192
- claude-mode create --style declaudified # Built-in: lead with the answer, cut filler and stock phrases
193
- claude-mode create --style ./house-style.md # Or a custom fragment / config-defined name
194
- ```
195
-
196
- No style is applied unless you pass `--style`, set `defaultStyle` in config, or use a preset that declares one.
197
-
198
188
  Add modifiers:
199
189
 
200
190
  ```bash
@@ -208,9 +198,12 @@ claude-mode create --append-system-prompt "Use Rust, not TypeScript"
208
198
  Pass flags through to Claude Code:
209
199
 
210
200
  ```bash
211
- claude-mode create -- --verbose --model sonnet
201
+ claude-mode create --model sonnet # model choice also lands in the prompt's environment info
202
+ claude-mode create -- --verbose # anything after -- goes to claude verbatim
212
203
  ```
213
204
 
205
+ The environment section of the assembled prompt reports the model claude will actually run: from `--model` (before or after `--`), else the `ANTHROPIC_MODEL` env var, else Claude settings files (`.claude/settings.local.json`, `.claude/settings.json`, `~/.claude/settings.json`), else the newest known model.
206
+
214
207
  Debug the assembled prompt:
215
208
 
216
209
  ```bash
@@ -259,9 +252,6 @@ Example `.claude-mode.json`:
259
252
  "modifiers": {
260
253
  "team-rules": "./prompts/team-rules.md"
261
254
  },
262
- "styles": {
263
- "house": "./prompts/house-style.md"
264
- },
265
255
  "axes": {
266
256
  "quality": {
267
257
  "team-standard": "./prompts/team-quality.md"
@@ -272,7 +262,6 @@ Example `.claude-mode.json`:
272
262
  "agency": "collaborative",
273
263
  "quality": "team-standard",
274
264
  "scope": "adjacent",
275
- "style": "house",
276
265
  "modifiers": ["team-rules"]
277
266
  }
278
267
  }
@@ -281,15 +270,13 @@ Example `.claude-mode.json`:
281
270
 
282
271
  - **`defaultModifiers`** — always applied to every invocation (no flag needed)
283
272
  - **`modifiers`** — named modifiers referencing markdown files
284
- - **`styles`** — named writing styles referencing markdown files
285
273
  - **`axes`** — custom axis values (replace built-in fragments)
286
274
  - **`presets`** — named presets composing built-in and custom values
287
275
 
288
- Config also supports bases and a default style:
276
+ Config also supports bases:
289
277
 
290
278
  - **`defaultBase`** — base to use when `--base` isn't specified
291
279
  - **`bases`** — named bases referencing directories with `base.json` manifests
292
- - **`defaultStyle`** — style to apply when `--style` isn't specified
293
280
 
294
281
  Config searches `.claude-mode.json` in the current directory first, then `~/.config/claude-mode/config.json` as a global fallback. All commands accept `--global` to target the global config.
295
282
 
@@ -302,8 +289,6 @@ claude-mode config add-default <name-or-path> # Add to defaultModifiers
302
289
  claude-mode config remove-default <name> # Remove from defaultModifiers
303
290
  claude-mode config add-modifier <name> <path> # Register named modifier
304
291
  claude-mode config remove-modifier <name> # Unregister named modifier
305
- claude-mode config add-style <name> <path> # Register named style
306
- claude-mode config remove-style <name> # Unregister named style
307
292
  claude-mode config add-axis <axis> <name> <path> # Register custom axis value
308
293
  claude-mode config remove-axis <axis> <name> # Unregister custom axis value
309
294
  claude-mode config add-preset <name> [flags] # Create custom preset
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-code-modes",
3
- "version": "0.4.0",
3
+ "version": "0.4.1",
4
4
  "description": "Behaviorally-tuned system prompts for Claude Code",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -8,4 +8,4 @@ Examples of the kind of risky actions that warrant user confirmation:
8
8
  - Actions visible to others or that affect shared state: pushing code, creating/closing/commenting on PRs or issues, sending messages (Slack, email, GitHub), posting to external services, modifying shared infrastructure or permissions
9
9
  - Uploading content to third-party web tools (diagram renderers, pastebins, gists) publishes it - consider whether it could be sensitive before sending, since it may be cached or indexed even if later deleted.
10
10
 
11
- When you encounter an obstacle, do not use destructive actions as a shortcut to simply make it go away. For instance, try to identify root causes and fix underlying issues rather than bypassing safety checks (e.g. --no-verify). If you discover unexpected state like unfamiliar files, branches, or configuration, investigate before deleting or overwriting, as it may represent the user's in-progress work. If you're unsure whether the user would want something kept, prefer a reversible step (move it aside, rename it, or stash it) over deleting; files you created yourself this session (scratch outputs, experiment intermediates) are yours to clean up freely. For example, typically resolve merge conflicts rather than discarding changes; similarly, if a lock file exists, investigate what process holds it rather than deleting it. In a git repository, run `git status` before any command that could discard uncommitted work (git checkout/restore/reset/clean, rm -rf on a repo path, restoring from a snapshot), and stash (with `-u` for untracked) or commit anything you find first. In short: only take risky actions carefully, and when in doubt, ask before acting. Follow both the spirit and letter of these instructions - measure twice, cut once.
11
+ When you encounter an obstacle, do not use destructive actions as a shortcut to simply make it go away. For instance, try to identify root causes and fix underlying issues rather than bypassing safety checks (e.g. --no-verify). If you discover unexpected state like unfamiliar files, branches, or configuration, investigate before deleting or overwriting, as it may represent the user's in-progress work. If you're unsure whether the user would want something kept, prefer a reversible step (move it aside, rename it, or stash it) over deleting; files you created yourself this session (scratch outputs, experiment intermediates) are yours to clean up freely. For example, typically resolve merge conflicts rather than discarding changes; similarly, if a lock file exists, investigate what process holds it rather than deleting it. In a git repository, run `git status` before any command that could discard uncommitted work (git checkout/restore/reset/clean, rm -rf on a repo path, restoring from a snapshot), and stash (with `-u` for untracked) or commit anything you find first. And when staging or committing: review what's included (`git status` after a broad `git add`), and if you see anything suspicious that might reveal secrets — even if the filename looks innocuous — double-check the file's contents before pushing. In short: only take risky actions carefully, and when in doubt, ask before acting. Follow both the spirit and letter of these instructions - measure twice, cut once.
package/src/args.ts CHANGED
@@ -18,10 +18,22 @@ export interface ParsedArgs {
18
18
  forwarded: {
19
19
  appendSystemPrompt?: string;
20
20
  appendSystemPromptFile?: string;
21
+ model?: string;
21
22
  };
23
+ /** Model claude will run — from --model, or peeked from after `--`; drives env detection */
24
+ modelHint: string | null;
22
25
  passthroughArgs: string[];
23
26
  }
24
27
 
28
+ // After `--` everything is passed verbatim, but the model choice still shapes
29
+ // the environment section — peek without disturbing the passthrough list
30
+ function peekModelAfterDashDash(args: string[]): string | null {
31
+ const flagIdx = args.indexOf("--model");
32
+ if (flagIdx >= 0 && flagIdx + 1 < args.length) return args[flagIdx + 1];
33
+ const inline = args.find((arg) => arg.startsWith("--model="));
34
+ return inline ? inline.slice("--model=".length) : null;
35
+ }
36
+
25
37
  export function parseCliArgs(argv: string[]): ParsedArgs {
26
38
  // Split at -- separator
27
39
  const dashDashIdx = argv.indexOf("--");
@@ -42,6 +54,7 @@ export function parseCliArgs(argv: string[]): ParsedArgs {
42
54
  "context-pacing": { type: "boolean" },
43
55
  "append-system-prompt": { type: "string" },
44
56
  "append-system-prompt-file": { type: "string" },
57
+ model: { type: "string" },
45
58
  "system-prompt": { type: "string" },
46
59
  "system-prompt-file": { type: "string" },
47
60
  help: { type: "boolean" },
@@ -81,7 +94,7 @@ export function parseCliArgs(argv: string[]): ParsedArgs {
81
94
  // Collect unknown flags for passthrough
82
95
  const knownFlags = new Set([
83
96
  "base", "agency", "quality", "scope", "style", "modifier", "readonly", "print", "context-pacing",
84
- "append-system-prompt", "append-system-prompt-file",
97
+ "append-system-prompt", "append-system-prompt-file", "model",
85
98
  "system-prompt", "system-prompt-file", "help", "version",
86
99
  ]);
87
100
  const unknownPassthrough: string[] = [];
@@ -115,7 +128,9 @@ export function parseCliArgs(argv: string[]): ParsedArgs {
115
128
  forwarded: {
116
129
  appendSystemPrompt: values["append-system-prompt"] as string | undefined,
117
130
  appendSystemPromptFile: values["append-system-prompt-file"] as string | undefined,
131
+ model: values.model as string | undefined,
118
132
  },
133
+ modelHint: (values.model as string | undefined) ?? peekModelAfterDashDash(afterDashDash),
119
134
  passthroughArgs,
120
135
  };
121
136
  }
package/src/build-info.ts CHANGED
@@ -12,6 +12,6 @@ export interface BuildInfo {
12
12
  export const BUILD_INFO: BuildInfo = {
13
13
  "repo": "https://github.com/nklisch/claude-code-modes",
14
14
  "branch": null,
15
- "commit": "91f6069",
15
+ "commit": "6303b59",
16
16
  "dirty": false
17
17
  };
@@ -98,7 +98,7 @@ function main(): void {
98
98
  }
99
99
 
100
100
  // Detect environment and build template vars
101
- const env = detectEnv();
101
+ const env = detectEnv(parsed.modelHint);
102
102
  const templateVars = buildTemplateVars(env);
103
103
 
104
104
  // Assemble the prompt
@@ -127,6 +127,9 @@ function main(): void {
127
127
  if (parsed.forwarded.appendSystemPromptFile) {
128
128
  claudeArgs.push("--append-system-prompt-file", parsed.forwarded.appendSystemPromptFile);
129
129
  }
130
+ if (parsed.forwarded.model) {
131
+ claudeArgs.push("--model", parsed.forwarded.model);
132
+ }
130
133
 
131
134
  // Add passthrough args
132
135
  claudeArgs.push(...parsed.passthroughArgs);
package/src/cli.ts CHANGED
@@ -119,7 +119,7 @@ async function main(): Promise<void> {
119
119
  }
120
120
 
121
121
  // Detect environment and build template vars
122
- const env = detectEnv();
122
+ const env = detectEnv(parsed.modelHint);
123
123
  const templateVars = buildTemplateVars(env);
124
124
 
125
125
  // Assemble the prompt
@@ -150,6 +150,9 @@ async function main(): Promise<void> {
150
150
  if (parsed.forwarded.appendSystemPromptFile) {
151
151
  claudeArgs.push("--append-system-prompt-file", parsed.forwarded.appendSystemPromptFile);
152
152
  }
153
+ if (parsed.forwarded.model) {
154
+ claudeArgs.push("--model", parsed.forwarded.model);
155
+ }
153
156
 
154
157
  // Add passthrough args
155
158
  claudeArgs.push(...parsed.passthroughArgs);
@@ -45,7 +45,7 @@ Examples of the kind of risky actions that warrant user confirmation:
45
45
  - Actions visible to others or that affect shared state: pushing code, creating/closing/commenting on PRs or issues, sending messages (Slack, email, GitHub), posting to external services, modifying shared infrastructure or permissions
46
46
  - Uploading content to third-party web tools (diagram renderers, pastebins, gists) publishes it - consider whether it could be sensitive before sending, since it may be cached or indexed even if later deleted.
47
47
 
48
- When you encounter an obstacle, do not use destructive actions as a shortcut to simply make it go away. For instance, try to identify root causes and fix underlying issues rather than bypassing safety checks (e.g. --no-verify). If you discover unexpected state like unfamiliar files, branches, or configuration, investigate before deleting or overwriting, as it may represent the user's in-progress work. If you're unsure whether the user would want something kept, prefer a reversible step (move it aside, rename it, or stash it) over deleting; files you created yourself this session (scratch outputs, experiment intermediates) are yours to clean up freely. For example, typically resolve merge conflicts rather than discarding changes; similarly, if a lock file exists, investigate what process holds it rather than deleting it. In a git repository, run \`git status\` before any command that could discard uncommitted work (git checkout/restore/reset/clean, rm -rf on a repo path, restoring from a snapshot), and stash (with \`-u\` for untracked) or commit anything you find first. In short: only take risky actions carefully, and when in doubt, ask before acting. Follow both the spirit and letter of these instructions - measure twice, cut once.
48
+ When you encounter an obstacle, do not use destructive actions as a shortcut to simply make it go away. For instance, try to identify root causes and fix underlying issues rather than bypassing safety checks (e.g. --no-verify). If you discover unexpected state like unfamiliar files, branches, or configuration, investigate before deleting or overwriting, as it may represent the user's in-progress work. If you're unsure whether the user would want something kept, prefer a reversible step (move it aside, rename it, or stash it) over deleting; files you created yourself this session (scratch outputs, experiment intermediates) are yours to clean up freely. For example, typically resolve merge conflicts rather than discarding changes; similarly, if a lock file exists, investigate what process holds it rather than deleting it. In a git repository, run \`git status\` before any command that could discard uncommitted work (git checkout/restore/reset/clean, rm -rf on a repo path, restoring from a snapshot), and stash (with \`-u\` for untracked) or commit anything you find first. And when staging or committing: review what's included (\`git status\` after a broad \`git add\`), and if you see anything suspicious that might reveal secrets — even if the filename looks innocuous — double-check the file's contents before pushing. In short: only take risky actions carefully, and when in doubt, ask before acting. Follow both the spirit and letter of these instructions - measure twice, cut once.
49
49
  `,
50
50
  "base/tools.md": `# Using your tools
51
51
  - Use your dedicated tools instead of shell equivalents. Read works better than cat or grep. Editing via sed or awk is error-prone and slow compared to Edit or your global search-and-replace tools. Using pgrep or echo for process monitoring just slows us down without adding control. Bash tools require user approval and may be rejected, especially in a sequence — calling them when a dedicated tool would do is a cost we don't need to pay.
package/src/env.ts CHANGED
@@ -1,6 +1,8 @@
1
1
  import { execSync } from "node:child_process";
2
- import { basename } from "node:path";
3
- import type { EnvInfo, TemplateVars } from "./types.js";
2
+ import { readFileSync } from "node:fs";
3
+ import { homedir } from "node:os";
4
+ import { basename, join } from "node:path";
5
+ import type { EnvInfo, ModelInfo, TemplateVars } from "./types.js";
4
6
 
5
7
  function exec(command: string): string | null {
6
8
  try {
@@ -18,7 +20,77 @@ function exec(command: string): string | null {
18
20
  }
19
21
  }
20
22
 
21
- export function detectEnv(): EnvInfo {
23
+ // Model metadata table extracted from the Claude Code binary — update when Claude Code updates.
24
+ // Extraction: grep the native binary for `{id:"claude-...,display_name:...,knowledge_cutoff:...`
25
+ const MODEL_TABLE: readonly ModelInfo[] = [
26
+ { id: "claude-fable-5", name: "Fable 5", cutoff: "January 2026" },
27
+ { id: "claude-mythos-5", name: "Mythos 5", cutoff: "January 2026" },
28
+ { id: "claude-opus-4-8", name: "Opus 4.8", cutoff: "January 2026" },
29
+ { id: "claude-opus-4-7", name: "Opus 4.7", cutoff: "January 2026" },
30
+ { id: "claude-opus-4-6", name: "Opus 4.6", cutoff: "May 2025" },
31
+ { id: "claude-opus-4-5", name: "Opus 4.5", cutoff: "May 2025" },
32
+ { id: "claude-opus-4-1", name: "Opus 4.1", cutoff: "January 2025" },
33
+ { id: "claude-opus-4-0", name: "Opus 4", cutoff: "January 2025" },
34
+ { id: "claude-sonnet-5", name: "Sonnet 5", cutoff: "January 2026" },
35
+ { id: "claude-sonnet-4-6", name: "Sonnet 4.6", cutoff: "August 2025" },
36
+ { id: "claude-sonnet-4-5", name: "Sonnet 4.5", cutoff: "January 2025" },
37
+ { id: "claude-sonnet-4-0", name: "Sonnet 4", cutoff: "January 2025" },
38
+ { id: "claude-haiku-4-5", name: "Haiku 4.5", cutoff: "February 2025" },
39
+ ] as const;
40
+
41
+ // Aliases resolve to the newest model of the family; opusplan executes on opus
42
+ const MODEL_ALIASES: Record<string, string> = {
43
+ opus: "claude-opus-4-8",
44
+ opusplan: "claude-opus-4-8",
45
+ sonnet: "claude-sonnet-5",
46
+ haiku: "claude-haiku-4-5",
47
+ fable: "claude-fable-5",
48
+ };
49
+
50
+ const DEFAULT_MODEL: ModelInfo = MODEL_TABLE[0];
51
+
52
+ export function resolveModel(raw: string | null | undefined): ModelInfo {
53
+ if (!raw || raw === "default") return DEFAULT_MODEL;
54
+
55
+ const has1m = raw.endsWith("[1m]");
56
+ const base = has1m ? raw.slice(0, -"[1m]".length) : raw;
57
+ const id = MODEL_ALIASES[base] ?? base;
58
+
59
+ // Exact id first, then dated variants like claude-haiku-4-5-20251001
60
+ const entry =
61
+ MODEL_TABLE.find((m) => m.id === id) ?? MODEL_TABLE.find((m) => id.startsWith(`${m.id}-`));
62
+ if (!entry) {
63
+ // Unknown model — likely newer than the table; surface the id as the name
64
+ // and keep the newest known cutoff rather than inventing one
65
+ return { name: id, id: has1m ? `${id}[1m]` : id, cutoff: DEFAULT_MODEL.cutoff };
66
+ }
67
+
68
+ return {
69
+ name: has1m ? `${entry.name} (1M context)` : entry.name,
70
+ id: has1m ? `${id}[1m]` : id,
71
+ cutoff: entry.cutoff,
72
+ };
73
+ }
74
+
75
+ // Claude Code reads its model setting from these files, most specific first
76
+ function readConfiguredModel(cwd: string): string | null {
77
+ const candidates = [
78
+ join(cwd, ".claude", "settings.local.json"),
79
+ join(cwd, ".claude", "settings.json"),
80
+ join(homedir(), ".claude", "settings.json"),
81
+ ];
82
+ for (const path of candidates) {
83
+ try {
84
+ const settings = JSON.parse(readFileSync(path, "utf8"));
85
+ if (typeof settings.model === "string" && settings.model !== "") return settings.model;
86
+ } catch {
87
+ // Missing or malformed settings file — try the next candidate
88
+ }
89
+ }
90
+ return null;
91
+ }
92
+
93
+ export function detectEnv(modelArg?: string | null): EnvInfo {
22
94
  const cwd = process.cwd();
23
95
  const isGit = exec("git rev-parse --is-inside-work-tree") === "true";
24
96
 
@@ -39,15 +111,11 @@ export function detectEnv(): EnvInfo {
39
111
  const platform = exec("uname -s")?.toLowerCase() ?? "unknown";
40
112
  const shell = basename(process.env.SHELL || "bash");
41
113
  const osVersion = exec("uname -sr") ?? "unknown";
114
+ const model = resolveModel(modelArg ?? process.env.ANTHROPIC_MODEL ?? readConfiguredModel(cwd));
42
115
 
43
- return { cwd, isGit, isWorktree, gitBranch, gitStatus, gitLog, platform, shell, osVersion };
116
+ return { cwd, isGit, isWorktree, gitBranch, gitStatus, gitLog, platform, shell, osVersion, model };
44
117
  }
45
118
 
46
- // Hardcoded model info — update when Claude Code updates
47
- const MODEL_NAME = "Fable 5";
48
- const MODEL_ID = "claude-fable-5";
49
- const KNOWLEDGE_CUTOFF = "January 2026";
50
-
51
119
  export function buildTemplateVars(env: EnvInfo): TemplateVars {
52
120
  let gitStatusBlock = "";
53
121
  if (env.isGit) {
@@ -73,9 +141,9 @@ export function buildTemplateVars(env: EnvInfo): TemplateVars {
73
141
  PLATFORM: env.platform,
74
142
  SHELL: env.shell,
75
143
  OS_VERSION: env.osVersion,
76
- MODEL_NAME,
77
- MODEL_ID,
78
- KNOWLEDGE_CUTOFF,
144
+ MODEL_NAME: env.model.name,
145
+ MODEL_ID: env.model.id,
146
+ KNOWLEDGE_CUTOFF: env.model.cutoff,
79
147
  GIT_STATUS: gitStatusBlock,
80
148
  WORKTREE_NOTICE: worktreeNotice,
81
149
  };
package/src/types.ts CHANGED
@@ -80,6 +80,13 @@ export interface ModeConfig {
80
80
  modifiers: string[]; // ordered list of modifier fragment paths (embedded keys or absolute paths)
81
81
  }
82
82
 
83
+ /** Resolved model metadata for env.md substitution */
84
+ export interface ModelInfo {
85
+ name: string;
86
+ id: string;
87
+ cutoff: string;
88
+ }
89
+
83
90
  export interface EnvInfo {
84
91
  cwd: string;
85
92
  isGit: boolean;
@@ -90,6 +97,7 @@ export interface EnvInfo {
90
97
  platform: string;
91
98
  shell: string;
92
99
  osVersion: string;
100
+ model: ModelInfo;
93
101
  }
94
102
 
95
103
  /** Template variables for env.md substitution */
package/src/usage.ts CHANGED
@@ -56,6 +56,8 @@ Modifiers:
56
56
  Forwarded to claude:
57
57
  --append-system-prompt <text>
58
58
  --append-system-prompt-file <path>
59
+ --model <alias|id> Also sets the model info in the prompt's environment section.
60
+ Without --model, model info comes from ANTHROPIC_MODEL or Claude settings files.
59
61
 
60
62
  Config: .claude-mode.json (project) or ~/.config/claude-mode/config.json (global)
61
63