@navels/neal 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.
Files changed (170) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +527 -0
  3. package/SECURITY.md +91 -0
  4. package/config.yml +104 -0
  5. package/dist/neal/activity-footer.js +177 -0
  6. package/dist/neal/activity-reporting.js +1 -0
  7. package/dist/neal/adjudicator/artifacts.js +58 -0
  8. package/dist/neal/adjudicator/blocked-adjudicator.js +223 -0
  9. package/dist/neal/adjudicator/contracts.js +139 -0
  10. package/dist/neal/adjudicator/execute.js +611 -0
  11. package/dist/neal/adjudicator/final-completion.js +104 -0
  12. package/dist/neal/adjudicator/planning.js +145 -0
  13. package/dist/neal/adjudicator/specs.js +453 -0
  14. package/dist/neal/agents/prompts.js +120 -0
  15. package/dist/neal/agents/rounds.js +706 -0
  16. package/dist/neal/agents/schemas.js +832 -0
  17. package/dist/neal/agents/structured-coder.js +82 -0
  18. package/dist/neal/agents/structured-json.js +528 -0
  19. package/dist/neal/agents.js +4 -0
  20. package/dist/neal/atomic-write.js +18 -0
  21. package/dist/neal/blocked-guidance.js +406 -0
  22. package/dist/neal/cli.js +471 -0
  23. package/dist/neal/commands/check.js +401 -0
  24. package/dist/neal/commands/compat.js +807 -0
  25. package/dist/neal/commands/interactive-activity.js +57 -0
  26. package/dist/neal/commands/new-run.js +79 -0
  27. package/dist/neal/commands/plan-and-execute.js +44 -0
  28. package/dist/neal/commands/recovery-guidance.js +217 -0
  29. package/dist/neal/commands/resume-run.js +395 -0
  30. package/dist/neal/commands/review.js +21 -0
  31. package/dist/neal/commands/runtime.js +557 -0
  32. package/dist/neal/commands/setup.js +596 -0
  33. package/dist/neal/commands/squash.js +113 -0
  34. package/dist/neal/commands/status.js +33 -0
  35. package/dist/neal/commands/writer-exit-codes.js +42 -0
  36. package/dist/neal/commit-message.js +17 -0
  37. package/dist/neal/config.js +432 -0
  38. package/dist/neal/context/artifacts.js +140 -0
  39. package/dist/neal/context/context.js +324 -0
  40. package/dist/neal/context/inline-review-context.js +131 -0
  41. package/dist/neal/context/reviewer-context.js +166 -0
  42. package/dist/neal/context/shared.js +117 -0
  43. package/dist/neal/context/types.js +1 -0
  44. package/dist/neal/diagnostic.js +208 -0
  45. package/dist/neal/execute-finalization.js +5 -0
  46. package/dist/neal/final-completion-review.js +188 -0
  47. package/dist/neal/final-completion.js +229 -0
  48. package/dist/neal/git.js +339 -0
  49. package/dist/neal/index.js +135 -0
  50. package/dist/neal/interactive-controls.js +85 -0
  51. package/dist/neal/logger.js +102 -0
  52. package/dist/neal/manual-gates.js +121 -0
  53. package/dist/neal/orchestrator/artifacts.js +70 -0
  54. package/dist/neal/orchestrator/completion.js +531 -0
  55. package/dist/neal/orchestrator/failures.js +31 -0
  56. package/dist/neal/orchestrator/notifications.js +175 -0
  57. package/dist/neal/orchestrator/phases/coder.js +516 -0
  58. package/dist/neal/orchestrator/phases/planning.js +540 -0
  59. package/dist/neal/orchestrator/phases/recovery.js +798 -0
  60. package/dist/neal/orchestrator/phases/review.js +136 -0
  61. package/dist/neal/orchestrator/phases/shared.js +279 -0
  62. package/dist/neal/orchestrator/run-loop.js +113 -0
  63. package/dist/neal/orchestrator/split-plan.js +235 -0
  64. package/dist/neal/orchestrator/transitions.js +309 -0
  65. package/dist/neal/orchestrator.js +215 -0
  66. package/dist/neal/phase-display.js +27 -0
  67. package/dist/neal/plan-doc.js +154 -0
  68. package/dist/neal/plan-queue.js +1092 -0
  69. package/dist/neal/plan-refinement.js +39 -0
  70. package/dist/neal/plan-validation.js +525 -0
  71. package/dist/neal/progress.js +237 -0
  72. package/dist/neal/prompts/assert-builder.js +13 -0
  73. package/dist/neal/prompts/execute.js +290 -0
  74. package/dist/neal/prompts/guidance.js +70 -0
  75. package/dist/neal/prompts/planning.js +313 -0
  76. package/dist/neal/prompts/review-doctrine.js +142 -0
  77. package/dist/neal/prompts/shared.js +101 -0
  78. package/dist/neal/prompts/specialized.js +212 -0
  79. package/dist/neal/prompts/specs.js +572 -0
  80. package/dist/neal/providers/anthropic-claude.js +1599 -0
  81. package/dist/neal/providers/detection.js +139 -0
  82. package/dist/neal/providers/generic-agentic-tools.js +586 -0
  83. package/dist/neal/providers/generic-agentic.js +1238 -0
  84. package/dist/neal/providers/liveness.js +151 -0
  85. package/dist/neal/providers/openai-codex.js +1014 -0
  86. package/dist/neal/providers/openai-compatible.js +654 -0
  87. package/dist/neal/providers/registry.js +389 -0
  88. package/dist/neal/providers/telemetry.js +208 -0
  89. package/dist/neal/providers/types.js +21 -0
  90. package/dist/neal/recovery-artifacts.js +50 -0
  91. package/dist/neal/resume-decision.js +220 -0
  92. package/dist/neal/resume-planner.js +265 -0
  93. package/dist/neal/retrospective.js +391 -0
  94. package/dist/neal/review-debt.js +18 -0
  95. package/dist/neal/review-findings/artifacts.js +173 -0
  96. package/dist/neal/review-findings/prompts.js +172 -0
  97. package/dist/neal/review-findings/provider.js +330 -0
  98. package/dist/neal/review-findings/run.js +373 -0
  99. package/dist/neal/review-findings/types.js +1 -0
  100. package/dist/neal/review-mode.js +67 -0
  101. package/dist/neal/review.js +137 -0
  102. package/dist/neal/run-lock.js +334 -0
  103. package/dist/neal/run-metrics.js +355 -0
  104. package/dist/neal/run-narrative-types.js +1 -0
  105. package/dist/neal/run-narrative.js +1374 -0
  106. package/dist/neal/run-registry.js +218 -0
  107. package/dist/neal/run-status.js +25 -0
  108. package/dist/neal/scopes.js +451 -0
  109. package/dist/neal/sensitive-text.js +8 -0
  110. package/dist/neal/squash-message.js +379 -0
  111. package/dist/neal/squash.js +591 -0
  112. package/dist/neal/state-invariants.js +496 -0
  113. package/dist/neal/state-views.js +344 -0
  114. package/dist/neal/state.js +887 -0
  115. package/dist/neal/status-footer.js +258 -0
  116. package/dist/neal/status.js +1260 -0
  117. package/dist/neal/storage-paths.js +57 -0
  118. package/dist/neal/support.js +58 -0
  119. package/dist/neal/terminal-narrator.js +435 -0
  120. package/dist/neal/types.js +1 -0
  121. package/dist/neal/verification-events.js +81 -0
  122. package/dist/neal/version.js +37 -0
  123. package/dist/neal/worktree-status.js +137 -0
  124. package/dist/notifier.js +44 -0
  125. package/docs/ADJUDICATOR_INVENTORY.md +310 -0
  126. package/docs/PROMPT_SPECS.md +266 -0
  127. package/docs/README.md +22 -0
  128. package/docs/architecture.md +113 -0
  129. package/docs/assets/neal-execution-flow.png +0 -0
  130. package/docs/automation.md +65 -0
  131. package/docs/comparison.md +105 -0
  132. package/docs/compat.md +269 -0
  133. package/docs/compatible-models.md +135 -0
  134. package/docs/demo.md +55 -0
  135. package/docs/maintenance.md +64 -0
  136. package/docs/plan-format.md +213 -0
  137. package/docs/providers.md +751 -0
  138. package/docs/release.md +147 -0
  139. package/docs/state-machine.md +266 -0
  140. package/docs/storage.md +207 -0
  141. package/docs/troubleshooting.md +152 -0
  142. package/examples/compat/add-edit-verify/PLAN.md +29 -0
  143. package/examples/compat/add-edit-verify/broken.diff +8 -0
  144. package/examples/compat/add-edit-verify/good.diff +8 -0
  145. package/examples/compat/add-edit-verify/package.json +5 -0
  146. package/examples/compat/add-edit-verify/src/add.js +2 -0
  147. package/examples/compat/add-edit-verify/test/add.test.js +9 -0
  148. package/examples/compat/is-even-add-test/PLAN.md +30 -0
  149. package/examples/compat/is-even-add-test/broken.diff +11 -0
  150. package/examples/compat/is-even-add-test/good.diff +11 -0
  151. package/examples/compat/is-even-add-test/package.json +5 -0
  152. package/examples/compat/is-even-add-test/src/is-even.js +3 -0
  153. package/examples/compat/is-even-add-test/test/is-even.test.js +9 -0
  154. package/examples/compat/manifest.json +60 -0
  155. package/examples/compat/plan-greeting/ISSUE.md +25 -0
  156. package/examples/compat/plan-greeting/package.json +5 -0
  157. package/examples/compat/plan-greeting/src/greet.js +2 -0
  158. package/examples/compat/plan-greeting/test/greet.test.js +8 -0
  159. package/examples/compat/reverse-grep-edit/PLAN.md +32 -0
  160. package/examples/compat/reverse-grep-edit/broken.diff +12 -0
  161. package/examples/compat/reverse-grep-edit/good.diff +12 -0
  162. package/examples/compat/reverse-grep-edit/package.json +5 -0
  163. package/examples/compat/reverse-grep-edit/src/strings.js +10 -0
  164. package/examples/compat/reverse-grep-edit/test/strings.test.js +15 -0
  165. package/examples/issue-triage-js/PLAN.md +83 -0
  166. package/examples/issue-triage-js/README.md +76 -0
  167. package/examples/issue-triage-js/package.json +9 -0
  168. package/examples/issue-triage-js/src/issue-triage.js +87 -0
  169. package/examples/issue-triage-js/test/issue-triage.test.js +107 -0
  170. package/package.json +70 -0
