spine-rigc 1.0.2 → 1.1.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 +33 -2
- package/cli.ts +274 -3
- package/docs/AUTHORING.md +22 -0
- package/package.json +1 -1
- package/skills/rigc/SKILL.md +16 -9
- package/skills/{face → rigc-face}/SKILL.md +11 -7
- package/skills/{ingest → rigc-ingest}/SKILL.md +10 -6
- package/skills/{motion → rigc-motion}/SKILL.md +11 -7
- package/skills/{rigging → rigc-rigging}/SKILL.md +11 -7
package/README.md
CHANGED
|
@@ -146,8 +146,9 @@ to do with it, and it is the one instrument here that can see a wrong animation.
|
|
|
146
146
|
|
|
147
147
|
The guides under [Documentation](#documentation) also ship as
|
|
148
148
|
[Agent Skills](https://agentskills.io) — `skills/<name>/SKILL.md`, in this
|
|
149
|
-
repository and in the npm package —
|
|
150
|
-
|
|
149
|
+
repository and in the npm package — which a host reads once they are where it
|
|
150
|
+
looks: Claude Code through the plugin below, Codex, Gemini CLI and Antigravity
|
|
151
|
+
through `rigc skills install`. Each skill is a router and nothing more: when to load it, the
|
|
151
152
|
non-negotiables in a line apiece, and a link to the guide that owns every rule, so
|
|
152
153
|
a rule keeps living in exactly one place. The repository is also a Claude Code
|
|
153
154
|
plugin marketplace:
|
|
@@ -162,6 +163,35 @@ loads the same skills without a marketplace. The plugin carries no version of it
|
|
|
162
163
|
own — `/plugin update` follows `main` commit by commit, and the only version on
|
|
163
164
|
disk stays the one in `package.json`.
|
|
164
165
|
|
|
166
|
+
Codex, Gemini CLI and Antigravity read skills from one directory in the workspace,
|
|
167
|
+
`.agents/skills/` ([Codex](https://learn.chatgpt.com/docs/build-skills),
|
|
168
|
+
[Gemini CLI](https://github.com/google-gemini/gemini-cli/blob/main/docs/cli/skills.md),
|
|
169
|
+
[Antigravity](https://antigravity.google/docs/skills/)), and none of them reads
|
|
170
|
+
`node_modules`. With the package installed, one command puts every skill there:
|
|
171
|
+
|
|
172
|
+
```shell
|
|
173
|
+
bun add -d spine-rigc
|
|
174
|
+
bun rigc skills install # relative links: .agents/skills/rigc -> ../../node_modules/spine-rigc/skills/rigc
|
|
175
|
+
bun rigc skills install --copy # the folders themselves, for a host that does not follow a link
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
Run it through the project's own install, as above: `bunx spine-rigc skills install`
|
|
179
|
+
in a project that has the package was measured running the registry's copy instead
|
|
180
|
+
of the project's. A link reaches every upgrade of the package with no second run,
|
|
181
|
+
and a second run has nothing to do; an entry already there that this command did not
|
|
182
|
+
make is refused by name and nothing is written. Gemini CLI 0.41.1 was measured
|
|
183
|
+
listing a linked skill from both its workspace and its user directory — the
|
|
184
|
+
workspace one only in a folder it trusts. Codex's documentation says it follows a
|
|
185
|
+
symlinked skill folder, which is not measured here, and Antigravity CLI 1.1.9 has no
|
|
186
|
+
way to list skills without starting a session, so what it does with a link is not
|
|
187
|
+
measured either; `--copy` is the shape that asks nothing of a host. Gemini CLI can
|
|
188
|
+
also fetch a skill itself, one folder at a time:
|
|
189
|
+
`gemini skills install https://github.com/firejune/rigc.git --path skills/rigc --scope workspace`.
|
|
190
|
+
The routers are named `rigc-rigging`, `rigc-motion`, `rigc-face` and `rigc-ingest`
|
|
191
|
+
because that directory is flat — beside another tool's `motion`, a bare name is
|
|
192
|
+
whichever one the host picked — and under the Claude Code plugin they read
|
|
193
|
+
`rigc:rigc-motion` and so on.
|
|
194
|
+
|
|
165
195
|
## First rig in ten minutes
|
|
166
196
|
|
|
167
197
|
A whole rig, end to end, in a scratch directory: three tiny plates, two JSON
|
|
@@ -511,6 +541,7 @@ commands take it and what its default is.
|
|
|
511
541
|
| `bonedist --candidate … --reference … --bones …` | per-frame, per-bone world-transform distance against another skeleton — the ladder's stage 3, run on its own. `--bones <correspondence.json \| identity>` is required rather than defaulted: a candidate is entitled to its own bone names, so the pairing is stated |
|
|
512
542
|
| `check --candidate <dir> --frames <dir>` | the candidate against reference pictures — the only instrument here that can see a *wrong animation* |
|
|
513
543
|
| `bench <rung> --candidate <dir>` | one rung of the benchmark ladder |
|
|
544
|
+
| `skills install [--dir …] [--copy]` | links every agent skill the package ships into `.agents/skills` (or `--dir`) as relative symlinks, or copies them with `--copy` — where Codex, Gemini CLI and Antigravity look. A second run has nothing to do; an entry already there that is not this command's is refused by name and nothing is written |
|
|
514
545
|
|
|
515
546
|
`diff`, `bonedist`, `check` and `bench` measure against something you were given; the
|
|
516
547
|
first three work on any reference you have, and `bench` is a repository workflow that needs a clone
|
package/cli.ts
CHANGED
|
@@ -31,8 +31,22 @@
|
|
|
31
31
|
* Its paths resolve against the cuts.json file itself, so the table travels
|
|
32
32
|
* with the project that owns the art rather than with this repository.
|
|
33
33
|
*/
|
|
34
|
-
import {
|
|
35
|
-
|
|
34
|
+
import {
|
|
35
|
+
appendFileSync,
|
|
36
|
+
cpSync,
|
|
37
|
+
existsSync,
|
|
38
|
+
lstatSync,
|
|
39
|
+
mkdirSync,
|
|
40
|
+
readdirSync,
|
|
41
|
+
readFileSync,
|
|
42
|
+
readlinkSync,
|
|
43
|
+
realpathSync,
|
|
44
|
+
rmSync,
|
|
45
|
+
statSync,
|
|
46
|
+
symlinkSync,
|
|
47
|
+
writeFileSync,
|
|
48
|
+
} from 'node:fs';
|
|
49
|
+
import { basename, dirname, join, relative, resolve } from 'node:path';
|
|
36
50
|
import {
|
|
37
51
|
BallotError,
|
|
38
52
|
buildBallot,
|
|
@@ -229,7 +243,7 @@ function repositoryUrl(): string {
|
|
|
229
243
|
* needs a value` (issue #328). `CLI10`/`CLI11` in `selftest.ts` now hold the two
|
|
230
244
|
* halves together by reading `--help` rather than by naming a flag.
|
|
231
245
|
*/
|
|
232
|
-
const BOOLEAN_FLAGS = new Set(['all-frames', 'all-bones', 'help', 'copy-images', 'again', 'pack']);
|
|
246
|
+
const BOOLEAN_FLAGS = new Set(['all-frames', 'all-bones', 'help', 'copy-images', 'again', 'pack', 'copy']);
|
|
233
247
|
|
|
234
248
|
/**
|
|
235
249
|
* The flags a command is allowed to spell more than once.
|
|
@@ -3270,6 +3284,223 @@ function cmdIngest(flags: Record<string, string>, positional: string[]): void {
|
|
|
3270
3284
|
}
|
|
3271
3285
|
}
|
|
3272
3286
|
|
|
3287
|
+
// ---------------------------------------------------------------------------
|
|
3288
|
+
// skills install — put the shipped skills where an agent host looks (issue #831)
|
|
3289
|
+
// ---------------------------------------------------------------------------
|
|
3290
|
+
//
|
|
3291
|
+
// After `bun add -d spine-rigc` the skills sit at `node_modules/spine-rigc/skills/`,
|
|
3292
|
+
// which no host reads. Codex, Gemini CLI and Antigravity all read
|
|
3293
|
+
// `<workspace>/.agents/skills/<name>/`, so this links every `skills/<name>/` the
|
|
3294
|
+
// package ships into one directory — `.agents/skills` under the working
|
|
3295
|
+
// directory unless `--dir` says otherwise.
|
|
3296
|
+
//
|
|
3297
|
+
// ⭐ The skills are found from THIS FILE's location, never from the working
|
|
3298
|
+
// directory: the command installs the package it is, and a cwd that happens to
|
|
3299
|
+
// hold some other `skills/` is not a source. That is also why it lives here and
|
|
3300
|
+
// not in `src/`: its one input is where the CLI was installed, nothing else
|
|
3301
|
+
// calls it, and `src/` is about rigs.
|
|
3302
|
+
//
|
|
3303
|
+
// A RELATIVE symlink by default, so the directory survives the project being
|
|
3304
|
+
// moved or cloned elsewhere and an upgrade of the package is seen with no second
|
|
3305
|
+
// run. `--copy` writes the folder instead, for a host that does not follow a
|
|
3306
|
+
// linked skill folder.
|
|
3307
|
+
//
|
|
3308
|
+
// 🔒 **An entry that is already there and is not what this command would write is
|
|
3309
|
+
// refused by name, and then nothing at all is written.** The check runs over
|
|
3310
|
+
// every skill before the first write, so a refusal never leaves half an install
|
|
3311
|
+
// behind. The one entry that is NOT refused is the one this command would have
|
|
3312
|
+
// made — a link that already resolves to the same skill folder, however it is
|
|
3313
|
+
// spelled, or with `--copy` a folder whose files are byte for byte the package's
|
|
3314
|
+
// — and a run over only those says it had nothing to do. No lifecycle script
|
|
3315
|
+
// does this on install: a postinstall writing into a consumer's project root is
|
|
3316
|
+
// refused as design, and Bun does not run a dependency's lifecycle scripts
|
|
3317
|
+
// outside `trustedDependencies`, so half the installs would silently skip it.
|
|
3318
|
+
// ---------------------------------------------------------------------------
|
|
3319
|
+
|
|
3320
|
+
/** The default `--dir`, resolved against the caller's working directory. */
|
|
3321
|
+
const DEFAULT_SKILLS_DIR = '.agents/skills';
|
|
3322
|
+
|
|
3323
|
+
/** What `rigc skills` offers. One today; the list is what the refusal of any other word prints. */
|
|
3324
|
+
const SKILLS_SUBCOMMANDS = ['install'];
|
|
3325
|
+
|
|
3326
|
+
/** An install refused before its first write — nothing to install, or an entry in the way. Exit 1. */
|
|
3327
|
+
class SkillsInstallError extends Error {}
|
|
3328
|
+
|
|
3329
|
+
type SkillsInstallAction = 'linked' | 'copied' | 'already linked' | 'already copied';
|
|
3330
|
+
|
|
3331
|
+
interface SkillsInstallEntry {
|
|
3332
|
+
/** `<dir>/<name>`. */
|
|
3333
|
+
target: string;
|
|
3334
|
+
/** `<package>/skills/<name>`. */
|
|
3335
|
+
source: string;
|
|
3336
|
+
action: SkillsInstallAction;
|
|
3337
|
+
/** The link text, relative to the directory it sits in, when the entry is a link. */
|
|
3338
|
+
link: string;
|
|
3339
|
+
}
|
|
3340
|
+
|
|
3341
|
+
/** Every `<name>/` under `source` that holds a `SKILL.md`, in name order, so two runs print the same lines. */
|
|
3342
|
+
function shippedSkills(source: string): string[] {
|
|
3343
|
+
if (!existsSync(source) || !statSync(source).isDirectory()) return [];
|
|
3344
|
+
return readdirSync(source)
|
|
3345
|
+
.filter((name) => statSync(join(source, name)).isDirectory() && existsSync(join(source, name, 'SKILL.md')))
|
|
3346
|
+
.sort();
|
|
3347
|
+
}
|
|
3348
|
+
|
|
3349
|
+
/**
|
|
3350
|
+
* The real path of `path` whether or not it exists yet: the real path of its
|
|
3351
|
+
* nearest existing ancestor with the rest appended. A relative link has to be
|
|
3352
|
+
* computed between two paths spelled the same way, and on macOS the temp
|
|
3353
|
+
* directory alone is reached as `/var/…` and is really `/private/var/…`.
|
|
3354
|
+
*/
|
|
3355
|
+
function realpathAhead(path: string): string {
|
|
3356
|
+
const rest: string[] = [];
|
|
3357
|
+
let at = resolve(path);
|
|
3358
|
+
while (!existsSync(at)) {
|
|
3359
|
+
const up = dirname(at);
|
|
3360
|
+
if (up === at) break;
|
|
3361
|
+
rest.unshift(basename(at));
|
|
3362
|
+
at = up;
|
|
3363
|
+
}
|
|
3364
|
+
return join(realpathSync(at), ...rest);
|
|
3365
|
+
}
|
|
3366
|
+
|
|
3367
|
+
/** Every file under `root`, relative and sorted, so two trees compare in one order. */
|
|
3368
|
+
function filesUnder(root: string, prefix = ''): string[] {
|
|
3369
|
+
const out: string[] = [];
|
|
3370
|
+
for (const name of readdirSync(join(root, prefix)).sort()) {
|
|
3371
|
+
const rel = prefix === '' ? name : `${prefix}/${name}`;
|
|
3372
|
+
if (lstatSync(join(root, rel)).isDirectory()) out.push(...filesUnder(root, rel));
|
|
3373
|
+
else out.push(rel);
|
|
3374
|
+
}
|
|
3375
|
+
return out;
|
|
3376
|
+
}
|
|
3377
|
+
|
|
3378
|
+
/** The first way `copy` differs from `original`, or null when every file is the same bytes. */
|
|
3379
|
+
function firstDifference(copy: string, original: string): string | null {
|
|
3380
|
+
const theirs = filesUnder(copy);
|
|
3381
|
+
const ours = filesUnder(original);
|
|
3382
|
+
for (const rel of ours) {
|
|
3383
|
+
if (!theirs.includes(rel)) return `${rel} is missing from it`;
|
|
3384
|
+
if (!readFileSync(join(copy, rel)).equals(readFileSync(join(original, rel)))) return `${rel} differs`;
|
|
3385
|
+
}
|
|
3386
|
+
for (const rel of theirs) if (!ours.includes(rel)) return `${rel} is in it and not in the package`;
|
|
3387
|
+
return null;
|
|
3388
|
+
}
|
|
3389
|
+
|
|
3390
|
+
/**
|
|
3391
|
+
* Install every shipped skill into `dir`, or refuse and write nothing.
|
|
3392
|
+
*
|
|
3393
|
+
* The plan is made in full before the first write: every entry is classified as
|
|
3394
|
+
* absent, already this command's, or in the way, and one entry in the way
|
|
3395
|
+
* refuses the whole call with every such entry named.
|
|
3396
|
+
*/
|
|
3397
|
+
function installSkills(source: string, dir: string, copy: boolean): SkillsInstallEntry[] {
|
|
3398
|
+
const names = shippedSkills(source);
|
|
3399
|
+
if (names.length === 0) {
|
|
3400
|
+
throw new SkillsInstallError(
|
|
3401
|
+
`no skill to install: ${source} ${existsSync(source) ? 'holds no <name>/SKILL.md' : 'is not there'}, and it is ` +
|
|
3402
|
+
'the skills/ directory of the package this command ran from; nothing was written',
|
|
3403
|
+
);
|
|
3404
|
+
}
|
|
3405
|
+
if (existsSync(dir) && !statSync(dir).isDirectory()) {
|
|
3406
|
+
throw new SkillsInstallError(`${dir} exists and is not a directory, so no skill can be installed into it; nothing was written`);
|
|
3407
|
+
}
|
|
3408
|
+
const realDir = realpathAhead(dir);
|
|
3409
|
+
const planned: SkillsInstallEntry[] = [];
|
|
3410
|
+
const refused: string[] = [];
|
|
3411
|
+
for (const name of names) {
|
|
3412
|
+
const target = join(dir, name);
|
|
3413
|
+
const from = join(source, name);
|
|
3414
|
+
const realFrom = realpathSync(from);
|
|
3415
|
+
const link = relative(realDir, realFrom);
|
|
3416
|
+
let action: SkillsInstallAction = copy ? 'copied' : 'linked';
|
|
3417
|
+
let found: string | null = null;
|
|
3418
|
+
const stat = existsSync(target) || isLink(target) ? lstatSync(target) : null;
|
|
3419
|
+
if (stat === null) {
|
|
3420
|
+
// absent: this command writes it
|
|
3421
|
+
} else if (stat.isSymbolicLink()) {
|
|
3422
|
+
const text = readlinkSync(target);
|
|
3423
|
+
const pointsAt = resolve(realDir, text);
|
|
3424
|
+
const lands = existsSync(pointsAt) ? realpathSync(pointsAt) : null;
|
|
3425
|
+
if (lands === realFrom && !copy) action = 'already linked';
|
|
3426
|
+
else if (lands === realFrom) found = `a symlink to ${text}, the package's own folder, and --copy asks for a directory in its place`;
|
|
3427
|
+
else found = `a symlink to ${text}, ${lands === null ? 'which resolves to nothing' : `which resolves to ${lands}`}`;
|
|
3428
|
+
} else if (stat.isDirectory()) {
|
|
3429
|
+
const difference = copy ? firstDifference(target, from) : null;
|
|
3430
|
+
if (!copy) found = 'a directory';
|
|
3431
|
+
else if (difference === null) action = 'already copied';
|
|
3432
|
+
else found = `a directory that is not the package's copy (${difference})`;
|
|
3433
|
+
} else {
|
|
3434
|
+
found = 'a plain file';
|
|
3435
|
+
}
|
|
3436
|
+
if (found !== null) refused.push(`${target} is ${found}; ${copy ? `a copy of ${from}` : `a symlink to ${link}`} was required`);
|
|
3437
|
+
else planned.push({ target, source: from, action, link });
|
|
3438
|
+
}
|
|
3439
|
+
if (refused.length > 0) {
|
|
3440
|
+
throw new SkillsInstallError(
|
|
3441
|
+
`${refused.length} of the ${names.length} skill(s) cannot be installed into ${dir}, and nothing was written:\n` +
|
|
3442
|
+
refused.map((line) => ` ${line}`).join('\n') +
|
|
3443
|
+
'\nRemove the entries named above, or pass --dir to install somewhere else.',
|
|
3444
|
+
);
|
|
3445
|
+
}
|
|
3446
|
+
mkdirSync(dir, { recursive: true });
|
|
3447
|
+
for (const entry of planned) {
|
|
3448
|
+
if (entry.action === 'linked') symlinkSync(entry.link, entry.target, 'dir');
|
|
3449
|
+
else if (entry.action === 'copied') cpSync(entry.source, entry.target, { recursive: true, errorOnExist: true, force: false });
|
|
3450
|
+
}
|
|
3451
|
+
return planned;
|
|
3452
|
+
}
|
|
3453
|
+
|
|
3454
|
+
/** A dangling link is not `existsSync`, and is still an entry in the way. */
|
|
3455
|
+
function isLink(path: string): boolean {
|
|
3456
|
+
try {
|
|
3457
|
+
return lstatSync(path).isSymbolicLink();
|
|
3458
|
+
} catch {
|
|
3459
|
+
return false;
|
|
3460
|
+
}
|
|
3461
|
+
}
|
|
3462
|
+
|
|
3463
|
+
function cmdSkills(flags: Record<string, string>, positional: string[]): void {
|
|
3464
|
+
const [sub, ...extra] = positional;
|
|
3465
|
+
if (sub === undefined) {
|
|
3466
|
+
throw new UsageError(
|
|
3467
|
+
`skills takes a subcommand: ${SKILLS_SUBCOMMANDS.join(', ')} — \`rigc skills install\` links every skill this ` +
|
|
3468
|
+
`package ships into ${DEFAULT_SKILLS_DIR}`,
|
|
3469
|
+
);
|
|
3470
|
+
}
|
|
3471
|
+
if (!SKILLS_SUBCOMMANDS.includes(sub)) {
|
|
3472
|
+
throw new UsageError(`unknown skills subcommand: ${sub} (rigc skills offers ${SKILLS_SUBCOMMANDS.join(', ')})`);
|
|
3473
|
+
}
|
|
3474
|
+
if (extra.length > 0) {
|
|
3475
|
+
throw new UsageError(
|
|
3476
|
+
`skills install takes no positional argument, and ${JSON.stringify(extra[0])} was given — the directory is --dir <path>`,
|
|
3477
|
+
);
|
|
3478
|
+
}
|
|
3479
|
+
const takes = COMMANDS.find((c) => c.name === 'skills')?.flags ?? [];
|
|
3480
|
+
const foreign = Object.keys(flags).filter((flag) => !takes.includes(flag));
|
|
3481
|
+
if (foreign.length > 0) {
|
|
3482
|
+
throw new UsageError(
|
|
3483
|
+
`skills install takes ${takes.map((flag) => `--${flag}`).join(' and ')}; ` +
|
|
3484
|
+
`${foreign.map((flag) => `--${flag}`).join(', ')} is not one of them`,
|
|
3485
|
+
);
|
|
3486
|
+
}
|
|
3487
|
+
const copy = flags.copy !== undefined;
|
|
3488
|
+
const dir = resolve(process.cwd(), flags.dir ?? DEFAULT_SKILLS_DIR);
|
|
3489
|
+
const entries = installSkills(join(import.meta.dir, 'skills'), dir, copy);
|
|
3490
|
+
for (const entry of entries) {
|
|
3491
|
+
const ends = entry.action.endsWith('linked') ? `${entry.target} -> ${entry.link} (${entry.source})` : `${entry.target} <- ${entry.source}`;
|
|
3492
|
+
console.log(` ${entry.action.padEnd(14)} ${ends}`);
|
|
3493
|
+
}
|
|
3494
|
+
const wrote = entries.filter((entry) => entry.action === 'linked' || entry.action === 'copied').length;
|
|
3495
|
+
const verb = copy ? 'copied' : 'linked';
|
|
3496
|
+
console.log(
|
|
3497
|
+
wrote === 0
|
|
3498
|
+
? `rigc skills install: nothing to do — all ${entries.length} skill(s) are already ${verb} into ${dir}`
|
|
3499
|
+
: `rigc skills install: ${wrote} of ${entries.length} skill(s) ${verb} into ${dir}` +
|
|
3500
|
+
(wrote < entries.length ? `, ${entries.length - wrote} already there` : ''),
|
|
3501
|
+
);
|
|
3502
|
+
}
|
|
3503
|
+
|
|
3273
3504
|
// ---------------------------------------------------------------------------
|
|
3274
3505
|
// usage / per-command help
|
|
3275
3506
|
// ---------------------------------------------------------------------------
|
|
@@ -3368,6 +3599,14 @@ const FLAG_MEANINGS: Record<string, string> = {
|
|
|
3368
3599
|
ballot: `the ballot the --record'd vote answers (default \`${DEFAULT_BALLOT}\`); its embedded manifest is what the vote is checked against`,
|
|
3369
3600
|
ledger: `the append-only JSONL the vote lands in (default \`${DEFAULT_LEDGER}\`)`,
|
|
3370
3601
|
again: 'record a second vote on a ballot the ledger already has; without it, a repeat is refused rather than doubled',
|
|
3602
|
+
dir:
|
|
3603
|
+
`the directory to install into, resolved against your working directory (default \`${DEFAULT_SKILLS_DIR}\`, the ` +
|
|
3604
|
+
'workspace directory Codex, Gemini CLI and Antigravity read skills from)',
|
|
3605
|
+
copy:
|
|
3606
|
+
'copy each skill folder instead of linking it, for a host that does not follow a linked skill folder. A copy ' +
|
|
3607
|
+
'is not reached by an upgrade of the package, and one that is no longer the package\'s bytes is refused by name ' +
|
|
3608
|
+
'on the next run — remove it and run again (default: a relative symlink, which an upgrade reaches with no ' +
|
|
3609
|
+
'second run)',
|
|
3371
3610
|
name: "the rig spec's own name, which the motion spec's archetype must match (default: the skeleton file's basename)",
|
|
3372
3611
|
art: 'how the written spec reaches the art, which a skeleton does not encode: `loose` names an image per ' +
|
|
3373
3612
|
"attachment, measured out of the rig spec's own images directory (--images writes it; without it, `build " +
|
|
@@ -3430,6 +3669,7 @@ const FLAG_VALUES: Record<string, string> = {
|
|
|
3430
3669
|
name: '<n>',
|
|
3431
3670
|
art: 'loose|none',
|
|
3432
3671
|
stage: '<x,y,w,h>',
|
|
3672
|
+
dir: '<path>',
|
|
3433
3673
|
};
|
|
3434
3674
|
|
|
3435
3675
|
interface CommandDoc {
|
|
@@ -3723,6 +3963,21 @@ const COMMANDS: CommandDoc[] = [
|
|
|
3723
3963
|
},
|
|
3724
3964
|
},
|
|
3725
3965
|
},
|
|
3966
|
+
{
|
|
3967
|
+
name: 'skills',
|
|
3968
|
+
usage: [`rigc skills install [--dir ${DEFAULT_SKILLS_DIR}] [--copy] (every skill this package ships, where an agent host looks)`],
|
|
3969
|
+
flags: ['dir', 'copy'],
|
|
3970
|
+
notes: [
|
|
3971
|
+
'the skills installed are the skills/ directory of the package this command runs from,',
|
|
3972
|
+
'never whatever the working directory holds. Each becomes <dir>/<name>: a relative',
|
|
3973
|
+
'symlink into that folder, or with --copy a copy of it. An entry already there that',
|
|
3974
|
+
'is not a link to the same folder — or, with --copy, not the same bytes — is refused',
|
|
3975
|
+
'by name, exit 1, and nothing is written; a run over only what this command made has',
|
|
3976
|
+
'nothing to do and exits 0. Codex, Gemini CLI and Antigravity read',
|
|
3977
|
+
`<workspace>/${DEFAULT_SKILLS_DIR}; Claude Code installs the plugin instead (README,`,
|
|
3978
|
+
'"Install it into your agent").',
|
|
3979
|
+
],
|
|
3980
|
+
},
|
|
3726
3981
|
];
|
|
3727
3982
|
|
|
3728
3983
|
const KNOWN_COMMANDS = COMMANDS.map((c) => c.name);
|
|
@@ -3829,6 +4084,13 @@ const USAGE = [
|
|
|
3829
4084
|
'answer rather than a missing one, and a result whose hashes are not the ballot\'s is',
|
|
3830
4085
|
'refused by name instead of appended.',
|
|
3831
4086
|
'',
|
|
4087
|
+
'skills install puts the agent skills this package ships where an agent host looks',
|
|
4088
|
+
'for them, since none of them reads node_modules:',
|
|
4089
|
+
` rigc skills install relative links in ${DEFAULT_SKILLS_DIR}, which Codex, Gemini CLI`,
|
|
4090
|
+
' and Antigravity read; --copy writes the folders instead',
|
|
4091
|
+
'An entry already there that this command did not make is refused by name and',
|
|
4092
|
+
'nothing is written; a second run has nothing to do. See `rigc skills --help`.',
|
|
4093
|
+
'',
|
|
3832
4094
|
'a cuts.json is { "<name>": { "rig": "...", "motion": "...", "out": "...",',
|
|
3833
4095
|
' "manifest": "..." (optional) } }, with every path',
|
|
3834
4096
|
'resolved relative to the cuts.json file itself.',
|
|
@@ -3870,6 +4132,7 @@ try {
|
|
|
3870
4132
|
else if (command === 'pose') cmdPose(flags);
|
|
3871
4133
|
else if (command === 'chainfit') cmdChainFit(flags);
|
|
3872
4134
|
else if (command === 'vote') cmdVote(flags, lists.candidate ?? []);
|
|
4135
|
+
else if (command === 'skills') cmdSkills(flags, positional);
|
|
3873
4136
|
} catch (err) {
|
|
3874
4137
|
if (err instanceof UsageError) {
|
|
3875
4138
|
console.error(`rigc: ${err.message}\n\n${USAGE}`);
|
|
@@ -3940,5 +4203,13 @@ try {
|
|
|
3940
4203
|
console.error(`rigc: ${err.message}`);
|
|
3941
4204
|
process.exit(1);
|
|
3942
4205
|
}
|
|
4206
|
+
// An install refused before its first write (issue #831): the invocation was
|
|
4207
|
+
// fine and an entry on disk was not what this command would write, so exit 1
|
|
4208
|
+
// like a file that is not a PNG. The message names every such entry, what is
|
|
4209
|
+
// there and what was required; the usage under it would bury that.
|
|
4210
|
+
if (err instanceof SkillsInstallError) {
|
|
4211
|
+
console.error(`rigc skills install: ${err.message}`);
|
|
4212
|
+
process.exit(1);
|
|
4213
|
+
}
|
|
3943
4214
|
throw err;
|
|
3944
4215
|
}
|
package/docs/AUTHORING.md
CHANGED
|
@@ -186,6 +186,8 @@ What the flags mean:
|
|
|
186
186
|
| `--min-visible` | `chainfit` only: below this share of a part surviving the parts drawn over it, the placement is refused `occluded` instead of reported flat (default `0.25`) — §12.4 |
|
|
187
187
|
| `--passes` | `chainfit` only: how many times the occluder masks are rebuilt from the answers and the fit rerun (default `2`) — §12.4 |
|
|
188
188
|
| `--anchor-residual` | `chainfit` only: the residual a `pose` placement must be within to anchor a chain (default `0.16`) — §12.2 |
|
|
189
|
+
| `--dir` | `skills install` only: the directory to install the agent skills into, resolved against your working directory (default `.agents/skills`, which Codex, Gemini CLI and Antigravity read). The skills are the `skills/` directory of the package the command runs from, never the working directory's |
|
|
190
|
+
| `--copy` | `skills install` only: copy each skill folder instead of writing a relative symlink to it. A copy is not reached by an upgrade of the package; one that is no longer the package's bytes is refused on the next run, naming the first file that differs |
|
|
189
191
|
|
|
190
192
|
`render` also takes `--fps <n>` (the rate it samples at, default 12 — the same
|
|
191
193
|
protocol rate the reference frames use) and `--max <px>` (the long side of a
|
|
@@ -193,6 +195,26 @@ frame, default 256). Three commands take `--out`: a directory for `render`
|
|
|
193
195
|
(default `render/`), the `.html` file for `preview` (default `preview.html`) and
|
|
194
196
|
for `vote` (default `ballot.html`).
|
|
195
197
|
|
|
198
|
+
`skills install` is the one command that is not about a rig: it puts the agent
|
|
199
|
+
skills the package ships where an agent host looks for them. Run it through the
|
|
200
|
+
project's own install — `bun rigc skills install` — so the links point into that
|
|
201
|
+
project's `node_modules/spine-rigc/skills/`. A second run has nothing to do and
|
|
202
|
+
exits 0. An entry already at `<dir>/<name>` that is not a link to the same folder
|
|
203
|
+
(or, under `--copy`, not the same bytes) is refused, exit 1, and **nothing is
|
|
204
|
+
written** — every such entry is named with what is there and what was required:
|
|
205
|
+
|
|
206
|
+
```text
|
|
207
|
+
rigc skills install: <n> of the <m> skill(s) cannot be installed into <dir>, and nothing was written:
|
|
208
|
+
<dir>/rigc-motion is a plain file; a symlink to ../../node_modules/spine-rigc/skills/rigc-motion was required
|
|
209
|
+
Remove the entries named above, or pass --dir to install somewhere else.
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
What is found is one of `a plain file`, `a directory`, `a symlink to <text>, which
|
|
213
|
+
resolves to <path>` (or `to nothing`), and under `--copy` `a directory that is not
|
|
214
|
+
the package's copy (<file> differs)`. A link to the package's own folder is not in
|
|
215
|
+
the way however it is spelled, so an absolute link somebody else made is kept and
|
|
216
|
+
reported `already linked`.
|
|
217
|
+
|
|
196
218
|
Pick the profile deliberately, and know which one you got by saying nothing. The
|
|
197
219
|
default is `spine`: "is this valid Spine 4.3 that any runtime plays correctly",
|
|
198
220
|
which is the question a rig authored anywhere is asking. `--profile spine-html`
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "spine-rigc",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "Rig compiler for Spine — declarative rig specs in, Spine 4.3 skeleton data out, verified by a spine-core round-trip. Built so AI agents can author rigs and check their own work; the output imports into the Spine editor.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
package/skills/rigc/SKILL.md
CHANGED
|
@@ -34,8 +34,13 @@ whole interface, and this skill only says which of them to open.
|
|
|
34
34
|
```shell
|
|
35
35
|
bunx spine-rigc --help # run it without installing
|
|
36
36
|
bun add -d spine-rigc # or pin it in the project; the command is `rigc`
|
|
37
|
+
bun rigc skills install # then link these skills into .agents/skills
|
|
37
38
|
```
|
|
38
39
|
|
|
40
|
+
Codex, Gemini CLI and Antigravity read skills from `.agents/skills/` in the
|
|
41
|
+
workspace and none of them reads `node_modules`; `rigc skills install` puts every
|
|
42
|
+
skill the package ships there, and `rigc skills --help` says what it refuses.
|
|
43
|
+
|
|
39
44
|
## The loop
|
|
40
45
|
|
|
41
46
|
1. `rigc build --rig <spec> --motion <spec> --images <dir> --out <dir>` compiles,
|
|
@@ -61,17 +66,19 @@ AUTHORING §0.
|
|
|
61
66
|
|
|
62
67
|
## Which guide, for which need
|
|
63
68
|
|
|
64
|
-
Read [AUTHORING.md](
|
|
69
|
+
Read [AUTHORING.md](https://github.com/firejune/rigc/blob/main/docs/AUTHORING.md) first, whatever the need: the two
|
|
65
70
|
spec files field by field, the emission rules, the loop, and the failure map. Then:
|
|
66
71
|
|
|
67
72
|
| The request is… | Open | Skill |
|
|
68
73
|
| --- | --- | --- |
|
|
69
|
-
| a **skeleton** — how many bones, where each pivot sits, what hangs off what | [RIGGING.md](
|
|
70
|
-
| a **movement** — an idle, a loop, from this pose to that one | [MOTION.md](
|
|
71
|
-
| a **face** — a blink, a gaze, a head turn a few degrees off axis | [FACE.md](
|
|
72
|
-
| a **skeleton.json somebody else authored** — read it, repair it, extend it | [INGEST.md](
|
|
73
|
-
| you are the **person operating** the agent rather than the agent | [PROMPTING.md](
|
|
74
|
+
| a **skeleton** — how many bones, where each pivot sits, what hangs off what | [RIGGING.md](https://github.com/firejune/rigc/blob/main/docs/RIGGING.md) | `rigc-rigging` |
|
|
75
|
+
| a **movement** — an idle, a loop, from this pose to that one | [MOTION.md](https://github.com/firejune/rigc/blob/main/docs/MOTION.md) | `rigc-motion` |
|
|
76
|
+
| a **face** — a blink, a gaze, a head turn a few degrees off axis | [FACE.md](https://github.com/firejune/rigc/blob/main/docs/FACE.md) | `rigc-face` |
|
|
77
|
+
| a **skeleton.json somebody else authored** — read it, repair it, extend it | [INGEST.md](https://github.com/firejune/rigc/blob/main/docs/INGEST.md) | `rigc-ingest` |
|
|
78
|
+
| you are the **person operating** the agent rather than the agent | [PROMPTING.md](https://github.com/firejune/rigc/blob/main/docs/PROMPTING.md) | — |
|
|
74
79
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
80
|
+
Every guide linked here is in the installed package at `node_modules/spine-rigc/docs/`,
|
|
81
|
+
which is the copy that matches the rigc you run; the links go to the repository's
|
|
82
|
+
`main`. Inside the Claude Code plugin the same files are at `${CLAUDE_PLUGIN_ROOT}/docs/`.
|
|
83
|
+
Formats, the CLI reference and the licence chain:
|
|
84
|
+
[README.md](https://github.com/firejune/rigc/blob/main/README.md), installed at `node_modules/spine-rigc/README.md`.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
name: face
|
|
2
|
+
name: rigc-face
|
|
3
3
|
description: Author a face on plain Spine data with rigc — a blink, a gaze shift, a breathing portrait and a head turn a few degrees off axis, built from deform timelines and per-part parallax. Use when the request is a talking or living portrait, a standing character, an expression or a head turn, such as "rig this face", "make the portrait blink and look around" or "turn the head". Not for Live2D file conversion, cutting a face illustration into parts, or VTuber-style real-time face tracking.
|
|
4
4
|
license: MIT
|
|
5
5
|
compatibility: Requires Bun 1.2 or later and the npm package spine-rigc.
|
|
@@ -9,7 +9,7 @@ compatibility: Requires Bun 1.2 or later and the npm package spine-rigc.
|
|
|
9
9
|
|
|
10
10
|
Load this when the request is a **head rather than a body**: a drawn face that
|
|
11
11
|
breathes, blinks, moves its eyes and turns a few degrees off axis. Every rule below
|
|
12
|
-
is owned by [FACE.md](
|
|
12
|
+
is owned by [FACE.md](https://github.com/firejune/rigc/blob/main/docs/FACE.md); this file says when to open it and what
|
|
13
13
|
it will not do for you.
|
|
14
14
|
|
|
15
15
|
## Non-negotiables
|
|
@@ -40,17 +40,21 @@ differential audit and the three limits it does not lift.
|
|
|
40
40
|
|
|
41
41
|
## Read, in this order
|
|
42
42
|
|
|
43
|
-
1. [AUTHORING.md](
|
|
43
|
+
1. [AUTHORING.md](https://github.com/firejune/rigc/blob/main/docs/AUTHORING.md) — the `deform` timeline field by field
|
|
44
44
|
and what rigc refuses in it (§4), the failure map (§5–§6), the editor's
|
|
45
45
|
conventions (§10).
|
|
46
|
-
2. [MOTION.md](
|
|
46
|
+
2. [MOTION.md](https://github.com/firejune/rigc/blob/main/docs/MOTION.md) — timing, easing and the offset table (§3),
|
|
47
47
|
candidates and the ballot (§4–§5). A blink and a gaze are ordinary motion work.
|
|
48
|
-
3. [FACE.md](
|
|
48
|
+
3. [FACE.md](https://github.com/firejune/rigc/blob/main/docs/FACE.md) — the face's own geometry: the closed form every
|
|
49
49
|
number in a turn comes from (§1), the hierarchy underneath (§3), what
|
|
50
50
|
foreshortens (§5), and the deform audit gap (§9).
|
|
51
|
-
4. Then [RIGGING.md](
|
|
52
|
-
rather than this closed form, and [INGEST.md](
|
|
51
|
+
4. Then [RIGGING.md](https://github.com/firejune/rigc/blob/main/docs/RIGGING.md) for the hierarchy as a general rule
|
|
52
|
+
rather than this closed form, and [INGEST.md](https://github.com/firejune/rigc/blob/main/docs/INGEST.md) if the head
|
|
53
53
|
arrived as a compiled skeleton.
|
|
54
54
|
|
|
55
|
+
Every guide linked here is in the installed package at `node_modules/spine-rigc/docs/`,
|
|
56
|
+
which is the copy that matches the rigc you run; the links go to the repository's
|
|
57
|
+
`main`. Inside the Claude Code plugin the same files are at `${CLAUDE_PLUGIN_ROOT}/docs/`.
|
|
58
|
+
|
|
55
59
|
The install line and the build → validate → render → check loop are in the `rigc`
|
|
56
60
|
skill.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
name: ingest
|
|
2
|
+
name: rigc-ingest
|
|
3
3
|
description: Work with a Spine skeleton.json somebody else authored — exported from the Spine editor or another tool — using rigc. Read and validate it, understand a complaint rigc raised about it, decompile it into rigc specs with `rigc ingest`, normalise, re-pivot or rename it, and extend it with an animation it does not have. Use when the input is an existing skeleton.json with its .atlas and page images rather than loose part PNGs. Not for Live2D file conversion or runtime tracking.
|
|
4
4
|
license: MIT
|
|
5
5
|
compatibility: Requires Bun 1.2 or later and the npm package spine-rigc.
|
|
@@ -10,7 +10,7 @@ compatibility: Requires Bun 1.2 or later and the npm package spine-rigc.
|
|
|
10
10
|
Load this when what you were handed is **already a skeleton**: a `skeleton.json`
|
|
11
11
|
with its `.atlas` and page images, and a request to understand it, answer a
|
|
12
12
|
complaint about it, re-express it, or extend it. Every rule below is owned by
|
|
13
|
-
[INGEST.md](
|
|
13
|
+
[INGEST.md](https://github.com/firejune/rigc/blob/main/docs/INGEST.md); this file says when to open it and what it will
|
|
14
14
|
not do for you.
|
|
15
15
|
|
|
16
16
|
## Non-negotiables
|
|
@@ -61,14 +61,18 @@ thing to reach for; transcription by hand is what you fall back on for a constru
|
|
|
61
61
|
|
|
62
62
|
## Read, in this order
|
|
63
63
|
|
|
64
|
-
1. [INGEST.md](
|
|
64
|
+
1. [INGEST.md](https://github.com/firejune/rigc/blob/main/docs/INGEST.md) — what every command will and will not do with
|
|
65
65
|
a foreign file (§0), `ingest` and transcription (§2), what each validator
|
|
66
66
|
complaint means on an export (§3), and the re-pivot, rename and extend
|
|
67
67
|
recipes (§4).
|
|
68
|
-
2. [AUTHORING.md](
|
|
68
|
+
2. [AUTHORING.md](https://github.com/firejune/rigc/blob/main/docs/AUTHORING.md) — the two spec files the transcription
|
|
69
69
|
targets (§3–§4), the failure map (§5–§6), and the coordinate contract (§11.2).
|
|
70
|
-
3. Then [RIGGING.md](
|
|
71
|
-
it has, and [MOTION.md](
|
|
70
|
+
3. Then [RIGGING.md](https://github.com/firejune/rigc/blob/main/docs/RIGGING.md) for why the re-pivot edit has the shape
|
|
71
|
+
it has, and [MOTION.md](https://github.com/firejune/rigc/blob/main/docs/MOTION.md) for the animation you are adding.
|
|
72
|
+
|
|
73
|
+
Every guide linked here is in the installed package at `node_modules/spine-rigc/docs/`,
|
|
74
|
+
which is the copy that matches the rigc you run; the links go to the repository's
|
|
75
|
+
`main`. Inside the Claude Code plugin the same files are at `${CLAUDE_PLUGIN_ROOT}/docs/`.
|
|
72
76
|
|
|
73
77
|
The install line and the build → validate → render → check loop are in the `rigc`
|
|
74
78
|
skill.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
name: motion
|
|
2
|
+
name: rigc-motion
|
|
3
3
|
description: Author a Spine animation with rigc from key poses — an idle, a loop, a move from one picture to another — with timing and spacing, ease in and out, anticipation, arcs, overlap, follow-through, squash and stretch, and candidate variants a person can choose between. Use when the request is a movement on an existing or planned rig, in the animator's words too: "animate this rig", "make it breathe", "go from pose A to pose B", "make it feel heavier", "the cape should follow through". Not for Live2D, separating an image into parts, or real-time tracking.
|
|
4
4
|
license: MIT
|
|
5
5
|
compatibility: Requires Bun 1.2 or later and the npm package spine-rigc.
|
|
@@ -10,7 +10,7 @@ compatibility: Requires Bun 1.2 or later and the npm package spine-rigc.
|
|
|
10
10
|
Load this when the request is a **movement rather than a skeleton**: a sentence of
|
|
11
11
|
intent, between zero and N pictures of what the movement passes through, and a
|
|
12
12
|
Spine animation somebody would choose coming back. Every rule below is owned by
|
|
13
|
-
[MOTION.md](
|
|
13
|
+
[MOTION.md](https://github.com/firejune/rigc/blob/main/docs/MOTION.md); this file says when to open it and what it will
|
|
14
14
|
not do for you.
|
|
15
15
|
|
|
16
16
|
## Non-negotiables
|
|
@@ -31,17 +31,21 @@ thing that judges a movement is a person's eye through `rigc vote` — MOTION §
|
|
|
31
31
|
|
|
32
32
|
## Read, in this order
|
|
33
33
|
|
|
34
|
-
1. [AUTHORING.md](
|
|
34
|
+
1. [AUTHORING.md](https://github.com/firejune/rigc/blob/main/docs/AUTHORING.md) — the motion spec field by field (§4),
|
|
35
35
|
the failure map (§5–§6), reading reference frames (§8) and checking against
|
|
36
36
|
them (§9), what the editor does when nobody tells it otherwise (§10), and
|
|
37
37
|
reading a pose out of a picture (§11–§12).
|
|
38
|
-
2. [MOTION.md](
|
|
38
|
+
2. [MOTION.md](https://github.com/firejune/rigc/blob/main/docs/MOTION.md) — the normal form every motion request
|
|
39
39
|
reduces to (§0), timing, easing and the per-bone offset table (§3), and how to
|
|
40
40
|
spread candidates so a ballot informs (§4–§5).
|
|
41
|
-
3. Then [RIGGING.md](
|
|
42
|
-
to decide; [FACE.md](
|
|
43
|
-
head turn; [INGEST.md](
|
|
41
|
+
3. Then [RIGGING.md](https://github.com/firejune/rigc/blob/main/docs/RIGGING.md) if the skeleton itself is what you have
|
|
42
|
+
to decide; [FACE.md](https://github.com/firejune/rigc/blob/main/docs/FACE.md) if the movement is a blink, a gaze or a
|
|
43
|
+
head turn; [INGEST.md](https://github.com/firejune/rigc/blob/main/docs/INGEST.md) if the rig arrived as a compiled
|
|
44
44
|
skeleton rather than loose parts.
|
|
45
45
|
|
|
46
|
+
Every guide linked here is in the installed package at `node_modules/spine-rigc/docs/`,
|
|
47
|
+
which is the copy that matches the rigc you run; the links go to the repository's
|
|
48
|
+
`main`. Inside the Claude Code plugin the same files are at `${CLAUDE_PLUGIN_ROOT}/docs/`.
|
|
49
|
+
|
|
46
50
|
The install line and the build → validate → render → check loop are in the `rigc`
|
|
47
51
|
skill.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
name: rigging
|
|
2
|
+
name: rigc-rigging
|
|
3
3
|
description: Decide a Spine rig's hierarchy with rigc — how many bones, where each pivot sits, what hangs off what, offsets, chains and what a chain can reach, siblings versus chains, constraints as structure — and which of those decisions the reference frames can check. Use when the request is a skeleton from loose part PNGs, such as "rig these parts", "make a Spine skeleton" or "where do the joints go", before any motion is authored. Not for Live2D, cutting an illustration into parts, or VTuber-style tracking.
|
|
4
4
|
license: MIT
|
|
5
5
|
compatibility: Requires Bun 1.2 or later and the npm package spine-rigc.
|
|
@@ -9,7 +9,7 @@ compatibility: Requires Bun 1.2 or later and the npm package spine-rigc.
|
|
|
9
9
|
|
|
10
10
|
Load this when the request is a **skeleton rather than a movement**: loose part
|
|
11
11
|
PNGs in, a bone hierarchy out. Every rule below is owned by
|
|
12
|
-
[RIGGING.md](
|
|
12
|
+
[RIGGING.md](https://github.com/firejune/rigc/blob/main/docs/RIGGING.md); this file says when to open it and what it
|
|
13
13
|
will not do for you.
|
|
14
14
|
|
|
15
15
|
## Non-negotiables
|
|
@@ -30,16 +30,20 @@ are named in RIGGING §11 — neither as a pass bar.
|
|
|
30
30
|
|
|
31
31
|
## Read, in this order
|
|
32
32
|
|
|
33
|
-
1. [AUTHORING.md](
|
|
33
|
+
1. [AUTHORING.md](https://github.com/firejune/rigc/blob/main/docs/AUTHORING.md) — the rig spec field by field (§3),
|
|
34
34
|
the failure map (§5–§6), reading a pose out of a picture (§8.1, §11, §12),
|
|
35
35
|
and the editor's own conventions (§10).
|
|
36
|
-
2. [RIGGING.md](
|
|
36
|
+
2. [RIGGING.md](https://github.com/firejune/rigc/blob/main/docs/RIGGING.md) — the structure itself: what identifies a
|
|
37
37
|
pivot and what moving one costs (§2–§3), gauges (§4), what a chain can reach
|
|
38
38
|
(§6), and the instruments that see structure (§11).
|
|
39
|
-
3. Then [MOTION.md](
|
|
40
|
-
[FACE.md](
|
|
41
|
-
[INGEST.md](
|
|
39
|
+
3. Then [MOTION.md](https://github.com/firejune/rigc/blob/main/docs/MOTION.md) once the skeleton exists;
|
|
40
|
+
[FACE.md](https://github.com/firejune/rigc/blob/main/docs/FACE.md) if the figure is a head;
|
|
41
|
+
[INGEST.md](https://github.com/firejune/rigc/blob/main/docs/INGEST.md) if the skeleton was handed to you already
|
|
42
42
|
compiled.
|
|
43
43
|
|
|
44
|
+
Every guide linked here is in the installed package at `node_modules/spine-rigc/docs/`,
|
|
45
|
+
which is the copy that matches the rigc you run; the links go to the repository's
|
|
46
|
+
`main`. Inside the Claude Code plugin the same files are at `${CLAUDE_PLUGIN_ROOT}/docs/`.
|
|
47
|
+
|
|
44
48
|
The install line and the build → validate → render → check loop are in the `rigc`
|
|
45
49
|
skill.
|