@alessandroraffa/tangyr 4.2.0 → 5.0.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 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 every hook event is dropped and recorded as a `hook-as-plugin` loss
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) {
@@ -31877,6 +31876,7 @@ SKIPPED CONFLICTS (${skippedTargetPaths.size} target(s) left unchanged):`
31877
31876
  missingRulesByTarget(rulesCoverage)
31878
31877
  );
31879
31878
  reportRulesCoverage(rulesCoverage, logger);
31879
+ recordRulesCoverageLoss(rulesCoverage, installLossReport);
31880
31880
  for (const [relativePath, sourcePath] of collectAllFiles(kitInfo, config)) {
31881
31881
  addFileToManifest(manifest, {
31882
31882
  relativePath,
@@ -35009,6 +35009,7 @@ async function runSyncCommand(options, logger) {
35009
35009
  missingRulesByTarget(rulesCoverage)
35010
35010
  );
35011
35011
  reportRulesCoverage(rulesCoverage, logger);
35012
+ recordRulesCoverageLoss(rulesCoverage, lossReport);
35012
35013
  }
35013
35014
  }
35014
35015
  writeLossReport(options.lossReport, lossReport);
@@ -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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alessandroraffa/tangyr",
3
- "version": "4.2.0",
3
+ "version": "5.0.0",
4
4
  "description": "CLI for the Tangyr discipline — install and manage operating kits for AI coding tools",
5
5
  "license": "MIT",
6
6
  "engines": {