@hybridlabor-api/aos 4.12.0 → 4.13.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 (33) hide show
  1. package/.agents/vendor-manifest.json +6 -0
  2. package/.claude/agents/reviewer.md +1 -1
  3. package/.claude/agents/techlead.md +1 -1
  4. package/.opencode/agents/architect.md +1 -1
  5. package/.opencode/agents/reviewer.md +1 -1
  6. package/.opencode/agents/techlead.md +1 -1
  7. package/README.de.md +297 -725
  8. package/README.md +295 -831
  9. package/README.pt.md +317 -677
  10. package/THIRD_PARTY_NOTICES.md +94 -0
  11. package/bin/aos-store.mjs +48 -45
  12. package/installer.js +22 -20
  13. package/lib/scenario-store-index.json +1 -0
  14. package/lib/store-shared.mjs +81 -4
  15. package/lib/store-ui/index.html +14 -5
  16. package/lib/store-ui/server.mjs +44 -21
  17. package/package.json +2 -2
  18. package/scripts/build-plugin-manifest.mjs +36 -13
  19. package/scripts/build-scenario-store-index.mjs +154 -0
  20. package/skills/bdbrainstorm/SKILL.md +1 -0
  21. package/skills/global_config/agent-pipeline/SKILL.md +4 -0
  22. package/skills/global_config/aos-store/SKILL.md +4 -4
  23. package/skills/global_config/design-control-loop/SKILL.md +184 -0
  24. package/skills/global_config/design-control-loop/references/agent-iteration.ts +174 -0
  25. package/skills/global_config/design-control-loop/references/agent-runner-templates.md +157 -0
  26. package/skills/global_config/design-control-loop/references/control-loop-taxonomy.md +75 -0
  27. package/skills/global_config/design-control-loop/references/example-control-loop.md +57 -0
  28. package/skills/global_config/design-control-loop/references/example-skill.md +171 -0
  29. package/skills/global_config/design-control-loop/references/memory-template.md +7 -0
  30. package/skills/global_config/design-control-loop/references/prompt-template.md +58 -0
  31. package/skills/global_config/design-control-loop/references/response-template.md +103 -0
  32. package/skills/global_config/design-control-loop/references/skill-template.md +57 -0
  33. package/skills/global_config/design-control-loop/references/workflow-template.yml +273 -0
@@ -413,7 +413,101 @@ store. Reasons are in `docs/handover/` and the branch description for
413
413
 
414
414
  ---
415
415
 
416
+ ## humanlayer/skills
417
+
418
+ <https://github.com/humanlayer/skills> — MIT. Copyright (c) 2026 HumanLayer.
419
+
420
+ Tracked in `.agents/vendor-manifest.json` at pinned commit
421
+ `ca7c8088db69e315a8b2deea43820270457f8f3c`.
422
+
423
+ One skill is vendored as `skills/global_config/design-control-loop/`
424
+ (`SKILL.md` plus its 10 `references/` files, including the unmodified
425
+ `agent-iteration.ts` and `workflow-template.yml` templates). The upstream
426
+ `.claude-plugin/plugin.json` was not carried over.
427
+
428
+ AOS modifications, all in `SKILL.md` only: `category:`, `source:` and `license:`
429
+ added to the frontmatter; one delimited "AOS notes" section inserted directly
430
+ after the H1. Upstream prose and all `references/` files are byte-identical.
431
+
432
+ ```
433
+ MIT License
434
+
435
+ Copyright (c) 2026 HumanLayer
436
+
437
+ Permission is hereby granted, free of charge, to any person obtaining a copy
438
+ of this software and associated documentation files (the "Software"), to deal
439
+ in the Software without restriction, including without limitation the rights
440
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
441
+ copies of the Software, and to permit persons to whom the Software is
442
+ furnished to do so, subject to the following conditions:
443
+
444
+ The above copyright notice and this permission notice shall be included in all
445
+ copies or substantial portions of the Software.
446
+
447
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
448
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
449
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
450
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
451
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
452
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
453
+ SOFTWARE.
454
+ ```
455
+
456
+ ---
457
+
416
458
  ## Note on `mcps/`
417
459
 
418
460
  Sub-repositories vendored under `mcps/` carry their own `LICENSE` files in
419
461
  their own directories and are not restated here.
462
+
463
+ ---
464
+
465
+ ## scenario-labs/skills
466
+
467
+ - **Upstream:** <https://github.com/scenario-labs/skills>
468
+ - **License:** MIT
469
+ - **Copyright:** Copyright (c) 2026 Scenario
470
+ - **Pinned commit:** `5786accd3bb6d79808330230c698898862e8a1d4`
471
+
472
+ ### Store index (second AOS Store source)
473
+
474
+ The AOS Store lists a starter set of skills from this project through
475
+ `lib/scenario-store-index.json` (regenerated by
476
+ `scripts/build-scenario-store-index.mjs` from a local clone and the pinned
477
+ commit). The index holds metadata and a per-file SHA-256 list only. The files
478
+ are not vendored: they are downloaded from
479
+ `https://raw.githubusercontent.com/scenario-labs/skills/<pinned commit>/` on
480
+ demand, only after an explicit `--net` or UI confirmation, and each file is
481
+ verified against its SHA-256 before anything is written.
482
+
483
+ Some of these skills contain Python and C# scripts, one of which
484
+ (`scenario-unity-expert`) executes code received through a local channel
485
+ without authentication. The store and its UI mark this before installation.
486
+
487
+ The MIT license covers the software and documentation in that repository. The
488
+ Scenario name and logo are trademarks of their owner and are **not** licensed
489
+ by it; AOS does not use the logo.
490
+
491
+ ```
492
+ MIT License
493
+
494
+ Copyright (c) 2026 Scenario
495
+
496
+ Permission is hereby granted, free of charge, to any person obtaining a copy
497
+ of this software and associated documentation files (the "Software"), to deal
498
+ in the Software without restriction, including without limitation the rights
499
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
500
+ copies of the Software, and to permit persons to whom the Software is
501
+ furnished to do so, subject to the following conditions:
502
+
503
+ The above copyright notice and this permission notice shall be included in all
504
+ copies or substantial portions of the Software.
505
+
506
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
507
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
508
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
509
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
510
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
511
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
512
+ SOFTWARE.
513
+ ```
package/bin/aos-store.mjs CHANGED
@@ -1,15 +1,13 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  import { createHash } from 'node:crypto';
4
- import { mkdirSync, readFileSync, writeFileSync } from 'node:fs';
5
- import { dirname, join, resolve } from 'node:path';
4
+ import { mkdirSync, writeFileSync } from 'node:fs';
5
+ import { dirname, resolve } from 'node:path';
6
6
  import { fileURLToPath } from 'node:url';
7
- import { fetchSkillFiles, fileTargets, localAgents, localSkills, targetsFor as sharedTargetsFor, validateFiles, writeSkillFiles } from '../lib/store-shared.mjs';
7
+ import { fetchSkillFiles, fileTargets, findItem, installPlan, loadSources, localAgents, localSkills, planSkillFiles, planSummary, targetsFor as sharedTargetsFor, validateFiles, writePlan } from '../lib/store-shared.mjs';
8
8
 
9
9
  const ROOT = resolve(dirname(fileURLToPath(import.meta.url)), '..');
10
- const INDEX_PATH = process.env.AOS_STORE_INDEX || join(ROOT, 'lib', 'ecc-store-index.json');
11
- const RAW_BASE = (process.env.AOS_STORE_RAW_BASE || 'https://raw.githubusercontent.com/affaan-m/ECC').replace(/\/+$/, '');
12
- const index = JSON.parse(readFileSync(INDEX_PATH, 'utf8'));
10
+ const sources = loadSources(ROOT);
13
11
  const args = process.argv.slice(2);
14
12
  const dryRun = args.includes('--dry-run');
15
13
  const project = args.includes('--project');
@@ -17,21 +15,9 @@ const net = args.includes('--net');
17
15
  const positional = args.filter((arg) => !arg.startsWith('--'));
18
16
  const option = (name) => args.find((arg) => arg === name || arg.startsWith(`${name}=`))?.split('=').slice(1).join('=');
19
17
 
20
- function loadIndex() {
21
- if (!index.pinned_commit || !index.skills || !index.subagents) throw new Error('Invalid AOS store index');
22
- return index;
23
- }
24
-
25
18
  function allItems(type) {
26
19
  const selected = type === 'agents' || type === 'subagents' ? 'subagents' : 'skills';
27
- return Object.entries(index[selected] || {}).map(([name, item]) => ({ name, item, type: selected }));
28
- }
29
-
30
- function findItem(name) {
31
- for (const type of ['skills', 'subagents']) {
32
- if (index[type]?.[name]) return { name, item: index[type][name], type };
33
- }
34
- return null;
20
+ return sources.flatMap((src) => Object.entries(src.index[selected] || {}).map(([name, item]) => ({ name, item, type: selected, src })));
35
21
  }
36
22
 
37
23
  const localSkillNames = () => new Set(localSkills(ROOT).keys());
@@ -42,15 +28,15 @@ function printItems(items) {
42
28
  console.log('No store items found.');
43
29
  return;
44
30
  }
45
- for (const { name, item, type } of items) {
31
+ for (const { name, item, type, src } of items) {
46
32
  const kind = type === 'skills' ? 'skill' : 'agent';
47
- console.log(`${kind}\t${name}\t${item.category}\t${item.description}`);
33
+ console.log(`${kind}\t${name}\t${item.category}\t${src.label}\t${item.description}`);
48
34
  }
49
35
  console.log(`\n${items.length} item(s).`);
50
36
  }
51
37
 
