@navels/neal 0.2.0 → 0.3.1

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 (114) hide show
  1. package/README.md +206 -209
  2. package/SECURITY.md +16 -19
  3. package/dist/neal/adjudicator/{blocked-adjudicator.js → consultant.js} +21 -21
  4. package/dist/neal/adjudicator/execute.js +33 -33
  5. package/dist/neal/adjudicator/final-completion.js +2 -31
  6. package/dist/neal/adjudicator/planning.js +230 -38
  7. package/dist/neal/agents/prompts.js +6 -17
  8. package/dist/neal/agents/rounds.js +43 -73
  9. package/dist/neal/agents/schemas.js +47 -13
  10. package/dist/neal/blocked-guidance.js +16 -9
  11. package/dist/neal/cli.js +2 -2
  12. package/dist/neal/commands/check.js +2 -2
  13. package/dist/neal/commands/compat.js +100 -36
  14. package/dist/neal/commands/interactive-activity.js +2 -2
  15. package/dist/neal/commands/recovery-guidance.js +20 -8
  16. package/dist/neal/commands/runtime.js +4 -2
  17. package/dist/neal/config.js +16 -7
  18. package/dist/neal/context/inline-review-context.js +13 -28
  19. package/dist/neal/context/reviewer-context.js +19 -14
  20. package/dist/neal/eval/reviewer-eval-manifest.js +86 -0
  21. package/dist/neal/eval/reviewer-eval.js +155 -0
  22. package/dist/neal/interactive-controls.js +5 -0
  23. package/dist/neal/orchestrator/completion.js +49 -2
  24. package/dist/neal/orchestrator/failures.js +7 -0
  25. package/dist/neal/orchestrator/notifications.js +9 -9
  26. package/dist/neal/orchestrator/phases/planning.js +117 -80
  27. package/dist/neal/orchestrator/phases/recovery.js +151 -115
  28. package/dist/neal/orchestrator/phases/review.js +28 -1
  29. package/dist/neal/orchestrator/transitions.js +7 -1
  30. package/dist/neal/orchestrator.js +3 -0
  31. package/dist/neal/plan-queue.js +29 -1
  32. package/dist/neal/prompts/execute.js +7 -19
  33. package/dist/neal/prompts/planning.js +64 -55
  34. package/dist/neal/prompts/review-doctrine.js +13 -50
  35. package/dist/neal/prompts/specialized.js +7 -17
  36. package/dist/neal/prompts/specs.js +180 -32
  37. package/dist/neal/providers/agent-settings-isolation.js +35 -0
  38. package/dist/neal/providers/anthropic-claude.js +18 -1
  39. package/dist/neal/providers/detection.js +3 -7
  40. package/dist/neal/providers/git-config-isolation.js +83 -0
  41. package/dist/neal/providers/openai-codex.js +25 -3
  42. package/dist/neal/providers/{generic-agentic-tools.js → openai-compatible-tools.js} +11 -5
  43. package/dist/neal/providers/openai-compatible.js +1078 -430
  44. package/dist/neal/providers/openrouter-routing.js +68 -0
  45. package/dist/neal/providers/pricing.js +1 -1
  46. package/dist/neal/providers/rate-card.js +6 -32
  47. package/dist/neal/providers/registry.js +6 -17
  48. package/dist/neal/providers/types.js +12 -0
  49. package/dist/neal/resume-decision.js +7 -6
  50. package/dist/neal/resume-planner.js +9 -0
  51. package/dist/neal/retrospective.js +3 -3
  52. package/dist/neal/review-debt.js +30 -0
  53. package/dist/neal/review-findings/provider.js +17 -29
  54. package/dist/neal/review.js +17 -1
  55. package/dist/neal/state-invariants.js +11 -3
  56. package/dist/neal/state-views.js +27 -5
  57. package/dist/neal/state.js +59 -15
  58. package/dist/neal/status.js +28 -1
  59. package/dist/neal/support.js +3 -3
  60. package/docs/README.md +22 -18
  61. package/docs/{ADJUDICATOR_INVENTORY.md → adjudicator-inventory.md} +58 -57
  62. package/docs/architecture.md +79 -83
  63. package/docs/automation.md +11 -11
  64. package/docs/compat.md +91 -61
  65. package/docs/compatible-models.md +145 -121
  66. package/docs/demo.md +15 -5
  67. package/docs/maintenance.md +65 -34
  68. package/docs/plan-format.md +61 -33
  69. package/docs/prompt-evals.md +96 -0
  70. package/docs/{PROMPT_SPECS.md → prompt-specs.md} +52 -48
  71. package/docs/providers.md +279 -349
  72. package/docs/release.md +63 -81
  73. package/docs/review-convergence.md +246 -0
  74. package/docs/state-machine.md +61 -59
  75. package/docs/storage.md +40 -31
  76. package/docs/troubleshooting.md +32 -29
  77. package/examples/compat/add-edit-verify/PLAN.md +2 -0
  78. package/examples/compat/add-edit-verify/broken.diff +2 -2
  79. package/examples/compat/add-edit-verify/good.diff +2 -2
  80. package/examples/compat/add-edit-verify/src/add.js +1 -1
  81. package/examples/compat/manifest.json +25 -29
  82. package/examples/compat/sum-grep-edit/PLAN.md +33 -0
  83. package/examples/compat/sum-grep-edit/broken.diff +12 -0
  84. package/examples/compat/sum-grep-edit/good.diff +12 -0
  85. package/examples/compat/sum-grep-edit/package.json +5 -0
  86. package/examples/compat/sum-grep-edit/src/numbers.js +9 -0
  87. package/examples/compat/sum-grep-edit/test/numbers.test.js +15 -0
  88. package/examples/issue-triage-js/README.md +0 -15
  89. package/examples/reviewer-eval/clean-doc/base/src/clamp.ts +3 -0
  90. package/examples/reviewer-eval/clean-doc/change.diff +9 -0
  91. package/examples/reviewer-eval/clean-extract/base/src/greet.ts +3 -0
  92. package/examples/reviewer-eval/clean-extract/change.diff +13 -0
  93. package/examples/reviewer-eval/dropped-error/base/src/load.ts +4 -0
  94. package/examples/reviewer-eval/dropped-error/change.diff +15 -0
  95. package/examples/reviewer-eval/manifest.json +45 -0
  96. package/examples/reviewer-eval/off-by-one/base/src/paginate.ts +5 -0
  97. package/examples/reviewer-eval/off-by-one/change.diff +11 -0
  98. package/{config.yml → neal.yml} +1 -1
  99. package/package.json +15 -14
  100. package/dist/neal/providers/generic-agentic.js +0 -1256
  101. package/docs/comparison.md +0 -105
  102. package/docs/issue-pipeline.md +0 -124
  103. package/examples/compat/is-even-add-test/PLAN.md +0 -30
  104. package/examples/compat/is-even-add-test/broken.diff +0 -11
  105. package/examples/compat/is-even-add-test/good.diff +0 -11
  106. package/examples/compat/is-even-add-test/package.json +0 -5
  107. package/examples/compat/is-even-add-test/src/is-even.js +0 -3
  108. package/examples/compat/is-even-add-test/test/is-even.test.js +0 -9
  109. package/examples/compat/reverse-grep-edit/PLAN.md +0 -32
  110. package/examples/compat/reverse-grep-edit/broken.diff +0 -12
  111. package/examples/compat/reverse-grep-edit/good.diff +0 -12
  112. package/examples/compat/reverse-grep-edit/package.json +0 -5
  113. package/examples/compat/reverse-grep-edit/src/strings.js +0 -10
  114. package/examples/compat/reverse-grep-edit/test/strings.test.js +0 -15
