loreli 0.0.0 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (88) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +670 -97
  3. package/bin/loreli.js +89 -0
  4. package/package.json +74 -14
  5. package/packages/README.md +101 -0
  6. package/packages/action/README.md +98 -0
  7. package/packages/action/src/index.js +656 -0
  8. package/packages/agent/README.md +517 -0
  9. package/packages/agent/src/backends/claude.js +287 -0
  10. package/packages/agent/src/backends/codex.js +278 -0
  11. package/packages/agent/src/backends/cursor.js +294 -0
  12. package/packages/agent/src/backends/index.js +329 -0
  13. package/packages/agent/src/base.js +138 -0
  14. package/packages/agent/src/cli.js +198 -0
  15. package/packages/agent/src/factory.js +119 -0
  16. package/packages/agent/src/index.js +12 -0
  17. package/packages/agent/src/models.js +141 -0
  18. package/packages/agent/src/output.js +62 -0
  19. package/packages/agent/src/session.js +162 -0
  20. package/packages/agent/src/trace.js +186 -0
  21. package/packages/config/README.md +833 -0
  22. package/packages/config/src/defaults.js +134 -0
  23. package/packages/config/src/index.js +192 -0
  24. package/packages/config/src/schema.js +273 -0
  25. package/packages/config/src/validate.js +160 -0
  26. package/packages/context/README.md +165 -0
  27. package/packages/context/src/index.js +198 -0
  28. package/packages/hub/README.md +338 -0
  29. package/packages/hub/src/base.js +154 -0
  30. package/packages/hub/src/github.js +1558 -0
  31. package/packages/hub/src/index.js +79 -0
  32. package/packages/hub/src/labels.js +48 -0
  33. package/packages/identity/README.md +288 -0
  34. package/packages/identity/src/index.js +620 -0
  35. package/packages/identity/src/themes/avatar.js +217 -0
  36. package/packages/identity/src/themes/digimon.js +217 -0
  37. package/packages/identity/src/themes/dragonball.js +217 -0
  38. package/packages/identity/src/themes/lotr.js +217 -0
  39. package/packages/identity/src/themes/marvel.js +217 -0
  40. package/packages/identity/src/themes/pokemon.js +217 -0
  41. package/packages/identity/src/themes/starwars.js +217 -0
  42. package/packages/identity/src/themes/transformers.js +217 -0
  43. package/packages/identity/src/themes/zelda.js +217 -0
  44. package/packages/knowledge/README.md +237 -0
  45. package/packages/knowledge/src/index.js +412 -0
  46. package/packages/log/README.md +93 -0
  47. package/packages/log/src/index.js +252 -0
  48. package/packages/marker/README.md +200 -0
  49. package/packages/marker/src/index.js +184 -0
  50. package/packages/mcp/README.md +279 -0
  51. package/packages/mcp/instructions.md +121 -0
  52. package/packages/mcp/scaffolding/.agents/skills/loreli-context/SKILL.md +89 -0
  53. package/packages/mcp/scaffolding/ISSUE_TEMPLATE/config.yml +2 -0
  54. package/packages/mcp/scaffolding/ISSUE_TEMPLATE/loreli.yml +83 -0
  55. package/packages/mcp/scaffolding/loreli.yml +453 -0
  56. package/packages/mcp/scaffolding/mcp-configs/.codex/config.toml +3 -0
  57. package/packages/mcp/scaffolding/mcp-configs/.cursor/mcp.json +11 -0
  58. package/packages/mcp/scaffolding/mcp-configs/.mcp.json +11 -0
  59. package/packages/mcp/scaffolding/pull-request.md +23 -0
  60. package/packages/mcp/src/index.js +571 -0
  61. package/packages/mcp/src/tools/agents.js +429 -0
  62. package/packages/mcp/src/tools/context.js +199 -0
  63. package/packages/mcp/src/tools/github.js +1199 -0
  64. package/packages/mcp/src/tools/hitl.js +149 -0
  65. package/packages/mcp/src/tools/index.js +17 -0
  66. package/packages/mcp/src/tools/start.js +835 -0
  67. package/packages/mcp/src/tools/status.js +146 -0
  68. package/packages/mcp/src/tools/work.js +124 -0
  69. package/packages/orchestrator/README.md +192 -0
  70. package/packages/orchestrator/src/index.js +1226 -0
  71. package/packages/planner/README.md +168 -0
  72. package/packages/planner/src/index.js +1166 -0
  73. package/packages/review/README.md +129 -0
  74. package/packages/review/src/index.js +1283 -0
  75. package/packages/risk/README.md +119 -0
  76. package/packages/risk/src/index.js +428 -0
  77. package/packages/session/README.md +165 -0
  78. package/packages/session/src/index.js +215 -0
  79. package/packages/test-utils/README.md +96 -0
  80. package/packages/test-utils/src/index.js +354 -0
  81. package/packages/tmux/README.md +261 -0
  82. package/packages/tmux/src/index.js +452 -0
  83. package/packages/workflow/README.md +313 -0
  84. package/packages/workflow/src/index.js +481 -0
  85. package/packages/workflow/src/proof-of-life.js +74 -0
  86. package/packages/workspace/README.md +143 -0
  87. package/packages/workspace/src/index.js +1076 -0
  88. package/index.js +0 -8
