kodelyth-ecc 1.8.0 → 1.8.2

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.
Files changed (91) hide show
  1. package/CLAUDE.md +1 -1
  2. package/VERSION +1 -1
  3. package/bin/kodelyth-ecc.js +54 -5
  4. package/install.ps1 +30 -13
  5. package/install.sh +11 -4
  6. package/package.json +1 -1
  7. package/rules/common/agent-intent-routing.md +96 -195
  8. package/.github/ISSUE_TEMPLATE/bug_report.md +0 -47
  9. package/.github/ISSUE_TEMPLATE/config.yml +0 -8
  10. package/.github/ISSUE_TEMPLATE/feature_request.md +0 -41
  11. package/.github/ISSUE_TEMPLATE/good_first_issue.md +0 -36
  12. package/.github/ISSUE_TEMPLATE/new_agent.md +0 -52
  13. package/.github/PULL_REQUEST_TEMPLATE.md +0 -53
  14. package/.github/workflows/ci.yml +0 -45
  15. package/.github/workflows/publish.yml +0 -60
  16. package/brand/concepts.svg +0 -109
  17. package/brand/convert.js +0 -161
  18. package/brand/favicon.svg +0 -7
  19. package/brand/fb-cover.svg +0 -34
  20. package/brand/fb-profile.svg +0 -21
  21. package/brand/kodelyth-dark.svg +0 -24
  22. package/brand/kodelyth-light.svg +0 -24
  23. package/brand/kodelyth-mark.svg +0 -20
  24. package/brand/package.json +0 -12
  25. package/docs/dashboard.md +0 -211
  26. package/docs/evolve.md +0 -303
  27. package/docs/mcp-clients.md +0 -167
  28. package/docs/mcp.md +0 -178
  29. package/docs/replay.md +0 -244
  30. package/docs/supply-chain.md +0 -207
  31. package/docs/swarm.md +0 -243
  32. package/skills/skill-comply/tests/test_grader.py +0 -137
  33. package/skills/skill-comply/tests/test_parser.py +0 -90
  34. package/social/card-agents.svg +0 -104
  35. package/social/card-install.svg +0 -58
  36. package/social/card-main.svg +0 -54
  37. package/social/facebook-v150.svg +0 -110
  38. package/social/fb-ad-main.svg +0 -128
  39. package/social/fb-post-features.svg +0 -118
  40. package/social/fb-post-launch.svg +0 -144
  41. package/social/fb-post-platforms.svg +0 -135
  42. package/social/github-social-preview.svg +0 -151
  43. package/social/hype-compound-learning.svg +0 -129
  44. package/social/hype-devil-mode.svg +0 -240
  45. package/social/hype-mcp-server.svg +0 -148
  46. package/social/hype-parallel-agents.svg +0 -162
  47. package/social/hype-stats-hero.svg +0 -138
  48. package/social/og-image.svg +0 -151
  49. package/social/readme-agents.svg +0 -138
  50. package/social/readme-hero.svg +0 -102
  51. package/social/section-agents.svg +0 -69
  52. package/social/section-author.svg +0 -66
  53. package/social/section-dashboard.svg +0 -71
  54. package/social/section-devil.svg +0 -66
  55. package/social/section-hooks.svg +0 -63
  56. package/social/section-install.svg +0 -54
  57. package/social/section-learning.svg +0 -64
  58. package/social/section-mcp.svg +0 -58
  59. package/social/section-memory.svg +0 -69
  60. package/social/section-parallel.svg +0 -84
  61. package/social/section-routing.svg +0 -63
  62. package/social/twitter-threads.md +0 -621
  63. package/social/x-card-agents-grid.svg +0 -120
  64. package/social/x-card-free.svg +0 -71
  65. package/social/x-card-hook.svg +0 -94
  66. package/tests/dashboard/data.test.js +0 -391
  67. package/tests/dashboard/server.test.js +0 -257
  68. package/tests/evolve/analyze.test.js +0 -169
  69. package/tests/evolve/proposals.test.js +0 -173
  70. package/tests/evolve/stats.test.js +0 -167
  71. package/tests/hooks/branch-name-check.test.js +0 -184
  72. package/tests/hooks/smart-suggest.test.js +0 -149
  73. package/tests/hooks/test-reminder.test.js +0 -181
  74. package/tests/mcp/catalog.test.js +0 -98
  75. package/tests/mcp/client.test.js +0 -113
  76. package/tests/mcp/resources-prompts.test.js +0 -70
  77. package/tests/mcp/tools.test.js +0 -159
  78. package/tests/memory/auto-recall.test.js +0 -132
  79. package/tests/memory/instincts.test.js +0 -258
  80. package/tests/memory/store.test.js +0 -203
  81. package/tests/replay/bundle.test.js +0 -181
  82. package/tests/replay/replay.test.js +0 -131
  83. package/tests/router/classify.test.js +0 -164
  84. package/tests/run-all.js +0 -55
  85. package/tests/safety/patterns.test.js +0 -94
  86. package/tests/safety/prompt-injection-guard.test.js +0 -111
  87. package/tests/safety/token-budget.test.js +0 -119
  88. package/tests/supply-chain/manifest.test.js +0 -147
  89. package/tests/supply-chain/sbom.test.js +0 -170
  90. package/tests/supply-chain/verify.test.js +0 -146
  91. package/tests/swarm/build-plan.test.js +0 -188