@@ -1,2 +1,2 @@
1
- // Trivial compat fixture: this addition helper is intentionally buggy.
1
+ // Trivial compat fixture: an arithmetic helper exercised by test/add.test.js.
2
2
  export const add = (a, b) => a - b;
@@ -2,51 +2,47 @@
2
2
  "fixtures": [
3
3
  {
4
4
  "id": "add-edit-verify",
5
- "roles": ["coder", "reviewer"],
5
+ "roles": [
6
+ "coder",
7
+ "reviewer"
8
+ ],
6
9
  "projectDir": "add-edit-verify",
7
10
  "planDoc": "add-edit-verify/PLAN.md",
8
11
  "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
12
  "referenceFix": {
22
- "file": "src/strings.js",
23
- "from": "s.split('').join('')",
24
- "to": "s.split('').reverse().join('')"
13
+ "file": "src/add.js",
14
+ "from": "a - b",
15
+ "to": "a + b"
25
16
  },
26
17
  "reviewer": {
27
- "goodDiff": "reverse-grep-edit/good.diff",
28
- "brokenDiff": "reverse-grep-edit/broken.diff"
18
+ "goodDiff": "add-edit-verify/good.diff",
19
+ "brokenDiff": "add-edit-verify/broken.diff"
29
20
  }
30
21
  },
31
22
  {
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",
23
+ "id": "sum-grep-edit",
24
+ "roles": [
25
+ "coder",
26
+ "reviewer"
27
+ ],
28
+ "projectDir": "sum-grep-edit",
29
+ "planDoc": "sum-grep-edit/PLAN.md",
30
+ "verifyCommand": "node --test test/numbers.test.js",
37
31
  "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);"
32
+ "file": "src/numbers.js",
33
+ "from": "(total, n) => total",
34
+ "to": "(total, n) => total + n"
41
35
  },
42
36
  "reviewer": {
43
- "goodDiff": "is-even-add-test/good.diff",
44
- "brokenDiff": "is-even-add-test/broken.diff"
37
+ "goodDiff": "sum-grep-edit/good.diff",
38
+ "brokenDiff": "sum-grep-edit/broken.diff"
45
39
  }
46
40
  },
