reviewflow 3.8.1 → 3.10.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/CHANGELOG.md +16 -0
- package/README.md +2 -0
- package/dist/config/projectConfig.d.ts +3 -5
- package/dist/config/projectConfig.d.ts.map +1 -1
- package/dist/config/projectConfig.js +19 -1
- package/dist/config/projectConfig.js.map +1 -1
- package/dist/entities/modelRouting/modelRouting.gateway.d.ts +5 -0
- package/dist/entities/modelRouting/modelRouting.gateway.d.ts.map +1 -0
- package/dist/entities/modelRouting/modelRouting.gateway.js +2 -0
- package/dist/entities/modelRouting/modelRouting.gateway.js.map +1 -0
- package/dist/entities/modelRouting/modelRouting.schema.d.ts +13 -0
- package/dist/entities/modelRouting/modelRouting.schema.d.ts.map +1 -0
- package/dist/entities/modelRouting/modelRouting.schema.js +7 -0
- package/dist/entities/modelRouting/modelRouting.schema.js.map +1 -0
- package/dist/entities/tokenUsage/tokenUsage.gateway.d.ts +6 -0
- package/dist/entities/tokenUsage/tokenUsage.gateway.d.ts.map +1 -0
- package/dist/entities/tokenUsage/tokenUsage.gateway.js +2 -0
- package/dist/entities/tokenUsage/tokenUsage.gateway.js.map +1 -0
- package/dist/entities/tokenUsage/tokenUsage.schema.d.ts +30 -0
- package/dist/entities/tokenUsage/tokenUsage.schema.d.ts.map +1 -0
- package/dist/entities/tokenUsage/tokenUsage.schema.js +19 -0
- package/dist/entities/tokenUsage/tokenUsage.schema.js.map +1 -0
- package/dist/frameworks/claude/claudeInvoker.d.ts +4 -0
- package/dist/frameworks/claude/claudeInvoker.d.ts.map +1 -1
- package/dist/frameworks/claude/claudeInvoker.js +86 -27
- package/dist/frameworks/claude/claudeInvoker.js.map +1 -1
- package/dist/frameworks/claude/streamJsonParser.d.ts +44 -0
- package/dist/frameworks/claude/streamJsonParser.d.ts.map +1 -0
- package/dist/frameworks/claude/streamJsonParser.js +96 -0
- package/dist/frameworks/claude/streamJsonParser.js.map +1 -0
- package/dist/frameworks/queue/pQueueAdapter.d.ts +2 -0
- package/dist/frameworks/queue/pQueueAdapter.d.ts.map +1 -1
- package/dist/frameworks/queue/pQueueAdapter.js.map +1 -1
- package/dist/frameworks/settings/runtimeSettings.d.ts +1 -1
- package/dist/frameworks/settings/runtimeSettings.d.ts.map +1 -1
- package/dist/frameworks/settings/runtimeSettings.js +1 -1
- package/dist/frameworks/settings/runtimeSettings.js.map +1 -1
- package/dist/interface-adapters/gateways/projectConfig/routingPolicy.projectConfig.gateway.d.ts +6 -0
- package/dist/interface-adapters/gateways/projectConfig/routingPolicy.projectConfig.gateway.d.ts.map +1 -0
- package/dist/interface-adapters/gateways/projectConfig/routingPolicy.projectConfig.gateway.js +8 -0
- package/dist/interface-adapters/gateways/projectConfig/routingPolicy.projectConfig.gateway.js.map +1 -0
- package/dist/interface-adapters/gateways/tokenUsage/tokenUsage.filesystem.gateway.d.ts +7 -0
- package/dist/interface-adapters/gateways/tokenUsage/tokenUsage.filesystem.gateway.d.ts.map +1 -0
- package/dist/interface-adapters/gateways/tokenUsage/tokenUsage.filesystem.gateway.js +37 -0
- package/dist/interface-adapters/gateways/tokenUsage/tokenUsage.filesystem.gateway.js.map +1 -0
- package/dist/tests/factories/routingPolicy.factory.d.ts +5 -0
- package/dist/tests/factories/routingPolicy.factory.d.ts.map +1 -0
- package/dist/tests/factories/routingPolicy.factory.js +10 -0
- package/dist/tests/factories/routingPolicy.factory.js.map +1 -0
- package/dist/tests/factories/tokenUsage.factory.d.ts +8 -0
- package/dist/tests/factories/tokenUsage.factory.d.ts.map +1 -0
- package/dist/tests/factories/tokenUsage.factory.js +28 -0
- package/dist/tests/factories/tokenUsage.factory.js.map +1 -0
- package/dist/tests/stubs/tokenUsage.stub.d.ts +11 -0
- package/dist/tests/stubs/tokenUsage.stub.d.ts.map +1 -0
- package/dist/tests/stubs/tokenUsage.stub.js +19 -0
- package/dist/tests/stubs/tokenUsage.stub.js.map +1 -0
- package/dist/tests/units/config/projectConfig.test.js +47 -0
- package/dist/tests/units/config/projectConfig.test.js.map +1 -1
- package/dist/tests/units/frameworks/claude/streamJsonParser.test.d.ts +2 -0
- package/dist/tests/units/frameworks/claude/streamJsonParser.test.d.ts.map +1 -0
- package/dist/tests/units/frameworks/claude/streamJsonParser.test.js +83 -0
- package/dist/tests/units/frameworks/claude/streamJsonParser.test.js.map +1 -0
- package/dist/tests/units/interface-adapters/gateways/projectConfig/routingPolicy.projectConfig.gateway.test.d.ts +2 -0
- package/dist/tests/units/interface-adapters/gateways/projectConfig/routingPolicy.projectConfig.gateway.test.d.ts.map +1 -0
- package/dist/tests/units/interface-adapters/gateways/projectConfig/routingPolicy.projectConfig.gateway.test.js +44 -0
- package/dist/tests/units/interface-adapters/gateways/projectConfig/routingPolicy.projectConfig.gateway.test.js.map +1 -0
- package/dist/tests/units/interface-adapters/gateways/tokenUsage/tokenUsage.filesystem.gateway.test.d.ts +2 -0
- package/dist/tests/units/interface-adapters/gateways/tokenUsage/tokenUsage.filesystem.gateway.test.d.ts.map +1 -0
- package/dist/tests/units/interface-adapters/gateways/tokenUsage/tokenUsage.filesystem.gateway.test.js +57 -0
- package/dist/tests/units/interface-adapters/gateways/tokenUsage/tokenUsage.filesystem.gateway.test.js.map +1 -0
- package/dist/tests/units/usecases/selectModelForReview/selectModelForReview.usecase.test.d.ts +2 -0
- package/dist/tests/units/usecases/selectModelForReview/selectModelForReview.usecase.test.d.ts.map +1 -0
- package/dist/tests/units/usecases/selectModelForReview/selectModelForReview.usecase.test.js +55 -0
- package/dist/tests/units/usecases/selectModelForReview/selectModelForReview.usecase.test.js.map +1 -0
- package/dist/tests/units/usecases/summarizeTokenUsage/summarizeTokenUsage.usecase.test.d.ts +2 -0
- package/dist/tests/units/usecases/summarizeTokenUsage/summarizeTokenUsage.usecase.test.d.ts.map +1 -0
- package/dist/tests/units/usecases/summarizeTokenUsage/summarizeTokenUsage.usecase.test.js +64 -0
- package/dist/tests/units/usecases/summarizeTokenUsage/summarizeTokenUsage.usecase.test.js.map +1 -0
- package/dist/tests/units/usecases/trackTokenUsage/trackTokenUsage.usecase.test.d.ts +2 -0
- package/dist/tests/units/usecases/trackTokenUsage/trackTokenUsage.usecase.test.d.ts.map +1 -0
- package/dist/tests/units/usecases/trackTokenUsage/trackTokenUsage.usecase.test.js +26 -0
- package/dist/tests/units/usecases/trackTokenUsage/trackTokenUsage.usecase.test.js.map +1 -0
- package/dist/usecases/selectModelForReview/selectModelForReview.usecase.d.ts +15 -0
- package/dist/usecases/selectModelForReview/selectModelForReview.usecase.d.ts.map +1 -0
- package/dist/usecases/selectModelForReview/selectModelForReview.usecase.js +16 -0
- package/dist/usecases/selectModelForReview/selectModelForReview.usecase.js.map +1 -0
- package/dist/usecases/summarizeTokenUsage/summarizeTokenUsage.usecase.d.ts +23 -0
- package/dist/usecases/summarizeTokenUsage/summarizeTokenUsage.usecase.d.ts.map +1 -0
- package/dist/usecases/summarizeTokenUsage/summarizeTokenUsage.usecase.js +38 -0
- package/dist/usecases/summarizeTokenUsage/summarizeTokenUsage.usecase.js.map +1 -0
- package/dist/usecases/trackTokenUsage/trackTokenUsage.usecase.d.ts +8 -0
- package/dist/usecases/trackTokenUsage/trackTokenUsage.usecase.d.ts.map +1 -0
- package/dist/usecases/trackTokenUsage/trackTokenUsage.usecase.js +10 -0
- package/dist/usecases/trackTokenUsage/trackTokenUsage.usecase.js.map +1 -0
- package/package.json +3 -3
- package/scripts/hooks/enforce-dependency-rule.sh +61 -0
- package/scripts/hooks/enforce-gateway-port-purity.sh +35 -0
- package/scripts/hooks/enforce-presenter-class.sh +34 -0
- package/scripts/hooks/no-barrel-exports.sh +23 -0
- package/scripts/hooks/parse-json.sh +17 -0
- package/scripts/hooks/pre-commit-gate.sh +29 -0
- package/scripts/hooks/protect-main-branch.sh +28 -0
- package/scripts/hooks/protect-main-push.sh +38 -0
- package/scripts/hooks/require-spec.sh +58 -0
- package/scripts/hooks/session-context.sh +47 -0
- package/scripts/hooks/tests/run-tests.sh +189 -0
- package/scripts/hooks/tests/test-architecture-hooks.sh +163 -0
- package/scripts/hooks/verify-spec-updated.sh +75 -0
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
2
|
+
import * as fs from 'node:fs';
|
|
3
|
+
import { ProjectConfigRoutingPolicyGateway } from '../../../../../interface-adapters/gateways/projectConfig/routingPolicy.projectConfig.gateway.js';
|
|
4
|
+
vi.mock('node:fs');
|
|
5
|
+
describe('ProjectConfigRoutingPolicyGateway', () => {
|
|
6
|
+
const gateway = new ProjectConfigRoutingPolicyGateway();
|
|
7
|
+
beforeEach(() => {
|
|
8
|
+
vi.resetAllMocks();
|
|
9
|
+
});
|
|
10
|
+
it('returns null when config.json does not exist', async () => {
|
|
11
|
+
vi.mocked(fs.existsSync).mockReturnValue(false);
|
|
12
|
+
const result = await gateway.load('/fake/path');
|
|
13
|
+
expect(result).toBeNull();
|
|
14
|
+
});
|
|
15
|
+
it('returns null when config.json has no routingPolicy field', async () => {
|
|
16
|
+
vi.mocked(fs.existsSync).mockReturnValue(true);
|
|
17
|
+
vi.mocked(fs.readFileSync).mockReturnValue(JSON.stringify({
|
|
18
|
+
github: true,
|
|
19
|
+
gitlab: false,
|
|
20
|
+
defaultModel: 'sonnet',
|
|
21
|
+
reviewSkill: 'review',
|
|
22
|
+
reviewFollowupSkill: 'review-followup',
|
|
23
|
+
}));
|
|
24
|
+
const result = await gateway.load('/fake/path');
|
|
25
|
+
expect(result).toBeNull();
|
|
26
|
+
});
|
|
27
|
+
it('returns the routingPolicy object when config.json has a valid routingPolicy', async () => {
|
|
28
|
+
vi.mocked(fs.existsSync).mockReturnValue(true);
|
|
29
|
+
vi.mocked(fs.readFileSync).mockReturnValue(JSON.stringify({
|
|
30
|
+
github: true,
|
|
31
|
+
gitlab: false,
|
|
32
|
+
defaultModel: 'sonnet',
|
|
33
|
+
reviewSkill: 'review',
|
|
34
|
+
reviewFollowupSkill: 'review-followup',
|
|
35
|
+
routingPolicy: {
|
|
36
|
+
haikuMaxLines: 50,
|
|
37
|
+
sonnetMaxLines: 500,
|
|
38
|
+
},
|
|
39
|
+
}));
|
|
40
|
+
const result = await gateway.load('/fake/path');
|
|
41
|
+
expect(result).toEqual({ haikuMaxLines: 50, sonnetMaxLines: 500 });
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
//# sourceMappingURL=routingPolicy.projectConfig.gateway.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"routingPolicy.projectConfig.gateway.test.js","sourceRoot":"","sources":["../../../../../../src/tests/units/interface-adapters/gateways/projectConfig/routingPolicy.projectConfig.gateway.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AAC9D,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,EAAE,iCAAiC,EAAE,MAAM,oFAAoF,CAAC;AAEvI,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;AAEnB,QAAQ,CAAC,mCAAmC,EAAE,GAAG,EAAE;IACjD,MAAM,OAAO,GAAG,IAAI,iCAAiC,EAAE,CAAC;IAExD,UAAU,CAAC,GAAG,EAAE;QACd,EAAE,CAAC,aAAa,EAAE,CAAC;IACrB,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,8CAA8C,EAAE,KAAK,IAAI,EAAE;QAC5D,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC;QAEhD,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;QAEhD,MAAM,CAAC,MAAM,CAAC,CAAC,QAAQ,EAAE,CAAC;IAC5B,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,0DAA0D,EAAE,KAAK,IAAI,EAAE;QACxE,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC;QAC/C,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC,YAAY,CAAC,CAAC,eAAe,CACxC,IAAI,CAAC,SAAS,CAAC;YACb,MAAM,EAAE,IAAI;YACZ,MAAM,EAAE,KAAK;YACb,YAAY,EAAE,QAAQ;YACtB,WAAW,EAAE,QAAQ;YACrB,mBAAmB,EAAE,iBAAiB;SACvC,CAAC,CACH,CAAC;QAEF,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;QAEhD,MAAM,CAAC,MAAM,CAAC,CAAC,QAAQ,EAAE,CAAC;IAC5B,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6EAA6E,EAAE,KAAK,IAAI,EAAE;QAC3F,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC;QAC/C,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC,YAAY,CAAC,CAAC,eAAe,CACxC,IAAI,CAAC,SAAS,CAAC;YACb,MAAM,EAAE,IAAI;YACZ,MAAM,EAAE,KAAK;YACb,YAAY,EAAE,QAAQ;YACtB,WAAW,EAAE,QAAQ;YACrB,mBAAmB,EAAE,iBAAiB;YACtC,aAAa,EAAE;gBACb,aAAa,EAAE,EAAE;gBACjB,cAAc,EAAE,GAAG;aACpB;SACF,CAAC,CACH,CAAC;QAEF,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;QAEhD,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,EAAE,aAAa,EAAE,EAAE,EAAE,cAAc,EAAE,GAAG,EAAE,CAAC,CAAC;IACrE,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tokenUsage.filesystem.gateway.test.d.ts","sourceRoot":"","sources":["../../../../../../src/tests/units/interface-adapters/gateways/tokenUsage/tokenUsage.filesystem.gateway.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
2
|
+
import { mkdtempSync, rmSync } from 'node:fs';
|
|
3
|
+
import { tmpdir } from 'node:os';
|
|
4
|
+
import { join } from 'node:path';
|
|
5
|
+
import { FilesystemTokenUsageGateway } from '../../../../../interface-adapters/gateways/tokenUsage/tokenUsage.filesystem.gateway.js';
|
|
6
|
+
import { TokenUsageRecordFactory } from '../../../../../tests/factories/tokenUsage.factory.js';
|
|
7
|
+
describe('FilesystemTokenUsageGateway', () => {
|
|
8
|
+
let tempDir;
|
|
9
|
+
let gateway;
|
|
10
|
+
beforeEach(() => {
|
|
11
|
+
tempDir = mkdtempSync(join(tmpdir(), 'reviewflow-test-'));
|
|
12
|
+
gateway = new FilesystemTokenUsageGateway();
|
|
13
|
+
});
|
|
14
|
+
afterEach(() => {
|
|
15
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
16
|
+
});
|
|
17
|
+
it('should record and load a single record (roundtrip)', async () => {
|
|
18
|
+
const record = TokenUsageRecordFactory.create({ localPath: tempDir });
|
|
19
|
+
await gateway.record(record);
|
|
20
|
+
const loaded = await gateway.loadAll(tempDir);
|
|
21
|
+
expect(loaded).toHaveLength(1);
|
|
22
|
+
expect(loaded[0]).toEqual(record);
|
|
23
|
+
});
|
|
24
|
+
it('should record multiple records and load all', async () => {
|
|
25
|
+
const record1 = TokenUsageRecordFactory.create({ jobId: 'job-1', localPath: tempDir });
|
|
26
|
+
const record2 = TokenUsageRecordFactory.create({ jobId: 'job-2', localPath: tempDir });
|
|
27
|
+
const record3 = TokenUsageRecordFactory.create({ jobId: 'job-3', localPath: tempDir });
|
|
28
|
+
await gateway.record(record1);
|
|
29
|
+
await gateway.record(record2);
|
|
30
|
+
await gateway.record(record3);
|
|
31
|
+
const loaded = await gateway.loadAll(tempDir);
|
|
32
|
+
expect(loaded).toHaveLength(3);
|
|
33
|
+
});
|
|
34
|
+
it('should return empty array when file does not exist', async () => {
|
|
35
|
+
const loaded = await gateway.loadAll(tempDir);
|
|
36
|
+
expect(loaded).toEqual([]);
|
|
37
|
+
});
|
|
38
|
+
it('should silently skip invalid lines in file', async () => {
|
|
39
|
+
const { writeFileSync, mkdirSync } = await import('node:fs');
|
|
40
|
+
const dir = join(tempDir, '.claude', 'reviews');
|
|
41
|
+
mkdirSync(dir, { recursive: true });
|
|
42
|
+
const validRecord = TokenUsageRecordFactory.create({ localPath: tempDir });
|
|
43
|
+
const invalidLine = 'not valid json';
|
|
44
|
+
const validLine = JSON.stringify(validRecord);
|
|
45
|
+
writeFileSync(join(dir, 'usage.jsonl'), `${invalidLine}\n${validLine}\n`);
|
|
46
|
+
const loaded = await gateway.loadAll(tempDir);
|
|
47
|
+
expect(loaded).toHaveLength(1);
|
|
48
|
+
expect(loaded[0]).toEqual(validRecord);
|
|
49
|
+
});
|
|
50
|
+
it('should create directory if it does not exist', async () => {
|
|
51
|
+
const record = TokenUsageRecordFactory.create({ localPath: tempDir });
|
|
52
|
+
await expect(gateway.record(record)).resolves.not.toThrow();
|
|
53
|
+
const loaded = await gateway.loadAll(tempDir);
|
|
54
|
+
expect(loaded).toHaveLength(1);
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
//# sourceMappingURL=tokenUsage.filesystem.gateway.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tokenUsage.filesystem.gateway.test.js","sourceRoot":"","sources":["../../../../../../src/tests/units/interface-adapters/gateways/tokenUsage/tokenUsage.filesystem.gateway.test.ts"],"names":[],"mappings":"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"}
|
package/dist/tests/units/usecases/selectModelForReview/selectModelForReview.usecase.test.d.ts.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"selectModelForReview.usecase.test.d.ts","sourceRoot":"","sources":["../../../../../src/tests/units/usecases/selectModelForReview/selectModelForReview.usecase.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { SelectModelForReviewUseCase } from '../../../../usecases/selectModelForReview/selectModelForReview.usecase.js';
|
|
3
|
+
import { RoutingPolicyFactory } from '../../../../tests/factories/routingPolicy.factory.js';
|
|
4
|
+
describe('SelectModelForReviewUseCase', () => {
|
|
5
|
+
const useCase = new SelectModelForReviewUseCase();
|
|
6
|
+
it('returns defaultModel when policy is null', () => {
|
|
7
|
+
const result = useCase.execute({
|
|
8
|
+
diffStats: { additions: 200, deletions: 100 },
|
|
9
|
+
policy: null,
|
|
10
|
+
defaultModel: 'opus',
|
|
11
|
+
});
|
|
12
|
+
expect(result).toBe('opus');
|
|
13
|
+
});
|
|
14
|
+
it('returns haiku for a 30-line MR with standard policy', () => {
|
|
15
|
+
const result = useCase.execute({
|
|
16
|
+
diffStats: { additions: 20, deletions: 10 },
|
|
17
|
+
policy: RoutingPolicyFactory.create(),
|
|
18
|
+
defaultModel: 'opus',
|
|
19
|
+
});
|
|
20
|
+
expect(result).toBe('haiku');
|
|
21
|
+
});
|
|
22
|
+
it('returns sonnet for a 200-line MR with standard policy', () => {
|
|
23
|
+
const result = useCase.execute({
|
|
24
|
+
diffStats: { additions: 150, deletions: 50 },
|
|
25
|
+
policy: RoutingPolicyFactory.create(),
|
|
26
|
+
defaultModel: 'opus',
|
|
27
|
+
});
|
|
28
|
+
expect(result).toBe('sonnet');
|
|
29
|
+
});
|
|
30
|
+
it('returns opus for a 1000-line MR with standard policy', () => {
|
|
31
|
+
const result = useCase.execute({
|
|
32
|
+
diffStats: { additions: 600, deletions: 400 },
|
|
33
|
+
policy: RoutingPolicyFactory.create(),
|
|
34
|
+
defaultModel: 'haiku',
|
|
35
|
+
});
|
|
36
|
+
expect(result).toBe('opus');
|
|
37
|
+
});
|
|
38
|
+
it('returns haiku for exactly 50 lines (boundary inclusive)', () => {
|
|
39
|
+
const result = useCase.execute({
|
|
40
|
+
diffStats: { additions: 30, deletions: 20 },
|
|
41
|
+
policy: RoutingPolicyFactory.create(),
|
|
42
|
+
defaultModel: 'opus',
|
|
43
|
+
});
|
|
44
|
+
expect(result).toBe('haiku');
|
|
45
|
+
});
|
|
46
|
+
it('returns sonnet for exactly 51 lines', () => {
|
|
47
|
+
const result = useCase.execute({
|
|
48
|
+
diffStats: { additions: 30, deletions: 21 },
|
|
49
|
+
policy: RoutingPolicyFactory.create(),
|
|
50
|
+
defaultModel: 'opus',
|
|
51
|
+
});
|
|
52
|
+
expect(result).toBe('sonnet');
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
//# sourceMappingURL=selectModelForReview.usecase.test.js.map
|
package/dist/tests/units/usecases/selectModelForReview/selectModelForReview.usecase.test.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"selectModelForReview.usecase.test.js","sourceRoot":"","sources":["../../../../../src/tests/units/usecases/selectModelForReview/selectModelForReview.usecase.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,2BAA2B,EAAE,MAAM,iEAAiE,CAAC;AAC9G,OAAO,EAAE,oBAAoB,EAAE,MAAM,4CAA4C,CAAC;AAElF,QAAQ,CAAC,6BAA6B,EAAE,GAAG,EAAE;IAC3C,MAAM,OAAO,GAAG,IAAI,2BAA2B,EAAE,CAAC;IAElD,EAAE,CAAC,0CAA0C,EAAE,GAAG,EAAE;QAClD,MAAM,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;YAC7B,SAAS,EAAE,EAAE,SAAS,EAAE,GAAG,EAAE,SAAS,EAAE,GAAG,EAAE;YAC7C,MAAM,EAAE,IAAI;YACZ,YAAY,EAAE,MAAM;SACrB,CAAC,CAAC;QAEH,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAC9B,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,qDAAqD,EAAE,GAAG,EAAE;QAC7D,MAAM,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;YAC7B,SAAS,EAAE,EAAE,SAAS,EAAE,EAAE,EAAE,SAAS,EAAE,EAAE,EAAE;YAC3C,MAAM,EAAE,oBAAoB,CAAC,MAAM,EAAE;YACrC,YAAY,EAAE,MAAM;SACrB,CAAC,CAAC;QAEH,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAC/B,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,uDAAuD,EAAE,GAAG,EAAE;QAC/D,MAAM,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;YAC7B,SAAS,EAAE,EAAE,SAAS,EAAE,GAAG,EAAE,SAAS,EAAE,EAAE,EAAE;YAC5C,MAAM,EAAE,oBAAoB,CAAC,MAAM,EAAE;YACrC,YAAY,EAAE,MAAM;SACrB,CAAC,CAAC;QAEH,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAChC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,sDAAsD,EAAE,GAAG,EAAE;QAC9D,MAAM,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;YAC7B,SAAS,EAAE,EAAE,SAAS,EAAE,GAAG,EAAE,SAAS,EAAE,GAAG,EAAE;YAC7C,MAAM,EAAE,oBAAoB,CAAC,MAAM,EAAE;YACrC,YAAY,EAAE,OAAO;SACtB,CAAC,CAAC;QAEH,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAC9B,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,yDAAyD,EAAE,GAAG,EAAE;QACjE,MAAM,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;YAC7B,SAAS,EAAE,EAAE,SAAS,EAAE,EAAE,EAAE,SAAS,EAAE,EAAE,EAAE;YAC3C,MAAM,EAAE,oBAAoB,CAAC,MAAM,EAAE;YACrC,YAAY,EAAE,MAAM;SACrB,CAAC,CAAC;QAEH,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAC/B,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,qCAAqC,EAAE,GAAG,EAAE;QAC7C,MAAM,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;YAC7B,SAAS,EAAE,EAAE,SAAS,EAAE,EAAE,EAAE,SAAS,EAAE,EAAE,EAAE;YAC3C,MAAM,EAAE,oBAAoB,CAAC,MAAM,EAAE;YACrC,YAAY,EAAE,MAAM;SACrB,CAAC,CAAC;QAEH,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAChC,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
package/dist/tests/units/usecases/summarizeTokenUsage/summarizeTokenUsage.usecase.test.d.ts.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"summarizeTokenUsage.usecase.test.d.ts","sourceRoot":"","sources":["../../../../../src/tests/units/usecases/summarizeTokenUsage/summarizeTokenUsage.usecase.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach } from 'vitest';
|
|
2
|
+
import { SummarizeTokenUsageUseCase } from '../../../../usecases/summarizeTokenUsage/summarizeTokenUsage.usecase.js';
|
|
3
|
+
import { StubTokenUsageGateway } from '../../../../tests/stubs/tokenUsage.stub.js';
|
|
4
|
+
import { TokenUsageRecordFactory } from '../../../../tests/factories/tokenUsage.factory.js';
|
|
5
|
+
describe('SummarizeTokenUsageUseCase', () => {
|
|
6
|
+
let gateway;
|
|
7
|
+
let useCase;
|
|
8
|
+
beforeEach(() => {
|
|
9
|
+
gateway = new StubTokenUsageGateway();
|
|
10
|
+
useCase = new SummarizeTokenUsageUseCase(gateway);
|
|
11
|
+
});
|
|
12
|
+
it('should return zero summary when no records', async () => {
|
|
13
|
+
const summary = await useCase.execute({ localPath: '/project' });
|
|
14
|
+
expect(summary.recordCount).toBe(0);
|
|
15
|
+
expect(summary.totalCostUsd).toBe(0);
|
|
16
|
+
expect(summary.totalInputTokens).toBe(0);
|
|
17
|
+
expect(summary.totalOutputTokens).toBe(0);
|
|
18
|
+
expect(summary.totalCacheRead).toBe(0);
|
|
19
|
+
expect(summary.totalCacheCreation).toBe(0);
|
|
20
|
+
expect(summary.byModel).toEqual({});
|
|
21
|
+
});
|
|
22
|
+
it('should aggregate all records', async () => {
|
|
23
|
+
gateway.setRecords([
|
|
24
|
+
TokenUsageRecordFactory.create({
|
|
25
|
+
model: 'claude-opus-4-7',
|
|
26
|
+
usage: { inputTokens: 1000, outputTokens: 200, cacheCreationInputTokens: 100, cacheReadInputTokens: 500, costUsd: 0.01 },
|
|
27
|
+
}),
|
|
28
|
+
TokenUsageRecordFactory.create({
|
|
29
|
+
model: 'claude-opus-4-7',
|
|
30
|
+
usage: { inputTokens: 2000, outputTokens: 300, cacheCreationInputTokens: 50, cacheReadInputTokens: 800, costUsd: 0.02 },
|
|
31
|
+
}),
|
|
32
|
+
]);
|
|
33
|
+
const summary = await useCase.execute({ localPath: '/project' });
|
|
34
|
+
expect(summary.recordCount).toBe(2);
|
|
35
|
+
expect(summary.totalInputTokens).toBe(3000);
|
|
36
|
+
expect(summary.totalOutputTokens).toBe(500);
|
|
37
|
+
expect(summary.totalCacheCreation).toBe(150);
|
|
38
|
+
expect(summary.totalCacheRead).toBe(1300);
|
|
39
|
+
expect(summary.totalCostUsd).toBeCloseTo(0.03);
|
|
40
|
+
});
|
|
41
|
+
it('should group by model', async () => {
|
|
42
|
+
gateway.setRecords([
|
|
43
|
+
TokenUsageRecordFactory.create({ model: 'claude-opus-4-7', usage: { inputTokens: 100, outputTokens: 10, cacheCreationInputTokens: 0, cacheReadInputTokens: 0, costUsd: 0.01 } }),
|
|
44
|
+
TokenUsageRecordFactory.create({ model: 'claude-sonnet-4-6', usage: { inputTokens: 200, outputTokens: 20, cacheCreationInputTokens: 0, cacheReadInputTokens: 0, costUsd: 0.005 } }),
|
|
45
|
+
TokenUsageRecordFactory.create({ model: 'claude-opus-4-7', usage: { inputTokens: 50, outputTokens: 5, cacheCreationInputTokens: 0, cacheReadInputTokens: 0, costUsd: 0.003 } }),
|
|
46
|
+
]);
|
|
47
|
+
const summary = await useCase.execute({ localPath: '/project' });
|
|
48
|
+
expect(summary.byModel['claude-opus-4-7'].count).toBe(2);
|
|
49
|
+
expect(summary.byModel['claude-opus-4-7'].costUsd).toBeCloseTo(0.013);
|
|
50
|
+
expect(summary.byModel['claude-sonnet-4-6'].count).toBe(1);
|
|
51
|
+
expect(summary.byModel['claude-sonnet-4-6'].costUsd).toBeCloseTo(0.005);
|
|
52
|
+
});
|
|
53
|
+
it('should filter records by since date', async () => {
|
|
54
|
+
gateway.setRecords([
|
|
55
|
+
TokenUsageRecordFactory.create({ recordedAt: '2025-01-01T00:00:00Z', usage: { inputTokens: 100, outputTokens: 10, cacheCreationInputTokens: 0, cacheReadInputTokens: 0, costUsd: 0.01 } }),
|
|
56
|
+
TokenUsageRecordFactory.create({ recordedAt: '2025-06-01T00:00:00Z', usage: { inputTokens: 200, outputTokens: 20, cacheCreationInputTokens: 0, cacheReadInputTokens: 0, costUsd: 0.02 } }),
|
|
57
|
+
TokenUsageRecordFactory.create({ recordedAt: '2025-12-01T00:00:00Z', usage: { inputTokens: 300, outputTokens: 30, cacheCreationInputTokens: 0, cacheReadInputTokens: 0, costUsd: 0.03 } }),
|
|
58
|
+
]);
|
|
59
|
+
const summary = await useCase.execute({ localPath: '/project', since: '2025-05-01T00:00:00Z' });
|
|
60
|
+
expect(summary.recordCount).toBe(2);
|
|
61
|
+
expect(summary.totalInputTokens).toBe(500);
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
//# sourceMappingURL=summarizeTokenUsage.usecase.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"summarizeTokenUsage.usecase.test.js","sourceRoot":"","sources":["../../../../../src/tests/units/usecases/summarizeTokenUsage/summarizeTokenUsage.usecase.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"trackTokenUsage.usecase.test.d.ts","sourceRoot":"","sources":["../../../../../src/tests/units/usecases/trackTokenUsage/trackTokenUsage.usecase.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach } from 'vitest';
|
|
2
|
+
import { TrackTokenUsageUseCase } from '../../../../usecases/trackTokenUsage/trackTokenUsage.usecase.js';
|
|
3
|
+
import { StubTokenUsageGateway } from '../../../../tests/stubs/tokenUsage.stub.js';
|
|
4
|
+
import { TokenUsageRecordFactory } from '../../../../tests/factories/tokenUsage.factory.js';
|
|
5
|
+
describe('TrackTokenUsageUseCase', () => {
|
|
6
|
+
let gateway;
|
|
7
|
+
let useCase;
|
|
8
|
+
beforeEach(() => {
|
|
9
|
+
gateway = new StubTokenUsageGateway();
|
|
10
|
+
useCase = new TrackTokenUsageUseCase(gateway);
|
|
11
|
+
});
|
|
12
|
+
it('should delegate to gateway', async () => {
|
|
13
|
+
const record = TokenUsageRecordFactory.create();
|
|
14
|
+
await useCase.execute(record);
|
|
15
|
+
expect(gateway.records).toHaveLength(1);
|
|
16
|
+
expect(gateway.records[0]).toEqual(record);
|
|
17
|
+
});
|
|
18
|
+
it('should record multiple calls', async () => {
|
|
19
|
+
const record1 = TokenUsageRecordFactory.create({ jobId: 'job-1' });
|
|
20
|
+
const record2 = TokenUsageRecordFactory.create({ jobId: 'job-2' });
|
|
21
|
+
await useCase.execute(record1);
|
|
22
|
+
await useCase.execute(record2);
|
|
23
|
+
expect(gateway.records).toHaveLength(2);
|
|
24
|
+
});
|
|
25
|
+
});
|
|
26
|
+
//# sourceMappingURL=trackTokenUsage.usecase.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"trackTokenUsage.usecase.test.js","sourceRoot":"","sources":["../../../../../src/tests/units/usecases/trackTokenUsage/trackTokenUsage.usecase.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AAC1D,OAAO,EAAE,sBAAsB,EAAE,MAAM,uDAAuD,CAAC;AAC/F,OAAO,EAAE,qBAAqB,EAAE,MAAM,kCAAkC,CAAC;AACzE,OAAO,EAAE,uBAAuB,EAAE,MAAM,yCAAyC,CAAC;AAElF,QAAQ,CAAC,wBAAwB,EAAE,GAAG,EAAE;IACtC,IAAI,OAA8B,CAAC;IACnC,IAAI,OAA+B,CAAC;IAEpC,UAAU,CAAC,GAAG,EAAE;QACd,OAAO,GAAG,IAAI,qBAAqB,EAAE,CAAC;QACtC,OAAO,GAAG,IAAI,sBAAsB,CAAC,OAAO,CAAC,CAAC;IAChD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,4BAA4B,EAAE,KAAK,IAAI,EAAE;QAC1C,MAAM,MAAM,GAAG,uBAAuB,CAAC,MAAM,EAAE,CAAC;QAEhD,MAAM,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;QAE9B,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QACxC,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IAC7C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,8BAA8B,EAAE,KAAK,IAAI,EAAE;QAC5C,MAAM,OAAO,GAAG,uBAAuB,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC;QACnE,MAAM,OAAO,GAAG,uBAAuB,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC;QAEnE,MAAM,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QAC/B,MAAM,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QAE/B,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;IAC1C,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { ClaudeModelName, RoutingPolicy } from '../../entities/modelRouting/modelRouting.schema.js';
|
|
2
|
+
type DiffStatsInput = {
|
|
3
|
+
additions: number;
|
|
4
|
+
deletions: number;
|
|
5
|
+
};
|
|
6
|
+
type Input = {
|
|
7
|
+
diffStats: DiffStatsInput;
|
|
8
|
+
policy: RoutingPolicy | null;
|
|
9
|
+
defaultModel: ClaudeModelName;
|
|
10
|
+
};
|
|
11
|
+
export declare class SelectModelForReviewUseCase {
|
|
12
|
+
execute({ diffStats, policy, defaultModel }: Input): ClaudeModelName;
|
|
13
|
+
}
|
|
14
|
+
export {};
|
|
15
|
+
//# sourceMappingURL=selectModelForReview.usecase.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"selectModelForReview.usecase.d.ts","sourceRoot":"","sources":["../../../src/usecases/selectModelForReview/selectModelForReview.usecase.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,gDAAgD,CAAC;AAErG,KAAK,cAAc,GAAG;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAAC;AAE/D,KAAK,KAAK,GAAG;IACX,SAAS,EAAE,cAAc,CAAC;IAC1B,MAAM,EAAE,aAAa,GAAG,IAAI,CAAC;IAC7B,YAAY,EAAE,eAAe,CAAC;CAC/B,CAAC;AAEF,qBAAa,2BAA2B;IACtC,OAAO,CAAC,EAAE,SAAS,EAAE,MAAM,EAAE,YAAY,EAAE,EAAE,KAAK,GAAG,eAAe;CAiBrE"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export class SelectModelForReviewUseCase {
|
|
2
|
+
execute({ diffStats, policy, defaultModel }) {
|
|
3
|
+
if (policy === null) {
|
|
4
|
+
return defaultModel;
|
|
5
|
+
}
|
|
6
|
+
const totalLines = diffStats.additions + diffStats.deletions;
|
|
7
|
+
if (totalLines <= policy.haikuMaxLines) {
|
|
8
|
+
return 'haiku';
|
|
9
|
+
}
|
|
10
|
+
if (totalLines <= policy.sonnetMaxLines) {
|
|
11
|
+
return 'sonnet';
|
|
12
|
+
}
|
|
13
|
+
return 'opus';
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=selectModelForReview.usecase.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"selectModelForReview.usecase.js","sourceRoot":"","sources":["../../../src/usecases/selectModelForReview/selectModelForReview.usecase.ts"],"names":[],"mappings":"AAUA,MAAM,OAAO,2BAA2B;IACtC,OAAO,CAAC,EAAE,SAAS,EAAE,MAAM,EAAE,YAAY,EAAS;QAChD,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;YACpB,OAAO,YAAY,CAAC;QACtB,CAAC;QAED,MAAM,UAAU,GAAG,SAAS,CAAC,SAAS,GAAG,SAAS,CAAC,SAAS,CAAC;QAE7D,IAAI,UAAU,IAAI,MAAM,CAAC,aAAa,EAAE,CAAC;YACvC,OAAO,OAAO,CAAC;QACjB,CAAC;QAED,IAAI,UAAU,IAAI,MAAM,CAAC,cAAc,EAAE,CAAC;YACxC,OAAO,QAAQ,CAAC;QAClB,CAAC;QAED,OAAO,MAAM,CAAC;IAChB,CAAC;CACF"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { TokenUsageGateway } from '../../entities/tokenUsage/tokenUsage.gateway.js';
|
|
2
|
+
export type TokenUsageSummary = {
|
|
3
|
+
totalInputTokens: number;
|
|
4
|
+
totalOutputTokens: number;
|
|
5
|
+
totalCacheRead: number;
|
|
6
|
+
totalCacheCreation: number;
|
|
7
|
+
totalCostUsd: number;
|
|
8
|
+
recordCount: number;
|
|
9
|
+
byModel: Record<string, {
|
|
10
|
+
count: number;
|
|
11
|
+
costUsd: number;
|
|
12
|
+
}>;
|
|
13
|
+
};
|
|
14
|
+
export type SummarizeInput = {
|
|
15
|
+
localPath: string;
|
|
16
|
+
since?: string;
|
|
17
|
+
};
|
|
18
|
+
export declare class SummarizeTokenUsageUseCase {
|
|
19
|
+
private readonly gateway;
|
|
20
|
+
constructor(gateway: TokenUsageGateway);
|
|
21
|
+
execute({ localPath, since }: SummarizeInput): Promise<TokenUsageSummary>;
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=summarizeTokenUsage.usecase.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"summarizeTokenUsage.usecase.d.ts","sourceRoot":"","sources":["../../../src/usecases/summarizeTokenUsage/summarizeTokenUsage.usecase.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,6CAA6C,CAAC;AAErF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,gBAAgB,EAAE,MAAM,CAAC;IACzB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,cAAc,EAAE,MAAM,CAAC;IACvB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAC7D,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,qBAAa,0BAA0B;IACzB,OAAO,CAAC,QAAQ,CAAC,OAAO;gBAAP,OAAO,EAAE,iBAAiB;IAEjD,OAAO,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,EAAE,cAAc,GAAG,OAAO,CAAC,iBAAiB,CAAC;CAmChF"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
export class SummarizeTokenUsageUseCase {
|
|
2
|
+
gateway;
|
|
3
|
+
constructor(gateway) {
|
|
4
|
+
this.gateway = gateway;
|
|
5
|
+
}
|
|
6
|
+
async execute({ localPath, since }) {
|
|
7
|
+
const allRecords = await this.gateway.loadAll(localPath);
|
|
8
|
+
const records = since
|
|
9
|
+
? allRecords.filter(record => record.recordedAt >= since)
|
|
10
|
+
: allRecords;
|
|
11
|
+
const summary = {
|
|
12
|
+
totalInputTokens: 0,
|
|
13
|
+
totalOutputTokens: 0,
|
|
14
|
+
totalCacheRead: 0,
|
|
15
|
+
totalCacheCreation: 0,
|
|
16
|
+
totalCostUsd: 0,
|
|
17
|
+
recordCount: records.length,
|
|
18
|
+
byModel: {},
|
|
19
|
+
};
|
|
20
|
+
for (const record of records) {
|
|
21
|
+
summary.totalInputTokens += record.usage.inputTokens;
|
|
22
|
+
summary.totalOutputTokens += record.usage.outputTokens;
|
|
23
|
+
summary.totalCacheRead += record.usage.cacheReadInputTokens;
|
|
24
|
+
summary.totalCacheCreation += record.usage.cacheCreationInputTokens;
|
|
25
|
+
summary.totalCostUsd += record.usage.costUsd;
|
|
26
|
+
const modelEntry = summary.byModel[record.model];
|
|
27
|
+
if (modelEntry) {
|
|
28
|
+
modelEntry.count += 1;
|
|
29
|
+
modelEntry.costUsd += record.usage.costUsd;
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
summary.byModel[record.model] = { count: 1, costUsd: record.usage.costUsd };
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return summary;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=summarizeTokenUsage.usecase.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"summarizeTokenUsage.usecase.js","sourceRoot":"","sources":["../../../src/usecases/summarizeTokenUsage/summarizeTokenUsage.usecase.ts"],"names":[],"mappings":"AAiBA,MAAM,OAAO,0BAA0B;IACR;IAA7B,YAA6B,OAA0B;QAA1B,YAAO,GAAP,OAAO,CAAmB;IAAG,CAAC;IAE3D,KAAK,CAAC,OAAO,CAAC,EAAE,SAAS,EAAE,KAAK,EAAkB;QAChD,MAAM,UAAU,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;QAEzD,MAAM,OAAO,GAAG,KAAK;YACnB,CAAC,CAAC,UAAU,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,UAAU,IAAI,KAAK,CAAC;YACzD,CAAC,CAAC,UAAU,CAAC;QAEf,MAAM,OAAO,GAAsB;YACjC,gBAAgB,EAAE,CAAC;YACnB,iBAAiB,EAAE,CAAC;YACpB,cAAc,EAAE,CAAC;YACjB,kBAAkB,EAAE,CAAC;YACrB,YAAY,EAAE,CAAC;YACf,WAAW,EAAE,OAAO,CAAC,MAAM;YAC3B,OAAO,EAAE,EAAE;SACZ,CAAC;QAEF,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;YAC7B,OAAO,CAAC,gBAAgB,IAAI,MAAM,CAAC,KAAK,CAAC,WAAW,CAAC;YACrD,OAAO,CAAC,iBAAiB,IAAI,MAAM,CAAC,KAAK,CAAC,YAAY,CAAC;YACvD,OAAO,CAAC,cAAc,IAAI,MAAM,CAAC,KAAK,CAAC,oBAAoB,CAAC;YAC5D,OAAO,CAAC,kBAAkB,IAAI,MAAM,CAAC,KAAK,CAAC,wBAAwB,CAAC;YACpE,OAAO,CAAC,YAAY,IAAI,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC;YAE7C,MAAM,UAAU,GAAG,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YACjD,IAAI,UAAU,EAAE,CAAC;gBACf,UAAU,CAAC,KAAK,IAAI,CAAC,CAAC;gBACtB,UAAU,CAAC,OAAO,IAAI,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC;YAC7C,CAAC;iBAAM,CAAC;gBACN,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE,MAAM,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC;YAC9E,CAAC;QACH,CAAC;QAED,OAAO,OAAO,CAAC;IACjB,CAAC;CACF"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { TokenUsageGateway } from '../../entities/tokenUsage/tokenUsage.gateway.js';
|
|
2
|
+
import type { TokenUsageRecord } from '../../entities/tokenUsage/tokenUsage.schema.js';
|
|
3
|
+
export declare class TrackTokenUsageUseCase {
|
|
4
|
+
private readonly gateway;
|
|
5
|
+
constructor(gateway: TokenUsageGateway);
|
|
6
|
+
execute(record: TokenUsageRecord): Promise<void>;
|
|
7
|
+
}
|
|
8
|
+
//# sourceMappingURL=trackTokenUsage.usecase.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"trackTokenUsage.usecase.d.ts","sourceRoot":"","sources":["../../../src/usecases/trackTokenUsage/trackTokenUsage.usecase.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,6CAA6C,CAAC;AACrF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,4CAA4C,CAAC;AAEnF,qBAAa,sBAAsB;IACrB,OAAO,CAAC,QAAQ,CAAC,OAAO;gBAAP,OAAO,EAAE,iBAAiB;IAEjD,OAAO,CAAC,MAAM,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC;CAGvD"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"trackTokenUsage.usecase.js","sourceRoot":"","sources":["../../../src/usecases/trackTokenUsage/trackTokenUsage.usecase.ts"],"names":[],"mappings":"AAGA,MAAM,OAAO,sBAAsB;IACJ;IAA7B,YAA6B,OAA0B;QAA1B,YAAO,GAAP,OAAO,CAAmB;IAAG,CAAC;IAE3D,KAAK,CAAC,OAAO,CAAC,MAAwB;QACpC,MAAM,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IACpC,CAAC;CACF"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "reviewflow",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.10.0",
|
|
4
4
|
"description": "AI-powered code review automation for GitLab/GitHub using Claude Code",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/main/server.js",
|
|
@@ -54,9 +54,9 @@
|
|
|
54
54
|
"docs:preview": "vitepress preview docs"
|
|
55
55
|
},
|
|
56
56
|
"dependencies": {
|
|
57
|
-
"@fastify/static": "^
|
|
57
|
+
"@fastify/static": "^9.1.1",
|
|
58
58
|
"@fastify/websocket": "^11.0.0",
|
|
59
|
-
"@hono/node-server": "1.19.
|
|
59
|
+
"@hono/node-server": "1.19.13",
|
|
60
60
|
"@inquirer/prompts": "^8.2.0",
|
|
61
61
|
"@modelcontextprotocol/sdk": "^1.26.0",
|
|
62
62
|
"dotenv": "^16.4.0",
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
# Enforces the Clean Architecture Dependency Rule.
|
|
5
|
+
# entities/ must not import from usecases/, interface-adapters/, or frameworks/.
|
|
6
|
+
# usecases/ must not import from interface-adapters/ or frameworks/.
|
|
7
|
+
# Applies to all files under src/.
|
|
8
|
+
# Used as PreToolUse hook on Write|Edit.
|
|
9
|
+
# Exit 0 = allow, Exit 2 = block with feedback to Claude.
|
|
10
|
+
|
|
11
|
+
HOOK_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
12
|
+
INPUT=$(cat)
|
|
13
|
+
FILE_PATH=$(echo "$INPUT" | "$HOOK_DIR/parse-json.sh" tool_input.file_path)
|
|
14
|
+
|
|
15
|
+
is_in_src() {
|
|
16
|
+
[[ "$FILE_PATH" == */src/* ]]
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
if ! is_in_src; then
|
|
20
|
+
exit 0
|
|
21
|
+
fi
|
|
22
|
+
|
|
23
|
+
CONTENT=$(echo "$INPUT" | "$HOOK_DIR/parse-json.sh" tool_input.content)
|
|
24
|
+
NEW_STRING=$(echo "$INPUT" | "$HOOK_DIR/parse-json.sh" tool_input.new_string)
|
|
25
|
+
TEXT_TO_CHECK="${CONTENT}${NEW_STRING}"
|
|
26
|
+
|
|
27
|
+
is_entity_file() {
|
|
28
|
+
[[ "$FILE_PATH" == */src/entities/* ]]
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
is_usecase_file() {
|
|
32
|
+
[[ "$FILE_PATH" == */src/usecases/* ]]
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if is_entity_file; then
|
|
36
|
+
if echo "$TEXT_TO_CHECK" | grep -qE "from\s*['\"][^'\"]*@/interface-adapters/|from\s*['\"][^'\"]*[./]interface-adapters/"; then
|
|
37
|
+
echo "Dependency Rule violation in $FILE_PATH: entities/ must not import from interface-adapters/. Inner layers are framework-agnostic." >&2
|
|
38
|
+
exit 2
|
|
39
|
+
fi
|
|
40
|
+
if echo "$TEXT_TO_CHECK" | grep -qE "from\s*['\"][^'\"]*@/usecases/|from\s*['\"][^'\"]*[./]usecases/"; then
|
|
41
|
+
echo "Dependency Rule violation in $FILE_PATH: entities/ must not import from usecases/. Dependencies point inward only." >&2
|
|
42
|
+
exit 2
|
|
43
|
+
fi
|
|
44
|
+
if echo "$TEXT_TO_CHECK" | grep -qE "from\s*['\"][^'\"]*@/frameworks/|from\s*['\"][^'\"]*[./]frameworks/"; then
|
|
45
|
+
echo "Dependency Rule violation in $FILE_PATH: entities/ must not import from frameworks/. Inner layers are framework-agnostic." >&2
|
|
46
|
+
exit 2
|
|
47
|
+
fi
|
|
48
|
+
fi
|
|
49
|
+
|
|
50
|
+
if is_usecase_file; then
|
|
51
|
+
if echo "$TEXT_TO_CHECK" | grep -qE "from\s*['\"][^'\"]*@/interface-adapters/|from\s*['\"][^'\"]*[./]interface-adapters/"; then
|
|
52
|
+
echo "Dependency Rule violation in $FILE_PATH: usecases/ must not import from interface-adapters/. Depend on gateway contracts (under entities/), not implementations." >&2
|
|
53
|
+
exit 2
|
|
54
|
+
fi
|
|
55
|
+
if echo "$TEXT_TO_CHECK" | grep -qE "from\s*['\"][^'\"]*@/frameworks/|from\s*['\"][^'\"]*[./]frameworks/"; then
|
|
56
|
+
echo "Dependency Rule violation in $FILE_PATH: usecases/ must not import from frameworks/. Depend on gateway contracts (under entities/), not infrastructure." >&2
|
|
57
|
+
exit 2
|
|
58
|
+
fi
|
|
59
|
+
fi
|
|
60
|
+
|
|
61
|
+
exit 0
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
# Enforces gateway port purity for ReviewFlow.
|
|
5
|
+
# Gateway contracts live in src/entities/**/*.gateway.ts and must be interfaces or abstract classes.
|
|
6
|
+
# A plain 'export class' in a port file means the contract has leaked an implementation.
|
|
7
|
+
# Implementation gateway files (in interface-adapters/gateways/) are excluded.
|
|
8
|
+
# Used as PreToolUse hook on Write|Edit.
|
|
9
|
+
# Exit 0 = allow, Exit 2 = block with feedback to Claude.
|
|
10
|
+
|
|
11
|
+
HOOK_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
12
|
+
INPUT=$(cat)
|
|
13
|
+
FILE_PATH=$(echo "$INPUT" | "$HOOK_DIR/parse-json.sh" tool_input.file_path)
|
|
14
|
+
|
|
15
|
+
is_gateway_port() {
|
|
16
|
+
local filename
|
|
17
|
+
filename=$(basename "$FILE_PATH")
|
|
18
|
+
[[ "$FILE_PATH" == */src/entities/* ]] \
|
|
19
|
+
&& [[ "$filename" == *.gateway.ts ]]
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if ! is_gateway_port; then
|
|
23
|
+
exit 0
|
|
24
|
+
fi
|
|
25
|
+
|
|
26
|
+
CONTENT=$(echo "$INPUT" | "$HOOK_DIR/parse-json.sh" tool_input.content)
|
|
27
|
+
NEW_STRING=$(echo "$INPUT" | "$HOOK_DIR/parse-json.sh" tool_input.new_string)
|
|
28
|
+
TEXT_TO_CHECK="${CONTENT}${NEW_STRING}"
|
|
29
|
+
|
|
30
|
+
if echo "$TEXT_TO_CHECK" | grep -qE "^\s*export\s+class\s+"; then
|
|
31
|
+
echo "Gateway port violation in $FILE_PATH: port files in entities/ must use 'interface' or 'abstract class'. A plain 'export class' belongs in interface-adapters/gateways/, not in the entities layer." >&2
|
|
32
|
+
exit 2
|
|
33
|
+
fi
|
|
34
|
+
|
|
35
|
+
exit 0
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
# Enforces that presenter files export a class named *Presenter or *Calculator.
|
|
5
|
+
# Applies to *.presenter.ts files under src/interface-adapters/presenters/.
|
|
6
|
+
# Functions or plain objects are not allowed — presenters must be classes for testability.
|
|
7
|
+
# Used as PreToolUse hook on Write (full file content only).
|
|
8
|
+
# Exit 0 = allow, Exit 2 = block with feedback to Claude.
|
|
9
|
+
|
|
10
|
+
HOOK_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
11
|
+
INPUT=$(cat)
|
|
12
|
+
FILE_PATH=$(echo "$INPUT" | "$HOOK_DIR/parse-json.sh" tool_input.file_path)
|
|
13
|
+
|
|
14
|
+
is_presenter_file() {
|
|
15
|
+
[[ "$FILE_PATH" == *.presenter.ts ]] \
|
|
16
|
+
&& [[ "$FILE_PATH" == */interface-adapters/presenters/* ]]
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
if ! is_presenter_file; then
|
|
20
|
+
exit 0
|
|
21
|
+
fi
|
|
22
|
+
|
|
23
|
+
CONTENT=$(echo "$INPUT" | "$HOOK_DIR/parse-json.sh" tool_input.content)
|
|
24
|
+
|
|
25
|
+
if [[ -z "$CONTENT" ]]; then
|
|
26
|
+
exit 0
|
|
27
|
+
fi
|
|
28
|
+
|
|
29
|
+
if ! echo "$CONTENT" | grep -qE "class\s+\w*(Presenter|Calculator)\b"; then
|
|
30
|
+
echo "Presenter convention violation in $FILE_PATH: .presenter.ts files must export a class ending with 'Presenter' (or 'Calculator' for pure computation presenters). Export a function is not allowed." >&2
|
|
31
|
+
exit 2
|
|
32
|
+
fi
|
|
33
|
+
|
|
34
|
+
exit 0
|