rafcode 3.0.0 → 3.8.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 +3 -1
- package/CLAUDE.md +0 -1
- package/RAF/38-dual-wielder/decisions.md +9 -0
- package/RAF/38-dual-wielder/input.md +6 -1
- package/RAF/38-dual-wielder/outcomes/8-e2e-test-codex-provider.md +139 -0
- package/RAF/38-dual-wielder/plans/8-e2e-test-codex-provider.md +95 -0
- package/RAF/39-pathless-rover/decisions.md +16 -0
- package/RAF/39-pathless-rover/input.md +2 -0
- package/RAF/39-pathless-rover/outcomes/1-fix-codex-stream-renderer.md +21 -0
- package/RAF/39-pathless-rover/outcomes/2-wire-provider-flag.md +28 -0
- package/RAF/39-pathless-rover/outcomes/3-remove-worktree-flag-do.md +41 -0
- package/RAF/39-pathless-rover/outcomes/4-remove-worktree-flag-plan-amend.md +30 -0
- package/RAF/39-pathless-rover/outcomes/5-update-prompts-and-docs.md +26 -0
- package/RAF/39-pathless-rover/plans/1-fix-codex-stream-renderer.md +43 -0
- package/RAF/39-pathless-rover/plans/2-wire-provider-flag.md +48 -0
- package/RAF/39-pathless-rover/plans/3-remove-worktree-flag-do.md +41 -0
- package/RAF/39-pathless-rover/plans/4-remove-worktree-flag-plan-amend.md +43 -0
- package/RAF/39-pathless-rover/plans/5-update-prompts-and-docs.md +31 -0
- package/RAF/40-numeric-order-fix/decisions.md +7 -0
- package/RAF/40-numeric-order-fix/input.md +19 -0
- package/RAF/40-numeric-order-fix/outcomes/1-fix-numeric-sort-order.md +18 -0
- package/RAF/40-numeric-order-fix/outcomes/2-add-npm-keywords.md +10 -0
- package/RAF/40-numeric-order-fix/plans/1-fix-numeric-sort-order.md +48 -0
- package/RAF/40-numeric-order-fix/plans/2-add-npm-keywords.md +23 -0
- package/RAF/41-echo-chamber/decisions.md +13 -0
- package/RAF/41-echo-chamber/input.md +4 -0
- package/RAF/41-echo-chamber/outcomes/1-update-codex-model-defaults.md +24 -0
- package/RAF/41-echo-chamber/outcomes/2-e2e-test-codex-provider.md +74 -0
- package/RAF/41-echo-chamber/plans/1-update-codex-model-defaults.md +28 -0
- package/RAF/41-echo-chamber/plans/2-e2e-test-codex-provider.md +103 -0
- package/RAF/42-patch-parade/decisions.md +29 -0
- package/RAF/42-patch-parade/input.md +9 -0
- package/RAF/42-patch-parade/outcomes/1-fix-codex-model-resolution.md +36 -0
- package/RAF/42-patch-parade/outcomes/2-fix-provider-aware-name-generation.md +31 -0
- package/RAF/42-patch-parade/outcomes/3-fix-codex-error-event-rendering.md +32 -0
- package/RAF/42-patch-parade/outcomes/4-update-cli-help-docs.md +28 -0
- package/RAF/42-patch-parade/outcomes/5-update-default-codex-models-to-gpt-5-4.md +33 -0
- package/RAF/42-patch-parade/outcomes/6-unify-model-config-schema.md +89 -0
- package/RAF/42-patch-parade/plans/1-fix-codex-model-resolution.md +35 -0
- package/RAF/42-patch-parade/plans/2-fix-provider-aware-name-generation.md +38 -0
- package/RAF/42-patch-parade/plans/3-fix-codex-error-event-rendering.md +32 -0
- package/RAF/42-patch-parade/plans/4-update-cli-help-docs.md +31 -0
- package/RAF/42-patch-parade/plans/5-update-default-codex-models-to-gpt-5-4.md +35 -0
- package/RAF/42-patch-parade/plans/6-unify-model-config-schema.md +46 -0
- package/RAF/43-swiss-army/decisions.md +34 -0
- package/RAF/43-swiss-army/input.md +7 -0
- package/RAF/43-swiss-army/outcomes/1-fix-model-validation.md +21 -0
- package/RAF/43-swiss-army/outcomes/2-update-commit-format.md +31 -0
- package/RAF/43-swiss-army/outcomes/3-wire-reasoning-effort.md +28 -0
- package/RAF/43-swiss-army/outcomes/4-remove-provider-flag.md +27 -0
- package/RAF/43-swiss-army/outcomes/5-config-wizard-validation.md +23 -0
- package/RAF/43-swiss-army/outcomes/6-add-fast-mode.md +32 -0
- package/RAF/43-swiss-army/outcomes/7-config-preset.md +31 -0
- package/RAF/43-swiss-army/plans/1-fix-model-validation.md +38 -0
- package/RAF/43-swiss-army/plans/2-update-commit-format.md +46 -0
- package/RAF/43-swiss-army/plans/3-wire-reasoning-effort.md +39 -0
- package/RAF/43-swiss-army/plans/4-remove-provider-flag.md +43 -0
- package/RAF/43-swiss-army/plans/5-config-wizard-validation.md +42 -0
- package/RAF/43-swiss-army/plans/6-add-fast-mode.md +46 -0
- package/RAF/43-swiss-army/plans/7-config-preset.md +51 -0
- package/RAF/44-config-api-change/decisions.md +22 -0
- package/RAF/44-config-api-change/input.md +5 -0
- package/RAF/44-config-api-change/outcomes/1-restructure-config-subcommands.md +19 -0
- package/RAF/44-config-api-change/outcomes/2-move-preset-under-config.md +17 -0
- package/RAF/44-config-api-change/outcomes/3-update-existing-tests-for-config-api.md +14 -0
- package/RAF/44-config-api-change/outcomes/4-update-config-command-docs.md +11 -0
- package/RAF/44-config-api-change/outcomes/5-fix-codex-name-generation.md +18 -0
- package/RAF/44-config-api-change/plans/1-restructure-config-subcommands.md +37 -0
- package/RAF/44-config-api-change/plans/2-move-preset-under-config.md +38 -0
- package/RAF/44-config-api-change/plans/3-update-existing-tests-for-config-api.md +38 -0
- package/RAF/44-config-api-change/plans/4-update-config-command-docs.md +36 -0
- package/RAF/44-config-api-change/plans/5-fix-codex-name-generation.md +49 -0
- package/RAF/45-signal-cairn/decisions.md +7 -0
- package/RAF/45-signal-cairn/input.md +2 -0
- package/RAF/45-signal-cairn/outcomes/1-rename-provider-to-harness.md +19 -0
- package/RAF/45-signal-cairn/outcomes/2-normalize-model-display-names.md +18 -0
- package/RAF/45-signal-cairn/plans/1-rename-provider-to-harness.md +40 -0
- package/RAF/45-signal-cairn/plans/2-normalize-model-display-names.md +41 -0
- package/RAF/45-signal-lantern/decisions.md +10 -0
- package/RAF/45-signal-lantern/input.md +2 -0
- package/RAF/45-signal-lantern/outcomes/1-add-effort-and-fast-to-do-model-display.md +15 -0
- package/RAF/45-signal-lantern/outcomes/2-capture-codex-post-run-token-usage.md +15 -0
- package/RAF/45-signal-lantern/outcomes/3-show-codex-token-summaries-without-fake-cost.md +14 -0
- package/RAF/45-signal-lantern/plans/1-add-effort-and-fast-to-do-model-display.md +38 -0
- package/RAF/45-signal-lantern/plans/2-capture-codex-post-run-token-usage.md +37 -0
- package/RAF/45-signal-lantern/plans/3-show-codex-token-summaries-without-fake-cost.md +40 -0
- package/RAF/46-lantern-arc/decisions.md +19 -0
- package/RAF/46-lantern-arc/input.md +6 -0
- package/RAF/46-lantern-arc/outcomes/1-remove-spark-alias.md +16 -0
- package/RAF/46-lantern-arc/outcomes/2-clean-up-worktree-plan-command.md +30 -0
- package/RAF/46-lantern-arc/outcomes/3-fix-token-usage-accumulation.md +32 -0
- package/RAF/46-lantern-arc/outcomes/4-display-effort-in-compact-mode.md +22 -0
- package/RAF/46-lantern-arc/outcomes/5-codex-fast-mode-research.md +38 -0
- package/RAF/46-lantern-arc/outcomes/6-optimize-llm-prompts.md +39 -0
- package/RAF/46-lantern-arc/plans/1-remove-spark-alias.md +38 -0
- package/RAF/46-lantern-arc/plans/2-clean-up-worktree-plan-command.md +33 -0
- package/RAF/46-lantern-arc/plans/3-fix-token-usage-accumulation.md +33 -0
- package/RAF/46-lantern-arc/plans/4-display-effort-in-compact-mode.md +28 -0
- package/RAF/46-lantern-arc/plans/5-codex-fast-mode-research.md +34 -0
- package/RAF/46-lantern-arc/plans/6-optimize-llm-prompts.md +48 -0
- package/RAF/47-signal-trim/decisions.md +13 -0
- package/RAF/47-signal-trim/input.md +2 -0
- package/RAF/47-signal-trim/plans/1-remove-cache-from-status.md +73 -0
- package/README.md +50 -63
- package/dist/commands/config.d.ts.map +1 -1
- package/dist/commands/config.js +47 -49
- package/dist/commands/config.js.map +1 -1
- package/dist/commands/do.d.ts +2 -0
- package/dist/commands/do.d.ts.map +1 -1
- package/dist/commands/do.js +91 -230
- package/dist/commands/do.js.map +1 -1
- package/dist/commands/plan.d.ts.map +1 -1
- package/dist/commands/plan.js +54 -259
- package/dist/commands/plan.js.map +1 -1
- package/dist/commands/preset.d.ts +3 -0
- package/dist/commands/preset.d.ts.map +1 -0
- package/dist/commands/preset.js +158 -0
- package/dist/commands/preset.js.map +1 -0
- package/dist/core/claude-runner.d.ts +2 -0
- package/dist/core/claude-runner.d.ts.map +1 -1
- package/dist/core/claude-runner.js +36 -12
- package/dist/core/claude-runner.js.map +1 -1
- package/dist/core/codex-runner.d.ts +1 -0
- package/dist/core/codex-runner.d.ts.map +1 -1
- package/dist/core/codex-runner.js +26 -7
- package/dist/core/codex-runner.js.map +1 -1
- package/dist/core/failure-analyzer.js +2 -1
- package/dist/core/failure-analyzer.js.map +1 -1
- package/dist/core/git.d.ts +2 -2
- package/dist/core/git.d.ts.map +1 -1
- package/dist/core/git.js +53 -3
- package/dist/core/git.js.map +1 -1
- package/dist/core/project-manager.d.ts.map +1 -1
- package/dist/core/project-manager.js +2 -2
- package/dist/core/project-manager.js.map +1 -1
- package/dist/core/pull-request.js +5 -5
- package/dist/core/pull-request.js.map +1 -1
- package/dist/core/runner-factory.d.ts +4 -4
- package/dist/core/runner-factory.d.ts.map +1 -1
- package/dist/core/runner-factory.js +8 -8
- package/dist/core/runner-factory.js.map +1 -1
- package/dist/core/runner-interface.d.ts +1 -1
- package/dist/core/runner-types.d.ts +17 -4
- package/dist/core/runner-types.d.ts.map +1 -1
- package/dist/core/state-derivation.js +3 -3
- package/dist/core/state-derivation.js.map +1 -1
- package/dist/parsers/codex-stream-renderer.d.ts +28 -4
- package/dist/parsers/codex-stream-renderer.d.ts.map +1 -1
- package/dist/parsers/codex-stream-renderer.js +110 -0
- package/dist/parsers/codex-stream-renderer.js.map +1 -1
- package/dist/prompts/amend.d.ts +0 -1
- package/dist/prompts/amend.d.ts.map +1 -1
- package/dist/prompts/amend.js +31 -104
- package/dist/prompts/amend.js.map +1 -1
- package/dist/prompts/execution.d.ts.map +1 -1
- package/dist/prompts/execution.js +17 -34
- package/dist/prompts/execution.js.map +1 -1
- package/dist/prompts/planning.d.ts.map +1 -1
- package/dist/prompts/planning.js +23 -123
- package/dist/prompts/planning.js.map +1 -1
- package/dist/types/config.d.ts +33 -32
- package/dist/types/config.d.ts.map +1 -1
- package/dist/types/config.js +14 -28
- package/dist/types/config.js.map +1 -1
- package/dist/utils/config.d.ts +36 -16
- package/dist/utils/config.d.ts.map +1 -1
- package/dist/utils/config.js +209 -104
- package/dist/utils/config.js.map +1 -1
- package/dist/utils/name-generator.d.ts.map +1 -1
- package/dist/utils/name-generator.js +25 -12
- package/dist/utils/name-generator.js.map +1 -1
- package/dist/utils/paths.d.ts +5 -0
- package/dist/utils/paths.d.ts.map +1 -1
- package/dist/utils/paths.js +9 -0
- package/dist/utils/paths.js.map +1 -1
- package/dist/utils/terminal-symbols.d.ts +15 -2
- package/dist/utils/terminal-symbols.d.ts.map +1 -1
- package/dist/utils/terminal-symbols.js +36 -4
- package/dist/utils/terminal-symbols.js.map +1 -1
- package/dist/utils/token-tracker.d.ts +6 -1
- package/dist/utils/token-tracker.d.ts.map +1 -1
- package/dist/utils/token-tracker.js +84 -51
- package/dist/utils/token-tracker.js.map +1 -1
- package/dist/utils/validation.d.ts +1 -2
- package/dist/utils/validation.d.ts.map +1 -1
- package/dist/utils/validation.js +4 -25
- package/dist/utils/validation.js.map +1 -1
- package/package.json +7 -2
- package/src/commands/config.ts +60 -63
- package/src/commands/do.ts +96 -262
- package/src/commands/plan.ts +55 -279
- package/src/commands/preset.ts +186 -0
- package/src/core/claude-runner.ts +45 -5
- package/src/core/codex-runner.ts +32 -7
- package/src/core/failure-analyzer.ts +2 -1
- package/src/core/git.ts +57 -3
- package/src/core/project-manager.ts +2 -1
- package/src/core/pull-request.ts +5 -5
- package/src/core/runner-factory.ts +9 -9
- package/src/core/runner-interface.ts +1 -1
- package/src/core/runner-types.ts +17 -4
- package/src/core/state-derivation.ts +3 -3
- package/src/parsers/codex-stream-renderer.ts +149 -4
- package/src/prompts/amend.ts +30 -105
- package/src/prompts/config-docs.md +206 -62
- package/src/prompts/execution.ts +17 -34
- package/src/prompts/planning.ts +23 -124
- package/src/types/config.ts +47 -59
- package/src/utils/config.ts +248 -115
- package/src/utils/name-generator.ts +29 -13
- package/src/utils/paths.ts +10 -0
- package/src/utils/terminal-symbols.ts +46 -6
- package/src/utils/token-tracker.ts +96 -57
- package/src/utils/validation.ts +5 -30
- package/tests/unit/amend-prompt.test.ts +3 -2
- package/tests/unit/claude-runner-interactive.test.ts +21 -3
- package/tests/unit/claude-runner.test.ts +39 -0
- package/tests/unit/codex-runner.test.ts +163 -0
- package/tests/unit/codex-stream-renderer.test.ts +127 -0
- package/tests/unit/command-output.test.ts +57 -0
- package/tests/unit/commit-planning-artifacts-worktree.test.ts +24 -7
- package/tests/unit/commit-planning-artifacts.test.ts +26 -4
- package/tests/unit/config-command.test.ts +215 -303
- package/tests/unit/config.test.ts +319 -235
- package/tests/unit/dependency-integration.test.ts +27 -1
- package/tests/unit/do-model-display.test.ts +35 -0
- package/tests/unit/execution-prompt.test.ts +49 -19
- package/tests/unit/name-generator.test.ts +82 -12
- package/tests/unit/plan-command-auto-flag.test.ts +7 -10
- package/tests/unit/plan-command.test.ts +14 -17
- package/tests/unit/planning-prompt.test.ts +9 -8
- package/tests/unit/terminal-symbols.test.ts +94 -3
- package/tests/unit/token-tracker.test.ts +180 -1
- package/tests/unit/validation.test.ts +9 -41
- package/tests/unit/worktree-flag-override.test.ts +0 -186
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import * as fs from 'node:fs';
|
|
2
|
+
import * as path from 'node:path';
|
|
3
|
+
import * as os from 'node:os';
|
|
4
|
+
import { Command } from 'commander';
|
|
5
|
+
import { logger } from '../utils/logger.js';
|
|
6
|
+
import {
|
|
7
|
+
getConfigPath,
|
|
8
|
+
validateConfig,
|
|
9
|
+
ConfigValidationError,
|
|
10
|
+
} from '../utils/config.js';
|
|
11
|
+
|
|
12
|
+
const PRESETS_DIR = path.join(os.homedir(), '.raf', 'presets');
|
|
13
|
+
|
|
14
|
+
function ensurePresetsDir(): void {
|
|
15
|
+
if (!fs.existsSync(PRESETS_DIR)) {
|
|
16
|
+
fs.mkdirSync(PRESETS_DIR, { recursive: true });
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function getPresetPath(name: string): string {
|
|
21
|
+
return path.join(PRESETS_DIR, `${name}.json`);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function validatePresetName(name: string): void {
|
|
25
|
+
if (!/^[a-zA-Z0-9_-]+$/.test(name)) {
|
|
26
|
+
logger.error('Preset name must contain only letters, numbers, hyphens, and underscores.');
|
|
27
|
+
process.exit(1);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function savePreset(name: string): void {
|
|
32
|
+
validatePresetName(name);
|
|
33
|
+
ensurePresetsDir();
|
|
34
|
+
|
|
35
|
+
const configPath = getConfigPath();
|
|
36
|
+
if (!fs.existsSync(configPath)) {
|
|
37
|
+
logger.error('No config file found. Run `raf config wizard` or `raf config set <key> <value>` to create one first.');
|
|
38
|
+
process.exit(1);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const presetPath = getPresetPath(name);
|
|
42
|
+
const configContent = fs.readFileSync(configPath, 'utf-8');
|
|
43
|
+
|
|
44
|
+
// Validate the config before saving
|
|
45
|
+
try {
|
|
46
|
+
const parsed = JSON.parse(configContent);
|
|
47
|
+
validateConfig(parsed);
|
|
48
|
+
} catch (err) {
|
|
49
|
+
if (err instanceof ConfigValidationError) {
|
|
50
|
+
logger.error(`Current config is invalid: ${err.message}`);
|
|
51
|
+
process.exit(1);
|
|
52
|
+
}
|
|
53
|
+
throw err;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const existed = fs.existsSync(presetPath);
|
|
57
|
+
fs.writeFileSync(presetPath, configContent);
|
|
58
|
+
logger.info(`${existed ? 'Updated' : 'Saved'} preset "${name}" at ${presetPath}`);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function loadPreset(name: string): void {
|
|
62
|
+
validatePresetName(name);
|
|
63
|
+
const presetPath = getPresetPath(name);
|
|
64
|
+
|
|
65
|
+
if (!fs.existsSync(presetPath)) {
|
|
66
|
+
logger.error(`Preset "${name}" not found. Run \`raf config preset list\` to see available presets.`);
|
|
67
|
+
process.exit(1);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const content = fs.readFileSync(presetPath, 'utf-8');
|
|
71
|
+
|
|
72
|
+
// Validate preset before applying
|
|
73
|
+
let parsed: unknown;
|
|
74
|
+
try {
|
|
75
|
+
parsed = JSON.parse(content);
|
|
76
|
+
} catch {
|
|
77
|
+
logger.error(`Preset "${name}" contains invalid JSON.`);
|
|
78
|
+
process.exit(1);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
try {
|
|
82
|
+
validateConfig(parsed);
|
|
83
|
+
} catch (err) {
|
|
84
|
+
if (err instanceof ConfigValidationError) {
|
|
85
|
+
logger.error(`Preset "${name}" is invalid: ${err.message}`);
|
|
86
|
+
process.exit(1);
|
|
87
|
+
}
|
|
88
|
+
throw err;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const configPath = getConfigPath();
|
|
92
|
+
const configDir = path.dirname(configPath);
|
|
93
|
+
if (!fs.existsSync(configDir)) {
|
|
94
|
+
fs.mkdirSync(configDir, { recursive: true });
|
|
95
|
+
}
|
|
96
|
+
fs.writeFileSync(configPath, content);
|
|
97
|
+
logger.info(`Loaded preset "${name}" into ${configPath}`);
|
|
98
|
+
|
|
99
|
+
// Show a brief summary of what was loaded
|
|
100
|
+
const config = parsed as Record<string, unknown>;
|
|
101
|
+
const keys = Object.keys(config);
|
|
102
|
+
if (keys.length > 0) {
|
|
103
|
+
logger.info(` Keys: ${keys.join(', ')}`);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function listPresets(): void {
|
|
108
|
+
ensurePresetsDir();
|
|
109
|
+
|
|
110
|
+
const files = fs.readdirSync(PRESETS_DIR).filter(f => f.endsWith('.json'));
|
|
111
|
+
if (files.length === 0) {
|
|
112
|
+
logger.info('No presets saved. Use `raf config preset save <name>` to create one.');
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
logger.info(`Found ${files.length} preset(s):\n`);
|
|
117
|
+
for (const file of files) {
|
|
118
|
+
const name = path.basename(file, '.json');
|
|
119
|
+
const filePath = path.join(PRESETS_DIR, file);
|
|
120
|
+
try {
|
|
121
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
122
|
+
const config = JSON.parse(content) as Record<string, unknown>;
|
|
123
|
+
|
|
124
|
+
// Build a brief summary from models
|
|
125
|
+
const summary: string[] = [];
|
|
126
|
+
if (config.models && typeof config.models === 'object') {
|
|
127
|
+
const models = config.models as Record<string, { model?: string; harness?: string }>;
|
|
128
|
+
const harnesses = new Set<string>();
|
|
129
|
+
for (const entry of Object.values(models)) {
|
|
130
|
+
if (entry?.harness) harnesses.add(entry.harness);
|
|
131
|
+
}
|
|
132
|
+
if (harnesses.size > 0) {
|
|
133
|
+
summary.push(`harnesses: ${[...harnesses].join(', ')}`);
|
|
134
|
+
}
|
|
135
|
+
if (models.execute?.model) {
|
|
136
|
+
summary.push(`execute: ${models.execute.model}`);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const info = summary.length > 0 ? ` (${summary.join('; ')})` : '';
|
|
141
|
+
logger.info(` ${name}${info}`);
|
|
142
|
+
} catch {
|
|
143
|
+
logger.info(` ${name} (invalid JSON)`);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function deletePreset(name: string): void {
|
|
149
|
+
validatePresetName(name);
|
|
150
|
+
const presetPath = getPresetPath(name);
|
|
151
|
+
|
|
152
|
+
if (!fs.existsSync(presetPath)) {
|
|
153
|
+
logger.error(`Preset "${name}" not found.`);
|
|
154
|
+
process.exit(1);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
fs.unlinkSync(presetPath);
|
|
158
|
+
logger.info(`Deleted preset "${name}".`);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
export function createPresetCommand(): Command {
|
|
162
|
+
const preset = new Command('preset')
|
|
163
|
+
.description('Manage named config presets under `raf config preset`');
|
|
164
|
+
|
|
165
|
+
preset
|
|
166
|
+
.command('save <name>')
|
|
167
|
+
.description('Save current config as a named preset')
|
|
168
|
+
.action((name: string) => savePreset(name));
|
|
169
|
+
|
|
170
|
+
preset
|
|
171
|
+
.command('load <name>')
|
|
172
|
+
.description('Load a preset into the active config (overwrites current config)')
|
|
173
|
+
.action((name: string) => loadPreset(name));
|
|
174
|
+
|
|
175
|
+
preset
|
|
176
|
+
.command('list')
|
|
177
|
+
.description('List all saved presets')
|
|
178
|
+
.action(() => listPresets());
|
|
179
|
+
|
|
180
|
+
preset
|
|
181
|
+
.command('delete <name>')
|
|
182
|
+
.description('Delete a saved preset')
|
|
183
|
+
.action((name: string) => deletePreset(name));
|
|
184
|
+
|
|
185
|
+
return preset;
|
|
186
|
+
}
|
|
@@ -4,6 +4,7 @@ import { execSync, spawn } from 'node:child_process';
|
|
|
4
4
|
import { logger } from '../utils/logger.js';
|
|
5
5
|
import { renderStreamEvent } from '../parsers/stream-renderer.js';
|
|
6
6
|
import { getModel } from '../utils/config.js';
|
|
7
|
+
import { mergeUsageData } from '../utils/token-tracker.js';
|
|
7
8
|
import type { ICliRunner } from './runner-interface.js';
|
|
8
9
|
import type { RunnerOptions, RunnerConfig, RunResult } from './runner-types.js';
|
|
9
10
|
import { createCompletionDetector } from './completion-detector.js';
|
|
@@ -32,9 +33,13 @@ export class ClaudeRunner implements ICliRunner {
|
|
|
32
33
|
private activeProcess: pty.IPty | null = null;
|
|
33
34
|
private killed = false;
|
|
34
35
|
private model: string;
|
|
36
|
+
private reasoningEffort?: string;
|
|
37
|
+
private fast?: boolean;
|
|
35
38
|
|
|
36
39
|
constructor(config: RunnerConfig = {}) {
|
|
37
|
-
this.model = config.model ?? getModel('execute');
|
|
40
|
+
this.model = config.model ?? getModel('execute').model;
|
|
41
|
+
this.reasoningEffort = config.reasoningEffort;
|
|
42
|
+
this.fast = config.fast;
|
|
38
43
|
}
|
|
39
44
|
|
|
40
45
|
/**
|
|
@@ -55,6 +60,16 @@ export class ClaudeRunner implements ICliRunner {
|
|
|
55
60
|
return new Promise((resolve) => {
|
|
56
61
|
const args = ['--model', this.model];
|
|
57
62
|
|
|
63
|
+
// Add reasoning effort flag when configured
|
|
64
|
+
if (this.reasoningEffort) {
|
|
65
|
+
args.push('--effort', this.reasoningEffort);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Add fast mode when configured
|
|
69
|
+
if (this.fast) {
|
|
70
|
+
args.push('--settings', '{"fastMode": true}');
|
|
71
|
+
}
|
|
72
|
+
|
|
58
73
|
// Add --dangerously-skip-permissions if requested (for --auto mode)
|
|
59
74
|
if (dangerouslySkipPermissions) {
|
|
60
75
|
args.push('--dangerously-skip-permissions');
|
|
@@ -147,6 +162,16 @@ export class ClaudeRunner implements ICliRunner {
|
|
|
147
162
|
return new Promise((resolve) => {
|
|
148
163
|
const args = ['--resume', '--model', this.model];
|
|
149
164
|
|
|
165
|
+
// Add reasoning effort flag when configured
|
|
166
|
+
if (this.reasoningEffort) {
|
|
167
|
+
args.push('--effort', this.reasoningEffort);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// Add fast mode when configured
|
|
171
|
+
if (this.fast) {
|
|
172
|
+
args.push('--settings', '{"fastMode": true}');
|
|
173
|
+
}
|
|
174
|
+
|
|
150
175
|
logger.debug(`Starting session resume picker with model: ${this.model}`);
|
|
151
176
|
|
|
152
177
|
this.activeProcess = pty.spawn(getClaudePath(), args, {
|
|
@@ -272,10 +297,23 @@ export class ClaudeRunner implements ICliRunner {
|
|
|
272
297
|
// including tool calls, file operations, and token usage in the result event.
|
|
273
298
|
// --dangerously-skip-permissions bypasses interactive prompts
|
|
274
299
|
// -p enables print mode (non-interactive)
|
|
275
|
-
const
|
|
300
|
+
const execArgs = [
|
|
276
301
|
'--dangerously-skip-permissions',
|
|
277
302
|
'--model',
|
|
278
303
|
this.model,
|
|
304
|
+
];
|
|
305
|
+
|
|
306
|
+
// Add reasoning effort flag when configured
|
|
307
|
+
if (this.reasoningEffort) {
|
|
308
|
+
execArgs.push('--effort', this.reasoningEffort);
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
// Add fast mode when configured
|
|
312
|
+
if (this.fast) {
|
|
313
|
+
execArgs.push('--settings', '{"fastMode": true}');
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
execArgs.push(
|
|
279
317
|
'--append-system-prompt',
|
|
280
318
|
prompt,
|
|
281
319
|
'--output-format',
|
|
@@ -283,7 +321,9 @@ export class ClaudeRunner implements ICliRunner {
|
|
|
283
321
|
'--verbose',
|
|
284
322
|
'-p',
|
|
285
323
|
'Execute the task as described in the system prompt.',
|
|
286
|
-
|
|
324
|
+
);
|
|
325
|
+
|
|
326
|
+
const proc = spawn(claudePath, execArgs, {
|
|
287
327
|
cwd,
|
|
288
328
|
env: process.env,
|
|
289
329
|
stdio: ['ignore', 'pipe', 'pipe'], // no stdin needed
|
|
@@ -346,7 +386,7 @@ export class ClaudeRunner implements ICliRunner {
|
|
|
346
386
|
|
|
347
387
|
// Capture usage data from result events
|
|
348
388
|
if (rendered.usageData) {
|
|
349
|
-
usageData = rendered.usageData;
|
|
389
|
+
usageData = mergeUsageData(usageData, rendered.usageData);
|
|
350
390
|
}
|
|
351
391
|
|
|
352
392
|
if (shouldDisplay() && rendered.display) {
|
|
@@ -368,7 +408,7 @@ export class ClaudeRunner implements ICliRunner {
|
|
|
368
408
|
output += rendered.textContent;
|
|
369
409
|
}
|
|
370
410
|
if (rendered.usageData) {
|
|
371
|
-
usageData = rendered.usageData;
|
|
411
|
+
usageData = mergeUsageData(usageData, rendered.usageData);
|
|
372
412
|
}
|
|
373
413
|
if (shouldDisplay() && rendered.display) {
|
|
374
414
|
process.stdout.write(rendered.display);
|
package/src/core/codex-runner.ts
CHANGED
|
@@ -4,6 +4,7 @@ import { execSync, spawn } from 'node:child_process';
|
|
|
4
4
|
import { logger } from '../utils/logger.js';
|
|
5
5
|
import { renderCodexStreamEvent } from '../parsers/codex-stream-renderer.js';
|
|
6
6
|
import { getModel } from '../utils/config.js';
|
|
7
|
+
import { mergeUsageData } from '../utils/token-tracker.js';
|
|
7
8
|
import type { ICliRunner } from './runner-interface.js';
|
|
8
9
|
import type { RunnerOptions, RunnerConfig, RunResult } from './runner-types.js';
|
|
9
10
|
import { createCompletionDetector } from './completion-detector.js';
|
|
@@ -36,9 +37,11 @@ export class CodexRunner implements ICliRunner {
|
|
|
36
37
|
private activeProcess: pty.IPty | null = null;
|
|
37
38
|
private killed = false;
|
|
38
39
|
private model: string;
|
|
40
|
+
private reasoningEffort?: string;
|
|
39
41
|
|
|
40
42
|
constructor(config: RunnerConfig = {}) {
|
|
41
|
-
this.model = config.model ?? getModel('execute'
|
|
43
|
+
this.model = config.model ?? getModel('execute').model;
|
|
44
|
+
this.reasoningEffort = config.reasoningEffort;
|
|
42
45
|
}
|
|
43
46
|
|
|
44
47
|
/**
|
|
@@ -54,7 +57,14 @@ export class CodexRunner implements ICliRunner {
|
|
|
54
57
|
|
|
55
58
|
return new Promise((resolve) => {
|
|
56
59
|
const combinedPrompt = buildCombinedPrompt(systemPrompt, userMessage);
|
|
57
|
-
const args = ['-m', this.model
|
|
60
|
+
const args = ['-m', this.model];
|
|
61
|
+
|
|
62
|
+
// Add reasoning effort via config override when configured
|
|
63
|
+
if (this.reasoningEffort) {
|
|
64
|
+
args.push('-c', `model_reasoning_effort="${this.reasoningEffort}"`);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
args.push(combinedPrompt);
|
|
58
68
|
|
|
59
69
|
logger.debug(`Starting interactive Codex session with model: ${this.model}`);
|
|
60
70
|
|
|
@@ -167,6 +177,7 @@ export class CodexRunner implements ICliRunner {
|
|
|
167
177
|
let stderr = '';
|
|
168
178
|
let timedOut = false;
|
|
169
179
|
let contextOverflow = false;
|
|
180
|
+
let usageData: import('../types/config.js').UsageData | undefined;
|
|
170
181
|
|
|
171
182
|
const codexPath = getCodexPath();
|
|
172
183
|
|
|
@@ -175,15 +186,23 @@ export class CodexRunner implements ICliRunner {
|
|
|
175
186
|
logger.debug(`Codex path: ${codexPath}`);
|
|
176
187
|
|
|
177
188
|
logger.debug('Spawning process...');
|
|
178
|
-
const
|
|
189
|
+
const execArgs = [
|
|
179
190
|
'exec',
|
|
180
191
|
'--full-auto',
|
|
181
192
|
'--json',
|
|
182
193
|
'--ephemeral',
|
|
183
194
|
'-m',
|
|
184
195
|
this.model,
|
|
185
|
-
|
|
186
|
-
|
|
196
|
+
];
|
|
197
|
+
|
|
198
|
+
// Add reasoning effort via config override when configured
|
|
199
|
+
if (this.reasoningEffort) {
|
|
200
|
+
execArgs.push('-c', `model_reasoning_effort="${this.reasoningEffort}"`);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
execArgs.push(prompt);
|
|
204
|
+
|
|
205
|
+
const proc = spawn(codexPath, execArgs, {
|
|
187
206
|
cwd,
|
|
188
207
|
env: process.env,
|
|
189
208
|
stdio: ['ignore', 'pipe', 'pipe'],
|
|
@@ -244,6 +263,10 @@ export class CodexRunner implements ICliRunner {
|
|
|
244
263
|
}
|
|
245
264
|
}
|
|
246
265
|
|
|
266
|
+
if (rendered.usageData) {
|
|
267
|
+
usageData = mergeUsageData(usageData, rendered.usageData);
|
|
268
|
+
}
|
|
269
|
+
|
|
247
270
|
if (shouldDisplay() && rendered.display) {
|
|
248
271
|
process.stdout.write(rendered.display);
|
|
249
272
|
}
|
|
@@ -262,6 +285,9 @@ export class CodexRunner implements ICliRunner {
|
|
|
262
285
|
if (rendered.textContent) {
|
|
263
286
|
output += rendered.textContent;
|
|
264
287
|
}
|
|
288
|
+
if (rendered.usageData) {
|
|
289
|
+
usageData = mergeUsageData(usageData, rendered.usageData);
|
|
290
|
+
}
|
|
265
291
|
if (shouldDisplay() && rendered.display) {
|
|
266
292
|
process.stdout.write(rendered.display);
|
|
267
293
|
}
|
|
@@ -281,8 +307,7 @@ export class CodexRunner implements ICliRunner {
|
|
|
281
307
|
exitCode: exitCode ?? (this.killed ? 130 : 1),
|
|
282
308
|
timedOut,
|
|
283
309
|
contextOverflow,
|
|
284
|
-
|
|
285
|
-
usageData: undefined,
|
|
310
|
+
usageData,
|
|
286
311
|
});
|
|
287
312
|
});
|
|
288
313
|
});
|
|
@@ -308,7 +308,8 @@ Respond with ONLY a markdown report in this exact format:
|
|
|
308
308
|
const claudePath = getClaudePath();
|
|
309
309
|
|
|
310
310
|
// Use configured model for failure analysis
|
|
311
|
-
const
|
|
311
|
+
const failureEntry = getModel('failureAnalysis');
|
|
312
|
+
const failureModel = failureEntry.model;
|
|
312
313
|
const proc = spawn(claudePath, [
|
|
313
314
|
'--model', failureModel,
|
|
314
315
|
'--no-session-persistence',
|
package/src/core/git.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { execSync } from 'node:child_process';
|
|
2
|
+
import * as fs from 'node:fs';
|
|
2
3
|
import * as path from 'node:path';
|
|
3
4
|
import { logger } from '../utils/logger.js';
|
|
4
5
|
import { extractProjectNumber, extractProjectName } from '../utils/paths.js';
|
|
@@ -213,10 +214,56 @@ export function isFileCommittedInHead(filePath: string): boolean {
|
|
|
213
214
|
}
|
|
214
215
|
}
|
|
215
216
|
|
|
217
|
+
/**
|
|
218
|
+
* Extract a concise description from input.md for use in plan commit messages.
|
|
219
|
+
* Reads the first non-empty line and truncates to ~70 chars.
|
|
220
|
+
* Falls back to the project name if input.md can't be read.
|
|
221
|
+
*/
|
|
222
|
+
function extractPlanDescription(projectPath: string, fallback: string): string {
|
|
223
|
+
const inputPath = path.join(projectPath, 'input.md');
|
|
224
|
+
try {
|
|
225
|
+
const content = fs.readFileSync(inputPath, 'utf-8');
|
|
226
|
+
const firstLine = content.split('\n').find(line => line.trim().length > 0);
|
|
227
|
+
if (firstLine) {
|
|
228
|
+
// Strip leading markdown markers (-, *, [ ], [x], #, etc.)
|
|
229
|
+
let cleaned = firstLine.trim().replace(/^[-*#>\s]*(\[.\]\s*)?/, '').trim();
|
|
230
|
+
if (cleaned.length > 70) {
|
|
231
|
+
cleaned = cleaned.substring(0, 67) + '...';
|
|
232
|
+
}
|
|
233
|
+
if (cleaned.length > 0) {
|
|
234
|
+
return cleaned;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
} catch {
|
|
238
|
+
// Can't read input.md, use fallback
|
|
239
|
+
}
|
|
240
|
+
return fallback;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Generate a description for amend commit messages.
|
|
245
|
+
* Uses the count and names of additional plan files if provided.
|
|
246
|
+
*/
|
|
247
|
+
function extractAmendDescription(additionalFiles?: string[]): string {
|
|
248
|
+
if (!additionalFiles || additionalFiles.length === 0) {
|
|
249
|
+
return 'updated plans';
|
|
250
|
+
}
|
|
251
|
+
// Extract task names from plan file paths (e.g., "plans/3-fix-bug.md" → "fix-bug")
|
|
252
|
+
const taskNames = additionalFiles
|
|
253
|
+
.map(f => path.basename(f, '.md'))
|
|
254
|
+
.map(name => name.replace(/^\d+-/, ''))
|
|
255
|
+
.filter(name => name.length > 0);
|
|
256
|
+
|
|
257
|
+
if (taskNames.length <= 3) {
|
|
258
|
+
return taskNames.join(', ');
|
|
259
|
+
}
|
|
260
|
+
return `${taskNames.length} tasks`;
|
|
261
|
+
}
|
|
262
|
+
|
|
216
263
|
/**
|
|
217
264
|
* Commit planning artifacts (input.md and decisions.md) for a project.
|
|
218
|
-
* Uses commit message format: RAF[
|
|
219
|
-
* For amendments: RAF[
|
|
265
|
+
* Uses commit message format: RAF[project-name] Plan: description
|
|
266
|
+
* For amendments: RAF[project-name] Amend: description
|
|
220
267
|
*
|
|
221
268
|
* @param projectPath - Full path to the project folder (e.g., /path/to/RAF/017-decision-vault)
|
|
222
269
|
* @param options - Optional settings
|
|
@@ -254,10 +301,17 @@ export async function commitPlanningArtifacts(projectPath: string, options?: { c
|
|
|
254
301
|
const formatType = options?.isAmend ? 'amend' as const : 'plan' as const;
|
|
255
302
|
const template = getCommitFormat(formatType);
|
|
256
303
|
const commitPrefix = getCommitPrefix();
|
|
304
|
+
|
|
305
|
+
// Auto-generate description based on commit type
|
|
306
|
+
const description = options?.isAmend
|
|
307
|
+
? extractAmendDescription(options?.additionalFiles)
|
|
308
|
+
: extractPlanDescription(projectPath, projectName);
|
|
309
|
+
|
|
257
310
|
const commitMessage = renderCommitMessage(template, {
|
|
258
311
|
prefix: commitPrefix,
|
|
259
|
-
projectId:
|
|
312
|
+
projectId: projectName, // backwards compat: {projectId} resolves to projectName
|
|
260
313
|
projectName,
|
|
314
|
+
description,
|
|
261
315
|
});
|
|
262
316
|
|
|
263
317
|
// Build list of files to stage (absolute paths)
|
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
getInputPath,
|
|
13
13
|
listProjects,
|
|
14
14
|
TASK_ID_PATTERN,
|
|
15
|
+
numericFileSort,
|
|
15
16
|
} from '../utils/paths.js';
|
|
16
17
|
import { sanitizeProjectName } from '../utils/validation.js';
|
|
17
18
|
import { logger } from '../utils/logger.js';
|
|
@@ -143,7 +144,7 @@ export class ProjectManager {
|
|
|
143
144
|
}
|
|
144
145
|
|
|
145
146
|
const outcomes: Array<{ taskId: string; content: string }> = [];
|
|
146
|
-
const files = fs.readdirSync(outcomesDir).filter((f) => f.endsWith('.md')).sort();
|
|
147
|
+
const files = fs.readdirSync(outcomesDir).filter((f) => f.endsWith('.md')).sort(numericFileSort);
|
|
147
148
|
|
|
148
149
|
for (const file of files) {
|
|
149
150
|
const match = file.match(new RegExp(`^(${TASK_ID_PATTERN})-`));
|
package/src/core/pull-request.ts
CHANGED
|
@@ -3,8 +3,8 @@ import * as fs from 'node:fs';
|
|
|
3
3
|
import * as os from 'node:os';
|
|
4
4
|
import * as path from 'node:path';
|
|
5
5
|
import { logger } from '../utils/logger.js';
|
|
6
|
-
import {
|
|
7
|
-
import { extractProjectName, getInputPath, getDecisionsPath, getOutcomesDir, TASK_ID_PATTERN } from '../utils/paths.js';
|
|
6
|
+
import { formatModelDisplay, getModel } from '../utils/config.js';
|
|
7
|
+
import { extractProjectName, getInputPath, getDecisionsPath, getOutcomesDir, TASK_ID_PATTERN, numericFileSort } from '../utils/paths.js';
|
|
8
8
|
|
|
9
9
|
export interface PrCreateResult {
|
|
10
10
|
success: boolean;
|
|
@@ -208,7 +208,7 @@ export function readProjectContext(projectPath: string): {
|
|
|
208
208
|
|
|
209
209
|
const outcomesDir = getOutcomesDir(projectPath);
|
|
210
210
|
if (fs.existsSync(outcomesDir)) {
|
|
211
|
-
const files = fs.readdirSync(outcomesDir).filter(f => f.endsWith('.md')).sort();
|
|
211
|
+
const files = fs.readdirSync(outcomesDir).filter(f => f.endsWith('.md')).sort(numericFileSort);
|
|
212
212
|
const taskIdPattern = new RegExp(`^(${TASK_ID_PATTERN})-`);
|
|
213
213
|
for (const file of files) {
|
|
214
214
|
const match = file.match(taskIdPattern);
|
|
@@ -279,7 +279,7 @@ Respond with ONLY the PR body in this exact format (no extra text, no code fence
|
|
|
279
279
|
[1-3 bullet points describing how to verify these changes work correctly]`;
|
|
280
280
|
|
|
281
281
|
try {
|
|
282
|
-
const prModel =
|
|
282
|
+
const prModel = formatModelDisplay(getModel('prGeneration').model);
|
|
283
283
|
logger.info(`Generating PR with ${prModel}...`);
|
|
284
284
|
const body = await callClaudeForPrBody(prompt, timeoutMs);
|
|
285
285
|
return body;
|
|
@@ -365,7 +365,7 @@ async function callClaudeForPrBody(prompt: string, timeoutMs: number): Promise<s
|
|
|
365
365
|
let output = '';
|
|
366
366
|
let stderr = '';
|
|
367
367
|
|
|
368
|
-
const prModel = getModel('prGeneration');
|
|
368
|
+
const prModel = getModel('prGeneration').model;
|
|
369
369
|
const proc = spawn(claudePath, [
|
|
370
370
|
'--model', prModel,
|
|
371
371
|
'--no-session-persistence',
|
|
@@ -1,35 +1,35 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { HarnessName } from '../types/config.js';
|
|
2
2
|
import type { ICliRunner } from './runner-interface.js';
|
|
3
3
|
import type { RunnerConfig } from './runner-types.js';
|
|
4
4
|
import { ClaudeRunner } from './claude-runner.js';
|
|
5
5
|
import { CodexRunner } from './codex-runner.js';
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
|
-
* Create a CLI runner for the given
|
|
8
|
+
* Create a CLI runner for the given harness configuration.
|
|
9
9
|
*/
|
|
10
10
|
export function createRunner(config: RunnerConfig = {}): ICliRunner {
|
|
11
|
-
const
|
|
11
|
+
const harness = config.harness ?? 'claude';
|
|
12
12
|
|
|
13
|
-
switch (
|
|
13
|
+
switch (harness) {
|
|
14
14
|
case 'claude':
|
|
15
15
|
return new ClaudeRunner(config);
|
|
16
16
|
case 'codex':
|
|
17
17
|
return new CodexRunner(config);
|
|
18
18
|
default:
|
|
19
|
-
throw new Error(`Unknown
|
|
19
|
+
throw new Error(`Unknown harness: ${harness}`);
|
|
20
20
|
}
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
/**
|
|
24
|
-
* Get the binary name for a given
|
|
24
|
+
* Get the binary name for a given harness.
|
|
25
25
|
*/
|
|
26
|
-
export function
|
|
27
|
-
switch (
|
|
26
|
+
export function getHarnessBinaryName(harness: HarnessName): string {
|
|
27
|
+
switch (harness) {
|
|
28
28
|
case 'claude':
|
|
29
29
|
return 'claude';
|
|
30
30
|
case 'codex':
|
|
31
31
|
return 'codex';
|
|
32
32
|
default:
|
|
33
|
-
throw new Error(`Unknown
|
|
33
|
+
throw new Error(`Unknown harness: ${harness}`);
|
|
34
34
|
}
|
|
35
35
|
}
|
package/src/core/runner-types.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { UsageData,
|
|
1
|
+
import type { UsageData, HarnessName } from '../types/config.js';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Options for a single runner execution (run, runVerbose, runInteractive, etc.).
|
|
@@ -52,14 +52,27 @@ export interface RunnerOptions {
|
|
|
52
52
|
export interface RunnerConfig {
|
|
53
53
|
/**
|
|
54
54
|
* Model to use (e.g., opus, sonnet, haiku, gpt-5.4).
|
|
55
|
-
* Default:
|
|
55
|
+
* Default: harness-specific default.
|
|
56
56
|
*/
|
|
57
57
|
model?: string;
|
|
58
58
|
/**
|
|
59
|
-
* CLI
|
|
59
|
+
* CLI harness to use.
|
|
60
60
|
* Default: 'claude'.
|
|
61
61
|
*/
|
|
62
|
-
|
|
62
|
+
harness?: HarnessName;
|
|
63
|
+
/**
|
|
64
|
+
* Reasoning effort level to pass to the CLI.
|
|
65
|
+
* Claude CLI: --effort <level>
|
|
66
|
+
* Codex CLI: -c model_reasoning_effort="<level>"
|
|
67
|
+
* Only included when explicitly set.
|
|
68
|
+
*/
|
|
69
|
+
reasoningEffort?: string;
|
|
70
|
+
/**
|
|
71
|
+
* Enable fast mode for faster output.
|
|
72
|
+
* Claude CLI: --settings '{"fastMode": true}'
|
|
73
|
+
* Only included when explicitly set to true.
|
|
74
|
+
*/
|
|
75
|
+
fast?: boolean;
|
|
63
76
|
}
|
|
64
77
|
|
|
65
78
|
/**
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as fs from 'node:fs';
|
|
2
2
|
import * as path from 'node:path';
|
|
3
|
-
import { getPlansDir, getOutcomesDir, getInputPath, TASK_ID_PATTERN } from '../utils/paths.js';
|
|
3
|
+
import { getPlansDir, getOutcomesDir, getInputPath, TASK_ID_PATTERN, numericFileSort } from '../utils/paths.js';
|
|
4
4
|
import { parsePlanFrontmatter, type PlanFrontmatter } from '../utils/frontmatter.js';
|
|
5
5
|
|
|
6
6
|
export type DerivedTaskStatus = 'pending' | 'completed' | 'failed' | 'blocked';
|
|
@@ -198,14 +198,14 @@ export function deriveProjectState(projectPath: string): DerivedProjectState {
|
|
|
198
198
|
|
|
199
199
|
const planFiles = fs.readdirSync(plansDir)
|
|
200
200
|
.filter((f) => f.endsWith('.md'))
|
|
201
|
-
.sort();
|
|
201
|
+
.sort(numericFileSort);
|
|
202
202
|
|
|
203
203
|
// Build a map of outcome statuses
|
|
204
204
|
const outcomeStatuses = new Map<string, DerivedTaskStatus>();
|
|
205
205
|
if (fs.existsSync(outcomesDir)) {
|
|
206
206
|
const outcomeFiles = fs.readdirSync(outcomesDir)
|
|
207
207
|
.filter((f) => f.endsWith('.md'))
|
|
208
|
-
.sort();
|
|
208
|
+
.sort(numericFileSort);
|
|
209
209
|
|
|
210
210
|
for (const outcomeFile of outcomeFiles) {
|
|
211
211
|
const match = outcomeFile.match(new RegExp(`^(${TASK_ID_PATTERN})-`));
|