@mmnto/cli 0.17.0 → 0.19.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 (73) hide show
  1. package/dist/commands/briefing.js +1 -1
  2. package/dist/commands/briefing.js.map +1 -1
  3. package/dist/commands/compile.d.ts +9 -0
  4. package/dist/commands/compile.d.ts.map +1 -0
  5. package/dist/commands/compile.js +167 -0
  6. package/dist/commands/compile.js.map +1 -0
  7. package/dist/commands/docs.d.ts +5 -0
  8. package/dist/commands/docs.d.ts.map +1 -1
  9. package/dist/commands/docs.js +40 -3
  10. package/dist/commands/docs.js.map +1 -1
  11. package/dist/commands/docs.test.js +39 -3
  12. package/dist/commands/docs.test.js.map +1 -1
  13. package/dist/commands/extract.js +1 -1
  14. package/dist/commands/extract.js.map +1 -1
  15. package/dist/commands/handoff.js +1 -1
  16. package/dist/commands/handoff.js.map +1 -1
  17. package/dist/commands/shield.d.ts +1 -0
  18. package/dist/commands/shield.d.ts.map +1 -1
  19. package/dist/commands/shield.js +57 -2
  20. package/dist/commands/shield.js.map +1 -1
  21. package/dist/commands/shield.test.js +86 -1
  22. package/dist/commands/shield.test.js.map +1 -1
  23. package/dist/commands/spec.js +1 -1
  24. package/dist/commands/spec.js.map +1 -1
  25. package/dist/commands/triage.js +1 -1
  26. package/dist/commands/triage.js.map +1 -1
  27. package/dist/git.d.ts.map +1 -1
  28. package/dist/git.js +35 -24
  29. package/dist/git.js.map +1 -1
  30. package/dist/index.js +18 -0
  31. package/dist/index.js.map +1 -1
  32. package/dist/orchestrators/anthropic-orchestrator.d.ts +9 -0
  33. package/dist/orchestrators/anthropic-orchestrator.d.ts.map +1 -0
  34. package/dist/orchestrators/anthropic-orchestrator.js +62 -0
  35. package/dist/orchestrators/anthropic-orchestrator.js.map +1 -0
  36. package/dist/orchestrators/anthropic-orchestrator.test.d.ts +2 -0
  37. package/dist/orchestrators/anthropic-orchestrator.test.d.ts.map +1 -0
  38. package/dist/orchestrators/anthropic-orchestrator.test.js +109 -0
  39. package/dist/orchestrators/anthropic-orchestrator.test.js.map +1 -0
  40. package/dist/orchestrators/gemini-orchestrator.d.ts +9 -0
  41. package/dist/orchestrators/gemini-orchestrator.d.ts.map +1 -0
  42. package/dist/orchestrators/gemini-orchestrator.js +58 -0
  43. package/dist/orchestrators/gemini-orchestrator.js.map +1 -0
  44. package/dist/orchestrators/gemini-orchestrator.test.d.ts +2 -0
  45. package/dist/orchestrators/gemini-orchestrator.test.d.ts.map +1 -0
  46. package/dist/orchestrators/gemini-orchestrator.test.js +120 -0
  47. package/dist/orchestrators/gemini-orchestrator.test.js.map +1 -0
  48. package/dist/orchestrators/orchestrator.d.ts +33 -0
  49. package/dist/orchestrators/orchestrator.d.ts.map +1 -0
  50. package/dist/orchestrators/orchestrator.js +54 -0
  51. package/dist/orchestrators/orchestrator.js.map +1 -0
  52. package/dist/orchestrators/orchestrator.test.d.ts.map +1 -0
  53. package/dist/orchestrators/orchestrator.test.js +128 -0
  54. package/dist/orchestrators/orchestrator.test.js.map +1 -0
  55. package/dist/orchestrators/shell-orchestrator.d.ts +16 -0
  56. package/dist/orchestrators/shell-orchestrator.d.ts.map +1 -0
  57. package/dist/orchestrators/shell-orchestrator.js +131 -0
  58. package/dist/orchestrators/shell-orchestrator.js.map +1 -0
  59. package/dist/orchestrators/shell-orchestrator.test.d.ts +2 -0
  60. package/dist/orchestrators/shell-orchestrator.test.d.ts.map +1 -0
  61. package/dist/orchestrators/shell-orchestrator.test.js +267 -0
  62. package/dist/orchestrators/shell-orchestrator.test.js.map +1 -0
  63. package/dist/utils.d.ts +2 -19
  64. package/dist/utils.d.ts.map +1 -1
  65. package/dist/utils.js +6 -114
  66. package/dist/utils.js.map +1 -1
  67. package/dist/utils.test.js +1 -82
  68. package/dist/utils.test.js.map +1 -1
  69. package/package.json +16 -2
  70. package/dist/orchestrator.test.d.ts.map +0 -1
  71. package/dist/orchestrator.test.js +0 -108
  72. package/dist/orchestrator.test.js.map +0 -1
  73. /package/dist/{orchestrator.test.d.ts → orchestrators/orchestrator.test.d.ts} +0 -0
