@mmnto/cli 0.6.0 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adapters/gh-utils.d.ts +12 -0
- package/dist/adapters/gh-utils.d.ts.map +1 -0
- package/dist/adapters/gh-utils.js +53 -0
- package/dist/adapters/gh-utils.js.map +1 -0
- package/dist/adapters/gh-utils.test.d.ts +2 -0
- package/dist/adapters/gh-utils.test.d.ts.map +1 -0
- package/dist/adapters/gh-utils.test.js +81 -0
- package/dist/adapters/gh-utils.test.js.map +1 -0
- package/dist/adapters/github-cli-pr.d.ts +10 -0
- package/dist/adapters/github-cli-pr.d.ts.map +1 -0
- package/dist/adapters/github-cli-pr.js +83 -0
- package/dist/adapters/github-cli-pr.js.map +1 -0
- package/dist/adapters/github-cli-pr.test.d.ts +2 -0
- package/dist/adapters/github-cli-pr.test.d.ts.map +1 -0
- package/dist/adapters/github-cli-pr.test.js +108 -0
- package/dist/adapters/github-cli-pr.test.js.map +1 -0
- package/dist/adapters/github-cli.d.ts +0 -1
- package/dist/adapters/github-cli.d.ts.map +1 -1
- package/dist/adapters/github-cli.js +4 -40
- package/dist/adapters/github-cli.js.map +1 -1
- package/dist/adapters/github-cli.test.d.ts +2 -0
- package/dist/adapters/github-cli.test.d.ts.map +1 -0
- package/dist/adapters/github-cli.test.js +87 -0
- package/dist/adapters/github-cli.test.js.map +1 -0
- package/dist/adapters/pr-adapter.d.ts +41 -0
- package/dist/adapters/pr-adapter.d.ts.map +1 -0
- package/dist/adapters/pr-adapter.js +6 -0
- package/dist/adapters/pr-adapter.js.map +1 -0
- package/dist/commands/anchor.d.ts.map +1 -1
- package/dist/commands/anchor.js +6 -4
- package/dist/commands/anchor.js.map +1 -1
- package/dist/commands/briefing.d.ts +2 -0
- package/dist/commands/briefing.d.ts.map +1 -1
- package/dist/commands/briefing.js +19 -38
- package/dist/commands/briefing.js.map +1 -1
- package/dist/commands/briefing.test.d.ts +2 -0
- package/dist/commands/briefing.test.d.ts.map +1 -0
- package/dist/commands/briefing.test.js +26 -0
- package/dist/commands/briefing.test.js.map +1 -0
- package/dist/commands/handoff.d.ts +1 -0
- package/dist/commands/handoff.d.ts.map +1 -1
- package/dist/commands/handoff.js +17 -14
- package/dist/commands/handoff.js.map +1 -1
- package/dist/commands/handoff.test.d.ts +2 -0
- package/dist/commands/handoff.test.d.ts.map +1 -0
- package/dist/commands/handoff.test.js +35 -0
- package/dist/commands/handoff.test.js.map +1 -0
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +17 -15
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/learn.d.ts +12 -1
- package/dist/commands/learn.d.ts.map +1 -1
- package/dist/commands/learn.js +145 -141
- package/dist/commands/learn.js.map +1 -1
- package/dist/commands/learn.test.d.ts +2 -0
- package/dist/commands/learn.test.d.ts.map +1 -0
- package/dist/commands/learn.test.js +169 -0
- package/dist/commands/learn.test.js.map +1 -0
- package/dist/commands/search.d.ts.map +1 -1
- package/dist/commands/search.js +5 -4
- package/dist/commands/search.js.map +1 -1
- package/dist/commands/shield.d.ts.map +1 -1
- package/dist/commands/shield.js +21 -16
- package/dist/commands/shield.js.map +1 -1
- package/dist/commands/shield.test.js +4 -0
- package/dist/commands/shield.test.js.map +1 -1
- package/dist/commands/spec.d.ts +1 -1
- package/dist/commands/spec.d.ts.map +1 -1
- package/dist/commands/spec.js +56 -43
- package/dist/commands/spec.js.map +1 -1
- package/dist/commands/sync.d.ts.map +1 -1
- package/dist/commands/sync.js +15 -6
- package/dist/commands/sync.js.map +1 -1
- package/dist/commands/triage.d.ts +2 -0
- package/dist/commands/triage.d.ts.map +1 -1
- package/dist/commands/triage.js +15 -12
- package/dist/commands/triage.js.map +1 -1
- package/dist/commands/triage.test.d.ts +2 -0
- package/dist/commands/triage.test.d.ts.map +1 -0
- package/dist/commands/triage.test.js +33 -0
- package/dist/commands/triage.test.js.map +1 -0
- package/dist/index.js +9 -8
- package/dist/index.js.map +1 -1
- package/dist/orchestrator.test.d.ts +2 -0
- package/dist/orchestrator.test.d.ts.map +1 -0
- package/dist/orchestrator.test.js +90 -0
- package/dist/orchestrator.test.js.map +1 -0
- package/dist/ui.d.ts +26 -0
- package/dist/ui.d.ts.map +1 -0
- package/dist/ui.js +85 -0
- package/dist/ui.js.map +1 -0
- package/dist/ui.test.d.ts +2 -0
- package/dist/ui.test.d.ts.map +1 -0
- package/dist/ui.test.js +76 -0
- package/dist/ui.test.js.map +1 -0
- package/dist/utils.d.ts +16 -0
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +38 -9
- package/dist/utils.js.map +1 -1
- package/dist/utils.test.js +277 -2
- package/dist/utils.test.js.map +1 -1
- package/package.json +4 -2
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
/**
|
|
3
|
+
* Shared error handler for all GitHub CLI interactions.
|
|
4
|
+
* Re-throws [Totem Error] as-is, wraps ZodErrors and ENOENT, and
|
|
5
|
+
* falls through to a generic message for anything else.
|
|
6
|
+
*/
|
|
7
|
+
export declare function handleGhError(err: unknown, context: string): never;
|
|
8
|
+
/**
|
|
9
|
+
* Shared fetch → JSON.parse → Zod validate utility for all `gh` CLI calls.
|
|
10
|
+
*/
|
|
11
|
+
export declare function ghFetchAndParse<T>(args: string[], schema: z.ZodType<T>, context: string, cwd: string): T;
|
|
12
|
+
//# sourceMappingURL=gh-utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gh-utils.d.ts","sourceRoot":"","sources":["../../src/adapters/gh-utils.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAOxB;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,GAAG,KAAK,CAelE;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,CAAC,EAC/B,IAAI,EAAE,MAAM,EAAE,EACd,MAAM,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EACpB,OAAO,EAAE,MAAM,EACf,GAAG,EAAE,MAAM,GACV,CAAC,CAwBH"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { execFileSync } from 'node:child_process';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { GH_TIMEOUT_MS, IS_WIN } from '../utils.js';
|
|
4
|
+
const GH_MAX_BUFFER = 10 * 1024 * 1024; // 10MB — handles paginated API responses
|
|
5
|
+
const GH_PAGINATED_TIMEOUT_MS = 60_000; // 60s — paginated endpoints can be slow
|
|
6
|
+
/**
|
|
7
|
+
* Shared error handler for all GitHub CLI interactions.
|
|
8
|
+
* Re-throws [Totem Error] as-is, wraps ZodErrors and ENOENT, and
|
|
9
|
+
* falls through to a generic message for anything else.
|
|
10
|
+
*/
|
|
11
|
+
export function handleGhError(err, context) {
|
|
12
|
+
if (err instanceof Error && err.message.includes('[Totem Error]')) {
|
|
13
|
+
throw err;
|
|
14
|
+
}
|
|
15
|
+
if (err instanceof z.ZodError) {
|
|
16
|
+
throw new Error(`[Totem Error] Failed to parse GitHub ${context}`);
|
|
17
|
+
}
|
|
18
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
19
|
+
if (msg.includes('ENOENT')) {
|
|
20
|
+
throw new Error(`[Totem Error] GitHub CLI (gh) is required. Install: https://cli.github.com`);
|
|
21
|
+
}
|
|
22
|
+
if (/\b(403|429)\b/.test(msg) || /rate.limit/i.test(msg)) {
|
|
23
|
+
throw new Error(`[Totem Error] GitHub API rate limit exceeded. Try again later.`);
|
|
24
|
+
}
|
|
25
|
+
throw new Error(`[Totem Error] Failed to fetch ${context}: ${msg}`);
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Shared fetch → JSON.parse → Zod validate utility for all `gh` CLI calls.
|
|
29
|
+
*/
|
|
30
|
+
export function ghFetchAndParse(args, schema, context, cwd) {
|
|
31
|
+
const isPaginated = args.includes('--paginate');
|
|
32
|
+
try {
|
|
33
|
+
const raw = execFileSync('gh', args, {
|
|
34
|
+
cwd,
|
|
35
|
+
encoding: 'utf-8',
|
|
36
|
+
timeout: isPaginated ? GH_PAGINATED_TIMEOUT_MS : GH_TIMEOUT_MS,
|
|
37
|
+
shell: IS_WIN,
|
|
38
|
+
maxBuffer: GH_MAX_BUFFER,
|
|
39
|
+
});
|
|
40
|
+
let parsed;
|
|
41
|
+
try {
|
|
42
|
+
parsed = JSON.parse(raw);
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
throw new Error(`[Totem Error] GitHub CLI returned invalid JSON for ${context}. Run \`gh auth status\` to check your authentication.`);
|
|
46
|
+
}
|
|
47
|
+
return schema.parse(parsed);
|
|
48
|
+
}
|
|
49
|
+
catch (err) {
|
|
50
|
+
handleGhError(err, context);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=gh-utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gh-utils.js","sourceRoot":"","sources":["../../src/adapters/gh-utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAElD,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAEpD,MAAM,aAAa,GAAG,EAAE,GAAG,IAAI,GAAG,IAAI,CAAC,CAAC,yCAAyC;AACjF,MAAM,uBAAuB,GAAG,MAAM,CAAC,CAAC,wCAAwC;AAEhF;;;;GAIG;AACH,MAAM,UAAU,aAAa,CAAC,GAAY,EAAE,OAAe;IACzD,IAAI,GAAG,YAAY,KAAK,IAAI,GAAG,CAAC,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAC,EAAE,CAAC;QAClE,MAAM,GAAG,CAAC;IACZ,CAAC;IACD,IAAI,GAAG,YAAY,CAAC,CAAC,QAAQ,EAAE,CAAC;QAC9B,MAAM,IAAI,KAAK,CAAC,wCAAwC,OAAO,EAAE,CAAC,CAAC;IACrE,CAAC;IACD,MAAM,GAAG,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IAC7D,IAAI,GAAG,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC3B,MAAM,IAAI,KAAK,CAAC,4EAA4E,CAAC,CAAC;IAChG,CAAC;IACD,IAAI,eAAe,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,aAAa,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;QACzD,MAAM,IAAI,KAAK,CAAC,gEAAgE,CAAC,CAAC;IACpF,CAAC;IACD,MAAM,IAAI,KAAK,CAAC,iCAAiC,OAAO,KAAK,GAAG,EAAE,CAAC,CAAC;AACtE,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,eAAe,CAC7B,IAAc,EACd,MAAoB,EACpB,OAAe,EACf,GAAW;IAEX,MAAM,WAAW,GAAG,IAAI,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC;IAChD,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,YAAY,CAAC,IAAI,EAAE,IAAI,EAAE;YACnC,GAAG;YACH,QAAQ,EAAE,OAAO;YACjB,OAAO,EAAE,WAAW,CAAC,CAAC,CAAC,uBAAuB,CAAC,CAAC,CAAC,aAAa;YAC9D,KAAK,EAAE,MAAM;YACb,SAAS,EAAE,aAAa;SACzB,CAAC,CAAC;QAEH,IAAI,MAAe,CAAC;QACpB,IAAI,CAAC;YACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAC3B,CAAC;QAAC,MAAM,CAAC;YACP,MAAM,IAAI,KAAK,CACb,sDAAsD,OAAO,wDAAwD,CACtH,CAAC;QACJ,CAAC;QAED,OAAO,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IAC9B,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,aAAa,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;IAC9B,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gh-utils.test.d.ts","sourceRoot":"","sources":["../../src/adapters/gh-utils.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { execFileSync } from 'node:child_process';
|
|
2
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
3
|
+
import { z } from 'zod';
|
|
4
|
+
import { ghFetchAndParse, handleGhError } from './gh-utils.js';
|
|
5
|
+
vi.mock('node:child_process', () => ({
|
|
6
|
+
execFileSync: vi.fn(),
|
|
7
|
+
}));
|
|
8
|
+
const mockedExec = vi.mocked(execFileSync);
|
|
9
|
+
// ─── handleGhError ──────────────────────────────────────
|
|
10
|
+
describe('handleGhError', () => {
|
|
11
|
+
it('re-throws errors with [Totem Error] prefix as-is', () => {
|
|
12
|
+
const err = new Error('[Totem Error] Something went wrong');
|
|
13
|
+
expect(() => handleGhError(err, 'test')).toThrow('[Totem Error] Something went wrong');
|
|
14
|
+
});
|
|
15
|
+
it('wraps ZodErrors with context', () => {
|
|
16
|
+
const err = new z.ZodError([
|
|
17
|
+
{
|
|
18
|
+
code: 'invalid_type',
|
|
19
|
+
expected: 'string',
|
|
20
|
+
received: 'number',
|
|
21
|
+
path: ['title'],
|
|
22
|
+
message: 'Expected string',
|
|
23
|
+
},
|
|
24
|
+
]);
|
|
25
|
+
expect(() => handleGhError(err, 'issue #42')).toThrow('[Totem Error] Failed to parse GitHub issue #42');
|
|
26
|
+
});
|
|
27
|
+
it('detects ENOENT as missing gh CLI', () => {
|
|
28
|
+
const err = new Error('ENOENT');
|
|
29
|
+
expect(() => handleGhError(err, 'test')).toThrow('[Totem Error] GitHub CLI (gh) is required');
|
|
30
|
+
});
|
|
31
|
+
it('wraps unknown errors with context', () => {
|
|
32
|
+
const err = new Error('connection refused');
|
|
33
|
+
expect(() => handleGhError(err, 'open PRs')).toThrow('[Totem Error] Failed to fetch open PRs: connection refused');
|
|
34
|
+
});
|
|
35
|
+
it('handles non-Error values', () => {
|
|
36
|
+
expect(() => handleGhError('string error', 'test')).toThrow('[Totem Error] Failed to fetch test: string error');
|
|
37
|
+
});
|
|
38
|
+
it('detects 403 as rate limit error', () => {
|
|
39
|
+
const err = new Error('HTTP 403: rate limit exceeded');
|
|
40
|
+
expect(() => handleGhError(err, 'PRs')).toThrow('[Totem Error] GitHub API rate limit exceeded');
|
|
41
|
+
});
|
|
42
|
+
it('detects 429 as rate limit error', () => {
|
|
43
|
+
const err = new Error('HTTP 429: Too Many Requests');
|
|
44
|
+
expect(() => handleGhError(err, 'PRs')).toThrow('[Totem Error] GitHub API rate limit exceeded');
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
// ─── ghFetchAndParse ────────────────────────────────────
|
|
48
|
+
const TestSchema = z.object({ id: z.number(), name: z.string() });
|
|
49
|
+
describe('ghFetchAndParse', () => {
|
|
50
|
+
it('returns parsed and validated data on success', () => {
|
|
51
|
+
mockedExec.mockReturnValue(JSON.stringify({ id: 1, name: 'test' }));
|
|
52
|
+
const result = ghFetchAndParse(['test', 'cmd'], TestSchema, 'test item', '/cwd');
|
|
53
|
+
expect(result).toEqual({ id: 1, name: 'test' });
|
|
54
|
+
});
|
|
55
|
+
it('passes correct args to execFileSync', () => {
|
|
56
|
+
mockedExec.mockReturnValue(JSON.stringify({ id: 1, name: 'test' }));
|
|
57
|
+
ghFetchAndParse(['pr', 'list', '--state', 'open'], TestSchema, 'test', '/cwd');
|
|
58
|
+
expect(mockedExec).toHaveBeenCalledWith('gh', ['pr', 'list', '--state', 'open'], expect.objectContaining({ cwd: '/cwd', encoding: 'utf-8' }));
|
|
59
|
+
});
|
|
60
|
+
it('throws on invalid JSON', () => {
|
|
61
|
+
mockedExec.mockReturnValue('not json');
|
|
62
|
+
expect(() => ghFetchAndParse(['test'], TestSchema, 'test item', '/cwd')).toThrow('[Totem Error] GitHub CLI returned invalid JSON for test item');
|
|
63
|
+
});
|
|
64
|
+
it('throws on schema validation failure', () => {
|
|
65
|
+
mockedExec.mockReturnValue(JSON.stringify({ id: 'not-a-number', name: 'test' }));
|
|
66
|
+
expect(() => ghFetchAndParse(['test'], TestSchema, 'test item', '/cwd')).toThrow('[Totem Error] Failed to parse GitHub test item');
|
|
67
|
+
});
|
|
68
|
+
it('throws install message when gh is not found', () => {
|
|
69
|
+
mockedExec.mockImplementation(() => {
|
|
70
|
+
throw new Error('ENOENT');
|
|
71
|
+
});
|
|
72
|
+
expect(() => ghFetchAndParse(['test'], TestSchema, 'test', '/cwd')).toThrow('GitHub CLI (gh) is required');
|
|
73
|
+
});
|
|
74
|
+
it('wraps unexpected errors with context', () => {
|
|
75
|
+
mockedExec.mockImplementation(() => {
|
|
76
|
+
throw new Error('timeout exceeded');
|
|
77
|
+
});
|
|
78
|
+
expect(() => ghFetchAndParse(['test'], TestSchema, 'PR #5', '/cwd')).toThrow('[Totem Error] Failed to fetch PR #5: timeout exceeded');
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
//# sourceMappingURL=gh-utils.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gh-utils.test.js","sourceRoot":"","sources":["../../src/adapters/gh-utils.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { PrAdapter, StandardPr, StandardPrListItem, StandardReviewComment } from './pr-adapter.js';
|
|
2
|
+
export declare class GitHubCliPrAdapter implements PrAdapter {
|
|
3
|
+
private cwd;
|
|
4
|
+
constructor(cwd: string);
|
|
5
|
+
fetchOpenPRs(): StandardPrListItem[];
|
|
6
|
+
fetchPr(prNumber: number): StandardPr;
|
|
7
|
+
fetchReviewComments(prNumber: number): StandardReviewComment[];
|
|
8
|
+
private getRepoNwo;
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=github-cli-pr.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"github-cli-pr.d.ts","sourceRoot":"","sources":["../../src/adapters/github-cli-pr.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EACV,SAAS,EACT,UAAU,EACV,kBAAkB,EAClB,qBAAqB,EACtB,MAAM,iBAAiB,CAAC;AA0CzB,qBAAa,kBAAmB,YAAW,SAAS;IACtC,OAAO,CAAC,GAAG;gBAAH,GAAG,EAAE,MAAM;IAE/B,YAAY,IAAI,kBAAkB,EAAE;IAcpC,OAAO,CAAC,QAAQ,EAAE,MAAM,GAAG,UAAU;IAiBrC,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,qBAAqB,EAAE;IAmB9D,OAAO,CAAC,UAAU;CAYnB"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { execFileSync } from 'node:child_process';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { GH_TIMEOUT_MS, IS_WIN } from '../utils.js';
|
|
4
|
+
import { ghFetchAndParse, handleGhError } from './gh-utils.js';
|
|
5
|
+
// ─── Zod schemas for GitHub CLI JSON output ─────────────
|
|
6
|
+
const GhPrListItemSchema = z.object({
|
|
7
|
+
number: z.number(),
|
|
8
|
+
title: z.string(),
|
|
9
|
+
headRefName: z.string(),
|
|
10
|
+
});
|
|
11
|
+
const GhPrSchema = z.object({
|
|
12
|
+
number: z.number(),
|
|
13
|
+
title: z.string(),
|
|
14
|
+
body: z.string().nullable(),
|
|
15
|
+
state: z.string(),
|
|
16
|
+
comments: z.array(z.object({
|
|
17
|
+
author: z.object({ login: z.string() }),
|
|
18
|
+
body: z.string(),
|
|
19
|
+
})),
|
|
20
|
+
reviews: z.array(z.object({
|
|
21
|
+
author: z.object({ login: z.string() }),
|
|
22
|
+
state: z.string(),
|
|
23
|
+
body: z.string(),
|
|
24
|
+
})),
|
|
25
|
+
});
|
|
26
|
+
const GhReviewCommentSchema = z.object({
|
|
27
|
+
id: z.number(),
|
|
28
|
+
user: z.object({ login: z.string() }),
|
|
29
|
+
body: z.string(),
|
|
30
|
+
path: z.string(),
|
|
31
|
+
diff_hunk: z.string(),
|
|
32
|
+
in_reply_to_id: z.number().optional(),
|
|
33
|
+
created_at: z.string().optional(),
|
|
34
|
+
});
|
|
35
|
+
// ─── Adapter implementation ─────────────────────────────
|
|
36
|
+
export class GitHubCliPrAdapter {
|
|
37
|
+
cwd;
|
|
38
|
+
constructor(cwd) {
|
|
39
|
+
this.cwd = cwd;
|
|
40
|
+
}
|
|
41
|
+
fetchOpenPRs() {
|
|
42
|
+
const prs = ghFetchAndParse(['pr', 'list', '--state', 'open', '--json', 'number,title,headRefName'], z.array(GhPrListItemSchema), 'open PRs', this.cwd);
|
|
43
|
+
return prs.map((pr) => ({
|
|
44
|
+
number: pr.number,
|
|
45
|
+
title: pr.title,
|
|
46
|
+
headRefName: pr.headRefName,
|
|
47
|
+
}));
|
|
48
|
+
}
|
|
49
|
+
fetchPr(prNumber) {
|
|
50
|
+
const pr = ghFetchAndParse(['pr', 'view', String(prNumber), '--json', 'number,title,body,state,comments,reviews'], GhPrSchema, `PR #${prNumber}`, this.cwd);
|
|
51
|
+
return {
|
|
52
|
+
number: pr.number,
|
|
53
|
+
title: pr.title,
|
|
54
|
+
body: pr.body ?? '',
|
|
55
|
+
state: pr.state,
|
|
56
|
+
comments: pr.comments.map((c) => ({ author: c.author.login, body: c.body })),
|
|
57
|
+
reviews: pr.reviews.map((r) => ({ author: r.author.login, state: r.state, body: r.body })),
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
fetchReviewComments(prNumber) {
|
|
61
|
+
const nwo = this.getRepoNwo();
|
|
62
|
+
const comments = ghFetchAndParse(['api', `repos/${nwo}/pulls/${prNumber}/comments`, '--paginate'], z.array(GhReviewCommentSchema), `review comments for PR #${prNumber}`, this.cwd);
|
|
63
|
+
return comments.map((c) => ({
|
|
64
|
+
id: c.id,
|
|
65
|
+
author: c.user.login,
|
|
66
|
+
body: c.body,
|
|
67
|
+
path: c.path,
|
|
68
|
+
diffHunk: c.diff_hunk,
|
|
69
|
+
inReplyToId: c.in_reply_to_id,
|
|
70
|
+
createdAt: c.created_at,
|
|
71
|
+
}));
|
|
72
|
+
}
|
|
73
|
+
getRepoNwo() {
|
|
74
|
+
try {
|
|
75
|
+
const result = execFileSync('gh', ['repo', 'view', '--json', 'nameWithOwner', '-q', '.nameWithOwner'], { cwd: this.cwd, encoding: 'utf-8', timeout: GH_TIMEOUT_MS, shell: IS_WIN });
|
|
76
|
+
return result.trim();
|
|
77
|
+
}
|
|
78
|
+
catch (err) {
|
|
79
|
+
handleGhError(err, 'repository info');
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
//# sourceMappingURL=github-cli-pr.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"github-cli-pr.js","sourceRoot":"","sources":["../../src/adapters/github-cli-pr.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"github-cli-pr.test.d.ts","sourceRoot":"","sources":["../../src/adapters/github-cli-pr.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { execFileSync } from 'node:child_process';
|
|
2
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
3
|
+
import { GitHubCliPrAdapter } from './github-cli-pr.js';
|
|
4
|
+
vi.mock('node:child_process', () => ({
|
|
5
|
+
execFileSync: vi.fn(),
|
|
6
|
+
}));
|
|
7
|
+
const mockedExec = vi.mocked(execFileSync);
|
|
8
|
+
describe('GitHubCliPrAdapter', () => {
|
|
9
|
+
const adapter = new GitHubCliPrAdapter('/test/cwd');
|
|
10
|
+
describe('fetchOpenPRs', () => {
|
|
11
|
+
it('returns mapped PR list items', () => {
|
|
12
|
+
mockedExec.mockReturnValue(JSON.stringify([
|
|
13
|
+
{ number: 1, title: 'feat: add stuff', headRefName: 'feat/add-stuff' },
|
|
14
|
+
{ number: 2, title: 'fix: bug', headRefName: 'fix/bug' },
|
|
15
|
+
]));
|
|
16
|
+
const result = adapter.fetchOpenPRs();
|
|
17
|
+
expect(result).toEqual([
|
|
18
|
+
{ number: 1, title: 'feat: add stuff', headRefName: 'feat/add-stuff' },
|
|
19
|
+
{ number: 2, title: 'fix: bug', headRefName: 'fix/bug' },
|
|
20
|
+
]);
|
|
21
|
+
});
|
|
22
|
+
it('returns empty array when no open PRs', () => {
|
|
23
|
+
mockedExec.mockReturnValue('[]');
|
|
24
|
+
expect(adapter.fetchOpenPRs()).toEqual([]);
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
describe('fetchPr', () => {
|
|
28
|
+
it('maps gh output to StandardPr format', () => {
|
|
29
|
+
mockedExec.mockReturnValue(JSON.stringify({
|
|
30
|
+
number: 42,
|
|
31
|
+
title: 'feat: big feature',
|
|
32
|
+
body: 'Description here',
|
|
33
|
+
state: 'OPEN',
|
|
34
|
+
comments: [{ author: { login: 'alice' }, body: 'LGTM' }],
|
|
35
|
+
reviews: [{ author: { login: 'bob' }, state: 'APPROVED', body: 'Looks good' }],
|
|
36
|
+
}));
|
|
37
|
+
const result = adapter.fetchPr(42);
|
|
38
|
+
expect(result).toEqual({
|
|
39
|
+
number: 42,
|
|
40
|
+
title: 'feat: big feature',
|
|
41
|
+
body: 'Description here',
|
|
42
|
+
state: 'OPEN',
|
|
43
|
+
comments: [{ author: 'alice', body: 'LGTM' }],
|
|
44
|
+
reviews: [{ author: 'bob', state: 'APPROVED', body: 'Looks good' }],
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
it('normalizes null body to empty string', () => {
|
|
48
|
+
mockedExec.mockReturnValue(JSON.stringify({
|
|
49
|
+
number: 1,
|
|
50
|
+
title: 'test',
|
|
51
|
+
body: null,
|
|
52
|
+
state: 'OPEN',
|
|
53
|
+
comments: [],
|
|
54
|
+
reviews: [],
|
|
55
|
+
}));
|
|
56
|
+
expect(adapter.fetchPr(1).body).toBe('');
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
describe('fetchReviewComments', () => {
|
|
60
|
+
it('maps gh API output to StandardReviewComment format', () => {
|
|
61
|
+
// First call: getRepoNwo
|
|
62
|
+
mockedExec.mockReturnValueOnce('mmnto-ai/totem\n');
|
|
63
|
+
// Second call: fetchReviewComments
|
|
64
|
+
mockedExec.mockReturnValueOnce(JSON.stringify([
|
|
65
|
+
{
|
|
66
|
+
id: 100,
|
|
67
|
+
user: { login: 'reviewer' },
|
|
68
|
+
body: 'Fix this',
|
|
69
|
+
path: 'src/index.ts',
|
|
70
|
+
diff_hunk: '@@ -1,3 +1,4 @@',
|
|
71
|
+
in_reply_to_id: undefined,
|
|
72
|
+
created_at: '2026-03-01T00:00:00Z',
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
id: 101,
|
|
76
|
+
user: { login: 'author' },
|
|
77
|
+
body: 'Done',
|
|
78
|
+
path: 'src/index.ts',
|
|
79
|
+
diff_hunk: '@@ -1,3 +1,4 @@',
|
|
80
|
+
in_reply_to_id: 100,
|
|
81
|
+
created_at: '2026-03-01T01:00:00Z',
|
|
82
|
+
},
|
|
83
|
+
]));
|
|
84
|
+
const result = adapter.fetchReviewComments(42);
|
|
85
|
+
expect(result).toEqual([
|
|
86
|
+
{
|
|
87
|
+
id: 100,
|
|
88
|
+
author: 'reviewer',
|
|
89
|
+
body: 'Fix this',
|
|
90
|
+
path: 'src/index.ts',
|
|
91
|
+
diffHunk: '@@ -1,3 +1,4 @@',
|
|
92
|
+
inReplyToId: undefined,
|
|
93
|
+
createdAt: '2026-03-01T00:00:00Z',
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
id: 101,
|
|
97
|
+
author: 'author',
|
|
98
|
+
body: 'Done',
|
|
99
|
+
path: 'src/index.ts',
|
|
100
|
+
diffHunk: '@@ -1,3 +1,4 @@',
|
|
101
|
+
inReplyToId: 100,
|
|
102
|
+
createdAt: '2026-03-01T01:00:00Z',
|
|
103
|
+
},
|
|
104
|
+
]);
|
|
105
|
+
});
|
|
106
|
+
});
|
|
107
|
+
});
|
|
108
|
+
//# sourceMappingURL=github-cli-pr.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"github-cli-pr.test.js","sourceRoot":"","sources":["../../src/adapters/github-cli-pr.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -2,7 +2,6 @@ import type { IssueAdapter, StandardIssue, StandardIssueListItem } from './issue
|
|
|
2
2
|
export declare class GitHubCliAdapter implements IssueAdapter {
|
|
3
3
|
private cwd;
|
|
4
4
|
constructor(cwd: string);
|
|
5
|
-
private fetchAndParse;
|
|
6
5
|
fetchIssue(issueNumber: number): StandardIssue;
|
|
7
6
|
fetchOpenIssues(limit?: number): StandardIssueListItem[];
|
|
8
7
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"github-cli.d.ts","sourceRoot":"","sources":["../../src/adapters/github-cli.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"github-cli.d.ts","sourceRoot":"","sources":["../../src/adapters/github-cli.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAuB7F,qBAAa,gBAAiB,YAAW,YAAY;IACvC,OAAO,CAAC,GAAG;gBAAH,GAAG,EAAE,MAAM;IAE/B,UAAU,CAAC,WAAW,EAAE,MAAM,GAAG,aAAa;IAgB9C,eAAe,CAAC,KAAK,GAAE,MAA4B,GAAG,qBAAqB,EAAE;CAuB9E"}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import { execFileSync } from 'node:child_process';
|
|
2
1
|
import { z } from 'zod';
|
|
3
|
-
import {
|
|
2
|
+
import { ghFetchAndParse } from './gh-utils.js';
|
|
4
3
|
// ─── Zod schemas for GitHub CLI JSON output ─────────────
|
|
5
4
|
const GhIssueSchema = z.object({
|
|
6
5
|
number: z.number(),
|
|
@@ -15,20 +14,6 @@ const GhIssueListItemSchema = z.object({
|
|
|
15
14
|
labels: z.array(z.object({ name: z.string() })),
|
|
16
15
|
updatedAt: z.string().datetime(),
|
|
17
16
|
});
|
|
18
|
-
// ─── Shared error handling ──────────────────────────────
|
|
19
|
-
function handleGhError(err, context) {
|
|
20
|
-
if (err instanceof Error && err.message.includes('[Totem Error]')) {
|
|
21
|
-
throw err;
|
|
22
|
-
}
|
|
23
|
-
if (err instanceof z.ZodError) {
|
|
24
|
-
throw new Error(`[Totem Error] Failed to parse GitHub ${context}: ${err.message}`);
|
|
25
|
-
}
|
|
26
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
27
|
-
if (msg.includes('ENOENT')) {
|
|
28
|
-
throw new Error(`[Totem Error] GitHub CLI (gh) is required for issue fetching. Install: https://cli.github.com`);
|
|
29
|
-
}
|
|
30
|
-
throw new Error(`[Totem Error] Failed to fetch ${context}: ${msg}`);
|
|
31
|
-
}
|
|
32
17
|
// ─── Adapter implementation ─────────────────────────────
|
|
33
18
|
const DEFAULT_ISSUE_LIMIT = 100;
|
|
34
19
|
export class GitHubCliAdapter {
|
|
@@ -36,29 +21,8 @@ export class GitHubCliAdapter {
|
|
|
36
21
|
constructor(cwd) {
|
|
37
22
|
this.cwd = cwd;
|
|
38
23
|
}
|
|
39
|
-
fetchAndParse(args, schema, context) {
|
|
40
|
-
try {
|
|
41
|
-
const raw = execFileSync('gh', args, {
|
|
42
|
-
cwd: this.cwd,
|
|
43
|
-
encoding: 'utf-8',
|
|
44
|
-
timeout: GH_TIMEOUT_MS,
|
|
45
|
-
shell: IS_WIN,
|
|
46
|
-
});
|
|
47
|
-
let parsed;
|
|
48
|
-
try {
|
|
49
|
-
parsed = JSON.parse(raw);
|
|
50
|
-
}
|
|
51
|
-
catch {
|
|
52
|
-
throw new Error(`[Totem Error] GitHub CLI returned invalid JSON for ${context}. Are you authenticated?`);
|
|
53
|
-
}
|
|
54
|
-
return schema.parse(parsed);
|
|
55
|
-
}
|
|
56
|
-
catch (err) {
|
|
57
|
-
handleGhError(err, context);
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
24
|
fetchIssue(issueNumber) {
|
|
61
|
-
const issue =
|
|
25
|
+
const issue = ghFetchAndParse(['issue', 'view', String(issueNumber), '--json', 'number,title,body,labels,state'], GhIssueSchema, `issue #${issueNumber}`, this.cwd);
|
|
62
26
|
return {
|
|
63
27
|
number: issue.number,
|
|
64
28
|
title: issue.title,
|
|
@@ -68,7 +32,7 @@ export class GitHubCliAdapter {
|
|
|
68
32
|
};
|
|
69
33
|
}
|
|
70
34
|
fetchOpenIssues(limit = DEFAULT_ISSUE_LIMIT) {
|
|
71
|
-
const issues =
|
|
35
|
+
const issues = ghFetchAndParse([
|
|
72
36
|
'issue',
|
|
73
37
|
'list',
|
|
74
38
|
'--state',
|
|
@@ -77,7 +41,7 @@ export class GitHubCliAdapter {
|
|
|
77
41
|
'number,title,labels,updatedAt',
|
|
78
42
|
'--limit',
|
|
79
43
|
String(limit),
|
|
80
|
-
], z.array(GhIssueListItemSchema), 'open issues');
|
|
44
|
+
], z.array(GhIssueListItemSchema), 'open issues', this.cwd);
|
|
81
45
|
return issues.map((i) => ({
|
|
82
46
|
number: i.number,
|
|
83
47
|
title: i.title,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"github-cli.js","sourceRoot":"","sources":["../../src/adapters/github-cli.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"github-cli.js","sourceRoot":"","sources":["../../src/adapters/github-cli.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAGhD,2DAA2D;AAE3D,MAAM,aAAa,GAAG,CAAC,CAAC,MAAM,CAAC;IAC7B,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE;IAClB,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE;IACjB,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC3B,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IAC/C,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE;CAClB,CAAC,CAAC;AAEH,MAAM,qBAAqB,GAAG,CAAC,CAAC,MAAM,CAAC;IACrC,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE;IAClB,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE;IACjB,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IAC/C,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;CACjC,CAAC,CAAC;AAEH,2DAA2D;AAE3D,MAAM,mBAAmB,GAAG,GAAG,CAAC;AAEhC,MAAM,OAAO,gBAAgB;IACP;IAApB,YAAoB,GAAW;QAAX,QAAG,GAAH,GAAG,CAAQ;IAAG,CAAC;IAEnC,UAAU,CAAC,WAAmB;QAC5B,MAAM,KAAK,GAAG,eAAe,CAC3B,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,WAAW,CAAC,EAAE,QAAQ,EAAE,gCAAgC,CAAC,EAClF,aAAa,EACb,UAAU,WAAW,EAAE,EACvB,IAAI,CAAC,GAAG,CACT,CAAC;QACF,OAAO;YACL,MAAM,EAAE,KAAK,CAAC,MAAM;YACpB,KAAK,EAAE,KAAK,CAAC,KAAK;YAClB,IAAI,EAAE,KAAK,CAAC,IAAI,IAAI,EAAE;YACtB,KAAK,EAAE,KAAK,CAAC,KAAK;YAClB,MAAM,EAAE,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;SACxC,CAAC;IACJ,CAAC;IAED,eAAe,CAAC,QAAgB,mBAAmB;QACjD,MAAM,MAAM,GAAG,eAAe,CAC5B;YACE,OAAO;YACP,MAAM;YACN,SAAS;YACT,MAAM;YACN,QAAQ;YACR,+BAA+B;YAC/B,SAAS;YACT,MAAM,CAAC,KAAK,CAAC;SACd,EACD,CAAC,CAAC,KAAK,CAAC,qBAAqB,CAAC,EAC9B,aAAa,EACb,IAAI,CAAC,GAAG,CACT,CAAC;QACF,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YACxB,MAAM,EAAE,CAAC,CAAC,MAAM;YAChB,KAAK,EAAE,CAAC,CAAC,KAAK;YACd,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;YACnC,SAAS,EAAE,CAAC,CAAC,SAAS;SACvB,CAAC,CAAC,CAAC;IACN,CAAC;CACF"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"github-cli.test.d.ts","sourceRoot":"","sources":["../../src/adapters/github-cli.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { execFileSync } from 'node:child_process';
|
|
2
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
3
|
+
import { GitHubCliAdapter } from './github-cli.js';
|
|
4
|
+
vi.mock('node:child_process', () => ({
|
|
5
|
+
execFileSync: vi.fn(),
|
|
6
|
+
}));
|
|
7
|
+
const mockedExec = vi.mocked(execFileSync);
|
|
8
|
+
describe('GitHubCliAdapter', () => {
|
|
9
|
+
const adapter = new GitHubCliAdapter('/test/cwd');
|
|
10
|
+
describe('fetchIssue', () => {
|
|
11
|
+
it('maps gh output to StandardIssue format', () => {
|
|
12
|
+
mockedExec.mockReturnValue(JSON.stringify({
|
|
13
|
+
number: 42,
|
|
14
|
+
title: 'Fix the thing',
|
|
15
|
+
body: 'Detailed description',
|
|
16
|
+
labels: [{ name: 'bug' }, { name: 'P1' }],
|
|
17
|
+
state: 'OPEN',
|
|
18
|
+
}));
|
|
19
|
+
const result = adapter.fetchIssue(42);
|
|
20
|
+
expect(result).toEqual({
|
|
21
|
+
number: 42,
|
|
22
|
+
title: 'Fix the thing',
|
|
23
|
+
body: 'Detailed description',
|
|
24
|
+
state: 'OPEN',
|
|
25
|
+
labels: ['bug', 'P1'],
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
it('normalizes null body to empty string', () => {
|
|
29
|
+
mockedExec.mockReturnValue(JSON.stringify({
|
|
30
|
+
number: 1,
|
|
31
|
+
title: 'No body',
|
|
32
|
+
body: null,
|
|
33
|
+
labels: [],
|
|
34
|
+
state: 'OPEN',
|
|
35
|
+
}));
|
|
36
|
+
expect(adapter.fetchIssue(1).body).toBe('');
|
|
37
|
+
});
|
|
38
|
+
it('maps labels to string array', () => {
|
|
39
|
+
mockedExec.mockReturnValue(JSON.stringify({
|
|
40
|
+
number: 1,
|
|
41
|
+
title: 'test',
|
|
42
|
+
body: '',
|
|
43
|
+
labels: [{ name: 'enhancement' }, { name: 'P2' }, { name: 'architecture' }],
|
|
44
|
+
state: 'OPEN',
|
|
45
|
+
}));
|
|
46
|
+
expect(adapter.fetchIssue(1).labels).toEqual(['enhancement', 'P2', 'architecture']);
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
describe('fetchOpenIssues', () => {
|
|
50
|
+
it('returns mapped issue list items', () => {
|
|
51
|
+
mockedExec.mockReturnValue(JSON.stringify([
|
|
52
|
+
{
|
|
53
|
+
number: 1,
|
|
54
|
+
title: 'First',
|
|
55
|
+
labels: [{ name: 'bug' }],
|
|
56
|
+
updatedAt: '2026-03-01T00:00:00Z',
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
number: 2,
|
|
60
|
+
title: 'Second',
|
|
61
|
+
labels: [],
|
|
62
|
+
updatedAt: '2026-03-02T00:00:00Z',
|
|
63
|
+
},
|
|
64
|
+
]));
|
|
65
|
+
const result = adapter.fetchOpenIssues();
|
|
66
|
+
expect(result).toEqual([
|
|
67
|
+
{ number: 1, title: 'First', labels: ['bug'], updatedAt: '2026-03-01T00:00:00Z' },
|
|
68
|
+
{ number: 2, title: 'Second', labels: [], updatedAt: '2026-03-02T00:00:00Z' },
|
|
69
|
+
]);
|
|
70
|
+
});
|
|
71
|
+
it('returns empty array when no open issues', () => {
|
|
72
|
+
mockedExec.mockReturnValue('[]');
|
|
73
|
+
expect(adapter.fetchOpenIssues()).toEqual([]);
|
|
74
|
+
});
|
|
75
|
+
it('throws on invalid JSON', () => {
|
|
76
|
+
mockedExec.mockReturnValue('not json');
|
|
77
|
+
expect(() => adapter.fetchOpenIssues()).toThrow('[Totem Error] GitHub CLI returned invalid JSON for open issues. Run `gh auth status` to check your authentication.');
|
|
78
|
+
});
|
|
79
|
+
it('throws install message when gh is not found', () => {
|
|
80
|
+
mockedExec.mockImplementation(() => {
|
|
81
|
+
throw new Error('ENOENT');
|
|
82
|
+
});
|
|
83
|
+
expect(() => adapter.fetchOpenIssues()).toThrow('[Totem Error] GitHub CLI (gh) is required. Install: https://cli.github.com');
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
//# sourceMappingURL=github-cli.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"github-cli.test.js","sourceRoot":"","sources":["../../src/adapters/github-cli.test.ts"],"names":[],"mappings":"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"}
|