claude-code-modes 0.3.4 → 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 +5 -2
- package/package.json +1 -1
- package/prompts/base/actions.md +1 -1
- package/prompts/style/declaudified.md +16 -0
- package/src/args.ts +20 -2
- package/src/assemble.ts +9 -0
- package/src/build-info.ts +1 -1
- package/src/build-prompt.ts +4 -1
- package/src/cli.ts +4 -1
- package/src/config-cli.ts +45 -0
- package/src/config.ts +30 -2
- package/src/embedded-prompts.ts +18 -1
- package/src/env.ts +82 -13
- package/src/inspect.ts +7 -0
- package/src/presets.ts +1 -0
- package/src/resolve.ts +55 -1
- package/src/types.ts +16 -0
- package/src/update.ts +5 -3
- package/src/usage.ts +8 -0
package/README.md
CHANGED
|
@@ -150,7 +150,7 @@ prompts/
|
|
|
150
150
|
modifiers/ Behavioral layers (bold, debug, methodical, director, readonly, context-pacing, speak-plain, tdd, muse, flow, playful)
|
|
151
151
|
```
|
|
152
152
|
|
|
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.
|
|
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**.
|
|
154
154
|
|
|
155
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.
|
|
156
156
|
|
|
@@ -198,9 +198,12 @@ claude-mode create --append-system-prompt "Use Rust, not TypeScript"
|
|
|
198
198
|
Pass flags through to Claude Code:
|
|
199
199
|
|
|
200
200
|
```bash
|
|
201
|
-
claude-mode create --
|
|
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
|
|
202
203
|
```
|
|
203
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
|
+
|
|
204
207
|
Debug the assembled prompt:
|
|
205
208
|
|
|
206
209
|
```bash
|
package/package.json
CHANGED
package/prompts/base/actions.md
CHANGED
|
@@ -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. 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 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.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# Style: Declaudified
|
|
2
|
+
|
|
3
|
+
How to write everything the user reads — answers, summaries, explanations, commit messages, docs.
|
|
4
|
+
|
|
5
|
+
- Lead with the answer. State the point first, then support it.
|
|
6
|
+
- Cut filler and metadiscourse — language that narrates the communicating instead of communicating: "let me lay out," "it's worth noting," "as mentioned above." Say the thing; don't say you're about to say it.
|
|
7
|
+
- Delete your opening sentence. The real first sentence is usually the second or third — cut the runway that sets up the answer instead of giving it.
|
|
8
|
+
- Do add relevant surrounding context that hasn't already been established in the conversation — don't assume the user has perfect repo or domain knowledge. Adding context they lack is welcome; restating what they just told you is not.
|
|
9
|
+
- Don't signpost structure. A list or table is self-evident; don't preface it with "here's a table of."
|
|
10
|
+
- Plain language: real names for real things, minimal jargon, no padding adjectives. Use real terminology; don't invent labels ("status," not "milestones").
|
|
11
|
+
- Drop dead tech-metaphors and stock phrases ("ship," "load-bearing," "first-class," "surface" as a verb, "seamless," "leverage," "robust"). Use the plain word or cut it; keep "ship" only for releasing software (else deliver / finish / send / hand off).
|
|
12
|
+
- Don't state a cause without evidence — label speculation or leave it out.
|
|
13
|
+
|
|
14
|
+
In short: say the thing; don't say you're about to say it, and don't say you understood the question.
|
|
15
|
+
|
|
16
|
+
References: George Orwell, "Politics and the English Language" (his plain-English rules); Strunk & White, *The Elements of Style* ("omit needless words"); Joseph Williams, *Style: Toward Clarity and Grace* (on cutting metadiscourse).
|
package/src/args.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { parseArgs } from "node:util";
|
|
|
3
3
|
export interface ParsedArgs {
|
|
4
4
|
base?: string;
|
|
5
5
|
preset: string | null;
|
|
6
|
+
style?: string;
|
|
6
7
|
overrides: {
|
|
7
8
|
agency?: string;
|
|
8
9
|
quality?: string;
|
|
@@ -17,10 +18,22 @@ export interface ParsedArgs {
|
|
|
17
18
|
forwarded: {
|
|
18
19
|
appendSystemPrompt?: string;
|
|
19
20
|
appendSystemPromptFile?: string;
|
|
21
|
+
model?: string;
|
|
20
22
|
};
|
|
23
|
+
/** Model claude will run — from --model, or peeked from after `--`; drives env detection */
|
|
24
|
+
modelHint: string | null;
|
|
21
25
|
passthroughArgs: string[];
|
|
22
26
|
}
|
|
23
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
|
+
|
|
24
37
|
export function parseCliArgs(argv: string[]): ParsedArgs {
|
|
25
38
|
// Split at -- separator
|
|
26
39
|
const dashDashIdx = argv.indexOf("--");
|
|
@@ -34,12 +47,14 @@ export function parseCliArgs(argv: string[]): ParsedArgs {
|
|
|
34
47
|
agency: { type: "string" },
|
|
35
48
|
quality: { type: "string" },
|
|
36
49
|
scope: { type: "string" },
|
|
50
|
+
style: { type: "string" },
|
|
37
51
|
modifier: { type: "string", multiple: true },
|
|
38
52
|
readonly: { type: "boolean" },
|
|
39
53
|
print: { type: "boolean" },
|
|
40
54
|
"context-pacing": { type: "boolean" },
|
|
41
55
|
"append-system-prompt": { type: "string" },
|
|
42
56
|
"append-system-prompt-file": { type: "string" },
|
|
57
|
+
model: { type: "string" },
|
|
43
58
|
"system-prompt": { type: "string" },
|
|
44
59
|
"system-prompt-file": { type: "string" },
|
|
45
60
|
help: { type: "boolean" },
|
|
@@ -78,8 +93,8 @@ export function parseCliArgs(argv: string[]): ParsedArgs {
|
|
|
78
93
|
|
|
79
94
|
// Collect unknown flags for passthrough
|
|
80
95
|
const knownFlags = new Set([
|
|
81
|
-
"base", "agency", "quality", "scope", "modifier", "readonly", "print", "context-pacing",
|
|
82
|
-
"append-system-prompt", "append-system-prompt-file",
|
|
96
|
+
"base", "agency", "quality", "scope", "style", "modifier", "readonly", "print", "context-pacing",
|
|
97
|
+
"append-system-prompt", "append-system-prompt-file", "model",
|
|
83
98
|
"system-prompt", "system-prompt-file", "help", "version",
|
|
84
99
|
]);
|
|
85
100
|
const unknownPassthrough: string[] = [];
|
|
@@ -102,6 +117,7 @@ export function parseCliArgs(argv: string[]): ParsedArgs {
|
|
|
102
117
|
return {
|
|
103
118
|
base: values.base as string | undefined,
|
|
104
119
|
preset,
|
|
120
|
+
style: values.style as string | undefined,
|
|
105
121
|
overrides,
|
|
106
122
|
modifiers: {
|
|
107
123
|
readonly: values.readonly === true,
|
|
@@ -112,7 +128,9 @@ export function parseCliArgs(argv: string[]): ParsedArgs {
|
|
|
112
128
|
forwarded: {
|
|
113
129
|
appendSystemPrompt: values["append-system-prompt"] as string | undefined,
|
|
114
130
|
appendSystemPromptFile: values["append-system-prompt-file"] as string | undefined,
|
|
131
|
+
model: values.model as string | undefined,
|
|
115
132
|
},
|
|
133
|
+
modelHint: (values.model as string | undefined) ?? peekModelAfterDashDash(afterDashDash),
|
|
116
134
|
passthroughArgs,
|
|
117
135
|
};
|
|
118
136
|
}
|
package/src/assemble.ts
CHANGED
|
@@ -116,6 +116,15 @@ export function getFragmentOrder(mode: ModeConfig, promptsDir: string): string[]
|
|
|
116
116
|
}
|
|
117
117
|
}
|
|
118
118
|
}
|
|
119
|
+
// Style fragment rides the axes insertion point but applies even in
|
|
120
|
+
// none mode — an explicit --style is honored like explicit modifiers
|
|
121
|
+
if (mode.style) {
|
|
122
|
+
if (isAbsolute(mode.style)) {
|
|
123
|
+
fragments.push(mode.style);
|
|
124
|
+
} else {
|
|
125
|
+
fragments.push(`style/${mode.style}.md`);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
119
128
|
} else if (entry === "modifiers") {
|
|
120
129
|
// All modifiers are fragment paths — just add them
|
|
121
130
|
for (const modPath of mode.modifiers) {
|
package/src/build-info.ts
CHANGED
package/src/build-prompt.ts
CHANGED
|
@@ -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);
|
package/src/config-cli.ts
CHANGED
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
checkModifierNameCollision,
|
|
9
9
|
checkPresetNameCollision,
|
|
10
10
|
checkAxisValueCollision,
|
|
11
|
+
checkStyleNameCollision,
|
|
11
12
|
} from "./config.js";
|
|
12
13
|
import { AXIS_BUILTINS } from "./types.js";
|
|
13
14
|
const VALID_AXES = Object.keys(AXIS_BUILTINS) as Array<"agency" | "quality" | "scope">;
|
|
@@ -120,6 +121,33 @@ function configRemoveModifier(configPath: string, name: string): void {
|
|
|
120
121
|
);
|
|
121
122
|
}
|
|
122
123
|
|
|
124
|
+
function configAddStyle(configPath: string, name: string, mdPath: string): void {
|
|
125
|
+
checkStyleNameCollision(name);
|
|
126
|
+
const config = readConfig(configPath);
|
|
127
|
+
config.styles = { ...config.styles, [name]: mdPath };
|
|
128
|
+
writeConfig(configPath, config);
|
|
129
|
+
process.stdout.write(
|
|
130
|
+
`Registered style "${name}" in ${configFileName(configPath)}\n`
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function configRemoveStyle(configPath: string, name: string): void {
|
|
135
|
+
const config = readConfig(configPath);
|
|
136
|
+
const styles = config.styles ?? {};
|
|
137
|
+
if (!(name in styles)) {
|
|
138
|
+
throw new Error(
|
|
139
|
+
`Style "${name}" not found in ${configFileName(configPath)}`
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
const updated = { ...styles };
|
|
143
|
+
delete updated[name];
|
|
144
|
+
config.styles = updated;
|
|
145
|
+
writeConfig(configPath, config);
|
|
146
|
+
process.stdout.write(
|
|
147
|
+
`Unregistered style "${name}" from ${configFileName(configPath)}\n`
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
|
|
123
151
|
function configAddAxis(configPath: string, axis: string, name: string, mdPath: string): void {
|
|
124
152
|
if (!VALID_AXES.includes(axis as ValidAxis)) {
|
|
125
153
|
throw new Error(
|
|
@@ -173,6 +201,7 @@ function configAddPreset(configPath: string, name: string, flags: string[]): voi
|
|
|
173
201
|
agency: { type: "string" },
|
|
174
202
|
quality: { type: "string" },
|
|
175
203
|
scope: { type: "string" },
|
|
204
|
+
style: { type: "string" },
|
|
176
205
|
modifier: { type: "string", multiple: true },
|
|
177
206
|
readonly: { type: "boolean" },
|
|
178
207
|
"context-pacing": { type: "boolean" },
|
|
@@ -184,6 +213,7 @@ function configAddPreset(configPath: string, name: string, flags: string[]): voi
|
|
|
184
213
|
if (values.agency !== undefined) presetDef.agency = values.agency;
|
|
185
214
|
if (values.quality !== undefined) presetDef.quality = values.quality;
|
|
186
215
|
if (values.scope !== undefined) presetDef.scope = values.scope;
|
|
216
|
+
if (values.style !== undefined) presetDef.style = values.style;
|
|
187
217
|
if (values.modifier !== undefined && values.modifier.length > 0) {
|
|
188
218
|
presetDef.modifiers = values.modifier as string[];
|
|
189
219
|
}
|
|
@@ -238,6 +268,8 @@ Subcommands:
|
|
|
238
268
|
remove-default <name> Remove from defaultModifiers
|
|
239
269
|
add-modifier <name> <path> Register a named modifier
|
|
240
270
|
remove-modifier <name> Unregister a named modifier
|
|
271
|
+
add-style <name> <path> Register a named style
|
|
272
|
+
remove-style <name> Unregister a named style
|
|
241
273
|
add-axis <axis> <name> <path> Register custom axis value
|
|
242
274
|
remove-axis <axis> <name> Unregister custom axis value
|
|
243
275
|
add-preset <name> [flags] Create a custom preset
|
|
@@ -247,6 +279,7 @@ Flags for add-preset:
|
|
|
247
279
|
--agency <value>
|
|
248
280
|
--quality <value>
|
|
249
281
|
--scope <value>
|
|
282
|
+
--style <value>
|
|
250
283
|
--modifier <name> (repeatable)
|
|
251
284
|
--readonly
|
|
252
285
|
--context-pacing`;
|
|
@@ -287,6 +320,18 @@ Flags for add-preset:
|
|
|
287
320
|
break;
|
|
288
321
|
}
|
|
289
322
|
|
|
323
|
+
case "add-style": {
|
|
324
|
+
if (rest.length < 2) throw new Error("add-style requires <name> <path>");
|
|
325
|
+
configAddStyle(configPath, rest[0], rest[1]);
|
|
326
|
+
break;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
case "remove-style": {
|
|
330
|
+
if (rest.length < 1) throw new Error("remove-style requires <name>");
|
|
331
|
+
configRemoveStyle(configPath, rest[0]);
|
|
332
|
+
break;
|
|
333
|
+
}
|
|
334
|
+
|
|
290
335
|
case "add-axis": {
|
|
291
336
|
if (rest.length < 3) throw new Error("add-axis requires <axis> <name> <path>");
|
|
292
337
|
configAddAxis(configPath, rest[0], rest[1], rest[2]);
|
package/src/config.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { readFileSync, existsSync } from "node:fs";
|
|
2
2
|
import { join, dirname, resolve, isAbsolute } from "node:path";
|
|
3
3
|
import { homedir } from "node:os";
|
|
4
|
-
import { PRESET_NAMES, BUILTIN_MODIFIER_NAMES, AXIS_BUILTINS, BUILTIN_BASE_NAMES, isBuiltinModifier, isPresetName, isBuiltinBase, isBuiltinAxisValue } from "./types.js";
|
|
4
|
+
import { PRESET_NAMES, BUILTIN_MODIFIER_NAMES, AXIS_BUILTINS, BUILTIN_BASE_NAMES, STYLE_VALUES, isBuiltinModifier, isPresetName, isBuiltinBase, isBuiltinAxisValue, isBuiltinStyle } from "./types.js";
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* Matches paths that reference potentially sensitive files (SSH keys, credentials, etc.).
|
|
@@ -33,6 +33,8 @@ function validateConfigDefinedPath(path: string, context: string, configPath: st
|
|
|
33
33
|
export interface UserConfig {
|
|
34
34
|
defaultBase?: string;
|
|
35
35
|
bases?: Record<string, string>; // name → directory path (relative to config dir)
|
|
36
|
+
defaultStyle?: string;
|
|
37
|
+
styles?: Record<string, string>; // name → .md path (relative to config dir)
|
|
36
38
|
defaultModifiers?: string[];
|
|
37
39
|
modifiers?: Record<string, string>;
|
|
38
40
|
axes?: {
|
|
@@ -48,6 +50,7 @@ export interface CustomPresetDef {
|
|
|
48
50
|
agency?: string;
|
|
49
51
|
quality?: string;
|
|
50
52
|
scope?: string;
|
|
53
|
+
style?: string;
|
|
51
54
|
modifiers?: string[];
|
|
52
55
|
readonly?: boolean;
|
|
53
56
|
contextPacing?: boolean;
|
|
@@ -76,6 +79,15 @@ export function checkPresetNameCollision(name: string): void {
|
|
|
76
79
|
}
|
|
77
80
|
}
|
|
78
81
|
|
|
82
|
+
/** Throws if name collides with a built-in style name. */
|
|
83
|
+
export function checkStyleNameCollision(name: string): void {
|
|
84
|
+
if (isBuiltinStyle(name)) {
|
|
85
|
+
throw new Error(
|
|
86
|
+
`"${name}" is a built-in style name (${STYLE_VALUES.join(", ")}); choose a different name`
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
79
91
|
/** Throws if name collides with a built-in base name. */
|
|
80
92
|
export function checkBaseNameCollision(name: string): void {
|
|
81
93
|
if (isBuiltinBase(name)) {
|
|
@@ -204,6 +216,22 @@ function validateConfig(raw: unknown, configPath: string): UserConfig {
|
|
|
204
216
|
for (const key of Object.keys(bases)) checkBaseNameCollision(key);
|
|
205
217
|
}
|
|
206
218
|
|
|
219
|
+
// Validate defaultStyle
|
|
220
|
+
if (obj.defaultStyle !== undefined && typeof obj.defaultStyle !== "string") {
|
|
221
|
+
throw new Error(
|
|
222
|
+
`Invalid config file ${configPath}: "defaultStyle" must be a string`
|
|
223
|
+
);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// Validate styles map
|
|
227
|
+
if (obj.styles !== undefined) {
|
|
228
|
+
const styles = validateStringRecord(obj.styles, "styles", configPath);
|
|
229
|
+
for (const [key, val] of Object.entries(styles)) {
|
|
230
|
+
checkStyleNameCollision(key);
|
|
231
|
+
validateConfigDefinedPath(val, `"styles.${key}"`, configPath);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
207
235
|
// Validate defaultModifiers
|
|
208
236
|
if (obj.defaultModifiers !== undefined) {
|
|
209
237
|
const defaultModifiers = validateStringArray(obj.defaultModifiers, "defaultModifiers", configPath);
|
|
@@ -262,7 +290,7 @@ function validateConfig(raw: unknown, configPath: string): UserConfig {
|
|
|
262
290
|
`Invalid config file ${configPath}: preset "${presetName}.base" must be a string`
|
|
263
291
|
);
|
|
264
292
|
}
|
|
265
|
-
for (const field of ["agency", "quality", "scope"] as const) {
|
|
293
|
+
for (const field of ["agency", "quality", "scope", "style"] as const) {
|
|
266
294
|
if (def[field] !== undefined && typeof def[field] !== "string") {
|
|
267
295
|
throw new Error(
|
|
268
296
|
`Invalid config file ${configPath}: preset "${presetName}.${field}" must be a string`
|
package/src/embedded-prompts.ts
CHANGED
|
@@ -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. 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 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.
|
|
@@ -533,6 +533,23 @@ Stay strictly within the bounds of what was requested.
|
|
|
533
533
|
- Do not refactor, rename, or reorganize anything that isn't directly required by the task.
|
|
534
534
|
- If the request is to change function X, change function X. Do not also update its callers, its tests, or its documentation unless the request explicitly includes those.
|
|
535
535
|
- If completing the request requires changing more code than expected, pause and confirm the scope with the user before proceeding.
|
|
536
|
+
`,
|
|
537
|
+
"style/declaudified.md": `# Style: Declaudified
|
|
538
|
+
|
|
539
|
+
How to write everything the user reads — answers, summaries, explanations, commit messages, docs.
|
|
540
|
+
|
|
541
|
+
- Lead with the answer. State the point first, then support it.
|
|
542
|
+
- Cut filler and metadiscourse — language that narrates the communicating instead of communicating: "let me lay out," "it's worth noting," "as mentioned above." Say the thing; don't say you're about to say it.
|
|
543
|
+
- Delete your opening sentence. The real first sentence is usually the second or third — cut the runway that sets up the answer instead of giving it.
|
|
544
|
+
- Do add relevant surrounding context that hasn't already been established in the conversation — don't assume the user has perfect repo or domain knowledge. Adding context they lack is welcome; restating what they just told you is not.
|
|
545
|
+
- Don't signpost structure. A list or table is self-evident; don't preface it with "here's a table of."
|
|
546
|
+
- Plain language: real names for real things, minimal jargon, no padding adjectives. Use real terminology; don't invent labels ("status," not "milestones").
|
|
547
|
+
- Drop dead tech-metaphors and stock phrases ("ship," "load-bearing," "first-class," "surface" as a verb, "seamless," "leverage," "robust"). Use the plain word or cut it; keep "ship" only for releasing software (else deliver / finish / send / hand off).
|
|
548
|
+
- Don't state a cause without evidence — label speculation or leave it out.
|
|
549
|
+
|
|
550
|
+
In short: say the thing; don't say you're about to say it, and don't say you understood the question.
|
|
551
|
+
|
|
552
|
+
References: George Orwell, "Politics and the English Language" (his plain-English rules); Strunk & White, *The Elements of Style* ("omit needless words"); Joseph Williams, *Style: Toward Clarity and Grace* (on cutting metadiscourse).
|
|
536
553
|
`,
|
|
537
554
|
"modifiers/readonly.md": `# Read-only mode
|
|
538
555
|
|
package/src/env.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { execSync } from "node:child_process";
|
|
2
|
-
import {
|
|
3
|
-
import
|
|
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
|
-
|
|
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 = "Claude Opus 4.8";
|
|
48
|
-
const MODEL_ID = "claude-opus-4-8";
|
|
49
|
-
const KNOWLEDGE_CUTOFF = "January 2026";
|
|
50
|
-
|
|
51
119
|
export function buildTemplateVars(env: EnvInfo): TemplateVars {
|
|
52
120
|
let gitStatusBlock = "";
|
|
53
121
|
if (env.isGit) {
|
|
@@ -63,7 +131,8 @@ export function buildTemplateVars(env: EnvInfo): TemplateVars {
|
|
|
63
131
|
}
|
|
64
132
|
|
|
65
133
|
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."
|
|
134
|
+
? "\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." +
|
|
135
|
+
"\n - The git stash stack is shared with the main checkout and all other worktrees, and other Claude sessions may push or pop it concurrently. Never use bare `git stash` / `git stash pop` — you could pop another session's changes. Prefer a temporary WIP commit to set work aside; if you must stash, use `git stash push -u -m \"<unique-tag>\"`, immediately capture your entry's SHA via `git stash list --format='%H %gs'`, restore with `git stash apply <sha>` (not pop), and afterwards drop the entry, re-finding its current `stash@{n}` by tag first."
|
|
67
136
|
: "";
|
|
68
137
|
|
|
69
138
|
return {
|
|
@@ -72,9 +141,9 @@ export function buildTemplateVars(env: EnvInfo): TemplateVars {
|
|
|
72
141
|
PLATFORM: env.platform,
|
|
73
142
|
SHELL: env.shell,
|
|
74
143
|
OS_VERSION: env.osVersion,
|
|
75
|
-
MODEL_NAME,
|
|
76
|
-
MODEL_ID,
|
|
77
|
-
KNOWLEDGE_CUTOFF,
|
|
144
|
+
MODEL_NAME: env.model.name,
|
|
145
|
+
MODEL_ID: env.model.id,
|
|
146
|
+
KNOWLEDGE_CUTOFF: env.model.cutoff,
|
|
78
147
|
GIT_STATUS: gitStatusBlock,
|
|
79
148
|
WORKTREE_NOTICE: worktreeNotice,
|
|
80
149
|
};
|
package/src/inspect.ts
CHANGED
|
@@ -66,6 +66,13 @@ function collectConfigDefinedPaths(loadedConfig: LoadedConfig | null): Set<strin
|
|
|
66
66
|
}
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
+
// Style definitions
|
|
70
|
+
if (config.styles) {
|
|
71
|
+
for (const val of Object.values(config.styles)) {
|
|
72
|
+
paths.add(resolveConfigPath(configDir, val));
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
69
76
|
// Axis definitions
|
|
70
77
|
if (config.axes) {
|
|
71
78
|
for (const axisName of ["agency", "quality", "scope"] as const) {
|
package/src/presets.ts
CHANGED
package/src/resolve.ts
CHANGED
|
@@ -7,11 +7,13 @@ import {
|
|
|
7
7
|
AGENCY_VALUES,
|
|
8
8
|
QUALITY_VALUES,
|
|
9
9
|
SCOPE_VALUES,
|
|
10
|
+
STYLE_VALUES,
|
|
10
11
|
BUILTIN_MODIFIER_NAMES,
|
|
11
12
|
BUILTIN_BASE_NAMES,
|
|
12
13
|
PRESET_NAMES,
|
|
13
14
|
isBuiltinModifier,
|
|
14
15
|
isBuiltinBase,
|
|
16
|
+
isBuiltinStyle,
|
|
15
17
|
} from "./types.js";
|
|
16
18
|
import { resolve as pathResolve, isAbsolute } from "node:path";
|
|
17
19
|
|
|
@@ -128,6 +130,51 @@ function applyModifiers(
|
|
|
128
130
|
}
|
|
129
131
|
}
|
|
130
132
|
|
|
133
|
+
/**
|
|
134
|
+
* Resolves a style value to either a built-in name or an absolute path.
|
|
135
|
+
* Resolution order: built-in values → config-defined names → file path heuristic.
|
|
136
|
+
* Throws with descriptive error if unresolvable.
|
|
137
|
+
*/
|
|
138
|
+
function resolveStyleValue(raw: string, loadedConfig: LoadedConfig | null): string {
|
|
139
|
+
// 1. Built-in value
|
|
140
|
+
if (isBuiltinStyle(raw)) return raw;
|
|
141
|
+
|
|
142
|
+
// 2. Config-defined custom name
|
|
143
|
+
const configStyles = loadedConfig?.config.styles;
|
|
144
|
+
if (configStyles && raw in configStyles) {
|
|
145
|
+
return resolveConfigPath(loadedConfig!.configDir, configStyles[raw]);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// 3. File path
|
|
149
|
+
if (looksLikeFilePath(raw)) {
|
|
150
|
+
return isAbsolute(raw) ? raw : pathResolve(raw);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// 4. Unknown
|
|
154
|
+
const configHint = loadedConfig
|
|
155
|
+
? ` Config loaded from: ${loadedConfig.configDir}`
|
|
156
|
+
: " No config file found.";
|
|
157
|
+
throw new Error(
|
|
158
|
+
`Unknown --style value: "${raw}". ` +
|
|
159
|
+
`Must be one of: ${STYLE_VALUES.join(", ")}, ` +
|
|
160
|
+
`a name defined in your config, or a file path.${configHint}`
|
|
161
|
+
);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Resolves the active style, or null when none applies.
|
|
166
|
+
* Priority: CLI --style > config defaultStyle > preset style > none
|
|
167
|
+
*/
|
|
168
|
+
function resolveStyle(
|
|
169
|
+
raw: string | undefined,
|
|
170
|
+
loadedConfig: LoadedConfig | null,
|
|
171
|
+
presetStyle: string | undefined,
|
|
172
|
+
): string | null {
|
|
173
|
+
const value = raw ?? loadedConfig?.config.defaultStyle ?? presetStyle;
|
|
174
|
+
if (value === undefined) return null;
|
|
175
|
+
return resolveStyleValue(value, loadedConfig);
|
|
176
|
+
}
|
|
177
|
+
|
|
131
178
|
/**
|
|
132
179
|
* Resolves a base reference to a built-in name or absolute directory path.
|
|
133
180
|
* Priority: CLI --base > config defaultBase > preset base > "standard"
|
|
@@ -190,12 +237,14 @@ export function resolveConfig(
|
|
|
190
237
|
// 3. CLI --modifier flags — appended after defaults
|
|
191
238
|
applyModifiers(parsed.customModifiers, loadedConfig, modifierPaths, "append");
|
|
192
239
|
|
|
193
|
-
// Handle "none" preset — resolve base before early return
|
|
240
|
+
// Handle "none" preset — resolve base before early return.
|
|
241
|
+
// Explicit --style (or config defaultStyle) still applies, like modifiers.
|
|
194
242
|
if (parsed.preset === "none") {
|
|
195
243
|
const base = resolveBase(parsed.base, loadedConfig, undefined);
|
|
196
244
|
return {
|
|
197
245
|
base,
|
|
198
246
|
axes: null,
|
|
247
|
+
style: resolveStyle(parsed.style, loadedConfig, undefined),
|
|
199
248
|
modifiers: modifierPaths,
|
|
200
249
|
};
|
|
201
250
|
}
|
|
@@ -204,6 +253,7 @@ export function resolveConfig(
|
|
|
204
253
|
let quality: string;
|
|
205
254
|
let scope: string;
|
|
206
255
|
let presetBase: string | undefined;
|
|
256
|
+
let presetStyle: string | undefined;
|
|
207
257
|
|
|
208
258
|
if (parsed.preset) {
|
|
209
259
|
// Check built-in presets first, then config presets
|
|
@@ -220,6 +270,7 @@ export function resolveConfig(
|
|
|
220
270
|
? resolveAxisValue(parsed.overrides.scope, "scope", SCOPE_VALUES, loadedConfig)
|
|
221
271
|
: preset.axes.scope;
|
|
222
272
|
presetBase = preset.base;
|
|
273
|
+
presetStyle = preset.style;
|
|
223
274
|
|
|
224
275
|
// Apply preset's readonly flag as a modifier
|
|
225
276
|
if (preset.readonly) {
|
|
@@ -233,6 +284,7 @@ export function resolveConfig(
|
|
|
233
284
|
// Config-defined preset
|
|
234
285
|
const customPreset = config.presets[parsed.preset];
|
|
235
286
|
presetBase = customPreset.base;
|
|
287
|
+
presetStyle = customPreset.style;
|
|
236
288
|
agency = parsed.overrides.agency
|
|
237
289
|
? resolveAxisValue(parsed.overrides.agency, "agency", AGENCY_VALUES, loadedConfig)
|
|
238
290
|
: customPreset.agency
|
|
@@ -284,10 +336,12 @@ export function resolveConfig(
|
|
|
284
336
|
}
|
|
285
337
|
|
|
286
338
|
const base = resolveBase(parsed.base, loadedConfig, presetBase);
|
|
339
|
+
const style = resolveStyle(parsed.style, loadedConfig, presetStyle);
|
|
287
340
|
|
|
288
341
|
return {
|
|
289
342
|
base,
|
|
290
343
|
axes: { agency, quality, scope },
|
|
344
|
+
style,
|
|
291
345
|
modifiers: modifierPaths,
|
|
292
346
|
};
|
|
293
347
|
}
|
package/src/types.ts
CHANGED
|
@@ -7,6 +7,13 @@ export type Quality = (typeof QUALITY_VALUES)[number];
|
|
|
7
7
|
export const SCOPE_VALUES = ["unrestricted", "adjacent", "narrow"] as const;
|
|
8
8
|
export type Scope = (typeof SCOPE_VALUES)[number];
|
|
9
9
|
|
|
10
|
+
// Built-in style names — writing styles applied on top of any base/axes
|
|
11
|
+
export const STYLE_VALUES = ["declaudified"] as const;
|
|
12
|
+
export type Style = (typeof STYLE_VALUES)[number];
|
|
13
|
+
export function isBuiltinStyle(value: string): value is Style {
|
|
14
|
+
return (STYLE_VALUES as readonly string[]).includes(value);
|
|
15
|
+
}
|
|
16
|
+
|
|
10
17
|
export const PRESET_NAMES = [
|
|
11
18
|
"create",
|
|
12
19
|
"extend",
|
|
@@ -69,9 +76,17 @@ export interface AxisConfig {
|
|
|
69
76
|
export interface ModeConfig {
|
|
70
77
|
base: string; // built-in name ("standard", "chill") or absolute path to base directory
|
|
71
78
|
axes: AxisConfig | null; // null for "none" mode
|
|
79
|
+
style: string | null; // built-in name or absolute path to a custom fragment; null = no style
|
|
72
80
|
modifiers: string[]; // ordered list of modifier fragment paths (embedded keys or absolute paths)
|
|
73
81
|
}
|
|
74
82
|
|
|
83
|
+
/** Resolved model metadata for env.md substitution */
|
|
84
|
+
export interface ModelInfo {
|
|
85
|
+
name: string;
|
|
86
|
+
id: string;
|
|
87
|
+
cutoff: string;
|
|
88
|
+
}
|
|
89
|
+
|
|
75
90
|
export interface EnvInfo {
|
|
76
91
|
cwd: string;
|
|
77
92
|
isGit: boolean;
|
|
@@ -82,6 +97,7 @@ export interface EnvInfo {
|
|
|
82
97
|
platform: string;
|
|
83
98
|
shell: string;
|
|
84
99
|
osVersion: string;
|
|
100
|
+
model: ModelInfo;
|
|
85
101
|
}
|
|
86
102
|
|
|
87
103
|
/** Template variables for env.md substitution */
|
package/src/update.ts
CHANGED
|
@@ -159,11 +159,13 @@ export function classifyInstall(
|
|
|
159
159
|
execPath: string = process.execPath,
|
|
160
160
|
buildInfo: BuildInfo = BUILD_INFO,
|
|
161
161
|
): InstallClassification {
|
|
162
|
-
// 1. Runtime interpreter → source mode
|
|
163
|
-
|
|
162
|
+
// 1. Runtime interpreter → source mode. The npm distribution of bun names
|
|
163
|
+
// its binary "bun.exe" on every platform, so strip the suffix before matching.
|
|
164
|
+
const execName = basename(execPath).replace(/\.exe$/, "");
|
|
165
|
+
if (SOURCE_EXEC_NAMES.has(execName)) {
|
|
164
166
|
return {
|
|
165
167
|
kind: "source",
|
|
166
|
-
reason: `Running as ${
|
|
168
|
+
reason: `Running as ${execName} runtime (source mode)`,
|
|
167
169
|
};
|
|
168
170
|
}
|
|
169
171
|
|
package/src/usage.ts
CHANGED
|
@@ -42,6 +42,11 @@ Axis overrides:
|
|
|
42
42
|
--scope <value> Built-in: unrestricted, adjacent, narrow
|
|
43
43
|
Axis values can also be config-defined names or file paths (.md files).
|
|
44
44
|
|
|
45
|
+
Style:
|
|
46
|
+
--style <value> Writing style for output. Built-in: declaudified
|
|
47
|
+
Style can also be a config-defined name or a file path (.md file).
|
|
48
|
+
No style is applied unless set via --style, config defaultStyle, or a preset.
|
|
49
|
+
|
|
45
50
|
Modifiers:
|
|
46
51
|
--readonly Prevent file modifications
|
|
47
52
|
--context-pacing Include context pacing prompt
|
|
@@ -51,6 +56,8 @@ Modifiers:
|
|
|
51
56
|
Forwarded to claude:
|
|
52
57
|
--append-system-prompt <text>
|
|
53
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.
|
|
54
61
|
|
|
55
62
|
Config: .claude-mode.json (project) or ~/.config/claude-mode/config.json (global)
|
|
56
63
|
|
|
@@ -60,6 +67,7 @@ Examples:
|
|
|
60
67
|
claude-mode create
|
|
61
68
|
claude-mode create --base chill # use the chill base
|
|
62
69
|
claude-mode create --quality pragmatic
|
|
70
|
+
claude-mode create --style declaudified # lead-with-the-answer writing, no filler
|
|
63
71
|
claude-mode create --modifier ./my-rules.md
|
|
64
72
|
claude-mode --agency autonomous --quality ./team-quality.md
|
|
65
73
|
claude-mode team-default # custom preset from config
|