@@ -0,0 +1,168 @@
1
+ # loreli/planner
2
+
3
+ Planning workflow for Loreli's orchestration pipeline. Extends the `Workflow` base class to manage planner agents — dispatching objectives, adversarial plan review via yin/yang pairing, and promoting approved discussions to issues.
4
+
5
+ ## Research Findings
6
+
7
+ No existing npm packages cover planning workflow with cross-provider adversarial review. This is domain-specific to Loreli's yin/yang agent model.
8
+
9
+ ## Architecture
10
+
11
+ The planner uses **GitHub Discussions** as its planning primitive. Discussions in a "Loreli" category serve as the workspace where plans are proposed, reviewed, revised, and eventually promoted to real issues.
12
+
13
+ Labels act as a state machine on each discussion:
14
+
15
+ | State | Labels Present | Next Action |
16
+ |-------|---------------|-------------|
17
+ | Needs review | _(none)_ | Reviewer dispatched |
18
+ | Changes requested | `loreli:changes-requested` | Planner revises |
19
+ | Blocked | `loreli:blocked` + `loreli:changes-requested` | Parked until dependency resolves |
20
+ | Approved | `loreli:approved` | Promoted to issue |
21
+
22
+ The reactor tick pipeline runs handlers in order: **planner-hydrate → planner-recover → unblock → revise → review → promote → link → planner-reap**. `unblock` runs before review so freshly unblocked discussions can enter review on the same tick.
23
+
24
+ ```mermaid
25
+ flowchart LR
26
+ H["planner-hydrate"] --> R["planner-recover"]
27
+ R --> U["unblock"]
28
+ U --> V["revise"]
29
+ V --> W["review"]
30
+ W --> P["promote"]
31
+ P --> L["link"]
32
+ L --> X["planner-reap"]
33
+ ```
34
+
35
+ ### Blocker Detection
36
+
37
+ When `revise()` processes a `changes-requested` discussion, it scans comments for `#N` issue references and uses keyword-based heuristics (via `knowledge.classifyRefs()`) to distinguish dependency blockers from informational references. No LLM dependency.
38
+
39
+ - **Blockers** (e.g., "depends on #5 being merged") — verified against `hub.issue()`. If open, the discussion is parked with `loreli:blocked`.
40
+ - **Informational** (e.g., "see #3 for prior art") — ignored, normal revision continues.
41
+
42
+ The `unblock()` handler checks blocked discussions every tick. When all blockers resolve, it removes both labels so the discussion re-enters review fresh.
43
+
44
+ ### Tiebreaker Protocol
45
+
46
+ When a discussion has no external blockers but cycles through `changes-requested` indefinitely, `_revisionRounds` tracks how many times `revise()` has processed it. Behavior varies by round count vs `watch.maxRounds` (default 3):
47
+
48
+ | Condition | Behavior |
49
+ |-----------|----------|
50
+ | `rounds < maxRounds` | Normal revision dispatch |
51
+ | `rounds === maxRounds` | Tiebreaker revision — planner gets a focused prompt to fundamentally restructure the plan |
52
+ | `rounds > maxRounds` | HITL escalation — `loreli:needs-attention` applied and human reviewers notified |
53
+
54
+ The reviewer prompt also changes based on rounds: normal reviews use `plan-reviewer.md`, tiebreaker reviews use `tiebreaker-reviewer.md` with pragmatic evaluation guidance.
55
+
56
+ ### Dedicated Reviewer Prompts
57
+
58
+ Plan review uses two dedicated prompts (separate from the PR-review `reviewer.md`):
59
+
60
+ | Template | Location | Purpose |
61
+ |----------|----------|---------|
62
+ | `plan-reviewer.md` | `prompts/` | Standard plan review — scope, clarity, feasibility |
63
+ | `tiebreaker-reviewer.md` | `prompts/` | Pragmatic tiebreaker review — approve if directionally correct |
64
+
65
+ ## API Reference
66
+
67
+ ### `PlannerWorkflow` (extends Workflow)
68
+
69
+ ```js
70
+ import { PlannerWorkflow } from 'loreli/planner';
71
+
72
+ const planner = new PlannerWorkflow(orchestrator, hub);
73
+ ```
74
+
75
+ The constructor accepts the orchestrator (EventEmitter) and hub (GitHub API).
76
+
77
+ #### Static Properties
78
+
79
+ | Property | Value | Description |
80
+ |----------|-------|-------------|
81
+ | `role` | `'planner'` | Agent role this workflow manages |
82
+ | `template` | `prompts/planner.md` | Mustache template for planner prompts |
83
+
84
+ ### Methods
85
+
86
+ #### `planner.plan(repo, objective)` → Promise\<{planners, categoryId}\>
87
+
88
+ Dispatch the planning objective to all available planner agents. Finds the "Loreli" discussion category, renders the planner prompt with the objective, and sends to each agent.
89
+
90
+ The example below shows how `start_planning` triggers the planning workflow. The orchestrator coordinates agent lifecycle, while the planner package owns the planning logic:
91
+
92
+ ```js
93
+ const result = await planner.plan('owner/repo', 'Build auth module');
94
+ // { planners: ['optimus-0'], categoryId: 'DIC_kwDO...' }
95
+ ```
96
+
97
+ #### `planner.unblock(repo)` → Promise\<void\>
98
+
99
+ Check blocked discussions for resolved dependencies. For each discussion in the `_blocked` map, verifies whether all blocking issues/PRs have been closed. When resolved, removes `loreli:blocked` and `loreli:changes-requested` labels so the discussion re-enters review.
100
+
101
+ Runs FIRST in the tick pipeline.
102
+
103
+ #### `planner.revise(repo)` → Promise\<Array\<{number, planner}\>\>
104
+
105
+ Handle discussions with `loreli:changes-requested` label (excluding blocked discussions). For each discussion:
106
+
107
+ 1. Scans comments for issue references and classifies them using keyword heuristics (`extractRefs` + `classifyRefs`)
108
+ 2. Parks blocked discussions (applies `loreli:blocked`, stores in `_blocked` map)
109
+ 3. Increments `_revisionRounds` for non-blocked discussions
110
+ 4. At `maxRounds`: dispatches tiebreaker revision with special prompt
111
+ 5. Beyond `maxRounds`: HITL escalation — applies `loreli:needs-attention` and notifies human reviewers
112
+ 6. Below `maxRounds`: dispatches normal revision
113
+
114
+ Runs SECOND in the tick pipeline. Returns an empty array if no discussions need revision.
115
+
116
+ #### `planner.review(repo)` → Promise\<{reviewer, discussions}\>
117
+
118
+ Dispatch unlabeled discussions (no `loreli:approved`, `loreli:changes-requested`, or `loreli:blocked`) to a reviewer. In dual-side mode it prefers yin/yang pairing; in single-side mode it falls back to same-side reviewer enlistment. Selects the reviewer template based on revision round count: `plan-reviewer.md` for normal reviews, `tiebreaker-reviewer.md` when `_revisionRounds >= maxRounds`.
119
+
120
+ Returns `{ reviewer: null, discussions: 0 }` when no reviewer can be determined.
121
+
122
+ #### `planner.promote(repo)` → Promise\<Array\<{number, title}\>\>
123
+
124
+ Convert approved discussions (with `loreli:approved` label) to real GitHub issues. Posts a closing comment linking to the new issue, then closes and locks the discussion.
125
+
126
+ #### `planner.escalate(repo, title, body, fallbackIdentity?)` → Promise\<{discussionId, categoryId}\>
127
+
128
+ Signal a concern or feature idea by creating a discussion in the Loreli category. The discussion enters the standard review → revise → approve → promote pipeline.
129
+
130
+ ### Reactor Handlers
131
+
132
+ The planner registers eight handlers with the orchestrator's reactor:
133
+
134
+ ```js
135
+ planner.reactor()
136
+ // → {
137
+ // 'planner-hydrate': fn,
138
+ // 'planner-recover': fn,
139
+ // unblock: fn,
140
+ // revise: fn,
141
+ // review: fn,
142
+ // promote: fn,
143
+ // link: fn,
144
+ // 'planner-reap': fn
145
+ // }
146
+ ```
147
+
148
+ ### Events
149
+
150
+ The planner subscribes to no orchestrator events currently. All coordination happens through explicit method calls from the MCP tools layer.
151
+
152
+ ## Errors
153
+
154
+ | Error | When | Resolution |
155
+ |-------|------|------------|
156
+ | `No planner agents available` | plan() called with no planner agents | Add a planner agent first |
157
+ | `Discussion category "Loreli" not found` | plan() called without the Loreli category | Enable GitHub Discussions and create the category in repo settings |
158
+ | `Cannot escalate without an identity` | escalate() called without agents or fallback identity | Add at least one agent before escalating |
159
+
160
+ ## Autonomous Operation
161
+
162
+ Planner agents run in headless tmux panes with no human operator. The `Workflow.render()` method automatically prepends a shared autonomous-mode preamble to every rendered prompt, instructing agents to never ask clarifying questions, skip interactive skills, and proceed with best judgment. See the `loreli/workflow` README for details on the preamble. The orchestrator's stall detection acts as a safety net for agents that block despite these directives.
163
+
164
+ ## Scope Boundary
165
+
166
+ **In scope**: Plan dispatch, adversarial plan review, discussion revision, discussion promotion, planner prompt rendering.
167
+
168
+ **Out of scope**: Agent lifecycle, escalation UI (handled by MCP tools), discussion category creation (manual via GitHub settings).