package/docs/swarm.md DELETED
@@ -1,243 +0,0 @@
1
- # Kodelyth ECC — Swarm Orchestrator
2
-
3
- Run N ECC specialist agents in parallel inside isolated git worktrees, coordinated by a tmux session.
4
-
5
- > **Phase 2.7 of the [Devil Roadmap](../README.md).** Promotes the existing tmux-worktree orchestrator infrastructure to a first-class CLI surface. The generalized form of `/devil-mode` — pick any task, pick any agents, get N panes ready to attach.
6
-
7
- ---
8
-
9
- ## Why swarm
10
-
11
- A single specialist agent has tunnel vision. A pre-flight production change usually needs:
12
-
13
- - Code quality review (`code-reviewer`)
14
- - Security audit (`security-reviewer`)
15
- - API contract check (`api-guardian`)
16
- - Test coverage check (`tdd-guide`)
17
- - UX/a11y pass (`ux-reviewer`)
18
- - Performance check (`performance-optimizer`)
19
- - Doc update (`doc-updater`)
20
- - Release readiness (`release-captain`)
21
-
22
- Sequential = 2 hours. Parallel = 15 minutes. Each runs in its own git worktree so their changes don't collide; their handoffs merge into one folder for human review.
23
-
24
- ---
25
-
26
- ## CLI quick reference
27
-
28
- ```bash
29
- # Auto-pick from task signals
30
- npx kodelyth-ecc swarm --task "audit oauth flow for security regressions" --agents 4
31
-
32
- # Explicit agent list
33
- npx kodelyth-ecc swarm \
34
- --task "ship v2.0" \
35
- --agents release-captain,security-reviewer,e2e-runner,code-reviewer \
36
- --execute
37
-
38
- # Power-user plan.json
39
- npx kodelyth-ecc swarm --plan plan.json --execute
40
- ```
41
-
42
- | Flag | Description |
43
- |---|---|
44
- | `--task "..."` | Required (unless using `--plan`). The shared task all workers receive. |
45
- | `--agents N` | Smart-pick N specialists from task signals + baseline + rotation. |
46
- | `--agents name1,name2,...` | Explicit agent list. |
47
- | `--harness <h>` | `claude` / `codex` / `opencode` / `windsurf` / `echo`. Default `claude`. |
48
- | `--launcher-cmd "<tmpl>"` | Custom launcher template (overrides `--harness`). |
49
- | `--seed <path>` | Overlay a path from the main repo into each worktree. Repeatable. |
50
- | `--session <name>` | Override the auto-generated tmux session name. |
51
- | `--worktree-root <dir>` | Where to create worktrees. Default: parent of repo. |
52
- | `--coordination-root <dir>` | Where to write coordination files. Default: `<repo>/.orchestration/`. |
53
- | `--base-ref <ref>` | Branch base for all worktrees. Default: `HEAD`. |
54
- | `--replace` | Tear down any existing session/worktrees/branches with the same names. |
55
- | `--execute` | Actually create worktrees + tmux + launch. |
56
- | `--write-only` | Just write coordination files. |
57
- | `--json` | Print the full plan as JSON. |
58
- | `--plan plan.json` | Use a hand-written plan (skips auto-build). |
59
-
60
- Default mode is **dry-run** — prints a summary and does NOT spawn anything. Always inspect first.
61
-
62
- ---
63
-
64
- ## Smart agent picking
65
-
66
- When you pass `--agents N` (a number), the swarm picks specialists in this priority order:
67
-
68
- ### 1. Signal-driven (highest priority)
69
-
70
- Task text is scanned for 14 signal classes. Each match adds a specialist. Examples:
71
-
72
- | Task contains | Picks |
73
- |---|---|
74
- | `security`, `auth`, `vuln`, `cve`, `injection`, `owasp` | `security-reviewer` |
75
- | `perf`, `slow`, `p99`, `latency`, `bottleneck` | `performance-optimizer` |
76
- | `load test`, `stress test`, `capacity`, `k6` | `load-tester` |
77
- | `architect`, `design`, `ADR`, `RFC`, `trade-off` | `architect` |
78
- | `api`, `endpoint`, `contract`, `breaking change` | `api-guardian` |
79
- | `test`, `tdd`, `coverage`, `spec` | `tdd-guide` |
80
- | `refactor`, `tech debt`, `code smell` | `refactor-cleaner` |
81
- | `ux`, `accessibility`, `wcag`, `a11y` | `ux-reviewer` |
82
- | `doc`, `readme`, `guide`, `tutorial` | `doc-updater` |
83
- | `release`, `ship`, `tag`, `semver` | `release-captain` |
84
- | `incident`, `outage`, `down`, `P0`, `P1` | `incident-commander` |
85
- | `debug`, `why is`, `stack trace`, `silent fail` | `debug-detective` |
86
- | `database`, `sql`, `postgres`, `index`, `n+1` | `database-reviewer` |
87
- | `devil-mode`, `adversarial`, `red team` | `prompt-injection-hunter` |
88
-
89
- ### 2. Baseline anchors
90
-
91
- `code-reviewer` and `pair-programmer` are added if not already picked — every task benefits from generalist eyes.
92
-
93
- ### 3. Rotation fill
94
-
95
- If we still need more, fills from a tuned default rotation:
96
-
97
- | Count | Rotation |
98
- |---|---|
99
- | 4 | code-reviewer, security-reviewer, pair-programmer, tdd-guide |
100
- | 6 | + performance-optimizer, api-guardian |
101
- | 8 | + ux-reviewer, doc-updater |
102
-
103
- You can always override the picker with `--agents code-reviewer,security-reviewer,architect`.
104
-
105
- ---
106
-
107
- ## Coordination protocol
108
-
109
- Each worker gets three files in `<coord-root>/<session>/<worker-slug>/`:
110
-
111
- ### `task.md` (auto-generated)
112
-
113
- Agent-shaped task with required handoff sections:
114
-
115
- ```
116
- # <agent> — swarm task
117
-
118
- You are running as the ECC `<agent>` specialist in a parallel swarm. Other agents
119
- are running the same shared task in sibling worktrees. Stay strictly inside your
120
- specialty — do not duplicate what other agents will cover. Produce a focused
121
- handoff that names exactly which findings are yours and yours alone.
122
-
123
- ## Shared Task
124
- <your task>
125
-
126
- ## Required handoff sections
127
- 1. Summary
128
- 2. Files Changed
129
- 3. Validation
130
- 4. Remaining Risks
131
- ```
132
-
133
- ### `handoff.md` (worker fills in)
134
-
135
- Where the worker's output lands. Watched by the launcher.
136
-
137
- ### `status.md` (launcher updates)
138
-
139
- `running` / `completed` / `failed` plus timestamp + branch + worktree path.
140
-
141
- Monitor a swarm in flight:
142
-
143
- ```bash
144
- # All worker statuses
145
- cat .orchestration/<session>/*/status.md
146
-
147
- # Specific handoff
148
- cat .orchestration/<session>/security-reviewer/handoff.md
149
- ```
150
-
151
- ---
152
-
153
- ## Harness adapters
154
-
155
- | `--harness` | Launcher template |
156
- |---|---|
157
- | `claude` | `claude --print --dangerously-skip-permissions "$(cat {task_file_sh})" 2>&1 \| tee -a {handoff_file_sh}; ...` |
158
- | `codex` | `bash {repo_root}/scripts/orchestrate-codex-worker.sh {task_file_sh} {handoff_file_sh} {status_file_sh}` |
159
- | `opencode` | `opencode run --task-file {task_file_sh} --output {handoff_file_sh} --status {status_file_sh}` |
160
- | `windsurf` | `windsurf-cli run --task-file {task_file_sh} --output {handoff_file_sh}` |
161
- | `echo` | `printf 'demo {worker_slug}\n' >> {handoff_file_sh}; printf 'state: completed\n' >> {status_file_sh}` |
162
-
163
- Custom harness via `--launcher-cmd`. Available placeholders:
164
-
165
- ```
166
- {worker_name} {worker_slug} {session_name} {repo_root}
167
- {worktree_path} {branch_name} {task_file} {handoff_file} {status_file}
168
- ```
169
-
170
- Suffix with `_sh` for shell-quoted variants: `{task_file_sh}`, `{handoff_file_sh}`, etc.
171
-
172
- ---
173
-
174
- ## Worktree lifecycle
175
-
176
- 1. **Pre-flight** — verify `git rev-parse --is-inside-work-tree` and `tmux -V`.
177
- 2. **Cleanup** — if `--replace`, tear down any existing session, worktrees, branches.
178
- 3. **Materialize** — write coordination files for every worker.
179
- 4. **Branch + worktree** — `git worktree add -b orchestrator-<session>-<slug> <path> <base-ref>`.
180
- 5. **Seed overlay** — copy any `--seed` paths into each worktree.
181
- 6. **Tmux session** — `tmux new-session -d -s <session> -n orchestrator -c <repo>`.
182
- 7. **Per-worker pane** — `tmux split-window` + `select-pane` + `send-keys` to launch.
183
-
184
- On failure, automatic rollback removes worktrees, branches, and the tmux session in reverse order.
185
-
186
- Manually clean up later:
187
-
188
- ```bash
189
- tmux kill-session -t <session>
190
- git worktree list # verify
191
- git worktree remove <path>
192
- git branch -D orchestrator-<session>-<slug>
193
- rm -rf .orchestration/<session>
194
- ```
195
-
196
- ---
197
-
198
- ## Pairing with the rest of ECC
199
-
200
- | Pairs with | How |
201
- |---|---|
202
- | **`/devil-mode`** | Devil-mode is a hardcoded swarm of adversarial agents. Swarm generalizes it. |
203
- | **Phase 2.4 cost router** | Each worker classifies its own task tier; security/incident workers force hard. |
204
- | **Phase 2.10 token-budget hook** | Each worker has its own token budget — one rogue worker can't blow the cap. |
205
- | **Phase 2.5 MCP client mode** | Workers can call registered external MCP servers (github, postgres, brave, redis). |
206
- | **kodelyth-memory** | Save the merged handoff bundle: `kodelyth-ecc remember "..." --approach "..."`. |
207
- | **`release-captain`** | After a swarm, run `release-captain` to merge the best work and ship. |
208
-
209
- ---
210
-
211
- ## Hard rules
212
-
213
- 1. **Never `--execute` without inspecting the dry-run first.** Worktree creation mutates the repo.
214
- 2. **Don't mix incompatible agents.** `release-captain` + `migration-guide` in the same swarm produces conflicting handoffs.
215
- 3. **Cap N at 8 for a single repo.** Past 8, worktree contention + pane crowding hurts.
216
- 4. **Never share tmux sessions.** Use `--session NAME` explicitly when running multiple swarms.
217
- 5. **Always human-merge handoffs.** Don't auto-apply changes from N parallel agents.
218
-
219
- ---
220
-
221
- ## Troubleshooting
222
-
223
- **`tmux session already exists`** — pass `--replace` to tear it down, or `--session <new-name>`.
224
-
225
- **`Worker X is missing a launcherCommand`** — pass `--harness <name>` or `--launcher-cmd "<template>"`.
226
-
227
- **`seedPaths entries must stay inside repoRoot`** — the seed must be a subpath of the repo, not an absolute external path.
228
-
229
- **Worktrees have stale state** — `--replace` cleans them. Or manually: `git worktree prune --expire now`.
230
-
231
- **A worker stalled** — `tmux attach -t <session>` and inspect its pane. Or `cat .orchestration/<session>/<slug>/status.md`.
232
-
233
- ---
234
-
235
- ## Roadmap interactions
236
-
237
- - **Phase 2.3 — local dashboard** will surface live swarm status (running / completed / failed per worker, latency, token usage).
238
- - **Phase 2.6 — sandbox layer** will optionally Docker-isolate each worker.
239
- - **Phase 2.8 — replay** will let you replay a finished swarm from its coordination files.
240
-
241
- ---
242
-
243
- Built into [Kodelyth ECC](../README.md). MIT licensed.
@@ -1,137 +0,0 @@
1
- """Tests for grader module — compliance scoring with LLM classification."""
2
-
3
- from pathlib import Path
4
- from unittest.mock import patch
5
-
6
- import pytest
7
-
8
- from scripts.grader import ComplianceResult, StepResult, grade
9
- from scripts.parser import parse_spec, parse_trace
10
-
11
- FIXTURES = Path(__file__).parent.parent / "fixtures"
12
-
13
-
14
- @pytest.fixture
15
- def tdd_spec():
16
- return parse_spec(FIXTURES / "tdd_spec.yaml")
17
-
18
-
19
- @pytest.fixture
20
- def compliant_trace():
21
- return parse_trace(FIXTURES / "compliant_trace.jsonl")
22
-
23
-
24
- @pytest.fixture
25
- def noncompliant_trace():
26
- return parse_trace(FIXTURES / "noncompliant_trace.jsonl")
27
-
28
-
29
- def _mock_compliant_classification(spec, trace, model="haiku"): # noqa: ARG001
30
- """Simulate LLM correctly classifying a compliant trace."""
31
- return {
32
- "write_test": [0],
33
- "run_test_red": [1],
34
- "write_impl": [2],
35
- "run_test_green": [3],
36
- "refactor": [4],
37
- }
38
-
39
-
40
- def _mock_noncompliant_classification(spec, trace, model="haiku"):
41
- """Simulate LLM classifying a noncompliant trace (impl before test)."""
42
- return {
43
- "write_impl": [0], # src/fib.py written first
44
- "write_test": [1], # test written second
45
- "run_test_green": [2], # only a passing test run
46
- }
47
-
48
-
49
- def _mock_empty_classification(spec, trace, model="haiku"):
50
- return {}
51
-
52
-
53
- class TestGradeCompliant:
54
- @patch("scripts.grader.classify_events", side_effect=_mock_compliant_classification)
55
- def test_returns_compliance_result(self, mock_cls, tdd_spec, compliant_trace) -> None:
56
- result = grade(tdd_spec, compliant_trace)
57
- assert isinstance(result, ComplianceResult)
58
-
59
- @patch("scripts.grader.classify_events", side_effect=_mock_compliant_classification)
60
- def test_full_compliance(self, mock_cls, tdd_spec, compliant_trace) -> None:
61
- result = grade(tdd_spec, compliant_trace)
62
- assert result.compliance_rate == 1.0
63
-
64
- @patch("scripts.grader.classify_events", side_effect=_mock_compliant_classification)
65
- def test_all_required_steps_detected(self, mock_cls, tdd_spec, compliant_trace) -> None:
66
- result = grade(tdd_spec, compliant_trace)
67
- required_results = [s for s in result.steps if s.step_id in
68
- ("write_test", "run_test_red", "write_impl", "run_test_green")]
69
- assert all(s.detected for s in required_results)
70
-
71
- @patch("scripts.grader.classify_events", side_effect=_mock_compliant_classification)
72
- def test_optional_step_detected(self, mock_cls, tdd_spec, compliant_trace) -> None:
73
- result = grade(tdd_spec, compliant_trace)
74
- refactor = next(s for s in result.steps if s.step_id == "refactor")
75
- assert refactor.detected is True
76
-
77
- @patch("scripts.grader.classify_events", side_effect=_mock_compliant_classification)
78
- def test_no_hook_promotion_recommended(self, mock_cls, tdd_spec, compliant_trace) -> None:
79
- result = grade(tdd_spec, compliant_trace)
80
- assert result.recommend_hook_promotion is False
81
-
82
- @patch("scripts.grader.classify_events", side_effect=_mock_compliant_classification)
83
- def test_step_evidence_not_empty(self, mock_cls, tdd_spec, compliant_trace) -> None:
84
- result = grade(tdd_spec, compliant_trace)
85
- for step in result.steps:
86
- if step.detected:
87
- assert len(step.evidence) > 0
88
-
89
-
90
- class TestGradeNoncompliant:
91
- @patch("scripts.grader.classify_events", side_effect=_mock_noncompliant_classification)
92
- def test_low_compliance(self, mock_cls, tdd_spec, noncompliant_trace) -> None:
93
- result = grade(tdd_spec, noncompliant_trace)
94
- assert result.compliance_rate < 1.0
95
-
96
- @patch("scripts.grader.classify_events", side_effect=_mock_noncompliant_classification)
97
- def test_write_test_fails_ordering(self, mock_cls, tdd_spec, noncompliant_trace) -> None:
98
- """write_test has before_step=write_impl, but test is written AFTER impl."""
99
- result = grade(tdd_spec, noncompliant_trace)
100
- write_test = next(s for s in result.steps if s.step_id == "write_test")
101
- assert write_test.detected is False
102
-
103
- @patch("scripts.grader.classify_events", side_effect=_mock_noncompliant_classification)
104
- def test_run_test_red_not_detected(self, mock_cls, tdd_spec, noncompliant_trace) -> None:
105
- result = grade(tdd_spec, noncompliant_trace)
106
- run_red = next(s for s in result.steps if s.step_id == "run_test_red")
107
- assert run_red.detected is False
108
-
109
- @patch("scripts.grader.classify_events", side_effect=_mock_noncompliant_classification)
110
- def test_hook_promotion_recommended(self, mock_cls, tdd_spec, noncompliant_trace) -> None:
111
- result = grade(tdd_spec, noncompliant_trace)
112
- assert result.recommend_hook_promotion is True
113
-
114
- @patch("scripts.grader.classify_events", side_effect=_mock_noncompliant_classification)
115
- def test_failure_reasons_present(self, mock_cls, tdd_spec, noncompliant_trace) -> None:
116
- result = grade(tdd_spec, noncompliant_trace)
117
- failed_steps = [s for s in result.steps if not s.detected and s.step_id != "refactor"]
118
- for step in failed_steps:
119
- assert step.failure_reason is not None
120
-
121
-
122
- class TestGradeEdgeCases:
123
- @patch("scripts.grader.classify_events", side_effect=_mock_empty_classification)
124
- def test_empty_trace(self, mock_cls, tdd_spec) -> None:
125
- result = grade(tdd_spec, [])
126
- assert result.compliance_rate == 0.0
127
- assert result.recommend_hook_promotion is True
128
-
129
- @patch("scripts.grader.classify_events", side_effect=_mock_compliant_classification)
130
- def test_compliance_rate_is_ratio_of_required_only(self, mock_cls, tdd_spec, compliant_trace) -> None:
131
- result = grade(tdd_spec, compliant_trace)
132
- assert result.compliance_rate == 1.0
133
-
134
- @patch("scripts.grader.classify_events", side_effect=_mock_compliant_classification)
135
- def test_spec_id_in_result(self, mock_cls, tdd_spec, compliant_trace) -> None:
136
- result = grade(tdd_spec, compliant_trace)
137
- assert result.spec_id == "tdd-workflow"
@@ -1,90 +0,0 @@
1
- """Tests for parser module — JSONL trace and YAML spec parsing."""
2
-
3
- from pathlib import Path
4
-
5
- import pytest
6
-
7
- from scripts.parser import (
8
- ComplianceSpec,
9
- Detector,
10
- ObservationEvent,
11
- Step,
12
- parse_spec,
13
- parse_trace,
14
- )
15
-
16
- FIXTURES = Path(__file__).parent.parent / "fixtures"
17
-
18
-
19
- class TestParseTrace:
20
- def test_parses_compliant_trace(self) -> None:
21
- events = parse_trace(FIXTURES / "compliant_trace.jsonl")
22
- assert len(events) == 5
23
- assert all(isinstance(e, ObservationEvent) for e in events)
24
-
25
- def test_events_sorted_by_timestamp(self) -> None:
26
- events = parse_trace(FIXTURES / "compliant_trace.jsonl")
27
- timestamps = [e.timestamp for e in events]
28
- assert timestamps == sorted(timestamps)
29
-
30
- def test_event_fields(self) -> None:
31
- events = parse_trace(FIXTURES / "compliant_trace.jsonl")
32
- first = events[0]
33
- assert first.tool == "Write"
34
- assert first.session == "sess-001"
35
- assert "test_fib.py" in first.input
36
- assert first.output == "File created"
37
-
38
- def test_parses_noncompliant_trace(self) -> None:
39
- events = parse_trace(FIXTURES / "noncompliant_trace.jsonl")
40
- assert len(events) == 3
41
- assert "src/fib.py" in events[0].input
42
-
43
- def test_empty_file_returns_empty_list(self, tmp_path: Path) -> None:
44
- empty = tmp_path / "empty.jsonl"
45
- empty.write_text("")
46
- events = parse_trace(empty)
47
- assert events == []
48
-
49
- def test_nonexistent_file_raises(self) -> None:
50
- with pytest.raises(FileNotFoundError):
51
- parse_trace(Path("/nonexistent/trace.jsonl"))
52
-
53
-
54
- class TestParseSpec:
55
- def test_parses_tdd_spec(self) -> None:
56
- spec = parse_spec(FIXTURES / "tdd_spec.yaml")
57
- assert isinstance(spec, ComplianceSpec)
58
- assert spec.id == "tdd-workflow"
59
- assert len(spec.steps) == 5
60
-
61
- def test_step_fields(self) -> None:
62
- spec = parse_spec(FIXTURES / "tdd_spec.yaml")
63
- first = spec.steps[0]
64
- assert isinstance(first, Step)
65
- assert first.id == "write_test"
66
- assert first.required is True
67
- assert isinstance(first.detector, Detector)
68
- assert "test file" in first.detector.description
69
- assert first.detector.before_step == "write_impl"
70
-
71
- def test_optional_detector_fields(self) -> None:
72
- spec = parse_spec(FIXTURES / "tdd_spec.yaml")
73
- write_test = spec.steps[0]
74
- assert write_test.detector.after_step is None
75
-
76
- run_test_red = spec.steps[1]
77
- assert run_test_red.detector.after_step == "write_test"
78
- assert run_test_red.detector.before_step == "write_impl"
79
-
80
- def test_scoring_threshold(self) -> None:
81
- spec = parse_spec(FIXTURES / "tdd_spec.yaml")
82
- assert spec.threshold_promote_to_hook == 0.6
83
-
84
- def test_required_vs_optional_steps(self) -> None:
85
- spec = parse_spec(FIXTURES / "tdd_spec.yaml")
86
- required = [s for s in spec.steps if s.required]
87
- optional = [s for s in spec.steps if not s.required]
88
- assert len(required) == 4
89
- assert len(optional) == 1
90
- assert optional[0].id == "refactor"
@@ -1,104 +0,0 @@
1
- <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 700 420" width="700" height="420" font-family="'Segoe UI', system-ui, -apple-system, sans-serif">
2
- <defs>
3
- <linearGradient id="bg" x1="0%" y1="0%" x2="100%" y2="100%">
4
- <stop offset="0%" style="stop-color:#050811"/>
5
- <stop offset="100%" style="stop-color:#0d1117"/>
6
- </linearGradient>
7
- <linearGradient id="mem" x1="0%" y1="0%" x2="0%" y2="100%">
8
- <stop offset="0%" style="stop-color:#059669"/>
9
- <stop offset="100%" style="stop-color:#065f46"/>
10
- </linearGradient>
11
- </defs>
12
-
13
- <rect width="700" height="420" fill="url(#bg)" rx="12"/>
14
- <rect width="700" height="420" fill="none" stroke="#21262d" stroke-width="1" rx="12"/>
15
-
16
- <!-- Header -->
17
- <text x="28" y="42" font-size="16" font-weight="800" fill="#ffffff">70 Agents — The Complete Roster</text>
18
- <rect x="28" y="54" width="644" height="1" fill="#21262d"/>
19
-
20
- <!-- Row 1 -->
21
- <rect x="28" y="68" width="196" height="56" rx="6" fill="#161b22" stroke="#21262d" stroke-width="1"/>
22
- <rect x="28" y="68" width="2.5" height="56" rx="1" fill="#a78bfa"/>
23
- <text x="40" y="88" font-size="11" font-weight="700" fill="#ffffff">kodelyth-advisor</text>
24
- <text x="40" y="105" font-size="10" fill="#484f58">lost / overwhelmed</text>
25
-
26
- <rect x="234" y="68" width="196" height="56" rx="6" fill="#161b22" stroke="#21262d" stroke-width="1"/>
27
- <rect x="234" y="68" width="2.5" height="56" rx="1" fill="#a78bfa"/>
28
- <text x="246" y="88" font-size="11" font-weight="700" fill="#ffffff">pair-programmer</text>
29
- <text x="246" y="105" font-size="10" fill="#484f58">before you write code</text>
30
-
31
- <rect x="440" y="68" width="232" height="56" rx="6" fill="#0d2b1d" stroke="#059669" stroke-opacity="0.4" stroke-width="1"/>
32
- <rect x="440" y="68" width="2.5" height="56" rx="1" fill="url(#mem)"/>
33
- <rect x="568" y="72" width="36" height="14" rx="7" fill="#059669" fill-opacity="0.3" stroke="#059669" stroke-opacity="0.5" stroke-width="1"/>
34
- <text x="586" y="83" font-size="8" fill="#10b981" text-anchor="middle" font-weight="700">ECC</text>
35
- <text x="452" y="88" font-size="11" font-weight="700" fill="#ffffff">kodelyth-memory</text>
36
- <text x="452" y="105" font-size="10" fill="#484f58">local BM25 self-learning</text>
37
-
38
- <!-- Row 2 -->
39
- <rect x="28" y="140" width="196" height="56" rx="6" fill="#161b22" stroke="#21262d" stroke-width="1"/>
40
- <rect x="28" y="140" width="2.5" height="56" rx="1" fill="#ef4444"/>
41
- <text x="40" y="160" font-size="11" font-weight="700" fill="#ffffff">debug-detective</text>
42
- <text x="40" y="177" font-size="10" fill="#484f58">root cause, never guesses</text>
43
-
44
- <rect x="234" y="140" width="196" height="56" rx="6" fill="#161b22" stroke="#21262d" stroke-width="1"/>
45
- <rect x="234" y="140" width="2.5" height="56" rx="1" fill="#f97316"/>
46
- <text x="246" y="160" font-size="11" font-weight="700" fill="#ffffff">silent-failure-hunter</text>
47
- <text x="246" y="177" font-size="10" fill="#484f58">bugs with no error</text>
48
-
49
- <rect x="440" y="140" width="232" height="56" rx="6" fill="#161b22" stroke="#21262d" stroke-width="1"/>
50
- <rect x="440" y="140" width="2.5" height="56" rx="1" fill="#ef4444"/>
51
- <text x="452" y="160" font-size="11" font-weight="700" fill="#ffffff">incident-commander</text>
52
- <text x="452" y="177" font-size="10" fill="#484f58">P0/P1 production triage</text>
53
-
54
- <!-- Row 3 -->
55
- <rect x="28" y="212" width="196" height="56" rx="6" fill="#161b22" stroke="#21262d" stroke-width="1"/>
56
- <rect x="28" y="212" width="2.5" height="56" rx="1" fill="#2563eb"/>
57
- <text x="40" y="232" font-size="11" font-weight="700" fill="#ffffff">security-reviewer</text>
58
- <text x="40" y="249" font-size="10" fill="#484f58">OWASP, auth, secrets</text>
59
-
60
- <rect x="234" y="212" width="196" height="56" rx="6" fill="#161b22" stroke="#21262d" stroke-width="1"/>
61
- <rect x="234" y="212" width="2.5" height="56" rx="1" fill="#2563eb"/>
62
- <text x="246" y="232" font-size="11" font-weight="700" fill="#ffffff">api-guardian</text>
63
- <text x="246" y="249" font-size="10" fill="#484f58">breaking change detection</text>
64
-
65
- <rect x="440" y="212" width="232" height="56" rx="6" fill="#0d2b1d" stroke="#059669" stroke-opacity="0.4" stroke-width="1"/>
66
- <rect x="440" y="212" width="2.5" height="56" rx="1" fill="url(#mem)"/>
67
- <rect x="568" y="216" width="46" height="14" rx="7" fill="#059669" fill-opacity="0.3" stroke="#059669" stroke-opacity="0.5" stroke-width="1"/>
68
- <text x="591" y="227" font-size="8" fill="#10b981" text-anchor="middle" font-weight="700">ECC</text>
69
- <text x="452" y="232" font-size="11" font-weight="700" fill="#ffffff">image-architect</text>
70
- <text x="452" y="249" font-size="10" fill="#484f58">Gemini / DALL-E / SVG</text>
71
-
72
- <!-- Row 4 -->
73
- <rect x="28" y="284" width="196" height="56" rx="6" fill="#161b22" stroke="#21262d" stroke-width="1"/>
74
- <rect x="28" y="284" width="2.5" height="56" rx="1" fill="#f59e0b"/>
75
- <text x="40" y="304" font-size="11" font-weight="700" fill="#ffffff">performance-optimizer</text>
76
- <text x="40" y="321" font-size="10" fill="#484f58">bottlenecks, N+1, bundle</text>
77
-
78
- <rect x="234" y="284" width="196" height="56" rx="6" fill="#161b22" stroke="#21262d" stroke-width="1"/>
79
- <rect x="234" y="284" width="2.5" height="56" rx="1" fill="#f59e0b"/>
80
- <text x="246" y="304" font-size="11" font-weight="700" fill="#ffffff">load-tester</text>
81
- <text x="246" y="321" font-size="10" fill="#484f58">k6, Locust, capacity plan</text>
82
-
83
- <rect x="440" y="284" width="232" height="56" rx="6" fill="#161b22" stroke="#21262d" stroke-width="1"/>
84
- <rect x="440" y="284" width="2.5" height="56" rx="1" fill="#22d3ee"/>
85
- <text x="452" y="304" font-size="11" font-weight="700" fill="#ffffff">migration-guide</text>
86
- <text x="452" y="321" font-size="10" fill="#484f58">React / Next / Node upgrades</text>
87
-
88
- <!-- Footer -->
89
- <rect x="28" y="356" width="644" height="44" rx="6" fill="#161b22" stroke="#21262d" stroke-width="1"/>
90
- <text x="350" y="373" font-size="11" fill="#484f58" text-anchor="middle">+ 50 more: language reviewers, build resolvers, tdd-guide, refactor-cleaner, ux-reviewer, git-rescue ...</text>
91
- <text x="350" y="390" font-size="11" fill="#484f58" text-anchor="middle">All agents load automatically via semantic intent routing — no command names needed</text>
92
-
93
- <!-- Ghost Depth Mark — brand stamp (scale 0.45×) -->
94
- <g transform="translate(606, 120) scale(0.45)" opacity="0.16">
95
- <polyline points="-44,4 12,100 -44,196"
96
- stroke="#ffffff" stroke-width="10"
97
- stroke-linecap="round" stroke-linejoin="round"
98
- fill="none"/>
99
- <polyline points="0,0 88,100 0,200"
100
- stroke="#ffffff" stroke-width="32"
101
- stroke-linecap="round" stroke-linejoin="round"
102
- fill="none"/>
103
- </g>
104
- </svg>
@@ -1,58 +0,0 @@
1
- <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 360" width="640" height="360" font-family="'Segoe UI', system-ui, -apple-system, sans-serif">
2
- <defs>
3
- <linearGradient id="bg" x1="0%" y1="0%" x2="100%" y2="100%">
4
- <stop offset="0%" style="stop-color:#050811"/>
5
- <stop offset="100%" style="stop-color:#0d1117"/>
6
- </linearGradient>
7
- <linearGradient id="termGrad" x1="0%" y1="0%" x2="0%" y2="100%">
8
- <stop offset="0%" style="stop-color:#161b22"/>
9
- <stop offset="100%" style="stop-color:#0d1117"/>
10
- </linearGradient>
11
- </defs>
12
-
13
- <rect width="640" height="360" fill="url(#bg)" rx="12"/>
14
- <rect width="640" height="360" fill="none" stroke="#21262d" stroke-width="1" rx="12"/>
15
-
16
- <!-- Title section -->
17
- <rect x="28" y="24" width="72" height="20" rx="10" fill="#059669" fill-opacity="0.15" stroke="#059669" stroke-opacity="0.4" stroke-width="1"/>
18
- <text x="64" y="38" font-size="9" fill="#10b981" text-anchor="middle" font-weight="700" letter-spacing="0.5">INSTALL</text>
19
-
20
- <text x="28" y="72" font-size="24" font-weight="800" fill="#ffffff">One command. All platforms.</text>
21
- <text x="28" y="96" font-size="13" fill="#6e7681">Works on Claude Code, Windsurf, Cursor, Codex, Antigravity, OpenCode</text>
22
-
23
- <line x1="28" y1="112" x2="612" y2="112" stroke="#21262d" stroke-width="1"/>
24
-
25
- <!-- Terminal -->
26
- <rect x="28" y="124" width="584" height="180" rx="8" fill="url(#termGrad)" stroke="#21262d" stroke-width="1"/>
27
- <rect x="28" y="124" width="584" height="26" rx="8" fill="#161b22"/>
28
- <rect x="28" y="140" width="584" height="10" fill="#161b22"/>
29
- <circle cx="48" cy="137" r="4" fill="#ff5f57"/>
30
- <circle cx="62" cy="137" r="4" fill="#febc2e"/>
31
- <circle cx="76" cy="137" r="4" fill="#28c840"/>
32
- <text x="320" y="141" font-size="10" fill="#484f58" text-anchor="middle">terminal</text>
33
-
34
- <text x="48" y="168" font-family="'Courier New', Courier, monospace" font-size="12" fill="#10b981">$</text>
35
- <text x="60" y="168" font-family="'Courier New', Courier, monospace" font-size="12" fill="#e6edf3"> npx kodelyth-ecc</text>
36
- <text x="48" y="184" font-family="'Courier New', Courier, monospace" font-size="11" fill="#484f58"> # or for Windsurf:</text>
37
- <text x="48" y="200" font-family="'Courier New', Courier, monospace" font-size="12" fill="#e6edf3"> npx kodelyth-ecc --target windsurf-project</text>
38
- <text x="48" y="216" font-family="'Courier New', Courier, monospace" font-size="11" fill="#484f58"> # or Antigravity:</text>
39
- <text x="48" y="232" font-family="'Courier New', Courier, monospace" font-size="12" fill="#e6edf3"> npx kodelyth-ecc --target antigravity</text>
40
- <text x="48" y="256" font-family="'Courier New', Courier, monospace" font-size="11" fill="#10b981"> Installing v1.8.0 — 70 agents, 194 skills, 97 commands...</text>
41
- <text x="48" y="292" font-family="'Courier New', Courier, monospace" font-size="11" fill="#10b981"> Done. Type /kodelyth-quickstart to begin.</text>
42
-
43
- <!-- Footer -->
44
- <text x="28" y="332" font-size="11" fill="#484f58">Node.js 18+ required</text>
45
- <text x="612" y="332" font-size="11" fill="#484f58" text-anchor="end">github.com/sifxprime/kodelyth-ecc</text>
46
-
47
- <!-- Ghost Depth Mark — brand stamp (scale 0.4×) -->
48
- <g transform="translate(550, 100) scale(0.4)" opacity="0.16">
49
- <polyline points="-44,4 12,100 -44,196"
50
- stroke="#ffffff" stroke-width="10"
51
- stroke-linecap="round" stroke-linejoin="round"
52
- fill="none"/>
53
- <polyline points="0,0 88,100 0,200"
54
- stroke="#ffffff" stroke-width="32"
55
- stroke-linecap="round" stroke-linejoin="round"
56
- fill="none"/>
57
- </g>
58
- </svg>