ae-agent-setup 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/CSXS/manifest.xml +44 -0
  2. package/README.ja.md +249 -0
  3. package/README.md +249 -0
  4. package/bin/ae-agent-setup.mjs +337 -0
  5. package/client/CSInterface.js +1291 -0
  6. package/client/index.html +64 -0
  7. package/client/lib/bridge_utils.js +56 -0
  8. package/client/lib/logging.js +10 -0
  9. package/client/lib/request_handlers.js +468 -0
  10. package/client/lib/request_handlers_essential.js +35 -0
  11. package/client/lib/request_handlers_layer_structure.js +180 -0
  12. package/client/lib/request_handlers_scene.js +38 -0
  13. package/client/lib/request_handlers_shape.js +288 -0
  14. package/client/lib/request_handlers_timeline.js +115 -0
  15. package/client/lib/runtime.js +35 -0
  16. package/client/lib/server.js +33 -0
  17. package/client/main.js +1 -0
  18. package/host/index.jsx +11 -0
  19. package/host/json2.js +504 -0
  20. package/host/lib/common.jsx +128 -0
  21. package/host/lib/mutation_handlers.jsx +358 -0
  22. package/host/lib/mutation_keyframe_handlers.jsx +265 -0
  23. package/host/lib/mutation_layer_structure_handlers.jsx +235 -0
  24. package/host/lib/mutation_scene_handlers.jsx +1226 -0
  25. package/host/lib/mutation_shape_handlers.jsx +358 -0
  26. package/host/lib/mutation_timeline_handlers.jsx +137 -0
  27. package/host/lib/property_utils.jsx +105 -0
  28. package/host/lib/query_handlers.jsx +427 -0
  29. package/package.json +21 -0
  30. package/scripts/signing/build-zxp.sh +56 -0
  31. package/scripts/signing/create-dev-cert.sh +97 -0
  32. package/scripts/signing/install-zxp.sh +29 -0
  33. package/templates/skills/aftereffects-cli.SKILL.md +74 -0
  34. package/templates/skills/aftereffects-declarative.SKILL.md +112 -0
