rafcode 2.1.0 → 2.2.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.
Files changed (130) hide show
  1. package/.claude/settings.local.json +4 -1
  2. package/CLAUDE.md +59 -11
  3. package/RAF/ahslfe-config-wizard/decisions.md +34 -0
  4. package/RAF/ahslfe-config-wizard/input.md +1 -0
  5. package/RAF/ahslfe-config-wizard/outcomes/01-define-config-schema.md +38 -0
  6. package/RAF/ahslfe-config-wizard/outcomes/02-refactor-codebase-to-use-config.md +67 -0
  7. package/RAF/ahslfe-config-wizard/outcomes/03-create-config-documentation.md +37 -0
  8. package/RAF/ahslfe-config-wizard/outcomes/04-implement-raf-config-command.md +47 -0
  9. package/RAF/ahslfe-config-wizard/outcomes/05-update-claude-md.md +26 -0
  10. package/RAF/ahslfe-config-wizard/plans/01-define-config-schema.md +73 -0
  11. package/RAF/ahslfe-config-wizard/plans/02-refactor-codebase-to-use-config.md +74 -0
  12. package/RAF/ahslfe-config-wizard/plans/03-create-config-documentation.md +57 -0
  13. package/RAF/ahslfe-config-wizard/plans/04-implement-raf-config-command.md +66 -0
  14. package/RAF/ahslfe-config-wizard/plans/05-update-claude-md.md +60 -0
  15. package/RAF/ahstvo-token-tracker/decisions.md +44 -0
  16. package/RAF/ahstvo-token-tracker/input.md +3 -0
  17. package/RAF/ahstvo-token-tracker/outcomes/01-full-model-id-support.md +43 -0
  18. package/RAF/ahstvo-token-tracker/outcomes/02-name-generation-no-session.md +33 -0
  19. package/RAF/ahstvo-token-tracker/outcomes/03-unify-stream-json-execution.md +48 -0
  20. package/RAF/ahstvo-token-tracker/outcomes/04-token-tracking-cost-calculation.md +53 -0
  21. package/RAF/ahstvo-token-tracker/outcomes/05-token-cost-console-reporting.md +57 -0
  22. package/RAF/ahstvo-token-tracker/outcomes/06-runtime-verbose-toggle.md +53 -0
  23. package/RAF/ahstvo-token-tracker/outcomes/07-readme-config-docs.md +36 -0
  24. package/RAF/ahstvo-token-tracker/plans/01-full-model-id-support.md +35 -0
  25. package/RAF/ahstvo-token-tracker/plans/02-name-generation-no-session.md +36 -0
  26. package/RAF/ahstvo-token-tracker/plans/03-unify-stream-json-execution.md +44 -0
  27. package/RAF/ahstvo-token-tracker/plans/04-token-tracking-cost-calculation.md +56 -0
  28. package/RAF/ahstvo-token-tracker/plans/05-token-cost-console-reporting.md +55 -0
  29. package/RAF/ahstvo-token-tracker/plans/06-runtime-verbose-toggle.md +48 -0
  30. package/RAF/ahstvo-token-tracker/plans/07-readme-config-docs.md +44 -0
  31. package/README.md +34 -0
  32. package/dist/commands/config.d.ts +3 -0
  33. package/dist/commands/config.d.ts.map +1 -0
  34. package/dist/commands/config.js +173 -0
  35. package/dist/commands/config.js.map +1 -0
  36. package/dist/commands/do.d.ts.map +1 -1
  37. package/dist/commands/do.js +50 -28
  38. package/dist/commands/do.js.map +1 -1
  39. package/dist/commands/plan.d.ts.map +1 -1
  40. package/dist/commands/plan.js +3 -2
  41. package/dist/commands/plan.js.map +1 -1
  42. package/dist/core/claude-runner.d.ts +17 -13
  43. package/dist/core/claude-runner.d.ts.map +1 -1
  44. package/dist/core/claude-runner.js +42 -257
  45. package/dist/core/claude-runner.js.map +1 -1
  46. package/dist/core/failure-analyzer.d.ts.map +1 -1
  47. package/dist/core/failure-analyzer.js +6 -3
  48. package/dist/core/failure-analyzer.js.map +1 -1
  49. package/dist/core/git.d.ts.map +1 -1
  50. package/dist/core/git.js +10 -3
  51. package/dist/core/git.js.map +1 -1
  52. package/dist/core/pull-request.d.ts +1 -1
  53. package/dist/core/pull-request.d.ts.map +1 -1
  54. package/dist/core/pull-request.js +7 -4
  55. package/dist/core/pull-request.js.map +1 -1
  56. package/dist/core/shutdown-handler.d.ts.map +1 -1
  57. package/dist/core/shutdown-handler.js +0 -4
  58. package/dist/core/shutdown-handler.js.map +1 -1
  59. package/dist/index.js +2 -0
  60. package/dist/index.js.map +1 -1
  61. package/dist/parsers/stream-renderer.d.ts +16 -4
  62. package/dist/parsers/stream-renderer.d.ts.map +1 -1
  63. package/dist/parsers/stream-renderer.js +35 -5
  64. package/dist/parsers/stream-renderer.js.map +1 -1
  65. package/dist/prompts/execution.d.ts.map +1 -1
  66. package/dist/prompts/execution.js +11 -1
  67. package/dist/prompts/execution.js.map +1 -1
  68. package/dist/types/config.d.ts +95 -5
  69. package/dist/types/config.d.ts.map +1 -1
  70. package/dist/types/config.js +63 -3
  71. package/dist/types/config.js.map +1 -1
  72. package/dist/utils/config.d.ts +59 -7
  73. package/dist/utils/config.d.ts.map +1 -1
  74. package/dist/utils/config.js +276 -21
  75. package/dist/utils/config.js.map +1 -1
  76. package/dist/utils/name-generator.d.ts +3 -7
  77. package/dist/utils/name-generator.d.ts.map +1 -1
  78. package/dist/utils/name-generator.js +75 -61
  79. package/dist/utils/name-generator.js.map +1 -1
  80. package/dist/utils/terminal-symbols.d.ts +21 -0
  81. package/dist/utils/terminal-symbols.d.ts.map +1 -1
  82. package/dist/utils/terminal-symbols.js +62 -0
  83. package/dist/utils/terminal-symbols.js.map +1 -1
  84. package/dist/utils/token-tracker.d.ts +45 -0
  85. package/dist/utils/token-tracker.d.ts.map +1 -0
  86. package/dist/utils/token-tracker.js +107 -0
  87. package/dist/utils/token-tracker.js.map +1 -0
  88. package/dist/utils/validation.d.ts +5 -5
  89. package/dist/utils/validation.d.ts.map +1 -1
  90. package/dist/utils/validation.js +10 -6
  91. package/dist/utils/validation.js.map +1 -1
  92. package/dist/utils/verbose-toggle.d.ts +33 -0
  93. package/dist/utils/verbose-toggle.d.ts.map +1 -0
  94. package/dist/utils/verbose-toggle.js +94 -0
  95. package/dist/utils/verbose-toggle.js.map +1 -0
  96. package/package.json +1 -1
  97. package/src/commands/config.ts +204 -0
  98. package/src/commands/do.ts +59 -27
  99. package/src/commands/plan.ts +3 -2
  100. package/src/core/claude-runner.ts +58 -311
  101. package/src/core/failure-analyzer.ts +6 -3
  102. package/src/core/git.ts +10 -3
  103. package/src/core/pull-request.ts +7 -4
  104. package/src/core/shutdown-handler.ts +0 -5
  105. package/src/index.ts +2 -0
  106. package/src/parsers/stream-renderer.ts +55 -8
  107. package/src/prompts/config-docs.md +331 -0
  108. package/src/prompts/execution.ts +13 -1
  109. package/src/types/config.ts +156 -8
  110. package/src/utils/config.ts +335 -21
  111. package/src/utils/name-generator.ts +84 -71
  112. package/src/utils/terminal-symbols.ts +68 -0
  113. package/src/utils/token-tracker.ts +135 -0
  114. package/src/utils/validation.ts +15 -10
  115. package/src/utils/verbose-toggle.ts +103 -0
  116. package/tests/unit/claude-runner.test.ts +216 -403
  117. package/tests/unit/config-command.test.ts +163 -0
  118. package/tests/unit/config.test.ts +608 -30
  119. package/tests/unit/name-generator.test.ts +99 -75
  120. package/tests/unit/pull-request.test.ts +2 -0
  121. package/tests/unit/stream-renderer.test.ts +83 -30
  122. package/tests/unit/terminal-symbols.test.ts +157 -0
  123. package/tests/unit/token-tracker.test.ts +352 -0
  124. package/tests/unit/verbose-toggle.test.ts +204 -0
  125. package/RAF/ahrtxf-session-sentinel/decisions.md +0 -19
  126. package/RAF/ahrtxf-session-sentinel/input.md +0 -1
  127. package/RAF/ahrtxf-session-sentinel/outcomes/01-capture-session-id.md +0 -37
  128. package/RAF/ahrtxf-session-sentinel/outcomes/02-resume-flag.md +0 -45
  129. package/RAF/ahrtxf-session-sentinel/plans/01-capture-session-id.md +0 -41
  130. package/RAF/ahrtxf-session-sentinel/plans/02-resume-flag.md +0 -51
