@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
|
@@ -2,112 +2,23 @@ import { execSync } from 'node:child_process';
|
|
|
2
2
|
import fs from 'node:fs';
|
|
3
3
|
import path from 'node:path';
|
|
4
4
|
import type { ToolDefinition, ToolContext } from '@laitszkin/tool-registry';
|
|
5
|
-
|
|
6
|
-
interface AspectArgs {
|
|
7
|
-
inputVideo: string | null;
|
|
8
|
-
outputVideo: string | null;
|
|
9
|
-
inPlace: boolean;
|
|
10
|
-
targetSize: string | null;
|
|
11
|
-
targetWidth: number | null;
|
|
12
|
-
targetHeight: number | null;
|
|
13
|
-
aspect: string | null;
|
|
14
|
-
force: boolean;
|
|
15
|
-
ffmpegBin: string;
|
|
16
|
-
ffprobeBin: string;
|
|
17
|
-
help: boolean;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
function parseArgs(args: string[]): AspectArgs {
|
|
21
|
-
const parsed: AspectArgs = {
|
|
22
|
-
inputVideo: null,
|
|
23
|
-
outputVideo: null,
|
|
24
|
-
inPlace: false,
|
|
25
|
-
targetSize: null,
|
|
26
|
-
targetWidth: null,
|
|
27
|
-
targetHeight: null,
|
|
28
|
-
aspect: null,
|
|
29
|
-
force: false,
|
|
30
|
-
ffmpegBin: 'ffmpeg',
|
|
31
|
-
ffprobeBin: 'ffprobe',
|
|
32
|
-
help: false,
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
for (let i = 0; i < args.length; i++) {
|
|
36
|
-
const arg = args[i];
|
|
37
|
-
if (arg === '--help' || arg === '-h') {
|
|
38
|
-
parsed.help = true;
|
|
39
|
-
continue;
|
|
40
|
-
}
|
|
41
|
-
if (arg.startsWith('--')) {
|
|
42
|
-
const eqIndex = arg.indexOf('=');
|
|
43
|
-
let key: string;
|
|
44
|
-
let value: string;
|
|
45
|
-
|
|
46
|
-
if (eqIndex !== -1) {
|
|
47
|
-
key = arg.slice(2, eqIndex);
|
|
48
|
-
value = arg.slice(eqIndex + 1);
|
|
49
|
-
} else {
|
|
50
|
-
key = arg.slice(2);
|
|
51
|
-
value = args[++i] || '';
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
switch (key) {
|
|
55
|
-
case 'input':
|
|
56
|
-
case 'input-video':
|
|
57
|
-
parsed.inputVideo = value;
|
|
58
|
-
break;
|
|
59
|
-
case 'output':
|
|
60
|
-
case 'output-video':
|
|
61
|
-
parsed.outputVideo = value;
|
|
62
|
-
break;
|
|
63
|
-
case 'in-place':
|
|
64
|
-
parsed.inPlace = true;
|
|
65
|
-
break;
|
|
66
|
-
case 'aspect':
|
|
67
|
-
parsed.aspect = value;
|
|
68
|
-
break;
|
|
69
|
-
case 'target-size':
|
|
70
|
-
parsed.targetSize = value;
|
|
71
|
-
break;
|
|
72
|
-
case 'target-width':
|
|
73
|
-
parsed.targetWidth = parseInt(value, 10) || null;
|
|
74
|
-
break;
|
|
75
|
-
case 'target-height':
|
|
76
|
-
parsed.targetHeight = parseInt(value, 10) || null;
|
|
77
|
-
break;
|
|
78
|
-
case 'force':
|
|
79
|
-
parsed.force = true;
|
|
80
|
-
break;
|
|
81
|
-
case 'ffmpeg-bin':
|
|
82
|
-
parsed.ffmpegBin = value;
|
|
83
|
-
break;
|
|
84
|
-
case 'ffprobe-bin':
|
|
85
|
-
parsed.ffprobeBin = value;
|
|
86
|
-
break;
|
|
87
|
-
}
|
|
88
|
-
} else if (!parsed.inputVideo && !arg.startsWith('-')) {
|
|
89
|
-
parsed.inputVideo = arg;
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
return parsed;
|
|
94
|
-
}
|
|
5
|
+
import { SystemError, UserInputError, createToolRunner } from '@laitszkin/tool-utils';
|
|
95
6
|
|
|
96
7
|
function parseSize(value: string): { width: number; height: number } {
|
|
97
8
|
const match = value.trim().toLowerCase().match(/^(\d{2,5})x(\d{2,5})$/);
|
|
98
|
-
if (!match) throw new
|
|
9
|
+
if (!match) throw new UserInputError('Invalid size format. Use WIDTHxHEIGHT, for example 1080x1920.');
|
|
99
10
|
const width = parseInt(match[1], 10);
|
|
100
11
|
const height = parseInt(match[2], 10);
|
|
101
|
-
if (width <= 0 || height <= 0) throw new
|
|
12
|
+
if (width <= 0 || height <= 0) throw new UserInputError('Width and height must be positive integers.');
|
|
102
13
|
return { width, height };
|
|
103
14
|
}
|
|
104
15
|
|
|
105
16
|
function parseRatio(value: string): { width: number; height: number } {
|
|
106
17
|
const match = value.trim().match(/^(\d+):(\d+)$/);
|
|
107
|
-
if (!match) throw new
|
|
18
|
+
if (!match) throw new UserInputError('Invalid aspect ratio format. Use WIDTH:HEIGHT, for example 16:9.');
|
|
108
19
|
const width = parseInt(match[1], 10);
|
|
109
20
|
const height = parseInt(match[2], 10);
|
|
110
|
-
if (width <= 0 || height <= 0) throw new
|
|
21
|
+
if (width <= 0 || height <= 0) throw new UserInputError('Aspect ratio values must be positive integers.');
|
|
111
22
|
return { width, height };
|
|
112
23
|
}
|
|
113
24
|
|
|
@@ -120,14 +31,14 @@ function probeVideoSize(videoPath: string, ffprobeBin: string): { width: number;
|
|
|
120
31
|
const payload = JSON.parse(result);
|
|
121
32
|
const streams = payload.streams;
|
|
122
33
|
if (!Array.isArray(streams) || streams.length === 0) {
|
|
123
|
-
throw new
|
|
34
|
+
throw new SystemError(`No video stream found in ${videoPath}.`);
|
|
124
35
|
}
|
|
125
36
|
|
|
126
37
|
const first = streams[0];
|
|
127
38
|
const width = first.width;
|
|
128
39
|
const height = first.height;
|
|
129
40
|
if (typeof width !== 'number' || typeof height !== 'number' || width <= 0 || height <= 0) {
|
|
130
|
-
throw new
|
|
41
|
+
throw new SystemError(`Invalid video dimensions from ffprobe for ${videoPath}.`);
|
|
131
42
|
}
|
|
132
43
|
return { width, height };
|
|
133
44
|
}
|
|
@@ -174,87 +85,97 @@ function buildVideoFilter(
|
|
|
174
85
|
};
|
|
175
86
|
}
|
|
176
87
|
|
|
177
|
-
function resolveTargetSize(
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
if (opts.targetSize && (opts.targetWidth !== null || opts.targetHeight !== null)) {
|
|
185
|
-
throw new Error('Use either --target-size or --target-width/--target-height, not both.');
|
|
88
|
+
function resolveTargetSize(
|
|
89
|
+
targetSize: string | null,
|
|
90
|
+
targetWidth: number | null,
|
|
91
|
+
targetHeight: number | null,
|
|
92
|
+
): { width: number; height: number } {
|
|
93
|
+
if (targetSize && (targetWidth !== null || targetHeight !== null)) {
|
|
94
|
+
throw new UserInputError('Use either --target-size or --target-width/--target-height, not both.');
|
|
186
95
|
}
|
|
187
96
|
|
|
188
|
-
if (
|
|
97
|
+
if (targetSize) return parseSize(targetSize);
|
|
189
98
|
|
|
190
|
-
const width =
|
|
191
|
-
const height =
|
|
99
|
+
const width = targetWidth || parseInt(process.env.TEXT_TO_SHORT_VIDEO_WIDTH || '1080', 10);
|
|
100
|
+
const height = targetHeight || parseInt(process.env.TEXT_TO_SHORT_VIDEO_HEIGHT || '1920', 10);
|
|
192
101
|
|
|
193
|
-
if (width <= 0 || height <= 0) throw new
|
|
102
|
+
if (width <= 0 || height <= 0) throw new UserInputError('Target width and height must be positive integers.');
|
|
194
103
|
return { width, height };
|
|
195
104
|
}
|
|
196
105
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
106
|
+
const schema = {
|
|
107
|
+
options: {
|
|
108
|
+
'input': { type: 'string' as const },
|
|
109
|
+
'input-video': { type: 'string' as const },
|
|
110
|
+
'output': { type: 'string' as const },
|
|
111
|
+
'output-video': { type: 'string' as const },
|
|
112
|
+
'in-place': { type: 'boolean' as const, default: false },
|
|
113
|
+
'aspect': { type: 'string' as const },
|
|
114
|
+
'target-size': { type: 'string' as const },
|
|
115
|
+
'target-width': { type: 'string' as const },
|
|
116
|
+
'target-height': { type: 'string' as const },
|
|
117
|
+
'force': { type: 'boolean' as const, default: false },
|
|
118
|
+
'ffmpeg-bin': { type: 'string' as const, default: 'ffmpeg' },
|
|
119
|
+
'ffprobe-bin': { type: 'string' as const, default: 'ffprobe' },
|
|
120
|
+
},
|
|
121
|
+
allowPositionals: true,
|
|
122
|
+
usage: 'apltk enforce-video-aspect-ratio [options]',
|
|
123
|
+
description: 'Resize video output to a target aspect ratio or size.',
|
|
124
|
+
handler: async (
|
|
125
|
+
values: Record<string, unknown>,
|
|
126
|
+
positionals: string[],
|
|
127
|
+
context: ToolContext,
|
|
128
|
+
): Promise<number> => {
|
|
129
|
+
const stdout = context.stdout || process.stdout;
|
|
130
|
+
|
|
131
|
+
const inputVideo = (values['input'] as string | undefined) ||
|
|
132
|
+
(values['input-video'] as string | undefined) ||
|
|
133
|
+
positionals[0] ||
|
|
134
|
+
null;
|
|
135
|
+
|
|
136
|
+
const outputVideo = (values['output'] as string | undefined) ||
|
|
137
|
+
(values['output-video'] as string | undefined) ||
|
|
138
|
+
null;
|
|
139
|
+
|
|
140
|
+
const inPlace = !!values['in-place'];
|
|
141
|
+
const targetSize = (values['target-size'] as string | undefined) ?? null;
|
|
142
|
+
const targetWidthVal = values['target-width'] ? parseInt(values['target-width'] as string, 10) || null : null;
|
|
143
|
+
const targetHeightVal = values['target-height'] ? parseInt(values['target-height'] as string, 10) || null : null;
|
|
144
|
+
const aspect = (values['aspect'] as string | undefined) ?? null;
|
|
145
|
+
const force = !!values['force'];
|
|
146
|
+
const ffmpegBin = (values['ffmpeg-bin'] as string) || 'ffmpeg';
|
|
147
|
+
const ffprobeBin = (values['ffprobe-bin'] as string) || 'ffprobe';
|
|
148
|
+
|
|
149
|
+
if (!inputVideo) {
|
|
150
|
+
throw new UserInputError('--input-video is required.');
|
|
227
151
|
}
|
|
228
152
|
|
|
229
|
-
const inputPath = path.resolve(
|
|
153
|
+
const inputPath = path.resolve(inputVideo);
|
|
230
154
|
if (!fs.existsSync(inputPath)) {
|
|
231
|
-
|
|
232
|
-
return 1;
|
|
155
|
+
throw new UserInputError(`Input video not found: ${inputPath}`);
|
|
233
156
|
}
|
|
234
157
|
|
|
235
|
-
if (
|
|
236
|
-
|
|
237
|
-
return 1;
|
|
158
|
+
if (inPlace && outputVideo) {
|
|
159
|
+
throw new UserInputError('Do not pass --output-video with --in-place.');
|
|
238
160
|
}
|
|
239
161
|
|
|
240
162
|
// Validate ffmpeg/ffprobe availability
|
|
241
163
|
try {
|
|
242
|
-
execSync(`which ${
|
|
243
|
-
execSync(`which ${
|
|
164
|
+
execSync(`which ${ffmpegBin}`, { stdio: 'ignore' });
|
|
165
|
+
execSync(`which ${ffprobeBin}`, { stdio: 'ignore' });
|
|
244
166
|
} catch {
|
|
245
|
-
|
|
246
|
-
return 1;
|
|
167
|
+
throw new UserInputError(`Missing required commands: ${ffmpegBin}, ${ffprobeBin}`);
|
|
247
168
|
}
|
|
248
169
|
|
|
249
|
-
const inputSize = probeVideoSize(inputPath,
|
|
170
|
+
const inputSize = probeVideoSize(inputPath, ffprobeBin);
|
|
250
171
|
|
|
251
172
|
// Resolve target dimensions
|
|
252
173
|
let targetWidth: number;
|
|
253
174
|
let targetHeight: number;
|
|
254
175
|
|
|
255
|
-
if (
|
|
176
|
+
if (aspect) {
|
|
256
177
|
// Derive from aspect ratio based on input dimensions
|
|
257
|
-
const ratio = parseRatio(
|
|
178
|
+
const ratio = parseRatio(aspect);
|
|
258
179
|
const inputWider = inputSize.width * ratio.height > inputSize.height * ratio.width;
|
|
259
180
|
|
|
260
181
|
if (inputWider) {
|
|
@@ -268,7 +189,7 @@ Options:
|
|
|
268
189
|
targetWidth = evenFloor(targetWidth);
|
|
269
190
|
targetHeight = evenFloor(targetHeight);
|
|
270
191
|
} else {
|
|
271
|
-
const target = resolveTargetSize(
|
|
192
|
+
const target = resolveTargetSize(targetSize, targetWidthVal, targetHeightVal);
|
|
272
193
|
targetWidth = target.width;
|
|
273
194
|
targetHeight = target.height;
|
|
274
195
|
}
|
|
@@ -277,26 +198,24 @@ Options:
|
|
|
277
198
|
let outputPath: string;
|
|
278
199
|
let replaceInPlace = false;
|
|
279
200
|
|
|
280
|
-
if (
|
|
201
|
+
if (inPlace) {
|
|
281
202
|
outputPath = path.join(
|
|
282
203
|
path.dirname(inputPath),
|
|
283
204
|
`.tmp_${path.basename(inputPath)}`,
|
|
284
205
|
);
|
|
285
206
|
replaceInPlace = true;
|
|
286
|
-
} else if (
|
|
287
|
-
outputPath = path.resolve(
|
|
207
|
+
} else if (outputVideo) {
|
|
208
|
+
outputPath = path.resolve(outputVideo);
|
|
288
209
|
if (outputPath === inputPath) {
|
|
289
|
-
|
|
290
|
-
return 1;
|
|
210
|
+
throw new UserInputError('Output path equals input path. Use --in-place to replace the input file.');
|
|
291
211
|
}
|
|
292
212
|
} else {
|
|
293
213
|
const parsed = path.parse(inputPath);
|
|
294
214
|
outputPath = path.join(parsed.dir, `${parsed.name}_aspect_fixed.mp4`);
|
|
295
215
|
}
|
|
296
216
|
|
|
297
|
-
if (!replaceInPlace && fs.existsSync(outputPath) && !
|
|
298
|
-
|
|
299
|
-
return 1;
|
|
217
|
+
if (!replaceInPlace && fs.existsSync(outputPath) && !force) {
|
|
218
|
+
throw new UserInputError(`Output already exists: ${outputPath}. Use --force to overwrite.`);
|
|
300
219
|
}
|
|
301
220
|
|
|
302
221
|
const { filter, cropApplied } = buildVideoFilter(
|
|
@@ -321,10 +240,10 @@ Options:
|
|
|
321
240
|
fs.mkdirSync(path.dirname(outputPath), { recursive: true });
|
|
322
241
|
|
|
323
242
|
const ffmpegCmd = [
|
|
324
|
-
|
|
243
|
+
ffmpegBin,
|
|
325
244
|
'-hide_banner',
|
|
326
245
|
'-loglevel', 'error',
|
|
327
|
-
|
|
246
|
+
force || replaceInPlace ? '-y' : '-n',
|
|
328
247
|
'-i', inputPath,
|
|
329
248
|
'-vf', filter,
|
|
330
249
|
'-map', '0:v:0',
|
|
@@ -342,7 +261,7 @@ Options:
|
|
|
342
261
|
} catch (err: unknown) {
|
|
343
262
|
if (replaceInPlace && fs.existsSync(outputPath)) fs.unlinkSync(outputPath);
|
|
344
263
|
const msg = err instanceof Error ? err.message : 'unknown error';
|
|
345
|
-
throw new
|
|
264
|
+
throw new SystemError(`ffmpeg failed: ${msg}`, undefined, { cause: err });
|
|
346
265
|
}
|
|
347
266
|
|
|
348
267
|
if (replaceInPlace) {
|
|
@@ -350,7 +269,7 @@ Options:
|
|
|
350
269
|
outputPath = inputPath;
|
|
351
270
|
}
|
|
352
271
|
|
|
353
|
-
const finalSize = probeVideoSize(outputPath,
|
|
272
|
+
const finalSize = probeVideoSize(outputPath, ffprobeBin);
|
|
354
273
|
stdout.write(
|
|
355
274
|
`[OK] Processed video written: ${outputPath}\n` +
|
|
356
275
|
`[INFO] Input size: ${inputSize.width}x${inputSize.height}\n` +
|
|
@@ -360,16 +279,12 @@ Options:
|
|
|
360
279
|
);
|
|
361
280
|
|
|
362
281
|
return 0;
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
stderr.write(`Error: ${msg}\n`);
|
|
366
|
-
return 1;
|
|
367
|
-
}
|
|
368
|
-
}
|
|
282
|
+
},
|
|
283
|
+
};
|
|
369
284
|
|
|
370
285
|
export const tool: ToolDefinition = {
|
|
371
286
|
name: 'enforce-video-aspect-ratio',
|
|
372
287
|
category: 'media',
|
|
373
288
|
description: 'Resize video output to a target aspect ratio or size.',
|
|
374
|
-
handler:
|
|
289
|
+
handler: createToolRunner(schema),
|
|
375
290
|
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@laitszkin/tool-enforce-video-aspect-ratio",
|
|
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
|
+
}
|
|
@@ -24,6 +24,13 @@ import { scoreAllTests } from './scorer.js';
|
|
|
24
24
|
import { generateReport, writeReport } from './reporter.js';
|
|
25
25
|
import { loadAllScores, extractIssues, deduplicateIssues, generateSuggestedFix, generateOptimizationPlan, optimizeSkillMd, } from './optimizer.js';
|
|
26
26
|
import { promisePool } from './lib/promise-pool.js';
|
|
27
|
+
// ╔══════════════════════════════════════════════════════════════════════════╗
|
|
28
|
+
// ║ SCOPE EXCLUSION NOTICE ║
|
|
29
|
+
// ║ This tool is explicitly excluded from the CLI refactoring scope ║
|
|
30
|
+
// ║ (SPEC.md L28). It predates createToolRunner, AppError, and ║
|
|
31
|
+
// ║ PlatformAdapter. DO NOT use this tool's patterns as a template ║
|
|
32
|
+
// ║ for new tools. New tools should use createToolRunner + AppError. ║
|
|
33
|
+
// ╚══════════════════════════════════════════════════════════════════════════╝
|
|
27
34
|
// ─── Helpers ────────────────────────────────────────────────────────────────
|
|
28
35
|
/**
|
|
29
36
|
* Resolve the project root directory.
|