@@ -0,0 +1,427 @@
1
+ function getLayers() {
2
+ try {
3
+ ensureJSON();
4
+ var comp = app.project.activeItem;
5
+ if (!comp || !(comp instanceof CompItem)) {
6
+ log("getLayers(): Active composition not found.");
7
+ return encodePayload({ status: "error", message: "Active composition not found." });
8
+ }
9
+
10
+ var layers = [];
11
+ for (var i = 1; i <= comp.numLayers; i++) {
12
+ var layer = comp.layer(i);
13
+ layers.push({
14
+ id: layer.index,
15
+ layerUid: aeTryGetLayerUid(layer),
16
+ name: layer.name,
17
+ type: getLayerTypeName(layer)
18
+ });
19
+ }
20
+ return encodePayload(layers);
21
+ } catch (e) {
22
+ log("getLayers() threw: " + e.toString());
23
+ return encodePayload({ status: "error", message: e.toString() });
24
+ }
25
+ }
26
+
27
+ function listComps() {
28
+ try {
29
+ ensureJSON();
30
+ if (!app.project) {
31
+ return encodePayload([]);
32
+ }
33
+
34
+ var activeComp = app.project.activeItem;
35
+ var activeCompId = null;
36
+ if (activeComp && activeComp instanceof CompItem) {
37
+ activeCompId = activeComp.id;
38
+ }
39
+
40
+ var comps = [];
41
+ for (var i = 1; i <= app.project.numItems; i++) {
42
+ var item = app.project.item(i);
43
+ if (!item || !(item instanceof CompItem)) {
44
+ continue;
45
+ }
46
+ comps.push({
47
+ id: item.id,
48
+ name: item.name,
49
+ width: item.width,
50
+ height: item.height,
51
+ pixelAspect: item.pixelAspect,
52
+ duration: item.duration,
53
+ frameRate: item.frameRate,
54
+ isActive: activeCompId !== null && item.id === activeCompId
55
+ });
56
+ }
57
+ return encodePayload(comps);
58
+ } catch (e) {
59
+ log("listComps() threw: " + e.toString());
60
+ return encodePayload({ status: "error", message: e.toString() });
61
+ }
62
+ }
63
+
64
+ function getProperties(layerId, optionsJSON) {
65
+ try {
66
+ ensureJSON();
67
+ var comp = app.project.activeItem;
68
+ if (!comp || !(comp instanceof CompItem)) {
69
+ log("getProperties(): Active composition not found.");
70
+ return encodePayload({ status: "Error", message: "Active composition not found." });
71
+ }
72
+
73
+ var options = {};
74
+ if (optionsJSON && optionsJSON !== "null") {
75
+ try {
76
+ options = JSON.parse(optionsJSON);
77
+ } catch (e) {
78
+ log("getProperties(): Failed to parse options JSON - " + e.toString());
79
+ options = {};
80
+ }
81
+ }
82
+
83
+ function normalizeStringArray(value) {
84
+ if (!value) {
85
+ return [];
86
+ }
87
+ if (value instanceof Array) {
88
+ var filtered = [];
89
+ for (var i = 0; i < value.length; i++) {
90
+ var entry = value[i];
91
+ if (typeof entry === "string" && entry.length > 0) {
92
+ filtered.push(entry);
93
+ }
94
+ }
95
+ return filtered;
96
+ }
97
+ if (typeof value === "string" && value.length > 0) {
98
+ return [value];
99
+ }
100
+ return [];
101
+ }
102
+
103
+ function parseMaxDepth(rawDepth) {
104
+ if (rawDepth === null || rawDepth === undefined) {
105
+ return null;
106
+ }
107
+ var parsed = parseInt(rawDepth, 10);
108
+ if (isNaN(parsed) || parsed <= 0) {
109
+ return null;
110
+ }
111
+ return parsed;
112
+ }
113
+
114
+ var includeGroups = normalizeStringArray(options.includeGroups);
115
+ var excludeGroups = normalizeStringArray(options.excludeGroups);
116
+ var maxDepth = parseMaxDepth(options.maxDepth);
117
+ var includeGroupChildren = options.includeGroupChildren === true;
118
+ var evaluationTime = null;
119
+ if (options.time !== null && options.time !== undefined) {
120
+ evaluationTime = Number(options.time);
121
+ if (isNaN(evaluationTime)) {
122
+ return encodePayload({ status: "Error", message: "time must be a number." });
123
+ }
124
+ }
125
+
126
+ var layerName = null;
127
+ if (options.layerName !== null && options.layerName !== undefined) {
128
+ layerName = String(options.layerName);
129
+ }
130
+ var resolvedLayer = aeResolveLayer(comp, layerId, layerName);
131
+ if (resolvedLayer.error) {
132
+ log("getProperties(): " + resolvedLayer.error);
133
+ return encodePayload({ status: "Error", message: resolvedLayer.error });
134
+ }
135
+ var layer = resolvedLayer.layer;
136
+ var properties = [];
137
+
138
+ function shouldSkipTopLevel(matchName, depth) {
139
+ if (depth !== 0 || !matchName || matchName.length === 0) {
140
+ return false;
141
+ }
142
+ if (includeGroups.length > 0 && !aeArrayContains(includeGroups, matchName)) {
143
+ return true;
144
+ }
145
+ if (excludeGroups.length > 0 && aeArrayContains(excludeGroups, matchName)) {
146
+ return true;
147
+ }
148
+ return false;
149
+ }
150
+
151
+ function scanProperties(propGroup, pathPrefix, depth, forceRecursive) {
152
+ if (!propGroup || typeof propGroup.numProperties !== "number") {
153
+ return;
154
+ }
155
+ for (var i = 1; i <= propGroup.numProperties; i++) {
156
+ var prop = propGroup.property(i);
157
+ if (!prop) {
158
+ continue;
159
+ }
160
+
161
+ var identifier = aeGetPropertyIdentifier(prop, i);
162
+ var currentPath = pathPrefix ? pathPrefix + "." + identifier : identifier;
163
+ var nextDepth = depth + 1;
164
+
165
+ var matchName = "";
166
+ try {
167
+ matchName = prop.matchName || "";
168
+ } catch (eMatch) {}
169
+
170
+ if (shouldSkipTopLevel(matchName, depth)) {
171
+ continue;
172
+ }
173
+ if (!forceRecursive && maxDepth !== null && nextDepth > maxDepth) {
174
+ continue;
175
+ }
176
+
177
+ if (aeIsPropertyNode(prop)) {
178
+ if (!aeCanExposeProperty(prop)) {
179
+ continue;
180
+ }
181
+ var hasExpression = false;
182
+ try {
183
+ hasExpression = prop.expressionEnabled;
184
+ } catch (e) {}
185
+ properties.push({
186
+ name: prop.name,
187
+ path: currentPath,
188
+ value: aePropertyValueToString(prop),
189
+ hasExpression: hasExpression
190
+ });
191
+ }
192
+
193
+ var shouldRecurse = aeCanTraverseProperty(prop)
194
+ && (forceRecursive || maxDepth === null || nextDepth < maxDepth);
195
+ if (!shouldRecurse) {
196
+ continue;
197
+ }
198
+ var childForceRecursive = forceRecursive;
199
+ if (includeGroupChildren && depth === 0 && matchName && aeArrayContains(includeGroups, matchName)) {
200
+ childForceRecursive = true;
201
+ }
202
+ scanProperties(prop, currentPath, nextDepth, childForceRecursive);
203
+ }
204
+ }
205
+
206
+ var previousTime = null;
207
+ if (evaluationTime !== null) {
208
+ previousTime = comp.time;
209
+ comp.time = evaluationTime;
210
+ }
211
+ try {
212
+ scanProperties(layer, "", 0, false);
213
+ } finally {
214
+ if (evaluationTime !== null && previousTime !== null) {
215
+ comp.time = previousTime;
216
+ }
217
+ }
218
+ return encodePayload(properties);
219
+ } catch (e) {
220
+ log("getProperties() threw: " + e.toString());
221
+ return encodePayload({ status: "Error", message: e.toString() });
222
+ }
223
+ }
224
+
225
+ function getSelectedProperties() {
226
+ try {
227
+ ensureJSON();
228
+ var comp = app.project.activeItem;
229
+ if (!comp || !(comp instanceof CompItem)) {
230
+ return encodePayload({ status: "Error", message: "Active composition not found." });
231
+ }
232
+
233
+ var selectedLayers = comp.selectedLayers;
234
+ if (!selectedLayers || selectedLayers.length === 0) {
235
+ return encodePayload([]);
236
+ }
237
+
238
+ function getPathIdentifier(prop) {
239
+ return aeGetPropertyIdentifier(prop, null);
240
+ }
241
+
242
+ function buildPropertyPath(prop) {
243
+ var segments = [];
244
+ var current = prop;
245
+ var guard = 0;
246
+ while (current && guard < 100) {
247
+ var parent = null;
248
+ try {
249
+ parent = current.parentProperty;
250
+ } catch (eParent) {
251
+ parent = null;
252
+ }
253
+ if (!parent) {
254
+ break;
255
+ }
256
+ segments.unshift(getPathIdentifier(current));
257
+ current = parent;
258
+ guard += 1;
259
+ }
260
+ if (segments.length === 0) {
261
+ return "";
262
+ }
263
+ return segments.join(".");
264
+ }
265
+
266
+ var selectedPropsPayload = [];
267
+ for (var i = 0; i < selectedLayers.length; i++) {
268
+ var layer = selectedLayers[i];
269
+ if (!layer) {
270
+ continue;
271
+ }
272
+ var props;
273
+ try {
274
+ props = layer.selectedProperties;
275
+ } catch (eProps) {
276
+ props = null;
277
+ }
278
+ if (!props || props.length === 0) {
279
+ continue;
280
+ }
281
+ for (var j = 0; j < props.length; j++) {
282
+ var prop = props[j];
283
+ if (!prop || !aeIsPropertyNode(prop) || !aeCanExposeProperty(prop)) {
284
+ continue;
285
+ }
286
+
287
+ var path = buildPropertyPath(prop);
288
+ if (!path || path.length === 0) {
289
+ continue;
290
+ }
291
+
292
+ var hasExpression = false;
293
+ try {
294
+ hasExpression = prop.expressionEnabled;
295
+ } catch (eHas) {}
296
+
297
+ selectedPropsPayload.push({
298
+ layerId: layer.index,
299
+ layerName: layer.name,
300
+ name: prop.name,
301
+ path: path,
302
+ value: aePropertyValueToString(prop),
303
+ hasExpression: hasExpression
304
+ });
305
+ }
306
+ }
307
+
308
+ return encodePayload(selectedPropsPayload);
309
+ } catch (e) {
310
+ log("getSelectedProperties() threw: " + e.toString());
311
+ return encodePayload({ status: "Error", message: e.toString() });
312
+ }
313
+ }
314
+
315
+ function getExpressionErrors() {
316
+ try {
317
+ ensureJSON();
318
+ var comp = app.project.activeItem;
319
+ if (!comp || !(comp instanceof CompItem)) {
320
+ return encodePayload({ status: "Error", message: "Active composition not found." });
321
+ }
322
+
323
+ function getPathIdentifier(prop) {
324
+ return aeGetPropertyIdentifier(prop, null);
325
+ }
326
+
327
+ function buildPropertyPath(prop) {
328
+ var segments = [];
329
+ var current = prop;
330
+ var guard = 0;
331
+ while (current && guard < 100) {
332
+ var parent = null;
333
+ try {
334
+ parent = current.parentProperty;
335
+ } catch (eParent) {
336
+ parent = null;
337
+ }
338
+ if (!parent) {
339
+ break;
340
+ }
341
+ segments.unshift(getPathIdentifier(current));
342
+ current = parent;
343
+ guard += 1;
344
+ }
345
+ if (segments.length === 0) {
346
+ return "";
347
+ }
348
+ return segments.join(".");
349
+ }
350
+
351
+ function collectLayerExpressionErrors(layer) {
352
+ var issues = [];
353
+ function scan(prop) {
354
+ if (!prop) {
355
+ return;
356
+ }
357
+
358
+ if (aeCanTraverseProperty(prop)) {
359
+ for (var i = 1; i <= prop.numProperties; i++) {
360
+ scan(prop.property(i));
361
+ }
362
+ return;
363
+ }
364
+
365
+ var canSetExpression = false;
366
+ try {
367
+ canSetExpression = prop.canSetExpression === true;
368
+ } catch (eCanSet) {}
369
+ if (!canSetExpression) {
370
+ return;
371
+ }
372
+
373
+ var enabled = false;
374
+ try {
375
+ enabled = prop.expressionEnabled === true;
376
+ } catch (eEnabled) {}
377
+ if (!enabled) {
378
+ return;
379
+ }
380
+
381
+ var errorMessage = null;
382
+ try {
383
+ if (typeof prop.expressionError === "string" && prop.expressionError.length > 0) {
384
+ errorMessage = prop.expressionError;
385
+ }
386
+ } catch (eErrorRead) {}
387
+ if (!errorMessage) {
388
+ return;
389
+ }
390
+
391
+ issues.push({
392
+ layerId: layer.index,
393
+ layerUid: aeTryGetLayerUid(layer),
394
+ layerName: layer.name,
395
+ propertyPath: buildPropertyPath(prop),
396
+ propertyName: prop.name,
397
+ message: errorMessage
398
+ });
399
+ }
400
+
401
+ scan(layer);
402
+ return issues;
403
+ }
404
+
405
+ var allIssues = [];
406
+ for (var i = 1; i <= comp.numLayers; i++) {
407
+ var layer = comp.layer(i);
408
+ if (!layer) {
409
+ continue;
410
+ }
411
+ var layerIssues = collectLayerExpressionErrors(layer);
412
+ for (var j = 0; j < layerIssues.length; j++) {
413
+ allIssues.push(layerIssues[j]);
414
+ }
415
+ }
416
+
417
+ return encodePayload({
418
+ compId: comp.id,
419
+ compName: comp.name,
420
+ count: allIssues.length,
421
+ issues: allIssues
422
+ });
423
+ } catch (e) {
424
+ log("getExpressionErrors() threw: " + e.toString());
425
+ return encodePayload({ status: "Error", message: e.toString() });
426
+ }
427
+ }
package/package.json ADDED
@@ -0,0 +1,21 @@
1
+ {
2
+ "name": "ae-agent-setup",
3
+ "version": "0.2.0",
4
+ "description": "One-command installer for the ae-agent-skill After Effects extension and CLI.",
5
+ "type": "module",
6
+ "bin": {
7
+ "ae-agent-setup": "bin/ae-agent-setup.mjs"
8
+ },
9
+ "files": [
10
+ "bin",
11
+ "templates",
12
+ "scripts/signing",
13
+ "CSXS",
14
+ "client",
15
+ "host"
16
+ ],
17
+ "engines": {
18
+ "node": ">=18"
19
+ },
20
+ "license": "MIT"
21
+ }
@@ -0,0 +1,56 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ # Build a signed ZXP from this repository.
5
+ # Required env:
6
+ # SIGN_CERT_P12=certs/dev-cert.p12
7
+ # SIGN_CERT_PASSWORD=your-pass
8
+ # Optional env:
9
+ # ZXPSIGNCMD_BIN=ZXPSignCmd
10
+
11
+ ROOT_DIR="$(cd "$(dirname "$0")/../.." && pwd)"
12
+ ZXPSIGNCMD_BIN="${ZXPSIGNCMD_BIN:-ZXPSignCmd}"
13
+ DIST_DIR="${DIST_DIR:-${ROOT_DIR}/dist}"
14
+ STAGE_DIR="${DIST_DIR}/stage/ae-agent-skill"
15
+ CERT_P12="${SIGN_CERT_P12:-}"
16
+ CERT_PASSWORD="${SIGN_CERT_PASSWORD:-}"
17
+ TIMESTAMP_URL="${TIMESTAMP_URL:-http://timestamp.digicert.com}"
18
+
19
+ if [[ -z "${CERT_P12}" ]]; then
20
+ echo "SIGN_CERT_P12 is required. Example: SIGN_CERT_P12=certs/dev-cert.p12" >&2
21
+ exit 1
22
+ fi
23
+
24
+ if [[ -z "${CERT_PASSWORD}" ]]; then
25
+ echo "SIGN_CERT_PASSWORD is required." >&2
26
+ exit 1
27
+ fi
28
+
29
+ if ! command -v "${ZXPSIGNCMD_BIN}" >/dev/null 2>&1; then
30
+ echo "ZXPSignCmd not found. Set ZXPSIGNCMD_BIN or install ZXPSignCmd." >&2
31
+ exit 1
32
+ fi
33
+
34
+ if [[ ! -f "${ROOT_DIR}/CSXS/manifest.xml" ]]; then
35
+ echo "CSXS/manifest.xml not found." >&2
36
+ exit 1
37
+ fi
38
+
39
+ VERSION="$(rg -o 'ExtensionBundleVersion=\"[^\"]+\"' "${ROOT_DIR}/CSXS/manifest.xml" | head -n1 | sed -E 's/.*\"([^\"]+)\"/\1/')"
40
+ if [[ -z "${VERSION}" ]]; then
41
+ echo "Failed to parse ExtensionBundleVersion from CSXS/manifest.xml" >&2
42
+ exit 1
43
+ fi
44
+
45
+ OUT_ZXP="${DIST_DIR}/ae-agent-skill-${VERSION}.zxp"
46
+
47
+ rm -rf "${STAGE_DIR}"
48
+ mkdir -p "${STAGE_DIR}" "${DIST_DIR}"
49
+
50
+ cp -R "${ROOT_DIR}/CSXS" "${STAGE_DIR}/"
51
+ cp -R "${ROOT_DIR}/client" "${STAGE_DIR}/"
52
+ cp -R "${ROOT_DIR}/host" "${STAGE_DIR}/"
53
+
54
+ "${ZXPSIGNCMD_BIN}" -sign "${STAGE_DIR}" "${OUT_ZXP}" "${CERT_P12}" "${CERT_PASSWORD}" -tsa "${TIMESTAMP_URL}"
55
+
56
+ echo "Built signed ZXP: ${OUT_ZXP}"
@@ -0,0 +1,97 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ # Create a development self-signed certificate for CEP extension signing.
5
+ # Usage:
6
+ # scripts/signing/create-dev-cert.sh --password "your-pass"
7
+ # Optional:
8
+ # --country JP --state Tokyo --org "Example Inc." --unit Dev --name "Your Name" --email you@example.com
9
+
10
+ CERT_DIR="${CERT_DIR:-certs}"
11
+ CERT_FILE="${CERT_FILE:-${CERT_DIR}/dev-cert.p12}"
12
+ KEY_FILE="${KEY_FILE:-${CERT_DIR}/dev-cert.key.pem}"
13
+ CRT_FILE="${CRT_FILE:-${CERT_DIR}/dev-cert.crt.pem}"
14
+
15
+ COUNTRY="JP"
16
+ STATE="Tokyo"
17
+ CITY="Tokyo"
18
+ ORG="AE Agent Skills"
19
+ UNIT="Development"
20
+ NAME="AE Agent Skills Dev"
21
+ EMAIL="dev@example.com"
22
+ PASSWORD=""
23
+
24
+ while [[ $# -gt 0 ]]; do
25
+ case "$1" in
26
+ --password)
27
+ PASSWORD="$2"
28
+ shift 2
29
+ ;;
30
+ --country)
31
+ COUNTRY="$2"
32
+ shift 2
33
+ ;;
34
+ --state)
35
+ STATE="$2"
36
+ shift 2
37
+ ;;
38
+ --org)
39
+ ORG="$2"
40
+ shift 2
41
+ ;;
42
+ --city)
43
+ CITY="$2"
44
+ shift 2
45
+ ;;
46
+ --unit)
47
+ UNIT="$2"
48
+ shift 2
49
+ ;;
50
+ --name)
51
+ NAME="$2"
52
+ shift 2
53
+ ;;
54
+ --email)
55
+ EMAIL="$2"
56
+ shift 2
57
+ ;;
58
+ *)
59
+ echo "Unknown argument: $1" >&2
60
+ exit 1
61
+ ;;
62
+ esac
63
+ done
64
+
65
+ if [[ -z "${PASSWORD}" ]]; then
66
+ echo "Missing required argument: --password" >&2
67
+ exit 1
68
+ fi
69
+
70
+ if ! command -v openssl >/dev/null 2>&1; then
71
+ echo "openssl not found." >&2
72
+ exit 1
73
+ fi
74
+
75
+ mkdir -p "${CERT_DIR}"
76
+
77
+ if [[ -f "${CERT_FILE}" || -f "${KEY_FILE}" || -f "${CRT_FILE}" ]]; then
78
+ echo "Certificate files already exist under ${CERT_DIR}. Remove them before recreating." >&2
79
+ exit 1
80
+ fi
81
+
82
+ SUBJECT="/C=${COUNTRY}/ST=${STATE}/L=${CITY}/O=${ORG}/OU=${UNIT}/CN=${NAME}/emailAddress=${EMAIL}"
83
+
84
+ openssl req -x509 -newkey rsa:2048 -sha256 -days 3650 -nodes \
85
+ -subj "${SUBJECT}" \
86
+ -keyout "${KEY_FILE}" \
87
+ -out "${CRT_FILE}"
88
+
89
+ openssl pkcs12 -export \
90
+ -inkey "${KEY_FILE}" \
91
+ -in "${CRT_FILE}" \
92
+ -out "${CERT_FILE}" \
93
+ -passout "pass:${PASSWORD}"
94
+
95
+ echo "Created development certificate: ${CERT_FILE}"
96
+ echo "Private key: ${KEY_FILE}"
97
+ echo "Certificate: ${CRT_FILE}"
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ # Install a signed ZXP package with ExManCmd.
5
+ # Usage:
6
+ # scripts/signing/install-zxp.sh dist/ae-agent-skill-0.2.0.zxp
7
+ # Optional env:
8
+ # EXMANCMD_BIN=ExManCmd
9
+
10
+ EXMANCMD_BIN="${EXMANCMD_BIN:-ExManCmd}"
11
+ ZXP_PATH="${1:-}"
12
+
13
+ if [[ -z "${ZXP_PATH}" ]]; then
14
+ echo "Usage: $0 <path-to-zxp>" >&2
15
+ exit 1
16
+ fi
17
+
18
+ if ! command -v "${EXMANCMD_BIN}" >/dev/null 2>&1; then
19
+ echo "ExManCmd not found. Set EXMANCMD_BIN or install ExManCmd." >&2
20
+ exit 1
21
+ fi
22
+
23
+ if [[ ! -f "${ZXP_PATH}" ]]; then
24
+ echo "ZXP not found: ${ZXP_PATH}" >&2
25
+ exit 1
26
+ fi
27
+
28
+ "${EXMANCMD_BIN}" --install "${ZXP_PATH}"
29
+ echo "Installed: ${ZXP_PATH}"
@@ -0,0 +1,74 @@
1
+ ---
2
+ name: aftereffects-cli-legacy
3
+ description: Command-by-command After Effects editing via ae-cli. Best for surgical edits on existing human-made scenes (especially partial expression/property changes) and for debugging.
4
+ ---
5
+
6
+ # aftereffects-cli-legacy
7
+
8
+ 低レベル CLI 操作(命令型)のスキル。
9
+ 新規構築の主軸は宣言型 `aftereffects-declarative` だが、**既存シーンへの外科的編集ではこのスキルを第一選択**にする。
10
+
11
+ ## 位置づけ
12
+
13
+ - 新規を組み上げる: 宣言型 `apply-scene` が主役
14
+ - 既存を部分修正する: 本スキル(命令型)が主役
15
+ - 両者は排他的ではなく、同一作業内で併用してよい
16
+
17
+ ## このスキルを使うべきケース
18
+
19
+ - 人間が作った既存シーン(scene JSON 未管理)に部分変更を入れる
20
+ - 複雑な既存レイヤーへ expression をピンポイント適用/調整する
21
+ - 既存 comp 全体を再宣言せず、1-2 箇所だけ安全に直したい
22
+ - 調査/デバッグで単発コマンドを叩きたい
23
+ - 宣言型で未対応の操作が必要
24
+
25
+ ## 基本フロー
26
+
27
+ 1. まず疎通確認: `ae-cli health`
28
+ 2. 状態確認:
29
+ - `ae-cli list-comps`
30
+ - `ae-cli layers`
31
+ - `ae-cli selected-properties`
32
+ - `ae-cli expression-errors`
33
+ 3. 必要な更新コマンドを最小回数で実行
34
+ 4. 変更後に `layers` / `properties` で結果確認
35
+
36
+ ## 宣言型へ切り替える目安
37
+
38
+ - 同種の変更を複数レイヤーへ繰り返す
39
+ - 再実行可能な形で変更履歴を残したい
40
+ - 変更対象を `layers[].id` で安定管理できる
41
+ - シーン全体を再構築/置換したい
42
+
43
+ ## 主要コマンド(レガシー)
44
+
45
+ - comp:
46
+ - `ae-cli create-comp ...`
47
+ - `ae-cli set-active-comp ...`
48
+ - `ae-cli delete-comp ...`
49
+ - レイヤー/プロパティ:
50
+ - `ae-cli add-layer ...`
51
+ - `ae-cli set-property ...`
52
+ - `ae-cli set-keyframe ...`
53
+ - `ae-cli set-expression ...`
54
+ - `ae-cli add-effect ...`
55
+ - `ae-cli add-essential-property ...`
56
+ - `ae-cli add-shape-repeater ...`
57
+ - タイムライン:
58
+ - `ae-cli set-in-out-point ...`
59
+ - `ae-cli move-layer-time ...`
60
+ - `ae-cli set-cti ...`
61
+ - `ae-cli set-work-area ...`
62
+ - 構造編集:
63
+ - `ae-cli parent-layer ...`
64
+ - `ae-cli precompose ...`
65
+ - `ae-cli duplicate-layer ...`
66
+ - `ae-cli move-layer-order ...`
67
+ - `ae-cli delete-layer ...`
68
+
69
+ ## 注意
70
+
71
+ - 既存シーンへの単発・部分修正は命令型の方が安全な場合が多い(影響範囲を局所化しやすい)。
72
+ - 同じ処理を複数コマンドで繰り返す必要がある場合は、宣言型 `apply-scene` へ切り替える。
73
+ - expression の不調は `ae-cli expression-errors` で確認する。
74
+ - scene JSON を一時的に作る場合は `work/`(作業中)と `done/`(完了保管)を使い、どちらもコミットしない。