@ketecode/loom 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +111 -11
- package/agents/code-reviewer/evals/behavior.json +60 -0
- package/agents/inbox-triager/AGENT.md +98 -0
- package/agents/inbox-triager/evals/triggers.json +16 -0
- package/agents/spec-reviewer/AGENT.md +95 -0
- package/agents/spec-reviewer/evals/behavior.json +57 -0
- package/agents/spec-reviewer/evals/triggers.json +16 -0
- package/agents/task-implementer/AGENT.md +89 -0
- package/agents/task-implementer/evals/behavior.json +47 -0
- package/agents/task-implementer/evals/triggers.json +16 -0
- package/bin/kete-loom.js +46 -6
- package/commands/brainstorm.md +25 -0
- package/commands/debug.md +27 -0
- package/commands/dual-review.md +34 -0
- package/commands/feature-dev.md +8 -7
- package/commands/finish-branch.md +26 -0
- package/commands/fix-bug.md +38 -0
- package/commands/production-readiness.md +45 -0
- package/commands/usage-report.md +43 -0
- package/hooks/block-foreground-servers/HOOK.md +34 -0
- package/hooks/block-foreground-servers/cases.json +31 -0
- package/hooks/block-foreground-servers/hook.js +41 -0
- package/hooks/capture-lessons/HOOK.md +24 -0
- package/hooks/capture-lessons/hook.js +48 -0
- package/hooks/notify-on-stop/HOOK.md +25 -0
- package/hooks/notify-on-stop/hook.js +27 -0
- package/hooks/protect-lint-config/HOOK.md +29 -0
- package/hooks/protect-lint-config/cases.json +18 -0
- package/hooks/protect-lint-config/fixtures/.eslintrc.json +1 -0
- package/hooks/protect-lint-config/fixtures/.golangci.yml +1 -0
- package/hooks/protect-lint-config/fixtures/.prettierrc +1 -0
- package/hooks/protect-lint-config/fixtures/.rubocop.yml +1 -0
- package/hooks/protect-lint-config/fixtures/biome.json +1 -0
- package/hooks/protect-lint-config/fixtures/eslint.config.mjs +1 -0
- package/hooks/protect-lint-config/fixtures/ruff.toml +1 -0
- package/hooks/protect-lint-config/hook.js +25 -0
- package/hooks/read-before-edit/HOOK.md +26 -0
- package/hooks/read-before-edit/cases.json +15 -0
- package/hooks/read-before-edit/hook.js +50 -0
- package/hooks/run.js +22 -6
- package/hooks/verify-before-stop/HOOK.md +32 -0
- package/hooks/verify-before-stop/hook.js +70 -0
- package/mcp/clickhouse.json +11 -0
- package/mcp/exa.json +11 -0
- package/mcp/firecrawl.json +11 -0
- package/mcp/vercel.json +11 -0
- package/package.json +6 -4
- package/profiles.json +54 -5
- package/rules/use-skills.md +23 -0
- package/scripts/lib/behavior.js +52 -0
- package/scripts/lib/hooks-install.js +9 -7
- package/scripts/lib/lint.js +24 -2
- package/scripts/lib/mcp-install.js +5 -1
- package/scripts/lib/render.js +6 -0
- package/scripts/lib/skills.js +1 -0
- package/scripts/lib/targets.js +10 -0
- package/skills/agent-failure-debugging/SKILL.md +92 -0
- package/skills/agent-failure-debugging/evals/triggers.json +16 -0
- package/skills/agent-outbound-messaging/SKILL.md +92 -0
- package/skills/agent-outbound-messaging/evals/triggers.json +16 -0
- package/skills/agent-security/SKILL.md +117 -0
- package/skills/agent-security/evals/triggers.json +16 -0
- package/skills/agent-security/references/host-configs.md +37 -0
- package/skills/api-client-integration/SKILL.md +86 -0
- package/skills/api-client-integration/evals/triggers.json +16 -0
- package/skills/api-client-integration/references/resilience.md +57 -0
- package/skills/blender-automation/SKILL.md +100 -0
- package/skills/blender-automation/evals/triggers.json +16 -0
- package/skills/blender-automation/references/batch-and-render.md +77 -0
- package/skills/blender-automation/references/bpy-inspection.md +91 -0
- package/skills/blender-automation/references/rig-and-retarget-checks.md +23 -0
- package/skills/brand-strategy/SKILL.md +83 -0
- package/skills/brand-strategy/evals/triggers.json +16 -0
- package/skills/change-impact-analysis/SKILL.md +89 -0
- package/skills/change-impact-analysis/evals/triggers.json +16 -0
- package/skills/change-impact-analysis/references/graph-commands.md +28 -0
- package/skills/contract-drafting/SKILL.md +96 -0
- package/skills/contract-drafting/evals/triggers.json +16 -0
- package/skills/contract-drafting/references/term-sheets.md +68 -0
- package/skills/contract-first-development/SKILL.md +86 -0
- package/skills/contract-first-development/evals/triggers.json +16 -0
- package/skills/contract-first-development/references/tooling.md +46 -0
- package/skills/decision-council/SKILL.md +98 -0
- package/skills/decision-council/evals/triggers.json +16 -0
- package/skills/design-brainstorming/SKILL.md +132 -0
- package/skills/design-brainstorming/evals/behavior.json +55 -0
- package/skills/design-brainstorming/evals/triggers.json +16 -0
- package/skills/design-brainstorming/references/visual-mockups.md +34 -0
- package/skills/desktop-app-testing/SKILL.md +104 -0
- package/skills/desktop-app-testing/evals/triggers.json +16 -0
- package/skills/desktop-app-testing/references/drivers.md +81 -0
- package/skills/document-processing/SKILL.md +100 -0
- package/skills/document-processing/evals/triggers.json +17 -0
- package/skills/document-processing/references/extraction-and-ocr.md +71 -0
- package/skills/document-processing/references/forms-and-conversion.md +69 -0
- package/skills/document-processing/references/redaction.md +61 -0
- package/skills/energy-procurement/SKILL.md +106 -0
- package/skills/energy-procurement/evals/triggers.json +16 -0
- package/skills/finishing-a-branch/SKILL.md +127 -0
- package/skills/finishing-a-branch/evals/behavior.json +49 -0
- package/skills/finishing-a-branch/evals/triggers.json +16 -0
- package/skills/freight-carrier-management/SKILL.md +107 -0
- package/skills/freight-carrier-management/evals/triggers.json +16 -0
- package/skills/git-worktrees/SKILL.md +130 -0
- package/skills/git-worktrees/evals/behavior.json +56 -0
- package/skills/git-worktrees/evals/triggers.json +16 -0
- package/skills/google-workspace-integration/SKILL.md +65 -0
- package/skills/google-workspace-integration/evals/triggers.json +17 -0
- package/skills/google-workspace-integration/references/api.md +121 -0
- package/skills/harmonyos-arkts/SKILL.md +117 -0
- package/skills/harmonyos-arkts/evals/triggers.json +16 -0
- package/skills/harmonyos-arkts/references/build-errors.md +42 -0
- package/skills/implementation-plans/SKILL.md +117 -0
- package/skills/implementation-plans/evals/behavior.json +54 -0
- package/skills/implementation-plans/evals/triggers.json +16 -0
- package/skills/ios-platform-features/SKILL.md +99 -0
- package/skills/ios-platform-features/evals/triggers.json +16 -0
- package/skills/ios-platform-features/references/foundation-models.md +90 -0
- package/skills/jvm-sql-mappers/SKILL.md +98 -0
- package/skills/jvm-sql-mappers/evals/triggers.json +16 -0
- package/skills/jvm-sql-mappers/references/examples.md +129 -0
- package/skills/mobile-accessibility/SKILL.md +94 -0
- package/skills/mobile-accessibility/evals/triggers.json +16 -0
- package/skills/network-design/SKILL.md +98 -0
- package/skills/network-design/evals/triggers.json +16 -0
- package/skills/observability/SKILL.md +125 -0
- package/skills/observability/evals/triggers.json +17 -0
- package/skills/observability/references/otel-setup.md +87 -0
- package/skills/observability/references/slo-alerts.md +70 -0
- package/skills/open-source-release/SKILL.md +107 -0
- package/skills/open-source-release/evals/triggers.json +15 -0
- package/skills/open-source-release/references/history-scrub.md +65 -0
- package/skills/patent-research/SKILL.md +94 -0
- package/skills/patent-research/evals/triggers.json +16 -0
- package/skills/patent-research/references/databases-and-queries.md +52 -0
- package/skills/prospect-research/SKILL.md +87 -0
- package/skills/prospect-research/evals/triggers.json +16 -0
- package/skills/python-app-distribution/SKILL.md +107 -0
- package/skills/python-app-distribution/evals/triggers.json +16 -0
- package/skills/python-app-distribution/references/pypi-trusted-publishing.md +55 -0
- package/skills/python-app-distribution/references/windows-macos-signing.md +70 -0
- package/skills/quality-nonconformance/SKILL.md +107 -0
- package/skills/quality-nonconformance/evals/triggers.json +16 -0
- package/skills/quality-nonconformance/references/8d-and-capa.md +46 -0
- package/skills/react-testing/SKILL.md +121 -0
- package/skills/react-testing/evals/triggers.json +18 -0
- package/skills/react-testing/references/msw.md +64 -0
- package/skills/react-testing/references/queries.md +41 -0
- package/skills/react-testing/references/setup.md +74 -0
- package/skills/recommender-systems/SKILL.md +119 -0
- package/skills/recommender-systems/evals/triggers.json +16 -0
- package/skills/recommender-systems/references/evaluation.md +39 -0
- package/skills/refactoring-safely/evals/behavior.json +57 -0
- package/skills/reproducible-dev-environments/SKILL.md +132 -0
- package/skills/reproducible-dev-environments/evals/triggers.json +16 -0
- package/skills/responding-to-code-review/SKILL.md +124 -0
- package/skills/responding-to-code-review/evals/behavior.json +54 -0
- package/skills/responding-to-code-review/evals/triggers.json +16 -0
- package/skills/security-review/evals/behavior.json +56 -0
- package/skills/session-handoff/evals/behavior.json +55 -0
- package/skills/skill-authoring/SKILL.md +13 -2
- package/skills/skill-authoring/evals/triggers.json +2 -1
- package/skills/skill-authoring/references/pressure-testing.md +87 -0
- package/skills/slide-decks/SKILL.md +107 -0
- package/skills/slide-decks/evals/triggers.json +18 -0
- package/skills/slide-decks/references/html-decks.md +86 -0
- package/skills/slide-decks/references/layouts.md +45 -0
- package/skills/slide-decks/references/pptx.md +87 -0
- package/skills/subagent-driven-development/SKILL.md +109 -0
- package/skills/subagent-driven-development/evals/behavior.json +53 -0
- package/skills/subagent-driven-development/evals/triggers.json +15 -0
- package/skills/subagent-driven-development/references/implementer-brief.md +60 -0
- package/skills/subagent-driven-development/references/reviewer-briefs.md +52 -0
- package/skills/systematic-debugging/SKILL.md +92 -0
- package/skills/systematic-debugging/evals/behavior.json +59 -0
- package/skills/systematic-debugging/evals/triggers.json +16 -0
- package/skills/systematic-debugging/references/condition-waiting.md +52 -0
- package/skills/systematic-debugging/references/root-cause-tracing.md +49 -0
- package/skills/systematic-debugging/references/test-pollution.md +46 -0
- package/skills/tdd-workflow/SKILL.md +3 -1
- package/skills/tdd-workflow/evals/behavior.json +55 -0
- package/skills/tdd-workflow/evals/triggers.json +2 -1
- package/skills/tdd-workflow/references/testing-anti-patterns.md +87 -0
- package/skills/transactional-email/SKILL.md +68 -0
- package/skills/transactional-email/evals/triggers.json +17 -0
- package/skills/transactional-email/references/providers.md +63 -0
- package/skills/ui-demo-recording/SKILL.md +93 -0
- package/skills/ui-demo-recording/evals/triggers.json +16 -0
- package/skills/ui-demo-recording/references/cursor-and-captions.md +48 -0
- package/skills/ui-demo-recording/references/playwright-recording.md +63 -0
- package/skills/ui-demo-recording/references/post-processing.md +57 -0
- package/skills/ui-design-polish/SKILL.md +113 -0
- package/skills/ui-design-polish/evals/triggers.json +18 -0
- package/skills/ui-design-polish/references/detail-checklist.md +45 -0
- package/skills/ui-design-polish/references/tokens.md +68 -0
- package/skills/ui-flow-audit/SKILL.md +112 -0
- package/skills/ui-flow-audit/evals/triggers.json +17 -0
- package/skills/ui-motion/SKILL.md +103 -0
- package/skills/ui-motion/evals/triggers.json +18 -0
- package/skills/ui-motion/references/patterns.md +138 -0
- package/skills/ui-motion/references/tokens.md +52 -0
- package/skills/unicode-text-processing/SKILL.md +103 -0
- package/skills/unicode-text-processing/evals/triggers.json +17 -0
- package/skills/unicode-text-processing/references/apis.md +89 -0
- package/skills/unicode-text-processing/references/script-notes.md +50 -0
- package/skills/verification-loop/evals/behavior.json +52 -0
- package/skills/video-editing/SKILL.md +111 -0
- package/skills/video-editing/evals/triggers.json +16 -0
- package/skills/video-editing/references/edl-and-logging.md +44 -0
- package/skills/video-editing/references/ffmpeg-edit-recipes.md +92 -0
- package/skills/video-editing/references/resolve-scripting.md +53 -0
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: blender-automation
|
|
3
|
+
description: Scripts Blender through its bpy Python API and command line - inspecting armatures, bone hierarchies, rest and pose transforms, constraints and vertex groups, diagnosing broken rigs and retargeted animation (wrong bone roll, scale on the armature, mismatched rest poses, missing action slots), editing actions and F-Curves through layers, strips and channelbags, batch-editing objects, materials and modifiers across many .blend files, and rendering headless with blender -b. Use when a rig or imported FBX animation looks wrong in Blender, when the same change must be applied to dozens of scenes or assets, or when renders must run from a script or on a farm without the UI.
|
|
4
|
+
license: MIT
|
|
5
|
+
compatibility: Any Agent Skills host. Needs a local Blender install on PATH (examples target Blender 4.4 and later, where actions use slots; 5.0 removed Action.fcurves). GPU rendering needs a supported GPU and driver.
|
|
6
|
+
metadata:
|
|
7
|
+
kete-loom/version: "0.1.0"
|
|
8
|
+
kete-loom/category: coding
|
|
9
|
+
kete-loom/risk: runs-commands
|
|
10
|
+
kete-loom/requires: "blender"
|
|
11
|
+
kete-loom/owner: "Kete-org"
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
# Blender automation
|
|
15
|
+
|
|
16
|
+
Drive Blender from scripts so every change is repeatable, reviewable and can run without the UI. Inspect first, change on a copy, verify by reading the data back and rendering a check frame.
|
|
17
|
+
|
|
18
|
+
## 0. Before you run anything
|
|
19
|
+
|
|
20
|
+
1. Find the Blender version: `blender --version`. The Python API changes between releases; open the API docs for that exact version (`https://docs.blender.org/api/<major.minor>/`).
|
|
21
|
+
2. Ask the user which `.blend` files are in scope and where outputs should go. **Work on copies**: never save over the user's originals unless they say so.
|
|
22
|
+
3. Run scripts headless with an exit code on failure:
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
blender -b --factory-startup scene.blend --python-exit-code 1 -P script.py -- --out /abs/out
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Arguments run in order: load the `.blend` first, then `-o` / `-F`, then `-f` or `-a`. Everything after `--` reaches the script via `sys.argv`. `--factory-startup` skips the user's startup file and add-ons, so enable add-ons the script needs explicitly.
|
|
29
|
+
|
|
30
|
+
## 1. Inspect before changing
|
|
31
|
+
|
|
32
|
+
Dump the facts into a text or JSON report rather than guessing from screenshots. Snippets in `references/bpy-inspection.md`:
|
|
33
|
+
|
|
34
|
+
- Scene: objects, types, parents, collections, frame range, fps, render engine, output settings.
|
|
35
|
+
- Armature: bones with parent, head, tail, roll, `use_deform`, `use_connect`, inherit scale mode; pose bones with rotation mode and constraints.
|
|
36
|
+
- Mesh binding: Armature modifier target, vertex groups that have no matching deform bone, and deform bones with no vertex group.
|
|
37
|
+
- Animation: each object's `animation_data.action`, `action_slot`, NLA tracks, and per-bone F-Curve key counts and frame range.
|
|
38
|
+
- Object transforms: any non-applied scale or rotation on the armature or mesh object.
|
|
39
|
+
|
|
40
|
+
## 2. Diagnose rigs and animation
|
|
41
|
+
|
|
42
|
+
Work through `references/rig-and-retarget-checks.md`. The most common causes, in order:
|
|
43
|
+
|
|
44
|
+
1. **Object scale or rotation not applied** on the armature (typical with FBX at 0.01 scale or a 90 degree X rotation). Animation then moves in the wrong units or axis.
|
|
45
|
+
2. **Rest pose mismatch** between source and target (A-pose versus T-pose), so retargeted rotations are offset everywhere.
|
|
46
|
+
3. **Bone roll differs** between rigs, so local rotation axes do not match and limbs twist.
|
|
47
|
+
4. **Name mismatch**: F-Curve data paths like `pose.bones["mixamorig:LeftArm"].rotation_quaternion` do not match the target bone names, so channels silently do nothing.
|
|
48
|
+
5. **Rotation mode mismatch**: curves on `rotation_euler` while the bone uses quaternions, or the reverse.
|
|
49
|
+
6. **Action not assigned to a slot** (Blender 4.4+): the action is set but `animation_data.action_slot` is empty or targets a different ID, so nothing plays.
|
|
50
|
+
7. **Constraints or IK** overriding the keyed pose, or bones with `inherit_scale` causing creeping scale.
|
|
51
|
+
|
|
52
|
+
For each issue found, record: symptom, evidence (values read from the file), cause, fix. **Stop and show the user the diagnosis before changing a rig** that other files link to or that is shared by a team.
|
|
53
|
+
|
|
54
|
+
## 3. Fix
|
|
55
|
+
|
|
56
|
+
- Apply transforms with `bpy.ops.object.transform_apply` only on a copy, in Object mode with the object active and selected, and check the effect on its children and animation (applying scale to an animated armature changes what the location keys mean).
|
|
57
|
+
- Rename bones or remap F-Curve data paths with a mapping table the user confirms; never guess a mapping for ambiguous bones (fingers, twist bones).
|
|
58
|
+
- To retarget, prefer constraints (Copy Rotation in local space, or a dedicated retargeting add-on the user already uses) and bake to a new action (`bpy.ops.nla.bake` with `visual_keying=True`, `clear_constraints=True`), keeping the original action.
|
|
59
|
+
- Edit keys through the layered action API (`references/bpy-inspection.md`, "Actions and F-Curves"). In Blender 5.0 and later, `Action.fcurves` no longer exists.
|
|
60
|
+
|
|
61
|
+
## 4. Batch edits
|
|
62
|
+
|
|
63
|
+
Pattern in `references/batch-and-render.md`:
|
|
64
|
+
|
|
65
|
+
1. List the target files and print the plan (file, what will change) before changing anything.
|
|
66
|
+
2. Open each file in a fresh Blender process (one `blender -b file.blend -P edit.py` per file) so state does not leak between files.
|
|
67
|
+
3. Make the change through data (`bpy.data`), not operators, wherever possible; operators depend on context and selection.
|
|
68
|
+
4. Save to an output folder with `bpy.ops.wm.save_as_mainfile(filepath=..., copy=True)`.
|
|
69
|
+
5. Re-open each output and assert the change took effect; write a per-file result line (ok, skipped, failed with reason).
|
|
70
|
+
|
|
71
|
+
## 5. Headless rendering
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
# one frame
|
|
75
|
+
blender -b shot.blend -E CYCLES -o /abs/renders/shot_#### -F PNG -f 1
|
|
76
|
+
# a range
|
|
77
|
+
blender -b shot.blend -o /abs/renders/shot_#### -F OPEN_EXR -s 1 -e 240 -a
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
- `#` characters become the zero-padded frame number; `//` means relative to the `.blend`.
|
|
81
|
+
- Pick the device and samples in a pre-render script (Cycles GPU setup in `references/batch-and-render.md`); without it Cycles uses whatever compute device that machine's saved preferences enable, and with `--factory-startup` that is the CPU.
|
|
82
|
+
- List engine names with `blender -b -E help`; names differ across versions.
|
|
83
|
+
- Render a single check frame and show it to the user before a long range or farm job.
|
|
84
|
+
- Render image sequences, not a movie file, for anything long: a crash only loses one frame, and the sequence can be encoded afterwards with FFmpeg.
|
|
85
|
+
|
|
86
|
+
## Never
|
|
87
|
+
|
|
88
|
+
- Never overwrite the user's `.blend`, linked libraries or textures in place.
|
|
89
|
+
- Never run scripts embedded in a `.blend` from an untrusted source (auto-run scripts stay off; never pass `-y` / `--enable-autoexec` for such files; pass `-Y` / `--disable-autoexec` to be explicit).
|
|
90
|
+
- Never delete actions, bones or vertex groups without a backup and the user's approval.
|
|
91
|
+
|
|
92
|
+
## Report back
|
|
93
|
+
|
|
94
|
+
```
|
|
95
|
+
Blender <version> Files: n processed (ok / skipped / failed)
|
|
96
|
+
Findings: <symptom> -> <cause> (evidence) -> <fix applied or proposed>
|
|
97
|
+
Changed files: <output paths> Renders: <path pattern, frames>
|
|
98
|
+
Verification: values read back, check frame <path>
|
|
99
|
+
Needs the user: mapping decisions, art direction, ...
|
|
100
|
+
```
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"should_trigger": [
|
|
3
|
+
"My Mixamo animation retargeted onto our character in Blender has twisted arms and the feet slide",
|
|
4
|
+
"Write a bpy script that sets roughness on every material across 40 .blend files and saves copies",
|
|
5
|
+
"Render frames 1 to 240 of this .blend headless on the Linux render box with the GPU",
|
|
6
|
+
"Imported FBX rig is at 0.01 scale and lying on its side, and the action does nothing in Blender 4.4",
|
|
7
|
+
"Dump every bone, its roll and its vertex group from this armature so I can see why the mesh tears"
|
|
8
|
+
],
|
|
9
|
+
"should_not_trigger": [
|
|
10
|
+
"Animate a 3D rotating cube proof with Manim for my maths lecture",
|
|
11
|
+
"Build a three.js scene that loads a glTF model in the browser",
|
|
12
|
+
"Turn this PNG image sequence into an MP4 at 24 fps",
|
|
13
|
+
"Generate a 3D product render from a text prompt with an image model",
|
|
14
|
+
"Edit the drone footage into a 90-second travel video with music"
|
|
15
|
+
]
|
|
16
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# Batch edits and rendering
|
|
2
|
+
|
|
3
|
+
## Driver script (one Blender process per file)
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
#!/usr/bin/env bash
|
|
7
|
+
set -euo pipefail
|
|
8
|
+
mkdir -p out
|
|
9
|
+
for f in assets/*.blend; do
|
|
10
|
+
blender -b --factory-startup "$f" --python-exit-code 1 -P edit.py -- "$PWD/out/$(basename "$f")" \
|
|
11
|
+
&& echo "ok $f" || echo "FAILED $f"
|
|
12
|
+
done
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## edit.py pattern
|
|
16
|
+
|
|
17
|
+
```python
|
|
18
|
+
import bpy, sys
|
|
19
|
+
out = sys.argv[sys.argv.index("--") + 1]
|
|
20
|
+
|
|
21
|
+
changed = 0
|
|
22
|
+
for mat in bpy.data.materials:
|
|
23
|
+
if not mat.use_nodes:
|
|
24
|
+
continue
|
|
25
|
+
bsdf = next((n for n in mat.node_tree.nodes if n.type == 'BSDF_PRINCIPLED'), None)
|
|
26
|
+
if bsdf:
|
|
27
|
+
bsdf.inputs["Roughness"].default_value = 0.6
|
|
28
|
+
changed += 1
|
|
29
|
+
|
|
30
|
+
for ob in bpy.data.objects:
|
|
31
|
+
if ob.type == 'MESH' and not any(m.type == 'WEIGHTED_NORMAL' for m in ob.modifiers):
|
|
32
|
+
ob.modifiers.new("WeightedNormal", 'WEIGHTED_NORMAL')
|
|
33
|
+
|
|
34
|
+
print(f"RESULT changed_materials={changed}")
|
|
35
|
+
bpy.ops.wm.save_as_mainfile(filepath=out, copy=True)
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Rules:
|
|
39
|
+
- Socket names (such as "Roughness") and node types can change between versions; check the API docs for the installed version and fail loudly if a name is missing.
|
|
40
|
+
- Prefer `bpy.data` edits over `bpy.ops`; when an operator is unavoidable, use `with bpy.context.temp_override(...)` to give it the context it needs.
|
|
41
|
+
- Use `bpy.ops.wm.save_as_mainfile(..., copy=True)` so the open file name is not changed and originals are not touched.
|
|
42
|
+
- Linked library data is read-only in the file that links it; edit the library file itself.
|
|
43
|
+
|
|
44
|
+
## Cycles GPU pre-render script
|
|
45
|
+
|
|
46
|
+
```python
|
|
47
|
+
import bpy
|
|
48
|
+
prefs = bpy.context.preferences.addons["cycles"].preferences
|
|
49
|
+
prefs.compute_device_type = "OPTIX" # or CUDA, HIP, METAL, ONEAPI, depending on hardware
|
|
50
|
+
prefs.get_devices()
|
|
51
|
+
for d in prefs.devices:
|
|
52
|
+
d.use = d.type != "CPU"
|
|
53
|
+
scn = bpy.context.scene
|
|
54
|
+
scn.render.engine = "CYCLES"
|
|
55
|
+
scn.cycles.device = "GPU"
|
|
56
|
+
scn.cycles.samples = 256
|
|
57
|
+
scn.cycles.use_denoising = True
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Use it with `blender -b shot.blend -P gpu.py -o /abs/renders/shot_#### -F PNG -a`. If no GPU device appears in `prefs.devices`, say so; do not silently fall back to a CPU render that will take hours.
|
|
61
|
+
|
|
62
|
+
## Render settings from Python
|
|
63
|
+
|
|
64
|
+
```python
|
|
65
|
+
r = bpy.context.scene.render
|
|
66
|
+
r.resolution_x, r.resolution_y, r.resolution_percentage = 1920, 1080, 100
|
|
67
|
+
r.image_settings.file_format = "PNG"
|
|
68
|
+
r.filepath = "//renders/shot_####"
|
|
69
|
+
bpy.context.scene.frame_start, bpy.context.scene.frame_end = 1, 240
|
|
70
|
+
bpy.ops.render.render(animation=True)
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Encode the sequence afterwards
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
ffmpeg -framerate 24 -i renders/shot_%04d.png -c:v libx264 -crf 18 -pix_fmt yuv420p shot.mp4
|
|
77
|
+
```
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# bpy inspection snippets
|
|
2
|
+
|
|
3
|
+
Run with `blender -b file.blend --python-exit-code 1 -P inspect.py -- out.json`.
|
|
4
|
+
|
|
5
|
+
## Scene and objects
|
|
6
|
+
|
|
7
|
+
```python
|
|
8
|
+
import bpy, json, sys
|
|
9
|
+
out = sys.argv[sys.argv.index("--") + 1]
|
|
10
|
+
scn = bpy.context.scene
|
|
11
|
+
report = {
|
|
12
|
+
"blender": bpy.app.version_string,
|
|
13
|
+
"frame_range": [scn.frame_start, scn.frame_end], "fps": scn.render.fps / scn.render.fps_base,
|
|
14
|
+
"engine": scn.render.engine, "output": scn.render.filepath,
|
|
15
|
+
"objects": [{
|
|
16
|
+
"name": o.name, "type": o.type, "parent": o.parent.name if o.parent else None,
|
|
17
|
+
"location": list(o.location), "rotation": list(o.rotation_euler), "scale": list(o.scale),
|
|
18
|
+
"collections": [c.name for c in o.users_collection],
|
|
19
|
+
} for o in bpy.data.objects],
|
|
20
|
+
}
|
|
21
|
+
json.dump(report, open(out, "w"), indent=2)
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Armature, bones and pose
|
|
25
|
+
|
|
26
|
+
```python
|
|
27
|
+
arm = bpy.data.objects["Armature"]
|
|
28
|
+
for b in arm.data.bones: # rest data (edit-bone values, read-only here)
|
|
29
|
+
print(b.name, b.parent.name if b.parent else None, tuple(b.head_local), tuple(b.tail_local),
|
|
30
|
+
b.use_deform, b.use_connect, b.inherit_scale)
|
|
31
|
+
for pb in arm.pose.bones: # pose data
|
|
32
|
+
print(pb.name, pb.rotation_mode, [c.type for c in pb.constraints], tuple(pb.matrix_basis.to_euler()))
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Bone roll is only exposed on edit bones: switch to Edit mode on the armature (`bpy.ops.object.mode_set(mode='EDIT')` with it active) and read `arm.data.edit_bones[name].roll`, then return to Object mode.
|
|
36
|
+
|
|
37
|
+
## Vertex groups versus deform bones
|
|
38
|
+
|
|
39
|
+
```python
|
|
40
|
+
mesh = bpy.data.objects["Body"]
|
|
41
|
+
deform = {b.name for b in arm.data.bones if b.use_deform}
|
|
42
|
+
groups = {g.name for g in mesh.vertex_groups}
|
|
43
|
+
print("groups without bone:", sorted(groups - deform))
|
|
44
|
+
print("deform bones without group:", sorted(deform - groups))
|
|
45
|
+
print("armature modifiers:", [(m.name, m.object.name if m.object else None) for m in mesh.modifiers if m.type == 'ARMATURE'])
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Actions and F-Curves (Blender 4.4+ layered actions)
|
|
49
|
+
|
|
50
|
+
Actions hold layers, layers hold strips, and a keyframe strip holds one channelbag per slot. `Action.fcurves` was removed in 5.0.
|
|
51
|
+
|
|
52
|
+
```python
|
|
53
|
+
ad = arm.animation_data
|
|
54
|
+
act, slot = ad.action, ad.action_slot
|
|
55
|
+
print(act.name if act else None, slot.identifier if slot else None, [s.identifier for s in act.slots])
|
|
56
|
+
|
|
57
|
+
from bpy_extras import anim_utils
|
|
58
|
+
bag = anim_utils.action_get_channelbag_for_slot(act, slot) # None if the slot has no keys
|
|
59
|
+
for fc in bag.fcurves:
|
|
60
|
+
keys = fc.keyframe_points
|
|
61
|
+
print(fc.data_path, fc.array_index, len(keys), keys[0].co.x if keys else None, keys[-1].co.x if keys else None)
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
If `action_get_channelbag_for_slot` is missing in the installed version, walk it directly: `act.layers[0].strips[0].channelbag(slot)`.
|
|
65
|
+
|
|
66
|
+
Remap bone names in data paths (after the user confirms the mapping):
|
|
67
|
+
|
|
68
|
+
```python
|
|
69
|
+
mapping = {"mixamorig:LeftArm": "upper_arm.L"}
|
|
70
|
+
for fc in bag.fcurves:
|
|
71
|
+
for src, dst in mapping.items():
|
|
72
|
+
token = f'pose.bones["{src}"]'
|
|
73
|
+
if fc.data_path.startswith(token):
|
|
74
|
+
fc.data_path = fc.data_path.replace(token, f'pose.bones["{dst}"]', 1)
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Assign an action and its slot:
|
|
78
|
+
|
|
79
|
+
```python
|
|
80
|
+
ad = arm.animation_data or arm.animation_data_create()
|
|
81
|
+
ad.action = act
|
|
82
|
+
if ad.action_slot is None and act.slots:
|
|
83
|
+
ad.action_slot = act.slots[0]
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## NLA
|
|
87
|
+
|
|
88
|
+
```python
|
|
89
|
+
for t in ad.nla_tracks:
|
|
90
|
+
print(t.name, t.mute, [(s.name, s.action.name, s.frame_start, s.frame_end) for s in t.strips])
|
|
91
|
+
```
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Rig and retarget checks
|
|
2
|
+
|
|
3
|
+
Work top to bottom. For each, read the values with the inspection snippets; do not judge from a viewport screenshot alone.
|
|
4
|
+
|
|
5
|
+
| # | Symptom | Check | Typical fix |
|
|
6
|
+
|---|---|---|---|
|
|
7
|
+
| 1 | Character tiny, huge, lying down, or root motion in wrong units | Armature and mesh object `scale` not 1, or `rotation_euler.x` near 1.5708 (FBX import) | On a copy, apply rotation and scale (`transform_apply(location=False, rotation=True, scale=True)`), or re-import FBX with Apply Transform or a unit scale the user confirms; re-check location keys after |
|
|
8
|
+
| 2 | Every limb offset by the same amount after retarget | Rest poses differ (A-pose vs T-pose): compare `bone.matrix_local` of matching bones | Retarget with constraints in local or pose space and bake, or adjust the source rest pose to match before baking |
|
|
9
|
+
| 3 | Limbs twist or rotate around the wrong axis | Edit-bone `roll` differs for matching bones | Match roll on the target only if it is not yet skinned or the user accepts re-weighting; otherwise retarget in world or pose space |
|
|
10
|
+
| 4 | Some bones do not move | F-Curve data paths name bones that are not in the target | Rename bones or remap data paths with a confirmed mapping |
|
|
11
|
+
| 5 | Bones move but rotate wrongly or jitter | Curves on `rotation_euler` but `rotation_mode` is `QUATERNION`, or the reverse; quaternion sign flips between keys | Set `rotation_mode` to match the curves, or convert and bake; fix sign flips by negating the quaternion on flipped keys |
|
|
12
|
+
| 6 | Action assigned but nothing plays (4.4+) | `animation_data.action_slot` is None or its `target_id_type` does not match | Assign the right slot |
|
|
13
|
+
| 7 | NLA or another action overrides the keys | NLA tracks not muted, action influence, or tweak mode | Mute or remove tracks on a copy; push the action to an NLA strip deliberately |
|
|
14
|
+
| 8 | Pose ignores keys | Constraints (IK, Child Of, Copy Transforms) with influence 1 | Check each constraint's influence and target; bake visual transforms if the constraints are only for retargeting |
|
|
15
|
+
| 9 | Mesh tears or parts stay behind | Vertex groups with no matching deform bone, `use_deform` off, Armature modifier missing or on the wrong object, or modifier order (Armature after a Subdivision) | Fix names and flags; move the Armature modifier to the top of the stack |
|
|
16
|
+
| 10 | Creeping scale down a chain | `inherit_scale` and scale keys on parent bones | Remove scale keys that should not exist; set inherit scale as the rig design requires |
|
|
17
|
+
| 11 | Foot sliding after retarget | Different leg lengths between rigs | Scale root motion by the leg-length ratio, or use IK for feet and bake |
|
|
18
|
+
|
|
19
|
+
Verification after any fix:
|
|
20
|
+
|
|
21
|
+
1. Read the changed values back from the saved copy.
|
|
22
|
+
2. Render or capture three frames (first, middle, last of the action) from a fixed camera and compare with the source.
|
|
23
|
+
3. Report key counts per bone before and after a bake; a bake should not drop channels unexpectedly.
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: brand-strategy
|
|
3
|
+
description: Brand identity strategy run as a structured interview, one question block at a time, covering purpose, positioning against named alternatives, the primary audience and their job to be done, three to five values tied to behaviours, personality traits and a naming brief, then written up as a one-page brand brief with open questions marked. Use when a founder or team is starting or rebranding a company or product and needs its positioning, audience, values and personality decided before voice guides, names, logos or campaigns are made.
|
|
4
|
+
license: MIT
|
|
5
|
+
compatibility: Any Agent Skills host. No tools needed beyond reading what the user shares and writing the brief.
|
|
6
|
+
metadata:
|
|
7
|
+
kete-loom/version: "0.1.0"
|
|
8
|
+
kete-loom/category: business
|
|
9
|
+
kete-loom/risk: writes-files
|
|
10
|
+
kete-loom/owner: "Kete-org"
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# Brand strategy
|
|
14
|
+
|
|
15
|
+
Turn what the founders know (and argue about) into a short brand brief that later work can build on: the `brand-voice` guide, a naming round, a logo brief, a `marketing-campaign` plan. The brief records decisions, not adjectives.
|
|
16
|
+
|
|
17
|
+
## Ground rules
|
|
18
|
+
|
|
19
|
+
1. **Interview, don't invent.** Every line in the brief traces to something the user said or chose. Where you propose wording, mark it as a proposal until they accept it.
|
|
20
|
+
2. **One block at a time.** Ask the questions for one block, wait, play back what you heard in two or three lines, then move on. Never send all the questions at once.
|
|
21
|
+
3. **Specific beats aspirational.** "Innovative, trusted, customer-first" fits every company and decides nothing. Push each answer until it would exclude someone or something.
|
|
22
|
+
4. **Name the alternative.** Positioning only means something against what the customer would do instead (a named competitor, a spreadsheet, a cousin who does it for free, doing nothing).
|
|
23
|
+
5. **Evidence over hope.** If an answer about customers is a guess, say so and record it as an assumption to test, not a fact.
|
|
24
|
+
|
|
25
|
+
## Before you start
|
|
26
|
+
|
|
27
|
+
Ask for anything that already exists: pitch deck, website, customer interviews, reviews, sales call notes, a competitor list, an earlier brand doc. Read it first and skip questions it already answers; confirm those answers instead of asking again.
|
|
28
|
+
|
|
29
|
+
Confirm the scope: a company brand, a product brand under a parent, or a rebrand. For a rebrand, ask what is wrong with the current brand and what must survive (name, colour, equity customers recognise).
|
|
30
|
+
|
|
31
|
+
## The interview
|
|
32
|
+
|
|
33
|
+
Work through these blocks in order. Each has a goal; the questions are a starting point, so follow up where answers are vague.
|
|
34
|
+
|
|
35
|
+
1. **Purpose.** Why the business exists beyond revenue, and what changes for customers if it succeeds. Ask: "What would your best customers lose if you shut down tomorrow?" and "What do you refuse to do even if it would make money?"
|
|
36
|
+
2. **Audience.** One primary audience, described by situation and job to be done, not demographics alone. Ask who buys, who uses, who blocks the purchase, what triggers them to look for a solution, and what they use today. If they name three equal audiences, ask which one they would serve if they could only serve one.
|
|
37
|
+
3. **Positioning.** Fill the frame: *For [audience] who [need or trigger], [brand] is the [category] that [key benefit], unlike [named alternative], because [reason to believe].* Ask for the proof behind the "because" (a feature, a number, a process, a founder fact).
|
|
38
|
+
4. **Values.** Three to five values, each with a behaviour that shows it and a trade-off it implies ("Plain speaking: we tell customers about a fee before they find it, even when it costs a sale"). Drop any value nobody can give a behaviour for.
|
|
39
|
+
5. **Personality.** Three to five traits as "this, not that" pairs on sliders (playful to serious, expert to peer, bold to calm). Ask: "If the brand were a person at a dinner party, who would it be, and who would it never be?" This feeds `brand-voice`; do not write the voice guide here.
|
|
40
|
+
6. **Naming brief** (only if a name is needed or under review). Capture what the name must signal, what it must avoid, markets and languages it must work in, the preferred name type (descriptive, invented, metaphor, founder, acronym), length, and must-haves such as a .com or a matching handle. Generate candidates only if asked, and never present one as available.
|
|
41
|
+
7. **Proof and constraints.** Claims the brand can make today with evidence, claims it cannot make yet, regulatory limits on claims in its sector, and existing assets that must be kept.
|
|
42
|
+
|
|
43
|
+
## Decision rules
|
|
44
|
+
|
|
45
|
+
- Two founders disagree on a block: record both positions side by side and ask them to choose. Do not average them into a vague middle.
|
|
46
|
+
- The user wants to be everything to everyone: show what each audience would need the positioning to say; usually the conflict makes the choice obvious.
|
|
47
|
+
- No reason to believe exists yet: keep the positioning but list the proof as a gap to build.
|
|
48
|
+
- A value or trait contradicts how the business actually behaves (for example "transparent" with hidden fees): say so plainly and ask which one changes.
|
|
49
|
+
|
|
50
|
+
## Stop and ask the user
|
|
51
|
+
|
|
52
|
+
- Before writing the brief, if any of purpose, primary audience or positioning is still undecided.
|
|
53
|
+
- Before suggesting a name be used publicly: trademark clearance and domain checks are needed, and clearance in each market is a job for a trademark professional, not this skill.
|
|
54
|
+
- If a positioning claim is a comparative or health, financial or environmental claim, flag that it may need legal review before it is published.
|
|
55
|
+
|
|
56
|
+
## Never
|
|
57
|
+
|
|
58
|
+
- Never fill the brief with stock adjectives the user did not choose.
|
|
59
|
+
- Never claim a name is free to use, trademarked or not, or that a domain is available, without a check the user can see.
|
|
60
|
+
- Never design logos, colour palettes or typography here; write the brief a designer would work from.
|
|
61
|
+
|
|
62
|
+
## Output: the brand brief
|
|
63
|
+
|
|
64
|
+
Write one page in Markdown (to a file if the user wants one), in this shape:
|
|
65
|
+
|
|
66
|
+
```
|
|
67
|
+
# <Brand> brand brief (v<n>, <date>)
|
|
68
|
+
|
|
69
|
+
Purpose: <one sentence>
|
|
70
|
+
Primary audience: <situation + job to be done>; secondary: <if any>
|
|
71
|
+
Positioning: For ... who ..., <brand> is the ... that ..., unlike ..., because ...
|
|
72
|
+
Proof: <evidence for the "because">; gaps: <proof still to build>
|
|
73
|
+
Values: <value>: <behaviour> / <trade-off> (3-5 lines)
|
|
74
|
+
Personality: <this, not that> (3-5 pairs)
|
|
75
|
+
Naming brief: <must signal / must avoid / markets / type / constraints> (if in scope)
|
|
76
|
+
Can claim today: ... Cannot claim yet: ...
|
|
77
|
+
Keep from current brand: ...
|
|
78
|
+
Assumptions to test: ...
|
|
79
|
+
Open questions: ...
|
|
80
|
+
Next: brand-voice guide, naming round, visual identity brief, campaign plan
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Close by listing which open questions block the next step, and which items are proposals the user has not yet accepted.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"should_trigger": [
|
|
3
|
+
"We're launching a new fintech startup and haven't decided who we're really for or how we're different. Can you interview us and write a brand brief?",
|
|
4
|
+
"Help me work out our brand positioning statement against the big banks and mobile money apps",
|
|
5
|
+
"We're rebranding our bakery chain. Walk us through purpose, values and personality before we hire a designer.",
|
|
6
|
+
"My co-founder and I disagree on what our brand stands for. Run a structured brand workshop with us.",
|
|
7
|
+
"Write a naming brief for our new product line so an agency can start generating names"
|
|
8
|
+
],
|
|
9
|
+
"should_not_trigger": [
|
|
10
|
+
"Create a voice and tone guide with do and don't rewrites for our support team",
|
|
11
|
+
"Size the market for our school management software in Nigeria with TAM, SAM and SOM",
|
|
12
|
+
"Plan the four-week launch campaign for our new app with a budget split",
|
|
13
|
+
"Design a logo and a colour palette for our coffee shop",
|
|
14
|
+
"Does this homepage copy sound like our brand? Here is the style guide."
|
|
15
|
+
]
|
|
16
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: change-impact-analysis
|
|
3
|
+
description: Blast-radius analysis for a code change, built from the real dependency graph rather than guesswork, using import search, the build graph and monorepo tooling (nx affected, turbo --affected, bazel query rdeps, go list, pnpm filters, Maven -amd, Gradle buildDependents) to find downstream consumers, then running the tests of changed modules and every dependent, and reconciling the predicted impact against the actual diff. Use when asked what a change could break, which packages or services consume a module, which tests must run for a diff, or whether a shared library edit is safe to merge.
|
|
4
|
+
license: MIT
|
|
5
|
+
compatibility: Any Agent Skills host. Needs git and the project's own build or test tooling; monorepo commands apply only where that tool is already in use.
|
|
6
|
+
metadata:
|
|
7
|
+
kete-loom/version: "0.1.0"
|
|
8
|
+
kete-loom/category: coding
|
|
9
|
+
kete-loom/risk: runs-commands
|
|
10
|
+
kete-loom/requires: "git"
|
|
11
|
+
kete-loom/owner: "Kete-org"
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
# Change impact analysis
|
|
15
|
+
|
|
16
|
+
Answer "what can this change break?" with evidence: a list of consumers found from the dependency graph, the tests that cover them, and the results of running those tests. Never answer from intuition alone.
|
|
17
|
+
|
|
18
|
+
## When to run it
|
|
19
|
+
|
|
20
|
+
- **Before** a change: predict impact so the plan and the review cover every consumer.
|
|
21
|
+
- **After** a diff exists: compute actual impact, run the affected tests, compare with the prediction.
|
|
22
|
+
- Both, for shared libraries, public APIs, database schemas, config keys and anything published.
|
|
23
|
+
|
|
24
|
+
## 1. Pin down the change
|
|
25
|
+
|
|
26
|
+
1. Get the changed files: `git diff --name-only <base>...HEAD` (base is usually `origin/main`; use the merge base, not the tip). Include untracked files from `git status --short`.
|
|
27
|
+
2. For each file, list the changed **symbols**: exported functions, classes, types, routes, events, CLI flags, env vars, config keys, DB columns, message schemas. A file-level graph over-reports; symbols tell you which consumers really care.
|
|
28
|
+
3. Classify each symbol change: internal only, signature change, behaviour change with the same signature, removal, or new addition. Additions rarely break consumers; behaviour changes are the ones people miss.
|
|
29
|
+
|
|
30
|
+
## 2. Build the consumer graph from the tooling the repo already has
|
|
31
|
+
|
|
32
|
+
Detect the build system first (look for `nx.json`, `turbo.json`, `WORKSPACE`/`MODULE.bazel`, `go.mod`, `pnpm-workspace.yaml`, `pom.xml`, `settings.gradle(.kts)`, `Cargo.toml` workspaces). Use the tool's own graph; it knows about generated code and path aliases that grep misses. Commands are in `references/graph-commands.md`.
|
|
33
|
+
|
|
34
|
+
- **Monorepo tool present**: ask it for affected projects between base and head (for example `nx show projects --affected -t test --base=origin/main`, `turbo run test --affected`, `bazel query "rdeps(//..., //pkg:target)"`).
|
|
35
|
+
- **No graph tool**: search the codebase for imports of the changed module and for each changed symbol name, then repeat one level up for every hit until no new consumers appear. Note this is a search result, not a graph.
|
|
36
|
+
- **Outside the code graph** (tools can't see these, search for them by name):
|
|
37
|
+
- string-keyed references: route paths, event or queue names, feature-flag keys, env vars, config keys, SQL column names, reflection and dependency-injection names;
|
|
38
|
+
- other repositories, deployed services, mobile apps on old versions, and third parties that call a public API;
|
|
39
|
+
- generated clients, fixtures, snapshots, docs and examples that embed the old shape;
|
|
40
|
+
- CI, deploy and infrastructure files that reference changed paths.
|
|
41
|
+
|
|
42
|
+
## 3. Rate the blast radius
|
|
43
|
+
|
|
44
|
+
For each consumer record: name, how it depends (import, HTTP, event, schema, config), what it uses, and risk:
|
|
45
|
+
|
|
46
|
+
| Risk | Meaning |
|
|
47
|
+
|---|---|
|
|
48
|
+
| High | Uses a removed or changed signature or behaviour; deployed separately; no tests cover the usage |
|
|
49
|
+
| Medium | Uses the changed symbol, tests exist and will run |
|
|
50
|
+
| Low | Depends on the package but not on the changed symbols |
|
|
51
|
+
|
|
52
|
+
**Stop and ask the user** when a High consumer is outside this repository, when the change touches a published package, public API, database schema or message format, or when the graph says more than about 20 projects are affected (confirm the change should be split or staged).
|
|
53
|
+
|
|
54
|
+
## 4. Run the affected tests
|
|
55
|
+
|
|
56
|
+
1. Run the tests of the changed modules first, then every downstream consumer. Use the tool's affected mode (`nx affected -t test`, `turbo run test --affected`, `bazel test` on the rdeps test set, `go test` on the reverse-dependency packages, `mvn -pl <module> -amd verify`, `./gradlew :<module>:buildDependents`).
|
|
57
|
+
2. Inside a single package, narrow with the runner's related-test mode where it exists (`jest --findRelatedTests <files>`, `vitest related <files> --run`, `pytest --testmon` if pytest-testmon is installed), and still run the full package suite before calling it safe.
|
|
58
|
+
3. Include type-checks and builds of consumers; a signature change often breaks compilation, not tests.
|
|
59
|
+
4. If a consumer has **no** test touching the changed symbol, list it as a coverage gap. Do not report it as safe.
|
|
60
|
+
5. Shallow CI clones make affected detection treat everything as changed or nothing as changed; fetch enough history for the base.
|
|
61
|
+
|
|
62
|
+
## 5. Reconcile planned vs actual
|
|
63
|
+
|
|
64
|
+
After the diff is final, rerun step 1 and step 2 and compare with the prediction:
|
|
65
|
+
|
|
66
|
+
- Files or symbols changed that the plan didn't mention: explain each or flag it as scope creep.
|
|
67
|
+
- Consumers predicted but not actually affected: fine, note them.
|
|
68
|
+
- Consumers affected but not predicted: these are the important ones; run their tests and add them to the review.
|
|
69
|
+
|
|
70
|
+
## Never
|
|
71
|
+
|
|
72
|
+
- Never declare a change safe because one package's tests pass.
|
|
73
|
+
- Never skip consumers outside the build graph because the tool didn't list them.
|
|
74
|
+
- Never edit consumers to make the analysis pass without telling the user; that's a new change.
|
|
75
|
+
|
|
76
|
+
## Report
|
|
77
|
+
|
|
78
|
+
```
|
|
79
|
+
## Impact: <change>
|
|
80
|
+
Base: <ref> Head: <ref>
|
|
81
|
+
Changed symbols: <symbol> (<kind of change>) ...
|
|
82
|
+
Consumers
|
|
83
|
+
- <name> | via <import/http/event/schema/config> | uses <symbol> | risk High/Med/Low | tests: <ran: pass/fail | none>
|
|
84
|
+
Outside the graph: <string-keyed refs, other repos, external callers, or "none found">
|
|
85
|
+
Commands run: <command> -> <result>
|
|
86
|
+
Coverage gaps: <consumer: what is untested>
|
|
87
|
+
Planned vs actual: <unplanned changes, unpredicted consumers, or "matches">
|
|
88
|
+
Verdict: safe to merge | merge after <action> | needs staged rollout or split
|
|
89
|
+
```
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"should_trigger": [
|
|
3
|
+
"I changed the signature of formatCurrency in packages/shared-utils. Which apps in the monorepo use it and could break?",
|
|
4
|
+
"What's the blast radius of renaming the user_status column? Find every consumer before I merge.",
|
|
5
|
+
"Run nx affected for my branch and tell me which downstream projects need testing",
|
|
6
|
+
"Before I bump the logging library interface, map every service and package that depends on it and run their tests",
|
|
7
|
+
"My diff ended up touching more than I planned. Compare what I expected to affect against what actually changed."
|
|
8
|
+
],
|
|
9
|
+
"should_not_trigger": [
|
|
10
|
+
"Break this feature request into ordered implementation steps before I start coding",
|
|
11
|
+
"Rename the parseDate helper across the codebase without changing behaviour",
|
|
12
|
+
"Run lint, type-check and the test suite before I open a pull request",
|
|
13
|
+
"Scan our npm dependencies for known vulnerabilities",
|
|
14
|
+
"Explain how this repository is structured, I just cloned it"
|
|
15
|
+
]
|
|
16
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# Dependency graph commands by build system
|
|
2
|
+
|
|
3
|
+
Use only the tool the repository already uses. Check flags against the installed version with `--help`.
|
|
4
|
+
|
|
5
|
+
| Tool | Affected or reverse-dependency query | Run affected tests |
|
|
6
|
+
|---|---|---|
|
|
7
|
+
| Nx | `nx show projects --affected -t test --base=origin/main --head=HEAD`; `nx graph --affected` to view | `nx affected -t test --base=origin/main --head=HEAD` (or set `NX_BASE` / `NX_HEAD`) |
|
|
8
|
+
| Turborepo | `turbo run test --affected --dry-run` lists tasks; `--filter=...[origin/main]` selects changed packages and their dependents | `turbo run test --affected` (base defaults to `main`; override with `TURBO_SCM_BASE`, head with `TURBO_SCM_HEAD`) |
|
|
9
|
+
| Bazel | `bazel query "rdeps(//..., //path/to:target)"`; tests only: `bazel query "kind('.*_test', rdeps(//..., //path/to:target))"` | `bazel test $(bazel query "kind('.*_test', rdeps(//..., //path/to:target))")` |
|
|
10
|
+
| Go modules | No built-in reverse lookup. `go list -test -f '{{.ImportPath}} {{join .Deps " "}}' ./... \| grep -E ' <changed/import/path>( \|$)' \| awk '{print $1}' \| grep -v '\.test$' \| sort -u` lists packages whose code or tests depend on it directly or transitively; add the changed package itself | `go test <listed packages>` plus `go vet` |
|
|
11
|
+
| pnpm workspaces | `pnpm --filter "...[origin/main]" list --depth -1` (changed packages and their dependents); `pnpm --filter "...<pkg>"` for one package | `pnpm --filter "...[origin/main]" test` |
|
|
12
|
+
| Maven | `mvn -pl <module> -amd validate` prints the dependents in the Reactor Build Order (don't pass `-q`, it hides that list) | `mvn -pl <module> -amd verify` |
|
|
13
|
+
| Gradle | No built-in reverse-dependency listing; `buildDependents` runs them | `./gradlew :<module>:buildDependents` (builds and tests the project and every project that depends on it) |
|
|
14
|
+
| Cargo workspace | `cargo tree -i <crate> --workspace` (inverted tree) | `cargo test -p <crate> -p <each dependent>` |
|
|
15
|
+
| Python (no monorepo tool) | Search imports: `git grep -nE "^(from\|import) <package>"`; `pipdeptree -r -p <dist>` for installed distributions | `pytest <dirs>`; `pytest --testmon` with pytest-testmon for test-level selection |
|
|
16
|
+
|
|
17
|
+
## Related-test modes inside one package
|
|
18
|
+
|
|
19
|
+
- Jest: `jest --findRelatedTests <changed files>`, `jest --changedSince=origin/main`
|
|
20
|
+
- Vitest: `vitest related <changed files> --run`, `vitest --changed origin/main`
|
|
21
|
+
- Go: `go test ./<pkg>/...` is already scoped; there is no file-level mode.
|
|
22
|
+
|
|
23
|
+
These follow static imports only. Dynamic imports, string-keyed lookups, DI containers and reflection need a codebase search.
|
|
24
|
+
|
|
25
|
+
## CI notes
|
|
26
|
+
|
|
27
|
+
- Fetch enough history to contain the merge base (`fetch-depth: 0`, or a blobless clone with `--filter=blob:none`); a too-shallow clone makes turbo treat every package as changed.
|
|
28
|
+
- Use the last green commit on the default branch as the base when the tool supports it (Nx recommends this).
|