@@ -0,0 +1,109 @@
1
+ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
2
+ import { invokeAnthropicOrchestrator } from './anthropic-orchestrator.js';
3
+ // ─── Mock SDK ───────────────────────────────────────
4
+ const { mockCreate } = vi.hoisted(() => ({
5
+ mockCreate: vi.fn(),
6
+ }));
7
+ vi.mock('@anthropic-ai/sdk', () => ({
8
+ default: class {
9
+ messages = { create: mockCreate };
10
+ },
11
+ }));
12
+ vi.mock('../ui.js', () => ({
13
+ log: { info: vi.fn(), success: vi.fn(), warn: vi.fn(), error: vi.fn(), dim: vi.fn() },
14
+ }));
15
+ // ─── Tests ──────────────────────────────────────────
16
+ describe('invokeAnthropicOrchestrator', () => {
17
+ const baseOpts = {
18
+ prompt: 'test prompt',
19
+ model: 'claude-sonnet-4-5-20250514',
20
+ cwd: '.',
21
+ tag: 'Test',
22
+ totemDir: '.totem',
23
+ };
24
+ beforeEach(() => {
25
+ vi.clearAllMocks();
26
+ process.env['ANTHROPIC_API_KEY'] = 'test-key';
27
+ });
28
+ afterEach(() => {
29
+ delete process.env['ANTHROPIC_API_KEY'];
30
+ });
31
+ it('returns structured result from Anthropic API', async () => {
32
+ mockCreate.mockResolvedValueOnce({
33
+ content: [{ type: 'text', text: 'Hello from Claude' }],
34
+ usage: { input_tokens: 200, output_tokens: 75 },
35
+ stop_reason: 'end_turn',
36
+ });
37
+ const result = await invokeAnthropicOrchestrator(baseOpts);
38
+ expect(result.content).toBe('Hello from Claude');
39
+ expect(result.inputTokens).toBe(200);
40
+ expect(result.outputTokens).toBe(75);
41
+ expect(result.finishReason).toBe('end_turn');
42
+ expect(result.durationMs).toBeGreaterThanOrEqual(0);
43
+ });
44
+ it('passes model and prompt to the SDK', async () => {
45
+ mockCreate.mockResolvedValueOnce({
46
+ content: [{ type: 'text', text: 'ok' }],
47
+ usage: { input_tokens: 10, output_tokens: 5 },
48
+ stop_reason: 'end_turn',
49
+ });
50
+ await invokeAnthropicOrchestrator(baseOpts);
51
+ expect(mockCreate).toHaveBeenCalledWith(expect.objectContaining({
52
+ model: 'claude-sonnet-4-5-20250514',
53
+ messages: [{ role: 'user', content: 'test prompt' }],
54
+ }));
55
+ });
56
+ it('throws when ANTHROPIC_API_KEY is not set', async () => {
57
+ delete process.env['ANTHROPIC_API_KEY'];
58
+ await expect(invokeAnthropicOrchestrator(baseOpts)).rejects.toThrow('No Anthropic API key found');
59
+ });
60
+ it('converts 429 errors to QuotaError', async () => {
61
+ const rateLimitErr = Object.assign(new Error('rate_limit_error'), { status: 429 });
62
+ mockCreate.mockRejectedValueOnce(rateLimitErr);
63
+ try {
64
+ await invokeAnthropicOrchestrator(baseOpts);
65
+ expect.fail('Should have thrown');
66
+ }
67
+ catch (err) {
68
+ expect(err.name).toBe('QuotaError');
69
+ }
70
+ });
71
+ it('wraps other API errors with [Totem Error] prefix', async () => {
72
+ mockCreate.mockRejectedValueOnce(new Error('invalid_api_key'));
73
+ await expect(invokeAnthropicOrchestrator(baseOpts)).rejects.toThrow('[Totem Error] Anthropic API call failed');
74
+ });
75
+ it('joins multiple text blocks', async () => {
76
+ mockCreate.mockResolvedValueOnce({
77
+ content: [
78
+ { type: 'text', text: 'Part one' },
79
+ { type: 'text', text: 'Part two' },
80
+ ],
81
+ usage: { input_tokens: 100, output_tokens: 50 },
82
+ stop_reason: 'end_turn',
83
+ });
84
+ const result = await invokeAnthropicOrchestrator(baseOpts);
85
+ expect(result.content).toBe('Part one\nPart two');
86
+ });
87
+ it('skips non-text content blocks', async () => {
88
+ mockCreate.mockResolvedValueOnce({
89
+ content: [
90
+ { type: 'tool_use', id: 'x', name: 'foo', input: {} },
91
+ { type: 'text', text: 'Only text' },
92
+ ],
93
+ usage: { input_tokens: 100, output_tokens: 50 },
94
+ stop_reason: 'end_turn',
95
+ });
96
+ const result = await invokeAnthropicOrchestrator(baseOpts);
97
+ expect(result.content).toBe('Only text');
98
+ });
99
+ it('handles null stop_reason', async () => {
100
+ mockCreate.mockResolvedValueOnce({
101
+ content: [{ type: 'text', text: 'response' }],
102
+ usage: { input_tokens: 100, output_tokens: 50 },
103
+ stop_reason: null,
104
+ });
105
+ const result = await invokeAnthropicOrchestrator(baseOpts);
106
+ expect(result.finishReason).toBeUndefined();
107
+ });
108
+ });
109
+ //# sourceMappingURL=anthropic-orchestrator.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"anthropic-orchestrator.test.js","sourceRoot":"","sources":["../../src/orchestrators/anthropic-orchestrator.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ import type { OrchestratorInvokeOptions, OrchestratorResult } from './orchestrator.js';
2
+ /**
3
+ * Invoke the Gemini API via the `@google/genai` SDK.
4
+ * Requires: `pnpm add @google/genai` and `GEMINI_API_KEY` env var.
5
+ *
6
+ * @see https://github.com/mmnto-ai/totem/issues/231
7
+ */
8
+ export declare function invokeGeminiOrchestrator(opts: OrchestratorInvokeOptions): Promise<OrchestratorResult>;
9
+ //# sourceMappingURL=gemini-orchestrator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gemini-orchestrator.d.ts","sourceRoot":"","sources":["../../src/orchestrators/gemini-orchestrator.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,yBAAyB,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAuBvF;;;;;GAKG;AACH,wBAAsB,wBAAwB,CAC5C,IAAI,EAAE,yBAAyB,GAC9B,OAAO,CAAC,kBAAkB,CAAC,CAwC7B"}
@@ -0,0 +1,58 @@
1
+ import { log } from '../ui.js';
2
+ import { detectPackageManager, isQuotaError } from './orchestrator.js';
3
+ // ─── Constants ───────────────────────────────────────
4
+ const DEFAULT_MAX_OUTPUT_TOKENS = 16_384;
5
+ // ─── SDK loader (BYOSD) ─────────────────────────────
6
+ async function importGeminiSdk() {
7
+ try {
8
+ return await import('@google/genai');
9
+ }
10
+ catch {
11
+ throw new Error('[Totem Error] Gemini SDK (@google/genai) is not installed.\n' +
12
+ `Install it with: ${detectPackageManager()} add @google/genai\n` +
13
+ "Or use provider: 'shell' in your orchestrator config.");
14
+ }
15
+ }
16
+ // ─── Gemini API orchestrator ─────────────────────────
17
+ /**
18
+ * Invoke the Gemini API via the `@google/genai` SDK.
19
+ * Requires: `pnpm add @google/genai` and `GEMINI_API_KEY` env var.
20
+ *
21
+ * @see https://github.com/mmnto-ai/totem/issues/231
22
+ */
23
+ export async function invokeGeminiOrchestrator(opts) {
24
+ const { prompt, model, tag } = opts;
25
+ const apiKey = process.env['GEMINI_API_KEY'] ?? process.env['GOOGLE_API_KEY'];
26
+ if (!apiKey) {
27
+ throw new Error('[Totem Error] No Gemini API key found.\n' +
28
+ 'Set GEMINI_API_KEY (or GOOGLE_API_KEY) in your .env file.');
29
+ }
30
+ const { GoogleGenAI } = await importGeminiSdk();
31
+ const ai = new GoogleGenAI({ apiKey });
32
+ log.info(tag, 'Invoking Gemini API (this may take 15-60 seconds)...');
33
+ const startMs = Date.now();
34
+ try {
35
+ const response = await ai.models.generateContent({
36
+ model,
37
+ contents: prompt,
38
+ config: { maxOutputTokens: DEFAULT_MAX_OUTPUT_TOKENS },
39
+ });
40
+ const durationMs = Date.now() - startMs;
41
+ return {
42
+ content: response.text ?? '',
43
+ inputTokens: response.usageMetadata?.promptTokenCount ?? null,
44
+ outputTokens: response.usageMetadata?.candidatesTokenCount ?? null,
45
+ durationMs,
46
+ finishReason: response.candidates?.[0]?.finishReason ?? undefined,
47
+ };
48
+ }
49
+ catch (err) {
50
+ if (isQuotaError(err)) {
51
+ err.name = 'QuotaError';
52
+ throw err;
53
+ }
54
+ const msg = err instanceof Error ? err.message : String(err);
55
+ throw new Error(`[Totem Error] Gemini API call failed: ${msg}`);
56
+ }
57
+ }
58
+ //# sourceMappingURL=gemini-orchestrator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gemini-orchestrator.js","sourceRoot":"","sources":["../../src/orchestrators/gemini-orchestrator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAE/B,OAAO,EAAE,oBAAoB,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAEvE,wDAAwD;AAExD,MAAM,yBAAyB,GAAG,MAAM,CAAC;AAEzC,uDAAuD;AAEvD,KAAK,UAAU,eAAe;IAC5B,IAAI,CAAC;QACH,OAAO,MAAM,MAAM,CAAC,eAAe,CAAC,CAAC;IACvC,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,KAAK,CACb,8DAA8D;YAC5D,oBAAoB,oBAAoB,EAAE,sBAAsB;YAChE,uDAAuD,CAC1D,CAAC;IACJ,CAAC;AACH,CAAC;AAED,wDAAwD;AAExD;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,wBAAwB,CAC5C,IAA+B;IAE/B,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;IAEpC,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,IAAI,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC;IAC9E,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CACb,0CAA0C;YACxC,2DAA2D,CAC9D,CAAC;IACJ,CAAC;IAED,MAAM,EAAE,WAAW,EAAE,GAAG,MAAM,eAAe,EAAE,CAAC;IAChD,MAAM,EAAE,GAAG,IAAI,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC;IAEvC,GAAG,CAAC,IAAI,CAAC,GAAG,EAAE,sDAAsD,CAAC,CAAC;IACtE,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IAE3B,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,MAAM,EAAE,CAAC,MAAM,CAAC,eAAe,CAAC;YAC/C,KAAK;YACL,QAAQ,EAAE,MAAM;YAChB,MAAM,EAAE,EAAE,eAAe,EAAE,yBAAyB,EAAE;SACvD,CAAC,CAAC;QAEH,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,OAAO,CAAC;QACxC,OAAO;YACL,OAAO,EAAE,QAAQ,CAAC,IAAI,IAAI,EAAE;YAC5B,WAAW,EAAE,QAAQ,CAAC,aAAa,EAAE,gBAAgB,IAAI,IAAI;YAC7D,YAAY,EAAE,QAAQ,CAAC,aAAa,EAAE,oBAAoB,IAAI,IAAI;YAClE,UAAU;YACV,YAAY,EAAE,QAAQ,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,YAAY,IAAI,SAAS;SAClE,CAAC;IACJ,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAI,YAAY,CAAC,GAAG,CAAC,EAAE,CAAC;YACrB,GAAa,CAAC,IAAI,GAAG,YAAY,CAAC;YACnC,MAAM,GAAG,CAAC;QACZ,CAAC;QACD,MAAM,GAAG,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QAC7D,MAAM,IAAI,KAAK,CAAC,yCAAyC,GAAG,EAAE,CAAC,CAAC;IAClE,CAAC;AACH,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=gemini-orchestrator.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gemini-orchestrator.test.d.ts","sourceRoot":"","sources":["../../src/orchestrators/gemini-orchestrator.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,120 @@
1
+ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
2
+ import { invokeGeminiOrchestrator } from './gemini-orchestrator.js';
3
+ // ─── Mock SDK ───────────────────────────────────────
4
+ const { mockGenerateContent } = vi.hoisted(() => ({
5
+ mockGenerateContent: vi.fn(),
6
+ }));
7
+ vi.mock('@google/genai', () => ({
8
+ GoogleGenAI: class {
9
+ models = { generateContent: mockGenerateContent };
10
+ },
11
+ }));
12
+ vi.mock('../ui.js', () => ({
13
+ log: { info: vi.fn(), success: vi.fn(), warn: vi.fn(), error: vi.fn(), dim: vi.fn() },
14
+ }));
15
+ // ─── Tests ──────────────────────────────────────────
16
+ describe('invokeGeminiOrchestrator', () => {
17
+ const baseOpts = {
18
+ prompt: 'test prompt',
19
+ model: 'gemini-2.5-flash',
20
+ cwd: '.',
21
+ tag: 'Test',
22
+ totemDir: '.totem',
23
+ };
24
+ beforeEach(() => {
25
+ vi.clearAllMocks();
26
+ process.env['GEMINI_API_KEY'] = 'test-key';
27
+ });
28
+ afterEach(() => {
29
+ delete process.env['GEMINI_API_KEY'];
30
+ delete process.env['GOOGLE_API_KEY'];
31
+ });
32
+ it('returns structured result from Gemini API', async () => {
33
+ mockGenerateContent.mockResolvedValueOnce({
34
+ text: 'Hello from Gemini',
35
+ usageMetadata: { promptTokenCount: 100, candidatesTokenCount: 50 },
36
+ candidates: [{ finishReason: 'STOP' }],
37
+ });
38
+ const result = await invokeGeminiOrchestrator(baseOpts);
39
+ expect(result.content).toBe('Hello from Gemini');
40
+ expect(result.inputTokens).toBe(100);
41
+ expect(result.outputTokens).toBe(50);
42
+ expect(result.finishReason).toBe('STOP');
43
+ expect(result.durationMs).toBeGreaterThanOrEqual(0);
44
+ });
45
+ it('passes model and prompt to the SDK', async () => {
46
+ mockGenerateContent.mockResolvedValueOnce({
47
+ text: 'ok',
48
+ usageMetadata: {},
49
+ candidates: [],
50
+ });
51
+ await invokeGeminiOrchestrator(baseOpts);
52
+ expect(mockGenerateContent).toHaveBeenCalledWith(expect.objectContaining({
53
+ model: 'gemini-2.5-flash',
54
+ contents: 'test prompt',
55
+ }));
56
+ });
57
+ it('falls back to GOOGLE_API_KEY when GEMINI_API_KEY is not set', async () => {
58
+ delete process.env['GEMINI_API_KEY'];
59
+ process.env['GOOGLE_API_KEY'] = 'google-key';
60
+ mockGenerateContent.mockResolvedValueOnce({
61
+ text: 'ok',
62
+ usageMetadata: {},
63
+ candidates: [],
64
+ });
65
+ // Should not throw — GOOGLE_API_KEY is accepted
66
+ await invokeGeminiOrchestrator(baseOpts);
67
+ });
68
+ it('throws when no API key is set', async () => {
69
+ delete process.env['GEMINI_API_KEY'];
70
+ delete process.env['GOOGLE_API_KEY'];
71
+ await expect(invokeGeminiOrchestrator(baseOpts)).rejects.toThrow('No Gemini API key found');
72
+ });
73
+ it('converts 429 status errors to QuotaError', async () => {
74
+ const rateLimitErr = Object.assign(new Error('Resource exhausted'), { status: 429 });
75
+ mockGenerateContent.mockRejectedValueOnce(rateLimitErr);
76
+ try {
77
+ await invokeGeminiOrchestrator(baseOpts);
78
+ expect.fail('Should have thrown');
79
+ }
80
+ catch (err) {
81
+ expect(err.name).toBe('QuotaError');
82
+ }
83
+ });
84
+ it('converts quota keyword errors to QuotaError', async () => {
85
+ mockGenerateContent.mockRejectedValueOnce(new Error('quota exceeded for model'));
86
+ try {
87
+ await invokeGeminiOrchestrator(baseOpts);
88
+ expect.fail('Should have thrown');
89
+ }
90
+ catch (err) {
91
+ expect(err.name).toBe('QuotaError');
92
+ }
93
+ });
94
+ it('wraps other API errors with [Totem Error] prefix', async () => {
95
+ mockGenerateContent.mockRejectedValueOnce(new Error('Model not found'));
96
+ await expect(invokeGeminiOrchestrator(baseOpts)).rejects.toThrow('[Totem Error] Gemini API call failed');
97
+ });
98
+ it('handles missing usageMetadata gracefully', async () => {
99
+ mockGenerateContent.mockResolvedValueOnce({
100
+ text: 'response',
101
+ candidates: [],
102
+ });
103
+ const result = await invokeGeminiOrchestrator(baseOpts);
104
+ expect(result.content).toBe('response');
105
+ expect(result.inputTokens).toBeNull();
106
+ expect(result.outputTokens).toBeNull();
107
+ expect(result.finishReason).toBeUndefined();
108
+ });
109
+ it('handles undefined text as empty string', async () => {
110
+ mockGenerateContent.mockResolvedValueOnce({
111
+ text: undefined,
112
+ usageMetadata: { promptTokenCount: 10, candidatesTokenCount: 0 },
113
+ candidates: [{ finishReason: 'SAFETY' }],
114
+ });
115
+ const result = await invokeGeminiOrchestrator(baseOpts);
116
+ expect(result.content).toBe('');
117
+ expect(result.finishReason).toBe('SAFETY');
118
+ });
119
+ });
120
+ //# sourceMappingURL=gemini-orchestrator.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gemini-orchestrator.test.js","sourceRoot":"","sources":["../../src/orchestrators/gemini-orchestrator.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,33 @@
1
+ import type { Orchestrator as OrchestratorConfig } from '@mmnto/totem';
2
+ export interface OrchestratorResult {
3
+ content: string;
4
+ inputTokens: number | null;
5
+ outputTokens: number | null;
6
+ durationMs: number;
7
+ finishReason?: string;
8
+ }
9
+ export interface OrchestratorInvokeOptions {
10
+ prompt: string;
11
+ model: string;
12
+ cwd: string;
13
+ tag: string;
14
+ totemDir: string;
15
+ }
16
+ /** A provider-bound function that invokes an LLM and returns the result. */
17
+ export type InvokeOrchestrator = (options: OrchestratorInvokeOptions) => Promise<OrchestratorResult>;
18
+ /**
19
+ * Detect the active package manager from the `npm_config_user_agent` env var
20
+ * (set by npm, pnpm, yarn, and bun when running scripts). Falls back to `npm`.
21
+ */
22
+ export declare function detectPackageManager(): string;
23
+ /**
24
+ * Detect whether an error is a rate-limit / quota-exhaustion response.
25
+ * Used by both Gemini and Anthropic orchestrators to normalize QuotaError.
26
+ */
27
+ export declare function isQuotaError(err: unknown): boolean;
28
+ /**
29
+ * Create an orchestrator invoker bound to the given provider config.
30
+ * Mirrors the `createEmbedder()` pattern from `packages/core/src/embedders/`.
31
+ */
32
+ export declare function createOrchestrator(config: OrchestratorConfig): InvokeOrchestrator;
33
+ //# sourceMappingURL=orchestrator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"orchestrator.d.ts","sourceRoot":"","sources":["../../src/orchestrators/orchestrator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,IAAI,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAMvE,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,yBAAyB;IACxC,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,4EAA4E;AAC5E,MAAM,MAAM,kBAAkB,GAAG,CAC/B,OAAO,EAAE,yBAAyB,KAC/B,OAAO,CAAC,kBAAkB,CAAC,CAAC;AAIjC;;;GAGG;AACH,wBAAgB,oBAAoB,IAAI,MAAM,CAM7C;AAID;;;GAGG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAUlD;AAID;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,kBAAkB,GAAG,kBAAkB,CAejF"}
@@ -0,0 +1,54 @@
1
+ import { invokeShellOrchestrator } from './shell-orchestrator.js';
2
+ // ─── Package manager detection (#236) ───────────────
3
+ /**
4
+ * Detect the active package manager from the `npm_config_user_agent` env var
5
+ * (set by npm, pnpm, yarn, and bun when running scripts). Falls back to `npm`.
6
+ */
7
+ export function detectPackageManager() {
8
+ const ua = process.env['npm_config_user_agent'] ?? '';
9
+ if (ua.startsWith('pnpm'))
10
+ return 'pnpm';
11
+ if (ua.startsWith('yarn'))
12
+ return 'yarn';
13
+ if (ua.startsWith('bun'))
14
+ return 'bun';
15
+ return 'npm';
16
+ }
17
+ // ─── Quota detection (shared) ────────────────────────
18
+ /**
19
+ * Detect whether an error is a rate-limit / quota-exhaustion response.
20
+ * Used by both Gemini and Anthropic orchestrators to normalize QuotaError.
21
+ */
22
+ export function isQuotaError(err) {
23
+ if (!(err instanceof Error))
24
+ return false;
25
+ if ('status' in err && err.status === 429)
26
+ return true;
27
+ const msg = err.message.toLowerCase();
28
+ return (msg.includes('429') ||
29
+ msg.includes('quota') ||
30
+ msg.includes('rate limit') ||
31
+ msg.includes('too many requests'));
32
+ }
33
+ // ─── Factory ─────────────────────────────────────────
34
+ /**
35
+ * Create an orchestrator invoker bound to the given provider config.
36
+ * Mirrors the `createEmbedder()` pattern from `packages/core/src/embedders/`.
37
+ */
38
+ export function createOrchestrator(config) {
39
+ switch (config.provider) {
40
+ case 'shell':
41
+ return (opts) => invokeShellOrchestrator({ ...opts, command: config.command });
42
+ case 'gemini':
43
+ return async (opts) => {
44
+ const { invokeGeminiOrchestrator } = await import('./gemini-orchestrator.js');
45
+ return invokeGeminiOrchestrator(opts);
46
+ };
47
+ case 'anthropic':
48
+ return async (opts) => {
49
+ const { invokeAnthropicOrchestrator } = await import('./anthropic-orchestrator.js');
50
+ return invokeAnthropicOrchestrator(opts);
51
+ };
52
+ }
53
+ }
54
+ //# sourceMappingURL=orchestrator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"orchestrator.js","sourceRoot":"","sources":["../../src/orchestrators/orchestrator.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,uBAAuB,EAAE,MAAM,yBAAyB,CAAC;AAyBlE,uDAAuD;AAEvD;;;GAGG;AACH,MAAM,UAAU,oBAAoB;IAClC,MAAM,EAAE,GAAG,OAAO,CAAC,GAAG,CAAC,uBAAuB,CAAC,IAAI,EAAE,CAAC;IACtD,IAAI,EAAE,CAAC,UAAU,CAAC,MAAM,CAAC;QAAE,OAAO,MAAM,CAAC;IACzC,IAAI,EAAE,CAAC,UAAU,CAAC,MAAM,CAAC;QAAE,OAAO,MAAM,CAAC;IACzC,IAAI,EAAE,CAAC,UAAU,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IACvC,OAAO,KAAK,CAAC;AACf,CAAC;AAED,wDAAwD;AAExD;;;GAGG;AACH,MAAM,UAAU,YAAY,CAAC,GAAY;IACvC,IAAI,CAAC,CAAC,GAAG,YAAY,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IAC1C,IAAI,QAAQ,IAAI,GAAG,IAAK,GAA+B,CAAC,MAAM,KAAK,GAAG;QAAE,OAAO,IAAI,CAAC;IACpF,MAAM,GAAG,GAAG,GAAG,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC;IACtC,OAAO,CACL,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC;QACnB,GAAG,CAAC,QAAQ,CAAC,OAAO,CAAC;QACrB,GAAG,CAAC,QAAQ,CAAC,YAAY,CAAC;QAC1B,GAAG,CAAC,QAAQ,CAAC,mBAAmB,CAAC,CAClC,CAAC;AACJ,CAAC;AAED,wDAAwD;AAExD;;;GAGG;AACH,MAAM,UAAU,kBAAkB,CAAC,MAA0B;IAC3D,QAAQ,MAAM,CAAC,QAAQ,EAAE,CAAC;QACxB,KAAK,OAAO;YACV,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,uBAAuB,CAAC,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC;QACjF,KAAK,QAAQ;YACX,OAAO,KAAK,EAAE,IAAI,EAAE,EAAE;gBACpB,MAAM,EAAE,wBAAwB,EAAE,GAAG,MAAM,MAAM,CAAC,0BAA0B,CAAC,CAAC;gBAC9E,OAAO,wBAAwB,CAAC,IAAI,CAAC,CAAC;YACxC,CAAC,CAAC;QACJ,KAAK,WAAW;YACd,OAAO,KAAK,EAAE,IAAI,EAAE,EAAE;gBACpB,MAAM,EAAE,2BAA2B,EAAE,GAAG,MAAM,MAAM,CAAC,6BAA6B,CAAC,CAAC;gBACpF,OAAO,2BAA2B,CAAC,IAAI,CAAC,CAAC;YAC3C,CAAC,CAAC;IACN,CAAC;AACH,CAAC"}
@@ -0,0 +1 @@
1
+ {"version":3,"file":"orchestrator.test.d.ts","sourceRoot":"","sources":["../../src/orchestrators/orchestrator.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,128 @@
1
+ import { afterEach, describe, expect, it, vi } from 'vitest';
2
+ import { createOrchestrator, detectPackageManager, isQuotaError } from './orchestrator.js';
3
+ // ─── Mock provider modules ──────────────────────────
4
+ vi.mock('./gemini-orchestrator.js', () => ({
5
+ invokeGeminiOrchestrator: vi.fn().mockResolvedValue({
6
+ content: 'gemini result',
7
+ inputTokens: 100,
8
+ outputTokens: 50,
9
+ durationMs: 1000,
10
+ }),
11
+ }));
12
+ vi.mock('./anthropic-orchestrator.js', () => ({
13
+ invokeAnthropicOrchestrator: vi.fn().mockResolvedValue({
14
+ content: 'anthropic result',
15
+ inputTokens: 200,
16
+ outputTokens: 75,
17
+ durationMs: 2000,
18
+ }),
19
+ }));
20
+ // ─── Tests ──────────────────────────────────────────
21
+ describe('createOrchestrator', () => {
22
+ it('returns a function for shell provider', () => {
23
+ const config = {
24
+ provider: 'shell',
25
+ command: 'echo {file}',
26
+ };
27
+ const invoke = createOrchestrator(config);
28
+ expect(typeof invoke).toBe('function');
29
+ });
30
+ it('returns a function for gemini provider', () => {
31
+ const config = {
32
+ provider: 'gemini',
33
+ defaultModel: 'gemini-2.5-flash',
34
+ };
35
+ const invoke = createOrchestrator(config);
36
+ expect(typeof invoke).toBe('function');
37
+ });
38
+ it('returns a function for anthropic provider', () => {
39
+ const config = {
40
+ provider: 'anthropic',
41
+ defaultModel: 'claude-sonnet-4-5-20250514',
42
+ };
43
+ const invoke = createOrchestrator(config);
44
+ expect(typeof invoke).toBe('function');
45
+ });
46
+ it('gemini invoker dispatches to gemini-orchestrator module', async () => {
47
+ const config = { provider: 'gemini' };
48
+ const invoke = createOrchestrator(config);
49
+ const result = await invoke({
50
+ prompt: 'test',
51
+ model: 'gemini-2.5-flash',
52
+ cwd: '.',
53
+ tag: 'Test',
54
+ totemDir: '.totem',
55
+ });
56
+ expect(result.content).toBe('gemini result');
57
+ expect(result.inputTokens).toBe(100);
58
+ });
59
+ it('anthropic invoker dispatches to anthropic-orchestrator module', async () => {
60
+ const config = { provider: 'anthropic' };
61
+ const invoke = createOrchestrator(config);
62
+ const result = await invoke({
63
+ prompt: 'test',
64
+ model: 'claude-sonnet-4-5-20250514',
65
+ cwd: '.',
66
+ tag: 'Test',
67
+ totemDir: '.totem',
68
+ });
69
+ expect(result.content).toBe('anthropic result');
70
+ expect(result.inputTokens).toBe(200);
71
+ });
72
+ });
73
+ // ─── detectPackageManager ───────────────────────────
74
+ describe('detectPackageManager', () => {
75
+ const originalUa = process.env['npm_config_user_agent'];
76
+ afterEach(() => {
77
+ if (originalUa !== undefined) {
78
+ process.env['npm_config_user_agent'] = originalUa;
79
+ }
80
+ else {
81
+ delete process.env['npm_config_user_agent'];
82
+ }
83
+ });
84
+ it('detects pnpm', () => {
85
+ process.env['npm_config_user_agent'] = 'pnpm/9.15.0 npm/? node/v22.0.0';
86
+ expect(detectPackageManager()).toBe('pnpm');
87
+ });
88
+ it('detects yarn', () => {
89
+ process.env['npm_config_user_agent'] = 'yarn/4.0.0 npm/? node/v22.0.0';
90
+ expect(detectPackageManager()).toBe('yarn');
91
+ });
92
+ it('detects bun', () => {
93
+ process.env['npm_config_user_agent'] = 'bun/1.0.0';
94
+ expect(detectPackageManager()).toBe('bun');
95
+ });
96
+ it('defaults to npm when env var is missing', () => {
97
+ delete process.env['npm_config_user_agent'];
98
+ expect(detectPackageManager()).toBe('npm');
99
+ });
100
+ it('defaults to npm for unknown user agent', () => {
101
+ process.env['npm_config_user_agent'] = 'npm/10.0.0 node/v22.0.0';
102
+ expect(detectPackageManager()).toBe('npm');
103
+ });
104
+ });
105
+ // ─── isQuotaError ───────────────────────────────────
106
+ describe('isQuotaError', () => {
107
+ it('detects 429 status errors', () => {
108
+ const err = Object.assign(new Error('rate limit'), { status: 429 });
109
+ expect(isQuotaError(err)).toBe(true);
110
+ });
111
+ it('detects quota keyword in message', () => {
112
+ expect(isQuotaError(new Error('quota exceeded'))).toBe(true);
113
+ });
114
+ it('detects rate limit keyword in message', () => {
115
+ expect(isQuotaError(new Error('rate limit reached'))).toBe(true);
116
+ });
117
+ it('detects too many requests keyword', () => {
118
+ expect(isQuotaError(new Error('Too Many Requests'))).toBe(true);
119
+ });
120
+ it('returns false for non-quota errors', () => {
121
+ expect(isQuotaError(new Error('Model not found'))).toBe(false);
122
+ });
123
+ it('returns false for non-Error values', () => {
124
+ expect(isQuotaError('some string')).toBe(false);
125
+ expect(isQuotaError(null)).toBe(false);
126
+ });
127
+ });
128
+ //# sourceMappingURL=orchestrator.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"orchestrator.test.js","sourceRoot":"","sources":["../../src/orchestrators/orchestrator.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ import type { OrchestratorInvokeOptions, OrchestratorResult } from './orchestrator.js';
2
+ /**
3
+ * Try to parse Gemini CLI JSON output. Returns extracted data or null if
4
+ * the output is not valid Gemini JSON (e.g. raw text from a non-Gemini orchestrator).
5
+ */
6
+ export declare function tryParseGeminiJson(raw: string): {
7
+ content: string;
8
+ inputTokens: number;
9
+ outputTokens: number;
10
+ latencyMs: number | null;
11
+ } | null;
12
+ export interface ShellOrchestratorOptions extends OrchestratorInvokeOptions {
13
+ command: string;
14
+ }
15
+ export declare function invokeShellOrchestrator(opts: ShellOrchestratorOptions): Promise<OrchestratorResult>;
16
+ //# sourceMappingURL=shell-orchestrator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shell-orchestrator.d.ts","sourceRoot":"","sources":["../../src/orchestrators/shell-orchestrator.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,yBAAyB,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAwBvF;;;GAGG;AACH,wBAAgB,kBAAkB,CAChC,GAAG,EAAE,MAAM,GACV;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,GAAG,IAAI,CAwBjG;AAID,MAAM,WAAW,wBAAyB,SAAQ,yBAAyB;IACzE,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,wBAAsB,uBAAuB,CAC3C,IAAI,EAAE,wBAAwB,GAC7B,OAAO,CAAC,kBAAkB,CAAC,CAoG7B"}