@@ -0,0 +1,152 @@
1
+ # Troubleshooting
2
+
3
+ Symptom / cause / fix, in the order you'll hit them. Quoted text is what neal
4
+ prints. When in doubt: `neal check`, then `.neal/runs/<run-id>/stderr.log`.
5
+
6
+ ## Install and first run
7
+
8
+ **Symptom:** install or startup fails on an old Node.
9
+ **Cause:** neal requires Node.js >= 22.13 (`engines`); `.nvmrc` pins `24`.
10
+ **Fix:** `nvm use` in this repo, or upgrade Node before `npm install -g @navels/neal`.
11
+
12
+ **Symptom:** `pnpm: command not found` when running from source.
13
+ **Cause:** the source path uses pnpm >= 11 via corepack.
14
+ **Fix:** `corepack enable && corepack prepare pnpm@11 --activate`, then `pnpm install`.
15
+
16
+ **Symptom:** `neal` not found after `pnpm run dev:link`.
17
+ **Cause:** the dev link registers `bin.neal` with the active Node installation only.
18
+ **Fix:** diagnose with `command -v neal` and `npm list -g --depth 0 --link=true`.
19
+ After source changes, `pnpm build` again so the linked command sees `dist/`.
20
+
21
+ **Symptom:** `neal setup` lists a provider with `- runtime not found`.
22
+ **Cause:** setup detects local runtime surfaces only (the `@openai/codex-sdk`
23
+ entrypoint; an SDK-bundled or on-PATH `claude` executable) — never auth.
24
+ **Fix:** install the provider's SDK/CLI, then rerun `neal setup` and `neal check`.
25
+
26
+ ## Provider auth failures (`neal check`)
27
+
28
+ `neal check` sends one small prompt per unique configured provider/model
29
+ (interactive TTY only — non-interactive input prints
30
+ `Provider verification skipped: non-interactive input.`). Known failures:
31
+
32
+ **Symptom:** `Claude Code refused bypass-permissions mode while running as root.`
33
+ **Cause:** the Claude provider uses `permissionMode: bypassPermissions`, which
34
+ Claude Code refuses under root/sudo.
35
+ **Fix:** run neal from a non-root user, then `neal check` again.
36
+
37
+ **Symptom:** `OpenAI Codex has not trusted this directory yet.`
38
+ **Cause:** the Codex CLI's own per-directory trust prompt has not been answered.
39
+ **Fix:** complete the provider's normal local trust/setup step for the current
40
+ directory (e.g. run the `codex` CLI there once), then `neal check` again.
41
+
42
+ **Symptom:** `OpenAI Codex could not persist the check session.`
43
+ **Fix:** transient; run `neal check` again. If it repeats, redo the provider's
44
+ local setup.
45
+
46
+ **Symptom:** a `not logged in` message from either vendor CLI.
47
+ **Cause:** provider auth is provider-owned; neal never collects credentials.
48
+ **Fix:** complete the provider's normal local auth setup (the `codex` / `claude`
49
+ login flows), then `neal check` again.
50
+
51
+ **Symptom:** `Set providers.openai_compatible.base_url (or OPENAI_COMPATIBLE_BASE_URL) and OPENAI_COMPATIBLE_API_KEY before running Neal.`
52
+ (or `No model is resolvable for the openai-compatible ... role`).
53
+ **Cause:** `openai-compatible` / `generic-agentic` resolve settings config-first
54
+ with env fallbacks: `base_url` → `OPENAI_COMPATIBLE_BASE_URL`; the key is read
55
+ from the env var named by `api_key_env` (default `OPENAI_COMPATIBLE_API_KEY`);
56
+ model from `agent.<role>.model` → `default_model` → `OPENAI_COMPATIBLE_MODEL`.
57
+ **Fix:** set the missing key. For OpenRouter: `base_url: https://openrouter.ai/api/v1`,
58
+ `api_key_env: OPENROUTER_API_KEY`, and export `OPENROUTER_API_KEY`. (OpenRouter
59
+ wraps upstream 429s in HTTP-200 bodies; neal unwraps and retries them.)
60
+
61
+ ## Runs that won't start
62
+
63
+ **Symptom:** plan validation errors such as
64
+ `Missing required `## Execution Shape` section.` or
65
+ ``## Execution Shape` must contain exactly one non-empty line.`
66
+ **Cause:** every executable plan must declare exactly one of
67
+ `executionShape: one_shot | multi_scope | multi_scope_unknown`; `multi_scope`
68
+ also requires an `## Execution Queue` with contiguous `### Scope N:` headings,
69
+ each carrying `- Goal:` / `- Verification:` / `- Success Condition:` bullets.
70
+ **Fix:** see [plan-format.md](plan-format.md), or let `neal plan` refine the
71
+ document into executable shape.
72
+
73
+ **Symptom:** a missing-config error naming `agent.coder.provider` or
74
+ `agent.reviewer.provider`.
75
+ **Cause:** fresh writer runs require explicit coder and reviewer providers;
76
+ built-in defaults are not enough.
77
+ **Fix:** `neal setup`, then `neal check`.
78
+
79
+ **Symptom:** `Cannot start neal execute with a dirty worktree:` followed by
80
+ `git status` lines (queues: `Cannot continue neal run with a dirty worktree:`).
81
+ **Cause:** writer admission requires a clean worktree; only the selected plan
82
+ document and neal-owned paths (`.neal/`) are exempt. Leftover reviewer scratch
83
+ (`build_review/`, `scratch/`, …) gets a
84
+ `Likely Neal reviewer scratch leakage detected:` diagnostic but still blocks —
85
+ the paths are not proven neal-owned.
86
+ **Fix:** use `neal resume` for in-progress scope work, or commit, stash, move,
87
+ or remove the dirty paths and start clean.
88
+
89
+ **Symptom:** a Git precondition error before any provider runs.
90
+ **Fix:** writer commands require a Git repository with an existing `HEAD`
91
+ commit; create the initial baseline commit first.
92
+
93
+ ## Stuck or blocked runs
94
+
95
+ **Symptom:** the run stops with `status: "blocked"` (exit code 2).
96
+ **Cause:** blocked is a controlled state, not a failure: the coder hit
97
+ something it may not resolve alone, and attended runs wait for guidance.
98
+ **Fix:** `neal status` explains the blocker and prints the exact
99
+ `neal resume --run <run-id> --message "..."` command. `--message` is only
100
+ accepted in that waiting-for-guidance state.
101
+
102
+ **Symptom:** `effectiveStatus: "waiting_for_manual_gate"`.
103
+ **Cause:** the scope reached expected human work; instructions are in the
104
+ run-local `GATE-<id>.md` file shown by `neal status`.
105
+ **Fix:** do the manual step, then `neal resume --run <run-id>` — it re-runs
106
+ the gate's checks and resumes the scope when they pass. No `--message` here.
107
+
108
+ **Symptom:** a run seems hung or died silently.
109
+ **Where to look:** raw detail is persisted even when hidden from the terminal:
110
+ `.neal/runs/<run-id>/stderr.log` (full transcript) and
111
+ `.neal/runs/<run-id>/events.ndjson` (event log; startup-silence retries appear
112
+ as `provider.turn_liveness_*` events). On a live TTY, press `v` for the
113
+ low-level detail view. `neal status --json --run <run-id>` carries the latest
114
+ `providerError` classification.
115
+
116
+ ## Lock issues
117
+
118
+ The writer lock is `.neal/active-run.lock`; writer processes remove it on
119
+ normal shutdown and on SIGINT/SIGTERM.
120
+
121
+ **Symptom:** `another Neal writer run is active in this checkout`.
122
+ **Fix:** as printed — `neal resume --run <run-id>` to continue that run, or
123
+ wait for it to finish.
124
+
125
+ **Symptom:** `another Neal process is already resuming this run` (with
126
+ `owning pid`). A live same-run lock under another pid is refused, not shared.
127
+ **Fix:** wait for that process, or inspect with `neal status --run <run-id>`.
128
+
129
+ **Symptom:** `stale Neal writer lock found in this checkout` /
130
+ `no process with that PID is running on this host.`
131
+ **Fix:** `neal resume --run <run-id>` clears stale same-host locks itself; if
132
+ you're starting different work instead, inspect the run, then remove the lock
133
+ file the message names.
134
+
135
+ **Symptom:** `Neal writer lock belongs to another host`.
136
+ **Fix:** inspect the other host before removing the lock; the message prints
137
+ the exact resume command for that checkout.
138
+
139
+ **Symptom:** `could not read the active Neal writer lock`.
140
+ **Fix:** as printed: inspect the named lock file before starting another
141
+ writer run; remove it only after confirming no writer process is active.
142
+
143
+ ## "It squashed when I didn't want it to"
144
+
145
+ **Symptom:** per-scope commits are gone after a completed run.
146
+ **Cause:** completed `neal execute` / `neal run` runs squash run-owned commits
147
+ by default, without confirmation.
148
+ **Fix:** pass `--no-squash` to keep individual commits. The squashed range is
149
+ recorded in `.neal/runs/<run-id>/SQUASH_RESULT.json` and under `squash`
150
+ (`originalBaseCommit` / `replacementCommit`) in `neal status --json`.
151
+ Standalone `neal squash` is the opposite: it previews, requires interactive TTY
152
+ confirmation, and preserves later commits by replaying them on top.
@@ -0,0 +1,29 @@
1
+ # Compat fixture: fix `add`
2
+
3
+ ## Execution Shape
4
+
5
+ executionShape: one_shot
6
+
7
+ ## Objective
8
+
9
+ `src/add.js` exports an `add(a, b)` helper that is supposed to return the sum of
10
+ its two arguments, but it currently returns the difference. The test in
11
+ `test/add.test.js` fails because of this bug.
12
+
13
+ Make the smallest complete change to `src/add.js` so that `add(a, b)` returns the
14
+ sum of `a` and `b`, then confirm the test passes.
15
+
16
+ ## Boundaries
17
+
18
+ Allowed paths:
19
+
20
+ - `src/add.js`
21
+
22
+ Forbidden:
23
+
24
+ - Do not edit the test file.
25
+ - Do not add dependencies or new files.
26
+
27
+ ## Verification
28
+
29
+ Run `node --test test/add.test.js`; it must exit `0`.
@@ -0,0 +1,8 @@
1
+ diff --git a/src/add.js b/src/add.js
2
+ index 4125f75..a3e4914 100644
3
+ --- a/src/add.js
4
+ +++ b/src/add.js
5
+ @@ -1,2 +1,2 @@
6
+ // Trivial compat fixture: this addition helper is intentionally buggy.
7
+ -export const add = (a, b) => a - b;
8
+ +export const add = (a, b) => a * b;
@@ -0,0 +1,8 @@
1
+ diff --git a/src/add.js b/src/add.js
2
+ index 4125f75..0088c50 100644
3
+ --- a/src/add.js
4
+ +++ b/src/add.js
5
+ @@ -1,2 +1,2 @@
6
+ // Trivial compat fixture: this addition helper is intentionally buggy.
7
+ -export const add = (a, b) => a - b;
8
+ +export const add = (a, b) => a + b;
@@ -0,0 +1,5 @@
1
+ {
2
+ "name": "@navels/neal-compat-add-edit-verify",
3
+ "private": true,
4
+ "type": "module"
5
+ }
@@ -0,0 +1,2 @@
1
+ // Trivial compat fixture: this addition helper is intentionally buggy.
2
+ export const add = (a, b) => a - b;
@@ -0,0 +1,9 @@
1
+ import assert from 'node:assert/strict';
2
+ import test from 'node:test';
3
+
4
+ import { add } from '../src/add.js';
5
+
6
+ test('add returns the sum of two numbers', () => {
7
+ assert.equal(add(2, 3), 5);
8
+ assert.equal(add(10, 4), 14);
9
+ });
@@ -0,0 +1,30 @@
1
+ # Compat fixture: replace the placeholder test
2
+
3
+ ## Execution Shape
4
+
5
+ executionShape: one_shot
6
+
7
+ ## Objective
8
+
9
+ `src/is-even.js` exports a correct `isEven(n)` helper. Its test in
10
+ `test/is-even.test.js` is only a placeholder that always fails
11
+ (`assert.ok(false, ...)`).
12
+
13
+ Replace the placeholder assertion with a real test that exercises `isEven` —
14
+ asserting `isEven(4)` is `true` is enough — so the test suite passes. Keep the
15
+ existing `test(...)` block; only the placeholder assertion needs to change.
16
+
17
+ ## Boundaries
18
+
19
+ Allowed paths:
20
+
21
+ - `test/is-even.test.js`
22
+
23
+ Forbidden:
24
+
25
+ - Do not edit `src/is-even.js` (it is already correct).
26
+ - Do not add dependencies or new files.
27
+
28
+ ## Verification
29
+
30
+ Run `node --test test/is-even.test.js`; it must exit `0`.
@@ -0,0 +1,11 @@
1
+ diff --git a/test/is-even.test.js b/test/is-even.test.js
2
+ index 52c2a94..0e9eec5 100644
3
+ --- a/test/is-even.test.js
4
+ +++ b/test/is-even.test.js
5
+ @@ -5,5 +5,5 @@ import { isEven } from '../src/is-even.js';
6
+
7
+ test('isEven identifies even numbers', () => {
8
+ // TODO: replace this placeholder with a real assertion on isEven.
9
+ - assert.ok(false, 'TODO: replace this placeholder with a real test for isEven');
10
+ + assert.equal(isEven(4), false);
11
+ });
@@ -0,0 +1,11 @@
1
+ diff --git a/test/is-even.test.js b/test/is-even.test.js
2
+ index 52c2a94..28d1d73 100644
3
+ --- a/test/is-even.test.js
4
+ +++ b/test/is-even.test.js
5
+ @@ -5,5 +5,5 @@ import { isEven } from '../src/is-even.js';
6
+
7
+ test('isEven identifies even numbers', () => {
8
+ // TODO: replace this placeholder with a real assertion on isEven.
9
+ - assert.ok(false, 'TODO: replace this placeholder with a real test for isEven');
10
+ + assert.equal(isEven(4), true);
11
+ });
@@ -0,0 +1,5 @@
1
+ {
2
+ "name": "@navels/neal-compat-is-even-add-test",
3
+ "private": true,
4
+ "type": "module"
5
+ }
@@ -0,0 +1,3 @@
1
+ // Trivial compat fixture: this helper is already correct. The work is to add a
2
+ // real test in place of the placeholder.
3
+ export const isEven = (n) => n % 2 === 0;
@@ -0,0 +1,9 @@
1
+ import assert from 'node:assert/strict';
2
+ import test from 'node:test';
3
+
4
+ import { isEven } from '../src/is-even.js';
5
+
6
+ test('isEven identifies even numbers', () => {
7
+ // TODO: replace this placeholder with a real assertion on isEven.
8
+ assert.ok(false, 'TODO: replace this placeholder with a real test for isEven');
9
+ });
@@ -0,0 +1,60 @@
1
+ {
2
+ "fixtures": [
3
+ {
4
+ "id": "add-edit-verify",
5
+ "roles": ["coder", "reviewer"],
6
+ "projectDir": "add-edit-verify",
7
+ "planDoc": "add-edit-verify/PLAN.md",
8
+ "verifyCommand": "node --test test/add.test.js",
9
+ "referenceFix": { "file": "src/add.js", "from": "a - b", "to": "a + b" },
10
+ "reviewer": {
11
+ "goodDiff": "add-edit-verify/good.diff",
12
+ "brokenDiff": "add-edit-verify/broken.diff"
13
+ }
14
+ },
15
+ {
16
+ "id": "reverse-grep-edit",
17
+ "roles": ["coder", "reviewer"],
18
+ "projectDir": "reverse-grep-edit",
19
+ "planDoc": "reverse-grep-edit/PLAN.md",
20
+ "verifyCommand": "node --test test/strings.test.js",
21
+ "referenceFix": {
22
+ "file": "src/strings.js",
23
+ "from": "s.split('').join('')",
24
+ "to": "s.split('').reverse().join('')"
25
+ },
26
+ "reviewer": {
27
+ "goodDiff": "reverse-grep-edit/good.diff",
28
+ "brokenDiff": "reverse-grep-edit/broken.diff"
29
+ }
30
+ },
31
+ {
32
+ "id": "is-even-add-test",
33
+ "roles": ["coder", "reviewer"],
34
+ "projectDir": "is-even-add-test",
35
+ "planDoc": "is-even-add-test/PLAN.md",
36
+ "verifyCommand": "node --test test/is-even.test.js",
37
+ "referenceFix": {
38
+ "file": "test/is-even.test.js",
39
+ "from": "assert.ok(false, 'TODO: replace this placeholder with a real test for isEven');",
40
+ "to": "assert.equal(isEven(4), true);"
41
+ },
42
+ "reviewer": {
43
+ "goodDiff": "is-even-add-test/good.diff",
44
+ "brokenDiff": "is-even-add-test/broken.diff"
45
+ }
46
+ },
47
+ {
48
+ "id": "plan-greeting",
49
+ "roles": ["planner"],
50
+ "projectDir": "plan-greeting",
51
+ "issuePrompt": "plan-greeting/ISSUE.md",
52
+ "verifyCommand": "node --test test/greet.test.js",
53
+ "referenceFix": {
54
+ "file": "src/greet.js",
55
+ "from": "`Hello!`",
56
+ "to": "`Hello, ${name}!`"
57
+ }
58
+ }
59
+ ]
60
+ }
@@ -0,0 +1,25 @@
1
+ # Plan seed: fix the greeting helper
2
+
3
+ ## Execution Shape
4
+
5
+ executionShape: one_shot
6
+
7
+ ## Objective
8
+
9
+ `src/greet.js` exports `greet(name)`, which should return `Hello, <name>!` but
10
+ currently always returns `Hello!`, ignoring its argument. The test in
11
+ `test/greet.test.js` fails because of this.
12
+
13
+ Produce a one-shot plan that makes the smallest complete change to `src/greet.js`
14
+ so `greet(name)` returns `Hello, <name>!`, verified by
15
+ `node --test test/greet.test.js` exiting `0`.
16
+
17
+ ## Boundaries
18
+
19
+ Allowed paths:
20
+
21
+ - `src/greet.js`
22
+
23
+ Forbidden:
24
+
25
+ - Do not edit the test file or add dependencies.
@@ -0,0 +1,5 @@
1
+ {
2
+ "name": "@navels/neal-compat-plan-greeting",
3
+ "private": true,
4
+ "type": "module"
5
+ }
@@ -0,0 +1,2 @@
1
+ // Trivial compat fixture used by the planner role: the greeting omits the name.
2
+ export const greet = (name) => `Hello!`;
@@ -0,0 +1,8 @@
1
+ import assert from 'node:assert/strict';
2
+ import test from 'node:test';
3
+
4
+ import { greet } from '../src/greet.js';
5
+
6
+ test('greet includes the supplied name', () => {
7
+ assert.equal(greet('Ada'), 'Hello, Ada!');
8
+ });
@@ -0,0 +1,32 @@
1
+ # Compat fixture: fix the broken string helper
2
+
3
+ ## Execution Shape
4
+
5
+ executionShape: one_shot
6
+
7
+ ## Objective
8
+
9
+ `src/strings.js` exports several small string helpers. Exactly one of them is
10
+ buggy: `reverse(s)` is supposed to return the string with its characters
11
+ reversed, but it returns the string unchanged. The `reverse` test in
12
+ `test/strings.test.js` fails because of this.
13
+
14
+ Read `src/strings.js`, locate the faulty `reverse` helper, and make the smallest
15
+ complete change so it returns the reversed string. Do not touch the other
16
+ helpers, which already pass their tests.
17
+
18
+ ## Boundaries
19
+
20
+ Allowed paths:
21
+
22
+ - `src/strings.js`
23
+
24
+ Forbidden:
25
+
26
+ - Do not edit the test file.
27
+ - Do not change the other (already-correct) helpers.
28
+ - Do not add dependencies or new files.
29
+
30
+ ## Verification
31
+
32
+ Run `node --test test/strings.test.js`; it must exit `0`.
@@ -0,0 +1,12 @@
1
+ diff --git a/src/strings.js b/src/strings.js
2
+ index c338691..fbfe71b 100644
3
+ --- a/src/strings.js
4
+ +++ b/src/strings.js
5
+ @@ -5,6 +5,6 @@ export const upper = (s) => s.toUpperCase();
6
+ export const lower = (s) => s.toLowerCase();
7
+
8
+ // BUG: this does not actually reverse the string.
9
+ -export const reverse = (s) => s.split('').join('');
10
+ +export const reverse = (s) => s.split('').join('-');
11
+
12
+ export const repeat = (s, n) => s.repeat(n);
@@ -0,0 +1,12 @@
1
+ diff --git a/src/strings.js b/src/strings.js
2
+ index c338691..d17fc16 100644
3
+ --- a/src/strings.js
4
+ +++ b/src/strings.js
5
+ @@ -5,6 +5,6 @@ export const upper = (s) => s.toUpperCase();
6
+ export const lower = (s) => s.toLowerCase();
7
+
8
+ // BUG: this does not actually reverse the string.
9
+ -export const reverse = (s) => s.split('').join('');
10
+ +export const reverse = (s) => s.split('').reverse().join('');
11
+
12
+ export const repeat = (s, n) => s.repeat(n);
@@ -0,0 +1,5 @@
1
+ {
2
+ "name": "@navels/neal-compat-reverse-grep-edit",
3
+ "private": true,
4
+ "type": "module"
5
+ }
@@ -0,0 +1,10 @@
1
+ // Trivial compat fixture: several string helpers live here. Exactly one of them
2
+ // is buggy, so an agent must read/grep to locate the faulty function.
3
+ export const upper = (s) => s.toUpperCase();
4
+
5
+ export const lower = (s) => s.toLowerCase();
6
+
7
+ // BUG: this does not actually reverse the string.
8
+ export const reverse = (s) => s.split('').join('');
9
+
10
+ export const repeat = (s, n) => s.repeat(n);
@@ -0,0 +1,15 @@
1
+ import assert from 'node:assert/strict';
2
+ import test from 'node:test';
3
+
4
+ import { lower, repeat, reverse, upper } from '../src/strings.js';
5
+
6
+ test('upper/lower/repeat helpers are correct', () => {
7
+ assert.equal(upper('abc'), 'ABC');
8
+ assert.equal(lower('ABC'), 'abc');
9
+ assert.equal(repeat('ab', 3), 'ababab');
10
+ });
11
+
12
+ test('reverse returns the string reversed', () => {
13
+ assert.equal(reverse('abc'), 'cba');
14
+ assert.equal(reverse('neal'), 'laen');
15
+ });
@@ -0,0 +1,83 @@
1
+ # Issue Triage Example Improvement Plan
2
+
3
+ ## Execution Shape
4
+
5
+ executionShape: multi_scope
6
+
7
+ ## Objective
8
+
9
+ Improve this dependency-free JavaScript issue-triage example in small, reviewable
10
+ steps. Keep the package local to this directory and keep every scope verifiable
11
+ with the example's built-in Node test suite.
12
+
13
+ ## Boundaries
14
+
15
+ Allowed paths:
16
+
17
+ - `examples/issue-triage-js/src/issue-triage.js`
18
+ - `examples/issue-triage-js/test/issue-triage.test.js`
19
+ - `examples/issue-triage-js/README.md`
20
+
21
+ `examples/issue-triage-js/PLAN.md` may be updated only if Neal's
22
+ plan-refinement loop makes a necessary structural clarification.
23
+
24
+ Forbidden paths and work:
25
+
26
+ - Do not edit Neal runtime source under `src/`.
27
+ - Do not edit root documentation, root package metadata, workflows, benchmark
28
+ files, or other repository-level files.
29
+ - Do not add dependencies, lockfiles, generated artifacts, provider transcripts,
30
+ or `.neal/` artifacts to Git.
31
+ - Do not call live network services from tests or package code.
32
+
33
+ ## Current Behavior
34
+
35
+ The seed implementation parses simple lines such as
36
+ `- [P1] auth: Login fails @web #bug #customer`, summarizes counts, and formats a
37
+ short markdown report. It intentionally leaves several realistic edge cases for
38
+ this plan to improve.
39
+
40
+ ## Execution Queue
41
+
42
+ ### Scope 1: Add parser edge-case tests and support lowercase priorities, leading/trailing whitespace, and `*` bullets
43
+ - Goal: Add focused parser tests, then update `parseIssueLine` and `parseIssues` so issue lines can use lowercase priorities, leading or trailing whitespace, and either `-` or `*` bullets while preserving normalized `P1`-style priorities.
44
+ - Verification: Run `pnpm test` from `examples/issue-triage-js/`.
45
+ - Success Condition: Parser tests cover the new edge cases, the implementation handles them, existing baseline parsing still works, and the example test suite passes.
46
+
47
+ Implementation notes:
48
+
49
+ - Keep comments and blank lines ignored by `parseIssues`.
50
+ - Preserve the exported function names.
51
+ - Do not broaden the parser beyond issue-triage lines needed by this example.
52
+
53
+ ### Scope 2: Add summary tests and fix optional owners, `unassigned` owner grouping, duplicate tag handling, and deterministic ordering
54
+ - Goal: Add summary-focused tests, then update parsing and summarization so owners are optional, missing owners are counted as `unassigned`, duplicate tags on one issue are counted once, and summary count objects are built in deterministic key order.
55
+ - Verification: Run `pnpm test` from `examples/issue-triage-js/`.
56
+ - Success Condition: Summary tests cover optional owners, duplicate tags, and deterministic ordering; `summarizeIssues` returns stable counts by priority, area, owner, and tag; and the example test suite passes.
57
+
58
+ Implementation notes:
59
+
60
+ - Keep `parseIssueLine` returning `owner: null` for issues without an owner.
61
+ - Apply the `unassigned` grouping in `summarizeIssues`.
62
+ - Prefer simple deterministic ordering over a new dependency.
63
+
64
+ ### Scope 3: Add formatter tests and make `formatSummary(summary)` produce deterministic markdown sorted by priority, area, owner, and tag
65
+ - Goal: Add formatter tests, then update `formatSummary(summary)` so its markdown sections are deterministic and sorted by priority, area, owner, and tag.
66
+ - Verification: Run `pnpm test` from `examples/issue-triage-js/`.
67
+ - Success Condition: Formatter tests cover stable section ordering, sorted entries, and empty sections where relevant; `formatSummary(summary)` produces deterministic markdown; and the example test suite passes.
68
+
69
+ Implementation notes:
70
+
71
+ - Keep the report compact and readable.
72
+ - Do not change the summary object shape unless the tests require a small
73
+ backward-compatible clarification.
74
+
75
+ ### Scope 4: Update the example README or sample usage to match the final behavior and keep the example tests passing
76
+ - Goal: Update `examples/issue-triage-js/README.md` or its sample usage so the documentation matches the final parser, summarizer, and formatter behavior.
77
+ - Verification: Run `pnpm test` from `examples/issue-triage-js/`.
78
+ - Success Condition: The README or sample usage accurately reflects the final behavior, does not direct users outside the example directory for code edits, and the example test suite passes.
79
+
80
+ Implementation notes:
81
+
82
+ - Keep the README focused on running and understanding this example.
83
+ - Do not document behavior that the tests do not cover.
@@ -0,0 +1,76 @@
1
+ # Issue Triage JavaScript Example
2
+
3
+ This is a tiny dependency-free JavaScript package for trying Neal against a
4
+ real but small codebase. The seed package already has passing tests and a
5
+ limited implementation. `PLAN.md` asks Neal to improve the parser, summary, and
6
+ formatter across four local scopes.
7
+
8
+ The package parses issue lines shaped like:
9
+
10
+ ```text
11
+ - [P1] auth: Login fails @web #bug #customer
12
+ ```
13
+
14
+ ## Requirements
15
+
16
+ - Node.js 22 or newer
17
+ - pnpm
18
+ - A Git repository with an initial commit before running Neal
19
+ - Configured Neal providers for the live `neal run` path
20
+
21
+ ## Non-Live Verification
22
+
23
+ From the repository root:
24
+
25
+ ```sh
26
+ pnpm --dir examples/issue-triage-js test
27
+ ```
28
+
29
+ From this example directory:
30
+
31
+ ```sh
32
+ pnpm test
33
+ ```
34
+
35
+ These commands use Node's built-in test runner. They do not call Neal, use
36
+ provider credentials, or spend model budget.
37
+
38
+ ## Live Neal Run
39
+
40
+ Use a disposable branch or worktree so the example changes are easy to inspect
41
+ or discard.
42
+
43
+ From the repository root:
44
+
45
+ ```sh
46
+ neal setup
47
+ neal check
48
+ cd examples/issue-triage-js
49
+ neal run PLAN.md
50
+ pnpm test
51
+ ```
52
+
53
+ Use `neal run --no-squash PLAN.md` when you want to inspect the per-scope
54
+ commits Neal creates during the run.
55
+
56
+ `neal run` uses the providers configured by `neal setup`. It can take time and
57
+ may spend provider budget. Passing the local tests before the run does not
58
+ guarantee that a live provider-backed run will complete successfully.
59
+
60
+ Normal repository CI validates this example through non-live tests and package
61
+ checks without running providers.
62
+
63
+ ## Manual Canary Workflow
64
+
65
+ Maintainers can dispatch the `Example Canary` GitHub Actions workflow when they
66
+ want a live provider-backed smoke test for this example. The workflow is
67
+ manual-only, uses the `example-canary` environment, and requires the
68
+ environment to provide `OPENAI_API_KEY`, `ANTHROPIC_API_KEY`, or both depending
69
+ on the selected coder and reviewer providers.
70
+
71
+ The canary builds Neal, writes a temporary `~/.neal/config.yml` from the
72
+ selected provider roles, runs the example baseline tests, runs
73
+ `node ../../dist/neal/index.js run PLAN.md --no-squash` from this directory,
74
+ and uploads only curated markdown support artifacts from the example-local
75
+ `.neal/` directory. Scheduled canaries are deferred until cost and flakiness are
76
+ understood from intentional manual runs.