52
- async function download(upstreamPath) {
53
- const url = `${RAW_BASE}/${index.pinned_commit}/${upstreamPath}`;
38
+ async function download(src, upstreamPath) {
39
+ const url = `${src.raw_base}/${src.pinned_commit}/${upstreamPath}`;
54
40
  const response = await fetch(url, { headers: { 'user-agent': 'aos-store' } });
55
41
  if (!response.ok) throw new Error(`Download failed (${response.status}): ${url}`);
56
42
  return Buffer.from(await response.arrayBuffer());
@@ -60,42 +46,60 @@ const targetsFor = (type, name) => sharedTargetsFor(type, name, { project });
60
46
 
61
47
  async function install(name) {
62
48
  if (!/^[A-Za-z0-9_-]+$/.test(name)) throw new Error(`Invalid store name: ${name}`);
63
- const found = findItem(name);
49
+ const found = findItem(sources, name);
64
50
  if (!found) throw new Error(`Store item not found: ${name}`);
65
- const { item, type } = found;
51
+ const { type, src } = found;
66
52
  const coreNames = type === 'skills' ? localSkillNames() : localAgentNames();
67
53
  if (coreNames.has(name)) {
68
54
  console.log(`Skipped ${name}: AOS core already provides it.`);
69
55
  return;
70
56
  }
71
- const targets = targetsFor(type, name);
72
- const multi = type === 'skills' && item.files?.length > 1;
73
- if (multi) validateFiles(item.files);
57
+ const plan = installPlan(sources, found).filter((p) => !(p.type === 'skills' && p.name !== name && localSkillNames().has(p.name)));
58
+ for (const p of plan) if (p.item.files?.length > 1) validateFiles(p.item.files);
59
+ const multi = (p) => p.type === 'skills' && p.item.files?.length > 1;
60
+ const summary = planSummary(plan);
61
+ const notes = [];
62
+ if (summary.requires.length) notes.push(`also installs required skill(s): ${summary.requires.join(', ')}`);
63
+ if (summary.execBridge) notes.push(`WARNING: contains a script that executes code received over a local socket or file channel without authentication (${summary.execBridgeFiles.map((f) => `${f.skill}/${f.path}`).join(', ')}); install only if you use this bridge`);
64
+ else if (summary.hasScripts) notes.push(`WARNING: contains ${summary.scriptCount} script file(s); review before use`);
74
65
  if (dryRun) {
75
- for (const target of targets) {
76
- if (multi) for (const file of fileTargets(target, item.files)) console.log(`[dry-run] write ${file}`);
77
- else console.log(`[dry-run] write ${target}`);
66
+ for (const note of notes) console.log(note);
67
+ for (const p of plan) {
68
+ for (const target of targetsFor(p.type, p.name)) {
69
+ if (multi(p)) for (const file of fileTargets(target, p.item.files)) console.log(`[dry-run] write ${file}`);
70
+ else console.log(`[dry-run] write ${target}`);
71
+ }
78
72
  }
79
73
  return;
80
74
  }
81
75
  if (!net) {
82
76
  throw new Error(`Downloading '${name}' requires internet access to fetch from upstream GitHub. Pass --net to confirm: aos store install ${name} --net`);
83
77
  }
84
- if (multi) {
85
- const files = await fetchSkillFiles(RAW_BASE, index.pinned_commit, dirname(item.upstream_path), item.files);
86
- for (const target of targets) {
87
- const written = writeSkillFiles(target, files);
88
- console.log(`Installed ${name} (${written.length} files) -> ${dirname(target)}`);
78
+ // All-or-nothing: download, hash-check and path-check every skill in memory before the first write.
79
+ const writes = [];
80
+ for (const p of plan) {
81
+ const pSrc = p.src;
82
+ let files;
83
+ if (multi(p)) {
84
+ files = await fetchSkillFiles(pSrc.raw_base, pSrc.pinned_commit, dirname(p.item.upstream_path), p.item.files);
85
+ } else {
86
+ const content = await download(pSrc, p.item.upstream_path);
87
+ const digest = createHash('sha256').update(content).digest('hex');
88
+ if (digest !== p.item.sha256) throw new Error(`SHA-256 mismatch for ${p.name}: expected ${p.item.sha256}, got ${digest}`);
89
+ files = null;
90
+ for (const target of targetsFor(p.type, p.name)) writes.push({ name: p.name, single: { target, content } });
89
91
  }
90
- return;
92
+ if (files) for (const target of targetsFor(p.type, p.name)) writes.push({ name: p.name, target, plan: planSkillFiles(target, files) });
91
93
  }
92
- const content = await download(item.upstream_path);
93
- const digest = createHash('sha256').update(content).digest('hex');
94
- if (digest !== item.sha256) throw new Error(`SHA-256 mismatch for ${name}: expected ${item.sha256}, got ${digest}`);
95
- for (const target of targets) {
96
- mkdirSync(dirname(target), { recursive: true });
97
- writeFileSync(target, content);
98
- console.log(`Installed ${name} -> ${target}`);
94
+ for (const note of notes) console.log(note);
95
+ for (const w of writes) {
96
+ if (w.single) {
97
+ mkdirSync(dirname(w.single.target), { recursive: true });
98
+ writeFileSync(w.single.target, w.single.content);
99
+ console.log(`Installed ${w.name} -> ${w.single.target}`);
100
+ } else {
101
+ console.log(`Installed ${w.name} (${writePlan(w.plan).length} files) -> ${dirname(w.target)}`);
102
+ }
99
103
  }
100
104
  }
101
105
 
@@ -108,7 +112,6 @@ function usage() {
108
112
  }
109
113
 
110
114
  async function main() {
111
- loadIndex();
112
115
  const command = positional[0] || 'list';
113
116
  if (command === 'list') {
114
117
  printItems(allItems(option('--type') || 'skills'));
package/installer.js CHANGED
@@ -3354,27 +3354,27 @@ function loadPipelineConfig(projectDir = currentDir) {
3354
3354
  function resolveAgentConfig(agentSlug, harness, pipelineConfig = null) {
3355
3355
  const normSlug = agentSlug.toLowerCase().replace(/-/g, '_');
3356
3356
  const roleCfg = pipelineConfig ? (pipelineConfig[normSlug] || pipelineConfig[agentSlug]) : null;
3357
- if (roleCfg) {
3358
- if (roleCfg.model) {
3359
- return {
3360
- model: roleCfg.model,
3361
- tier: roleCfg.tier || 'custom',
3362
- enabled: roleCfg.enabled !== false,
3363
- harness: roleCfg.harness || harness
3364
- };
3365
- }
3366
- if (roleCfg.tier && CANONICAL_TIERS[roleCfg.tier] && CANONICAL_TIERS[roleCfg.tier][harness]) {
3367
- return {
3368
- model: CANONICAL_TIERS[roleCfg.tier][harness],
3369
- tier: roleCfg.tier,
3370
- enabled: roleCfg.enabled !== false,
3371
- harness: roleCfg.harness || harness
3372
- };
3373
- }
3357
+ // A role's model/harness describes where AO runs that role; only valid for that harness.
3358
+ const ownHarness = !!roleCfg && (!roleCfg.harness || roleCfg.harness === harness);
3359
+ if (roleCfg && ownHarness && roleCfg.model) {
3360
+ return {
3361
+ model: roleCfg.model,
3362
+ tier: roleCfg.tier || 'custom',
3363
+ enabled: roleCfg.enabled !== false,
3364
+ harness
3365
+ };
3366
+ }
3367
+ if (roleCfg && roleCfg.tier && CANONICAL_TIERS[roleCfg.tier] && CANONICAL_TIERS[roleCfg.tier][harness]) {
3368
+ return {
3369
+ model: CANONICAL_TIERS[roleCfg.tier][harness],
3370
+ tier: roleCfg.tier,
3371
+ enabled: roleCfg.enabled !== false,
3372
+ harness
3373
+ };
3374
3374
  }
3375
3375
  const defaultTier = (normSlug === 'architect' || normSlug === 'reviewer') ? 'reasoning_max' : 'standard_fast';
3376
3376
  const fallbackModel = (CANONICAL_TIERS[defaultTier] && CANONICAL_TIERS[defaultTier][harness]) || 'inherit';
3377
- return { model: fallbackModel, tier: defaultTier, enabled: true, harness };
3377
+ return { model: fallbackModel, tier: defaultTier, enabled: !(roleCfg && roleCfg.model && roleCfg.enabled === false), harness };
3378
3378
  }
3379
3379
 
3380
3380
  // Generates .claude/agents/<name>.md — Claude Code's native subagent format.
@@ -3385,7 +3385,8 @@ function compileClaudeAgents(agents, targetDir, pipelineConfig = null) {
3385
3385
  for (const a of agents) {
3386
3386
  const slug = a.name.toLowerCase().replace(/_/g, '-');
3387
3387
  const resolved = resolveAgentConfig(slug, 'claude', pipelineConfig);
3388
- const model = (resolved && resolved.model) ? resolved.model.toLowerCase() : (a.model ? a.model.toLowerCase() : 'inherit');
3388
+ let model = (resolved && resolved.model) ? resolved.model.toLowerCase() : (a.model ? a.model.toLowerCase() : 'inherit');
3389
+ if (!/^(opus|sonnet|haiku|inherit|claude-.+)$/.test(model)) model = CANONICAL_TIERS[resolved.tier in CANONICAL_TIERS ? resolved.tier : 'standard_fast'].claude;
3389
3390
  const body = [
3390
3391
  a.role,
3391
3392
  a.skills.length ? `**Primary skills:** ${a.skills.join(', ')}` : null,
@@ -3412,7 +3413,8 @@ function compileOpenCodeAgents(agents, targetDir, pipelineConfig = null) {
3412
3413
  for (const a of agents) {
3413
3414
  const slug = a.name.toLowerCase().replace(/_/g, '-');
3414
3415
  const resolved = resolveAgentConfig(slug, 'opencode', pipelineConfig);
3415
- const model = resolved && resolved.model ? resolved.model : null;
3416
+ let model = resolved && resolved.model ? resolved.model : null;
3417
+ if (model && !model.includes('/')) model = CANONICAL_TIERS[resolved.tier in CANONICAL_TIERS ? resolved.tier : 'standard_fast'].opencode;
3416
3418
  const body = [
3417
3419
  a.role,
3418
3420
  a.skills.length ? `**Primary skills:** ${a.skills.join(', ')}` : null,
@@ -0,0 +1 @@
1
+ {"version":"1.1.0","generated_at":"2026-09-28T16:20:46+02:00","pinned_commit":"5786accd3bb6d79808330230c698898862e8a1d4","source":{"id":"scenario","label":"Scenario","upstream":"https://github.com/scenario-labs/skills","raw_base":"https://raw.githubusercontent.com/scenario-labs/skills","license":"MIT","copyright":"Copyright (c) 2026 Scenario","third_party":true},"harness_support":["claude","antigravity","codex","cursor","roo"],"skills":{"scenario-blender-animation":{"description":"Use when animating characters or objects in Blender through Python, a bouncing ball, walk or run cy…","category":"media-eventtech","tier":"free","origin":"scenario","requires_auth":false,"sha256":"aff7450263b6435bd3d654147d971f38ed8effb37ea6aac86e21a3e574d4fa49","upstream_path":"skills/dcc/blender/scenario-blender-animation/SKILL.md","requires":["scenario-blender-expert"],"software":["Blender"],"hasScripts":true,"scriptCount":1,"execBridge":false,"execBridgeFiles":[],"files":[{"path":"SKILL.md","sha256":"aff7450263b6435bd3d654147d971f38ed8effb37ea6aac86e21a3e574d4fa49","size":20288,"exec":false},{"path":"references/critique.md","sha256":"cb00a869d0847033d0d5dcc31ced3c4b21169f2c40158e8907ff22384d9c71da","size":29916,"exec":false},{"path":"references/expert-notes.md","sha256":"811e83faf618f9edfcc273d1c21d903c76c787dd73e2092c9c0d31cb3fd06454","size":27069,"exec":false},{"path":"references/procedures.md","sha256":"514cf2ce6f40ecb1184cc57ee63c21deb76193111986d2238e4e909e3fa9155c","size":41355,"exec":false},{"path":"references/sources.md","sha256":"1b45bbf9c056ce1bfd266a3bdfd55677803898758d47f4bafe39bdb34043f662","size":9058,"exec":false},{"path":"scripts/bx_anim.py","sha256":"75db8650041f02d881bb5895130571c8ea7e437d2c20fa77154d961ab8c5f005","size":103257,"exec":false}]},"scenario-blender-expert":{"description":"Use when doing any 3D work in Blender through Python or an MCP bridge, headless or in a live sessio…","category":"media-eventtech","tier":"free","origin":"scenario","requires_auth":false,"sha256":"66af9735948dd16ed619c48fdd66b03124a4b521e70543df0f90f77fe6f55376","upstream_path":"skills/dcc/blender/scenario-blender-expert/SKILL.md","software":["Blender"],"hasScripts":true,"scriptCount":3,"execBridge":false,"execBridgeFiles":[],"files":[{"path":"SKILL.md","sha256":"66af9735948dd16ed619c48fdd66b03124a4b521e70543df0f90f77fe6f55376","size":12817,"exec":false},{"path":"references/blender-5.2-deltas.md","sha256":"a3edcef99a5d44c3853901417b9682b17bdda3185bc74f3d1ccada6c7c5f6677","size":118596,"exec":false},{"path":"references/bpy-reliability.md","sha256":"b43625d8ae078dd983fb8b4ddc3d99fa3b71d793142ee1956d6b306b24957566","size":24633,"exec":false},{"path":"scripts/bx_audit.py","sha256":"0ff01ffdc6f9413c42be15016c84fbf8f0696434e1b30cc5ad952813d748a0f8","size":9810,"exec":false},{"path":"scripts/bx_gui.py","sha256":"d5c020f27d2714f7f773da30cfa190017be453a621bb45f1eccececa3a33d60c","size":8951,"exec":false},{"path":"scripts/bx_review.py","sha256":"3862dc1452960f01219206bbc0f24d487810d0f4cda6ab41b97108e522e53986","size":14914,"exec":false}]},"scenario-blender-geometry-nodes":{"description":"Use when building or debugging Geometry Nodes, headless or live: procedural modeling, scattering ob…","category":"media-eventtech","tier":"free","origin":"scenario","requires_auth":false,"sha256":"c7465134d8506ad0975063974d5c462ea3638aadf9f316d74dc81c4b7a522923","upstream_path":"skills/dcc/blender/scenario-blender-geometry-nodes/SKILL.md","requires":["scenario-blender-expert"],"software":[],"hasScripts":true,"scriptCount":1,"execBridge":false,"execBridgeFiles":[],"files":[{"path":"SKILL.md","sha256":"c7465134d8506ad0975063974d5c462ea3638aadf9f316d74dc81c4b7a522923","size":16371,"exec":false},{"path":"references/critique.md","sha256":"88331d11fad74946a8989cdfd1d79703f65fe8efd8565c9be152bdbb85cb9e8b","size":11373,"exec":false},{"path":"references/expert-notes.md","sha256":"49fa43b73f7a423a078e2405bd962545a3f6a2a10fa3e57965914b2eb9320c11","size":18172,"exec":false},{"path":"references/procedures.md","sha256":"8f0b2dc24dcbd01296351cee4177cdd7ce11fa7077402293541c9040f85cb58d","size":55863,"exec":false},{"path":"references/sources.md","sha256":"fe060140ec154d9f0c49e0efbe996a9ebf10c1eb0de0467614cf9aa2116df374","size":7510,"exec":false},{"path":"scripts/bx_gn.py","sha256":"b325317f54e1e944f33f1af91e141c20bd5ebe7d3a3c487738fa70fd725639e0","size":57043,"exec":false}]},"scenario-blender-hard-surface":{"description":"Use when modeling hard-surface objects in Blender (sci-fi panels, props, weapons, vehicles, robots,…","category":"media-eventtech","tier":"free","origin":"scenario","requires_auth":false,"sha256":"f14733f37d3f0ee2654fcac4f9f0bb3c1bc3112854a38848f71736eb155a525d","upstream_path":"skills/dcc/blender/scenario-blender-hard-surface/SKILL.md","requires":["scenario-blender-expert"],"software":["Blender"],"hasScripts":true,"scriptCount":1,"execBridge":false,"execBridgeFiles":[],"files":[{"path":"SKILL.md","sha256":"f14733f37d3f0ee2654fcac4f9f0bb3c1bc3112854a38848f71736eb155a525d","size":18295,"exec":false},{"path":"references/critique.md","sha256":"32fae7dc8e3759556aa4571b868d5fcb783217ce9270c9802aa8b380ca24ee4d","size":14411,"exec":false},{"path":"references/expert-notes.md","sha256":"b9682d071a0fcafa03afa99ae9aecf3831176681d5e74eb7d10c27369386a87b","size":17875,"exec":false},{"path":"references/procedures.md","sha256":"6dbe1b7567a6ff1f21ff5debf4c9235cd7fc4ccea51f736a1b74448bcccca70c","size":25309,"exec":false},{"path":"references/sources.md","sha256":"71d90f1effa9477abdfe4bd77bea4d6b0fb8c21ae2ec449168d9b7d972ba77eb","size":7412,"exec":false},{"path":"scripts/bx_hardsurface.py","sha256":"a7eb60b356c16609573ea8bb0005cd1278e908dcd7593eb7ff1f749ceb7b9999","size":69529,"exec":false}]},"scenario-blender-lighting-rendering":{"description":"Use when lighting, rendering or compositing in Blender: light a character, product or hero shot, in…","category":"media-eventtech","tier":"free","origin":"scenario","requires_auth":false,"sha256":"93a3bea574312104922feba0884afa05332a2a7e12720995b9fabb948e0d297e","upstream_path":"skills/dcc/blender/scenario-blender-lighting-rendering/SKILL.md","requires":["scenario-blender-expert"],"software":["Blender"],"hasScripts":true,"scriptCount":1,"execBridge":false,"execBridgeFiles":[],"files":[{"path":"SKILL.md","sha256":"93a3bea574312104922feba0884afa05332a2a7e12720995b9fabb948e0d297e","size":20126,"exec":false},{"path":"references/critique.md","sha256":"04c4b949a62019d4cfe70cbe536dc96b2411fdd0c82980b750004e0f04ce38db","size":8035,"exec":false},{"path":"references/expert-notes.md","sha256":"fcf07efdf876fa500f96e25ce8477c56bd70c3fe13451ee85b26146c75d70f38","size":19917,"exec":false},{"path":"references/procedures.md","sha256":"774ce5f5c1577d3a74845b73bd716348cb587d0f0d7ea447a5dd64e706ff84a0","size":30203,"exec":false},{"path":"references/sources.md","sha256":"8beafcf59b2822a81ce09f255ef81cc333d12a464f0a58e6f6ac5e37c3679999","size":15667,"exec":false},{"path":"scripts/bx_light.py","sha256":"6f6624765f2afdfebc0efc4b77313fee3cacef9e7d983bcb3a95c88c8fa2f8f2","size":102909,"exec":false}]},"scenario-blender-retopology":{"description":"Use when turning a dense sculpt, scan or AI-generated mesh into clean topology in Blender, for anim…","category":"media-eventtech","tier":"free","origin":"scenario","requires_auth":false,"sha256":"b6f31d60d7cd7d4cd055d0e7ba160eaa58b9d165739055eb3a1c1e381c08036e","upstream_path":"skills/dcc/blender/scenario-blender-retopology/SKILL.md","requires":["scenario-blender-expert"],"software":["Blender"],"hasScripts":true,"scriptCount":1,"execBridge":false,"execBridgeFiles":[],"files":[{"path":"SKILL.md","sha256":"b6f31d60d7cd7d4cd055d0e7ba160eaa58b9d165739055eb3a1c1e381c08036e","size":22026,"exec":false},{"path":"references/critique.md","sha256":"f23b9afc01bc0489d834978277323e1612f576983be98c388ba54e0c0e5ba7d7","size":15592,"exec":false},{"path":"references/expert-notes.md","sha256":"b1faff4c7fdce09eadbe438f4c8d52bceb05930729dd54ad5e6cae9f8406dd58","size":36275,"exec":false},{"path":"references/procedures.md","sha256":"d55d861c34134a6d1681d67577d3c81f4d5be904fdd3c87108e4a6b940790ae5","size":31490,"exec":false},{"path":"references/sources.md","sha256":"d25df1db2d06c8e378349bf188ba19d229ea7f912b46097854afce669d234017","size":17379,"exec":false},{"path":"scripts/bx_retopo.py","sha256":"871005569e63d4d947e5d5a6cd6344f6bee4113302ec5498270b028ad53922dd","size":128751,"exec":false}]},"scenario-blender-texturing-shading":{"description":"Use when texturing, shading or look-developing in Blender: building a PBR material from a texture s…","category":"media-eventtech","tier":"free","origin":"scenario","requires_auth":false,"sha256":"df7f11cc812e5b2dd61bd5770de88017fdb67898951e2957bbdc78717f40c45c","upstream_path":"skills/dcc/blender/scenario-blender-texturing-shading/SKILL.md","requires":["scenario-blender-expert"],"software":["Blender"],"hasScripts":true,"scriptCount":1,"execBridge":false,"execBridgeFiles":[],"files":[{"path":"SKILL.md","sha256":"df7f11cc812e5b2dd61bd5770de88017fdb67898951e2957bbdc78717f40c45c","size":15695,"exec":false},{"path":"references/critique.md","sha256":"8365bb221a2ad681120beb2346ec6ee194a0affca441d98243f35e66540a8d50","size":5828,"exec":false},{"path":"references/expert-notes.md","sha256":"0472e6a6faa26770e603f97a25bd1c34007b9c0220c98967973d937d7a4dbb20","size":28024,"exec":false},{"path":"references/procedures.md","sha256":"703f71d73daab57183ee472b6069e3368b6f79ce72e0a273f8a67ed29a5a61f4","size":31419,"exec":false},{"path":"references/sources.md","sha256":"093bd302b898e3bc3fc438cf0ed0be59cbc6057108c03091ea828d25f31e8503","size":11474,"exec":false},{"path":"scripts/bx_materials.py","sha256":"15877d81bea8d2c95e1393b7c6ce8c96182fdb1a8d26dfa6bfbaf2d88886e72b","size":87244,"exec":false}]},"scenario-blender-uv-baking":{"description":"Use when UV unwrapping, placing seams, straightening or packing UVs, setting texel density, laying …","category":"media-eventtech","tier":"free","origin":"scenario","requires_auth":false,"sha256":"4dba740b856bb7cda4b158c53f1972221d08059ef89bfaaca15a5ac985db9b90","upstream_path":"skills/dcc/blender/scenario-blender-uv-baking/SKILL.md","requires":["scenario-blender-expert"],"software":["Blender"],"hasScripts":true,"scriptCount":1,"execBridge":false,"execBridgeFiles":[],"files":[{"path":"SKILL.md","sha256":"4dba740b856bb7cda4b158c53f1972221d08059ef89bfaaca15a5ac985db9b90","size":17085,"exec":false},{"path":"references/critique.md","sha256":"e9a0bde9c4b2b416cd3d73ceb4dee21f584175fdd7b855a3fa3e04ac434c7a7b","size":17474,"exec":false},{"path":"references/expert-notes.md","sha256":"04fba3c9f09eb7643a5d27329c6eed250da06bb55df9b2374e760a8e0689e52c","size":23242,"exec":false},{"path":"references/procedures.md","sha256":"c8c2f81b18fb66360c2a6ac71c918a2494318b775648e0af2d725d5636df4a94","size":17979,"exec":false},{"path":"references/sources.md","sha256":"fb8a8a3b4d69ab7119e7db3f2f60756bedbe275912e8acbd5e9339cbfef9bb95","size":12550,"exec":false},{"path":"scripts/bx_uvbake.py","sha256":"f07fd3b6c02fdeff9dfaa77e164f24843ece1113e23ba073e61b68002d5c0762","size":107567,"exec":false}]},"scenario-unity-expert":{"description":"Use when an agent drives Unity 6.3 (6000.3) on a Mac for any task: batch mode and -executeMethod jo…","category":"media-eventtech","tier":"free","origin":"scenario","requires_auth":false,"sha256":"61a31dd57792d42514912d864f23aa4ac7511b2c288d78e491987f6747142357","upstream_path":"skills/game-engines/unity/scenario-unity-expert/SKILL.md","software":["Unity 6.3"],"hasScripts":true,"scriptCount":12,"execBridge":true,"execBridgeFiles":[{"path":"scripts/AgentKit/AgentBridge.cs","reason":"invokes a method named in a request + file inbox polled for requests"},{"path":"scripts/ut_live.py","reason":"calls exec()/eval() + file inbox polled for requests"}],"files":[{"path":"SKILL.md","sha256":"61a31dd57792d42514912d864f23aa4ac7511b2c288d78e491987f6747142357","size":20832,"exec":false},{"path":"references/gui-paths.md","sha256":"8571540332e3095ff1855b641ac847dc9a4bf14f64011883f77d1421c6c5f041","size":5161,"exec":false},{"path":"references/live-channels.md","sha256":"37c9bf7a84ee3f81cb003977de6ae7b819bddbc341babf842558ac4a33f10d81","size":10861,"exec":false},{"path":"references/procedures.md","sha256":"d0e79adbed05d073ca9d25f088155f677735210ebe99f27d10670007afed42ad","size":7975,"exec":false},{"path":"references/routing-handoffs.md","sha256":"99fc636babe797bd83f580d11d58cfd7db551528eb946251d09cd97067eb12b8","size":19866,"exec":false},{"path":"references/sources.md","sha256":"49d0bf4a5d0470f6c2d5d5f436cab43789b1f17e0b7b361bed277c397bc218cd","size":28778,"exec":false},{"path":"references/toolkit-map.md","sha256":"db1f8b6d2e1dd685d44398266c8f956d36b862cef867bb8b8cf7e3895a5ab7e3","size":38967,"exec":false},{"path":"references/unity-6.3-traps.md","sha256":"f2c308fdfde091b022077958cd93dac186441cb8fd5dc2ae049697533b4191f3","size":51439,"exec":false},{"path":"references/writing-jobs.md","sha256":"a0f58f7ba6ef31c460c437484305f88b57ee6621a5f5851ecfbc636cc54d8a7e","size":9767,"exec":false},{"path":"scripts/AgentKit/AgentAudit.cs","sha256":"ef3462273115de3e5dfe0728a1eb884a01bfca1368846f563ba3e82acc25c1a4","size":26345,"exec":false},{"path":"scripts/AgentKit/AgentBridge.cs","sha256":"1cd83d4ed4b03d4e4440eab88ca4ca777e6057478d93db2b98fb35f3cd5a0b10","size":12418,"exec":false},{"path":"scripts/AgentKit/AgentBuild.cs","sha256":"9d97386d404e30ba3b286e202e95645b39bcc7b449b791298ce181e9ec7dfa66","size":12631,"exec":false},{"path":"scripts/AgentKit/AgentCapture.cs","sha256":"ebbf7f867607a9f563760161fba53ab0336e91858bc741fb01ffdb183861390a","size":22677,"exec":false},{"path":"scripts/AgentKit/AgentJob.cs","sha256":"a7e56ca70de1a3714509f128d112ea2afaf7817ddfea5c8fd65e9cfd25498328","size":16347,"exec":false},{"path":"scripts/AgentKit/AgentJson.cs","sha256":"9ef0f999966536846a358d647126d863d38140dd5dcf1f2ab51719dc0f4ea6c0","size":12599,"exec":false},{"path":"scripts/AgentKit/AgentProfile.cs","sha256":"61047d48198e42ebd9b9d43d1fa51feab16475232b3ca02837d86850997d3d43","size":13210,"exec":false},{"path":"scripts/ut_env.py","sha256":"0945a5e3d32f0610f73a50a5c186b18cb63301df736ac25be0c8da27f83bc1ec","size":16048,"exec":false},{"path":"scripts/ut_live.py","sha256":"ad4b62c2f4f2551e5edd84a6ac70cb96b5a0956629d1c29034b20b2d92a177c7","size":21945,"exec":false},{"path":"scripts/ut_review.py","sha256":"b698fda1327c7073e1f82593f3f4050278ebd072631342f3a0bc709e0845bb44","size":24732,"exec":false},{"path":"scripts/ut_run.py","sha256":"64e0c0050faaf784c78e5bb3f575b1177d2d55ecc8c30e6dd2f14ae02b92a279","size":26972,"exec":false},{"path":"scripts/ut_stat.py","sha256":"367879ffa58641f67e7e44f190f01bac4b2864128f3ab567df028ac38f18ec9e","size":11680,"exec":false}]},"scenario-unity-performance":{"description":"Use when a Unity 6.3 game is slow or stutters: low fps, frame spikes, hitches when entering areas o…","category":"engineering-method","tier":"free","origin":"scenario","requires_auth":false,"sha256":"c12a5c903f396a72a15e56226d8a0dab553fef6caa13d9c801c54e8125c22333","upstream_path":"skills/game-engines/unity/scenario-unity-performance/SKILL.md","requires":["scenario-unity-expert"],"software":["Unity 6.3"],"hasScripts":true,"scriptCount":11,"execBridge":false,"execBridgeFiles":[],"files":[{"path":"SKILL.md","sha256":"c12a5c903f396a72a15e56226d8a0dab553fef6caa13d9c801c54e8125c22333","size":19744,"exec":false},{"path":"references/critique.md","sha256":"2731a46adbeec0f9c3b4a77c4b9c3f48ea8b942a1c1541f83f6f0fe620b5aa2d","size":8642,"exec":false},{"path":"references/expert-notes.md","sha256":"9980ee29a301309ab626a67fedefe84948ca03bebb033326f9623b7db8918169","size":25410,"exec":false},{"path":"references/gui-paths.md","sha256":"da2d3546fec852aafec6cb353d021c14ed24ea0bd8a21a3545f0f9563caa84da","size":24245,"exec":false},{"path":"references/procedures.md","sha256":"d28316c0c66d52e921c8e0b6a9c2cd5c9e892763be5b35e10a13999c2a6f258e","size":44495,"exec":false},{"path":"references/sources.md","sha256":"ce76807bc6ca64274be03f998726197261f1946604918a7e3b3be08a9c3f3c4c","size":24539,"exec":false},{"path":"scripts/AgentKit/Performance/PerfAudit.cs","sha256":"1f04553de72dbcfcee20ae496d5e80f256b4376dc7a849558c4586b6101e165b","size":17453,"exec":false},{"path":"scripts/AgentKit/Performance/PerfBuild.cs","sha256":"a89fa1b4532123a5816f97394ecd35ce12a5e45072764dc1abebdcf267f50d0e","size":11692,"exec":false},{"path":"scripts/AgentKit/Performance/PerfCapture.cs","sha256":"0a1788e09d50c2fe1d97fe749bf884b0e887f5ecc7514b86823e1c508d51c743","size":12297,"exec":false},{"path":"scripts/AgentKit/Performance/PerfFrameDebug.cs","sha256":"19e69f94b6aced6358a15edd8c8d78ec992677b25372b55b4fa09383469fd879","size":16446,"exec":false},{"path":"scripts/AgentKit/Performance/PerfLod.cs","sha256":"cc9bf20740a9d5bbaaa7410c75a0003660f0d556737012a067bb02e8dcf4bd70","size":5062,"exec":false},{"path":"scripts/AgentKit/Performance/PerfRendering.cs","sha256":"af7ba69beeb6d74d17de949a81e13ab2112d6d45a47156c3653f4ecae15c1b1b","size":33683,"exec":false},{"path":"scripts/AgentKit/Performance/PerfShaders.cs","sha256":"6aa5cae1262e930c9a4fa07e18555f9b95dcce3391c8da33f82ae266f5cef9cd","size":13473,"exec":false},{"path":"scripts/Runtime/Performance/AgentKit.Performance.Runtime.asmdef","sha256":"eb71145c132486c46bfecc868a63b51c196a4437043c422d61e01de40e30d184","size":382,"exec":false},{"path":"scripts/Runtime/Performance/AllocScope.cs","sha256":"f469ee9f659dbda3d387817854999d141b1fd56889b8a3209f0f4fc63159da8e","size":2206,"exec":false},{"path":"scripts/Runtime/Performance/PerfProbe.cs","sha256":"2a42c3dc9a416d8655bb9f43335ff81535133d25ae04f206ad3ff24a4680c80f","size":28166,"exec":false},{"path":"scripts/ut_perf.py","sha256":"46cb7a5b9922546a6510eef9de4779017989681b1715f3fa3328d1502dd53fb7","size":44913,"exec":false}]},"scenario-unity-pipeline-automation":{"description":"Use when automating a Unity 6.3 content pipeline or build: importing an art drop (FBX, PNG) with As…","category":"engineering-method","tier":"free","origin":"scenario","requires_auth":false,"sha256":"438d60621e5c93a35d0f6085af22e9c1377ed154b24486c134d79cc9d584948c","upstream_path":"skills/game-engines/unity/scenario-unity-pipeline-automation/SKILL.md","requires":["scenario-unity-expert"],"software":["Unity 6.3"],"hasScripts":true,"scriptCount":16,"execBridge":false,"execBridgeFiles":[],"files":[{"path":"SKILL.md","sha256":"438d60621e5c93a35d0f6085af22e9c1377ed154b24486c134d79cc9d584948c","size":19929,"exec":false},{"path":"references/critique.md","sha256":"4f35497615c24efa9c9265557f5794c5c343a20c99fd64e903c588bfa581e684","size":17680,"exec":false},{"path":"references/expert-notes.md","sha256":"134238c2280662d3e71153ea12c6e127713677f9a3b06441ce79250044168bbd","size":24228,"exec":false},{"path":"references/gui-paths.md","sha256":"441f6f46698e99cef420db6de67cf2cbf040d62e149528ad29174392b66c5038","size":15507,"exec":false},{"path":"references/procedures.md","sha256":"04c16e0d2b28fb1b619537f3f491a403d38c6304ea4b7b8befe7ab38575f2431","size":50486,"exec":false},{"path":"references/sources.md","sha256":"9db649871d50cec70bc7909ab0a4fd68bf902745cb128173c3994aa8b8c5a988","size":17352,"exec":false},{"path":"scripts/AgentKit/Pipeline/AddressablesJobs.cs","sha256":"862a2fc4c651be751cbfbf46a0e51b14eff7b7530f8691c72a8f0aedc2cf80dd","size":24414,"exec":false},{"path":"scripts/AgentKit/Pipeline/ArtDropJobs.cs","sha256":"38f5d8d349aa8362b453ae08e179dbaf6778fa2692ddf505601bea56fcd88f2b","size":37459,"exec":false},{"path":"scripts/AgentKit/Pipeline/EditorEditJobs.cs","sha256":"505a5816a64a92fb00536db5bfec238085fb1a95df0c960667260386c9627269","size":16776,"exec":false},{"path":"scripts/AgentKit/Pipeline/PipelineBuild.cs","sha256":"6774278798c91440489cce698138013cb1bf1a821e8b80a02c408656c1702729","size":17557,"exec":false},{"path":"scripts/AgentKit/Pipeline/PipelineChecks.cs","sha256":"558cb4716bd61abb73f6a8ef1ca0ebdb9f15df10be9c669b3f4102d7b1e76f05","size":13428,"exec":false},{"path":"scripts/AgentKit/Pipeline/PipelineRules.cs","sha256":"bec6dd9704f7668e9fed9cec3cc975bdfaebacbec4298b7c8a38056c36a263f4","size":7302,"exec":false},{"path":"scripts/AgentKit/Pipeline/PropImportPostprocessor.cs","sha256":"3cd1dee4c68e92e68e41757b932fa4c4e77f38f316f8d72fb9acfb275e06262e","size":6656,"exec":false},{"path":"scripts/AgentKit/Pipeline/Tests/PipelineContentTests.cs","sha256":"570276dda02f9ea24e887b5e6019ef9634cde5319974bfb2ce9a26e451be7227","size":7083,"exec":false},{"path":"scripts/AgentKit/PipelineCli/AgentKit.PipelineCli.asmdef","sha256":"73e86ed42f8560e39944febced4bc45c4b6087dc452495fe94d017724e7069e2","size":612,"exec":false},{"path":"scripts/AgentKit/PipelineCli/PipelineCliCommands.cs","sha256":"a0143e932002e3f6b54ef9b4bc334415c39823ae794751fc7db2d7dd622203f4","size":4296,"exec":false},{"path":"scripts/Runtime/AgentAddressablesSmoke.cs","sha256":"9dd0d8cb0b95ec65bed284d026eb1abc71ea2e2d62bdaa029f96b6198013ed6d","size":4174,"exec":false},{"path":"scripts/templates/PipelineAsmdef/Editor/Game.Pipeline.Editor.asmdef","sha256":"cf802502971da70b9a6b9c6f18d0d32728d3f5ff9c6c2b948ccdf38ddb918ed3","size":389,"exec":false},{"path":"scripts/templates/PipelineAsmdef/Editor/TextureRuleAudit.cs","sha256":"7c4256ca74aa2be2a1e453c40653807b895b00b7eade2ae601897e3fb596c75b","size":2327,"exec":false},{"path":"scripts/templates/PipelineAsmdef/Tests/Game.Pipeline.Tests.asmdef","sha256":"5289a53406fef4ee53d882bbaa825cc52a063514c1eb8b3e759ef2dc637eab03","size":568,"exec":false},{"path":"scripts/templates/PipelineAsmdef/Tests/TextureRuleTests.cs","sha256":"9ab47e781c293bd22a74ac44ca95b55164b6a1b4f20eb9e446237a15aa8a0e0a","size":1280,"exec":false},{"path":"scripts/ut_pipeline.py","sha256":"8b93ba575f6859d7dc05ba0c664eb8a210ccee22eec123835a59b853d6e5a05d","size":56155,"exec":false}]},"scenario-unity-shaders":{"description":"Use when a Unity 6.3 URP task involves shaders: hand-written HLSL, Shader Graph water, toon, dissol…","category":"media-eventtech","tier":"free","origin":"scenario","requires_auth":false,"sha256":"bf0cf46c1e6bb2d0ffe3852fc363a2ad3d1ded020cf4b7b41ffd7cae7e653de3","upstream_path":"skills/game-engines/unity/scenario-unity-shaders/SKILL.md","requires":["scenario-unity-expert"],"software":["Unity 6.3"],"hasScripts":true,"scriptCount":21,"execBridge":false,"execBridgeFiles":[],"files":[{"path":"SKILL.md","sha256":"bf0cf46c1e6bb2d0ffe3852fc363a2ad3d1ded020cf4b7b41ffd7cae7e653de3","size":20513,"exec":false},{"path":"references/critique.md","sha256":"e8934e753805665a4bb8fbdea78db41268040eeb7695aba5ca790227137fd438","size":12242,"exec":false},{"path":"references/expert-notes.md","sha256":"23ba5da22cdd325b46c91df8b34c3b8a7b14fa28a0374ffb6beb888dea924856","size":25730,"exec":false},{"path":"references/gui-paths.md","sha256":"e00f3a24db197575541a9ae5287c5ac4350487fc517460d19e9c1316cd4ad315","size":18163,"exec":false},{"path":"references/procedures.md","sha256":"e4d6c4b92c3841d0a270533cec5c525bf258ddae8ac0fe8997e356da3630b1fa","size":44695,"exec":false},{"path":"references/sources.md","sha256":"6f498801b0b43200f1d6357f9077001c9fb78267ae0317086a903d27bc6d41f0","size":22805,"exec":false},{"path":"scripts/AgentKit/Shaders/ComputeJobs.cs","sha256":"e22096eb5b25adea4e7b685b4a671189c0a77ed7ecde3b0aa181a05269b2e333","size":6984,"exec":false},{"path":"scripts/AgentKit/Shaders/GpuCostJobs.cs","sha256":"48a53c13e2cabde1c7a13b66cde69baf67c2beeab15204a642cc72dc5edf5d41","size":9740,"exec":false},{"path":"scripts/AgentKit/Shaders/PsoJobs.cs","sha256":"e9e5a28f8f7df32005fbfb8943f328de127c764f004d06ef6fcc575f617056ab","size":9830,"exec":false},{"path":"scripts/AgentKit/Shaders/RendererFeatureJobs.cs","sha256":"db119383b251fced87b28ecf2d832578a576eb0beb8a54b23f5cb371f8385a3c","size":10833,"exec":false},{"path":"scripts/AgentKit/Shaders/ShaderGraphJobs.cs","sha256":"ce896c4f3d082f4f44221af6dfbd1ef0602f128e92a66af0bd626c0aa72808f3","size":9291,"exec":false},{"path":"scripts/AgentKit/Shaders/ShaderJobs.cs","sha256":"844d0757ddbe40212840ff7169d9a2a6a98d3e9ebcfbaad92acea85bf5b4ce98","size":27032,"exec":false},{"path":"scripts/AgentKit/Shaders/ShaderLab.cs","sha256":"87dccaefc8a312f3d0debdcacdcca2510fb415a4c70af987255bf67b6c67fb10","size":25080,"exec":false},{"path":"scripts/AgentKit/Shaders/VariantJobs.cs","sha256":"1b83dfa175aeeb8a12647d5a52221b3222f5b87a7d4fdcab1163bfc0e7b68ad4","size":24544,"exec":false},{"path":"scripts/Runtime/AgentFullscreenFeature.cs","sha256":"a838d52edd32af2fc3cb281d5ea89c957b453f9d7ad13d20213411e31c6f1e87","size":6243,"exec":false},{"path":"scripts/Runtime/AgentPsoProbe.cs","sha256":"d6da1632573854e9327e3a5b2b7d757a514f14e85ed5f7817038df1f73049f36","size":12803,"exec":false},{"path":"scripts/Shaders/AgentCompute.compute","sha256":"e63376793bdbfbd062e828e5c7e9e62b31fc2e3be62697df356a09466b548c80","size":1874,"exec":false},{"path":"scripts/Shaders/AgentCustomLighting.hlsl","sha256":"14092128d768901fadefc3e39d6c077a68c0438faae09a368ddee4fd77dfeb19","size":6105,"exec":false},{"path":"scripts/Shaders/AgentDissolve.shader","sha256":"c26c41c19b486df876c35670c95d7dc8a5417124b0df0b5ded34ab3373455e84","size":9467,"exec":false},{"path":"scripts/Shaders/AgentEdgeOutline.shader","sha256":"eb06e6c478344ed4a75acd2dbaee0f24e821fe6ad52241ad40b9958d4604d08e","size":4025,"exec":false},{"path":"scripts/Shaders/AgentGrayscale.shader","sha256":"3f986574b9af4b8013e0c7d361e410b8da00f327715e9dccbfc00c63fc28404b","size":1493,"exec":false},{"path":"scripts/Shaders/AgentLit.shader","sha256":"519555c889f33d8b53843aa0d3ab1396566d1428275f5f8464c2be45827dfe34","size":9152,"exec":false},{"path":"scripts/Shaders/AgentNoise.hlsl","sha256":"9d70aac4e3aacbf828fc6aaabd64bfa351446e87572ff3e5d618cfe038e4ce43","size":3208,"exec":false},{"path":"scripts/Shaders/AgentPasses.hlsl","sha256":"80b0ce1f1da27983f514228ad6639b5f52591e4bee9083491b2aa1bb551d37d8","size":5452,"exec":false},{"path":"scripts/Shaders/AgentToon.shader","sha256":"8f49cc1da741f457b6da3e0ac9ec564b4d89149d90cee0bff4df3007c05baa15","size":9828,"exec":false},{"path":"scripts/Shaders/AgentWater.shader","sha256":"b865a7bb72a28d1767d88b00e1c85e63676df0778c478b09d3d2e27a348e608a","size":9437,"exec":false},{"path":"scripts/ut_shaders.py","sha256":"71fcd7c4311b784d0fbc70f0d3f4a62f853455715f9dbe0d7952c7d4ec17f2cd","size":56132,"exec":false}]},"scenario-unity-ui":{"description":"Use when building or fixing game UI in Unity 6.3: main menu, settings screen (resolution, quality, …","category":"design-ui-ux","tier":"free","origin":"scenario","requires_auth":false,"sha256":"9cf61b9ca815b3ed1886d5bcec98ebfc22571f948803c5fc06bf9f584333f755","upstream_path":"skills/game-engines/unity/scenario-unity-ui/SKILL.md","requires":["scenario-unity-expert"],"software":["Unity 6.3"],"hasScripts":true,"scriptCount":32,"execBridge":false,"execBridgeFiles":[],"files":[{"path":"SKILL.md","sha256":"9cf61b9ca815b3ed1886d5bcec98ebfc22571f948803c5fc06bf9f584333f755","size":17307,"exec":false},{"path":"references/critique.md","sha256":"1467315c43abf62aad6d2174df54d93e9c4bdb232bf1834296c8f3c3766bb78a","size":8630,"exec":false},{"path":"references/expert-notes.md","sha256":"e0c128bf8645ee5fe221664cd5c781dabfb14ea6248f43d77d0733d378e7af8b","size":23781,"exec":false},{"path":"references/gui-paths.md","sha256":"66807ad5265940a31547daf5a8537cbc62141501065decd7b465e6dd4dbc1026","size":5200,"exec":false},{"path":"references/procedures.md","sha256":"29d4faa6e8f2ad331d9ce4aef85303e8b8d94f56f36df0d8274a2a4fdc0b438e","size":42170,"exec":false},{"path":"references/sources.md","sha256":"1c776696c9d54cf2b4b377ed8e61d2e92f9da8d4bc4a265ac4759ef71c7a28cb","size":16748,"exec":false},{"path":"scripts/AgentKit/UI/UIAudit.cs","sha256":"50714d1cacced7ed3b9744cfee771589ebc693727b275b8d55a0c98ed3795d45","size":39487,"exec":false},{"path":"scripts/AgentKit/UI/UIBuild.cs","sha256":"ac023db602fd898f174a5d2192aba29a629f825c0d5d48f9fbaeb1b2e21ab335","size":29856,"exec":false},{"path":"scripts/AgentKit/UI/UICapture.cs","sha256":"0c1ce99993d28f71c90f68897b7c6ca3ba8fadc85d20b399c48db2c7f2dfb964","size":33102,"exec":false},{"path":"scripts/AgentKit/UI/UIPerf.cs","sha256":"8985971fd81578a6fd3f9919de02108da269965dd6cd96065cbdff64a84368d4","size":26392,"exec":false},{"path":"scripts/AgentKit/UI/UISetup.cs","sha256":"07f23ef2a7e16eb5a1ab1833ed2972c7cdbc69e120309ae637f925e1f87cb2dc","size":5897,"exec":false},{"path":"scripts/AgentKit/UI/UIToolkitBuild.cs","sha256":"75049ce3ec93d2657d98046708afc5bf630fe2ac07f712ca6b584f5f1206d07d","size":20382,"exec":false},{"path":"scripts/Runtime/UI/AgentUI.Runtime.asmdef","sha256":"3c9fb95fe5977b71ae42137d01ba7d24cbfe674b2a0d5c5641b1da5038ccf4e3","size":444,"exec":false},{"path":"scripts/Runtime/UI/CanvasDeviceScale.cs","sha256":"43958d24558a260e2aff19a68a4d6dae2a4ee477dbda7ba050b8a1213fb0fa73","size":2069,"exec":false},{"path":"scripts/Runtime/UI/ClampedWidth.cs","sha256":"54f78309df4637a53c76250542a93189cdf0f9bfa06a15e24449a8857fe740ae","size":1994,"exec":false},{"path":"scripts/Runtime/UI/HudBar.cs","sha256":"14488466f574030b05a3eb35eeaedf9ae66b52d3d77878cbf877e802b5322abf","size":2906,"exec":false},{"path":"scripts/Runtime/UI/HudModel.cs","sha256":"4f44c529b07811e3486ec24816ed24203db203eb75442f76a5cc84cdc79f807b","size":3623,"exec":false},{"path":"scripts/Runtime/UI/HudView.cs","sha256":"712e93b3b1f6490007c4672dd31eb0bb44843af7e53bec9b8af55fac3ae9bbae","size":1481,"exec":false},{"path":"scripts/Runtime/UI/MenuRouter.cs","sha256":"3da348c7952fbf9c9e6f078a1eff44571b7e6dfe19697930b979ecd00ff3fdaa","size":4163,"exec":false},{"path":"scripts/Runtime/UI/MenuScreen.cs","sha256":"7b26db2aa577b13f19fd9a69c4d7112756e14c83d097258d65d84a56f3b6324d","size":4843,"exec":false},{"path":"scripts/Runtime/UI/PanelChangeLog.cs","sha256":"18ff907c0d203f42a47910a288337196d547176934600274fafbc49e123010fd","size":3676,"exec":false},{"path":"scripts/Runtime/UI/RebindRow.cs","sha256":"5650a5dcf31f703125916cf1af73b893ef9533286f91a0cd7e896c458b27ce5a","size":3454,"exec":false},{"path":"scripts/Runtime/UI/RebuildProbe.cs","sha256":"52c42816a5cf95d0b0cdb195ebc003db1dc8a75397e9a77b862bf51c4718283f","size":1212,"exec":false},{"path":"scripts/Runtime/UI/ScrollToSelection.cs","sha256":"d9401696c2b0cc5ee1659fef865801f358e0e226715b42a83ae25a9ad7944fbc","size":2264,"exec":false},{"path":"scripts/Runtime/UI/SelectableFeedback.cs","sha256":"ecf8bbab358a0a867208fb92e1e4a790c4cb993b992192d151f52f1ef0d80bad","size":3642,"exec":false},{"path":"scripts/Runtime/UI/SettingsScreen.cs","sha256":"7b386b9e7de1d4425fd4fdaaf9703d1a1499fbb1023baaee05456b23a7fa62e1","size":6628,"exec":false},{"path":"scripts/Runtime/UI/SettingsService.cs","sha256":"d19662fc7ed48b89b22af678db1c64c2ea3558331a5a3d44aad5fb550c7ef246","size":4291,"exec":false},{"path":"scripts/Runtime/UI/UILoader.cs","sha256":"ae3b8087cbe41f65d9c6aab54a2fbb41caa24fd7f8f4b504996945e6e8065f76","size":2504,"exec":false},{"path":"scripts/Runtime/UI/UiCounterSampler.cs","sha256":"34cebfc40d47925f8d378322b978bb84d54a61fc555699bafd77c1fbe649e824","size":4149,"exec":false},{"path":"scripts/Runtime/UI/UiPerfDriver.cs","sha256":"6ce266e1ca0f9e33fcbd4a45fadd0a29f996fe00ceca23cc4ef16591fe7d8288","size":927,"exec":false},{"path":"scripts/Runtime/UI/UiTheme.cs","sha256":"27a513e460d683f6c51f8cd16445aa53d4f7d3b11221d069d0717a13e98f265e","size":2572,"exec":false},{"path":"scripts/Runtime/UI/UitkHideDriver.cs","sha256":"ffe011cbf1b2d148a14b6ebe829269750de922594e52529fc63eae648829ff3b","size":6414,"exec":false},{"path":"scripts/Runtime/UI/UitkTextureGrid.cs","sha256":"8ae73058d4a3e59138d58849e9d2803559079ce0ef4cfb5c95396e980111e2ec","size":3052,"exec":false},{"path":"scripts/Runtime/UI/UitkVertexGrid.cs","sha256":"d33f5fe3c25e51f0e02f858981113fc67c5c63f4ca87f6f490a4f0dc43e8c3bf","size":2121,"exec":false},{"path":"scripts/templates/uitk/AgentRuntimeTheme.tss","sha256":"682e2b1c6cc1d6e332c15f56c840f7d8282cb1e5bc627eb6664580352a752fda","size":38,"exec":false},{"path":"scripts/templates/uitk/Hud.uss","sha256":"774b424ea9f9624b07970ef7c40f70fdec7835290e0d8bebdabb325db8e8d5cb","size":2445,"exec":false},{"path":"scripts/templates/uitk/Hud.uxml","sha256":"d379afb21468f1c516c4c4fe349f96b6d8d46d0e03406e1ac717a460d69b36f3","size":2643,"exec":false},{"path":"scripts/ut_ui.py","sha256":"0b2aaa743a160d4c8c31cb4e16513f9e4503de6a3129fdd4eaa4b13338ec5c46","size":24204,"exec":false}]}},"subagents":{}}
@@ -1,5 +1,5 @@
1
1
  import { createHash } from 'node:crypto';
2
- import { chmodSync, existsSync, lstatSync, mkdirSync, readdirSync, statSync, writeFileSync } from 'node:fs';
2
+ import { chmodSync, existsSync, lstatSync, mkdirSync, readdirSync, readFileSync, statSync, writeFileSync } from 'node:fs';
3
3
  import { homedir } from 'node:os';
4
4
  import { basename, dirname, join, resolve, sep } from 'node:path';
5
5
 
@@ -66,10 +66,10 @@ function assertNoSymlink(path) {
66
66
  catch (e) { if (e.code !== 'ENOENT') throw e; }
67
67
  }
68
68
 
69
- // Checks every destination first, then writes; throws before any write on an unsafe path.
70
- export function writeSkillFiles(skillMd, files) {
69
+ // Checks every destination; throws on an unsafe path and touches nothing.
70
+ export function planSkillFiles(skillMd, files) {
71
71
  const dir = resolve(dirname(skillMd));
72
- const plan = files.map((f) => {
72
+ return files.map((f) => {
73
73
  safeRel(f.path);
74
74
  const dest = resolve(dir, ...f.path.split('/'));
75
75
  if (!dest.startsWith(dir + sep)) throw new Error(`Path escapes skill dir: ${f.path}`);
@@ -77,6 +77,9 @@ export function writeSkillFiles(skillMd, files) {
77
77
  assertNoSymlink(dir);
78
78
  return { ...f, dest };
79
79
  });
80
+ }
81
+
82
+ export function writePlan(plan) {
80
83
  for (const f of plan) {
81
84
  mkdirSync(dirname(f.dest), { recursive: true });
82
85
  writeFileSync(f.dest, f.data);
@@ -85,6 +88,80 @@ export function writeSkillFiles(skillMd, files) {
85
88
  return plan.map((f) => f.dest);
86
89
  }
87
90
 
91
+ export const writeSkillFiles = (skillMd, files) => writePlan(planSkillFiles(skillMd, files));
92
+
93
+ const RAW_BASE_RE = /^https:\/\/raw\.githubusercontent\.com\/[\w.-]+\/[\w.-]+$/;
94
+ const ECC_SOURCE = {
95
+ id: 'ecc', label: 'ECC', raw_base: 'https://raw.githubusercontent.com/affaan-m/ECC',
96
+ license: 'MIT', copyright: 'Copyright (c) 2026 Affaan Mustafa', third_party: true,
97
+ };
98
+
99
+ // Test overrides: AOS_STORE_INDEX / AOS_STORE_RAW_BASE (ECC), AOS_STORE_INDEX_SCENARIO / AOS_STORE_RAW_BASE_SCENARIO (Scenario).
100
+ export function loadSources(root, env = process.env) {
101
+ const specs = [
102
+ { file: env.AOS_STORE_INDEX || join(root, 'lib', 'ecc-store-index.json'), base: env.AOS_STORE_RAW_BASE, fallback: ECC_SOURCE },
103
+ { file: env.AOS_STORE_INDEX_SCENARIO || join(root, 'lib', 'scenario-store-index.json'), base: env.AOS_STORE_RAW_BASE_SCENARIO },
104
+ ];
105
+ return specs.map(({ file, base, fallback }) => {
106
+ const index = JSON.parse(readFileSync(file, 'utf8'));
107
+ if (!index.pinned_commit || !index.skills || !index.subagents) throw new Error(`Invalid AOS store index: ${file}`);
108
+ const meta = { ...fallback, ...index.source };
109
+ if (!meta.id || !meta.label) throw new Error(`Store index has no source descriptor: ${file}`);
110
+ if (!base && !RAW_BASE_RE.test(meta.raw_base || '')) throw new Error(`Untrusted raw_base in ${file}: ${meta.raw_base}`);
111
+ return { ...meta, raw_base: (base || meta.raw_base).replace(/\/+$/, ''), pinned_commit: index.pinned_commit, index };
112
+ });
113
+ }
114
+
115
+ export function findItems(sources, name) {
116
+ const found = [];
117
+ for (const src of sources) {
118
+ for (const type of ['skills', 'subagents']) {
119
+ if (src.index[type]?.[name]) found.push({ name, item: src.index[type][name], type, src });
120
+ }
121
+ }
122
+ return found;
123
+ }
124
+
125
+ export function findItem(sources, name) {
126
+ const found = findItems(sources, name);
127
+ if (found.length > 1) {
128
+ throw new Error(`Name collision: '${name}' exists in ${found.map((f) => f.src.label).join(' and ')}; refusing to pick one or overwrite`);
129
+ }
130
+ return found[0] || null;
131
+ }
132
+
133
+ // The item plus the skills it requires (same source), dependencies first.
134
+ export function installPlan(sources, found) {
135
+ const plan = [];
136
+ const visit = (entry, chain) => {
137
+ if (chain.includes(entry.name)) throw new Error(`Circular requires: ${[...chain, entry.name].join(' -> ')}`);
138
+ for (const dep of entry.item.requires || []) {
139
+ const hit = findItem(sources, dep);
140
+ if (!hit || hit.src !== entry.src || hit.type !== 'skills') throw new Error(`${entry.name} requires '${dep}', which is not available in ${entry.src.label}`);
141
+ visit(hit, [...chain, entry.name]);
142
+ }
143
+ if (!plan.some((p) => p.name === entry.name)) plan.push(entry);
144
+ };
145
+ visit(found, []);
146
+ return plan;
147
+ }
148
+
149
+ export function planSummary(plan) {
150
+ const all = plan.flatMap((p) => (p.item.files || []).map((f) => ({ ...f, skill: p.name })));
151
+ const bridge = plan.flatMap((p) => (p.item.execBridgeFiles || []).map((f) => ({ skill: p.name, ...f })));
152
+ return {
153
+ skills: plan.map((p) => p.name),
154
+ requires: plan.slice(0, -1).map((p) => p.name),
155
+ fileCount: all.length || plan.length,
156
+ totalSize: all.reduce((n, f) => n + f.size, 0),
157
+ scriptCount: plan.reduce((n, p) => n + (p.item.scriptCount || 0), 0),
158
+ hasScripts: plan.some((p) => p.item.hasScripts),
159
+ execBridge: plan.some((p) => p.item.execBridge),
160
+ execBridgeFiles: bridge,
161
+ software: [...new Set(plan.flatMap((p) => p.item.software || []))],
162
+ };
163
+ }
164
+
88
165
  export function localSkills(root) {
89
166
  const found = new Map();
90
167
  const visit = (dir) => {
@@ -56,6 +56,8 @@ dialog h2{font-family:var(--serif);font-weight:500;margin:0 0 .6rem}
56
56
  dialog pre{margin:.6rem 0;padding:.7rem;border-radius:.6rem;background:var(--ink);color:var(--white);font-size:.7rem;overflow-x:auto;white-space:pre-wrap;word-break:break-all}
57
57
  dialog .acts{display:flex;gap:.5rem;justify-content:flex-end;margin-top:1rem}
58
58
  .err{color:oklch(0.5 0.2 25);font-size:.75rem}
59
+ .badge.warn{border-color:oklch(0.5 0.2 25);color:oklch(0.5 0.2 25)}
60
+ .warnbox{padding:.6rem .8rem;border:2px solid oklch(0.5 0.2 25);border-radius:.6rem;color:oklch(0.5 0.2 25);font-size:.78rem;font-weight:700}
59
61
  </style></head><body>
60
62
  <div class="container"><main id="app"></main></div>
61
63
  <dialog id="dlg"></dialog>
@@ -70,7 +72,7 @@ const visible=()=>{const q=S.q.toLowerCase();return base().filter(i=>(S.sub==='i
70
72
  const uniq=f=>[...new Set(S.data.items.map(f))].sort();
71
73
  const count=(f,v)=>base().filter(i=>f(i)===v).length;
72
74
  const chip=(a,v,label,n)=>`<button class="chip" aria-pressed="${S[a]===v}" data-a="${a}" data-v="${esc(v)}">${esc(label)}<b>${n}</b></button>`;
73
- function strip(){const all=S.data.items;const n=s=>all.filter(i=>i.source===s).length;return `<div class="strip"><div><strong>${all.length}</strong><small>total</small></div><div><strong>${n('AOS Core')}</strong><small>AOS Core</small></div><div><strong>${n('ECC')}</strong><small>ECC</small></div></div>`}
75
+ function strip(){const all=S.data.items;return `<div class="strip"><div><strong>${all.length}</strong><small>total</small></div>${uniq(i=>i.source).map(s=>`<div><strong>${all.filter(i=>i.source===s).length}</strong><small>${esc(s)}</small></div>`).join('')}</div>`}
74
76
  function tabs(){const n=base(),ic=n.filter(i=>i.installed).length,k=x=>S.data.items.filter(i=>i.kind===x).length;return `<div class="tabs">${chip('tab','skill','Skills',k('skill'))}${chip('tab','agent','Agents',k('agent'))}<span style="width:1rem"></span>${chip('sub','installed','Installed',ic)}${chip('sub','discover','Discover',n.length-ic)}</div>`}
75
77
  const scopeToggle=()=>`<div class="scope"><span class="kicker" style="margin:0">Scope</span>${chip('scope','global','Global','')}${chip('scope','project','Project','')}</div>`;
76
78
  function act(i){
@@ -81,7 +83,7 @@ function act(i){
81
83
  }
82
84
  function row(i){
83
85
  const k=key(i),open=S.expanded.has(k);
84
- const badge=i.fileCount>1?`<span class="badge">${i.fileCount} files</span>`:'';
86
+ const badge=(i.fileCount>1?`<span class="badge">${i.fileCount} files</span>`:'')+(i.execBridge?'<span class="badge warn">exec bridge</span>':'')+(i.requires.length?`<span class="badge">+${i.requires.length} required</span>`:'');
85
87
  let h=`<div class="rowwrap"><div class="row"><span class="dot ${i.installed?'g':''}"></span><span class="n" data-a="exp" data-v="${esc(k)}" style="cursor:pointer" title="Show description">${esc(i.name)}${badge}</span><span class="d" style="color:var(--muted)">${esc(i.description)}</span><span class="c cat">${esc(i.category)}</span><span class="s src">${esc(i.source)}</span><span style="text-align:right">${act(i)}</span></div>`;
86
88
  if(open){const d=i.core?i.description:S.detail[k];h+=`<div class="detail">${d===undefined?'Loading...':esc(d)}</div>`}
87
89
  return h+'</div>';
@@ -109,9 +111,16 @@ async function preview(k){
109
111
  try{
110
112
  const d=await post('/api/preview',i);
111
113
  const ok=d.ok&&d.targets.length;
112
- const tc=i.fileCount>0?Math.round(d.targets.length/i.fileCount):1;const size=i.totalSize!=null?` = ${i.fileCount} file(s) \u00d7 ${tc} target(s), ${(i.totalSize/1024).toFixed(1)} KB each`:'';
113
- const warn=i.hasHooks?'<p class="err">Includes hooks \u2014 copied only, NOT registered in your harness settings; the skill may need manual wiring.</p>':'';
114
- dlg.innerHTML=`<h2>Add ${esc(i.name)}</h2><p class="kicker">${scopeName()} scope \u2014 will write ${d.targets.length} file(s)${size}</p>${warn}<pre style="max-height:16rem;overflow:auto">${esc(ok?d.targets.join('\n'):(d.stdout+d.stderr).trim())}</pre><p class="cmdnote">Downloads ${i.fileCount>1?i.fileCount+' files':'the file'} from ECC at ${esc(S.data.pinned_commit.slice(0,10))}, each verified by SHA-256.</p><div id="res"></div><div class="acts"><button class="button secondary" data-a="close">Cancel</button>${ok?`<button class="button" data-a="confirm" data-v="${esc(k)}">Install to ${scopeName()}</button>`:''}</div>`;
114
+ const m=d.meta||{};
115
+ const fc=m.fileCount||i.fileCount,tc=fc>0?Math.round(d.targets.length/fc):1;const size=m.totalSize>0?` = ${fc} file(s) \u00d7 ${tc} target(s), ${(m.totalSize/1024).toFixed(1)} KB each`:(i.totalSize!=null?` = ${i.fileCount} file(s) \u00d7 ${tc} target(s), ${(i.totalSize/1024).toFixed(1)} KB each`:'');
116
+ const warn=(i.hasHooks?'<p class="err">Includes hooks \u2014 copied only, NOT registered in your harness settings; the skill may need manual wiring.</p>':'')
117
+ +(m.execBridge?`<p class="warnbox">Warning: contains a script that executes code received over a local socket or file channel without authentication \u2014 install only if you use this bridge.<br><span style="font-weight:400">${esc((m.execBridgeFiles||[]).map(f=>f.skill+'/'+f.path).join(', '))}</span></p>`:(m.hasScripts?`<p class="cmdnote">Contains ${m.scriptCount} script file(s) that run on your machine when the skill is used. Review them before use.</p>`:''))
118
+ +(m.software&&m.software.length?`<p class="cmdnote">Needs third-party software that is not included: ${esc(m.software.join(', '))}.</p>`:'')
119
+ +(m.requires&&m.requires.length?`<p class="cmdnote">Also installs required skill(s): ${esc(m.requires.join(', '))}.</p>`:'');
120
+ const srcLabel=m.source||i.source,pin=(m.pinned_commit||S.data.pinned_commit).slice(0,10);
121
+ const lic=m.license?` \u2014 ${esc(m.license)}${m.copyright?', '+esc(m.copyright):''}`:'';
122
+ const nfiles=m.fileCount||i.fileCount;
123
+ dlg.innerHTML=`<h2>Add ${esc(i.name)}</h2><p class="kicker">${scopeName()} scope \u2014 will write ${d.targets.length} file(s)${size}</p><p class="cmdnote">Source: ${esc(srcLabel)}${lic}${m.thirdParty?' (third-party content)':''}</p>${warn}<pre style="max-height:16rem;overflow:auto">${esc(ok?d.targets.join('\n'):(d.stdout+d.stderr).trim())}</pre><p class="cmdnote">Downloads ${nfiles>1?nfiles+' files':'the file'} from ${esc(srcLabel)} at ${esc(pin)}, each verified by SHA-256.</p><div id="res"></div><div class="acts"><button class="button secondary" data-a="close">Cancel</button>${ok?`<button class="button" data-a="confirm" data-v="${esc(k)}">Install to ${scopeName()}</button>`:''}</div>`;
115
124
  dlg.showModal();
116
125
  }catch(e){alert('Preview failed: '+e.message)}
117
126
  finally{S.busy=null;render()}