rafcode 2.1.1 → 2.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/settings.local.json +4 -1
- package/CLAUDE.md +59 -11
- package/RAF/ahslfe-config-wizard/decisions.md +34 -0
- package/RAF/ahslfe-config-wizard/input.md +1 -0
- package/RAF/ahslfe-config-wizard/outcomes/01-define-config-schema.md +38 -0
- package/RAF/ahslfe-config-wizard/outcomes/02-refactor-codebase-to-use-config.md +67 -0
- package/RAF/ahslfe-config-wizard/outcomes/03-create-config-documentation.md +37 -0
- package/RAF/ahslfe-config-wizard/outcomes/04-implement-raf-config-command.md +47 -0
- package/RAF/ahslfe-config-wizard/outcomes/05-update-claude-md.md +26 -0
- package/RAF/ahslfe-config-wizard/plans/01-define-config-schema.md +73 -0
- package/RAF/ahslfe-config-wizard/plans/02-refactor-codebase-to-use-config.md +74 -0
- package/RAF/ahslfe-config-wizard/plans/03-create-config-documentation.md +57 -0
- package/RAF/ahslfe-config-wizard/plans/04-implement-raf-config-command.md +66 -0
- package/RAF/ahslfe-config-wizard/plans/05-update-claude-md.md +60 -0
- package/RAF/ahstvo-token-tracker/decisions.md +44 -0
- package/RAF/ahstvo-token-tracker/input.md +3 -0
- package/RAF/ahstvo-token-tracker/outcomes/01-full-model-id-support.md +43 -0
- package/RAF/ahstvo-token-tracker/outcomes/02-name-generation-no-session.md +33 -0
- package/RAF/ahstvo-token-tracker/outcomes/03-unify-stream-json-execution.md +48 -0
- package/RAF/ahstvo-token-tracker/outcomes/04-token-tracking-cost-calculation.md +53 -0
- package/RAF/ahstvo-token-tracker/outcomes/05-token-cost-console-reporting.md +57 -0
- package/RAF/ahstvo-token-tracker/outcomes/06-runtime-verbose-toggle.md +53 -0
- package/RAF/ahstvo-token-tracker/outcomes/07-readme-config-docs.md +36 -0
- package/RAF/ahstvo-token-tracker/plans/01-full-model-id-support.md +35 -0
- package/RAF/ahstvo-token-tracker/plans/02-name-generation-no-session.md +36 -0
- package/RAF/ahstvo-token-tracker/plans/03-unify-stream-json-execution.md +44 -0
- package/RAF/ahstvo-token-tracker/plans/04-token-tracking-cost-calculation.md +56 -0
- package/RAF/ahstvo-token-tracker/plans/05-token-cost-console-reporting.md +55 -0
- package/RAF/ahstvo-token-tracker/plans/06-runtime-verbose-toggle.md +48 -0
- package/RAF/ahstvo-token-tracker/plans/07-readme-config-docs.md +44 -0
- package/RAF/ahtahs-token-reaper/decisions.md +37 -0
- package/RAF/ahtahs-token-reaper/input.md +20 -0
- package/RAF/ahtahs-token-reaper/outcomes/01-extend-token-tracker-data-model.md +42 -0
- package/RAF/ahtahs-token-reaper/outcomes/02-accumulate-usage-in-retry-loop.md +31 -0
- package/RAF/ahtahs-token-reaper/outcomes/03-per-attempt-display-formatting.md +60 -0
- package/RAF/ahtahs-token-reaper/outcomes/04-add-model-name-to-claude-call-logs.md +57 -0
- package/RAF/ahtahs-token-reaper/outcomes/05-handle-invalid-config-in-raf-config.md +46 -0
- package/RAF/ahtahs-token-reaper/outcomes/06-fix-verbose-toggle-timer-display.md +38 -0
- package/RAF/ahtahs-token-reaper/plans/01-extend-token-tracker-data-model.md +36 -0
- package/RAF/ahtahs-token-reaper/plans/02-accumulate-usage-in-retry-loop.md +36 -0
- package/RAF/ahtahs-token-reaper/plans/03-per-attempt-display-formatting.md +43 -0
- package/RAF/ahtahs-token-reaper/plans/04-add-model-name-to-claude-call-logs.md +38 -0
- package/RAF/ahtahs-token-reaper/plans/05-handle-invalid-config-in-raf-config.md +36 -0
- package/RAF/ahtahs-token-reaper/plans/06-fix-verbose-toggle-timer-display.md +40 -0
- package/README.md +34 -0
- package/dist/commands/config.d.ts +3 -0
- package/dist/commands/config.d.ts.map +1 -0
- package/dist/commands/config.js +195 -0
- package/dist/commands/config.js.map +1 -0
- package/dist/commands/do.d.ts.map +1 -1
- package/dist/commands/do.js +55 -7
- package/dist/commands/do.js.map +1 -1
- package/dist/commands/plan.d.ts.map +1 -1
- package/dist/commands/plan.js +5 -3
- package/dist/commands/plan.js.map +1 -1
- package/dist/core/claude-runner.d.ts +19 -2
- package/dist/core/claude-runner.d.ts.map +1 -1
- package/dist/core/claude-runner.js +43 -96
- package/dist/core/claude-runner.js.map +1 -1
- package/dist/core/failure-analyzer.d.ts.map +1 -1
- package/dist/core/failure-analyzer.js +6 -3
- package/dist/core/failure-analyzer.js.map +1 -1
- package/dist/core/git.d.ts.map +1 -1
- package/dist/core/git.js +10 -3
- package/dist/core/git.js.map +1 -1
- package/dist/core/pull-request.d.ts +1 -1
- package/dist/core/pull-request.d.ts.map +1 -1
- package/dist/core/pull-request.js +9 -4
- package/dist/core/pull-request.js.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/parsers/stream-renderer.d.ts +16 -1
- package/dist/parsers/stream-renderer.d.ts.map +1 -1
- package/dist/parsers/stream-renderer.js +34 -4
- package/dist/parsers/stream-renderer.js.map +1 -1
- package/dist/prompts/execution.d.ts.map +1 -1
- package/dist/prompts/execution.js +11 -1
- package/dist/prompts/execution.js.map +1 -1
- package/dist/types/config.d.ts +95 -4
- package/dist/types/config.d.ts.map +1 -1
- package/dist/types/config.js +63 -3
- package/dist/types/config.js.map +1 -1
- package/dist/utils/config.d.ts +65 -7
- package/dist/utils/config.d.ts.map +1 -1
- package/dist/utils/config.js +297 -21
- package/dist/utils/config.js.map +1 -1
- package/dist/utils/name-generator.d.ts +3 -7
- package/dist/utils/name-generator.d.ts.map +1 -1
- package/dist/utils/name-generator.js +75 -61
- package/dist/utils/name-generator.js.map +1 -1
- package/dist/utils/terminal-symbols.d.ts +25 -0
- package/dist/utils/terminal-symbols.d.ts.map +1 -1
- package/dist/utils/terminal-symbols.js +87 -0
- package/dist/utils/terminal-symbols.js.map +1 -1
- package/dist/utils/token-tracker.d.ts +55 -0
- package/dist/utils/token-tracker.d.ts.map +1 -0
- package/dist/utils/token-tracker.js +142 -0
- package/dist/utils/token-tracker.js.map +1 -0
- package/dist/utils/validation.d.ts +5 -5
- package/dist/utils/validation.d.ts.map +1 -1
- package/dist/utils/validation.js +10 -6
- package/dist/utils/validation.js.map +1 -1
- package/dist/utils/verbose-toggle.d.ts +33 -0
- package/dist/utils/verbose-toggle.d.ts.map +1 -0
- package/dist/utils/verbose-toggle.js +94 -0
- package/dist/utils/verbose-toggle.js.map +1 -0
- package/package.json +1 -1
- package/src/commands/config.ts +230 -0
- package/src/commands/do.ts +64 -6
- package/src/commands/plan.ts +5 -3
- package/src/core/claude-runner.ts +59 -115
- package/src/core/failure-analyzer.ts +6 -3
- package/src/core/git.ts +10 -3
- package/src/core/pull-request.ts +9 -4
- package/src/index.ts +2 -0
- package/src/parsers/stream-renderer.ts +54 -4
- package/src/prompts/config-docs.md +331 -0
- package/src/prompts/execution.ts +13 -1
- package/src/types/config.ts +156 -7
- package/src/utils/config.ts +357 -21
- package/src/utils/name-generator.ts +84 -71
- package/src/utils/terminal-symbols.ts +103 -0
- package/src/utils/token-tracker.ts +177 -0
- package/src/utils/validation.ts +15 -10
- package/src/utils/verbose-toggle.ts +103 -0
- package/tests/unit/claude-runner.test.ts +171 -7
- package/tests/unit/config-command.test.ts +242 -0
- package/tests/unit/config.test.ts +632 -30
- package/tests/unit/name-generator.test.ts +99 -75
- package/tests/unit/pull-request.test.ts +2 -0
- package/tests/unit/stream-renderer.test.ts +83 -0
- package/tests/unit/terminal-symbols.test.ts +245 -0
- package/tests/unit/timer-verbose-integration.test.ts +170 -0
- package/tests/unit/token-tracker.test.ts +685 -0
- package/tests/unit/verbose-toggle.test.ts +204 -0
package/dist/types/config.d.ts
CHANGED
|
@@ -1,12 +1,86 @@
|
|
|
1
|
+
/** Short alias for a Claude model family */
|
|
2
|
+
export type ClaudeModelAlias = 'sonnet' | 'haiku' | 'opus';
|
|
3
|
+
/**
|
|
4
|
+
* Accepts short aliases (`sonnet`, `haiku`, `opus`) or full model IDs
|
|
5
|
+
* matching the pattern `claude-{family}-{version}` (e.g., `claude-opus-4-5-20251101`).
|
|
6
|
+
*/
|
|
7
|
+
export type ClaudeModelName = ClaudeModelAlias | (string & {
|
|
8
|
+
__brand?: 'FullModelId';
|
|
9
|
+
});
|
|
10
|
+
export type EffortLevel = 'low' | 'medium' | 'high';
|
|
11
|
+
export type ModelScenario = 'plan' | 'execute' | 'nameGeneration' | 'failureAnalysis' | 'prGeneration' | 'config';
|
|
12
|
+
export type EffortScenario = ModelScenario;
|
|
13
|
+
export type CommitFormatType = 'task' | 'plan' | 'amend';
|
|
14
|
+
export interface ModelsConfig {
|
|
15
|
+
plan: ClaudeModelName;
|
|
16
|
+
execute: ClaudeModelName;
|
|
17
|
+
nameGeneration: ClaudeModelName;
|
|
18
|
+
failureAnalysis: ClaudeModelName;
|
|
19
|
+
prGeneration: ClaudeModelName;
|
|
20
|
+
config: ClaudeModelName;
|
|
21
|
+
}
|
|
22
|
+
export interface EffortConfig {
|
|
23
|
+
plan: EffortLevel;
|
|
24
|
+
execute: EffortLevel;
|
|
25
|
+
nameGeneration: EffortLevel;
|
|
26
|
+
failureAnalysis: EffortLevel;
|
|
27
|
+
prGeneration: EffortLevel;
|
|
28
|
+
config: EffortLevel;
|
|
29
|
+
}
|
|
30
|
+
export interface CommitFormatConfig {
|
|
31
|
+
task: string;
|
|
32
|
+
plan: string;
|
|
33
|
+
amend: string;
|
|
34
|
+
prefix: string;
|
|
35
|
+
}
|
|
36
|
+
/** Pricing category derived from model family name. */
|
|
37
|
+
export type PricingCategory = 'opus' | 'sonnet' | 'haiku';
|
|
38
|
+
/** Per-direction pricing for a single model category, in dollars per million tokens. */
|
|
39
|
+
export interface ModelPricing {
|
|
40
|
+
inputPerMTok: number;
|
|
41
|
+
outputPerMTok: number;
|
|
42
|
+
cacheReadPerMTok: number;
|
|
43
|
+
cacheCreatePerMTok: number;
|
|
44
|
+
}
|
|
45
|
+
/** Pricing config: per-category pricing in dollars per million tokens. */
|
|
46
|
+
export interface PricingConfig {
|
|
47
|
+
opus: ModelPricing;
|
|
48
|
+
sonnet: ModelPricing;
|
|
49
|
+
haiku: ModelPricing;
|
|
50
|
+
}
|
|
1
51
|
export interface RafConfig {
|
|
52
|
+
models: ModelsConfig;
|
|
53
|
+
effort: EffortConfig;
|
|
54
|
+
timeout: number;
|
|
55
|
+
maxRetries: number;
|
|
56
|
+
autoCommit: boolean;
|
|
57
|
+
worktree: boolean;
|
|
58
|
+
commitFormat: CommitFormatConfig;
|
|
59
|
+
claudeCommand: string;
|
|
60
|
+
pricing: PricingConfig;
|
|
61
|
+
}
|
|
62
|
+
export declare const DEFAULT_CONFIG: RafConfig;
|
|
63
|
+
/** Deep partial type for user config files — all fields optional at every level */
|
|
64
|
+
export type DeepPartial<T> = {
|
|
65
|
+
[P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P];
|
|
66
|
+
};
|
|
67
|
+
export type UserConfig = DeepPartial<RafConfig>;
|
|
68
|
+
export declare const VALID_MODEL_ALIASES: readonly ClaudeModelAlias[];
|
|
69
|
+
/**
|
|
70
|
+
* Regex for full Claude model IDs (e.g., `claude-sonnet-4-5-20250929`, `claude-opus-4-5-20251101`).
|
|
71
|
+
* Pattern: claude-{family}-{major}(-{minor})?(-{date})?
|
|
72
|
+
*/
|
|
73
|
+
export declare const FULL_MODEL_ID_PATTERN: RegExp;
|
|
74
|
+
/** @deprecated Use VALID_MODEL_ALIASES instead */
|
|
75
|
+
export declare const VALID_MODELS: readonly ClaudeModelAlias[];
|
|
76
|
+
export declare const VALID_EFFORTS: readonly EffortLevel[];
|
|
77
|
+
/** @deprecated Use DEFAULT_CONFIG instead */
|
|
78
|
+
export declare const DEFAULT_RAF_CONFIG: {
|
|
2
79
|
defaultTimeout: number;
|
|
3
80
|
defaultMaxRetries: number;
|
|
4
81
|
autoCommit: boolean;
|
|
5
82
|
claudeCommand: string;
|
|
6
|
-
|
|
7
|
-
}
|
|
8
|
-
export declare const DEFAULT_RAF_CONFIG: RafConfig;
|
|
9
|
-
export type ClaudeModelName = 'sonnet' | 'haiku' | 'opus';
|
|
83
|
+
};
|
|
10
84
|
export interface PlanCommandOptions {
|
|
11
85
|
projectName?: string;
|
|
12
86
|
model?: ClaudeModelName;
|
|
@@ -28,4 +102,21 @@ export interface MigrateCommandOptions {
|
|
|
28
102
|
dryRun?: boolean;
|
|
29
103
|
worktree?: boolean;
|
|
30
104
|
}
|
|
105
|
+
/** Per-model token usage breakdown from stream-json result event. */
|
|
106
|
+
export interface ModelTokenUsage {
|
|
107
|
+
inputTokens: number;
|
|
108
|
+
outputTokens: number;
|
|
109
|
+
cacheReadInputTokens: number;
|
|
110
|
+
cacheCreationInputTokens: number;
|
|
111
|
+
}
|
|
112
|
+
/** Token usage data extracted from Claude CLI stream-json result event. */
|
|
113
|
+
export interface UsageData {
|
|
114
|
+
/** Aggregate token counts across all models. */
|
|
115
|
+
inputTokens: number;
|
|
116
|
+
outputTokens: number;
|
|
117
|
+
cacheReadInputTokens: number;
|
|
118
|
+
cacheCreationInputTokens: number;
|
|
119
|
+
/** Per-model breakdown (e.g., { "claude-opus-4-6": { ... } }). */
|
|
120
|
+
modelUsage: Record<string, ModelTokenUsage>;
|
|
121
|
+
}
|
|
31
122
|
//# sourceMappingURL=config.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/types/config.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,SAAS;
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/types/config.ts"],"names":[],"mappings":"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"}
|
package/dist/types/config.js
CHANGED
|
@@ -1,7 +1,67 @@
|
|
|
1
|
-
export const
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
export const DEFAULT_CONFIG = {
|
|
2
|
+
models: {
|
|
3
|
+
plan: 'opus',
|
|
4
|
+
execute: 'opus',
|
|
5
|
+
nameGeneration: 'sonnet',
|
|
6
|
+
failureAnalysis: 'haiku',
|
|
7
|
+
prGeneration: 'sonnet',
|
|
8
|
+
config: 'sonnet',
|
|
9
|
+
},
|
|
10
|
+
effort: {
|
|
11
|
+
plan: 'high',
|
|
12
|
+
execute: 'medium',
|
|
13
|
+
nameGeneration: 'low',
|
|
14
|
+
failureAnalysis: 'low',
|
|
15
|
+
prGeneration: 'medium',
|
|
16
|
+
config: 'medium',
|
|
17
|
+
},
|
|
18
|
+
timeout: 60,
|
|
19
|
+
maxRetries: 3,
|
|
4
20
|
autoCommit: true,
|
|
21
|
+
worktree: false,
|
|
22
|
+
commitFormat: {
|
|
23
|
+
task: '{prefix}[{projectId}:{taskId}] {description}',
|
|
24
|
+
plan: '{prefix}[{projectId}] Plan: {projectName}',
|
|
25
|
+
amend: '{prefix}[{projectId}] Amend: {projectName}',
|
|
26
|
+
prefix: 'RAF',
|
|
27
|
+
},
|
|
5
28
|
claudeCommand: 'claude',
|
|
29
|
+
pricing: {
|
|
30
|
+
opus: {
|
|
31
|
+
inputPerMTok: 15,
|
|
32
|
+
outputPerMTok: 75,
|
|
33
|
+
cacheReadPerMTok: 1.5,
|
|
34
|
+
cacheCreatePerMTok: 18.75,
|
|
35
|
+
},
|
|
36
|
+
sonnet: {
|
|
37
|
+
inputPerMTok: 3,
|
|
38
|
+
outputPerMTok: 15,
|
|
39
|
+
cacheReadPerMTok: 0.3,
|
|
40
|
+
cacheCreatePerMTok: 3.75,
|
|
41
|
+
},
|
|
42
|
+
haiku: {
|
|
43
|
+
inputPerMTok: 1,
|
|
44
|
+
outputPerMTok: 5,
|
|
45
|
+
cacheReadPerMTok: 0.1,
|
|
46
|
+
cacheCreatePerMTok: 1.25,
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
};
|
|
50
|
+
export const VALID_MODEL_ALIASES = ['sonnet', 'haiku', 'opus'];
|
|
51
|
+
/**
|
|
52
|
+
* Regex for full Claude model IDs (e.g., `claude-sonnet-4-5-20250929`, `claude-opus-4-5-20251101`).
|
|
53
|
+
* Pattern: claude-{family}-{major}(-{minor})?(-{date})?
|
|
54
|
+
*/
|
|
55
|
+
export const FULL_MODEL_ID_PATTERN = /^claude-[a-z]+-\d+(-\d+)*$/;
|
|
56
|
+
/** @deprecated Use VALID_MODEL_ALIASES instead */
|
|
57
|
+
export const VALID_MODELS = VALID_MODEL_ALIASES;
|
|
58
|
+
export const VALID_EFFORTS = ['low', 'medium', 'high'];
|
|
59
|
+
// Keep backward-compat exports used by other modules
|
|
60
|
+
/** @deprecated Use DEFAULT_CONFIG instead */
|
|
61
|
+
export const DEFAULT_RAF_CONFIG = {
|
|
62
|
+
defaultTimeout: DEFAULT_CONFIG.timeout,
|
|
63
|
+
defaultMaxRetries: DEFAULT_CONFIG.maxRetries,
|
|
64
|
+
autoCommit: DEFAULT_CONFIG.autoCommit,
|
|
65
|
+
claudeCommand: DEFAULT_CONFIG.claudeCommand,
|
|
6
66
|
};
|
|
7
67
|
//# sourceMappingURL=config.js.map
|
package/dist/types/config.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/types/config.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/types/config.ts"],"names":[],"mappings":"AAqEA,MAAM,CAAC,MAAM,cAAc,GAAc;IACvC,MAAM,EAAE;QACN,IAAI,EAAE,MAAM;QACZ,OAAO,EAAE,MAAM;QACf,cAAc,EAAE,QAAQ;QACxB,eAAe,EAAE,OAAO;QACxB,YAAY,EAAE,QAAQ;QACtB,MAAM,EAAE,QAAQ;KACjB;IACD,MAAM,EAAE;QACN,IAAI,EAAE,MAAM;QACZ,OAAO,EAAE,QAAQ;QACjB,cAAc,EAAE,KAAK;QACrB,eAAe,EAAE,KAAK;QACtB,YAAY,EAAE,QAAQ;QACtB,MAAM,EAAE,QAAQ;KACjB;IACD,OAAO,EAAE,EAAE;IACX,UAAU,EAAE,CAAC;IACb,UAAU,EAAE,IAAI;IAChB,QAAQ,EAAE,KAAK;IACf,YAAY,EAAE;QACZ,IAAI,EAAE,8CAA8C;QACpD,IAAI,EAAE,2CAA2C;QACjD,KAAK,EAAE,4CAA4C;QACnD,MAAM,EAAE,KAAK;KACd;IACD,aAAa,EAAE,QAAQ;IACvB,OAAO,EAAE;QACP,IAAI,EAAE;YACJ,YAAY,EAAE,EAAE;YAChB,aAAa,EAAE,EAAE;YACjB,gBAAgB,EAAE,GAAG;YACrB,kBAAkB,EAAE,KAAK;SAC1B;QACD,MAAM,EAAE;YACN,YAAY,EAAE,CAAC;YACf,aAAa,EAAE,EAAE;YACjB,gBAAgB,EAAE,GAAG;YACrB,kBAAkB,EAAE,IAAI;SACzB;QACD,KAAK,EAAE;YACL,YAAY,EAAE,CAAC;YACf,aAAa,EAAE,CAAC;YAChB,gBAAgB,EAAE,GAAG;YACrB,kBAAkB,EAAE,IAAI;SACzB;KACF;CACF,CAAC;AASF,MAAM,CAAC,MAAM,mBAAmB,GAAgC,CAAC,QAAQ,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;AAE5F;;;GAGG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,4BAA4B,CAAC;AAElE,kDAAkD;AAClD,MAAM,CAAC,MAAM,YAAY,GAAG,mBAAmB,CAAC;AAChD,MAAM,CAAC,MAAM,aAAa,GAA2B,CAAC,KAAK,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC;AAE/E,qDAAqD;AACrD,6CAA6C;AAC7C,MAAM,CAAC,MAAM,kBAAkB,GAAG;IAChC,cAAc,EAAE,cAAc,CAAC,OAAO;IACtC,iBAAiB,EAAE,cAAc,CAAC,UAAU;IAC5C,UAAU,EAAE,cAAc,CAAC,UAAU;IACrC,aAAa,EAAE,cAAc,CAAC,aAAa;CAC5C,CAAC"}
|
package/dist/utils/config.d.ts
CHANGED
|
@@ -1,20 +1,78 @@
|
|
|
1
|
-
import { RafConfig } from '../types/config.js';
|
|
1
|
+
import { RafConfig, UserConfig, ClaudeModelName, EffortLevel, ModelScenario, EffortScenario, CommitFormatType, PricingCategory, ModelPricing, PricingConfig } from '../types/config.js';
|
|
2
|
+
export declare function getConfigPath(): string;
|
|
2
3
|
/**
|
|
3
4
|
* Get the path to Claude CLI settings file.
|
|
4
5
|
*/
|
|
5
6
|
export declare function getClaudeSettingsPath(): string;
|
|
6
|
-
export declare
|
|
7
|
-
|
|
7
|
+
export declare class ConfigValidationError extends Error {
|
|
8
|
+
constructor(message: string);
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Check whether a string is a valid model name — either a short alias or a full model ID.
|
|
12
|
+
*/
|
|
13
|
+
export declare function isValidModelName(value: string): boolean;
|
|
14
|
+
export declare function validateConfig(config: unknown): UserConfig;
|
|
15
|
+
/**
|
|
16
|
+
* Resolve the final config by loading from ~/.raf/raf.config.json and merging with defaults.
|
|
17
|
+
* Throws ConfigValidationError if the file contains invalid values.
|
|
18
|
+
*/
|
|
19
|
+
export declare function resolveConfig(configPath?: string): RafConfig;
|
|
20
|
+
/**
|
|
21
|
+
* @deprecated Use resolveConfig() instead. Kept for backward compatibility.
|
|
22
|
+
*/
|
|
23
|
+
export declare function loadConfig(_rafDir: string): {
|
|
24
|
+
defaultTimeout: number;
|
|
25
|
+
defaultMaxRetries: number;
|
|
26
|
+
autoCommit: boolean;
|
|
27
|
+
claudeCommand: string;
|
|
28
|
+
};
|
|
29
|
+
export declare function saveConfig(configPath: string, config: UserConfig): void;
|
|
30
|
+
/**
|
|
31
|
+
* Get the resolved config, caching the result for the process lifetime.
|
|
32
|
+
* Call resetConfigCache() in tests to clear.
|
|
33
|
+
*/
|
|
34
|
+
export declare function getResolvedConfig(): RafConfig;
|
|
35
|
+
export declare function resetConfigCache(): void;
|
|
36
|
+
export declare function getModel(scenario: ModelScenario): ClaudeModelName;
|
|
37
|
+
export declare function getEffort(scenario: EffortScenario): EffortLevel;
|
|
38
|
+
export declare function getCommitFormat(type: CommitFormatType): string;
|
|
39
|
+
export declare function getCommitPrefix(): string;
|
|
40
|
+
export declare function getTimeout(): number;
|
|
41
|
+
export declare function getMaxRetries(): number;
|
|
42
|
+
export declare function getAutoCommit(): boolean;
|
|
43
|
+
export declare function getWorktreeDefault(): boolean;
|
|
44
|
+
export declare function getClaudeCommand(): string;
|
|
45
|
+
/**
|
|
46
|
+
* Extract the short model alias (opus, sonnet, haiku) from a model ID.
|
|
47
|
+
* Works with both full model IDs (e.g., "claude-sonnet-4-5-20250929") and already-short names ("sonnet").
|
|
48
|
+
* Returns the original string if no known alias can be extracted.
|
|
49
|
+
*/
|
|
50
|
+
export declare function getModelShortName(modelId: string): string;
|
|
51
|
+
/**
|
|
52
|
+
* Map a full model ID (e.g., `claude-opus-4-6`) or short alias to a pricing category.
|
|
53
|
+
* Returns null if the model cannot be mapped.
|
|
54
|
+
*/
|
|
55
|
+
export declare function resolveModelPricingCategory(modelId: string): PricingCategory | null;
|
|
56
|
+
/**
|
|
57
|
+
* Get pricing config for a specific model category.
|
|
58
|
+
*/
|
|
59
|
+
export declare function getPricing(category: PricingCategory): ModelPricing;
|
|
60
|
+
/**
|
|
61
|
+
* Get the full pricing config.
|
|
62
|
+
*/
|
|
63
|
+
export declare function getPricingConfig(): PricingConfig;
|
|
64
|
+
/**
|
|
65
|
+
* Render a commit message template by replacing {placeholder} tokens with values.
|
|
66
|
+
* Unknown placeholders are left as-is.
|
|
67
|
+
*/
|
|
68
|
+
export declare function renderCommitMessage(template: string, variables: Record<string, string>): string;
|
|
8
69
|
export declare function getEditor(): string;
|
|
9
70
|
/**
|
|
10
71
|
* Get the Claude model name from Claude CLI settings.
|
|
11
|
-
* Returns the model name or null if not found.
|
|
12
|
-
* @param settingsPath Optional path to settings file (for testing)
|
|
13
72
|
*/
|
|
14
73
|
export declare function getClaudeModel(settingsPath?: string): string | null;
|
|
15
74
|
/**
|
|
16
|
-
*
|
|
17
|
-
* Returns default values which can be overridden by command line options.
|
|
75
|
+
* @deprecated Use getTimeout(), getMaxRetries(), getAutoCommit() instead.
|
|
18
76
|
*/
|
|
19
77
|
export declare function getConfig(): {
|
|
20
78
|
timeout: number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/utils/config.ts"],"names":[],"mappings":"AAGA,OAAO,
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/utils/config.ts"],"names":[],"mappings":"AAGA,OAAO,EACL,SAAS,EAGT,UAAU,EAIV,eAAe,EACf,WAAW,EACX,aAAa,EACb,cAAc,EACd,gBAAgB,EAChB,eAAe,EACf,YAAY,EACZ,aAAa,EACd,MAAM,oBAAoB,CAAC;AAK5B,wBAAgB,aAAa,IAAI,MAAM,CAEtC;AAED;;GAEG;AACH,wBAAgB,qBAAqB,IAAI,MAAM,CAE9C;AAsBD,qBAAa,qBAAsB,SAAQ,KAAK;gBAClC,OAAO,EAAE,MAAM;CAI5B;AAUD;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAEvD;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,OAAO,GAAG,UAAU,CA6G1D;AAkCD;;;GAGG;AACH,wBAAgB,aAAa,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAW5D;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG;IAAE,cAAc,EAAE,MAAM,CAAC;IAAC,iBAAiB,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,OAAO,CAAC;IAAC,aAAa,EAAE,MAAM,CAAA;CAAE,CAE7I;AAED,wBAAgB,UAAU,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,GAAG,IAAI,CAMvE;AAMD;;;GAGG;AACH,wBAAgB,iBAAiB,IAAI,SAAS,CAK7C;AAED,wBAAgB,gBAAgB,IAAI,IAAI,CAEvC;AAED,wBAAgB,QAAQ,CAAC,QAAQ,EAAE,aAAa,GAAG,eAAe,CAEjE;AAED,wBAAgB,SAAS,CAAC,QAAQ,EAAE,cAAc,GAAG,WAAW,CAE/D;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,gBAAgB,GAAG,MAAM,CAE9D;AAED,wBAAgB,eAAe,IAAI,MAAM,CAExC;AAED,wBAAgB,UAAU,IAAI,MAAM,CAEnC;AAED,wBAAgB,aAAa,IAAI,MAAM,CAEtC;AAED,wBAAgB,aAAa,IAAI,OAAO,CAEvC;AAED,wBAAgB,kBAAkB,IAAI,OAAO,CAE5C;AAED,wBAAgB,gBAAgB,IAAI,MAAM,CAEzC;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAezD;AAED;;;GAGG;AACH,wBAAgB,2BAA2B,CAAC,OAAO,EAAE,MAAM,GAAG,eAAe,GAAG,IAAI,CAcnF;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,QAAQ,EAAE,eAAe,GAAG,YAAY,CAElE;AAED;;GAEG;AACH,wBAAgB,gBAAgB,IAAI,aAAa,CAEhD;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,CAI/F;AAED,wBAAgB,SAAS,IAAI,MAAM,CAElC;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,YAAY,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAYnE;AAED;;GAEG;AACH,wBAAgB,SAAS,IAAI;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,OAAO,CAAA;CAAE,CAOxF"}
|
package/dist/utils/config.js
CHANGED
|
@@ -1,39 +1,315 @@
|
|
|
1
1
|
import * as fs from 'node:fs';
|
|
2
2
|
import * as path from 'node:path';
|
|
3
3
|
import * as os from 'node:os';
|
|
4
|
-
import { DEFAULT_RAF_CONFIG } from '../types/config.js';
|
|
4
|
+
import { DEFAULT_CONFIG, DEFAULT_RAF_CONFIG, VALID_MODEL_ALIASES, FULL_MODEL_ID_PATTERN, VALID_EFFORTS, } from '../types/config.js';
|
|
5
|
+
const CONFIG_DIR = path.join(os.homedir(), '.raf');
|
|
5
6
|
const CONFIG_FILENAME = 'raf.config.json';
|
|
7
|
+
export function getConfigPath() {
|
|
8
|
+
return path.join(CONFIG_DIR, CONFIG_FILENAME);
|
|
9
|
+
}
|
|
6
10
|
/**
|
|
7
11
|
* Get the path to Claude CLI settings file.
|
|
8
12
|
*/
|
|
9
13
|
export function getClaudeSettingsPath() {
|
|
10
14
|
return path.join(os.homedir(), '.claude', 'settings.json');
|
|
11
15
|
}
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
+
// ---- Validation ----
|
|
17
|
+
const VALID_TOP_LEVEL_KEYS = new Set([
|
|
18
|
+
'models', 'effort', 'timeout', 'maxRetries', 'autoCommit',
|
|
19
|
+
'worktree', 'commitFormat', 'claudeCommand', 'pricing',
|
|
20
|
+
]);
|
|
21
|
+
const VALID_PRICING_CATEGORIES = new Set(['opus', 'sonnet', 'haiku']);
|
|
22
|
+
const VALID_PRICING_FIELDS = new Set(['inputPerMTok', 'outputPerMTok', 'cacheReadPerMTok', 'cacheCreatePerMTok']);
|
|
23
|
+
const VALID_MODEL_KEYS = new Set([
|
|
24
|
+
'plan', 'execute', 'nameGeneration', 'failureAnalysis', 'prGeneration', 'config',
|
|
25
|
+
]);
|
|
26
|
+
const VALID_EFFORT_KEYS = new Set([
|
|
27
|
+
'plan', 'execute', 'nameGeneration', 'failureAnalysis', 'prGeneration', 'config',
|
|
28
|
+
]);
|
|
29
|
+
const VALID_COMMIT_FORMAT_KEYS = new Set(['task', 'plan', 'amend', 'prefix']);
|
|
30
|
+
export class ConfigValidationError extends Error {
|
|
31
|
+
constructor(message) {
|
|
32
|
+
super(message);
|
|
33
|
+
this.name = 'ConfigValidationError';
|
|
16
34
|
}
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
35
|
+
}
|
|
36
|
+
function checkUnknownKeys(obj, validKeys, prefix) {
|
|
37
|
+
for (const key of Object.keys(obj)) {
|
|
38
|
+
if (!validKeys.has(key)) {
|
|
39
|
+
throw new ConfigValidationError(`Unknown config key: ${prefix ? `${prefix}.` : ''}${key}`);
|
|
40
|
+
}
|
|
21
41
|
}
|
|
22
|
-
|
|
23
|
-
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Check whether a string is a valid model name — either a short alias or a full model ID.
|
|
45
|
+
*/
|
|
46
|
+
export function isValidModelName(value) {
|
|
47
|
+
return VALID_MODEL_ALIASES.includes(value) || FULL_MODEL_ID_PATTERN.test(value);
|
|
48
|
+
}
|
|
49
|
+
export function validateConfig(config) {
|
|
50
|
+
if (config === null || typeof config !== 'object' || Array.isArray(config)) {
|
|
51
|
+
throw new ConfigValidationError('Config must be a JSON object');
|
|
52
|
+
}
|
|
53
|
+
const obj = config;
|
|
54
|
+
checkUnknownKeys(obj, VALID_TOP_LEVEL_KEYS, '');
|
|
55
|
+
// models
|
|
56
|
+
if (obj.models !== undefined) {
|
|
57
|
+
if (typeof obj.models !== 'object' || obj.models === null || Array.isArray(obj.models)) {
|
|
58
|
+
throw new ConfigValidationError('models must be an object');
|
|
59
|
+
}
|
|
60
|
+
const models = obj.models;
|
|
61
|
+
checkUnknownKeys(models, VALID_MODEL_KEYS, 'models');
|
|
62
|
+
for (const [key, val] of Object.entries(models)) {
|
|
63
|
+
if (typeof val !== 'string' || !isValidModelName(val)) {
|
|
64
|
+
throw new ConfigValidationError(`models.${key} must be a short alias (${VALID_MODEL_ALIASES.join(', ')}) or a full model ID (e.g., claude-sonnet-4-5-20250929)`);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
// effort
|
|
69
|
+
if (obj.effort !== undefined) {
|
|
70
|
+
if (typeof obj.effort !== 'object' || obj.effort === null || Array.isArray(obj.effort)) {
|
|
71
|
+
throw new ConfigValidationError('effort must be an object');
|
|
72
|
+
}
|
|
73
|
+
const effort = obj.effort;
|
|
74
|
+
checkUnknownKeys(effort, VALID_EFFORT_KEYS, 'effort');
|
|
75
|
+
for (const [key, val] of Object.entries(effort)) {
|
|
76
|
+
if (typeof val !== 'string' || !VALID_EFFORTS.includes(val)) {
|
|
77
|
+
throw new ConfigValidationError(`effort.${key} must be one of: ${VALID_EFFORTS.join(', ')}`);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
// timeout
|
|
82
|
+
if (obj.timeout !== undefined) {
|
|
83
|
+
if (typeof obj.timeout !== 'number' || obj.timeout <= 0 || !Number.isFinite(obj.timeout)) {
|
|
84
|
+
throw new ConfigValidationError('timeout must be a positive number');
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
// maxRetries
|
|
88
|
+
if (obj.maxRetries !== undefined) {
|
|
89
|
+
if (typeof obj.maxRetries !== 'number' || obj.maxRetries < 0 || !Number.isInteger(obj.maxRetries)) {
|
|
90
|
+
throw new ConfigValidationError('maxRetries must be a non-negative integer');
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
// autoCommit
|
|
94
|
+
if (obj.autoCommit !== undefined) {
|
|
95
|
+
if (typeof obj.autoCommit !== 'boolean') {
|
|
96
|
+
throw new ConfigValidationError('autoCommit must be a boolean');
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
// worktree
|
|
100
|
+
if (obj.worktree !== undefined) {
|
|
101
|
+
if (typeof obj.worktree !== 'boolean') {
|
|
102
|
+
throw new ConfigValidationError('worktree must be a boolean');
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
// commitFormat
|
|
106
|
+
if (obj.commitFormat !== undefined) {
|
|
107
|
+
if (typeof obj.commitFormat !== 'object' || obj.commitFormat === null || Array.isArray(obj.commitFormat)) {
|
|
108
|
+
throw new ConfigValidationError('commitFormat must be an object');
|
|
109
|
+
}
|
|
110
|
+
const cf = obj.commitFormat;
|
|
111
|
+
checkUnknownKeys(cf, VALID_COMMIT_FORMAT_KEYS, 'commitFormat');
|
|
112
|
+
for (const [key, val] of Object.entries(cf)) {
|
|
113
|
+
if (typeof val !== 'string') {
|
|
114
|
+
throw new ConfigValidationError(`commitFormat.${key} must be a string`);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
// claudeCommand
|
|
119
|
+
if (obj.claudeCommand !== undefined) {
|
|
120
|
+
if (typeof obj.claudeCommand !== 'string' || obj.claudeCommand.trim() === '') {
|
|
121
|
+
throw new ConfigValidationError('claudeCommand must be a non-empty string');
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
// pricing
|
|
125
|
+
if (obj.pricing !== undefined) {
|
|
126
|
+
if (typeof obj.pricing !== 'object' || obj.pricing === null || Array.isArray(obj.pricing)) {
|
|
127
|
+
throw new ConfigValidationError('pricing must be an object');
|
|
128
|
+
}
|
|
129
|
+
const pricing = obj.pricing;
|
|
130
|
+
checkUnknownKeys(pricing, VALID_PRICING_CATEGORIES, 'pricing');
|
|
131
|
+
for (const [category, catVal] of Object.entries(pricing)) {
|
|
132
|
+
if (typeof catVal !== 'object' || catVal === null || Array.isArray(catVal)) {
|
|
133
|
+
throw new ConfigValidationError(`pricing.${category} must be an object`);
|
|
134
|
+
}
|
|
135
|
+
const fields = catVal;
|
|
136
|
+
checkUnknownKeys(fields, VALID_PRICING_FIELDS, `pricing.${category}`);
|
|
137
|
+
for (const [field, val] of Object.entries(fields)) {
|
|
138
|
+
if (typeof val !== 'number' || val < 0 || !Number.isFinite(val)) {
|
|
139
|
+
throw new ConfigValidationError(`pricing.${category}.${field} must be a non-negative number`);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
return config;
|
|
145
|
+
}
|
|
146
|
+
// ---- Deep merge ----
|
|
147
|
+
function deepMerge(defaults, overrides) {
|
|
148
|
+
const result = { ...defaults };
|
|
149
|
+
if (overrides.models) {
|
|
150
|
+
result.models = { ...defaults.models, ...overrides.models };
|
|
151
|
+
}
|
|
152
|
+
if (overrides.effort) {
|
|
153
|
+
result.effort = { ...defaults.effort, ...overrides.effort };
|
|
154
|
+
}
|
|
155
|
+
if (overrides.commitFormat) {
|
|
156
|
+
result.commitFormat = { ...defaults.commitFormat, ...overrides.commitFormat };
|
|
157
|
+
}
|
|
158
|
+
if (overrides.pricing) {
|
|
159
|
+
result.pricing = {
|
|
160
|
+
opus: { ...defaults.pricing.opus, ...overrides.pricing.opus },
|
|
161
|
+
sonnet: { ...defaults.pricing.sonnet, ...overrides.pricing.sonnet },
|
|
162
|
+
haiku: { ...defaults.pricing.haiku, ...overrides.pricing.haiku },
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
if (overrides.timeout !== undefined)
|
|
166
|
+
result.timeout = overrides.timeout;
|
|
167
|
+
if (overrides.maxRetries !== undefined)
|
|
168
|
+
result.maxRetries = overrides.maxRetries;
|
|
169
|
+
if (overrides.autoCommit !== undefined)
|
|
170
|
+
result.autoCommit = overrides.autoCommit;
|
|
171
|
+
if (overrides.worktree !== undefined)
|
|
172
|
+
result.worktree = overrides.worktree;
|
|
173
|
+
if (overrides.claudeCommand !== undefined)
|
|
174
|
+
result.claudeCommand = overrides.claudeCommand;
|
|
175
|
+
return result;
|
|
176
|
+
}
|
|
177
|
+
// ---- Config loading ----
|
|
178
|
+
/**
|
|
179
|
+
* Resolve the final config by loading from ~/.raf/raf.config.json and merging with defaults.
|
|
180
|
+
* Throws ConfigValidationError if the file contains invalid values.
|
|
181
|
+
*/
|
|
182
|
+
export function resolveConfig(configPath) {
|
|
183
|
+
const filePath = configPath ?? getConfigPath();
|
|
184
|
+
if (!fs.existsSync(filePath)) {
|
|
185
|
+
return { ...DEFAULT_CONFIG, models: { ...DEFAULT_CONFIG.models }, effort: { ...DEFAULT_CONFIG.effort }, commitFormat: { ...DEFAULT_CONFIG.commitFormat } };
|
|
24
186
|
}
|
|
187
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
188
|
+
const parsed = JSON.parse(content);
|
|
189
|
+
const validated = validateConfig(parsed);
|
|
190
|
+
return deepMerge(DEFAULT_CONFIG, validated);
|
|
25
191
|
}
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
192
|
+
/**
|
|
193
|
+
* @deprecated Use resolveConfig() instead. Kept for backward compatibility.
|
|
194
|
+
*/
|
|
195
|
+
export function loadConfig(_rafDir) {
|
|
196
|
+
return { ...DEFAULT_RAF_CONFIG };
|
|
197
|
+
}
|
|
198
|
+
export function saveConfig(configPath, config) {
|
|
199
|
+
const dir = path.dirname(configPath);
|
|
200
|
+
if (!fs.existsSync(dir)) {
|
|
201
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
202
|
+
}
|
|
203
|
+
fs.writeFileSync(configPath, JSON.stringify(config, null, 2) + '\n');
|
|
204
|
+
}
|
|
205
|
+
// ---- Helper accessors ----
|
|
206
|
+
let _cachedConfig = null;
|
|
207
|
+
/**
|
|
208
|
+
* Get the resolved config, caching the result for the process lifetime.
|
|
209
|
+
* Call resetConfigCache() in tests to clear.
|
|
210
|
+
*/
|
|
211
|
+
export function getResolvedConfig() {
|
|
212
|
+
if (!_cachedConfig) {
|
|
213
|
+
_cachedConfig = resolveConfig();
|
|
214
|
+
}
|
|
215
|
+
return _cachedConfig;
|
|
216
|
+
}
|
|
217
|
+
export function resetConfigCache() {
|
|
218
|
+
_cachedConfig = null;
|
|
219
|
+
}
|
|
220
|
+
export function getModel(scenario) {
|
|
221
|
+
return getResolvedConfig().models[scenario];
|
|
222
|
+
}
|
|
223
|
+
export function getEffort(scenario) {
|
|
224
|
+
return getResolvedConfig().effort[scenario];
|
|
225
|
+
}
|
|
226
|
+
export function getCommitFormat(type) {
|
|
227
|
+
return getResolvedConfig().commitFormat[type];
|
|
228
|
+
}
|
|
229
|
+
export function getCommitPrefix() {
|
|
230
|
+
return getResolvedConfig().commitFormat.prefix;
|
|
231
|
+
}
|
|
232
|
+
export function getTimeout() {
|
|
233
|
+
return getResolvedConfig().timeout;
|
|
234
|
+
}
|
|
235
|
+
export function getMaxRetries() {
|
|
236
|
+
return getResolvedConfig().maxRetries;
|
|
237
|
+
}
|
|
238
|
+
export function getAutoCommit() {
|
|
239
|
+
return getResolvedConfig().autoCommit;
|
|
240
|
+
}
|
|
241
|
+
export function getWorktreeDefault() {
|
|
242
|
+
return getResolvedConfig().worktree;
|
|
243
|
+
}
|
|
244
|
+
export function getClaudeCommand() {
|
|
245
|
+
return getResolvedConfig().claudeCommand;
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* Extract the short model alias (opus, sonnet, haiku) from a model ID.
|
|
249
|
+
* Works with both full model IDs (e.g., "claude-sonnet-4-5-20250929") and already-short names ("sonnet").
|
|
250
|
+
* Returns the original string if no known alias can be extracted.
|
|
251
|
+
*/
|
|
252
|
+
export function getModelShortName(modelId) {
|
|
253
|
+
// Already a short alias
|
|
254
|
+
if (modelId === 'opus' || modelId === 'sonnet' || modelId === 'haiku') {
|
|
255
|
+
return modelId;
|
|
256
|
+
}
|
|
257
|
+
// Extract family from full model ID: claude-{family}-{version}
|
|
258
|
+
const match = modelId.match(/^claude-([a-z]+)-/);
|
|
259
|
+
if (match) {
|
|
260
|
+
const family = match[1];
|
|
261
|
+
if (family === 'opus' || family === 'sonnet' || family === 'haiku') {
|
|
262
|
+
return family;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
// Unknown format, return as-is
|
|
266
|
+
return modelId;
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* Map a full model ID (e.g., `claude-opus-4-6`) or short alias to a pricing category.
|
|
270
|
+
* Returns null if the model cannot be mapped.
|
|
271
|
+
*/
|
|
272
|
+
export function resolveModelPricingCategory(modelId) {
|
|
273
|
+
// Short aliases map directly
|
|
274
|
+
if (modelId === 'opus' || modelId === 'sonnet' || modelId === 'haiku') {
|
|
275
|
+
return modelId;
|
|
276
|
+
}
|
|
277
|
+
// Full model IDs: extract family from `claude-{family}-{version}`
|
|
278
|
+
const match = modelId.match(/^claude-([a-z]+)-/);
|
|
279
|
+
if (match) {
|
|
280
|
+
const family = match[1];
|
|
281
|
+
if (family === 'opus' || family === 'sonnet' || family === 'haiku') {
|
|
282
|
+
return family;
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
return null;
|
|
286
|
+
}
|
|
287
|
+
/**
|
|
288
|
+
* Get pricing config for a specific model category.
|
|
289
|
+
*/
|
|
290
|
+
export function getPricing(category) {
|
|
291
|
+
return getResolvedConfig().pricing[category];
|
|
292
|
+
}
|
|
293
|
+
/**
|
|
294
|
+
* Get the full pricing config.
|
|
295
|
+
*/
|
|
296
|
+
export function getPricingConfig() {
|
|
297
|
+
return getResolvedConfig().pricing;
|
|
298
|
+
}
|
|
299
|
+
/**
|
|
300
|
+
* Render a commit message template by replacing {placeholder} tokens with values.
|
|
301
|
+
* Unknown placeholders are left as-is.
|
|
302
|
+
*/
|
|
303
|
+
export function renderCommitMessage(template, variables) {
|
|
304
|
+
return template.replace(/\{(\w+)\}/g, (match, key) => {
|
|
305
|
+
return variables[key] ?? match;
|
|
306
|
+
});
|
|
29
307
|
}
|
|
30
308
|
export function getEditor() {
|
|
31
309
|
return process.env['EDITOR'] ?? process.env['VISUAL'] ?? 'vi';
|
|
32
310
|
}
|
|
33
311
|
/**
|
|
34
312
|
* Get the Claude model name from Claude CLI settings.
|
|
35
|
-
* Returns the model name or null if not found.
|
|
36
|
-
* @param settingsPath Optional path to settings file (for testing)
|
|
37
313
|
*/
|
|
38
314
|
export function getClaudeModel(settingsPath) {
|
|
39
315
|
const filePath = settingsPath ?? getClaudeSettingsPath();
|
|
@@ -50,14 +326,14 @@ export function getClaudeModel(settingsPath) {
|
|
|
50
326
|
}
|
|
51
327
|
}
|
|
52
328
|
/**
|
|
53
|
-
*
|
|
54
|
-
* Returns default values which can be overridden by command line options.
|
|
329
|
+
* @deprecated Use getTimeout(), getMaxRetries(), getAutoCommit() instead.
|
|
55
330
|
*/
|
|
56
331
|
export function getConfig() {
|
|
332
|
+
const config = getResolvedConfig();
|
|
57
333
|
return {
|
|
58
|
-
timeout:
|
|
59
|
-
maxRetries:
|
|
60
|
-
autoCommit:
|
|
334
|
+
timeout: config.timeout,
|
|
335
|
+
maxRetries: config.maxRetries,
|
|
336
|
+
autoCommit: config.autoCommit,
|
|
61
337
|
};
|
|
62
338
|
}
|
|
63
339
|
//# sourceMappingURL=config.js.map
|