@@ -0,0 +1,163 @@
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 { createConfigCommand } from '../../src/commands/config.js';
6
+ import { validateConfig, ConfigValidationError } from '../../src/utils/config.js';
7
+
8
+ describe('Config Command', () => {
9
+ let tempDir: string;
10
+
11
+ beforeEach(() => {
12
+ tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'raf-config-cmd-test-'));
13
+ });
14
+
15
+ afterEach(() => {
16
+ fs.rmSync(tempDir, { recursive: true, force: true });
17
+ });
18
+
19
+ describe('Command setup', () => {
20
+ it('should create a command named "config"', () => {
21
+ const cmd = createConfigCommand();
22
+ expect(cmd.name()).toBe('config');
23
+ });
24
+
25
+ it('should have a description', () => {
26
+ const cmd = createConfigCommand();
27
+ expect(cmd.description()).toBeTruthy();
28
+ expect(cmd.description()).toContain('config');
29
+ });
30
+
31
+ it('should accept a variadic prompt argument', () => {
32
+ const cmd = createConfigCommand();
33
+ const args = cmd.registeredArguments;
34
+ expect(args.length).toBe(1);
35
+ expect(args[0]!.variadic).toBe(true);
36
+ });
37
+
38
+ it('should have a --reset option', () => {
39
+ const cmd = createConfigCommand();
40
+ const resetOption = cmd.options.find((o) => o.long === '--reset');
41
+ expect(resetOption).toBeDefined();
42
+ });
43
+
44
+ it('should register in a parent program', () => {
45
+ const program = new Command();
46
+ program.addCommand(createConfigCommand());
47
+ const configCmd = program.commands.find((c) => c.name() === 'config');
48
+ expect(configCmd).toBeDefined();
49
+ });
50
+ });
51
+
52
+ describe('Post-session validation logic', () => {
53
+ it('should accept valid config with model override', () => {
54
+ const config = { models: { execute: 'sonnet' } };
55
+ expect(() => validateConfig(config)).not.toThrow();
56
+ });
57
+
58
+ it('should accept valid config with effort override', () => {
59
+ const config = { effort: { plan: 'low' } };
60
+ expect(() => validateConfig(config)).not.toThrow();
61
+ });
62
+
63
+ it('should accept valid config with timeout', () => {
64
+ const config = { timeout: 120 };
65
+ expect(() => validateConfig(config)).not.toThrow();
66
+ });
67
+
68
+ it('should reject config with unknown keys', () => {
69
+ const config = { unknownKey: true };
70
+ expect(() => validateConfig(config)).toThrow(ConfigValidationError);
71
+ });
72
+
73
+ it('should reject config with invalid model name', () => {
74
+ const config = { models: { execute: 'gpt-4' } };
75
+ expect(() => validateConfig(config)).toThrow(ConfigValidationError);
76
+ });
77
+
78
+ it('should reject config with invalid effort level', () => {
79
+ const config = { effort: { plan: 'max' } };
80
+ expect(() => validateConfig(config)).toThrow(ConfigValidationError);
81
+ });
82
+
83
+ it('should reject non-object config', () => {
84
+ expect(() => validateConfig('string')).toThrow(ConfigValidationError);
85
+ expect(() => validateConfig(null)).toThrow(ConfigValidationError);
86
+ expect(() => validateConfig([])).toThrow(ConfigValidationError);
87
+ });
88
+
89
+ it('should accept an empty config (all defaults)', () => {
90
+ expect(() => validateConfig({})).not.toThrow();
91
+ });
92
+ });
93
+
94
+ describe('Reset flow - file operations', () => {
95
+ it('should be able to delete config file', () => {
96
+ const configPath = path.join(tempDir, 'raf.config.json');
97
+ fs.writeFileSync(configPath, JSON.stringify({ timeout: 90 }, null, 2));
98
+ expect(fs.existsSync(configPath)).toBe(true);
99
+
100
+ fs.unlinkSync(configPath);
101
+ expect(fs.existsSync(configPath)).toBe(false);
102
+ });
103
+
104
+ it('should handle non-existent config file gracefully', () => {
105
+ const configPath = path.join(tempDir, 'raf.config.json');
106
+ expect(fs.existsSync(configPath)).toBe(false);
107
+ // Reset when no file exists should not throw
108
+ });
109
+ });
110
+
111
+ describe('Config file round-trip', () => {
112
+ it('should write and read valid config', () => {
113
+ const configPath = path.join(tempDir, 'raf.config.json');
114
+ const config = { models: { execute: 'sonnet' as const }, timeout: 90 };
115
+
116
+ fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
117
+ const content = fs.readFileSync(configPath, 'utf-8');
118
+ const parsed = JSON.parse(content);
119
+
120
+ expect(parsed.models.execute).toBe('sonnet');
121
+ expect(parsed.timeout).toBe(90);
122
+ expect(() => validateConfig(parsed)).not.toThrow();
123
+ });
124
+
125
+ it('should detect invalid JSON after write', () => {
126
+ const configPath = path.join(tempDir, 'raf.config.json');
127
+ fs.writeFileSync(configPath, '{ invalid json }}}');
128
+
129
+ const content = fs.readFileSync(configPath, 'utf-8');
130
+ expect(() => JSON.parse(content)).toThrow(SyntaxError);
131
+ });
132
+
133
+ it('should detect validation errors after write', () => {
134
+ const configPath = path.join(tempDir, 'raf.config.json');
135
+ fs.writeFileSync(configPath, JSON.stringify({ badKey: true }, null, 2));
136
+
137
+ const content = fs.readFileSync(configPath, 'utf-8');
138
+ const parsed = JSON.parse(content);
139
+ expect(() => validateConfig(parsed)).toThrow(ConfigValidationError);
140
+ });
141
+ });
142
+
143
+ describe('System prompt construction', () => {
144
+ it('should indicate no config when file does not exist', () => {
145
+ const configPath = path.join(tempDir, 'raf.config.json');
146
+ const exists = fs.existsSync(configPath);
147
+ const state = exists
148
+ ? fs.readFileSync(configPath, 'utf-8')
149
+ : 'No config file exists yet.';
150
+ expect(state).toContain('No config file');
151
+ });
152
+
153
+ it('should include config contents when file exists', () => {
154
+ const configPath = path.join(tempDir, 'raf.config.json');
155
+ const config = { timeout: 120, worktree: true };
156
+ fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
157
+
158
+ const content = fs.readFileSync(configPath, 'utf-8');
159
+ expect(content).toContain('"timeout": 120');
160
+ expect(content).toContain('"worktree": true');
161
+ });
162
+ });
163
+ });