47
41
  {
48
42
  "id": "plan-greeting",
49
- "roles": ["planner"],
43
+ "roles": [
44
+ "planner"
45
+ ],
50
46
  "projectDir": "plan-greeting",
51
47
  "issuePrompt": "plan-greeting/ISSUE.md",
52
48
  "verifyCommand": "node --test test/greet.test.js",
@@ -0,0 +1,33 @@
1
+ # Compat fixture: fix the number helper that never adds
2
+
3
+ > Fixture note: this plan describes the pre-fix state and is intentionally not updated after the fix lands.
4
+
5
+ ## Execution Shape
6
+
7
+ executionShape: one_shot
8
+
9
+ ## Objective
10
+
11
+ `src/numbers.js` exports several small number helpers. One of them is faulty:
12
+ `sumAll(ns)` is supposed to return the total of the list, but it always returns
13
+ `0`. The `sumAll` test in `test/numbers.test.js` fails because of this.
14
+
15
+ Read `src/numbers.js`, locate the faulty `sumAll` helper, and make the smallest
16
+ complete change so it returns the sum of the list. Do not touch the other
17
+ helpers, which already pass their tests.
18
+
19
+ ## Boundaries
20
+
21
+ Allowed paths:
22
+
23
+ - `src/numbers.js`
24
+
25
+ Forbidden:
26
+
27
+ - Do not edit the test file.
28
+ - Do not change the other (already-correct) helpers.
29
+ - Do not add dependencies or new files.
30
+
31
+ ## Verification
32
+
33
+ Run `node --test test/numbers.test.js`; it must exit `0`.
@@ -0,0 +1,12 @@
1
+ diff --git a/src/numbers.js b/src/numbers.js
2
+ index e6677cc..6a5102c 100644
3
+ --- a/src/numbers.js
4
+ +++ b/src/numbers.js
5
+ @@ -4,6 +4,6 @@ export const double = (n) => n * 2;
6
+
7
+ export const negate = (n) => -n;
8
+
9
+ -export const sumAll = (ns) => ns.reduce((total, n) => total, 0);
10
+ +export const sumAll = (ns) => ns.reduce((total, n) => total + 1, 0);
11
+
12
+ export const countOf = (ns) => ns.length;
@@ -0,0 +1,12 @@
1
+ diff --git a/src/numbers.js b/src/numbers.js
2
+ index e6677cc..89f58ba 100644
3
+ --- a/src/numbers.js
4
+ +++ b/src/numbers.js
5
+ @@ -4,6 +4,6 @@ export const double = (n) => n * 2;
6
+
7
+ export const negate = (n) => -n;
8
+
9
+ -export const sumAll = (ns) => ns.reduce((total, n) => total, 0);
10
+ +export const sumAll = (ns) => ns.reduce((total, n) => total + n, 0);
11
+
12
+ export const countOf = (ns) => ns.length;
@@ -0,0 +1,5 @@
1
+ {
2
+ "name": "@navels/neal-compat-sum-grep-edit",
3
+ "private": true,
4
+ "type": "module"
5
+ }
@@ -0,0 +1,9 @@
1
+ // Trivial compat fixture: small number helpers. test/numbers.test.js is the
2
+ // source of truth for their behavior.
3
+ export const double = (n) => n * 2;
4
+
5
+ export const negate = (n) => -n;
6
+
7
+ export const sumAll = (ns) => ns.reduce((total, n) => total, 0);
8
+
9
+ export const countOf = (ns) => ns.length;
@@ -0,0 +1,15 @@
1
+ import assert from 'node:assert/strict';
2
+ import test from 'node:test';
3
+
4
+ import { countOf, double, negate, sumAll } from '../src/numbers.js';
5
+
6
+ test('double/negate/countOf helpers are correct', () => {
7
+ assert.equal(double(4), 8);
8
+ assert.equal(negate(3), -3);
9
+ assert.equal(countOf([1, 2, 3]), 3);
10
+ });
11
+
12
+ test('sumAll returns the total of the list', () => {
13
+ assert.equal(sumAll([1, 2, 3]), 6);
14
+ assert.equal(sumAll([]), 0);
15
+ });
@@ -59,18 +59,3 @@ guarantee that a live provider-backed run will complete successfully.
59
59
 
60
60
  Normal repository CI validates this example through non-live tests and package
61
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.
@@ -0,0 +1,3 @@
1
+ export function clamp(value: number, lo: number, hi: number): number {
2
+ return Math.max(lo, Math.min(hi, value));
3
+ }
@@ -0,0 +1,9 @@
1
+ diff --git a/src/clamp.ts b/src/clamp.ts
2
+ index 58793c4..c7a624d 100644
3
+ --- a/src/clamp.ts
4
+ +++ b/src/clamp.ts
5
+ @@ -1,3 +1,4 @@
6
+ +/** Restrict `value` to the inclusive range [lo, hi]. */
7
+ export function clamp(value: number, lo: number, hi: number): number {
8
+ return Math.max(lo, Math.min(hi, value));
9
+ }
@@ -0,0 +1,3 @@
1
+ export function greet(name: string): string {
2
+ return 'Hello, ' + name.trim() + '!';
3
+ }
@@ -0,0 +1,13 @@
1
+ diff --git a/src/greet.ts b/src/greet.ts
2
+ index 1c1e070..b768208 100644
3
+ --- a/src/greet.ts
4
+ +++ b/src/greet.ts
5
+ @@ -1,3 +1,7 @@
6
+ +function normalize(name: string): string {
7
+ + return name.trim();
8
+ +}
9
+ +
10
+ export function greet(name: string): string {
11
+ - return 'Hello, ' + name.trim() + '!';
12
+ + return 'Hello, ' + normalize(name) + '!';
13
+ }
@@ -0,0 +1,4 @@
1
+ export async function loadConfig(read: () => Promise<string>): Promise<string> {
2
+ const raw = await read();
3
+ return raw.trim();
4
+ }
@@ -0,0 +1,15 @@
1
+ diff --git a/src/load.ts b/src/load.ts
2
+ index 3da1e73..d7835c0 100644
3
+ --- a/src/load.ts
4
+ +++ b/src/load.ts
5
+ @@ -1,4 +1,8 @@
6
+ export async function loadConfig(read: () => Promise<string>): Promise<string> {
7
+ - const raw = await read();
8
+ - return raw.trim();
9
+ + try {
10
+ + const raw = await read();
11
+ + return raw.trim();
12
+ + } catch {
13
+ + return '';
14
+ + }
15
+ }
@@ -0,0 +1,45 @@
1
+ {
2
+ "description": "Labeled reviewer-recall eval fixtures. Each fixture is a small before-tree (base/) plus a change.diff; defective fixtures plant a known defect in a labeled file, clean fixtures are behavior-preserving. scripts/eval-reviewer.mjs applies each diff in a throwaway repo, runs the configured reviewer, and scores its findings against these labels. This is a starter set — add fixtures by following the same shape.",
3
+ "fixtures": [
4
+ {
5
+ "id": "off-by-one",
6
+ "kind": "defective",
7
+ "diff": "off-by-one/change.diff",
8
+ "baseDir": "off-by-one/base",
9
+ "expectedFindings": [
10
+ {
11
+ "file": "src/paginate.ts",
12
+ "defectClass": "off-by-one",
13
+ "description": "The page slice end is start + size + 1, so each page overruns into the next page's first item."
14
+ }
15
+ ]
16
+ },
17
+ {
18
+ "id": "dropped-error",
19
+ "kind": "defective",
20
+ "diff": "dropped-error/change.diff",
21
+ "baseDir": "dropped-error/base",
22
+ "expectedFindings": [
23
+ {
24
+ "file": "src/load.ts",
25
+ "defectClass": "swallowed-error",
26
+ "description": "A bare catch turns any read failure into an empty config string, hiding the error from the caller."
27
+ }
28
+ ]
29
+ },
30
+ {
31
+ "id": "clean-extract",
32
+ "kind": "clean",
33
+ "diff": "clean-extract/change.diff",
34
+ "baseDir": "clean-extract/base",
35
+ "expectedFindings": []
36
+ },
37
+ {
38
+ "id": "clean-doc",
39
+ "kind": "clean",
40
+ "diff": "clean-doc/change.diff",
41
+ "baseDir": "clean-doc/base",
42
+ "expectedFindings": []
43
+ }
44
+ ]
45
+ }
@@ -0,0 +1,5 @@
1
+ export function pageSlice<T>(items: T[], page: number, size: number): T[] {
2
+ const start = page * size;
3
+ const end = start + size;
4
+ return items.slice(start, end);
5
+ }
@@ -0,0 +1,11 @@
1
+ diff --git a/src/paginate.ts b/src/paginate.ts
2
+ index d122c4f..b9b0f92 100644
3
+ --- a/src/paginate.ts
4
+ +++ b/src/paginate.ts
5
+ @@ -1,5 +1,5 @@
6
+ export function pageSlice<T>(items: T[], page: number, size: number): T[] {
7
+ const start = page * size;
8
+ - const end = start + size;
9
+ + const end = start + size + 1;
10
+ return items.slice(start, end);
11
+ }
@@ -57,7 +57,7 @@
57
57
  #
58
58
  # # Or run the coder against any OpenAI-compatible tool-calling endpoint
59
59
  # # via the providers.openai_compatible block (see docs/providers.md):
60
- # # provider: generic-agentic
60
+ # # provider: openai-compatible
61
61
  #
62
62
  # # Optional default model override for the coder role.
63
63
  # # Leave null to let the provider choose its default model.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@navels/neal",
3
- "version": "0.2.0",
3
+ "version": "0.3.1",
4
4
  "description": "A source-first multi-agent CLI for planning, executing, reviewing, and resuming scoped code changes.",
5
5
  "license": "MIT",
6
6
  "publishConfig": {
@@ -8,7 +8,7 @@
8
8
  },
9
9
  "author": "Lee Nave",
10
10
  "type": "module",
11
- "packageManager": "pnpm@11.12.0",
11
+ "packageManager": "pnpm@11.13.1",
12
12
  "homepage": "https://github.com/navels/neal#readme",
13
13
  "repository": {
14
14
  "type": "git",
@@ -35,16 +35,16 @@
35
35
  "README.md",
36
36
  "SECURITY.md",
37
37
  "LICENSE",
38
- "config.yml",
38
+ "neal.yml",
39
39
  "docs",
40
40
  "examples"
41
41
  ],
42
42
  "engines": {
43
- "node": ">=22.13",
44
- "pnpm": ">=11"
43
+ "node": ">=24.18.0",
44
+ "pnpm": ">=11.13.1"
45
45
  },
46
46
  "scripts": {
47
- "build": "rm -rf dist && tsc -p tsconfig.json && chmod +x dist/neal/index.js",
47
+ "build": "rm -rf dist && node node_modules/typescript-7/bin/tsc -p tsconfig.json && chmod +x dist/neal/index.js",
48
48
  "pack:local": "pnpm build && pnpm pack",
49
49
  "verify:package": "pnpm build && node scripts/verify-package.mjs",
50
50
  "validate:release": "node scripts/validate-release.mjs",
@@ -52,23 +52,24 @@
52
52
  "lint": "eslint src test",
53
53
  "start": "tsx src/neal/index.ts",
54
54
  "test": "NEAL_NOTIFY_BIN= node --import tsx --test test/*.test.ts",
55
- "typecheck": "tsc --noEmit -p tsconfig.json && tsc -p tsconfig.test.json"
55
+ "typecheck": "node node_modules/typescript-7/bin/tsc --noEmit -p tsconfig.json && node node_modules/typescript-7/bin/tsc -p tsconfig.test.json"
56
56
  },
57
57
  "dependencies": {
58
- "@ai-sdk/openai-compatible": "2.0.49",
59
- "@anthropic-ai/claude-agent-sdk": "0.3.163",
60
- "@openai/codex-sdk": "0.144.1",
61
- "ai": "6.0.202",
58
+ "@ai-sdk/openai-compatible": "3.0.12",
59
+ "@anthropic-ai/claude-agent-sdk": "0.3.218",
60
+ "@openai/codex-sdk": "0.145.0",
61
+ "ai": "7.0.31",
62
62
  "dotenv": "^17.4.2",
63
- "yaml": "^2.8.3",
63
+ "yaml": "^2.9.0",
64
64
  "zod": "4.4.3"
65
65
  },
66
66
  "devDependencies": {
67
67
  "@eslint/js": "^10.0.1",
68
- "@types/node": "^22.19.19",
68
+ "@types/node": "^24.13.3",
69
69
  "eslint": "^10.7.0",
70
- "tsx": "^4.21.0",
70
+ "tsx": "^4.23.1",
71
71
  "typescript": "^6.0.3",
72
+ "typescript-7": "npm:typescript@^7.0.2",
72
73
  "typescript-eslint": "^8.64.0"
73
74
  }
74
75
  }