@laitszkin/apollo-toolkit 4.1.4 → 5.0.1
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 +45 -0
- package/bin/apollo-toolkit.ts +4 -0
- package/dist/bin/apollo-toolkit.js +4 -0
- package/package.json +3 -2
- package/packages/cli/dist/help-text-builder.d.ts +23 -0
- package/packages/cli/dist/help-text-builder.js +166 -0
- package/packages/cli/dist/index.d.ts +6 -17
- package/packages/cli/dist/index.js +52 -246
- package/packages/cli/dist/installer.d.ts +1 -0
- package/packages/cli/dist/installer.js +20 -7
- package/packages/cli/dist/parsers/install-parser.d.ts +15 -0
- package/packages/cli/dist/parsers/install-parser.js +87 -0
- package/packages/cli/dist/parsers/parser-utils.d.ts +9 -0
- package/packages/cli/dist/parsers/parser-utils.js +16 -0
- package/packages/cli/dist/parsers/tool-parser.d.ts +16 -0
- package/packages/cli/dist/parsers/tool-parser.js +58 -0
- package/packages/cli/dist/parsers/types.d.ts +50 -0
- package/packages/cli/dist/parsers/types.js +1 -0
- package/packages/cli/dist/parsers/uninstall-parser.d.ts +15 -0
- package/packages/cli/dist/parsers/uninstall-parser.js +67 -0
- package/packages/cli/dist/tool-registration.d.ts +2 -0
- package/packages/cli/dist/tool-registration.js +2 -0
- package/packages/cli/dist/tsconfig.tsbuildinfo +1 -1
- package/packages/cli/dist/types.d.ts +3 -1
- package/packages/cli/dist/updater.js +11 -5
- package/packages/cli/help-text-builder.ts +180 -0
- package/packages/cli/index.ts +59 -251
- package/packages/cli/installer.ts +19 -7
- package/packages/cli/package.json +14 -4
- package/packages/cli/parsers/install-parser.ts +94 -0
- package/packages/cli/parsers/parser-utils.ts +17 -0
- package/packages/cli/parsers/tool-parser.ts +65 -0
- package/packages/cli/parsers/types.ts +56 -0
- package/packages/cli/parsers/uninstall-parser.ts +75 -0
- package/packages/cli/tool-registration.ts +3 -0
- package/packages/cli/types.ts +6 -1
- package/packages/cli/updater.ts +11 -5
- package/packages/tool-registry/dist/registry.js +3 -4
- package/packages/tool-registry/dist/tsconfig.tsbuildinfo +1 -1
- package/packages/tool-registry/dist/types.d.ts +2 -9
- package/packages/tool-registry/package.json +11 -4
- package/packages/tool-registry/registry.ts +3 -4
- package/packages/tool-registry/tsconfig.json +6 -2
- package/packages/tool-registry/types.ts +3 -9
- package/packages/tool-utils/app-error.ts +97 -0
- package/packages/tool-utils/dist/app-error.d.ts +49 -0
- package/packages/tool-utils/dist/app-error.js +80 -0
- package/packages/tool-utils/dist/index.d.ts +5 -0
- package/packages/tool-utils/dist/index.js +3 -0
- package/packages/tool-utils/dist/platform-adapter.d.ts +48 -0
- package/packages/tool-utils/dist/platform-adapter.js +73 -0
- package/packages/tool-utils/dist/schema.d.ts +68 -0
- package/packages/tool-utils/dist/schema.js +67 -0
- package/packages/tool-utils/dist/tsconfig.tsbuildinfo +1 -1
- package/packages/tool-utils/index.ts +12 -0
- package/packages/tool-utils/package.json +11 -4
- package/packages/tool-utils/platform-adapter.ts +112 -0
- package/packages/tool-utils/schema.ts +122 -0
- package/packages/tools/architecture/dist/index.d.ts +13 -0
- package/packages/tools/architecture/dist/index.js +55 -57
- package/packages/tools/architecture/dist/index.test.js +17 -4
- package/packages/tools/architecture/dist/tsconfig.tsbuildinfo +1 -1
- package/packages/tools/architecture/index.test.ts +27 -14
- package/packages/tools/architecture/index.ts +85 -88
- package/packages/tools/architecture/package.json +11 -4
- package/packages/tools/codegraph/dist/index.js +12 -22
- package/packages/tools/codegraph/dist/tsconfig.tsbuildinfo +1 -1
- package/packages/tools/codegraph/index.ts +13 -22
- package/packages/tools/codegraph/package.json +11 -4
- package/packages/tools/create-review-report/dist/index.d.ts +1 -2
- package/packages/tools/create-review-report/dist/index.js +46 -77
- package/packages/tools/create-review-report/dist/tsconfig.tsbuildinfo +1 -1
- package/packages/tools/create-review-report/index.ts +52 -81
- package/packages/tools/create-review-report/package.json +11 -4
- package/packages/tools/create-specs/dist/index.d.ts +1 -2
- package/packages/tools/create-specs/dist/index.js +70 -123
- package/packages/tools/create-specs/dist/tsconfig.tsbuildinfo +1 -1
- package/packages/tools/create-specs/index.ts +82 -128
- package/packages/tools/create-specs/package.json +11 -4
- package/packages/tools/docs-to-voice/dist/index.d.ts +1 -2
- package/packages/tools/docs-to-voice/dist/index.js +116 -219
- package/packages/tools/docs-to-voice/dist/tsconfig.tsbuildinfo +1 -1
- package/packages/tools/docs-to-voice/index.ts +265 -385
- package/packages/tools/docs-to-voice/package.json +11 -4
- package/packages/tools/enforce-video-aspect-ratio/dist/index.d.ts +1 -2
- package/packages/tools/enforce-video-aspect-ratio/dist/index.js +77 -154
- package/packages/tools/enforce-video-aspect-ratio/dist/tsconfig.tsbuildinfo +1 -1
- package/packages/tools/enforce-video-aspect-ratio/index.ts +87 -172
- package/packages/tools/enforce-video-aspect-ratio/package.json +11 -4
- package/packages/tools/eval/dist/index.js +7 -0
- package/packages/tools/eval/dist/tsconfig.tsbuildinfo +1 -1
- package/packages/tools/eval/index.ts +8 -0
- package/packages/tools/eval/package.json +11 -4
- package/packages/tools/extract-conversations/dist/index.d.ts +1 -2
- package/packages/tools/extract-conversations/dist/index.js +31 -29
- package/packages/tools/extract-conversations/dist/tsconfig.tsbuildinfo +1 -1
- package/packages/tools/extract-conversations/index.ts +37 -30
- package/packages/tools/extract-conversations/package.json +11 -4
- package/packages/tools/extract-pdf-text/dist/index.d.ts +1 -2
- package/packages/tools/extract-pdf-text/dist/index.js +44 -65
- package/packages/tools/extract-pdf-text/dist/tsconfig.tsbuildinfo +1 -1
- package/packages/tools/extract-pdf-text/index.ts +55 -74
- package/packages/tools/extract-pdf-text/package.json +11 -4
- package/packages/tools/filter-logs/dist/index.js +60 -84
- package/packages/tools/filter-logs/dist/tsconfig.tsbuildinfo +1 -1
- package/packages/tools/filter-logs/index.ts +67 -97
- package/packages/tools/filter-logs/package.json +11 -4
- package/packages/tools/find-github-issues/dist/index.d.ts +10 -0
- package/packages/tools/find-github-issues/dist/index.js +34 -5
- package/packages/tools/find-github-issues/dist/tsconfig.tsbuildinfo +1 -1
- package/packages/tools/find-github-issues/index.ts +37 -5
- package/packages/tools/find-github-issues/package.json +11 -4
- package/packages/tools/generate-storyboard-images/dist/index.d.ts +1 -2
- package/packages/tools/generate-storyboard-images/dist/index.js +98 -173
- package/packages/tools/generate-storyboard-images/dist/tsconfig.tsbuildinfo +1 -1
- package/packages/tools/generate-storyboard-images/index.ts +100 -188
- package/packages/tools/generate-storyboard-images/package.json +11 -4
- package/packages/tools/open-github-issue/dist/index.d.ts +13 -0
- package/packages/tools/open-github-issue/dist/index.js +67 -68
- package/packages/tools/open-github-issue/dist/tsconfig.tsbuildinfo +1 -1
- package/packages/tools/open-github-issue/index.ts +71 -72
- package/packages/tools/open-github-issue/package.json +11 -4
- package/packages/tools/read-github-issue/dist/index.d.ts +16 -1
- package/packages/tools/read-github-issue/dist/index.js +32 -40
- package/packages/tools/read-github-issue/dist/tsconfig.tsbuildinfo +1 -1
- package/packages/tools/read-github-issue/index.ts +32 -45
- package/packages/tools/read-github-issue/package.json +11 -4
- package/packages/tools/render-error-book/dist/index.d.ts +1 -2
- package/packages/tools/render-error-book/dist/index.js +74 -95
- package/packages/tools/render-error-book/dist/tsconfig.tsbuildinfo +1 -1
- package/packages/tools/render-error-book/index.ts +88 -103
- package/packages/tools/render-error-book/package.json +11 -4
- package/packages/tools/render-katex/dist/index.d.ts +1 -2
- package/packages/tools/render-katex/dist/index.js +70 -157
- package/packages/tools/render-katex/dist/tsconfig.tsbuildinfo +1 -1
- package/packages/tools/render-katex/index.ts +138 -222
- package/packages/tools/render-katex/package.json +11 -4
- package/packages/tools/review-threads/dist/index.d.ts +12 -0
- package/packages/tools/review-threads/dist/index.js +83 -86
- package/packages/tools/review-threads/dist/tsconfig.tsbuildinfo +1 -1
- package/packages/tools/review-threads/index.ts +90 -84
- package/packages/tools/review-threads/package.json +11 -4
- package/packages/tools/search-logs/dist/index.js +100 -136
- package/packages/tools/search-logs/dist/tsconfig.tsbuildinfo +1 -1
- package/packages/tools/search-logs/index.ts +113 -145
- package/packages/tools/search-logs/package.json +11 -4
- package/packages/tools/sync-memory-index/dist/index.js +34 -28
- package/packages/tools/sync-memory-index/dist/tsconfig.tsbuildinfo +1 -1
- package/packages/tools/sync-memory-index/index.ts +37 -28
- package/packages/tools/sync-memory-index/package.json +11 -4
- package/packages/tools/validate-openai-agent-config/dist/index.js +13 -7
- package/packages/tools/validate-openai-agent-config/dist/tsconfig.tsbuildinfo +1 -1
- package/packages/tools/validate-openai-agent-config/index.ts +13 -7
- package/packages/tools/validate-openai-agent-config/package.json +11 -4
- package/packages/tools/validate-skill-frontmatter/dist/index.js +12 -6
- package/packages/tools/validate-skill-frontmatter/dist/tsconfig.tsbuildinfo +1 -1
- package/packages/tools/validate-skill-frontmatter/index.ts +12 -6
- package/packages/tools/validate-skill-frontmatter/package.json +11 -4
- package/packages/tui/dist/index.d.ts +2 -1
- package/packages/tui/dist/index.js +1 -0
- package/packages/tui/dist/stdio-adapter.d.ts +36 -0
- package/packages/tui/dist/stdio-adapter.js +69 -0
- package/packages/tui/dist/terminal.js +3 -1
- package/packages/tui/dist/tsconfig.tsbuildinfo +1 -1
- package/packages/tui/dist/types.d.ts +17 -0
- package/packages/tui/index.ts +2 -1
- package/packages/tui/package.json +14 -6
- package/packages/tui/stdio-adapter.ts +85 -0
- package/packages/tui/terminal.ts +3 -1
- package/packages/tui/tsconfig.json +5 -2
- package/packages/tui/types.ts +19 -0
- package/resources/project-architecture/assets/architecture.css +2 -1
- package/resources/project-architecture/atlas/atlas.history.log +1 -0
- package/resources/project-architecture/atlas/atlas.history.undo.json +13 -2
- package/resources/project-architecture/atlas/atlas.history.undo.stack.json +610 -0
- package/resources/project-architecture/atlas/atlas.index.yaml +81 -5
- package/resources/project-architecture/atlas/features/cli-dispatch.yaml +43 -0
- package/resources/project-architecture/atlas/features/terminal-ui.yaml +29 -0
- package/resources/project-architecture/atlas/features/tool-registry.yaml +22 -0
- package/resources/project-architecture/atlas/features/tool-utils.yaml +22 -0
- package/resources/project-architecture/features/cli-dispatch/arg-parser.html +40 -0
- package/resources/project-architecture/features/cli-dispatch/help-builder.html +40 -0
- package/resources/project-architecture/features/cli-dispatch/index.html +64 -0
- package/resources/project-architecture/features/cli-dispatch/installer-core.html +40 -0
- package/resources/project-architecture/features/cli-dispatch/tool-discovery.html +40 -0
- package/resources/project-architecture/features/cli-dispatch/update-checker.html +40 -0
- package/resources/project-architecture/features/terminal-ui/banner-display.html +40 -0
- package/resources/project-architecture/features/terminal-ui/index.html +50 -0
- package/resources/project-architecture/features/terminal-ui/interactive-prompts.html +40 -0
- package/resources/project-architecture/features/terminal-ui/terminal-detection.html +40 -0
- package/resources/project-architecture/features/tool-registry/formatter.html +40 -0
- package/resources/project-architecture/features/tool-registry/index.html +43 -0
- package/resources/project-architecture/features/tool-registry/registry-core.html +40 -0
- package/resources/project-architecture/features/tool-utils/index.html +43 -0
- package/resources/project-architecture/features/tool-utils/log-utils.html +40 -0
- package/resources/project-architecture/features/tool-utils/skill-discovery.html +40 -0
- package/resources/project-architecture/index.html +365 -121
- package/scripts/rewrite-imports.mjs +2 -2
- package/scripts/test.sh +144 -8
- package/skills/design/SKILL.md +57 -64
- package/skills/design/assets/templates/DESIGN.md +12 -0
- package/skills/design/references/code-smells.md +94 -0
- package/skills/design/references/module-boundary-adjustment.md +126 -0
- package/skills/design/references/module-internal-restructuring.md +132 -0
- package/skills/design/references/module-internal-simplification.md +164 -0
|
@@ -1,6 +1,16 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* review-threads — Carryover tool migrated from the pre-monorepo CLI
|
|
3
|
+
* architecture (originally resolve-review-comments/scripts/review_threads.py).
|
|
4
|
+
*
|
|
5
|
+
* Carryover status (Round 16, 2026-06-06):
|
|
6
|
+
* - Added --help/-h support for subcommand-based usage.
|
|
7
|
+
* - Stdout/stderr invariant: resolved data -> stdout, failures -> stderr.
|
|
8
|
+
*/
|
|
9
|
+
|
|
1
10
|
import { execFile } from 'node:child_process';
|
|
2
11
|
import { readFileSync } from 'node:fs';
|
|
3
12
|
import type { ToolDefinition, ToolContext } from '@laitszkin/tool-registry';
|
|
13
|
+
import { UserInputError, SystemError } from '@laitszkin/tool-utils';
|
|
4
14
|
|
|
5
15
|
const LIST_QUERY = `
|
|
6
16
|
query($owner: String!, $name: String!, $number: Int!, $after: String) {
|
|
@@ -60,6 +70,7 @@ interface ReviewThreadsArgs {
|
|
|
60
70
|
threadIdFile: string | null;
|
|
61
71
|
allUnresolved: boolean;
|
|
62
72
|
dryRun: boolean;
|
|
73
|
+
help: boolean;
|
|
63
74
|
}
|
|
64
75
|
|
|
65
76
|
function parseArgs(argv: string[]): ReviewThreadsArgs {
|
|
@@ -73,8 +84,15 @@ function parseArgs(argv: string[]): ReviewThreadsArgs {
|
|
|
73
84
|
threadIdFile: null,
|
|
74
85
|
allUnresolved: false,
|
|
75
86
|
dryRun: false,
|
|
87
|
+
help: false,
|
|
76
88
|
};
|
|
77
89
|
|
|
90
|
+
// Check for --help/-h before positional parsing
|
|
91
|
+
if (argv.includes('--help') || argv.includes('-h')) {
|
|
92
|
+
args.help = true;
|
|
93
|
+
return args;
|
|
94
|
+
}
|
|
95
|
+
|
|
78
96
|
// First argument is the subcommand (list/resolve)
|
|
79
97
|
let i = 0;
|
|
80
98
|
if (i < argv.length && !argv[i].startsWith('-')) {
|
|
@@ -158,12 +176,12 @@ function runGh(cmdArgs: string[]): Promise<CommandResult> {
|
|
|
158
176
|
function runGhJson(cmdArgs: string[]): Promise<Record<string, unknown>> {
|
|
159
177
|
return runGh(cmdArgs).then((result) => {
|
|
160
178
|
if (result.exitCode !== 0) {
|
|
161
|
-
throw new
|
|
179
|
+
throw new SystemError(result.stderr.trim() || 'gh command failed');
|
|
162
180
|
}
|
|
163
181
|
try {
|
|
164
182
|
return JSON.parse(result.stdout);
|
|
165
183
|
} catch (exc) {
|
|
166
|
-
throw new
|
|
184
|
+
throw new SystemError('Failed to parse gh JSON output', undefined, { cause: exc });
|
|
167
185
|
}
|
|
168
186
|
});
|
|
169
187
|
}
|
|
@@ -171,7 +189,7 @@ function runGhJson(cmdArgs: string[]): Promise<Record<string, unknown>> {
|
|
|
171
189
|
function parseOwnerRepo(repo: string): [string, string] {
|
|
172
190
|
const parts = repo.split('/');
|
|
173
191
|
if (parts.length !== 2 || !parts[0] || !parts[1]) {
|
|
174
|
-
throw new
|
|
192
|
+
throw new UserInputError('repo must be in owner/name format');
|
|
175
193
|
}
|
|
176
194
|
return [parts[0], parts[1]];
|
|
177
195
|
}
|
|
@@ -191,7 +209,7 @@ async function resolveRepo(repo: string | null): Promise<string> {
|
|
|
191
209
|
'.nameWithOwner',
|
|
192
210
|
]);
|
|
193
211
|
if (result.exitCode !== 0) {
|
|
194
|
-
throw new
|
|
212
|
+
throw new SystemError(result.stderr.trim() || 'Unable to resolve current repo');
|
|
195
213
|
}
|
|
196
214
|
return result.stdout.trim();
|
|
197
215
|
}
|
|
@@ -210,7 +228,7 @@ async function resolvePrNumber(repo: string, pr: number | null): Promise<number>
|
|
|
210
228
|
'.number',
|
|
211
229
|
]);
|
|
212
230
|
if (result.exitCode !== 0) {
|
|
213
|
-
throw new
|
|
231
|
+
throw new UserInputError(
|
|
214
232
|
'Unable to infer PR number from current branch context',
|
|
215
233
|
);
|
|
216
234
|
}
|
|
@@ -248,7 +266,7 @@ async function fetchReviewThreads(
|
|
|
248
266
|
|
|
249
267
|
const pr = (payload.data as Record<string, unknown>)?.repository as Record<string, unknown> | undefined;
|
|
250
268
|
if (!pr) {
|
|
251
|
-
throw new
|
|
269
|
+
throw new UserInputError(`PR #${prNumber} not found in ${repo}`);
|
|
252
270
|
}
|
|
253
271
|
|
|
254
272
|
const reviewThreads = pr.reviewThreads as Record<string, unknown>;
|
|
@@ -381,12 +399,12 @@ function loadThreadIds(filePath: string): string[] {
|
|
|
381
399
|
.map((item) => item.thread_id)
|
|
382
400
|
.filter((id) => id !== undefined);
|
|
383
401
|
} else {
|
|
384
|
-
throw new
|
|
402
|
+
throw new UserInputError(
|
|
385
403
|
'JSON must include thread_ids, adopted_thread_ids, or threads',
|
|
386
404
|
);
|
|
387
405
|
}
|
|
388
406
|
} else {
|
|
389
|
-
throw new
|
|
407
|
+
throw new UserInputError('Unsupported JSON payload for thread IDs');
|
|
390
408
|
}
|
|
391
409
|
|
|
392
410
|
const output = ids
|
|
@@ -438,11 +456,11 @@ async function resolveThreads(
|
|
|
438
456
|
payload.data as Record<string, unknown>
|
|
439
457
|
)?.resolveReviewThread as Record<string, unknown> | undefined;
|
|
440
458
|
if (!thread?.thread) {
|
|
441
|
-
throw new
|
|
459
|
+
throw new SystemError('thread did not resolve');
|
|
442
460
|
}
|
|
443
461
|
const resolvedThread = thread.thread as Record<string, unknown>;
|
|
444
462
|
if (!resolvedThread.isResolved) {
|
|
445
|
-
throw new
|
|
463
|
+
throw new SystemError('thread did not resolve');
|
|
446
464
|
}
|
|
447
465
|
resolved.push(threadId);
|
|
448
466
|
} catch (exc) {
|
|
@@ -459,31 +477,11 @@ async function cmdList(
|
|
|
459
477
|
args: ReviewThreadsArgs,
|
|
460
478
|
context: ToolContext,
|
|
461
479
|
): Promise<number> {
|
|
462
|
-
const { stdout
|
|
463
|
-
|
|
464
|
-
let repo: string;
|
|
465
|
-
try {
|
|
466
|
-
repo = await resolveRepo(args.repo);
|
|
467
|
-
} catch (err) {
|
|
468
|
-
stderr!.write(`Error: ${(err as Error).message}\n`);
|
|
469
|
-
return 1;
|
|
470
|
-
}
|
|
471
|
-
|
|
472
|
-
let prNumber: number;
|
|
473
|
-
try {
|
|
474
|
-
prNumber = await resolvePrNumber(repo, args.pr);
|
|
475
|
-
} catch (err) {
|
|
476
|
-
stderr!.write(`Error: ${(err as Error).message}\n`);
|
|
477
|
-
return 1;
|
|
478
|
-
}
|
|
480
|
+
const { stdout } = context;
|
|
479
481
|
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
} catch (err) {
|
|
484
|
-
stderr!.write(`Error: ${(err as Error).message}\n`);
|
|
485
|
-
return 1;
|
|
486
|
-
}
|
|
482
|
+
const repo = await resolveRepo(args.repo);
|
|
483
|
+
const prNumber = await resolvePrNumber(repo, args.pr);
|
|
484
|
+
const threads: Array<Record<string, unknown>> = await fetchReviewThreads(repo, prNumber);
|
|
487
485
|
|
|
488
486
|
const filtered = filterThreads(threads, args.state);
|
|
489
487
|
const normalized = filtered.map(normalizeThread);
|
|
@@ -512,79 +510,87 @@ async function cmdResolve(
|
|
|
512
510
|
args: ReviewThreadsArgs,
|
|
513
511
|
context: ToolContext,
|
|
514
512
|
): Promise<number> {
|
|
515
|
-
const { stdout
|
|
516
|
-
|
|
517
|
-
let repo: string;
|
|
518
|
-
try {
|
|
519
|
-
repo = await resolveRepo(args.repo);
|
|
520
|
-
} catch (err) {
|
|
521
|
-
stderr!.write(`Error: ${(err as Error).message}\n`);
|
|
522
|
-
return 1;
|
|
523
|
-
}
|
|
513
|
+
const { stdout } = context;
|
|
524
514
|
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
} catch (err) {
|
|
529
|
-
stderr!.write(`Error: ${(err as Error).message}\n`);
|
|
530
|
-
return 1;
|
|
531
|
-
}
|
|
532
|
-
|
|
533
|
-
let threads: Array<Record<string, unknown>>;
|
|
534
|
-
try {
|
|
535
|
-
threads = await fetchReviewThreads(repo, prNumber);
|
|
536
|
-
} catch (err) {
|
|
537
|
-
stderr!.write(`Error: ${(err as Error).message}\n`);
|
|
538
|
-
return 1;
|
|
539
|
-
}
|
|
515
|
+
const repo = await resolveRepo(args.repo);
|
|
516
|
+
const prNumber = await resolvePrNumber(repo, args.pr);
|
|
517
|
+
const threads: Array<Record<string, unknown>> = await fetchReviewThreads(repo, prNumber);
|
|
540
518
|
|
|
541
519
|
const unresolved = filterThreads(threads, 'unresolved').map(normalizeThread);
|
|
542
520
|
const threadIds = collectThreadIds(args, unresolved);
|
|
543
521
|
|
|
544
522
|
if (threadIds.length === 0) {
|
|
545
|
-
|
|
546
|
-
'
|
|
523
|
+
throw new UserInputError(
|
|
524
|
+
'no thread IDs selected. Use --thread-id, --thread-id-file, or --all-unresolved.',
|
|
547
525
|
);
|
|
548
|
-
return 1;
|
|
549
526
|
}
|
|
550
527
|
|
|
551
528
|
const { resolved, failed } = await resolveThreads(threadIds, args.dryRun);
|
|
552
529
|
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
pr_number: prNumber,
|
|
556
|
-
requested: threadIds,
|
|
557
|
-
resolved,
|
|
558
|
-
failed,
|
|
559
|
-
dry_run: args.dryRun,
|
|
560
|
-
};
|
|
561
|
-
stdout!.write(JSON.stringify(summary, null, 2) + '\n');
|
|
530
|
+
stdout!.write(JSON.stringify({ resolved }, null, 2) + '\n');
|
|
531
|
+
context.stderr!.write(JSON.stringify({ failed }, null, 2) + '\n');
|
|
562
532
|
|
|
563
533
|
return failed.length > 0 ? 1 : 0;
|
|
564
534
|
}
|
|
565
535
|
|
|
536
|
+
// ---- Help ----
|
|
537
|
+
|
|
538
|
+
function printHelp(stdout: NodeJS.WriteStream): void {
|
|
539
|
+
stdout.write(`Usage: apltk review-threads <subcommand> [options]
|
|
540
|
+
|
|
541
|
+
List or resolve GitHub PR review threads.
|
|
542
|
+
|
|
543
|
+
Subcommands:
|
|
544
|
+
list List review threads for a pull request
|
|
545
|
+
resolve Resolve specific review threads
|
|
546
|
+
|
|
547
|
+
Options:
|
|
548
|
+
--repo <owner/name> GitHub repository (default: current repo)
|
|
549
|
+
--pr <number> PR number (default: current branch's PR)
|
|
550
|
+
--state <unresolved|resolved|all>
|
|
551
|
+
Filter threads by state (default: unresolved)
|
|
552
|
+
--output <table|json> Output format for list (default: table)
|
|
553
|
+
--thread-id <id> Thread ID to resolve (may be repeated)
|
|
554
|
+
--thread-id-file <path> JSON file with thread IDs to resolve
|
|
555
|
+
--all-unresolved Resolve all unresolved threads
|
|
556
|
+
--dry-run Simulate resolution without mutating
|
|
557
|
+
--help, -h Show this help message
|
|
558
|
+
`);
|
|
559
|
+
}
|
|
560
|
+
|
|
566
561
|
// ---- Main handler ----
|
|
567
562
|
|
|
563
|
+
/**
|
|
564
|
+
* reviewThreadsHandler — Known carryover from createToolRunner migration.
|
|
565
|
+
*
|
|
566
|
+
* Reason for not using createToolRunner:
|
|
567
|
+
* - Positional subcommand architecture (list/resolve) doesn't map cleanly
|
|
568
|
+
* to createToolRunner's options schema.
|
|
569
|
+
* - Error handling follows the AppError convention (UserInputError/SystemError
|
|
570
|
+
* throws) — errors propagate to the CLI boundary's formatAppError.
|
|
571
|
+
* - Argument parsing is handled manually via a 63-line parseArgs().
|
|
572
|
+
*
|
|
573
|
+
* See DESIGN.md §2.3 for the full architecture discussion.
|
|
574
|
+
*/
|
|
568
575
|
export async function reviewThreadsHandler(
|
|
569
576
|
argv: string[],
|
|
570
577
|
context: ToolContext,
|
|
571
578
|
): Promise<number> {
|
|
572
|
-
const {
|
|
579
|
+
const { stdout } = context;
|
|
573
580
|
const args = parseArgs(argv);
|
|
574
581
|
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
return 1;
|
|
582
|
+
if (args.help) {
|
|
583
|
+
printHelp(stdout!);
|
|
584
|
+
return 0;
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
switch (args.command) {
|
|
588
|
+
case 'list':
|
|
589
|
+
return await cmdList(args, context);
|
|
590
|
+
case 'resolve':
|
|
591
|
+
return await cmdResolve(args, context);
|
|
592
|
+
default:
|
|
593
|
+
throw new UserInputError(`Unsupported command: ${args.command}`);
|
|
588
594
|
}
|
|
589
595
|
}
|
|
590
596
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@laitszkin/tool-review-threads",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "Apollo Toolkit
|
|
3
|
+
"version": "5.0.0",
|
|
4
|
+
"description": "Apollo Toolkit \u2014 CLI tool",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"main": "./dist/index.js",
|
|
@@ -18,5 +18,12 @@
|
|
|
18
18
|
},
|
|
19
19
|
"dependencies": {
|
|
20
20
|
"@laitszkin/tool-registry": "*"
|
|
21
|
-
}
|
|
22
|
-
|
|
21
|
+
},
|
|
22
|
+
"publishConfig": {
|
|
23
|
+
"access": "public",
|
|
24
|
+
"registry": "https://registry.npmjs.org"
|
|
25
|
+
},
|
|
26
|
+
"files": [
|
|
27
|
+
"dist"
|
|
28
|
+
]
|
|
29
|
+
}
|
|
@@ -1,75 +1,15 @@
|
|
|
1
|
-
import { extractTimestamp, inWindow, iterInputLines, parseCliTimestamp, buildTimezone,
|
|
2
|
-
function
|
|
3
|
-
const args = {
|
|
4
|
-
paths: [],
|
|
5
|
-
keyword: [],
|
|
6
|
-
regex: [],
|
|
7
|
-
mode: 'any',
|
|
8
|
-
ignoreCase: false,
|
|
9
|
-
start: null,
|
|
10
|
-
end: null,
|
|
11
|
-
assumeTimezone: 'UTC',
|
|
12
|
-
beforeContext: 0,
|
|
13
|
-
afterContext: 0,
|
|
14
|
-
countOnly: false,
|
|
15
|
-
};
|
|
16
|
-
let i = 0;
|
|
17
|
-
while (i < argv.length) {
|
|
18
|
-
const arg = argv[i];
|
|
19
|
-
if (arg === '--keyword' && i + 1 < argv.length) {
|
|
20
|
-
args.keyword.push(argv[++i]);
|
|
21
|
-
}
|
|
22
|
-
else if (arg === '--regex' && i + 1 < argv.length) {
|
|
23
|
-
args.regex.push(argv[++i]);
|
|
24
|
-
}
|
|
25
|
-
else if (arg === '--mode' && i + 1 < argv.length) {
|
|
26
|
-
const val = argv[++i];
|
|
27
|
-
if (val === 'any' || val === 'all') {
|
|
28
|
-
args.mode = val;
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
else if (arg === '--ignore-case') {
|
|
32
|
-
args.ignoreCase = true;
|
|
33
|
-
}
|
|
34
|
-
else if (arg === '--start' && i + 1 < argv.length) {
|
|
35
|
-
args.start = argv[++i];
|
|
36
|
-
}
|
|
37
|
-
else if (arg === '--end' && i + 1 < argv.length) {
|
|
38
|
-
args.end = argv[++i];
|
|
39
|
-
}
|
|
40
|
-
else if (arg === '--assume-timezone' && i + 1 < argv.length) {
|
|
41
|
-
args.assumeTimezone = argv[++i];
|
|
42
|
-
}
|
|
43
|
-
else if (arg === '--before-context' && i + 1 < argv.length) {
|
|
44
|
-
args.beforeContext = parseInt(argv[++i], 10) || 0;
|
|
45
|
-
}
|
|
46
|
-
else if (arg === '--after-context' && i + 1 < argv.length) {
|
|
47
|
-
args.afterContext = parseInt(argv[++i], 10) || 0;
|
|
48
|
-
}
|
|
49
|
-
else if (arg === '--count-only') {
|
|
50
|
-
args.countOnly = true;
|
|
51
|
-
}
|
|
52
|
-
else if (arg.startsWith('-')) {
|
|
53
|
-
// skip unknown flags
|
|
54
|
-
}
|
|
55
|
-
else {
|
|
56
|
-
args.paths.push(arg);
|
|
57
|
-
}
|
|
58
|
-
i++;
|
|
59
|
-
}
|
|
60
|
-
return args;
|
|
61
|
-
}
|
|
62
|
-
function buildMatchers(args) {
|
|
1
|
+
import { UserInputError, SystemError, createToolRunner, extractTimestamp, inWindow, iterInputLines, parseCliTimestamp, buildTimezone, } from '../../../tool-utils/dist/index.js';
|
|
2
|
+
function buildMatchers(keywords, regexPatterns, ignoreCase, mode) {
|
|
63
3
|
const matchers = [];
|
|
64
|
-
for (const keyword of
|
|
65
|
-
const needle =
|
|
4
|
+
for (const keyword of keywords) {
|
|
5
|
+
const needle = ignoreCase ? keyword.toLowerCase() : keyword;
|
|
66
6
|
matchers.push((line) => {
|
|
67
|
-
const haystack =
|
|
7
|
+
const haystack = ignoreCase ? line.toLowerCase() : line;
|
|
68
8
|
return haystack.includes(needle);
|
|
69
9
|
});
|
|
70
10
|
}
|
|
71
|
-
for (const pattern of
|
|
72
|
-
const flags =
|
|
11
|
+
for (const pattern of regexPatterns) {
|
|
12
|
+
const flags = ignoreCase ? 'i' : '';
|
|
73
13
|
const compiled = new RegExp(pattern, flags);
|
|
74
14
|
matchers.push((line) => compiled.test(line));
|
|
75
15
|
}
|
|
@@ -83,86 +23,110 @@ function lineMatches(line, matchers, mode) {
|
|
|
83
23
|
}
|
|
84
24
|
return matchers.every((m) => m(line));
|
|
85
25
|
}
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
26
|
+
const schema = {
|
|
27
|
+
options: {
|
|
28
|
+
keyword: { type: 'string', multiple: true },
|
|
29
|
+
regex: { type: 'string', multiple: true },
|
|
30
|
+
mode: { type: 'string', default: 'any' },
|
|
31
|
+
'ignore-case': { type: 'boolean', default: false },
|
|
32
|
+
start: { type: 'string' },
|
|
33
|
+
end: { type: 'string' },
|
|
34
|
+
'assume-timezone': { type: 'string', default: 'UTC' },
|
|
35
|
+
'before-context': { type: 'string', default: '0' },
|
|
36
|
+
'after-context': { type: 'string', default: '0' },
|
|
37
|
+
'count-only': { type: 'boolean', default: false },
|
|
38
|
+
},
|
|
39
|
+
allowPositionals: true,
|
|
40
|
+
strict: true,
|
|
41
|
+
usage: 'apltk search-logs [options] [<file>...]',
|
|
42
|
+
description: 'Search log lines by keywords or regex patterns with time filters',
|
|
43
|
+
handler: async (values, positionals, context) => {
|
|
44
|
+
const stdout = context.stdout ?? process.stdout;
|
|
45
|
+
const mode = values.mode;
|
|
46
|
+
if (mode !== 'any' && mode !== 'all') {
|
|
47
|
+
throw new UserInputError('--mode must be "any" or "all"');
|
|
102
48
|
}
|
|
103
|
-
|
|
104
|
-
|
|
49
|
+
const keywords = values.keyword || [];
|
|
50
|
+
const regexPatterns = values.regex || [];
|
|
51
|
+
const ignoreCase = values['ignore-case'];
|
|
52
|
+
const assumeTimezone = values['assume-timezone'];
|
|
53
|
+
const beforeContext = parseInt(values['before-context'], 10) || 0;
|
|
54
|
+
const afterContext = parseInt(values['after-context'], 10) || 0;
|
|
55
|
+
const countOnly = values['count-only'];
|
|
56
|
+
try {
|
|
57
|
+
buildTimezone(assumeTimezone);
|
|
105
58
|
}
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
59
|
+
catch {
|
|
60
|
+
throw new UserInputError(`invalid timezone: ${assumeTimezone}`);
|
|
61
|
+
}
|
|
62
|
+
let start = null;
|
|
63
|
+
let end = null;
|
|
64
|
+
try {
|
|
65
|
+
if (values.start) {
|
|
66
|
+
start = parseCliTimestamp(values.start, assumeTimezone);
|
|
67
|
+
}
|
|
68
|
+
if (values.end) {
|
|
69
|
+
end = parseCliTimestamp(values.end, assumeTimezone);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
catch (err) {
|
|
73
|
+
throw new UserInputError(err.message);
|
|
74
|
+
}
|
|
75
|
+
if (start && end && start > end) {
|
|
76
|
+
throw new UserInputError('--start must be earlier than or equal to --end.');
|
|
77
|
+
}
|
|
78
|
+
const matchers = buildMatchers(keywords, regexPatterns, ignoreCase, mode);
|
|
79
|
+
let matches = 0;
|
|
80
|
+
const beforeBuffer = [];
|
|
81
|
+
let afterRemaining = 0;
|
|
82
|
+
try {
|
|
83
|
+
for await (const line of iterInputLines(positionals)) {
|
|
84
|
+
const timestamp = extractTimestamp(line, assumeTimezone);
|
|
85
|
+
// When time filter is active, skip lines outside the window
|
|
86
|
+
if (values.start || values.end) {
|
|
87
|
+
if (!inWindow(timestamp, start, end)) {
|
|
88
|
+
beforeBuffer.push(line);
|
|
89
|
+
if (beforeBuffer.length > beforeContext) {
|
|
90
|
+
beforeBuffer.shift();
|
|
91
|
+
}
|
|
92
|
+
continue;
|
|
127
93
|
}
|
|
128
|
-
continue;
|
|
129
94
|
}
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
95
|
+
const isMatch = lineMatches(line, matchers, mode);
|
|
96
|
+
if (isMatch) {
|
|
97
|
+
matches++;
|
|
98
|
+
if (!countOnly) {
|
|
99
|
+
// Flush before context
|
|
100
|
+
for (const ctxLine of beforeBuffer) {
|
|
101
|
+
stdout.write(ctxLine + '\n');
|
|
102
|
+
}
|
|
103
|
+
stdout.write(line + '\n');
|
|
138
104
|
}
|
|
105
|
+
afterRemaining = afterContext;
|
|
106
|
+
}
|
|
107
|
+
else if (afterRemaining > 0 && !countOnly) {
|
|
139
108
|
stdout.write(line + '\n');
|
|
109
|
+
afterRemaining--;
|
|
110
|
+
}
|
|
111
|
+
// Maintain before context buffer
|
|
112
|
+
beforeBuffer.push(line);
|
|
113
|
+
if (beforeBuffer.length > beforeContext) {
|
|
114
|
+
beforeBuffer.shift();
|
|
140
115
|
}
|
|
141
|
-
afterRemaining = args.afterContext;
|
|
142
|
-
}
|
|
143
|
-
else if (afterRemaining > 0 && !args.countOnly) {
|
|
144
|
-
stdout.write(line + '\n');
|
|
145
|
-
afterRemaining--;
|
|
146
|
-
}
|
|
147
|
-
// Maintain before context buffer
|
|
148
|
-
beforeBuffer.push(line);
|
|
149
|
-
if (beforeBuffer.length > args.beforeContext) {
|
|
150
|
-
beforeBuffer.shift();
|
|
151
116
|
}
|
|
152
117
|
}
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
}
|
|
118
|
+
catch (err) {
|
|
119
|
+
throw new SystemError(err.message);
|
|
120
|
+
}
|
|
121
|
+
if (countOnly) {
|
|
122
|
+
stdout.write(String(matches) + '\n');
|
|
123
|
+
}
|
|
124
|
+
return 0;
|
|
125
|
+
},
|
|
126
|
+
};
|
|
163
127
|
export const tool = {
|
|
164
128
|
name: 'search-logs',
|
|
165
129
|
category: 'Log Analysis',
|
|
166
130
|
description: 'Search log lines by keywords or regex patterns with time filters',
|
|
167
|
-
handler:
|
|
131
|
+
handler: createToolRunner(schema),
|
|
168
132
|
};
|