@event4u/agent-config 5.4.0 → 5.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agent-src/commands/knowledge/cross-repo.md +71 -0
- package/.agent-src/commands/knowledge.md +2 -0
- package/.agent-src/commands/skill/preview.md +67 -0
- package/.agent-src/commands/skill.md +48 -0
- package/.agent-src/commands/skills/discover.md +76 -0
- package/.agent-src/commands/skills.md +56 -0
- package/.agent-src/commands/video/from-song.md +317 -0
- package/.agent-src/commands/video.md +19 -9
- package/.agent-src/rules/linked-projects-onboarding-gate.md +1 -1
- package/.agent-src/skills/song-to-script/SKILL.md +193 -0
- package/.claude-plugin/marketplace.json +9 -2
- package/CHANGELOG.md +49 -0
- package/CONTRIBUTING.md +6 -0
- package/README.md +3 -3
- package/dist/cli/registry.js +1 -0
- package/dist/cli/registry.js.map +1 -1
- package/dist/discovery/deprecation-report.md +1 -1
- package/dist/discovery/discovery-manifest.json +171 -17
- package/dist/discovery/discovery-manifest.json.sha256 +1 -1
- package/dist/discovery/discovery-manifest.summary.md +4 -4
- package/dist/discovery/orphan-report.md +1 -1
- package/dist/discovery/packs.json +17 -10
- package/dist/discovery/trust-report.md +3 -3
- package/dist/discovery/workspaces.json +13 -6
- package/dist/mcp/registry-manifest.json +2 -2
- package/docs/architecture.md +2 -2
- package/docs/contracts/command-clusters.md +4 -1
- package/docs/contracts/cross-repo-retrieval.md +64 -0
- package/docs/contracts/skill-discovery.md +80 -0
- package/docs/contracts/skill-dry-run.md +47 -0
- package/docs/decisions/ADR-032-linked-projects-scope.md +7 -3
- package/docs/getting-started.md +1 -1
- package/docs/guides/cross-repo-linked-projects.md +7 -0
- package/docs/guides/cross-repo-retrieval.md +61 -0
- package/docs/guides/skill-discovery.md +71 -0
- package/docs/guides/skill-preview.md +71 -0
- package/package.json +1 -1
- package/scripts/__pycache__/validate_frontmatter.cpython-312.pyc +0 -0
- package/scripts/_dispatch.bash +10 -0
- package/scripts/_lib/__pycache__/__init__.cpython-312.pyc +0 -0
- package/scripts/_lib/__pycache__/agent_src.cpython-312.pyc +0 -0
- package/scripts/ai-video/lib/probe-audio.sh +181 -0
- package/scripts/cross_repo_retrieve.py +172 -0
- package/scripts/inventory_meta_layers.py +288 -0
- package/scripts/linked_projects_list.py +91 -0
- package/scripts/memory_lookup.py +53 -2
- package/scripts/skill_discovery.py +254 -0
- package/scripts/skill_linter.py +8 -4
- package/scripts/skill_preview.py +179 -0
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
---
|
|
2
|
+
model_tier: inherit
|
|
3
|
+
name: knowledge:cross-repo
|
|
4
|
+
tier: 2
|
|
5
|
+
cluster: knowledge
|
|
6
|
+
sub: cross-repo
|
|
7
|
+
description: Targeted, read-only retrieval over opted-in linked-project siblings (ADR-032 Option A). Pulls a shared type / API contract / config without bulk-including sibling files.
|
|
8
|
+
skills: [file-editor]
|
|
9
|
+
suggestion:
|
|
10
|
+
eligible: true
|
|
11
|
+
trigger_description: "what does the frontend expect, find the shared type in the other repo, check the sibling repo's API contract, /knowledge:cross-repo <query>"
|
|
12
|
+
trigger_context: "user needs context that lives in an attached sibling repo without copying its files in"
|
|
13
|
+
workspaces:
|
|
14
|
+
- agent-config-maintainer
|
|
15
|
+
packs:
|
|
16
|
+
- meta
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
# /knowledge cross-repo
|
|
20
|
+
|
|
21
|
+
Targeted, **read-only** retrieval across the IDE-attached sibling repos the user
|
|
22
|
+
has opted into (`linked_projects[].include: true`). Returns a bounded set of
|
|
23
|
+
matches — a shared type, an API contract the frontend consumes, a config the
|
|
24
|
+
sibling owns — **without bulk-including** any sibling file. Implements
|
|
25
|
+
[`cross-repo-retrieval`](../../../docs/contracts/cross-repo-retrieval.md) and
|
|
26
|
+
stays inside [ADR-032](../../../docs/decisions/ADR-032-linked-projects-scope.md)
|
|
27
|
+
Option A.
|
|
28
|
+
|
|
29
|
+
## Prerequisites
|
|
30
|
+
|
|
31
|
+
- Python 3.10+ on the host.
|
|
32
|
+
- At least one sibling opted in (`agent-config linked-projects:list` shows them).
|
|
33
|
+
No opted-in sibling → the command is inert with a clear message.
|
|
34
|
+
|
|
35
|
+
## Steps
|
|
36
|
+
|
|
37
|
+
### 1. Parse the query
|
|
38
|
+
|
|
39
|
+
`/knowledge cross-repo "<query>" [--path-scope <glob>]`. The query is one
|
|
40
|
+
concept (≥ 1 term > 2 chars). A `--path-scope` glob narrows the search and is
|
|
41
|
+
**required** for `large`-flagged siblings.
|
|
42
|
+
|
|
43
|
+
### 2. Run the retrieval
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
python3 scripts/cross_repo_retrieve.py "<query>" [--path-scope <glob>] [--max-chunks 8]
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
The script reads opted-in siblings only, runs a targeted path-glob + content
|
|
50
|
+
grep (never a full walk), redacts secrets/PII from every chunk, and returns the
|
|
51
|
+
retrieval envelope.
|
|
52
|
+
|
|
53
|
+
### 3. Present matches
|
|
54
|
+
|
|
55
|
+
Render the table: `source_repo · path · freshness · why`. Each row names the
|
|
56
|
+
source sibling, the path inside it, the last-commit/mtime freshness stamp, and
|
|
57
|
+
why it matched. Use the chunks as *context*, never as files to copy wholesale.
|
|
58
|
+
|
|
59
|
+
### 4. Honour the scope guards
|
|
60
|
+
|
|
61
|
+
- A `large` sibling without `--path-scope` is skipped with a note — re-run with
|
|
62
|
+
a scope. Do not remove the guard.
|
|
63
|
+
- A sibling not `include: true` is never read.
|
|
64
|
+
|
|
65
|
+
## Rules
|
|
66
|
+
|
|
67
|
+
- **Read-only.** Never write to a sibling. Out-of-root writes still pass the
|
|
68
|
+
host permission gate; this surface writes nothing.
|
|
69
|
+
- **Opt-in only, targeted only.** No full-tree sweep; no implicit inclusion.
|
|
70
|
+
- **Secrets never cross repos** — the chunk redactor runs before any text is shown.
|
|
71
|
+
- **One concept per invocation.** Do not chain.
|
|
@@ -33,6 +33,7 @@ contract — input shapes, bounds, redaction defaults, storage layout.
|
|
|
33
33
|
| `/knowledge ingest` | `commands/knowledge/ingest.md` | Walk a local path, redact, chunk, persist to `agents/memory/knowledge/<ingest-id>/` |
|
|
34
34
|
| `/knowledge list` | `commands/knowledge/list.md` | List existing ingests (table or JSON); pin / unpin by id prefix |
|
|
35
35
|
| `/knowledge forget` | `commands/knowledge/forget.md` | Drop a single ingest by id prefix (atomic, no partial state) |
|
|
36
|
+
| `/knowledge cross-repo` | `commands/knowledge/cross-repo.md` | Targeted read-only retrieval over opted-in linked-project siblings (ADR-032 Option A) |
|
|
36
37
|
|
|
37
38
|
Sub-command names match the locked contract in
|
|
38
39
|
[`docs/contracts/command-clusters.md`](../docs/contracts/command-clusters.md).
|
|
@@ -48,6 +49,7 @@ Sub-command names match the locked contract in
|
|
|
48
49
|
> 1. ingest — point at a local folder, `.zip`, or file
|
|
49
50
|
> 2. list — show what's already ingested (`--pin` / `--unpin` to flag)
|
|
50
51
|
> 3. forget — drop an ingest by id prefix
|
|
52
|
+
> 4. cross-repo — targeted read-only retrieval over opted-in siblings
|
|
51
53
|
|
|
52
54
|
## Rules
|
|
53
55
|
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
---
|
|
2
|
+
model_tier: inherit
|
|
3
|
+
name: skill:preview
|
|
4
|
+
tier: 2
|
|
5
|
+
cluster: skill
|
|
6
|
+
sub: preview
|
|
7
|
+
description: Non-destructive preview of a skill — its declared steps, execution type, allowed tools, and file/command targets — before you run it. Read-only, no execution.
|
|
8
|
+
skills: [file-editor]
|
|
9
|
+
suggestion:
|
|
10
|
+
eligible: true
|
|
11
|
+
trigger_description: "what does this skill do, preview <skill> before running, what will it change, is it safe, /skill:preview competitive-positioning"
|
|
12
|
+
trigger_context: "user wants to inspect a skill's declared intent before committing to run it"
|
|
13
|
+
workspaces:
|
|
14
|
+
- agent-config-maintainer
|
|
15
|
+
packs:
|
|
16
|
+
- meta
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
# /skill preview
|
|
20
|
+
|
|
21
|
+
Renders a skill's **declared intent** — its `## Steps`, execution type, handler,
|
|
22
|
+
`allowed_tools`, and any file/command targets named in its body — so you can
|
|
23
|
+
decide whether to run it. Read-only, no network, no execution. Implements the
|
|
24
|
+
[`skill-dry-run`](../../../docs/contracts/skill-dry-run.md) contract.
|
|
25
|
+
|
|
26
|
+
## Prerequisites
|
|
27
|
+
|
|
28
|
+
- Python 3.10 + PyYAML on the host.
|
|
29
|
+
- A skill name that resolves to `.agent-src/skills/<name>/SKILL.md`.
|
|
30
|
+
|
|
31
|
+
## Steps
|
|
32
|
+
|
|
33
|
+
### 1. Parse the argument
|
|
34
|
+
|
|
35
|
+
`/skill preview <name> [--technical]`. The name is the first positional
|
|
36
|
+
argument. Missing name → print usage and stop.
|
|
37
|
+
|
|
38
|
+
### 2. Run the previewer
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
python3 scripts/skill_preview.py <name>
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Add `--technical` for the raw frontmatter + numbered step list; default is the
|
|
45
|
+
plain-language summary. `--format json` is machine-readable.
|
|
46
|
+
|
|
47
|
+
### 3. Present the summary
|
|
48
|
+
|
|
49
|
+
Show the plain-language preview: the skill's execution type (a manual-execution skill
|
|
50
|
+
renders **"instructional only — no automatic execution"**; an assisted-execution skill
|
|
51
|
+
renders its proposed actions), declared steps, tools, and any file/command
|
|
52
|
+
targets. End on the contract reminder that preview shows *declared intent*, not
|
|
53
|
+
a guarantee of side-effect-freeness.
|
|
54
|
+
|
|
55
|
+
### 4. Hand back the decision
|
|
56
|
+
|
|
57
|
+
Preview never runs the skill. After showing it, let the user decide whether to
|
|
58
|
+
invoke the skill — that is the safe adoption loop:
|
|
59
|
+
`/skills:discover` → `/skill:preview` → run.
|
|
60
|
+
|
|
61
|
+
## Rules
|
|
62
|
+
|
|
63
|
+
- **Read-only, no execution.** Preview inspects the SKILL.md; it does not run it.
|
|
64
|
+
- **Not a sandbox** — it cannot prove a skill is harmless; it shows what the
|
|
65
|
+
skill *declares* it will touch.
|
|
66
|
+
- **Malformed / missing SKILL.md → a structured error**, never a crash.
|
|
67
|
+
- **One skill per invocation.**
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
---
|
|
2
|
+
model_tier: inherit
|
|
3
|
+
name: skill
|
|
4
|
+
tier: 2
|
|
5
|
+
description: Single-skill orchestrator — routes to preview. Non-destructive "what will this skill do?" before you run it.
|
|
6
|
+
cluster: skill
|
|
7
|
+
type: orchestrator
|
|
8
|
+
suggestion:
|
|
9
|
+
eligible: true
|
|
10
|
+
trigger_description: "what does this skill do, preview this skill before running, is this skill safe to run, what will it change, /skill:preview <name>"
|
|
11
|
+
trigger_context: "user wants to see a skill's declared steps + targets before committing to running it"
|
|
12
|
+
workspaces:
|
|
13
|
+
- agent-config-maintainer
|
|
14
|
+
packs:
|
|
15
|
+
- meta
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
# /skill
|
|
19
|
+
|
|
20
|
+
Top-level orchestrator for the `/skill` family — **single-skill** operations
|
|
21
|
+
(singular `skill` for one target; plural `/skills` is the catalog-wide
|
|
22
|
+
discovery cluster). Today it carries one verb: `preview`.
|
|
23
|
+
|
|
24
|
+
Anchors: [`skill-dry-run`](../docs/contracts/skill-dry-run.md) contract —
|
|
25
|
+
what "preview" means, the explicit non-goals, and the surface.
|
|
26
|
+
|
|
27
|
+
## Sub-commands
|
|
28
|
+
|
|
29
|
+
| Sub-command | Routes to | Purpose |
|
|
30
|
+
|---|---|---|
|
|
31
|
+
| `/skill preview` | `commands/skill/preview.md` | Render a skill's declared steps, execution type, tools, and file/command targets before running it |
|
|
32
|
+
|
|
33
|
+
Sub-command names match the locked contract in
|
|
34
|
+
[`docs/contracts/command-clusters.md`](../docs/contracts/command-clusters.md).
|
|
35
|
+
|
|
36
|
+
## Dispatch
|
|
37
|
+
|
|
38
|
+
1. Parse the user's argument: `/skill <sub-command> [args]`.
|
|
39
|
+
2. Look up the sub-command in the table above.
|
|
40
|
+
3. Load the body of the routed file and follow its `## Steps` section verbatim.
|
|
41
|
+
4. Unknown / missing sub-command → route to `preview` (the only verb today).
|
|
42
|
+
|
|
43
|
+
## Rules
|
|
44
|
+
|
|
45
|
+
- **Read-only.** Preview reads a skill's SKILL.md; it never runs the skill.
|
|
46
|
+
- **Not a sandbox.** Preview surfaces *declared intent*, not a guarantee of
|
|
47
|
+
side-effect-freeness — a contract non-goal.
|
|
48
|
+
- **One skill per invocation.** Do not chain.
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
---
|
|
2
|
+
model_tier: inherit
|
|
3
|
+
name: skills:discover
|
|
4
|
+
tier: 2
|
|
5
|
+
cluster: skills
|
|
6
|
+
sub: discover
|
|
7
|
+
description: Recommend skills for a role — ranked by four explained classes (most-useful-for-role, related-to-current-task, recently-adopted, popular-in-role). Local-only; every result carries a why.
|
|
8
|
+
skills: [file-editor]
|
|
9
|
+
suggestion:
|
|
10
|
+
eligible: true
|
|
11
|
+
trigger_description: "which skills should I use, recommend skills for my role, what fits this work, help me find a skill, /skills:discover sales"
|
|
12
|
+
trigger_context: "user wants a short, explained skill shortlist instead of scanning the 220-skill catalog"
|
|
13
|
+
workspaces:
|
|
14
|
+
- agent-config-maintainer
|
|
15
|
+
packs:
|
|
16
|
+
- meta
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
# /skills discover
|
|
20
|
+
|
|
21
|
+
Surfaces a short, explained skill shortlist for a role. Reuses existing local
|
|
22
|
+
signals only — the skill catalog frontmatter, the role's `skills.yml`
|
|
23
|
+
shortlist, and (when present and not opted out) the local-analytics JSONL.
|
|
24
|
+
Implements the [`skill-discovery`](../../../docs/contracts/skill-discovery.md)
|
|
25
|
+
contract. Local-only, read-only, no network.
|
|
26
|
+
|
|
27
|
+
## Prerequisites
|
|
28
|
+
|
|
29
|
+
- Python 3.10 + PyYAML on the host (already a package dependency).
|
|
30
|
+
- A role id — passed as `[role]`, or the active role from
|
|
31
|
+
`.agent-settings.yml` → `roles.active_role`.
|
|
32
|
+
|
|
33
|
+
## Steps
|
|
34
|
+
|
|
35
|
+
### 1. Resolve the role
|
|
36
|
+
|
|
37
|
+
The user invokes `/skills discover [role]`. The role is the first positional
|
|
38
|
+
argument. If omitted, the recommender falls back to `roles.active_role`. If
|
|
39
|
+
neither resolves, it prints the available roles and stops — do not guess.
|
|
40
|
+
|
|
41
|
+
### 2. Run the recommender
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
python3 scripts/skill_discovery.py --role <role>
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Optional flags: `--format json` (machine-readable), `--limit N` (results per
|
|
48
|
+
class, default 5). The script is pure-local and writes nothing.
|
|
49
|
+
|
|
50
|
+
### 3. Present the table
|
|
51
|
+
|
|
52
|
+
Render the recommender's Markdown table to the user:
|
|
53
|
+
`skill · class · why · first command`. Each row's `why` names the *signal*
|
|
54
|
+
(role match, domain adjacency, recent adoption, role popularity) — never a
|
|
55
|
+
bare score. The four classes are:
|
|
56
|
+
|
|
57
|
+
- `most-useful-for-role` — the role's priority shortlist.
|
|
58
|
+
- `related-to-current-task` — same-domain peers not already shortlisted.
|
|
59
|
+
- `recently-adopted` — used recently in this workspace (analytics) or the
|
|
60
|
+
shortlist tail when no usage signal exists yet.
|
|
61
|
+
- `popular-in-role` — launched most by this role locally (analytics) or the
|
|
62
|
+
shortlist when no usage signal exists yet.
|
|
63
|
+
|
|
64
|
+
### 4. Offer the first command
|
|
65
|
+
|
|
66
|
+
Each row carries a `first command` — the natural way to start with that skill.
|
|
67
|
+
Suggest the user pick one and run it. Do **not** auto-invoke a skill.
|
|
68
|
+
|
|
69
|
+
## Rules
|
|
70
|
+
|
|
71
|
+
- **Local-only, read-only.** No network, no writes, no prompt/response bodies.
|
|
72
|
+
- **Every result has a non-empty `why`** — a contract invariant.
|
|
73
|
+
- **Analytics opt-out honoured.** `AGENT_CONFIG_NO_LOCAL_ANALYTICS` env or
|
|
74
|
+
`analytics.local: off` → the analytics-backed classes fall back to the role
|
|
75
|
+
shortlist with an honest `why`; the surface never fabricates a usage signal.
|
|
76
|
+
- **One role per invocation.** Do not chain.
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
---
|
|
2
|
+
model_tier: inherit
|
|
3
|
+
name: skills
|
|
4
|
+
tier: 2
|
|
5
|
+
description: Skill discovery orchestrator — routes to discover. Local, explained skill recommendations over the catalog + role shortlists + optional local analytics.
|
|
6
|
+
cluster: skills
|
|
7
|
+
type: orchestrator
|
|
8
|
+
suggestion:
|
|
9
|
+
eligible: true
|
|
10
|
+
trigger_description: "which skills should I use, recommend skills for my role, what skills fit this work, I can't find the right skill, /skills:discover"
|
|
11
|
+
trigger_context: "user is lost in the 220-skill catalog and wants a short, explained shortlist for their role"
|
|
12
|
+
workspaces:
|
|
13
|
+
- agent-config-maintainer
|
|
14
|
+
packs:
|
|
15
|
+
- meta
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
# /skills
|
|
19
|
+
|
|
20
|
+
Top-level orchestrator for the `/skills` family — the **skill discovery**
|
|
21
|
+
cluster. Turns existing local signals (the skill catalog, the active role's
|
|
22
|
+
shortlist, and optional local analytics) into a short, *explained*
|
|
23
|
+
recommendation list. Local-only, no network, honours the analytics opt-out.
|
|
24
|
+
|
|
25
|
+
Anchors: [`skill-discovery`](../docs/contracts/skill-discovery.md) contract —
|
|
26
|
+
input signals, the four recommendation classes, and the non-negotiable
|
|
27
|
+
`why`-per-result requirement.
|
|
28
|
+
|
|
29
|
+
## Sub-commands
|
|
30
|
+
|
|
31
|
+
| Sub-command | Routes to | Purpose |
|
|
32
|
+
|---|---|---|
|
|
33
|
+
| `/skills discover` | `commands/skills/discover.md` | Rank skills for a role by four explained classes (most-useful / related / recently-adopted / popular) |
|
|
34
|
+
|
|
35
|
+
Sub-command names match the locked contract in
|
|
36
|
+
[`docs/contracts/command-clusters.md`](../docs/contracts/command-clusters.md).
|
|
37
|
+
|
|
38
|
+
## Dispatch
|
|
39
|
+
|
|
40
|
+
1. Parse the user's argument: `/skills <sub-command> [args]`.
|
|
41
|
+
2. Look up the sub-command in the table above.
|
|
42
|
+
3. Load the body of the routed file and follow its `## Steps` section
|
|
43
|
+
verbatim with the remaining args.
|
|
44
|
+
4. If the sub-command is unknown or missing, route to `discover` (the only
|
|
45
|
+
sub-command today) and print its menu.
|
|
46
|
+
|
|
47
|
+
## Rules
|
|
48
|
+
|
|
49
|
+
- **Local-only.** The recommender reads local files only — the catalog, the
|
|
50
|
+
role `skills.yml`, and (if present and not opted out) the local-analytics
|
|
51
|
+
JSONL. No network, no writes.
|
|
52
|
+
- **Every recommendation carries a `why`.** Never surface an unexplained
|
|
53
|
+
score — this is a contract invariant.
|
|
54
|
+
- **Honours the analytics opt-out** (`AGENT_CONFIG_NO_LOCAL_ANALYTICS` env or
|
|
55
|
+
`analytics.local: off`); degrades to catalog + role shortlist gracefully.
|
|
56
|
+
- **Do NOT chain sub-commands.** One `/skills <sub>` per turn.
|
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
---
|
|
2
|
+
model_tier: inherit
|
|
3
|
+
name: video:from-song
|
|
4
|
+
tier: 2
|
|
5
|
+
cluster: video
|
|
6
|
+
sub: from-song
|
|
7
|
+
description: Music-video from a song + reference images — accept or derive a timed scene script, optional character-lock, render, stitch, mux song as master track. Dry-run default; one batch gate for live calls.
|
|
8
|
+
personas: [hollywood-director, ai-video-technical-director]
|
|
9
|
+
skills: [song-to-script, scene-expander, video-director, character-consistency, motion-choreographer]
|
|
10
|
+
suggestion:
|
|
11
|
+
eligible: true
|
|
12
|
+
trigger_description: "make a music video from a song, turn a track into a video, lip-sync clip from images and audio, AI music video"
|
|
13
|
+
trigger_context: "user supplies an audio file plus reference images and wants a final MP4 cut to the song"
|
|
14
|
+
workspaces:
|
|
15
|
+
- agent-config-maintainer
|
|
16
|
+
packs:
|
|
17
|
+
- meta
|
|
18
|
+
lifecycle: experimental
|
|
19
|
+
trust:
|
|
20
|
+
level: experimental
|
|
21
|
+
install:
|
|
22
|
+
default: false
|
|
23
|
+
removable: true
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
# /video:from-song
|
|
27
|
+
|
|
28
|
+
`/video:from-song <images-dir> <song-file> [--brief "<description>"] [--auto-script] [--scene-durations <list>] [--character|--no-character] [--auto-pick] [--keep-native-audio] [--max-duration <min>] [--max-scenes <n>] [--image-provider <id>] [--video-provider <id>]`
|
|
29
|
+
|
|
30
|
+
Turns a **song** plus a **folder of reference images** into a finished
|
|
31
|
+
music-video. The scene script is either supplied by the operator
|
|
32
|
+
(`--brief`) or derived from the audio itself (`--auto-script`); if
|
|
33
|
+
neither flag is present the command asks. After the script exists this
|
|
34
|
+
command reuses the same render path as
|
|
35
|
+
[`/video:from-script`](from-script.md) and ends by muxing the song over
|
|
36
|
+
the cut as the **master audio track**.
|
|
37
|
+
|
|
38
|
+
Provider flags override the `<default-image-provider>` /
|
|
39
|
+
`<default-video-provider>` from
|
|
40
|
+
[`agents/.ai-video.xml`](../../../agents/templates/.ai-video.xml.example);
|
|
41
|
+
absent flags fall back to the XML defaults.
|
|
42
|
+
|
|
43
|
+
**Requires `pack-ai-video`.** The declared skills
|
|
44
|
+
(`song-to-script`, `scene-expander`, `video-director`,
|
|
45
|
+
`character-consistency`, `motion-choreographer`) ship in that pack; on a
|
|
46
|
+
global-only install Step 1's `validate-deps.sh` fails fast with the
|
|
47
|
+
missing-id list instead of an opaque mid-run error — install the pack
|
|
48
|
+
and re-run.
|
|
49
|
+
|
|
50
|
+
**Block-on-ambiguity:** a missing/empty images directory, an unreadable
|
|
51
|
+
audio file, contradictory mode flags (`--brief` *and* `--auto-script`),
|
|
52
|
+
contradictory character flags (`--character` *and* `--no-character`), or
|
|
53
|
+
a contradictory provider flag halts the run with a precise message — no
|
|
54
|
+
silent best-guess.
|
|
55
|
+
|
|
56
|
+
## Inputs
|
|
57
|
+
|
|
58
|
+
| Input | Required | Meaning |
|
|
59
|
+
|---|---|---|
|
|
60
|
+
| `<images-dir>` | yes | Folder of reference stills (`.png` / `.jpg`). When they contain a consistent human subject the on-screen identity is locked from them; otherwise the run is style-only (Step 6). |
|
|
61
|
+
| `<song-file>` | yes | Audio track (`.mp3` / `.wav` / `.m4a`). Defines total duration and, in `--auto-script` mode, the scene structure. |
|
|
62
|
+
| `--brief "<text>"` | one of brief/auto | Operator-written description of the video (mood, story, settings). |
|
|
63
|
+
| `--auto-script` | one of brief/auto | Derive the script from the song via the `song-to-script` skill. |
|
|
64
|
+
| `--scene-durations <list>` | no | Manual cut points (e.g. `0:00-0:15,0:15-0:30,…`). Overrides probe timing — the honest path when the track is flat (probe `method: interval`). |
|
|
65
|
+
| `--character` / `--no-character` | no | Force character-lock on/off. Default: auto-detect a subject in `<images-dir>`. |
|
|
66
|
+
| `--keep-native-audio` | no | Keep provider-generated audio instead of dropping it for the song (Step 8). |
|
|
67
|
+
|
|
68
|
+
## Steps
|
|
69
|
+
|
|
70
|
+
### 1. Validate dependencies
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
scripts/ai-video/lib/validate-deps.sh .agent-src.uncondensed/commands/video/from-song.md
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Fails fast with the missing-id list if any declared persona / skill is
|
|
77
|
+
absent from `.agent-src/personas/` or `.agent-src/skills/`. No network
|
|
78
|
+
call has happened yet.
|
|
79
|
+
|
|
80
|
+
Then confirm the **runtime helper scripts** exist and are executable —
|
|
81
|
+
`scripts/ai-video/lib/probe-audio.sh`, `scripts/ai-video/lib/load-config.sh`,
|
|
82
|
+
`scripts/ai-video/stitch.sh` — so a missing script fails here (with the
|
|
83
|
+
path), not mid-run at Step 2/9.
|
|
84
|
+
|
|
85
|
+
**Non-interactive contexts.** When stdin is not a TTY (CI, cron, headless
|
|
86
|
+
harness) the command cannot prompt: it requires `--brief` or
|
|
87
|
+
`--auto-script`, an explicit `--character`/`--no-character`, and
|
|
88
|
+
`--auto-pick`, and refuses live calls outright. Missing any → fail fast
|
|
89
|
+
with usage, never a deadlocked prompt.
|
|
90
|
+
|
|
91
|
+
### 2. Validate inputs + media-governance input gate
|
|
92
|
+
|
|
93
|
+
- `<images-dir>` exists and holds ≥1 `.png`/`.jpg`. Empty or missing →
|
|
94
|
+
halt, list what was found.
|
|
95
|
+
- `<song-file>` exists and is a readable audio container (`ffprobe`
|
|
96
|
+
returns an audio stream). Probe its length + structure now:
|
|
97
|
+
```bash
|
|
98
|
+
scripts/ai-video/lib/probe-audio.sh <song-file>
|
|
99
|
+
```
|
|
100
|
+
Emits `{duration, method, warning?, sections:[…]}` (deterministic, no
|
|
101
|
+
network). `duration` becomes the target length of the final cut. A
|
|
102
|
+
`method: interval` result (flat / brick-walled track) is surfaced to
|
|
103
|
+
the operator with the suggestion to pass `--scene-durations` for
|
|
104
|
+
musical cuts — never presented as beat-synced.
|
|
105
|
+
- **Media-governance input gate (mandatory).** Before any render,
|
|
106
|
+
consult the project-local media policies per
|
|
107
|
+
[`media-governance-routing`](../../rules/media-governance-routing.md):
|
|
108
|
+
- reference stills or brief depict a **real person's likeness** →
|
|
109
|
+
[`likeness`](../../../agents/settings/policies/media/likeness.md);
|
|
110
|
+
- a **recognised public figure** →
|
|
111
|
+
[`public-figures`](../../../agents/settings/policies/media/public-figures.md);
|
|
112
|
+
- the track is a **recognisable commercial song / a real artist's
|
|
113
|
+
voice** or the brief asks to clone one →
|
|
114
|
+
[`voice-cloning`](../../../agents/settings/policies/media/voice-cloning.md).
|
|
115
|
+
On a match: **refuse-and-surface** (one question per turn) — do not
|
|
116
|
+
best-guess past a likeness / rights concern.
|
|
117
|
+
|
|
118
|
+
### 3. Cost + duration guard (theory of failure)
|
|
119
|
+
|
|
120
|
+
Refuse, with a precise message, **before** loading providers:
|
|
121
|
+
|
|
122
|
+
- a song longer than the configured cap (default 8 min) — a 45-minute
|
|
123
|
+
track would launch a runaway paid render;
|
|
124
|
+
- a derived/briefed scene count above the cap (default 40 scenes).
|
|
125
|
+
|
|
126
|
+
The operator raises a cap explicitly (`--max-duration` / `--max-scenes`)
|
|
127
|
+
if they really mean it; the guard never silently proceeds.
|
|
128
|
+
|
|
129
|
+
### 4. Load config + resolve providers
|
|
130
|
+
|
|
131
|
+
Source `scripts/ai-video/lib/load-config.sh`. Resolve image / video
|
|
132
|
+
provider: command flag → `agents/.ai-video.xml` default → fail with the
|
|
133
|
+
available-providers list. A **malformed XML** or a default/flag naming a
|
|
134
|
+
provider with no `scripts/ai-video/adapters/<id>.sh` → fail fast here
|
|
135
|
+
with `provider '<id>' not found in adapters/` and the available list;
|
|
136
|
+
never a cryptic shell error mid-run. Surface the resolved provider's **lifecycle
|
|
137
|
+
tier** per
|
|
138
|
+
[`provider-lifecycle-discipline`](../../rules/provider-lifecycle-discipline.md);
|
|
139
|
+
all shipped adapters are `experimental` today, so the refuse-and-surface
|
|
140
|
+
path fires before any live call. For a music-video the **song is the
|
|
141
|
+
master track**, so a video provider with `audio-native=false` (e.g.
|
|
142
|
+
`kling`) is fine; native-audio providers (`gemini-veo`, `sora`) still
|
|
143
|
+
work — their audio is dropped at mux time in Step 8 unless
|
|
144
|
+
`--keep-native-audio` or a lip-sync scene needs it.
|
|
145
|
+
|
|
146
|
+
### 5. Select script mode (block on ambiguity)
|
|
147
|
+
|
|
148
|
+
- `--brief` and `--auto-script` both present → halt: "Pick one source
|
|
149
|
+
for the script."
|
|
150
|
+
- Exactly one present → use it.
|
|
151
|
+
- **Neither present → ask, then stop and wait:**
|
|
152
|
+
|
|
153
|
+
```
|
|
154
|
+
> How should I build the scene script?
|
|
155
|
+
>
|
|
156
|
+
> 1. From a description — I'll write the scenes to your brief
|
|
157
|
+
> 2. From the song — I'll derive scenes + timing from the audio
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
### 6. Build the timed scene script
|
|
161
|
+
|
|
162
|
+
Run the [`song-to-script`](../../skills/song-to-script/SKILL.md) skill
|
|
163
|
+
with the Step 2 probe result:
|
|
164
|
+
|
|
165
|
+
- **Brief mode** — the operator brief is the creative source; the audio
|
|
166
|
+
sections drive only the **cut timing**.
|
|
167
|
+
- **Auto mode** — the skill infers mood/energy per section and writes
|
|
168
|
+
both action and timing; vocal sections with lyrics populate
|
|
169
|
+
`dialogue:` for lip-sync.
|
|
170
|
+
- `--scene-durations` (if passed) overrides probe timing verbatim.
|
|
171
|
+
|
|
172
|
+
Output: `<project>/script.md` summing to the song length (reconciled in
|
|
173
|
+
Step 8). Present the script, the section→scene map, **and the probe
|
|
174
|
+
`method`**, then continue.
|
|
175
|
+
|
|
176
|
+
### 7. Character lock — optional, auto-detected
|
|
177
|
+
|
|
178
|
+
Detect whether `<images-dir>` contains a consistent **human subject**.
|
|
179
|
+
**Consistent** = the same recognisable face recurs across the **majority**
|
|
180
|
+
of stills. The branch:
|
|
181
|
+
|
|
182
|
+
- **Consistent subject (or `--character`)** → run `character-consistency`
|
|
183
|
+
once, seeding it with `<images-dir>`. Writes `<project>/character.json`
|
|
184
|
+
(subject, palette, wardrobe, prop, seed) reused verbatim downstream;
|
|
185
|
+
the stills are passed as `ref_images` so the locked identity matches.
|
|
186
|
+
- **No face at all (or `--no-character`)** → **skip the lock**, tell the
|
|
187
|
+
operator, and run **style-only continuity**: the reference stills set
|
|
188
|
+
palette / setting / look, and `song-to-script` runs in style mode
|
|
189
|
+
(abstract / landscape / visualiser videos are first-class — a face is
|
|
190
|
+
never required). Zero-face input is **not** an error.
|
|
191
|
+
- **Ambiguous** (faces in only some stills, or several *distinct* faces
|
|
192
|
+
with no clear lead) → **block and ask** which subject to lock or whether
|
|
193
|
+
to go style-only. Never silently pick a mode on a coin-flip; the
|
|
194
|
+
`--character`/`--no-character` flags pre-answer this for non-interactive
|
|
195
|
+
runs.
|
|
196
|
+
|
|
197
|
+
### 8. Render scenes (reuse from-script path) — ONE batch cost gate
|
|
198
|
+
|
|
199
|
+
For each scene in `<project>/script.md`, run Steps 3–7 of
|
|
200
|
+
[`/video:from-script`](from-script.md) verbatim: `scene-expander` →
|
|
201
|
+
blueprint → `video-director` eight-block image prompt → operator pick →
|
|
202
|
+
`motion-choreographer` → video adapter.
|
|
203
|
+
|
|
204
|
+
**Single batch COST confirmation (not per-step).** `AIV_DRYRUN=true` is
|
|
205
|
+
the default. Before the *first* live call, print the whole plan in one
|
|
206
|
+
prompt — image+video adapter, models, total scene count, and total
|
|
207
|
+
estimated cost — and refuse to continue without an explicit operator
|
|
208
|
+
confirmation (a literal yes) in this turn (mirrors
|
|
209
|
+
[`non-destructive-by-default`](../../rules/non-destructive-by-default.md)).
|
|
210
|
+
Once confirmed, the run proceeds through every scene + stitch + mux
|
|
211
|
+
without re-prompting **for cost**. The one remaining interactive surface
|
|
212
|
+
is `from-script`'s per-scene **operator-pick** (best-of-N still
|
|
213
|
+
selection) — a creative choice, not a spend gate; `--auto-pick` collapses
|
|
214
|
+
it to best-of-1 so the batch is fully unattended (required in
|
|
215
|
+
non-interactive contexts).
|
|
216
|
+
|
|
217
|
+
**Mid-batch failure + abort.** A per-scene adapter failure (rate-limit
|
|
218
|
+
`429`, provider content-policy refusal, network drop) **halts the batch**,
|
|
219
|
+
writes the completed-scene state to `<project>/`, and surfaces which
|
|
220
|
+
scene failed and why — it does not skip ahead or burn the rest of the
|
|
221
|
+
budget. `SIGINT` (Ctrl-C) writes state and exits clean. Re-running the
|
|
222
|
+
command resumes from the completed scenes (the "one project per
|
|
223
|
+
invocation" resume path), so a failed or aborted run is recoverable
|
|
224
|
+
without re-paying for finished scenes.
|
|
225
|
+
|
|
226
|
+
### 9. Stitch + master-audio mux + duration reconciliation
|
|
227
|
+
|
|
228
|
+
1. Build `<project>/manifest.json` with every scene as **video-only**
|
|
229
|
+
(`audio_embedded: false`) so the concat is silent — unless
|
|
230
|
+
`--keep-native-audio`, or a scene is flagged `character: talking`
|
|
231
|
+
(lip-sync), where dropping audio would desync mouth motion: keep that
|
|
232
|
+
scene's native audio and surface the mixed-audio decision rather than
|
|
233
|
+
silently dubbing.
|
|
234
|
+
2. Concatenate:
|
|
235
|
+
```bash
|
|
236
|
+
scripts/ai-video/stitch.sh <project>/manifest.json <project>/cut.mp4
|
|
237
|
+
```
|
|
238
|
+
3. **Reconcile duration explicitly** — compare the silent cut length to
|
|
239
|
+
the song:
|
|
240
|
+
- cut == song (±1.0 s) → mux straight through;
|
|
241
|
+
- cut **shorter** → offer `--loop-last` (hold the final scene) or
|
|
242
|
+
`--retime` (re-derive Step 6 timing); default = trim audio to the
|
|
243
|
+
cut with a short audio fade-out on the tail;
|
|
244
|
+
- cut **longer** → default = hard-trim video to the song length.
|
|
245
|
+
|
|
246
|
+
Never pad silently — **and never trim silently either**: whichever
|
|
247
|
+
default fires, report it concretely ("trimmed scene 12 from 18.0 s to
|
|
248
|
+
3.4 s to match the 3:45 song" / "faded the song tail by 0.6 s"), so the
|
|
249
|
+
operator sees exactly what the reconciliation did. Then mux the song as the master track:
|
|
250
|
+
```bash
|
|
251
|
+
ffmpeg -loglevel error -y -i <project>/cut.mp4 -i <song-file> \
|
|
252
|
+
-map 0:v:0 -map 1:a:0 -c:v copy -c:a aac -shortest <project>/final.mp4
|
|
253
|
+
```
|
|
254
|
+
4. **Mandatory AI-generation disclosure (non-removable).** Embed the
|
|
255
|
+
disclosure metadata into `final.mp4` per
|
|
256
|
+
[`disclosure`](../../../agents/settings/policies/media/disclosure.md)
|
|
257
|
+
and, where the container supports it, a provenance tag per
|
|
258
|
+
[`transparency`](../../../agents/settings/policies/media/transparency.md).
|
|
259
|
+
The run **cannot complete** without the disclosure — it is not a flag.
|
|
260
|
+
|
|
261
|
+
### 10. Report
|
|
262
|
+
|
|
263
|
+
Print: project slug, final MP4 path, song length vs. cut length, probe
|
|
264
|
+
`method`, scenes rendered, scenes skipped, script mode (`brief` | `auto`),
|
|
265
|
+
subject mode (`character` | `style`), provider + lifecycle tier,
|
|
266
|
+
**media-governance gate result** (pass / refused-and-surfaced — the audit
|
|
267
|
+
record), **reconciliation action** taken (Step 9.3), disclosure
|
|
268
|
+
confirmed, estimated cost (live mode) or `dry-run` marker. No commit. No
|
|
269
|
+
push.
|
|
270
|
+
|
|
271
|
+
## Rules
|
|
272
|
+
|
|
273
|
+
- **No commit, no push, no PR.** Pipeline produces artefacts; the
|
|
274
|
+
operator chooses what to ship.
|
|
275
|
+
- **Dry-run is the default.** One batch confirmation gates all live
|
|
276
|
+
calls — never a per-step interrogation, never a silent live run.
|
|
277
|
+
- **Media governance is a hard gate.** Input likeness / public-figure /
|
|
278
|
+
voice checks block before render; the output MP4 always carries a
|
|
279
|
+
non-removable AI-generation disclosure.
|
|
280
|
+
- **The song is the master audio track.** Provider-native audio is
|
|
281
|
+
dropped at mux unless `--keep-native-audio` or a lip-sync scene needs
|
|
282
|
+
it (surface the conflict, never silently dub).
|
|
283
|
+
- **Cost + duration are guarded.** Over-cap songs / scene counts are
|
|
284
|
+
refused before any provider loads.
|
|
285
|
+
- **Character lock is optional.** A no-face image folder produces a
|
|
286
|
+
style-only video; never abort for a missing subject.
|
|
287
|
+
- **Block on ambiguity** — never silently best-guess the script source,
|
|
288
|
+
scene timing, provider, or character mode.
|
|
289
|
+
- **Honest cut framing.** A flat track's `interval` cuts are never
|
|
290
|
+
presented as beat-synced; point the operator at `--scene-durations`.
|
|
291
|
+
- **One project per invocation.** Re-running on the same project resumes
|
|
292
|
+
from existing artefacts (skips completed scenes); a failed or aborted
|
|
293
|
+
batch is recoverable this way without re-paying for finished scenes.
|
|
294
|
+
- **Kill-switch.** Ships `lifecycle: experimental` · `install.default:
|
|
295
|
+
false`. Disable = remove the command + `song-to-script` skill (then
|
|
296
|
+
regenerate the projected tool trees); the `/video` orchestrator
|
|
297
|
+
degrades gracefully on an absent sub-command.
|
|
298
|
+
|
|
299
|
+
## Policies
|
|
300
|
+
|
|
301
|
+
- [`likeness`](../../../agents/settings/policies/media/likeness.md) ·
|
|
302
|
+
[`public-figures`](../../../agents/settings/policies/media/public-figures.md) ·
|
|
303
|
+
[`voice-cloning`](../../../agents/settings/policies/media/voice-cloning.md) —
|
|
304
|
+
input gate (Step 2).
|
|
305
|
+
- [`disclosure`](../../../agents/settings/policies/media/disclosure.md) ·
|
|
306
|
+
[`transparency`](../../../agents/settings/policies/media/transparency.md) —
|
|
307
|
+
mandatory output disclosure (Step 9.4).
|
|
308
|
+
|
|
309
|
+
## See also
|
|
310
|
+
|
|
311
|
+
- [`/video:from-script`](from-script.md) — same render path from a
|
|
312
|
+
hand-written script
|
|
313
|
+
- [`/video:scene`](scene.md) — single-scene iteration
|
|
314
|
+
- [`/video:stitch`](stitch.md) — re-stitch after operator edits
|
|
315
|
+
- [`song-to-script`](../../skills/song-to-script/SKILL.md) — audio →
|
|
316
|
+
timed scene script
|
|
317
|
+
- [`scripts/ai-video/lib/adapter-contract.md`](../../../scripts/ai-video/lib/adapter-contract.md)
|