@sjawhar/pi-legion-envoy 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,259 @@
1
+ # Repository Configuration (.legion/config.yml)
2
+
3
+ Workers load repository-specific configuration from `.legion/config.yml` at the workspace root. This file allows teams to customize Legion behavior per repository without modifying workflows.
4
+
5
+ ## Schema Overview
6
+
7
+ The config file is YAML with the following top-level sections:
8
+
9
+ ```yaml
10
+ merge:
11
+ require_smoke_test: boolean
12
+ require_reporter_approval: boolean
13
+ auto_merge_allowed: boolean
14
+
15
+ testing:
16
+ require_specific_task: boolean
17
+ require_taiga_evidence: boolean
18
+
19
+ notifications:
20
+ slack_channel: string
21
+ ping_reporter_on_pr: boolean
22
+
23
+ skills:
24
+ required:
25
+ - skill_name_1
26
+ - skill_name_2
27
+
28
+ phases:
29
+ architect:
30
+ # Phase-specific overrides (same keys as top-level)
31
+ plan:
32
+ # Phase-specific overrides
33
+ implement:
34
+ # Phase-specific overrides
35
+ test:
36
+ # Phase-specific overrides
37
+ review:
38
+ # Phase-specific overrides
39
+ ```
40
+
41
+ ## Field Reference
42
+
43
+ ### merge
44
+
45
+ Controls merge behavior and approval gates.
46
+
47
+ | Field | Type | Default | Description |
48
+ |-------|------|---------|-------------|
49
+ | `require_smoke_test` | boolean | `false` | If `true`, merge workflow requires evidence of smoke testing before auto-merge |
50
+ | `require_reporter_approval` | boolean | `false` | If `true`, merge workflow requires explicit reporter approval before merging |
51
+ | `auto_merge_allowed` | boolean | `false` | If `true`, merge workflow may auto-merge PRs that pass all gates |
52
+
53
+ ### testing
54
+
55
+ Controls test requirements and evidence gates.
56
+
57
+ | Field | Type | Default | Description |
58
+ |-------|------|---------|-------------|
59
+ | `require_specific_task` | boolean | `false` | If `true`, test workflow requires evidence of specific task testing (not just unit tests) |
60
+ | `require_taiga_evidence` | boolean | `false` | If `true`, test workflow requires Taiga evidence (e.g., screenshots, logs) attached to the issue |
61
+
62
+ ### notifications
63
+
64
+ Controls notifications and pings.
65
+
66
+ | Field | Type | Default | Description |
67
+ |-------|------|---------|-------------|
68
+ | `slack_channel` | string | `null` | Slack channel ID (e.g., `#eng-legion`) where implementation status updates are posted. If set and `slack-bot` skill is available, implement workflow posts PR URL and CI state |
69
+ | `ping_reporter_on_pr` | boolean | `false` | If `true`, notify the issue reporter when a PR is created |
70
+
71
+ ### skills
72
+
73
+ Specifies skills that should be invoked for all phases.
74
+
75
+ | Field | Type | Default | Description |
76
+ |-------|------|---------|-------------|
77
+ | `required` | string[] | `[]` | List of skill names to invoke in addition to plan handoff `requiredSkills` and independently discovered skills. Additive with other skill sources |
78
+
79
+ ### phases
80
+
81
+ Phase-specific overrides. Each phase (`architect`, `plan`, `implement`, `test`, `review`) can override any top-level key.
82
+
83
+ **Merge behavior:** Phase-specific values override top-level values. For example:
84
+
85
+ ```yaml
86
+ merge:
87
+ require_reporter_approval: false
88
+
89
+ phases:
90
+ implement:
91
+ merge:
92
+ require_reporter_approval: true # Override for implement phase only
93
+ ```
94
+
95
+ ## Recognized Keys
96
+
97
+ Workers recognize the following keys (case-sensitive):
98
+
99
+ - `merge.require_smoke_test`
100
+ - `merge.require_reporter_approval`
101
+ - `merge.auto_merge_allowed`
102
+ - `testing.require_specific_task`
103
+ - `testing.require_taiga_evidence`
104
+ - `notifications.slack_channel`
105
+ - `notifications.ping_reporter_on_pr`
106
+ - `skills.required`
107
+ - `phases.<mode>.*` (any of the above keys under a phase)
108
+
109
+ Unknown keys are silently ignored. Malformed YAML causes the config to be skipped (fallback to defaults).
110
+
111
+ ## Worker Behavior
112
+
113
+ ### Loading
114
+
115
+ Each workflow loads config at startup:
116
+
117
+ ```bash
118
+ if [ -f .legion/config.yml ]; then cat .legion/config.yml; fi
119
+ ```
120
+
121
+ If the file is missing or malformed, workers proceed with defaults (no error).
122
+
123
+ ### Parsing
124
+
125
+ Workers parse recognized keys and echo them for auditability:
126
+
127
+ ```
128
+ Repo config constraints from .legion/config.yml:
129
+ - merge.require_reporter_approval: true
130
+ - testing.require_specific_task: true
131
+ - skills.required: [test-driven-development, verification-before-completion]
132
+ ```
133
+
134
+ ### Application
135
+
136
+ Config values are applied to shape workflow behavior:
137
+
138
+ - **Plan phase:** Config constraints inform the plan preamble (e.g., "reporter approval required before merge")
139
+ - **Implement phase:** Config-required skills are invoked additively with plan handoff skills
140
+ - **Test phase:** Config gates (e.g., `require_specific_task`) are enforced
141
+ - **Review phase:** Config-required skills are invoked additively
142
+ - **Merge phase:** Config gates (e.g., `require_reporter_approval`) are enforced
143
+
144
+ ## Example Configuration
145
+
146
+ ### Minimal (all defaults)
147
+
148
+ ```yaml
149
+ # Empty file or omitted entirely
150
+ # All defaults apply
151
+ ```
152
+
153
+ ### Strict Testing
154
+
155
+ ```yaml
156
+ testing:
157
+ require_specific_task: true
158
+ require_taiga_evidence: true
159
+
160
+ skills:
161
+ required:
162
+ - test-driven-development
163
+ - verification-before-completion
164
+ ```
165
+
166
+ ### Slack Notifications + Approval Gate
167
+
168
+ ```yaml
169
+ notifications:
170
+ slack_channel: "#eng-legion"
171
+ ping_reporter_on_pr: true
172
+
173
+ merge:
174
+ require_reporter_approval: true
175
+ require_smoke_test: true
176
+ ```
177
+
178
+ ### Phase-Specific Overrides
179
+
180
+ ```yaml
181
+ # Default: no approval required
182
+ merge:
183
+ require_reporter_approval: false
184
+
185
+ # But for implement phase, require approval
186
+ phases:
187
+ implement:
188
+ merge:
189
+ require_reporter_approval: true
190
+
191
+ # And require specific task testing only in test phase
192
+ phases:
193
+ test:
194
+ testing:
195
+ require_specific_task: true
196
+ ```
197
+
198
+ ### Full Example
199
+
200
+ ```yaml
201
+ merge:
202
+ require_smoke_test: true
203
+ require_reporter_approval: false
204
+ auto_merge_allowed: false
205
+
206
+ testing:
207
+ require_specific_task: true
208
+ require_taiga_evidence: false
209
+
210
+ notifications:
211
+ slack_channel: "#eng-legion"
212
+ ping_reporter_on_pr: true
213
+
214
+ skills:
215
+ required:
216
+ - test-driven-development
217
+ - verification-before-completion
218
+
219
+ phases:
220
+ plan:
221
+ skills:
222
+ required:
223
+ - brainstorming
224
+ implement:
225
+ merge:
226
+ require_reporter_approval: true
227
+ test:
228
+ testing:
229
+ require_taiga_evidence: true
230
+ ```
231
+
232
+ ## Defaults
233
+
234
+ If a key is not specified in `.legion/config.yml`, the following defaults apply:
235
+
236
+ ```yaml
237
+ merge:
238
+ require_smoke_test: false
239
+ require_reporter_approval: false
240
+ auto_merge_allowed: false
241
+
242
+ testing:
243
+ require_specific_task: false
244
+ require_taiga_evidence: false
245
+
246
+ notifications:
247
+ slack_channel: null
248
+ ping_reporter_on_pr: false
249
+
250
+ skills:
251
+ required: []
252
+ ```
253
+
254
+ ## Notes
255
+
256
+ - **Additive skills:** Config-required skills are additive with plan handoff `requiredSkills` and independently discovered skills. All sources are combined.
257
+ - **Phase overrides:** Phase-specific values completely override top-level values for that phase. Partial overrides are not merged.
258
+ - **Missing file:** If `.legion/config.yml` is missing or malformed, workers proceed with defaults (no error).
259
+ - **Auditability:** Workers echo recognized keys and effective values at the start of each phase for transparency.
@@ -0,0 +1,98 @@
1
+ # Knowledge Injection Algorithm
2
+
3
+ Canonical algorithm for injecting relevant learnings from `docs/solutions/` before phase-specific work begins. All worker workflows reference this file for the injection procedure; each workflow specifies its own keyword sources.
4
+
5
+ ## Overview
6
+
7
+ Before starting main work, each phase checks the learnings index for applicable prior knowledge. This surfaces patterns, pitfalls, and institutional knowledge that previous workers documented.
8
+
9
+ **Injection must never block work.** If any step fails (missing index, invalid JSON, missing files, empty handoff data), skip silently and proceed with the phase's main work.
10
+
11
+ ## Algorithm
12
+
13
+ ### 1. Read the Index
14
+
15
+ Assemble the index by reading all per-entry JSON files in `docs/solutions/.index/`:
16
+
17
+ ```bash
18
+ # Read and merge all entry files in .index/ directory
19
+ for f in docs/solutions/.index/*.json; do
20
+ [ -f "$f" ] && cat "$f"
21
+ done
22
+ ```
23
+
24
+ Each file has the format `{ "version": 1, "entries": { "key": ["learning-path", ...] } }`. Merge all `entries` maps together, deduplicating learning paths per key.
25
+
26
+ If the `.index/` directory doesn't exist or contains no valid JSON files, skip injection entirely — proceed to the phase's main work.
27
+
28
+ ### 2. Extract Keywords
29
+
30
+ Collect keywords from the phase-specific sources (defined in each workflow file). The extraction algorithm:
31
+
32
+ 1. **Collect raw text** from the specified keyword sources (see the calling workflow's keyword source table)
33
+ 2. **Tokenize**: split on whitespace, `/`, `-`, `_`, and camelCase boundaries
34
+ 3. **Normalize**: lowercase all tokens
35
+ 4. **Filter**: remove tokens < 3 chars and common stopwords (the, and, for, with, this, that, from, into, when, will, should, would, could, also, been, have, each, etc.)
36
+ 5. **Deduplicate** tokens
37
+ 6. **Extract full path segments**: e.g., `packages/daemon/src/state` — keep as-is for path matching in addition to individual tokens
38
+
39
+ Also look for references to:
40
+ - Source path segments (e.g., `packages/daemon/src/state/`, `serve-manager`)
41
+ - Module names (e.g., "daemon", "controller", "worker", "state")
42
+ - Component names (e.g., "serve-manager", "decision", "fetch")
43
+ - Feature areas (e.g., "skills", "linear", "github", "review", "retro")
44
+ - Integration concerns (e.g., "PR", "labels", "MCP")
45
+ - Domain concepts and error keywords from the context
46
+
47
+ ### 3. Match Keywords Against Index
48
+
49
+ Use two matching modes against the keys in `.index`:
50
+
51
+ - **Path matching**: For each key that does NOT start with `tag:`, check if any extracted keyword appears as a substring of the key (case-insensitive). Collect all matched learning file paths.
52
+ - **Tag matching**: For each key that starts with `tag:`, extract the tag name (e.g., `tag:race-condition` → `race-condition`). Check if any extracted keyword matches the tag name (case-insensitive). Collect matched learning file paths.
53
+
54
+ ### 4. Deduplicate and Rank
55
+
56
+ - Remove duplicates (same file matched via multiple keys)
57
+ - **Status filter**: For each candidate, read its YAML front matter `status` field. Exclude any file with `status: superseded`. If the file doesn't exist or has no front matter, include it (graceful degradation).
58
+ - **Primary rank: tag overlap** — For each remaining candidate, read its `tags` front matter field. Count how many of its tags appear in the extracted keywords (case-insensitive). Higher overlap = higher rank.
59
+ - **Secondary rank: key specificity** — Learnings matched via longer/more-specific keys rank higher (e.g., a match on `packages/daemon/src/state` outranks a match on `packages/daemon`)
60
+ - **Tertiary rank: match count** — Number of distinct key matches (more matches = more relevant)
61
+ - **Cap at 3 learnings maximum**
62
+
63
+ ### 5. Read Matched Learnings
64
+
65
+ For each matched learning file (from `docs/solutions/<path>`):
66
+
67
+ 1. Read YAML front matter: extract `title` and `tags` fields
68
+ 2. Skip past front matter (`---` blocks) and headings, take the first paragraph of prose (typically the Problem or Overview section)
69
+ 3. Prepend structured header: `[{title} | tags: {comma-separated tags}]`
70
+ 4. Truncate entire output (header + prose) to **350 characters**
71
+
72
+ **If a matched file doesn't exist on disk:** Skip that entry silently (stale index entry from a file rename). Do not error.
73
+
74
+ ### 6. Output Injected Learnings
75
+
76
+ Output the injected learnings visibly in the session before proceeding with the phase's main work:
77
+
78
+ ```
79
+ ## Relevant Learnings (from docs/solutions/)
80
+
81
+ 1. [docs/solutions/<path>]: [{title} | tags: {tag1}, {tag2}] <prose excerpt> (350 chars max total)
82
+ 2. [docs/solutions/<path>]: [{title} | tags: {tag1}, {tag2}] <prose excerpt>
83
+ 3. [docs/solutions/<path>]: [{title} | tags: {tag1}, {tag2}] <prose excerpt>
84
+
85
+ (Review these for patterns and pitfalls relevant to this phase's work.)
86
+ ```
87
+
88
+ **If no matches found:** Output "No relevant learnings found." and proceed. Do NOT add an empty section.
89
+
90
+ **Canonical identifiers:** All references to learnings use their `docs/solutions/` relative file path (e.g., `daemon/controller-lifecycle-separation.md`). These paths are the stable IDs used for injection, handoff tracking, and future aggregation. Never use titles or truncated text as identifiers.
91
+
92
+ ## Fallback Behavior
93
+
94
+ When a keyword source is unavailable (missing handoff data, empty fields, missing phase data), silently fall back to the next available source as defined in the calling workflow's fallback rules. Never error on missing data.
95
+
96
+ ## Integration with Handoffs
97
+
98
+ If the phase writes handoff data, include a `learningsInjected` field listing the `docs/solutions/` relative paths of all injected learnings. This enables downstream phases to see what knowledge was available and supports future aggregation.
@@ -0,0 +1,22 @@
1
+ # Strategy: Cleanup & Deletion PRs
2
+
3
+ When deleting deprecated code, stale docs, or consolidating references.
4
+
5
+ ## When deleting a CLI command, check all four:
6
+
7
+ 1. Implementation file(s)
8
+ 2. `package.json` / `pyproject.toml` script entry
9
+ 3. All project documentation references (AGENTS.md or equivalent — check command tables AND section headings)
10
+ 4. Wrapper scripts or CI jobs that invoke it
11
+
12
+ ## project doc headings are documentation too
13
+
14
+ When updating a command reference, grep for the section heading and update it in the same commit. Headings that reference specific paths (`## Foo (meta/bar/)`) go stale when paths change.
15
+
16
+ ## Deletion PRs should be almost entirely deletions
17
+
18
+ Resist opportunistic refactors. If the diff has significant additions, the scope has crept. The value of a cleanup PR is its tight, reviewable scope.
19
+
20
+ ## Complete the deletion chain
21
+
22
+ If a feature has implementation + CLI wrapper + package.json entry + docs, remove all of them together. Partial deletion leaves broken references.
@@ -0,0 +1,19 @@
1
+ # Strategy: Systematic Rename
2
+
3
+ When a repo, package, or URL is renamed across a codebase.
4
+
5
+ ## Checklist
6
+
7
+ 1. **Scope by file type** — grep all text-bearing extensions, not just the obvious ones:
8
+ - Source code (`.ts`, `.js`, `.py`, `.sh`) — functional, must update
9
+ - CI/CD configs (`.yml`, `.yaml`) — functional, must update
10
+ - Documentation (`.md`) — correctness, should update
11
+ - Config files (`.json`, `.toml`) — check but may be immutable
12
+
13
+ 2. **Classify matches as mutable vs immutable** — historical records (transcripts, test snapshots, progress logs) must NOT be modified. Changing them falsifies history.
14
+
15
+ 3. **Check comments for semantic context** — a comment mentioning the old name may still be correct in intent. Update the name but preserve the reasoning.
16
+
17
+ 4. **Verify with grep before AND after** — capture pre-edit state as a baseline for comparison.
18
+
19
+ 5. **Use `jj diff --git`** for verification — plain `jj diff` without color concatenates old/new text confusingly (e.g., `old-nameNEW-name` without color codes).
@@ -0,0 +1,76 @@
1
+ ---
2
+ name: linear
3
+ description: Manage Linear issues. Use when working with tasks, tickets, bugs, or Linear.
4
+ mcp:
5
+ linear:
6
+ command: npx
7
+ args: ["-y", "github:obra/streamlinear"]
8
+ env:
9
+ LINEAR_API_TOKEN: ${LINEAR_API_TOKEN}
10
+ ---
11
+
12
+ # Linear (Stream Linear)
13
+
14
+ Single-tool MCP with action dispatch. All operations go through `linear_linear`.
15
+
16
+ ## Actions
17
+
18
+ ### Search Issues
19
+
20
+ ```
21
+ linear_linear(action="search") # Your active issues
22
+ linear_linear(action="search", query="auth bug") # Text search
23
+ linear_linear(action="search", query={state: "In Progress"}) # Filter
24
+ linear_linear(action="search", query={team: "ENG", assignee: "me"})
25
+ ```
26
+
27
+ ### Get Issue Details
28
+
29
+ ```
30
+ linear_linear(action="get", id="ABC-123") # By short ID
31
+ linear_linear(action="get", id="https://linear.app/...") # By URL
32
+ ```
33
+
34
+ Returns: title, description, status, labels, comments, attachments.
35
+
36
+ ### Update Issue
37
+
38
+ ```
39
+ linear_linear(action="update", id="ABC-123", state="Done")
40
+ linear_linear(action="update", id="ABC-123", priority=1)
41
+ linear_linear(action="update", id="ABC-123", assignee="me")
42
+ linear_linear(action="update", id="ABC-123", labels=["legion-backlog", "existing-label"])
43
+ ```
44
+
45
+ **Labels array replaces all labels.** Fetch current labels first, then append.
46
+
47
+ ### Comment on Issue
48
+
49
+ ```
50
+ linear_linear(action="comment", id="ABC-123", body="Fixed in commit abc123")
51
+ ```
52
+
53
+ ### Create Issue
54
+
55
+ ```
56
+ linear_linear(action="create", title="Bug: Login fails", team="ENG")
57
+ linear_linear(action="create", title="Bug", team="ENG", body="Details", priority=2)
58
+ ```
59
+
60
+ ### Raw GraphQL
61
+
62
+ ```
63
+ linear_linear(action="graphql", graphql="query { viewer { name } }")
64
+ ```
65
+
66
+ ### Help
67
+
68
+ ```
69
+ linear_linear(action="help")
70
+ ```
71
+
72
+ ## Reference
73
+
74
+ - Priority: 0=None, 1=Urgent, 2=High, 3=Medium, 4=Low
75
+ - State matching is fuzzy: "done" → "Done", "in prog" → "In Progress"
76
+ - IDs accept: `ABC-123`, Linear URLs, or UUIDs
package/package.json ADDED
@@ -0,0 +1,36 @@
1
+ {
2
+ "name": "@sjawhar/pi-legion-envoy",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "omp": {
6
+ "extensions": [
7
+ "dist/envoy.js"
8
+ ]
9
+ },
10
+ "repository": {
11
+ "type": "git",
12
+ "url": "https://github.com/sjawhar/legion"
13
+ },
14
+ "files": [
15
+ "dist"
16
+ ],
17
+ "scripts": {
18
+ "build": "bun build extensions/envoy.ts extensions/legion.ts --outdir dist --target bun --format esm --external @oh-my-pi/pi-coding-agent",
19
+ "prepack": "./scripts/prepack.sh",
20
+ "postpack": "rm -rf dist/skills",
21
+ "typecheck": "bunx tsc --noEmit",
22
+ "test": "bun test",
23
+ "lint": "bunx biome lint extensions/ src/"
24
+ },
25
+ "devDependencies": {
26
+ "@biomejs/biome": "^2.3.14",
27
+ "@legion/contracts": "0.10.0",
28
+ "@legion/envoy-client": "0.1.0",
29
+ "@legion/workspace": "0.1.0",
30
+ "@toon-format/toon": "^4.1.1",
31
+ "@types/bun": "latest",
32
+ "nats": "^2.29.2",
33
+ "typescript": "^5.3.0",
34
+ "zod": "^4.1.8"
35
+ }
36
+ }