-
Notifications
You must be signed in to change notification settings - Fork 1
feat(cli): add resolve-max-agents command with task complexity scaling #193
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,8 +2,8 @@ import { describe, it, expect, beforeEach, afterEach } from 'vitest'; | |
| import * as fs from 'node:fs'; | ||
| import * as path from 'node:path'; | ||
| import * as os from 'node:os'; | ||
| import { loadConfig, saveConfig, resolveModel, getConfigPath } from '../../src/core/config.js'; | ||
| import { AgentType, Model, ModelProfile, DEFAULT_CONFIG } from '../../src/core/types.js'; | ||
| import { loadConfig, saveConfig, resolveModel, resolveMaxAgents, getConfigPath } from '../../src/core/config.js'; | ||
| import { AgentType, Model, ModelProfile, TaskComplexity, DEFAULT_CONFIG, PARALLELISM_LIMITS } from '../../src/core/types.js'; | ||
|
|
||
| let tmpDir: string; | ||
|
|
||
|
|
@@ -174,3 +174,23 @@ describe('config-ensure-section command logic', () => { | |
| expect(JSON.parse(raw)).toHaveProperty('fresh_section'); | ||
| }); | ||
| }); | ||
|
|
||
| describe('resolve-max-agents command logic', () => { | ||
| it('returns max agents for a large project when --file-count is provided', () => { | ||
| expect(resolveMaxAgents(ModelProfile.BALANCED, 50)).toBe(PARALLELISM_LIMITS[ModelProfile.BALANCED].max_agents); | ||
| }); | ||
|
|
||
| it('defaults to file count 0 when --file-count flag is absent (small project cap applies)', () => { | ||
| expect(resolveMaxAgents(ModelProfile.BALANCED, 0)).toBe(Math.min(5, PARALLELISM_LIMITS[ModelProfile.BALANCED].max_agents)); | ||
| }); | ||
|
|
||
| it('invalid complexity value is not a member of TaskComplexity', () => { | ||
| expect(Object.values(TaskComplexity).includes('bogus' as TaskComplexity)).toBe(false); | ||
| }); | ||
|
|
||
| it('all TaskComplexity values are accepted by resolveMaxAgents without throwing', () => { | ||
| for (const complexity of Object.values(TaskComplexity)) { | ||
| expect(() => resolveMaxAgents(ModelProfile.BALANCED, 50, complexity)).not.toThrow(); | ||
| } | ||
| }); | ||
| }); | ||
|
Comment on lines
+178
to
+196
|
||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -9,7 +9,7 @@ import { | |||||
| resolveMaxAgents, | ||||||
| getConfigPath, | ||||||
| } from '../../src/core/config.js'; | ||||||
| import { Model, ModelProfile, AgentType, DEFAULT_CONFIG, PARALLELISM_LIMITS } from '../../src/core/types.js'; | ||||||
| import { Model, ModelProfile, AgentType, TaskComplexity, DEFAULT_CONFIG, PARALLELISM_LIMITS } from '../../src/core/types.js'; | ||||||
|
|
||||||
| let tmpDir: string; | ||||||
|
|
||||||
|
|
@@ -157,4 +157,38 @@ describe('resolveMaxAgents', () => { | |||||
| it('large project (>=25 files) uses full profile max for budget profile', () => { | ||||||
| expect(resolveMaxAgents(ModelProfile.BUDGET, 50)).toBe(PARALLELISM_LIMITS[ModelProfile.BUDGET].max_agents); | ||||||
| }); | ||||||
|
|
||||||
| it('backward compatibility: calling without complexity parameter defaults to medium', () => { | ||||||
| const withDefault = resolveMaxAgents(ModelProfile.BALANCED, 50); | ||||||
| const withMedium = resolveMaxAgents(ModelProfile.BALANCED, 50, TaskComplexity.MEDIUM); | ||||||
| expect(withDefault).toBe(withMedium); | ||||||
| }); | ||||||
|
|
||||||
| it('complexity=simple halves the cap (rounded down, min 1)', () => { | ||||||
| const limits = PARALLELISM_LIMITS[ModelProfile.BALANCED]; | ||||||
| const cap = limits.max_agents; | ||||||
| const expected = Math.max(1, Math.floor(cap / 2)); | ||||||
| expect(resolveMaxAgents(ModelProfile.BALANCED, 50, TaskComplexity.SIMPLE)).toBe(expected); | ||||||
| }); | ||||||
|
|
||||||
| it('complexity=complex uses the full cap', () => { | ||||||
| const limits = PARALLELISM_LIMITS[ModelProfile.BALANCED]; | ||||||
| expect(resolveMaxAgents(ModelProfile.BALANCED, 50, TaskComplexity.COMPLEX)).toBe(limits.max_agents); | ||||||
| }); | ||||||
|
|
||||||
| it('complexity=medium uses the same result as no complexity argument', () => { | ||||||
| expect(resolveMaxAgents(ModelProfile.QUALITY, 30, TaskComplexity.MEDIUM)).toBe( | ||||||
| resolveMaxAgents(ModelProfile.QUALITY, 30), | ||||||
| ); | ||||||
| }); | ||||||
|
|
||||||
| it('complexity=simple with small project caps at minimum 1', () => { | ||||||
|
||||||
| it('complexity=simple with small project caps at minimum 1', () => { | |
| it('complexity=simple with small project halves the cap (result 2 for cap 5)', () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
resolve-max-agentstreatsargs[1]as the optional profile, but the documented usage passes flags immediately after the command (e.g.resolve-max-agents --file-count 42 ...). In that caseargs[1]becomes--file-count,PARALLELISM_LIMITS[profile]is undefined, and the command will crash when resolving limits. Parse the profile as an actual positional arg (skip--*tokens), and/or introduce a--profileflag with validation against known ModelProfile values (fall back to config when absent).