@alessandroraffa/tangyr 4.2.0 → 5.0.1
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 +23 -3
- package/dist/index.js +37 -54
- package/mappings/README.md +3 -3
- package/mappings/loss-severity.yaml +10 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -363,6 +363,26 @@ acceptedLoss:
|
|
|
363
363
|
|
|
364
364
|
`--ignore-errors` remains the blunt instrument: it suppresses the exit code for every error entry, including ones nobody has looked at yet.
|
|
365
365
|
|
|
366
|
+
Not every error entry is structural. `rule-not-delivered` (below) reports a rule that did not reach a target on this run, which is usually fixable — by giving the target an instructions path of its own, or by not enabling it. Accept it when you have decided to live with it, not to quiet it.
|
|
367
|
+
|
|
368
|
+
## Rules that did not arrive
|
|
369
|
+
|
|
370
|
+
A rule reaches a target either as a file in that target's own rules slot or as a section in the instructions document it reads. Several targets resolve their instructions to one project-root `AGENTS.md` and write it in turn, so a rule folded into that file survives only for whoever wrote last.
|
|
371
|
+
|
|
372
|
+
After every target has written, the run measures what is actually on disk and says so — one line per target, the count first, and the rules that did **not** arrive named:
|
|
373
|
+
|
|
374
|
+
```text
|
|
375
|
+
Rules reaching each target (3 declared, measured on disk):
|
|
376
|
+
claude-code 3/3
|
|
377
|
+
copilot 3/3
|
|
378
|
+
codex 0/3 missing: style, testing, review
|
|
379
|
+
its rules are folded into AGENTS.md, which cline wrote last
|
|
380
|
+
opencode 3/3
|
|
381
|
+
cline 3/3
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
Nothing is printed when every rule arrived; `--verbose` shows the block either way. The same fact reaches `--loss-report <path>` as a `rule-not-delivered` entry per rule per target, at `error` severity, so a run that loses a rule exits `3` rather than reporting success.
|
|
385
|
+
|
|
366
386
|
## Exit codes
|
|
367
387
|
|
|
368
388
|
The full exit-code taxonomy, in numeric order. Every code below `5`
|
|
@@ -404,9 +424,9 @@ reaches them).
|
|
|
404
424
|
## Supported tools
|
|
405
425
|
|
|
406
426
|
- **Claude Code** — instructions (`AGENTS.md` plus the `CLAUDE.md` shim), agents, skills, commands, rules, output styles, hooks, settings, and MCP, placed in scope-resolved Claude Code paths (fanned out across every configured global root — see [Multiple Claude Code global roots](#multiple-claude-code-global-roots))
|
|
407
|
-
- **GitHub Copilot** — instructions (`AGENTS.md` plus the always-read `.github/copilot-instructions.md` mirror; project scope only, since Copilot has no global instructions slot), agents, prompts (compiled from commands), skills, hooks (`~/.copilot/hooks` or `.github/hooks`), and MCP. Hook matchers are rewritten to Copilot's own lowercase tool names (`bash`, `view`, `create`, `edit`, `grep`) — an untranslated `Bash` matcher never fires
|
|
408
|
-
- **OpenAI Codex** — `AGENTS.md`, agent TOML files, rules (`.rules` files for `exec-policy`, appended to the instructions file as guidance otherwise), hooks (`hooks.json` plus the shared `~/.agents/hooks` script bridge; macOS and Linux only), and MCP in `config.toml`
|
|
409
|
-
- **OpenCode** — instructions (`AGENTS.md`), agents, commands, skills (OpenCode-local plus the shared `~/.agents/skills` link), and `opencode.json` (MCP, permissions, custom provider), in XDG config paths. Hooks are **not** delivered: OpenCode exposes hooks only as plugin code, so
|
|
427
|
+
- **GitHub Copilot** — instructions (`AGENTS.md` plus the always-read `.github/copilot-instructions.md` mirror; project scope only, since Copilot has no global instructions slot), agents, prompts (compiled from commands), rules (`.github/instructions/<name>.instructions.md`, each carrying the rule's path scope as an `applyTo` glob; project scope only, since that is a repository path with no user-level equivalent), skills, hooks (`~/.copilot/hooks` or `.github/hooks`), and MCP. Hook matchers are rewritten to Copilot's own lowercase tool names (`bash`, `view`, `create`, `edit`, `grep`) — an untranslated `Bash` matcher never fires
|
|
428
|
+
- **OpenAI Codex** — `AGENTS.md`, agent TOML files, rules (`.rules` files for `exec-policy`, appended to the instructions file as guidance otherwise), skills and commands (both as skill directories at Codex's documented skills path — `~/.agents/skills` globally, `.agents/skills` inside a repository — because Codex deprecated its custom prompts in favour of skills), hooks (`hooks.json` plus the shared `~/.agents/hooks` script bridge; macOS and Linux only), and MCP in `config.toml`
|
|
429
|
+
- **OpenCode** — instructions (`~/.config/opencode/AGENTS.md` globally; `.opencode/AGENTS.md` inside a project, declared in the `instructions` field of `opencode.json`, because the project-root `AGENTS.md` is written by four other targets in turn and only the last writer's content survives), agents, commands, skills (OpenCode-local plus the shared `~/.agents/skills` link), and `opencode.json` (MCP, permissions, custom provider), in XDG config paths. Hooks are **not** delivered: OpenCode exposes hooks only as plugin code, so a declared hook event is dropped and recorded as a `hook-as-plugin` loss
|
|
410
430
|
- **Cursor** — instructions, subagents, `.mdc` rules, commands, skills (Cursor-local plus shared), `hooks.json` plus the shared hook-script bridge (matchers rewritten to Cursor's tool names — `Shell`, `Read`, `Write`, `Grep`), `mcp.json`, and `permissions.json`
|
|
411
431
|
- **Cline** — instructions (`AGENTS.md`, project scope only), rules, workflows (compiled from commands), skills, and MCP. Agents and hooks are **not** delivered: Cline has no committable persona file (`persona-not-representable`) and exposes hooks only as `@cline/sdk` plugin code (`hook-as-plugin`); both are recorded as losses rather than silently skipped
|
|
412
432
|
|
package/dist/index.js
CHANGED
|
@@ -21625,6 +21625,19 @@ var lossSeverityByEntryType = {
|
|
|
21625
21625
|
"rule-as-instruction": "info",
|
|
21626
21626
|
"rule-mode-downgrade": "warning",
|
|
21627
21627
|
"rule-path-scoping": "warning",
|
|
21628
|
+
// The rule is not there. Measured on the files each target reads, after every
|
|
21629
|
+
// target has written, so this says what arrived rather than what a compiler
|
|
21630
|
+
// intended — the two have disagreed, most often where several targets write
|
|
21631
|
+
// one instructions file and only the last writer's content survives.
|
|
21632
|
+
//
|
|
21633
|
+
// Error by the severity ladder's own definition: `warning` is "a real
|
|
21634
|
+
// reduction in fidelity that still produces a working artifact", and an
|
|
21635
|
+
// absent rule is not reduced, it is absent. `error` is "an intent that could
|
|
21636
|
+
// not be emitted at all and requires a manual step or a kit change", which is
|
|
21637
|
+
// exactly the remedy the run prints: give the target an instructions path of
|
|
21638
|
+
// its own, or disable it. `acceptedLoss` is how a project that has decided to
|
|
21639
|
+
// live with it says so.
|
|
21640
|
+
"rule-not-delivered": "error",
|
|
21628
21641
|
"hook-event-drop": "warning",
|
|
21629
21642
|
// A declaration the parser refuses installs nothing, anywhere, and used to
|
|
21630
21643
|
// do so silently. Error severity is what makes install and sync exit 3.
|
|
@@ -26421,6 +26434,26 @@ Rules reaching each target (${String(declared)} declared, measured on disk):`
|
|
|
26421
26434
|
function missingRulesByTarget(coverage) {
|
|
26422
26435
|
return new Map(coverage.map((entry) => [entry.target, entry.missing]));
|
|
26423
26436
|
}
|
|
26437
|
+
function recordRulesCoverageLoss(coverage, lossReport) {
|
|
26438
|
+
for (const entry of coverage) {
|
|
26439
|
+
if (entry.missing.length === 0) {
|
|
26440
|
+
continue;
|
|
26441
|
+
}
|
|
26442
|
+
addLoss(lossReport, entry.target, "rules", {
|
|
26443
|
+
source: "rules",
|
|
26444
|
+
fields: entry.missing.map((rule) => ({
|
|
26445
|
+
field: rule,
|
|
26446
|
+
action: "rule-not-delivered",
|
|
26447
|
+
severity: resolveLossSeverity(
|
|
26448
|
+
"agent-field",
|
|
26449
|
+
rule,
|
|
26450
|
+
"rule-not-delivered"
|
|
26451
|
+
),
|
|
26452
|
+
detail: `${rule}: not readable by ${entry.target}${entry.reason ? ` \u2014 ${entry.reason}` : ""}`
|
|
26453
|
+
}))
|
|
26454
|
+
});
|
|
26455
|
+
}
|
|
26456
|
+
}
|
|
26424
26457
|
|
|
26425
26458
|
// src/adapters/claude-code.ts
|
|
26426
26459
|
import fs38 from "fs";
|
|
@@ -27725,23 +27758,6 @@ function compileOpenCodeHooks(sourceHooksJson, lossReport, sourceLabel) {
|
|
|
27725
27758
|
]
|
|
27726
27759
|
});
|
|
27727
27760
|
}
|
|
27728
|
-
if (Object.keys(sourceHooks).length === 0) {
|
|
27729
|
-
addLoss(lossReport, "opencode", "hooks", {
|
|
27730
|
-
source: sourceLabel,
|
|
27731
|
-
fields: [
|
|
27732
|
-
{
|
|
27733
|
-
field: "hooks",
|
|
27734
|
-
action: "hook-as-plugin",
|
|
27735
|
-
severity: resolveLossSeverity(
|
|
27736
|
-
"agent-field",
|
|
27737
|
-
"hooks",
|
|
27738
|
-
"hook-as-plugin"
|
|
27739
|
-
),
|
|
27740
|
-
detail: "OpenCode exposes hooks only as plugin code \u2014 no declarative hook file emitted"
|
|
27741
|
-
}
|
|
27742
|
-
]
|
|
27743
|
-
});
|
|
27744
|
-
}
|
|
27745
27761
|
return { payload: {}, translated: 0, dropped: 0, droppedEvents: [] };
|
|
27746
27762
|
}
|
|
27747
27763
|
function compileClineHooks(sourceHooksJson, lossReport, sourceLabel) {
|
|
@@ -27763,23 +27779,6 @@ function compileClineHooks(sourceHooksJson, lossReport, sourceLabel) {
|
|
|
27763
27779
|
]
|
|
27764
27780
|
});
|
|
27765
27781
|
}
|
|
27766
|
-
if (Object.keys(sourceHooks).length === 0) {
|
|
27767
|
-
addLoss(lossReport, "cline", "hooks", {
|
|
27768
|
-
source: sourceLabel,
|
|
27769
|
-
fields: [
|
|
27770
|
-
{
|
|
27771
|
-
field: "hooks",
|
|
27772
|
-
action: "hook-as-plugin",
|
|
27773
|
-
severity: resolveLossSeverity(
|
|
27774
|
-
"agent-field",
|
|
27775
|
-
"hooks",
|
|
27776
|
-
"hook-as-plugin"
|
|
27777
|
-
),
|
|
27778
|
-
detail: "Cline exposes hooks only as @cline/sdk AgentPlugin TypeScript/JS code \u2014 no declarative hook file emitted"
|
|
27779
|
-
}
|
|
27780
|
-
]
|
|
27781
|
-
});
|
|
27782
|
-
}
|
|
27783
27782
|
return { payload: {}, translated: 0, dropped: 0, droppedEvents: [] };
|
|
27784
27783
|
}
|
|
27785
27784
|
function compileCopilotHooks(sourceHooksJson, mappings, lossReport, sourceLabel) {
|
|
@@ -28321,24 +28320,6 @@ import path39 from "path";
|
|
|
28321
28320
|
var import_yaml10 = __toESM(require_dist(), 1);
|
|
28322
28321
|
import fs40 from "fs";
|
|
28323
28322
|
function compileRules(sourceRulesDir, target, _mappings, lossReport) {
|
|
28324
|
-
if (target === "copilot") {
|
|
28325
|
-
addLoss(lossReport, "copilot", "rules", {
|
|
28326
|
-
source: "rules",
|
|
28327
|
-
fields: [
|
|
28328
|
-
{
|
|
28329
|
-
field: "rules",
|
|
28330
|
-
action: "rule-path-scoping",
|
|
28331
|
-
severity: resolveLossSeverity(
|
|
28332
|
-
"agent-field",
|
|
28333
|
-
"rules",
|
|
28334
|
-
"rule-path-scoping"
|
|
28335
|
-
),
|
|
28336
|
-
detail: "Rules: Skip \u2014 no verified global user-level rules surface for Copilot in v1"
|
|
28337
|
-
}
|
|
28338
|
-
]
|
|
28339
|
-
});
|
|
28340
|
-
return { agentsAppendix: "", dotRulesFiles: [] };
|
|
28341
|
-
}
|
|
28342
28323
|
if (target === "opencode") {
|
|
28343
28324
|
return compileOpenCodeRules(sourceRulesDir, lossReport);
|
|
28344
28325
|
}
|
|
@@ -28430,7 +28411,7 @@ ${parsed.body.trim()}`);
|
|
|
28430
28411
|
"rule",
|
|
28431
28412
|
"rule-path-scoping"
|
|
28432
28413
|
),
|
|
28433
|
-
detail: `${path39.basename(ruleFile)}: OpenCode has no per-agent rule scoping \u2014 rule appended to
|
|
28414
|
+
detail: `${path39.basename(ruleFile)}: OpenCode has no per-agent rule scoping \u2014 rule appended to its instructions file`
|
|
28434
28415
|
}
|
|
28435
28416
|
]
|
|
28436
28417
|
});
|
|
@@ -30113,7 +30094,7 @@ function extractPrimaryPersonaContent(config, sourceRoot, lossReport) {
|
|
|
30113
30094
|
"persona",
|
|
30114
30095
|
"instructions-shim"
|
|
30115
30096
|
),
|
|
30116
|
-
detail: `Primary persona from ${path43.basename(primaryFile)} folded into
|
|
30097
|
+
detail: `Primary persona from ${path43.basename(primaryFile)} folded into its instructions file (Cursor has no custom modes surface)`
|
|
30117
30098
|
}
|
|
30118
30099
|
]
|
|
30119
30100
|
});
|
|
@@ -31877,6 +31858,7 @@ SKIPPED CONFLICTS (${skippedTargetPaths.size} target(s) left unchanged):`
|
|
|
31877
31858
|
missingRulesByTarget(rulesCoverage)
|
|
31878
31859
|
);
|
|
31879
31860
|
reportRulesCoverage(rulesCoverage, logger);
|
|
31861
|
+
recordRulesCoverageLoss(rulesCoverage, installLossReport);
|
|
31880
31862
|
for (const [relativePath, sourcePath] of collectAllFiles(kitInfo, config)) {
|
|
31881
31863
|
addFileToManifest(manifest, {
|
|
31882
31864
|
relativePath,
|
|
@@ -35009,6 +34991,7 @@ async function runSyncCommand(options, logger) {
|
|
|
35009
34991
|
missingRulesByTarget(rulesCoverage)
|
|
35010
34992
|
);
|
|
35011
34993
|
reportRulesCoverage(rulesCoverage, logger);
|
|
34994
|
+
recordRulesCoverageLoss(rulesCoverage, lossReport);
|
|
35012
34995
|
}
|
|
35013
34996
|
}
|
|
35014
34997
|
writeLossReport(options.lossReport, lossReport);
|
package/mappings/README.md
CHANGED
|
@@ -5,9 +5,9 @@ Declarative translation inputs for `tangyr`.
|
|
|
5
5
|
These files are part of the canonical runtime bundle, but they are design-time metadata rather than user-facing runtime artifacts. They define the current active v1 translation baseline for:
|
|
6
6
|
|
|
7
7
|
- `Claude Code`
|
|
8
|
-
- `GitHub Copilot`
|
|
9
|
-
- `Codex`
|
|
10
|
-
- `OpenCode` — full six-slot profile: `instructions` (`AGENTS.md` at the XDG-based `~/.config/opencode/` path), `agents`, `commands`, `skills` (OpenCode-local), `skills_shared` (cross-tool `~/.agents/skills`), and `config_doc` (`opencode.json`)
|
|
8
|
+
- `GitHub Copilot` — `rules` is a project-only slot (`.github/instructions/`), `null` at global scope: that path is a repository surface with no documented user-level equivalent
|
|
9
|
+
- `Codex` — skills and commands both land at Codex's documented skills path (`~/.agents/skills` globally, `.agents/skills` in a repository), since Codex deprecated custom prompts in favour of skills; `exec-policy` rules emit as `.rules` files and guidance rules are folded into the instructions document
|
|
10
|
+
- `OpenCode` — full six-slot profile: `instructions` (`AGENTS.md` at the XDG-based `~/.config/opencode/` path globally; `.opencode/AGENTS.md` in a project, declared in the `instructions` field of `opencode.json`, because the project-root `AGENTS.md` is claimed by four other targets), `agents`, `commands`, `skills` (OpenCode-local), `skills_shared` (cross-tool `~/.agents/skills`), and `config_doc` (`opencode.json`)
|
|
11
11
|
- `Cursor` — full eight-slot profile: `instructions` (`AGENTS.md` at `~/.cursor/rules/`), `agents` (`~/.cursor/agents/`), `rules` (`~/.cursor/rules/`), `skills` (Cursor-local `~/.cursor/skills/`), `skills_shared` (cross-tool `~/.agents/skills`), `hooks_file` (`~/.cursor/hooks.json`), `mcp_file` (`~/.cursor/mcp.json`), and `permissions_file` (`~/.cursor/permissions.json`)
|
|
12
12
|
- `Cline` — five-slot profile: `instructions` (`AGENTS.md` at project root — project scope only; global `instructions` is `null` because Cline has no documented global `AGENTS.md` read location and the global custom-instructions field is interface-only), `rules` (project: `.clinerules/`; global: `~/Documents/Cline/Rules` — macOS primary), `workflows` (project: `.clinerules/workflows/`; global: `~/Documents/Cline/Workflows/`), `skills` (project: `.cline/skills/`; global: `~/.cline/skills/`), and `mcp_file` (project: `cline-mcp.json`; global: `~/.cline/mcp.json`)
|
|
13
13
|
|
|
@@ -18,13 +18,23 @@ notes:
|
|
|
18
18
|
- Severity is resolved per entry type, not per target - `resolveLossSeverity`
|
|
19
19
|
(compiler/loss-report.ts) keys only on the entry type, with an explicit severity from
|
|
20
20
|
hook-events.yaml overriding it.
|
|
21
|
+
- "`rule-not-delivered` was added on 2026-09-16 and is the one entry measured on the
|
|
22
|
+
files rather than recorded by the compiler that wrote them. It is emitted after every
|
|
23
|
+
target has run, once per rule that did not reach a target, which is the machine-readable
|
|
24
|
+
half of the per-target coverage block the run prints."
|
|
25
|
+
- "`hook-declaration-invalid` and `hook-source-ambiguous` were in the code's table and
|
|
26
|
+
missing from this one until 2026-09-16. Nothing compared the two, so the drift was
|
|
27
|
+
silent; tests/unit/mappings/loss-severity-matches-code.test.ts now does."
|
|
21
28
|
|
|
22
29
|
entry_types:
|
|
23
30
|
command-as-skill: info
|
|
24
31
|
rule-as-instruction: info
|
|
25
32
|
rule-mode-downgrade: warning
|
|
26
33
|
rule-path-scoping: warning
|
|
34
|
+
rule-not-delivered: error
|
|
27
35
|
hook-event-drop: warning
|
|
36
|
+
hook-declaration-invalid: error
|
|
37
|
+
hook-source-ambiguous: error
|
|
28
38
|
hook-as-plugin: error
|
|
29
39
|
instructions-shim: info
|
|
30
40
|
permission-coarsening: error
|