agentopology 0.9.1 → 0.10.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 +17 -19
- package/dist/__tests__/e2e/action-guard.test.d.ts +13 -0
- package/dist/__tests__/e2e/action-guard.test.d.ts.map +1 -0
- package/dist/__tests__/e2e/action-guard.test.js +299 -0
- package/dist/__tests__/e2e/action-guard.test.js.map +1 -0
- package/dist/__tests__/e2e/compile.test.js +1 -1
- package/dist/__tests__/e2e/compile.test.js.map +1 -1
- package/dist/__tests__/e2e/conformance.test.d.ts +13 -0
- package/dist/__tests__/e2e/conformance.test.d.ts.map +1 -0
- package/dist/__tests__/e2e/conformance.test.js +109 -0
- package/dist/__tests__/e2e/conformance.test.js.map +1 -0
- package/dist/__tests__/e2e/contract-drift.test.d.ts +11 -0
- package/dist/__tests__/e2e/contract-drift.test.d.ts.map +1 -0
- package/dist/__tests__/e2e/contract-drift.test.js +142 -0
- package/dist/__tests__/e2e/contract-drift.test.js.map +1 -0
- package/dist/__tests__/e2e/fail-closed.test.d.ts +11 -0
- package/dist/__tests__/e2e/fail-closed.test.d.ts.map +1 -0
- package/dist/__tests__/e2e/fail-closed.test.js +111 -0
- package/dist/__tests__/e2e/fail-closed.test.js.map +1 -0
- package/dist/__tests__/e2e/guard-check.test.d.ts +9 -0
- package/dist/__tests__/e2e/guard-check.test.d.ts.map +1 -0
- package/dist/__tests__/e2e/guard-check.test.js +316 -0
- package/dist/__tests__/e2e/guard-check.test.js.map +1 -0
- package/dist/__tests__/e2e/guard-target.test.d.ts +11 -0
- package/dist/__tests__/e2e/guard-target.test.d.ts.map +1 -0
- package/dist/__tests__/e2e/guard-target.test.js +198 -0
- package/dist/__tests__/e2e/guard-target.test.js.map +1 -0
- package/dist/__tests__/e2e/inventory.test.d.ts +18 -0
- package/dist/__tests__/e2e/inventory.test.d.ts.map +1 -0
- package/dist/__tests__/e2e/inventory.test.js +187 -0
- package/dist/__tests__/e2e/inventory.test.js.map +1 -0
- package/dist/__tests__/e2e/matcher.test.d.ts +14 -0
- package/dist/__tests__/e2e/matcher.test.d.ts.map +1 -0
- package/dist/__tests__/e2e/matcher.test.js +145 -0
- package/dist/__tests__/e2e/matcher.test.js.map +1 -0
- package/dist/__tests__/e2e/step-check.test.d.ts +9 -0
- package/dist/__tests__/e2e/step-check.test.d.ts.map +1 -0
- package/dist/__tests__/e2e/step-check.test.js +158 -0
- package/dist/__tests__/e2e/step-check.test.js.map +1 -0
- package/dist/__tests__/e2e/strength.test.d.ts +17 -0
- package/dist/__tests__/e2e/strength.test.d.ts.map +1 -0
- package/dist/__tests__/e2e/strength.test.js +130 -0
- package/dist/__tests__/e2e/strength.test.js.map +1 -0
- package/dist/__tests__/e2e/verified-on.test.d.ts +2 -0
- package/dist/__tests__/e2e/verified-on.test.d.ts.map +1 -0
- package/dist/__tests__/e2e/verified-on.test.js +57 -0
- package/dist/__tests__/e2e/verified-on.test.js.map +1 -0
- package/dist/__tests__/open-findings.test.js +1 -0
- package/dist/__tests__/open-findings.test.js.map +1 -1
- package/dist/bindings/__tests__/bindings.test.js +6 -686
- package/dist/bindings/__tests__/bindings.test.js.map +1 -1
- package/dist/bindings/__tests__/defaults-agreement.test.js +0 -12
- package/dist/bindings/__tests__/defaults-agreement.test.js.map +1 -1
- package/dist/bindings/__tests__/hook-command.test.js +2 -2
- package/dist/bindings/__tests__/hook-command.test.js.map +1 -1
- package/dist/bindings/__tests__/mod-plugin.test.js +2 -2
- package/dist/bindings/__tests__/mod-plugin.test.js.map +1 -1
- package/dist/bindings/claude-code.d.ts +45 -1
- package/dist/bindings/claude-code.d.ts.map +1 -1
- package/dist/bindings/claude-code.js +191 -19
- package/dist/bindings/claude-code.js.map +1 -1
- package/dist/bindings/index.d.ts +5 -5
- package/dist/bindings/index.d.ts.map +1 -1
- package/dist/bindings/index.js +9 -16
- package/dist/bindings/index.js.map +1 -1
- package/dist/bindings/lib/stub.d.ts +15 -1
- package/dist/bindings/lib/stub.d.ts.map +1 -1
- package/dist/bindings/lib/stub.js +27 -2
- package/dist/bindings/lib/stub.js.map +1 -1
- package/dist/bindings/types.d.ts +6 -1
- package/dist/bindings/types.d.ts.map +1 -1
- package/dist/cli/__tests__/cli.test.js +25 -0
- package/dist/cli/__tests__/cli.test.js.map +1 -1
- package/dist/cli/index.js +222 -8
- package/dist/cli/index.js.map +1 -1
- package/dist/conformance/claude-code.d.ts +99 -0
- package/dist/conformance/claude-code.d.ts.map +1 -0
- package/dist/conformance/claude-code.js +242 -0
- package/dist/conformance/claude-code.js.map +1 -0
- package/dist/conformance/extract.d.ts +70 -0
- package/dist/conformance/extract.d.ts.map +1 -0
- package/dist/conformance/extract.js +185 -0
- package/dist/conformance/extract.js.map +1 -0
- package/dist/docs/content.d.ts.map +1 -1
- package/dist/docs/content.js +57 -32
- package/dist/docs/content.js.map +1 -1
- package/dist/guard/check.d.ts +53 -0
- package/dist/guard/check.d.ts.map +1 -0
- package/dist/guard/check.js +366 -0
- package/dist/guard/check.js.map +1 -0
- package/dist/guard/target.d.ts +45 -0
- package/dist/guard/target.d.ts.map +1 -0
- package/dist/guard/target.js +168 -0
- package/dist/guard/target.js.map +1 -0
- package/dist/import/__tests__/import.test.js +0 -554
- package/dist/import/__tests__/import.test.js.map +1 -1
- package/dist/import/index.d.ts +0 -1
- package/dist/import/index.d.ts.map +1 -1
- package/dist/import/index.js +1 -7
- package/dist/import/index.js.map +1 -1
- package/dist/import/serializer.d.ts.map +1 -1
- package/dist/import/serializer.js +41 -0
- package/dist/import/serializer.js.map +1 -1
- package/dist/index.d.ts +0 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +0 -4
- package/dist/index.js.map +1 -1
- package/dist/inventory/index.d.ts +65 -0
- package/dist/inventory/index.d.ts.map +1 -0
- package/dist/inventory/index.js +272 -0
- package/dist/inventory/index.js.map +1 -0
- package/dist/parser/ast.d.ts +46 -0
- package/dist/parser/ast.d.ts.map +1 -1
- package/dist/parser/ast.js.map +1 -1
- package/dist/parser/index.d.ts +6 -1
- package/dist/parser/index.d.ts.map +1 -1
- package/dist/parser/index.js +66 -4
- package/dist/parser/index.js.map +1 -1
- package/dist/parser/known-fields.d.ts +3 -0
- package/dist/parser/known-fields.d.ts.map +1 -1
- package/dist/parser/known-fields.js +5 -3
- package/dist/parser/known-fields.js.map +1 -1
- package/dist/parser/ledger.d.ts +36 -0
- package/dist/parser/ledger.d.ts.map +1 -0
- package/dist/parser/ledger.js +72 -0
- package/dist/parser/ledger.js.map +1 -0
- package/dist/parser/run.d.ts +6 -0
- package/dist/parser/run.d.ts.map +1 -1
- package/dist/parser/run.js +10 -0
- package/dist/parser/run.js.map +1 -1
- package/dist/parser/validator.d.ts.map +1 -1
- package/dist/parser/validator.js +255 -1
- package/dist/parser/validator.js.map +1 -1
- package/dist/scaffold/manifest.d.ts.map +1 -1
- package/dist/scaffold/manifest.js +0 -8
- package/dist/scaffold/manifest.js.map +1 -1
- package/dist/scaffold/merge.d.ts.map +1 -1
- package/dist/scaffold/merge.js +1 -3
- package/dist/scaffold/merge.js.map +1 -1
- package/dist/step/check.d.ts +56 -0
- package/dist/step/check.d.ts.map +1 -0
- package/dist/step/check.js +125 -0
- package/dist/step/check.js.map +1 -0
- package/dist/sync/index.d.ts +0 -2
- package/dist/sync/index.d.ts.map +1 -1
- package/dist/sync/index.js +0 -109
- package/dist/sync/index.js.map +1 -1
- package/package.json +3 -1
- package/spec/claude-code/README.md +19 -0
- package/spec/claude-code/hooks-contract.2.1.295.json +143 -0
- package/spec/claude-code/hooks-contract.json +142 -0
- package/spec/grammar.md +45 -7
- package/spec/testing.md +16 -0
- package/spec/validation.md +11 -2
- package/dist/__tests__/live/zz-debug.test.d.ts +0 -2
- package/dist/__tests__/live/zz-debug.test.d.ts.map +0 -1
- package/dist/__tests__/live/zz-debug.test.js +0 -4
- package/dist/__tests__/live/zz-debug.test.js.map +0 -1
- package/dist/bindings/copilot-cli.d.ts +0 -20
- package/dist/bindings/copilot-cli.d.ts.map +0 -1
- package/dist/bindings/copilot-cli.js +0 -1516
- package/dist/bindings/copilot-cli.js.map +0 -1
- package/dist/bindings/gemini-cli.d.ts +0 -18
- package/dist/bindings/gemini-cli.d.ts.map +0 -1
- package/dist/bindings/gemini-cli.js +0 -1318
- package/dist/bindings/gemini-cli.js.map +0 -1
- package/dist/bindings/kiro.d.ts +0 -17
- package/dist/bindings/kiro.d.ts.map +0 -1
- package/dist/bindings/kiro.js +0 -1615
- package/dist/bindings/kiro.js.map +0 -1
- package/dist/bindings/openclaw.d.ts +0 -18
- package/dist/bindings/openclaw.d.ts.map +0 -1
- package/dist/bindings/openclaw.js +0 -1947
- package/dist/bindings/openclaw.js.map +0 -1
- package/dist/import/openclaw.d.ts +0 -70
- package/dist/import/openclaw.d.ts.map +0 -1
- package/dist/import/openclaw.js +0 -1060
- package/dist/import/openclaw.js.map +0 -1
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
{
|
|
2
|
+
"source": "https://code.claude.com/docs/en/hooks.md",
|
|
3
|
+
"fetched": "2026-11-10",
|
|
4
|
+
"claudeCode": "2.1.319",
|
|
5
|
+
"events": [
|
|
6
|
+
"SessionStart",
|
|
7
|
+
"Setup",
|
|
8
|
+
"InstructionsLoaded",
|
|
9
|
+
"UserPromptSubmit",
|
|
10
|
+
"UserPromptExpansion",
|
|
11
|
+
"MessageDisplay",
|
|
12
|
+
"PreToolUse",
|
|
13
|
+
"PermissionRequest",
|
|
14
|
+
"PostToolUse",
|
|
15
|
+
"PostToolUseFailure",
|
|
16
|
+
"PostToolBatch",
|
|
17
|
+
"PermissionDenied",
|
|
18
|
+
"Notification",
|
|
19
|
+
"SubagentStart",
|
|
20
|
+
"SubagentStop",
|
|
21
|
+
"TaskCreated",
|
|
22
|
+
"TaskCompleted",
|
|
23
|
+
"Stop",
|
|
24
|
+
"StopFailure",
|
|
25
|
+
"TeammateIdle",
|
|
26
|
+
"ConfigChange",
|
|
27
|
+
"CwdChanged",
|
|
28
|
+
"DirectoryAdded",
|
|
29
|
+
"FileChanged",
|
|
30
|
+
"WorktreeCreate",
|
|
31
|
+
"WorktreeRemove",
|
|
32
|
+
"PreCompact",
|
|
33
|
+
"PostCompact",
|
|
34
|
+
"PreModelSwitch",
|
|
35
|
+
"PostModelSwitch",
|
|
36
|
+
"SessionEnd",
|
|
37
|
+
"Elicitation",
|
|
38
|
+
"ElicitationResult"
|
|
39
|
+
],
|
|
40
|
+
"eventRequires": {
|
|
41
|
+
"PreModelSwitch": "2.1.251",
|
|
42
|
+
"PostModelSwitch": "2.1.251"
|
|
43
|
+
},
|
|
44
|
+
"noMatcherEvents": [
|
|
45
|
+
"CwdChanged",
|
|
46
|
+
"MessageDisplay",
|
|
47
|
+
"PostToolBatch",
|
|
48
|
+
"Stop",
|
|
49
|
+
"TaskCompleted",
|
|
50
|
+
"TaskCreated",
|
|
51
|
+
"TeammateIdle",
|
|
52
|
+
"UserPromptSubmit",
|
|
53
|
+
"WorktreeCreate",
|
|
54
|
+
"WorktreeRemove"
|
|
55
|
+
],
|
|
56
|
+
"handlerTypes": [
|
|
57
|
+
"command",
|
|
58
|
+
"http",
|
|
59
|
+
"mcp_tool",
|
|
60
|
+
"prompt",
|
|
61
|
+
"agent"
|
|
62
|
+
],
|
|
63
|
+
"fields": {
|
|
64
|
+
"common": {
|
|
65
|
+
"type": {
|
|
66
|
+
"required": true
|
|
67
|
+
},
|
|
68
|
+
"if": {
|
|
69
|
+
"required": false
|
|
70
|
+
},
|
|
71
|
+
"timeout": {
|
|
72
|
+
"required": false
|
|
73
|
+
},
|
|
74
|
+
"statusMessage": {
|
|
75
|
+
"required": false
|
|
76
|
+
},
|
|
77
|
+
"once": {
|
|
78
|
+
"required": false
|
|
79
|
+
}
|
|
80
|
+
},
|
|
81
|
+
"command": {
|
|
82
|
+
"command": {
|
|
83
|
+
"required": true
|
|
84
|
+
},
|
|
85
|
+
"args": {
|
|
86
|
+
"required": false
|
|
87
|
+
},
|
|
88
|
+
"async": {
|
|
89
|
+
"required": false
|
|
90
|
+
},
|
|
91
|
+
"asyncRewake": {
|
|
92
|
+
"required": false
|
|
93
|
+
},
|
|
94
|
+
"shell": {
|
|
95
|
+
"required": false
|
|
96
|
+
},
|
|
97
|
+
"onFailure": {
|
|
98
|
+
"required": false,
|
|
99
|
+
"requires": "2.1.295"
|
|
100
|
+
}
|
|
101
|
+
},
|
|
102
|
+
"http": {
|
|
103
|
+
"url": {
|
|
104
|
+
"required": true
|
|
105
|
+
},
|
|
106
|
+
"headers": {
|
|
107
|
+
"required": false
|
|
108
|
+
},
|
|
109
|
+
"allowedEnvVars": {
|
|
110
|
+
"required": false
|
|
111
|
+
},
|
|
112
|
+
"onFailure": {
|
|
113
|
+
"required": false,
|
|
114
|
+
"requires": "2.1.295"
|
|
115
|
+
}
|
|
116
|
+
},
|
|
117
|
+
"mcp_tool": {
|
|
118
|
+
"server": {
|
|
119
|
+
"required": true
|
|
120
|
+
},
|
|
121
|
+
"tool": {
|
|
122
|
+
"required": true
|
|
123
|
+
},
|
|
124
|
+
"input": {
|
|
125
|
+
"required": false
|
|
126
|
+
}
|
|
127
|
+
},
|
|
128
|
+
"prompt": {
|
|
129
|
+
"prompt": {
|
|
130
|
+
"required": true
|
|
131
|
+
},
|
|
132
|
+
"model": {
|
|
133
|
+
"required": false
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
},
|
|
137
|
+
"matcherRules": [
|
|
138
|
+
"`\"*\"`, `\"\"`, or omitted → Match all",
|
|
139
|
+
"Only letters, digits, `_`, `-`, spaces, `,`, and `|` → Exact string, or list of exact strings separated by `|` or `,` with optional surrounding whitespace",
|
|
140
|
+
"Contains any other character → JavaScript regular expression, unanchored"
|
|
141
|
+
]
|
|
142
|
+
}
|
package/spec/grammar.md
CHANGED
|
@@ -472,6 +472,7 @@ action <name> {
|
|
|
472
472
|
| `source` | string | no | -- |
|
|
473
473
|
| `commands` | name-list | no | `[]` |
|
|
474
474
|
| `description` | string | no | -- |
|
|
475
|
+
| `unit-from` | string (regex) | no | -- | One capture group that reads the unit (an episode id, a ticket) from the command line, e.g. `"./tool publish (\\S+)"`. Needs a capture group (V111) |
|
|
475
476
|
|
|
476
477
|
**action-kind:** `external` | `git` | `decision` | `inline` | `report`
|
|
477
478
|
|
|
@@ -941,6 +942,11 @@ flow {
|
|
|
941
942
|
| `[when x.y == z]` | Conditional: edge taken only when condition is true |
|
|
942
943
|
| `[max N]` | Bounded loop: back-edge limited to N iterations |
|
|
943
944
|
| `[per ticket]` | One instance per unit (currently only `ticket` is valid) |
|
|
945
|
+
| `[strength: none]` | Enforcement written down: `refuse`, `detect`, `hope` or `none` (V104), so an absence is declared, not implied |
|
|
946
|
+
| `[at: "story/SKILL.md"]` | Where the enforcement, or the prose that hopes, lives (V106) |
|
|
947
|
+
| `[guard: lock-bash]` / `[guard: [a, b]]` | The hooks or gates that guard this edge, by id (V105) |
|
|
948
|
+
|
|
949
|
+
**Enforcement.** `strength` says what stops a step: code or a hook **refuses** it, a critic **detects** it after the fact, prose **hopes**, or **none**. `by` names the mechanism (`code`, `hook`, `settings`, `gate`); `at` cites it, `match` is the text expected there, and `bypass` names the way around it. A gate or hook carries the evidence; an edge cites it with `guard:` or writes its strength down. Hooks take `strength` and `bypass`. validate checks the words and the references (V104 to V106) and never reads the project. Reading the cited files (does the line exist, is the `match` text there) is `guard --check`'s job, and lands with it in 0.10.0.
|
|
944
950
|
|
|
945
951
|
**Chained flows:** `a -> b -> c [when x]` desugars to `a -> b` + `b -> c [when x]`. Edge attributes apply to the **last edge only**.
|
|
946
952
|
|
|
@@ -1004,6 +1010,30 @@ gates {
|
|
|
1004
1010
|
| `on-fail` | `bounce-back` / `halt` | no | `halt` | Failure behavior |
|
|
1005
1011
|
| `behavior` | `advisory` / `blocking` | no | `blocking` | Advisory gates never block |
|
|
1006
1012
|
| `description` | string | no | -- | What the gate checks, in words. Prose goes here, never in `run` (V96) |
|
|
1013
|
+
| `strength` | `refuse` / `detect` / `hope` / `none` | no | -- | What stops the step, as written down (V104). See "Enforcement" below |
|
|
1014
|
+
| `by` | `code` / `hook` / `settings` / `gate` | no | -- | Which mechanism refuses (V104) |
|
|
1015
|
+
| `at` | `"path"` or `"path:line"` | no | -- | The code that refuses, project-relative (V106) |
|
|
1016
|
+
| `match` | string | no | -- | Text expected within 5 lines of `at`, so a refactor that removes the check is caught. Needs `at` (V106) |
|
|
1017
|
+
| `bypass` | string | no | -- | The declared way around it (an env switch, a flag) |
|
|
1018
|
+
| `ledger` | record id | no | the only record | The record a gate before a script step reads. Omitted, a topology with exactly one `record` uses it (V112) |
|
|
1019
|
+
|
|
1020
|
+
**Gates before a script step.** A gate whose `before:` is an `action` with `commands:` refuses that step in Claude Code sessions. The claude-code binding writes one script, `before-action.sh`, wired as a `PreToolUse` hook on `Bash` with `onFailure: "block"`. When the Bash command starts with one of the action's `commands:` (leading `VAR=value` words are ignored), the script checks the gate: if it has a ledger, the **last** line for the `after:` step (and the same unit, when the command names one with `unit-from`) must have `ok: true`; if it has `run:`, that wrapper must exit 0. Anything else exits 2 and says which gate, step, unit and ledger path refused. A gate with neither a ledger nor `run:` has nothing to check (V110).
|
|
1021
|
+
|
|
1022
|
+
```agenttopology
|
|
1023
|
+
records {
|
|
1024
|
+
record trace { path: "logs/{unit}/trace.jsonl" }
|
|
1025
|
+
}
|
|
1026
|
+
```
|
|
1027
|
+
|
|
1028
|
+
`records { record <id> { path: "<project-relative path>" } }` declares ledgers. `path` may contain `{unit}`. `append-only` and `written-by` are reserved and not implemented.
|
|
1029
|
+
|
|
1030
|
+
**Ledger line format** (JSON Lines, one object per line). The step's own script appends the line when it finishes; agentopology only reads it:
|
|
1031
|
+
|
|
1032
|
+
```json
|
|
1033
|
+
{"step": "<node id>", "unit": "<unit, omitted when none>", "ok": true, "at": "<ISO time>"}
|
|
1034
|
+
```
|
|
1035
|
+
|
|
1036
|
+
A hook only covers Claude Code sessions. A step run from a terminal or a scheduler checks itself, by calling `agentopology step check <file.at> <step-id> [--unit U] [--dir D]` at its start (`by: code`): it exits 0 when every gate before that step is satisfied and 1 otherwise, naming each failing gate and why. A gate is satisfied when its ledger shows the `after:` step ok (for the unit, if given) **and**, if it has `run:`, that command exits 0 from `--dir` (default `.`); a gate with neither is unsatisfiable ("nothing to check"). A step no gate guards exits 0 with `no gate guards <step>`. It reads and runs check scripts; it never writes. The hook sees a step inside a chained command (`cd dir && cmd`, `a; cmd`, `a || cmd`, `a | cmd`, a later line) and checks each segment; it does not see one run indirectly (`bash -c`, `eval`, a script that calls the step).
|
|
1007
1037
|
|
|
1008
1038
|
---
|
|
1009
1039
|
|
|
@@ -1180,6 +1210,7 @@ hooks {
|
|
|
1180
1210
|
|-------|------|----------|---------|
|
|
1181
1211
|
| `on` | hook-event | yes | -- |
|
|
1182
1212
|
| `matcher` | string | no | -- |
|
|
1213
|
+
| `if` | permission rule | no (tool events only) | -- |
|
|
1183
1214
|
| `run` | string | command, prompt, agent, mod | -- |
|
|
1184
1215
|
| `type` | hook-type | no | `command` |
|
|
1185
1216
|
| `url` | string | http | -- |
|
|
@@ -1200,6 +1231,16 @@ Code function hooks). Its `on:` is an `engine-event` (V95); a command or
|
|
|
1200
1231
|
prompt hook takes a PascalCase event. `flag:` names the env variable that
|
|
1201
1232
|
switches the Mod on. Fields are one per line (V90).
|
|
1202
1233
|
|
|
1234
|
+
A `matcher` is tested against the tool name (or agent type, or path) alone, the
|
|
1235
|
+
way Claude Code reads it: only letters, digits, `_`, `-`, spaces, `,` and `|`
|
|
1236
|
+
mean an exact name or a list; any other character makes it an unanchored
|
|
1237
|
+
regex. An MCP tool is `mcp__<server>__<tool>`. Permission-rule syntax
|
|
1238
|
+
(`Bash(rm -rf *)`) goes in `if:`, which Claude Code reads on tool events only.
|
|
1239
|
+
V107 refuses a matcher that can never match and an `if:` that is never read.
|
|
1240
|
+
`run: "exit 2"` runs as written; only exit 2 blocks, and V108 warns on any
|
|
1241
|
+
other code. V109 warns when a hook with `strength: refuse` guards a server but
|
|
1242
|
+
its matcher misses one of the server's tools that the topology names.
|
|
1243
|
+
|
|
1203
1244
|
---
|
|
1204
1245
|
|
|
1205
1246
|
### 4.17 `settings`
|
|
@@ -1889,13 +1930,10 @@ Bindings map universal permissions to platform-specific modes:
|
|
|
1889
1930
|
| Binding | Status | Target |
|
|
1890
1931
|
|---------|--------|--------|
|
|
1891
1932
|
| `claude-code` | Stable | Claude Code agent files, settings, MCP config (the event-driven HOST layer) |
|
|
1892
|
-
| `claude-workflow` |
|
|
1893
|
-
| `codex` |
|
|
1894
|
-
| `
|
|
1895
|
-
| `copilot-cli`
|
|
1896
|
-
| `openclaw` | Stable | OpenClaw soul.md, skill files, channel/gateway/workspace config |
|
|
1897
|
-
| `kiro` | Stable | Kiro agent definitions and config |
|
|
1898
|
-
| `cursor` | Stable | Cursor rules and agent config |
|
|
1933
|
+
| `claude-workflow` | Experimental | Claude Workflow tool script (`<topology>.workflow.js`) for the deterministic fan-out rung, plus SEAM/README/LOSSY-REPORT companion files |
|
|
1934
|
+
| `codex` | Experimental | Codex agent definitions and config |
|
|
1935
|
+
| `cursor` | Experimental | Cursor rules and agent config |
|
|
1936
|
+
| `gemini-cli`, `copilot-cli`, `openclaw`, `kiro` | Removed in 0.10.0 | Use agentopology 0.9.x |
|
|
1899
1937
|
| `agent-sdk` | Planned | Anthropic Agent SDK tool definitions |
|
|
1900
1938
|
| `e2b` | Planned | E2B sandbox configuration |
|
|
1901
1939
|
| `openai-swarm` | Planned | OpenAI Swarm agent definitions |
|
package/spec/testing.md
CHANGED
|
@@ -79,6 +79,21 @@ Run the live suite before every release, and after any change to a binding.
|
|
|
79
79
|
| I3 | Import reports the flow honestly: sources used and skipped, agents wired and unknown, agents with no file; no edge is invented | e2e/import-project.test.ts |
|
|
80
80
|
| N23 | Every field and block name in a node, `meta`, a hook, job, artifact, tool or skill, and every topology section, is in the AST or a V102 error naming it and its block (with the field it resembles, or the node type it belongs to); validate exits 1 and scaffold writes nothing; every field the docs list passes | e2e/unknown-names.test.ts |
|
|
81
81
|
| N24 | An agent's or gate's `behavior:` outside `advisory`/`blocking` is a V103 error, never emitted unchecked | e2e/unknown-names.test.ts |
|
|
82
|
+
| N27 | A generated gate fails closed: before-agent.sh and gate wrappers turn any error (unset CLAUDE_PROJECT_DIR, a missing jq or interpreter) into exit 2, a gate script nobody has written exits 2, and the guard's settings entry sets `onFailure: "block"` | e2e/fail-closed.test.ts |
|
|
83
|
+
| N28 | Every generated command hook names its script by a path anchored to `$CLAUDE_PROJECT_DIR`, because Claude Code runs a hook in the session's current directory: run from a subfolder, each one is found, exits 0 or 2, and prints stdout its event accepts (conformance v0, checked against Claude Code 2.1.319) | e2e/conformance.test.ts |
|
|
84
|
+
| N31 | `guard <file> --check` reads a project and reports, for every gate, approval step and deny/ask rule, how it is enforced today: refuse (its hook or rule is on disk), none (the binding generates one that is missing, or names its script by a relative path; exit 1), or hope (no hook exists for it). It prints `enforcement: k of m steps refuse` and writes nothing | e2e/guard-check.test.ts |
|
|
85
|
+
| N32 | A step's enforcement is written in the file and read: `strength`, `by`, `at`, `match`, `bypass` on a gate; `strength`, `bypass` on a hook; `[strength: …, at: "…", guard: …]` on a flow edge. All round-trip through serialize, V102 knows them, and V104 to V106 check the words, the guard names and the citation form | e2e/strength.test.ts |
|
|
86
|
+
| N33 | `guard --check` reads what the file cites: a gate's `at: "path:line"` is checked on disk (the file, the line, the `match:` text within 5 lines), so a refactor that moves or removes the check is `none` and exits 1; a gate with no hook of its own is credited by the hook its edge cites (`[guard: name]`) when that hook is in settings.json and declares its strength; an edge's written `[strength: none]` is reported, not failed. Credit reads "cited, not proven" | e2e/guard-check.test.ts |
|
|
87
|
+
| N34 | `inventory <dir>` lists what is live in a project (the .claude tree with a sha256 per file, each settings.json hook handler, .mcp.json servers, package.json scripts) and writes nothing; `validate <file> --inventory <dir or inventory.json>` prints `coverage: k of n live things are in the .at`, lists what nothing in the .at accounts for (unmapped, a warning) and what the .at points at by hand that is not on disk (missing, exit 1); things tagged `e` are counted apart | e2e/inventory.test.ts |
|
|
88
|
+
| N29 | A hook `run:` that is a shell command (`run: "exit 2"`) is written as the command itself, with no stub script named after its first word; V108 warns that `exit 1` (any code but 0 and 2) does not block | e2e/matcher.test.ts |
|
|
89
|
+
| N30 | A hook matcher in permission-rule syntax (`Bash(rm -rf *)`) is a V107 error that names the fix (`matcher: "Bash"`, `if: "Bash(rm -rf *)"`); `if:` is read, round-trips, is written on the handler, and is an error on an event that is not a tool event | e2e/matcher.test.ts |
|
|
90
|
+
| N36 | A matcher is read the way Claude Code reads it (exact names and lists, unanchored regex, match-all; `mcp__server` alone matches nothing). V107 refuses a matcher that can never match. V109 warns when a refuse hook guards an MCP server but misses one of its tools, from the topology's tool names or, with `--inventory`, the inventory's (`matcher gaps`) | e2e/matcher.test.ts, e2e/inventory.test.ts |
|
|
91
|
+
| N37 | Claude Code's hook contract is a committed snapshot extracted from the hooks reference page (events, no-matcher events, matcher rules, handler fields, "Requires vX"), with the minimum version's contract derived from it. The conformance constants match the snapshot, and every event and handler field the binding writes exists at the minimum version. `npm run drift` diffs the snapshot with the live page and exits 1 on drift | e2e/contract-drift.test.ts |
|
|
92
|
+
| N38 | A gate whose `before:` is a script step (an action with `commands:`) refuses it in Claude Code sessions. One generated `before-action.sh`, a PreToolUse Bash hook with `onFailure: "block"`, reads the command, ignores leading `VAR=value`, reads the unit with `unit-from`, and lets it through only when the LAST ledger line for the gate's `after:` step and unit has `ok: true` (missing ledger, other unit, later `ok: false`, bad JSON, unset `CLAUDE_PROJECT_DIR` all exit 2); a gate's `run:` wrapper runs too. `records`, `ledger`, `unit-from` parse and round-trip. V110 (nothing to check), V111 (bad `unit-from`), V112 (unknown ledger, unsafe record path, ambiguous ledger); V25 no longer warns for such a gate. `guard --check` reports refuse by hook, with the note that it covers Claude Code sessions only | e2e/action-guard.test.ts |
|
|
93
|
+
| N39 | `agentopology step check <file.at> <step-id> [--unit U] [--dir D]` lets a script step check itself when no hook guards it (a terminal, a scheduler). It exits 0 when every gate with `before: <step-id>` holds, else 1 naming each failing gate and why: with a ledger, the last record for the `after:` step (same unit, if given) is ok; with `run:`, it exits 0 from D (default `.`); with both, both; with neither, "nothing to check" (V110's case). An unguarded step exits 0 with "no gate guards <step>"; a step that is no node exits 1. It reads and runs check scripts and never writes | e2e/step-check.test.ts |
|
|
94
|
+
| N40 | `validate` ends every run (clean, warnings, errors, `--inventory`) with one dim line, `checked against Claude Code 2.1.319 (supports 2.1.295 and later)`, built from VERIFIED_ON and MINIMUM_VERSION in src/conformance/claude-code.ts; `scaffold --target claude-code` and the claude-code row of `targets` carry it too | e2e/verified-on.test.ts |
|
|
95
|
+
| N41 | `guard <file> --target claude-code` writes only the guard part of scaffold into a hand-built project: the scripts settings.json hook commands run, the gate wrappers they call and the checks the gates run (found by following script paths, not listed), and the `hooks` and `permissions` of settings.json merged by the scaffold merger. No agent, skill, command, rule, .mcp.json or CLAUDE.md is written; a user's hook, permission and other keys stay; a rerun changes nothing (not even the manifest) and adds no duplicates; an edited script and a check that is no longer a stub are kept and reported; `--dry-run` writes nothing; it then prints the `--check` report and exits like it | e2e/guard-target.test.ts |
|
|
96
|
+
| N42 | `before-action.sh` finds a guarded step anywhere in a chained Bash command, not only at the start of the whole line (measured: `cd repo && ./tool publish ep1`, `true; ./tool publish ep1` and `make x \|\| ./tool publish ep1` ran the step while `guard --check` reported the gate as refuse). It splits on `&&`, `\|\|`, `;`, `\|`, `&` and newlines (quoting is not parsed, which can only over-block), strips a leading `(`, `{`, `then`, `do`, `exec`, `time`, `nohup`, `env` and `VAR=value` per segment, reads `unit-from` from the matching segment, and checks every matching segment (two guarded steps in one command need both gates; a gate's check script that reads stdin does not hide a later step). `echo ./tool publish ep1` is not a step. `guard --check` notes that an indirect run (`bash -c`, `eval`, a script that calls the step) is not seen | e2e/action-guard.test.ts |
|
|
82
97
|
| L2 | Real Claude Code loads a hand-built project after import then scaffold | live/claude-code.live.test.ts |
|
|
83
98
|
| L3 | Real Claude Code registers a manual agent and an agent with a regex matcher and the phase-4 fields, registers `runs: fork`/`inline` nodes as skills, and runs a prompt hook | live/claude-code.live.test.ts |
|
|
84
99
|
| L1 | Real Claude Code loads every scaffolded example: every agent, skill and command registers, and the session succeeds | live/claude-code.live.test.ts |
|
|
@@ -98,3 +113,4 @@ real test and the row moves to Guarantees. Source: the compile audit,
|
|
|
98
113
|
| F4 | Agent `memory` scope (user/project/local) and `autoCompactWindow` are not first-class; `memory:` already names stores, so the scope needs another word | later |
|
|
99
114
|
| N25 | An agent's `role:` is documented (spec/grammar.md §4.6) but the parser never reads it: the role text comes only from the `roles` block by agent id, so `role: analyst` on `agent classifier` is lost | later |
|
|
100
115
|
| N26 | V102 does not look inside free-form or not-yet-listed blocks (`prompt`, `extensions`, `settings`, `env`, `memory`, `providers`, `mcp-servers`, `interfaces`, `triggers`, `depth`, `flow`, `defaults`, `observability`, `checkpoint`, ...): an unknown name there can still vanish | later |
|
|
116
|
+
| N35 | A second `hooks { }` block in a file is dropped without a word: the parser keeps the first block's hooks, validation reports nothing, and the second block's hooks are never scaffolded | later |
|
package/spec/validation.md
CHANGED
|
@@ -665,7 +665,7 @@ agent researcher {
|
|
|
665
665
|
|
|
666
666
|
## Summary Table
|
|
667
667
|
|
|
668
|
-
All
|
|
668
|
+
All 112 rules, generated from `src/parser/validator.ts` so the spec cannot drift
|
|
669
669
|
from the implementation. Rules V8, V12, V23, V24, V89, V90, V98 and V102 are collected at
|
|
670
670
|
**parse time** — the misuse they catch leaves no trace in the AST — and surfaced
|
|
671
671
|
through the validator like any other rule.
|
|
@@ -698,7 +698,7 @@ through the validator like any other rule.
|
|
|
698
698
|
| V22 | error/warning | Every model in a fallback-chain should exist in at least one provider's models list |
|
|
699
699
|
| V23 | error/warning | Duplicate top-level singleton sections |
|
|
700
700
|
| V24 | error/warning | Unknown sub-blocks in the `memory` section |
|
|
701
|
-
| V25 | error/warning | `on-fail: bounce-back` enforcement depends on the gate's `after` target |
|
|
701
|
+
| V25 | error/warning | `on-fail: bounce-back` enforcement depends on the gate's `after` target (a gate before an action with `commands:` is compiled to before-action.sh and does not warn) |
|
|
702
702
|
| V26 | error/warning | `action.kind` must be one of the allowed values |
|
|
703
703
|
| V27 | error/warning | `agent.permissions` should be one of the known values |
|
|
704
704
|
| V28 | error/warning | `metering.format` must be one of the allowed values |
|
|
@@ -777,6 +777,15 @@ through the validator like any other rule.
|
|
|
777
777
|
| V101 | error | An agent's `runs:` is `subagent`, `inline` or `fork`, and an `independent` node does not run `inline` (it would read the author's reasoning) |
|
|
778
778
|
| V102 | error | Every field and block name in a node, `meta`, a hook, job, artifact, tool or skill, and every topology section, is one the parser reads — a typo, a field of another node type or an unknown block vanished from the AST while validate passed. Contents of free-form blocks (`prompt`, `extensions`, `settings`, `env`, `memory`, ...) are not checked yet |
|
|
779
779
|
| V103 | error | An agent's or gate's `behavior:` is `advisory` or `blocking` — any other value was kept and emitted into the scaffold unchecked |
|
|
780
|
+
| V104 | error | `strength:` (on a gate, a hook or a flow edge) is `refuse`, `detect`, `hope` or `none`, and a gate's `by:` is `code`, `hook`, `settings` or `gate` |
|
|
781
|
+
| V105 | error | A flow edge's `guard:` names a hook in `hooks { }` or a gate in `gates { }`, so an edge is never credited with a refusal nothing provides |
|
|
782
|
+
| V106 | error | An `at:` citation is a safe project-relative `"path"` or `"path:line"`, and a gate's `match:` has an `at:` to be looked for in. Whether the file, line and match text exist is read by `guard --check`, never by validate |
|
|
783
|
+
| V107 | error | A hook matcher can match: not permission-rule syntax (`Bash(rm -rf *)`, which belongs in `if:`), not the dotted `mcp.server.*` form, not a bare `mcp__server` (which matches no tool), and a regex that compiles. A hook's `if:` sits on a tool event, the only ones Claude Code reads it on |
|
|
784
|
+
| V108 | warning | A hook's `run: "exit N"` with N other than 0 or 2 does not block: only exit 2 blocks |
|
|
785
|
+
| V109 | warning | A hook with `strength: refuse` on PreToolUse or PermissionRequest that matches a tool of an MCP server matches every tool of that server the topology names (`validate --inventory` adds the inventory's tool names) |
|
|
786
|
+
| V110 | warning | A gate before an action with `commands:` has neither a ledger (explicit, or the only record) nor `run:`, so there is nothing to check |
|
|
787
|
+
| V111 | error | An action's `unit-from` is not a valid regex, or has no capture group |
|
|
788
|
+
| V112 | error | A gate's `ledger:` names no declared record; a record path is unsafe (`{unit}` allowed); or a gate before an action needs a ledger, names none, and several records exist |
|
|
780
789
|
|
|
781
790
|
> Sections above document the first 35 rules with worked examples. The rest are
|
|
782
791
|
> single-line entries here plus their doc comment in the source. Adding an
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"zz-debug.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/live/zz-debug.test.ts"],"names":[],"mappings":""}
|
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
import { it } from "vitest";
|
|
2
|
-
import { spawnSync } from "node:child_process";
|
|
3
|
-
it("debug", () => { const r = spawnSync("sh", ["-c", "command -v claude; claude --version; echo $PATH | tr ':' '\n' | head -5"], { encoding: "utf8" }); console.log(r.stdout, r.stderr); });
|
|
4
|
-
//# sourceMappingURL=zz-debug.test.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"zz-debug.test.js","sourceRoot":"","sources":["../../../src/__tests__/live/zz-debug.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAC5B,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAC/C,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE,GAAG,MAAM,CAAC,GAAG,SAAS,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,yEAAyE,CAAC,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC"}
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* GitHub Copilot CLI binding.
|
|
3
|
-
*
|
|
4
|
-
* Generates the `.github/` directory structure for GitHub Copilot CLI
|
|
5
|
-
* (terminal agent) and Copilot Coding Agent (server-side):
|
|
6
|
-
* - `.github/copilot-instructions.md` — project-level instructions
|
|
7
|
-
* - `.github/instructions/{id}.instructions.md` — per-agent scoped instructions
|
|
8
|
-
* - `.github/agents/{id}.agent.md` — per-agent definitions with tools and prompt
|
|
9
|
-
* - `.github/copilot/settings.json` — CLI config (MCP, permissions)
|
|
10
|
-
* - `.github/skills/{name}/SKILL.md` — agent skills
|
|
11
|
-
* - `.github/hooks/{name}.json` — preToolUse/postToolUse hooks
|
|
12
|
-
* - `.github/workflows/copilot-setup-steps.yml` — server-side env setup
|
|
13
|
-
* - `AGENTS.md` — topology overview
|
|
14
|
-
*
|
|
15
|
-
* @module
|
|
16
|
-
*/
|
|
17
|
-
import type { BindingTarget } from "./types.js";
|
|
18
|
-
/** GitHub Copilot CLI binding. */
|
|
19
|
-
export declare const copilotCliBinding: BindingTarget;
|
|
20
|
-
//# sourceMappingURL=copilot-cli.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"copilot-cli.d.ts","sourceRoot":"","sources":["../../src/bindings/copilot-cli.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAeH,OAAO,KAAK,EAAE,aAAa,EAAiB,MAAM,YAAY,CAAC;AAm/C/D,kCAAkC;AAClC,eAAO,MAAM,iBAAiB,EAAE,aAiF/B,CAAC"}
|