@event4u/agent-config 1.39.0 → 1.40.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/.agent-src/commands/orchestrate.md +123 -0
- package/.agent-src/commands/sync-gitignore/fix.md +135 -0
- package/.agent-src/commands/sync-gitignore.md +31 -5
- package/.agent-src/skills/learning-to-rule-or-skill/SKILL.md +30 -2
- package/.agent-src/skills/subagent-orchestration/SKILL.md +9 -0
- package/.agent-src/skills/using-git-worktrees/SKILL.md +25 -0
- package/.agent-src/templates/agent-settings.md +9 -0
- package/.agent-src/templates/scripts/work_engine/orchestration.py +168 -0
- package/.claude-plugin/marketplace.json +3 -1
- package/CHANGELOG.md +42 -0
- package/README.md +5 -5
- package/bin/install.php +13 -6
- package/config/agent-settings.template.yml +21 -0
- package/docs/DISTRIBUTION_CHECKLIST.md +169 -0
- package/docs/architecture.md +1 -1
- package/docs/catalog.md +3 -2
- package/docs/contracts/audit-log-v1.md +142 -0
- package/docs/contracts/command-clusters.md +2 -0
- package/docs/contracts/file-ownership-matrix.json +20 -0
- package/docs/contracts/orchestration-dsl-v1.md +152 -0
- package/docs/getting-started.md +1 -1
- package/docs/installation.md +132 -0
- package/docs/setup/per-ide/aider.md +48 -0
- package/docs/setup/per-ide/claude-code.md +108 -0
- package/docs/setup/per-ide/claude-desktop.md +148 -0
- package/docs/setup/per-ide/cline.md +43 -0
- package/docs/setup/per-ide/codex.md +46 -0
- package/docs/setup/per-ide/copilot.md +80 -0
- package/docs/setup/per-ide/cursor.md +125 -0
- package/docs/setup/per-ide/gemini-cli.md +45 -0
- package/docs/setup/per-ide/windsurf.md +120 -0
- package/package.json +1 -1
- package/scripts/compress.py +153 -1
- package/scripts/extract_audit_patterns.py +202 -0
- package/scripts/install +156 -1
- package/scripts/install.py +270 -10
- package/scripts/install.sh +52 -7
- package/scripts/lint_orchestration_dsl.py +214 -0
- package/scripts/skill_linter.py +9 -0
- package/scripts/sync_gitignore.py +56 -1
- package/templates/claude_desktop_config.json.template +21 -0
- package/templates/cursor-rule.mdc.j2 +7 -0
- package/templates/global-install-manifest.yml +91 -0
- package/templates/marketing-copy.yml +64 -0
- package/templates/windsurf-rule.md.j2 +7 -0
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: orchestrate
|
|
3
|
+
cluster: orchestrate
|
|
4
|
+
skills: [subagent-orchestration]
|
|
5
|
+
description: Run a YAML pipeline defined under `.agent-config/orchestrations/` — chains personas / skills / commands / sub-agents per the orchestration-dsl-v1 contract
|
|
6
|
+
disable-model-invocation: true
|
|
7
|
+
suggestion:
|
|
8
|
+
eligible: true
|
|
9
|
+
trigger_description: "run a saved orchestration / pipeline / chain"
|
|
10
|
+
trigger_context: "user names a pipeline file or asks to replay a chain"
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# orchestrate
|
|
14
|
+
|
|
15
|
+
## Instructions
|
|
16
|
+
|
|
17
|
+
Execute a YAML pipeline file from `.agent-config/orchestrations/`
|
|
18
|
+
against the current workspace. Pipelines are deterministic chains of
|
|
19
|
+
personas, skills, commands, and sub-agents pinned by the
|
|
20
|
+
[`orchestration-dsl-v1`](../docs/contracts/orchestration-dsl-v1.md)
|
|
21
|
+
contract.
|
|
22
|
+
|
|
23
|
+
This command is the **runtime** side of the contract. The schema and
|
|
24
|
+
the linter (`scripts/lint_orchestration_dsl.py`) live on the authoring
|
|
25
|
+
side; this command reads the same shape and dispatches each step.
|
|
26
|
+
|
|
27
|
+
### 1. Resolve the pipeline file
|
|
28
|
+
|
|
29
|
+
- The user passes either a pipeline name (`pr-readiness-check`) or a
|
|
30
|
+
path (`.agent-config/orchestrations/pr-readiness-check.yaml`).
|
|
31
|
+
- Resolve to a path under `.agent-config/orchestrations/`. Refuse
|
|
32
|
+
paths outside that directory — pipelines live in one place.
|
|
33
|
+
- If the file does not exist, list the available pipelines and stop.
|
|
34
|
+
|
|
35
|
+
### 2. Validate before run
|
|
36
|
+
|
|
37
|
+
Run the linter against the resolved file:
|
|
38
|
+
|
|
39
|
+
```
|
|
40
|
+
python3 scripts/lint_orchestration_dsl.py --file <path>
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Exit code ≠ 0 → surface the linter output and stop. **Never** run
|
|
44
|
+
a pipeline that fails its own schema check.
|
|
45
|
+
|
|
46
|
+
### 3. Collect inputs
|
|
47
|
+
|
|
48
|
+
For each `inputs[]` entry in the pipeline:
|
|
49
|
+
|
|
50
|
+
- If the user supplied a value on invocation (`/orchestrate pr-readiness-check diff_target=feature/x`)
|
|
51
|
+
use it.
|
|
52
|
+
- Else use the `default` field.
|
|
53
|
+
- Else ask **one** question per missing input, in order. Stop after
|
|
54
|
+
the first unanswered required input — pipelines are batch-friendly
|
|
55
|
+
by design, but `ask-when-uncertain` still applies.
|
|
56
|
+
|
|
57
|
+
### 4. Dispatch the steps
|
|
58
|
+
|
|
59
|
+
Walk `steps[]` in order. For each step:
|
|
60
|
+
|
|
61
|
+
| `kind` | Dispatch path |
|
|
62
|
+
|---|---|
|
|
63
|
+
| `skill` | Invoke the skill identified by `ref` with the resolved `with` block. |
|
|
64
|
+
| `command` | Run the slash-command identified by `ref` as if the user had typed it. |
|
|
65
|
+
| `persona` | Set `roles.active_role` to `ref` for the next dependent step; does not produce its own `output`. |
|
|
66
|
+
| `subagent` | Delegate to [`subagent-orchestration`](../skills/subagent-orchestration/SKILL.md) using `ref` as the mode name. |
|
|
67
|
+
|
|
68
|
+
Capture each step's output in an in-memory `outputs[step.id]` map.
|
|
69
|
+
`${{ inputs.X }}` and `${{ steps.Y.output }}` are substituted via
|
|
70
|
+
string replacement only — no expressions, no shell-out.
|
|
71
|
+
|
|
72
|
+
### 5. Honour `when`
|
|
73
|
+
|
|
74
|
+
If a step has a `when` field, evaluate it as one of:
|
|
75
|
+
|
|
76
|
+
- `${{ steps.X.output }} == "<literal>"`
|
|
77
|
+
- `steps.X.success` / `steps.X.failure`
|
|
78
|
+
|
|
79
|
+
Anything else → stop the run with a clear error. The DSL is
|
|
80
|
+
deliberately tiny; richer logic belongs in a skill, not in the
|
|
81
|
+
pipeline file.
|
|
82
|
+
|
|
83
|
+
### 6. Halt on hard failure
|
|
84
|
+
|
|
85
|
+
A step failure ends the pipeline immediately. Surface:
|
|
86
|
+
|
|
87
|
+
- the failing step id and kind/ref
|
|
88
|
+
- the error or non-zero exit
|
|
89
|
+
- the steps that ran cleanly before it
|
|
90
|
+
|
|
91
|
+
Do **not** continue past a failure unless a downstream step has a
|
|
92
|
+
`when: steps.X.failure` guard explicitly authorizing it.
|
|
93
|
+
|
|
94
|
+
### 7. Produce the delivery report
|
|
95
|
+
|
|
96
|
+
When the pipeline reaches the end of `steps[]`:
|
|
97
|
+
|
|
98
|
+
- Resolve every `outputs[name]` entry by substituting the captured
|
|
99
|
+
step outputs.
|
|
100
|
+
- Print a Markdown delivery report:
|
|
101
|
+
- pipeline name + resolved input values
|
|
102
|
+
- per-step verdict (✅ / ❌, ref, one-line summary)
|
|
103
|
+
- the resolved `outputs:` map at the bottom
|
|
104
|
+
|
|
105
|
+
### 8. Audit trail
|
|
106
|
+
|
|
107
|
+
Per [`audit-log-v1`](../docs/contracts/audit-log-v1.md), append
|
|
108
|
+
one JSONL entry per step boundary to the current month's audit file
|
|
109
|
+
under `agents/state/audit/`. Counts + ids only — never the step's
|
|
110
|
+
output body.
|
|
111
|
+
|
|
112
|
+
### 9. What this command does NOT do
|
|
113
|
+
|
|
114
|
+
- Edit the pipeline file. Authoring is human or skill-driven.
|
|
115
|
+
- Commit, push, or open PRs. Those gates live elsewhere.
|
|
116
|
+
- Branch or invent steps. The pipeline file is the source of truth.
|
|
117
|
+
|
|
118
|
+
## See also
|
|
119
|
+
|
|
120
|
+
- Contract: [`orchestration-dsl-v1.md`](../docs/contracts/orchestration-dsl-v1.md)
|
|
121
|
+
- Linter: `scripts/lint_orchestration_dsl.py`
|
|
122
|
+
- Subagent runtime: [`subagent-orchestration`](../skills/subagent-orchestration/SKILL.md)
|
|
123
|
+
- Audit emission: [`audit-log-v1.md`](../docs/contracts/audit-log-v1.md)
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: sync-gitignore:fix
|
|
3
|
+
cluster: sync-gitignore
|
|
4
|
+
sub: fix
|
|
5
|
+
description: Scrub legacy pre-`/agents/` patterns from the consumer's .gitignore (inside or outside the managed block) and re-sync the canonical entries
|
|
6
|
+
disable-model-invocation: true
|
|
7
|
+
suggestion:
|
|
8
|
+
eligible: true
|
|
9
|
+
trigger_description: "fix .gitignore garbage, clean up legacy agent-config entries, my .gitignore has stale agent entries, /sync-gitignore is not picking up the right paths"
|
|
10
|
+
trigger_context: "consumer project carries pre-/agents/ runtime patterns (.agent-chat-history, .agent-prices.md) at the root from an older install"
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# /sync-gitignore:fix
|
|
14
|
+
|
|
15
|
+
Cleanup sibling of [`/sync-gitignore`](../sync-gitignore.md). Strips
|
|
16
|
+
legacy root-level patterns (pre-`/agents/` runtime artefacts —
|
|
17
|
+
`.agent-chat-history`, `.agent-chat-history.bak`,
|
|
18
|
+
`.agent-chat-history.*.bak`, `.agent-prices.md`, `.council-tmp/`) from
|
|
19
|
+
**anywhere** in the consumer's `.gitignore` — inside or outside the
|
|
20
|
+
managed block — then re-runs the regular sync so the current canonical
|
|
21
|
+
`/agents/`-prefixed entries land in the block.
|
|
22
|
+
|
|
23
|
+
Use when:
|
|
24
|
+
|
|
25
|
+
- An older installer (pre-May 2026) dropped root-level `.agent-chat-history`
|
|
26
|
+
/ `.agent-prices.md` lines that the current scripts no longer recognise.
|
|
27
|
+
- A hand-edit added one of those legacy paths and it now conflicts with
|
|
28
|
+
the managed `/agents/...` entry.
|
|
29
|
+
- `/sync-gitignore` reports "already in sync" but git is still ignoring
|
|
30
|
+
files at the wrong paths.
|
|
31
|
+
|
|
32
|
+
## When NOT to use
|
|
33
|
+
|
|
34
|
+
- To remove **user-added** lines from inside the block → that is
|
|
35
|
+
`--replace` on the base command, and it is destructive. This
|
|
36
|
+
sub-command only touches the legacy pattern list — nothing else.
|
|
37
|
+
- To delete the entire managed block → do it by hand.
|
|
38
|
+
- To migrate runtime files themselves (move `.agent-chat-history` →
|
|
39
|
+
`agents/.agent-chat-history`) → the installer's
|
|
40
|
+
`migrate_legacy_root_infra` step handles that. This sub-command only
|
|
41
|
+
fixes `.gitignore`.
|
|
42
|
+
|
|
43
|
+
## Steps
|
|
44
|
+
|
|
45
|
+
### 1. Locate script and target
|
|
46
|
+
|
|
47
|
+
Same resolution order as [`/sync-gitignore`](../sync-gitignore.md):
|
|
48
|
+
|
|
49
|
+
1. `./agent-config/scripts/sync_gitignore.py`
|
|
50
|
+
2. `vendor/event4u/agent-config/scripts/sync_gitignore.py`
|
|
51
|
+
3. `node_modules/@event4u/agent-config/scripts/sync_gitignore.py`
|
|
52
|
+
|
|
53
|
+
Target is `<project_root>/.gitignore`. If no `.gitignore` exists,
|
|
54
|
+
stop — there is nothing to fix:
|
|
55
|
+
|
|
56
|
+
```
|
|
57
|
+
> 📝 No .gitignore found at <project_root>. Nothing to clean up.
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### 2. Dry-run with cleanup
|
|
61
|
+
|
|
62
|
+
Run:
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
python3 <script> --cleanup-legacy --dry-run
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Capture stdout (unified diff) and stderr (summary line listing the
|
|
69
|
+
legacy entries that would be removed). Three outcomes:
|
|
70
|
+
|
|
71
|
+
- **Nothing legacy + block in sync** → tell the user and stop:
|
|
72
|
+
```
|
|
73
|
+
> ✅ .gitignore already clean — no legacy patterns, block in sync.
|
|
74
|
+
```
|
|
75
|
+
- **Diff produced** → show it and ask:
|
|
76
|
+
```
|
|
77
|
+
> 🧹 /sync-gitignore:fix would clean up .gitignore:
|
|
78
|
+
>
|
|
79
|
+
> {diff}
|
|
80
|
+
>
|
|
81
|
+
> Summary: would remove {N} legacy entr{y|ies}: {names}
|
|
82
|
+
> would add {M} entr{y|ies} to the managed block
|
|
83
|
+
>
|
|
84
|
+
> 1. Apply — write the changes
|
|
85
|
+
> 2. Skip — leave .gitignore untouched
|
|
86
|
+
```
|
|
87
|
+
- **Script error** (exit 2) → print the error and stop; do not prompt.
|
|
88
|
+
|
|
89
|
+
### 3. Act on the choice
|
|
90
|
+
|
|
91
|
+
- `1` (Apply) → re-run **without** `--dry-run`:
|
|
92
|
+
```bash
|
|
93
|
+
python3 <script> --cleanup-legacy
|
|
94
|
+
```
|
|
95
|
+
Confirm with the script's own summary lines (removed-legacy count and
|
|
96
|
+
added-entries count both surface there).
|
|
97
|
+
- `2` (Skip) → stop. No changes made.
|
|
98
|
+
|
|
99
|
+
Free-text replies (`"nö"`, `"leave it"`, unrecognized input) count as
|
|
100
|
+
`2`. Never write on ambiguous input.
|
|
101
|
+
|
|
102
|
+
### 4. Suggest the migration check (informational, do NOT auto-run)
|
|
103
|
+
|
|
104
|
+
If the cleanup removed `.agent-chat-history` or `.agent-prices.md`,
|
|
105
|
+
mention that the **file** at the root (if still present) may need to
|
|
106
|
+
move to `agents/`. The installer does this automatically via
|
|
107
|
+
[`migrate_legacy_root_infra`](../../../scripts/install.sh); the
|
|
108
|
+
agent does not run it from this command. One line of guidance is
|
|
109
|
+
enough:
|
|
110
|
+
|
|
111
|
+
```
|
|
112
|
+
> ℹ️ If `.agent-chat-history` still sits at the project root, re-run the installer (or move it to `agents/.agent-chat-history` by hand) so the runtime can find it again.
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## Rules
|
|
116
|
+
|
|
117
|
+
- **Append-only by default** — `--cleanup-legacy` removes legacy
|
|
118
|
+
patterns only. User-added non-legacy lines (inside or outside the
|
|
119
|
+
block) survive untouched.
|
|
120
|
+
- **Never combine with `--replace`** — the destructive full-block
|
|
121
|
+
rewrite is a separate concern; mixing the two surprises users.
|
|
122
|
+
- **Dry-run first, always** — the user must see the diff before any
|
|
123
|
+
write.
|
|
124
|
+
- **Do NOT push, commit, or modify other files** — this command writes
|
|
125
|
+
to `.gitignore` only.
|
|
126
|
+
|
|
127
|
+
## See also
|
|
128
|
+
|
|
129
|
+
- [`/sync-gitignore`](../sync-gitignore.md) — append-only sync of the
|
|
130
|
+
managed block (no legacy cleanup)
|
|
131
|
+
- [`scripts/sync_gitignore.py`](../../../scripts/sync_gitignore.py) —
|
|
132
|
+
the helper (`--cleanup-legacy` flag)
|
|
133
|
+
- [`scripts/install.sh`](../../../scripts/install.sh) —
|
|
134
|
+
`migrate_legacy_root_infra` (moves the **files**, complement to this
|
|
135
|
+
command which fixes the **ignore rules**)
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: sync-gitignore
|
|
3
|
+
cluster: sync-gitignore
|
|
3
4
|
description: Sync the `event4u/agent-config` block in the consumer project's .gitignore — adds missing entries, preserves user-added lines, shows a diff before writing
|
|
4
5
|
disable-model-invocation: true
|
|
5
6
|
suggestion:
|
|
@@ -9,6 +10,28 @@ suggestion:
|
|
|
9
10
|
|
|
10
11
|
# /sync-gitignore
|
|
11
12
|
|
|
13
|
+
Top-level entry point for the `/sync-gitignore` family. Bare `/sync-gitignore`
|
|
14
|
+
runs the interactive append-only sync described below. The `:fix`
|
|
15
|
+
sub-command additionally scrubs legacy patterns (pre-`/agents/` layout)
|
|
16
|
+
from anywhere in the consumer's `.gitignore` before re-syncing.
|
|
17
|
+
|
|
18
|
+
## Sub-commands
|
|
19
|
+
|
|
20
|
+
| Sub-command | Routes to | Purpose |
|
|
21
|
+
|---|---|---|
|
|
22
|
+
| `/sync-gitignore` (bare) | this file (`## Default flow`) | Interactive — append-only sync of the managed block, dry-run preview, confirm before write |
|
|
23
|
+
| `/sync-gitignore:fix` | `commands/sync-gitignore/fix.md` | Cleanup — strip legacy root-level patterns (pre-`/agents/` layout) wherever they appear, then sync |
|
|
24
|
+
|
|
25
|
+
## Dispatch
|
|
26
|
+
|
|
27
|
+
1. Parse the user's argument: `/sync-gitignore[:<sub>] [args]`.
|
|
28
|
+
2. Bare `/sync-gitignore` → run the `## Default flow` below verbatim.
|
|
29
|
+
3. `/sync-gitignore:fix` → load `commands/sync-gitignore/fix.md` and follow
|
|
30
|
+
its `## Steps` section verbatim.
|
|
31
|
+
4. Unknown sub-command → print the table above and ask which one.
|
|
32
|
+
|
|
33
|
+
## Default flow
|
|
34
|
+
|
|
12
35
|
Ensures the consumer project's `.gitignore` contains every entry the
|
|
13
36
|
package expects to be ignored (symlinked `.augment/` subdirectories,
|
|
14
37
|
`/agent-config` CLI wrapper, `.agent-settings*`, `agents/.agent-chat-history*`).
|
|
@@ -23,7 +46,7 @@ Use when:
|
|
|
23
46
|
setup, or installer ran with `--skip-gitignore`).
|
|
24
47
|
- You want to audit what the block **should** look like without writing.
|
|
25
48
|
|
|
26
|
-
|
|
49
|
+
### When NOT to use
|
|
27
50
|
|
|
28
51
|
- To disable logging or change what is logged → that is
|
|
29
52
|
`chat_history.enabled` in `.agent-settings.yml`, not `.gitignore`.
|
|
@@ -31,8 +54,8 @@ Use when:
|
|
|
31
54
|
re-remove its own entries.
|
|
32
55
|
- To change what the block contains → edit
|
|
33
56
|
`config/gitignore-block.txt` in the package repo and re-release.
|
|
34
|
-
|
|
35
|
-
|
|
57
|
+
- To clean up legacy garbage from older installs → use
|
|
58
|
+
[`/sync-gitignore:fix`](sync-gitignore/fix.md) instead.
|
|
36
59
|
|
|
37
60
|
### 1. Locate script and target
|
|
38
61
|
|
|
@@ -88,9 +111,11 @@ Free-text replies (`"nö"`, `"leave it"`, unrecognized input) count as
|
|
|
88
111
|
Do **not** suggest `--replace` by default. It rewrites the block in
|
|
89
112
|
full and drops user-added lines inside the block — destructive.
|
|
90
113
|
Mention it only if the user explicitly asks to clean up or reset the
|
|
91
|
-
block, and confirm once more before running it.
|
|
114
|
+
block, and confirm once more before running it. For removing legacy
|
|
115
|
+
root-level patterns (not user-added lines), prefer
|
|
116
|
+
[`/sync-gitignore:fix`](sync-gitignore/fix.md).
|
|
92
117
|
|
|
93
|
-
##
|
|
118
|
+
## Rules
|
|
94
119
|
|
|
95
120
|
- The script honors the explicit `# event4u/agent-config — END` marker.
|
|
96
121
|
Legacy blocks without it get the marker added automatically on the
|
|
@@ -99,6 +124,7 @@ block, and confirm once more before running it.
|
|
|
99
124
|
They do not survive `--replace`.
|
|
100
125
|
- Changes to `config/gitignore-block.txt` require a package update in
|
|
101
126
|
the consumer project before this command can apply them.
|
|
127
|
+
- **Do NOT chain sub-commands.** One `/sync-gitignore <sub>` per turn.
|
|
102
128
|
|
|
103
129
|
## See also
|
|
104
130
|
|
|
@@ -21,6 +21,11 @@ Use this skill when:
|
|
|
21
21
|
* Reviewing post-task learnings or retrospectives
|
|
22
22
|
* Deciding whether a learning belongs in a rule or a skill
|
|
23
23
|
* After completing a task — reflecting on what worked or caused friction
|
|
24
|
+
* Mining the audit log (`agents/state/audit/<YYYY-MM>.jsonl`,
|
|
25
|
+
[`audit-log-v1`](../../../docs/contracts/audit-log-v1.md)) surfaced
|
|
26
|
+
a repeated phase pattern via
|
|
27
|
+
[`extract_audit_patterns.py`](../../../scripts/extract_audit_patterns.py)
|
|
28
|
+
— the pattern's `count` ≥ 2 already satisfies the repetition gate
|
|
24
29
|
|
|
25
30
|
Do not use this skill when:
|
|
26
31
|
|
|
@@ -206,8 +211,10 @@ Mandatory fields the draft MUST fill:
|
|
|
206
211
|
* `source_learning` — path to the `agents/learnings/<date>-<slug>.md`
|
|
207
212
|
file this proposal was captured from
|
|
208
213
|
* `evidence` — **at least two independent** references (PR, issue,
|
|
209
|
-
incident, review-comment, test-failure
|
|
210
|
-
|
|
214
|
+
incident, review-comment, test-failure, **or audit-log line ids**
|
|
215
|
+
per [`audit-log-v1`](../../../docs/contracts/audit-log-v1.md));
|
|
216
|
+
entries that all resolve to the same PR or the same audit-log
|
|
217
|
+
`run_id` are rejected by the gate (independence floor)
|
|
211
218
|
* `Proposed artefact` (§4) — the full draft body, no `TODO` / `TBD`
|
|
212
219
|
* `Success signal` (§7) — one metric, one baseline, one target, one
|
|
213
220
|
evaluation date
|
|
@@ -295,6 +302,27 @@ audio) goes through the upstream `markitdown-mcp` server first; only
|
|
|
295
302
|
write a custom extractor if `markitdown` cannot handle the format and
|
|
296
303
|
the gap is documented in its skill body.
|
|
297
304
|
|
|
305
|
+
## Audit-derived learnings (optional source)
|
|
306
|
+
|
|
307
|
+
When the input is a pattern surfaced by
|
|
308
|
+
[`extract_audit_patterns.py`](../../../scripts/extract_audit_patterns.py)
|
|
309
|
+
mining `agents/state/audit/<YYYY-MM>.jsonl`
|
|
310
|
+
([`audit-log-v1`](../../../docs/contracts/audit-log-v1.md)):
|
|
311
|
+
|
|
312
|
+
1. Treat the script's pattern record as the **State the learning**
|
|
313
|
+
step input (§1) — `pattern.summary` is the one-sentence
|
|
314
|
+
statement, `pattern.line_ids` is the evidence.
|
|
315
|
+
2. The repetition gate is already satisfied for `count ≥ 2`. Skip
|
|
316
|
+
to §3 (decide the target) — overlap check (§4) and proposal
|
|
317
|
+
draft (§8) remain mandatory.
|
|
318
|
+
3. Independence floor still applies: two line ids from the same
|
|
319
|
+
`run_id` count as **one** piece of evidence. The mining script
|
|
320
|
+
already de-duplicates by `run_id`; the gate trusts that output.
|
|
321
|
+
4. Audit-derived proposals MUST set
|
|
322
|
+
`source_learning: agents/state/audit/<YYYY-MM>.jsonl#<line_ids>`
|
|
323
|
+
and link the mining-script run id, so the human reviewer can
|
|
324
|
+
reproduce the pattern from the raw audit log.
|
|
325
|
+
|
|
298
326
|
## Environment notes
|
|
299
327
|
|
|
300
328
|
Prefer updating existing rule/skill when possible.
|
|
@@ -240,6 +240,15 @@ prefer the cheaper one (`do-and-judge` < `do-and-judge-two-stage` <
|
|
|
240
240
|
`do-in-steps` < `do-in-parallel` < `do-competitively` <
|
|
241
241
|
`judge-with-debate` < `do-in-worktrees`).
|
|
242
242
|
|
|
243
|
+
**Mode 6 (`do-in-worktrees`) is gated by `worktrees.mode`** from
|
|
244
|
+
`.agent-settings.yml` (default: `ask`). Resolve before picking:
|
|
245
|
+
|
|
246
|
+
| `worktrees.mode` | Mode 6 |
|
|
247
|
+
|---|---|
|
|
248
|
+
| `ask` | Eligible. `using-git-worktrees` will run the per-creation permission ask. |
|
|
249
|
+
| `on` | Eligible. Per-creation ask suppressed. |
|
|
250
|
+
| `off` | **Not eligible.** Fall back to mode 3 (`do-in-steps`) — same step-by-step chain, in-place on the current branch. Unless the user **explicitly asked this turn** for a worktree chain, in which case proceed with mode 6 and acknowledge the override per [`using-git-worktrees § Pre-flight`](../using-git-worktrees/SKILL.md). |
|
|
251
|
+
|
|
243
252
|
### 4. Dispatch
|
|
244
253
|
|
|
245
254
|
Hand off to the matching command:
|
|
@@ -44,6 +44,31 @@ work and makes it impossible to tell what you broke.
|
|
|
44
44
|
|
|
45
45
|
## Procedure
|
|
46
46
|
|
|
47
|
+
### 0. Pre-flight — read `worktrees.mode`
|
|
48
|
+
|
|
49
|
+
Before anything else, read `worktrees.mode` from `.agent-settings.yml`
|
|
50
|
+
(default: `ask`). The setting is a **mechanical layer on top of**
|
|
51
|
+
`scope-control`'s permission gate — it narrows, never widens.
|
|
52
|
+
|
|
53
|
+
| `worktrees.mode` | Behaviour |
|
|
54
|
+
|---|---|
|
|
55
|
+
| `ask` | Status quo. Continue to step 1; `scope-control` permission gate applies for every worktree creation. |
|
|
56
|
+
| `on` | Standing permission. Skip the per-creation permission ask; continue to step 1. Iron-Law gates (ignore-check, clean baseline) still apply. |
|
|
57
|
+
| `off` | No autonomous worktree creation. **Refuse** unless the user explicitly asked **this turn** for a worktree ("do this in a worktree", "use mode 6", "spawn a worktree for X"). |
|
|
58
|
+
|
|
59
|
+
**Off, no explicit request** → stop. Tell the user the setting is `off`,
|
|
60
|
+
suggest the in-place alternative (`subagent-orchestration` mode 3
|
|
61
|
+
`do-in-steps`, or just stay on the current branch). Do not re-ask on
|
|
62
|
+
the same task.
|
|
63
|
+
|
|
64
|
+
**Off, with explicit request this turn** → acknowledge once
|
|
65
|
+
("`worktrees.mode` is `off`; running this on your explicit request
|
|
66
|
+
for this task") and continue to step 1. The override is for this one
|
|
67
|
+
task — it does not flip the setting.
|
|
68
|
+
|
|
69
|
+
The setting only suppresses **unprompted** usage. The tool stays
|
|
70
|
+
available when the user wants it.
|
|
71
|
+
|
|
47
72
|
### 1. Inspect current state
|
|
48
73
|
|
|
49
74
|
Before creating anything, check existing conventions — do not assume:
|
|
@@ -230,6 +230,14 @@ subagents:
|
|
|
230
230
|
# Set to 1 to serialize. Hard cap enforced by runtime.
|
|
231
231
|
max_parallel: 3
|
|
232
232
|
|
|
233
|
+
# --- Git worktrees ---
|
|
234
|
+
worktrees:
|
|
235
|
+
# off | on | ask (default: ask)
|
|
236
|
+
# off = no autonomous worktree creation (explicit user request overrides)
|
|
237
|
+
# on = standing permission (skill skips the per-creation ask)
|
|
238
|
+
# ask = status quo — skill asks before creating
|
|
239
|
+
mode: ask
|
|
240
|
+
|
|
233
241
|
# --- Role modes (see guidelines/agent-infra/role-contracts.md) ---
|
|
234
242
|
roles:
|
|
235
243
|
# Role the agent defaults to at the start of a session.
|
|
@@ -442,6 +450,7 @@ the canonical narrative lives in
|
|
|
442
450
|
| `subagents.implementer_model` | model alias or empty | _(empty)_ | Model for implementer subagents. Empty = same tier as session model. See [subagent-configuration](../contexts/subagent-configuration.md). |
|
|
443
451
|
| `subagents.judge_model` | model alias or empty | _(empty)_ | Model for judge subagents. Empty = one tier above implementer (opus if sonnet, sonnet if haiku). |
|
|
444
452
|
| `subagents.max_parallel` | integer | `3` | Maximum parallel subagent invocations. `1` serializes. |
|
|
453
|
+
| `worktrees.mode` | `off`, `on`, `ask` | `ask` | Controls autonomous `git worktree` usage. `off` = skill refuses unless the user explicitly asks for a worktree that turn (then it runs); `subagent-orchestration` mode 6 falls back to mode 3. `on` = standing permission (skill skips the per-creation ask; ignore-check and clean-baseline gates still apply). `ask` = status quo — `scope-control` permission gate runs every time. |
|
|
445
454
|
| `roles.default_role` | `""`, `developer`, `reviewer`, `tester`, `po`, `incident`, `planner` | _(empty)_ | Role the agent defaults to at the start of a session. See [`role-contracts`](../docs/guidelines/agent-infra/role-contracts.md). |
|
|
446
455
|
| `roles.active_role` | same as `default_role` | _(empty)_ | Role currently active; set by `/mode <name>`, cleared by `/mode none`. Enables the `role-mode-adherence` rule. |
|
|
447
456
|
| `personas.override` | list of persona ids | `[]` | Developer-local override of the team default lens cast. Empty = inherit `personas.default` from `.agent-project-settings.yml`. See [`layered-settings`](../docs/guidelines/agent-infra/layered-settings.md). |
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
"""State machine stub for the ``/orchestrate`` command.
|
|
2
|
+
|
|
3
|
+
Reads a pipeline file conforming to
|
|
4
|
+
``docs/contracts/orchestration-dsl-v1.md`` and produces an ordered
|
|
5
|
+
sequence of step descriptors the agent dispatches one at a time.
|
|
6
|
+
The runtime itself is **not** in Python — each step is executed by the
|
|
7
|
+
agent via skill / command / persona / subagent dispatch. This module
|
|
8
|
+
holds the deterministic bookkeeping:
|
|
9
|
+
|
|
10
|
+
- load + interpolate
|
|
11
|
+
- step iteration with success / failure / when-guard tracking
|
|
12
|
+
- output-map resolution at the end
|
|
13
|
+
|
|
14
|
+
Design constraints (R1 carve-outs from
|
|
15
|
+
``road-to-distribution-and-adoption.md``):
|
|
16
|
+
|
|
17
|
+
- No external dependencies. YAML loading reuses the dispatcher's
|
|
18
|
+
loader so the runtime sees what the linter sees.
|
|
19
|
+
- No side effects. The state machine never edits files, runs commands,
|
|
20
|
+
or emits hooks of its own. Audit emission is the caller's job.
|
|
21
|
+
- Forward-ref free. ``steps[].with`` references can only reach
|
|
22
|
+
earlier steps; this is enforced both by the linter and at runtime.
|
|
23
|
+
"""
|
|
24
|
+
from __future__ import annotations
|
|
25
|
+
|
|
26
|
+
import re
|
|
27
|
+
from dataclasses import dataclass, field
|
|
28
|
+
from pathlib import Path
|
|
29
|
+
from typing import Any, Iterator
|
|
30
|
+
|
|
31
|
+
_INTERP_RE = re.compile(
|
|
32
|
+
r"\$\{\{\s*(inputs|steps)\.([a-z0-9_-]+)(?:\.output)?\s*\}\}"
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
@dataclass
|
|
37
|
+
class StepResult:
|
|
38
|
+
"""One step's record after dispatch."""
|
|
39
|
+
step_id: str
|
|
40
|
+
kind: str
|
|
41
|
+
ref: str
|
|
42
|
+
success: bool = False
|
|
43
|
+
output: str = ""
|
|
44
|
+
error: str | None = None
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
@dataclass
|
|
48
|
+
class PipelineState:
|
|
49
|
+
"""Bookkeeping for a single ``/orchestrate`` run."""
|
|
50
|
+
name: str
|
|
51
|
+
inputs: dict[str, str]
|
|
52
|
+
results: dict[str, StepResult] = field(default_factory=dict)
|
|
53
|
+
halted: bool = False
|
|
54
|
+
halt_reason: str | None = None
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def _load_pipeline(path: Path) -> dict[str, Any]:
|
|
58
|
+
"""Reuse the linter's loader so the runtime accepts the same shape.
|
|
59
|
+
|
|
60
|
+
Walks parents to find a directory containing ``scripts/hooks/``
|
|
61
|
+
so the loader is reachable both when this module runs from the
|
|
62
|
+
consumer projection (``.agent-src/templates/scripts/work_engine/``)
|
|
63
|
+
and from the source-of-truth tree
|
|
64
|
+
(``.agent-src.uncompressed/templates/scripts/work_engine/``).
|
|
65
|
+
"""
|
|
66
|
+
import sys
|
|
67
|
+
here = Path(__file__).resolve()
|
|
68
|
+
for parent in here.parents:
|
|
69
|
+
candidate = parent / "scripts" / "hooks" / "dispatch_hook.py"
|
|
70
|
+
if candidate.is_file():
|
|
71
|
+
sys.path.insert(0, str(parent / "scripts"))
|
|
72
|
+
break
|
|
73
|
+
from hooks.dispatch_hook import _load_yaml # noqa: E402
|
|
74
|
+
doc = _load_yaml(path)
|
|
75
|
+
if not isinstance(doc, dict):
|
|
76
|
+
raise ValueError(f"{path}: top-level must be a mapping")
|
|
77
|
+
return doc
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def _interpolate(value: Any, state: PipelineState) -> Any:
|
|
81
|
+
"""Substitute ``${{ inputs.X }}`` / ``${{ steps.Y.output }}`` in a
|
|
82
|
+
nested value. Unknown references raise — the linter should have
|
|
83
|
+
caught them, but the runtime double-checks."""
|
|
84
|
+
if isinstance(value, str):
|
|
85
|
+
def replace(match: re.Match[str]) -> str:
|
|
86
|
+
ns, ident = match.group(1), match.group(2)
|
|
87
|
+
if ns == "inputs":
|
|
88
|
+
if ident not in state.inputs:
|
|
89
|
+
raise KeyError(f"unknown input '{ident}'")
|
|
90
|
+
return state.inputs[ident]
|
|
91
|
+
if ident not in state.results:
|
|
92
|
+
raise KeyError(f"unknown step '{ident}'")
|
|
93
|
+
return state.results[ident].output
|
|
94
|
+
return _INTERP_RE.sub(replace, value)
|
|
95
|
+
if isinstance(value, dict):
|
|
96
|
+
return {k: _interpolate(v, state) for k, v in value.items()}
|
|
97
|
+
if isinstance(value, list):
|
|
98
|
+
return [_interpolate(v, state) for v in value]
|
|
99
|
+
return value
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def _when_passes(when: str | None, state: PipelineState) -> bool:
|
|
103
|
+
"""Evaluate the limited ``when`` mini-language. Supports
|
|
104
|
+
``steps.X.success`` / ``steps.X.failure`` and equality on a
|
|
105
|
+
single ``${{ steps.X.output }}`` template against a literal."""
|
|
106
|
+
if not when:
|
|
107
|
+
return True
|
|
108
|
+
when = when.strip()
|
|
109
|
+
m = re.fullmatch(r"steps\.([a-z0-9_-]+)\.(success|failure)", when)
|
|
110
|
+
if m:
|
|
111
|
+
sid, kind = m.group(1), m.group(2)
|
|
112
|
+
if sid not in state.results:
|
|
113
|
+
return False
|
|
114
|
+
return state.results[sid].success if kind == "success" else not state.results[sid].success
|
|
115
|
+
m = re.fullmatch(r'\$\{\{\s*steps\.([a-z0-9_-]+)\.output\s*\}\}\s*==\s*"([^"]*)"', when)
|
|
116
|
+
if m:
|
|
117
|
+
sid, literal = m.group(1), m.group(2)
|
|
118
|
+
return state.results.get(sid, StepResult(sid, "", "")).output == literal
|
|
119
|
+
raise ValueError(f"unsupported when expression: {when!r}")
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def iter_steps(path: Path, inputs: dict[str, str]) -> Iterator[dict[str, Any]]:
|
|
123
|
+
"""Yield interpolated step descriptors in order.
|
|
124
|
+
|
|
125
|
+
Caller dispatches each descriptor via skill / command / persona /
|
|
126
|
+
subagent and feeds the result back via :func:`record_result`.
|
|
127
|
+
"""
|
|
128
|
+
doc = _load_pipeline(path)
|
|
129
|
+
merged_inputs = {
|
|
130
|
+
inp["id"]: inputs.get(inp["id"], inp.get("default", ""))
|
|
131
|
+
for inp in (doc.get("inputs") or [])
|
|
132
|
+
if isinstance(inp, dict) and isinstance(inp.get("id"), str)
|
|
133
|
+
}
|
|
134
|
+
state = PipelineState(name=doc.get("name", ""), inputs=merged_inputs)
|
|
135
|
+
for step in doc.get("steps") or []:
|
|
136
|
+
if state.halted:
|
|
137
|
+
break
|
|
138
|
+
if not _when_passes(step.get("when"), state):
|
|
139
|
+
continue
|
|
140
|
+
yield {
|
|
141
|
+
"id": step["id"],
|
|
142
|
+
"kind": step["kind"],
|
|
143
|
+
"ref": step["ref"],
|
|
144
|
+
"with": _interpolate(step.get("with") or {}, state),
|
|
145
|
+
"_state": state,
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
def record_result(descriptor: dict[str, Any], *, success: bool, output: str = "", error: str | None = None) -> None:
|
|
150
|
+
"""Caller hands the descriptor + outcome back so subsequent steps
|
|
151
|
+
can see ``${{ steps.<id>.output }}``."""
|
|
152
|
+
state: PipelineState = descriptor["_state"]
|
|
153
|
+
state.results[descriptor["id"]] = StepResult(
|
|
154
|
+
step_id=descriptor["id"], kind=descriptor["kind"], ref=descriptor["ref"],
|
|
155
|
+
success=success, output=output, error=error,
|
|
156
|
+
)
|
|
157
|
+
if not success:
|
|
158
|
+
state.halted = True
|
|
159
|
+
state.halt_reason = f"step {descriptor['id']} failed"
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def resolve_outputs(path: Path, state: PipelineState) -> dict[str, str]:
|
|
163
|
+
"""Resolve the pipeline's ``outputs:`` map against the captured
|
|
164
|
+
step outputs. Returns an empty map if the pipeline declares no
|
|
165
|
+
outputs."""
|
|
166
|
+
doc = _load_pipeline(path)
|
|
167
|
+
raw = doc.get("outputs") or {}
|
|
168
|
+
return {k: _interpolate(v, state) for k, v in raw.items()}
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
},
|
|
7
7
|
"metadata": {
|
|
8
8
|
"description": "Shared agent configuration \u2014 skills for AI coding tools (Claude Code, Augment, Cursor, Cline, Windsurf, Gemini CLI).",
|
|
9
|
-
"version": "1.
|
|
9
|
+
"version": "1.40.0"
|
|
10
10
|
},
|
|
11
11
|
"plugins": [
|
|
12
12
|
{
|
|
@@ -184,6 +184,7 @@
|
|
|
184
184
|
"./.claude/skills/optimize-prompt",
|
|
185
185
|
"./.claude/skills/optimize-rtk",
|
|
186
186
|
"./.claude/skills/optimize-skills",
|
|
187
|
+
"./.claude/skills/orchestrate",
|
|
187
188
|
"./.claude/skills/override",
|
|
188
189
|
"./.claude/skills/override-create",
|
|
189
190
|
"./.claude/skills/override-manage",
|
|
@@ -262,6 +263,7 @@
|
|
|
262
263
|
"./.claude/skills/subagent-orchestration",
|
|
263
264
|
"./.claude/skills/sync-agent-settings",
|
|
264
265
|
"./.claude/skills/sync-gitignore",
|
|
266
|
+
"./.claude/skills/sync-gitignore-fix",
|
|
265
267
|
"./.claude/skills/systematic-debugging",
|
|
266
268
|
"./.claude/skills/tailwind-engineer",
|
|
267
269
|
"./.claude/skills/tech-debt-tracker",
|