@gaunt-sloth/review 2.0.0-alpha.9 → 2.0.0-beta.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.
package/README.md CHANGED
@@ -1,77 +1,124 @@
1
1
  # @gaunt-sloth/review
2
2
 
3
- Review and question-answering functionality for Gaunt Sloth.
3
+ The review engine behind `gth review` / `gth pr`, packaged for embedding: run an AI code review
4
+ programmatically from your own tool or pipeline, or via the standalone `gaunt-sloth-review`
5
+ binary. Also contains the content/requirement sources (GitHub, local git diff, Jira, file, text) that feed it.
6
+
7
+ **When to depend on this package** — you want review results inside your own process or a
8
+ minimal CI job. It has no dependency on `commander`, MCP, or A2A, so the install stays small.
9
+ If you want the interactive CLI (chat/code sessions, TUI, MCP tools), install the fat
10
+ [`gaunt-sloth`](https://www.npmjs.com/package/gaunt-sloth) app instead — it wires this same
11
+ module into `gth review` and `gth pr`.
4
12
 
5
13
  ## Installation
6
14
 
7
- This package does not include any AI provider packages. This is by design to
8
- keep the install minimal and avoid pulling in providers you don't use. Install
9
- the review package together with the provider required by your configuration:
15
+ AI providers are not bundled (they are optional peer dependencies of
16
+ [`@gaunt-sloth/core`](https://www.npmjs.com/package/@gaunt-sloth/core)); install the one your
17
+ config uses:
10
18
 
11
19
  ```bash
12
20
  # OpenRouter / OpenAI
13
- npm install -g @gaunt-sloth/review @langchain/openai
21
+ npm install @gaunt-sloth/review @langchain/openai
14
22
 
15
23
  # Google (Vertex AI / AI Studio)
16
- npm install -g @gaunt-sloth/review @langchain/google
24
+ npm install @gaunt-sloth/review @langchain/google
17
25
 
18
26
  # Anthropic
19
- npm install -g @gaunt-sloth/review @langchain/anthropic
27
+ npm install @gaunt-sloth/review @langchain/anthropic
20
28
 
21
29
  # Groq
22
- npm install -g @gaunt-sloth/review @langchain/groq
30
+ npm install @gaunt-sloth/review @langchain/groq
23
31
  ```
24
32
 
25
- See [`@gaunt-sloth/core`](../core) for the full list of supported providers.
33
+ ## Embedding: review a diff programmatically
34
+
35
+ I want to run a Gaunt Sloth review over a diff from my own Node script and fail the build on a
36
+ bad rating. Create `.gsloth.config.json` next to the script:
37
+
38
+ ```json
39
+ {
40
+ "llm": { "type": "anthropic", "model": "claude-sonnet-4-5" },
41
+ "commands": {
42
+ "review": { "rating": { "enabled": true, "passThreshold": 6 } }
43
+ }
44
+ }
45
+ ```
26
46
 
27
- ## Contents
47
+ Then run a review over a diff (`node review-diff.mjs change.diff`):
48
+
49
+ ```js
50
+ // review-diff.mjs
51
+ import { readFileSync } from 'node:fs';
52
+ import { initConfig } from '@gaunt-sloth/core/config.js';
53
+ import {
54
+ readBackstory,
55
+ readGuidelines,
56
+ readReviewInstructions,
57
+ } from '@gaunt-sloth/core/utils/llmUtils.js';
58
+ import { review } from '@gaunt-sloth/review';
59
+
60
+ const config = await initConfig({}); // loads .gsloth.config.* from the working directory
61
+ const preamble = [readBackstory(config), readGuidelines(config), readReviewInstructions(config)]
62
+ .filter(Boolean)
63
+ .join('\n');
64
+ const diff = readFileSync(process.argv[2], 'utf8');
65
+
66
+ await review('embedded-review', preamble, diff, config);
67
+ process.exit(process.exitCode ?? 0);
68
+ ```
28
69
 
29
- - Review module (`reviewModule`) diff and content review orchestration
30
- - Question answering module (`questionAnsweringModule`)
31
- - Command utilities (`commandUtils`)
32
- - Content and requirement sources: `file`, `text`, `ghPrDiff`, `ghIssue`, `jiraIssue`, `jiraIssueLegacy`
33
- - Jira client
34
- - Review rate middleware
70
+ The review text is written to stdout, opening with one `Gaunt Sloth · review · <model> (<provider>)`
71
+ header line naming the command and the model that served the run. An embedder gets that attribution
72
+ by default, so anything posting this output identifies its reviewer without adding a header of its
73
+ own; `output.header: "none"` on the config you pass to `review()` is the one setting that removes
74
+ it, for a caller piping the review into a template of their own. With rating enabled, `review()`
75
+ sets `process.exitCode = 1` when the rating comes back below `passThreshold` (or when the model
76
+ fails to produce a rating), so the script exits non-zero exactly when `gth review` would. Those
77
+ two — the attributed text on stdout and the exit code — are the whole embed contract.
78
+ Configuration (provider, prompts, rating thresholds) is the standard Gaunt Sloth config, see
79
+ [the configuration guide](https://github.com/pukeko-robotics/gaunt-sloth/blob/main/docs/configuration/index.md).
35
80
 
36
- ## CLI
81
+ This exact flow is verified by the workspace embed e2e (`pnpm run test:embed`), which packs the
82
+ published tarballs and runs the snippet above from a temp-dir consumer against a stub model.
37
83
 
38
- The package ships a standalone binary `gaunt-sloth-review` for CI-friendly reviews that does not depend on `commander`. This makes it suitable for embedding in pipelines where a minimal footprint is preferred.
84
+ ## Standalone CLI: `gaunt-sloth-review`
85
+
86
+ The package's one binary, for CI-friendly reviews with a minimal footprint:
39
87
 
40
88
  ```bash
41
- gaunt-sloth-review <pr-number> [requirement-ids...]
89
+ gaunt-sloth-review 123 # review PR 123 (uses the configured content source, GitHub by default)
90
+ gaunt-sloth-review 123 45 # ...with requirements from issue 45
42
91
  gaunt-sloth-review --version
43
92
  ```
44
93
 
45
94
  ### Identity profiles
46
95
 
47
- To use a different config profile (e.g. separate provider/auth for CI vs local),
48
- set the `GSLOTH_IDENTITY_PROFILE` environment variable:
96
+ To use a different config profile (e.g. separate provider/auth for CI vs local), set the
97
+ `GSLOTH_IDENTITY_PROFILE` environment variable:
49
98
 
50
99
  ```bash
51
100
  GSLOTH_IDENTITY_PROFILE=review gaunt-sloth-review 123
52
101
  ```
53
102
 
54
- This loads config from `.gsloth-settings/review/` instead of the default
55
- `.gsloth/` directory. Useful when CI uses different credentials or a different
56
- LLM provider than local development.
57
-
58
- ## Dependencies
59
-
60
- - `@gaunt-sloth/core` (required)
61
-
62
- No MCP, no A2A, no commander. This is intentional to keep the package lightweight for CI use.
103
+ This loads config from `.gsloth-settings/review/` instead of the default `.gsloth/` directory.
104
+ Useful when CI uses different credentials or a different LLM provider than local development.
63
105
 
64
106
  ## Exports
65
107
 
66
- ```js
67
- import { reviewModule } from '@gaunt-sloth/review/reviewModule.js';
68
- import { questionAnsweringModule } from '@gaunt-sloth/review/questionAnsweringModule.js';
69
- import { commandUtils } from '@gaunt-sloth/review/commandUtils.js';
70
- ```
108
+ - `@gaunt-sloth/review` (the root export) is the public API: the review module (`review`,
109
+ `ReviewContext`), `commandUtils`, and the `gh` read-file tool — plus, deliberately, the whole
110
+ `@gaunt-sloth/core` config barrel (`initConfig`, `GthConfig`, `DEFAULT_CONFIG`, …), re-exported
111
+ so an embedder can resolve config from the review root without importing core directly.
112
+ This surface is what the embed example above and the fat CLI use.
113
+ - `@gaunt-sloth/review/<path>.js` deep paths (e.g.
114
+ `@gaunt-sloth/review/modules/reviewModule.js`) mirror the package's internal `dist/` layout
115
+ 1:1 and are deliberately kept open for reach-in. They are supported at your own risk: internal
116
+ files can move between alpha/minor versions without a deprecation cycle. Prefer the root
117
+ export where it suffices.
71
118
 
72
119
  ## Related packages
73
120
 
74
- - [`@gaunt-sloth/core`](../core) — Core utilities, config, and agent infrastructure
75
- - [`@gaunt-sloth/agent`](../agent) — Agent runtime: built-in tools, filesystem toolkit, middleware registry, API server, AG-UI, MCP, and A2A integration
76
- - [`@gaunt-sloth/review`](../review) — Review and Q&A modules with standalone CLI (this package)
77
- - [`gaunt-sloth`](../app) — Main CLI application
121
+ - [`@gaunt-sloth/core`](https://www.npmjs.com/package/@gaunt-sloth/core) — Core utilities, config, and agent infrastructure ([source](https://github.com/pukeko-robotics/gaunt-sloth/tree/main/packages/core))
122
+ - [`@gaunt-sloth/agent`](https://www.npmjs.com/package/@gaunt-sloth/agent) — Agent runtime: built-in tools, filesystem toolkit, middleware registry, API server, AG-UI, MCP, and A2A integration ([source](https://github.com/pukeko-robotics/gaunt-sloth/tree/main/packages/agent))
123
+ - [`@gaunt-sloth/batch`](https://www.npmjs.com/package/@gaunt-sloth/batch) — Batch / eval / workflow runtime ([source](https://github.com/pukeko-robotics/gaunt-sloth/tree/main/packages/batch))
124
+ - [`gaunt-sloth`](https://www.npmjs.com/package/gaunt-sloth) — Main CLI application ([source](https://github.com/pukeko-robotics/gaunt-sloth/tree/main/packages/app))
package/cli.js CHANGED
@@ -25,8 +25,12 @@ setEntryPoint(import.meta.url);
25
25
  import { initConfig } from '@gaunt-sloth/core/config.js';
26
26
  import { review } from '#src/modules/reviewModule.js';
27
27
  import { displayError } from '@gaunt-sloth/core/utils/consoleUtils.js';
28
- import { getContentFromSource, getRequirementsFromSource } from '#src/commands/commandUtils.js';
29
- import { buildSystemMessages } from '@gaunt-sloth/core/utils/llmUtils.js';
28
+ import {
29
+ getContentFromSource,
30
+ getRequirementsFromSource,
31
+ getReviewPreamble,
32
+ resolvePrIdFromArg,
33
+ } from '#src/commands/commandUtils.js';
30
34
 
31
35
  async function main() {
32
36
  try {
@@ -53,18 +57,20 @@ async function main() {
53
57
  }
54
58
  }
55
59
 
56
- // Build preamble from system messages
57
- const preamble = buildSystemMessages(config, 'pr');
58
- const preambleText = preamble
59
- .map((m) => (typeof m.content === 'string' ? m.content : ''))
60
- .join('\n');
60
+ // Build the review preamble (backstory + guidelines + review instructions + optional
61
+ // system prompt), the same composition `gth review` / `gth pr` use.
62
+ const preambleText = getReviewPreamble(config);
61
63
 
62
64
  // Combine requirements and content for the review
63
65
  const diffWithReqs = requirements
64
66
  ? `Requirements:\n${requirements}\nDiff:\n${content}`
65
67
  : content;
66
68
 
67
- await review('pr-review', preambleText, diffWithReqs, config, 'pr');
69
+ // Pass the PR id on so GitHub-only tools address the PR explicitly instead of trying to
70
+ // resolve it from the checked-out branch, which a CI runner's detached HEAD cannot provide.
71
+ await review('pr-review', preambleText, diffWithReqs, config, 'pr', undefined, {
72
+ prId: resolvePrIdFromArg(contentArg),
73
+ });
68
74
  } catch (error) {
69
75
  displayError(error instanceof Error ? error.message : String(error));
70
76
  process.exit(1);
@@ -1,14 +1,21 @@
1
1
  import type { GthConfig } from '@gaunt-sloth/core/config.js';
2
+ /**
3
+ * Compose the review system preamble the way the fat CLI's `gth review` / `gth pr` do:
4
+ * backstory + guidelines + review instructions + the optional project system prompt, each
5
+ * segment honouring the GS2-43 `prompts.*` config. Empty segments are dropped rather than
6
+ * left as blank lines (the shape the README embed example documents).
7
+ */
8
+ export declare function getReviewPreamble(config: GthConfig): string;
2
9
  /**
3
10
  * Requirement sources. Expected to be in `.sources/` dir.
4
11
  * Aliases are mapped to actual sources in this file
5
12
  */
6
13
  export declare const REQUIREMENTS_SOURCES: {
7
- readonly 'jira-legacy': "jiraIssueLegacySource.js";
8
- readonly jira: "jiraIssueSource.js";
9
- readonly github: "ghIssueSource.js";
10
- readonly text: "textSource.js";
11
- readonly file: "fileSource.js";
14
+ readonly 'jira-legacy': 'jiraIssueLegacySource.js';
15
+ readonly jira: 'jiraIssueSource.js';
16
+ readonly github: 'ghIssueSource.js';
17
+ readonly text: 'textSource.js';
18
+ readonly file: 'fileSource.js';
12
19
  };
13
20
  export type RequirementSourceType = keyof typeof REQUIREMENTS_SOURCES;
14
21
  /**
@@ -16,10 +23,21 @@ export type RequirementSourceType = keyof typeof REQUIREMENTS_SOURCES;
16
23
  * Aliases are mapped to actual sources in this file
17
24
  */
18
25
  export declare const CONTENT_SOURCES: {
19
- readonly github: "ghPrDiffSource.js";
20
- readonly text: "textSource.js";
21
- readonly file: "fileSource.js";
26
+ readonly github: 'ghPrDiffSource.js';
27
+ readonly git: 'gitDiffSource.js';
28
+ readonly text: 'textSource.js';
29
+ readonly file: 'fileSource.js';
22
30
  };
23
31
  export type ContentSourceType = keyof typeof CONTENT_SOURCES;
24
32
  export declare function getRequirementsFromSource(requirementSource: RequirementSourceType | undefined, requirementsId: string | undefined, config: GthConfig): Promise<string>;
25
33
  export declare function getContentFromSource(contentSource: ContentSourceType | undefined, contentId: string | undefined, config: GthConfig): Promise<string>;
34
+ /**
35
+ * Reads a PR id out of the CLI's first positional argument, which is a PR number in the GitHub
36
+ * flow and arbitrary content otherwise.
37
+ *
38
+ * The id binds GitHub-only review tools to the PR under review. Without it they fall back to
39
+ * resolving the PR from the checked-out branch, which no CI runner has: the standard checkout
40
+ * actions leave the workspace on a detached HEAD, so the fallback cannot succeed there and the
41
+ * tools degrade to unavailable.
42
+ */
43
+ export declare function resolvePrIdFromArg(contentArg: string | undefined): string | undefined;
@@ -1,5 +1,21 @@
1
1
  import { displayError } from '@gaunt-sloth/core/utils/consoleUtils.js';
2
- import { wrapContent } from '@gaunt-sloth/core/utils/llmUtils.js';
2
+ import { readBackstory, readGuidelines, readReviewInstructions, readSystemPrompt, wrapContent, } from '@gaunt-sloth/core/utils/llmUtils.js';
3
+ /**
4
+ * Compose the review system preamble the way the fat CLI's `gth review` / `gth pr` do:
5
+ * backstory + guidelines + review instructions + the optional project system prompt, each
6
+ * segment honouring the GS2-43 `prompts.*` config. Empty segments are dropped rather than
7
+ * left as blank lines (the shape the README embed example documents).
8
+ */
9
+ export function getReviewPreamble(config) {
10
+ return [
11
+ readBackstory(config),
12
+ readGuidelines(config),
13
+ readReviewInstructions(config),
14
+ readSystemPrompt(config),
15
+ ]
16
+ .filter(Boolean)
17
+ .join('\n');
18
+ }
3
19
  /**
4
20
  * Requirement sources. Expected to be in `.sources/` dir.
5
21
  * Aliases are mapped to actual sources in this file
@@ -17,6 +33,7 @@ export const REQUIREMENTS_SOURCES = {
17
33
  */
18
34
  export const CONTENT_SOURCES = {
19
35
  github: 'ghPrDiffSource.js',
36
+ git: 'gitDiffSource.js',
20
37
  text: 'textSource.js',
21
38
  file: 'fileSource.js',
22
39
  };
@@ -26,7 +43,21 @@ export async function getRequirementsFromSource(requirementSource, requirementsI
26
43
  }
27
44
  export async function getContentFromSource(contentSource, contentId, config) {
28
45
  const content = await getFromSource(contentSource, contentId, (config?.contentSourceConfig ?? {})[contentSource], CONTENT_SOURCES);
29
- return wrapContent(content, contentSource, contentSource === 'github' ? 'GitHub diff' : 'content');
46
+ return wrapContent(content, contentSource, contentSource === 'github' ? 'GitHub diff' : contentSource === 'git' ? 'git diff' : 'content');
47
+ }
48
+ /** A PR id is a bare number; anything else is literal content (a diff, a file path, text). */
49
+ const PR_ID_PATTERN = /^\d+$/;
50
+ /**
51
+ * Reads a PR id out of the CLI's first positional argument, which is a PR number in the GitHub
52
+ * flow and arbitrary content otherwise.
53
+ *
54
+ * The id binds GitHub-only review tools to the PR under review. Without it they fall back to
55
+ * resolving the PR from the checked-out branch, which no CI runner has: the standard checkout
56
+ * actions leave the workspace on a detached HEAD, so the fallback cannot succeed there and the
57
+ * tools degrade to unavailable.
58
+ */
59
+ export function resolvePrIdFromArg(contentArg) {
60
+ return contentArg !== undefined && PR_ID_PATTERN.test(contentArg) ? contentArg : undefined;
30
61
  }
31
62
  async function getFromSource(source, id,
32
63
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -1 +1 @@
1
- {"version":3,"file":"commandUtils.js","sourceRoot":"","sources":["../../src/commands/commandUtils.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,yCAAyC,CAAC;AAEvE,OAAO,EAAE,WAAW,EAAE,MAAM,qCAAqC,CAAC;AAElE;;;GAGG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG;IAClC,aAAa,EAAE,0BAA0B;IACzC,IAAI,EAAE,oBAAoB;IAC1B,MAAM,EAAE,kBAAkB;IAC1B,IAAI,EAAE,eAAe;IACrB,IAAI,EAAE,eAAe;CACb,CAAC;AAIX;;;GAGG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG;IAC7B,MAAM,EAAE,mBAAmB;IAC3B,IAAI,EAAE,eAAe;IACrB,IAAI,EAAE,eAAe;CACb,CAAC;AAIX,MAAM,CAAC,KAAK,UAAU,yBAAyB,CAC7C,iBAAoD,EACpD,cAAkC,EAClC,MAAiB;IAEjB,MAAM,YAAY,GAAG,MAAM,aAAa,CACtC,iBAAiB,EACjB,cAAc,EACd,CAAC,MAAM,EAAE,uBAAuB,IAAI,EAAE,CAAC,CAAC,iBAA2B,CAAC,EACpE,oBAAoB,CACrB,CAAC;IACF,OAAO,WAAW,CAAC,YAAY,EAAE,iBAAiB,EAAE,cAAc,CAAC,CAAC;AACtE,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,aAA4C,EAC5C,SAA6B,EAC7B,MAAiB;IAEjB,MAAM,OAAO,GAAG,MAAM,aAAa,CACjC,aAAa,EACb,SAAS,EACT,CAAC,MAAM,EAAE,mBAAmB,IAAI,EAAE,CAAC,CAAC,aAAuB,CAAC,EAC5D,eAAe,CAChB,CAAC;IACF,OAAO,WAAW,CAChB,OAAO,EACP,aAAa,EACb,aAAa,KAAK,QAAQ,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,SAAS,CACvD,CAAC;AACJ,CAAC;AAED,KAAK,UAAU,aAAa,CAC1B,MAA6D,EAC7D,EAAsB;AACtB,8DAA8D;AAC9D,MAAW,EACX,sBAA4E;IAE5E,IAAI,OAAO,MAAM,KAAK,QAAQ,EAAE,CAAC;QAC/B,oCAAoC;QACpC,IAAI,sBAAsB,CAAC,MAA6C,CAAC,EAAE,CAAC;YAC1E,MAAM,UAAU,GAAG,gBAAgB,sBAAsB,CAAC,MAA6C,CAAC,EAAE,CAAC;YAC3G,MAAM,EAAE,GAAG,EAAE,GAAG,MAAM,MAAM,CAAC,UAAU,CAAC,CAAC;YACzC,OAAO,MAAM,GAAG,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;QAC/B,CAAC;aAAM,CAAC;YACN,YAAY,CAAC,mBAAmB,MAAM,0BAA0B,CAAC,CAAC;QACpE,CAAC;IACH,CAAC;SAAM,IAAI,OAAO,MAAM,KAAK,UAAU,EAAE,CAAC;QACxC,yCAAyC;QACzC,OAAO,MAAO,MAAsD,CAAC,EAAE,CAAC,CAAC;IAC3E,CAAC;IACD,OAAO,EAAE,CAAC;AACZ,CAAC"}
1
+ {"version":3,"file":"commandUtils.js","sourceRoot":"","sources":["../../src/commands/commandUtils.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,yCAAyC,CAAC;AAEvE,OAAO,EACL,aAAa,EACb,cAAc,EACd,sBAAsB,EACtB,gBAAgB,EAChB,WAAW,GACZ,MAAM,qCAAqC,CAAC;AAE7C;;;;;GAKG;AACH,MAAM,UAAU,iBAAiB,CAAC,MAAiB;IACjD,OAAO;QACL,aAAa,CAAC,MAAM,CAAC;QACrB,cAAc,CAAC,MAAM,CAAC;QACtB,sBAAsB,CAAC,MAAM,CAAC;QAC9B,gBAAgB,CAAC,MAAM,CAAC;KACzB;SACE,MAAM,CAAC,OAAO,CAAC;SACf,IAAI,CAAC,IAAI,CAAC,CAAC;AAChB,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG;IAClC,aAAa,EAAE,0BAA0B;IACzC,IAAI,EAAE,oBAAoB;IAC1B,MAAM,EAAE,kBAAkB;IAC1B,IAAI,EAAE,eAAe;IACrB,IAAI,EAAE,eAAe;CACb,CAAC;AAIX;;;GAGG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG;IAC7B,MAAM,EAAE,mBAAmB;IAC3B,GAAG,EAAE,kBAAkB;IACvB,IAAI,EAAE,eAAe;IACrB,IAAI,EAAE,eAAe;CACb,CAAC;AAIX,MAAM,CAAC,KAAK,UAAU,yBAAyB,CAC7C,iBAAoD,EACpD,cAAkC,EAClC,MAAiB;IAEjB,MAAM,YAAY,GAAG,MAAM,aAAa,CACtC,iBAAiB,EACjB,cAAc,EACd,CAAC,MAAM,EAAE,uBAAuB,IAAI,EAAE,CAAC,CAAC,iBAA2B,CAAC,EACpE,oBAAoB,CACrB,CAAC;IACF,OAAO,WAAW,CAAC,YAAY,EAAE,iBAAiB,EAAE,cAAc,CAAC,CAAC;AACtE,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,aAA4C,EAC5C,SAA6B,EAC7B,MAAiB;IAEjB,MAAM,OAAO,GAAG,MAAM,aAAa,CACjC,aAAa,EACb,SAAS,EACT,CAAC,MAAM,EAAE,mBAAmB,IAAI,EAAE,CAAC,CAAC,aAAuB,CAAC,EAC5D,eAAe,CAChB,CAAC;IACF,OAAO,WAAW,CAChB,OAAO,EACP,aAAa,EACb,aAAa,KAAK,QAAQ,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,aAAa,KAAK,KAAK,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS,CAC9F,CAAC;AACJ,CAAC;AAED,8FAA8F;AAC9F,MAAM,aAAa,GAAG,OAAO,CAAC;AAE9B;;;;;;;;GAQG;AACH,MAAM,UAAU,kBAAkB,CAAC,UAA8B;IAC/D,OAAO,UAAU,KAAK,SAAS,IAAI,aAAa,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS,CAAC;AAC7F,CAAC;AAED,KAAK,UAAU,aAAa,CAC1B,MAA6D,EAC7D,EAAsB;AACtB,8DAA8D;AAC9D,MAAW,EACX,sBAA4E;IAE5E,IAAI,OAAO,MAAM,KAAK,QAAQ,EAAE,CAAC;QAC/B,oCAAoC;QACpC,IAAI,sBAAsB,CAAC,MAA6C,CAAC,EAAE,CAAC;YAC1E,MAAM,UAAU,GAAG,gBAAgB,sBAAsB,CAAC,MAA6C,CAAC,EAAE,CAAC;YAC3G,MAAM,EAAE,GAAG,EAAE,GAAG,MAAM,MAAM,CAAC,UAAU,CAAC,CAAC;YACzC,OAAO,MAAM,GAAG,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;QAC/B,CAAC;aAAM,CAAC;YACN,YAAY,CAAC,mBAAmB,MAAM,0BAA0B,CAAC,CAAC;QACpE,CAAC;IACH,CAAC;SAAM,IAAI,OAAO,MAAM,KAAK,UAAU,EAAE,CAAC;QACxC,yCAAyC;QACzC,OAAO,MAAO,MAAsD,CAAC,EAAE,CAAC,CAAC;IAC3E,CAAC;IACD,OAAO,EAAE,CAAC;AACZ,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"jiraClient.js","sourceRoot":"","sources":["../../../src/helpers/jira/jiraClient.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"jiraClient.js","sourceRoot":"","sources":["../../../src/helpers/jira/jiraClient.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Build the review's opening header: `Gaunt Sloth · <command> · <model> (<provider>)`, plus a
3
+ * trailing newline so the review body does not start flush against it.
4
+ *
5
+ * The line is assembled by the SHARED {@link runHeaderLine}, and the model half by the SHARED
6
+ * `model (provider)` spelling ({@link modelProviderLabel}) — the same two the agent's own run
7
+ * header uses, rather than a second spelling of the same facts.
8
+ *
9
+ * Both halves of the model are allowed to be missing, and neither prints a placeholder:
10
+ *
11
+ * - **No provider** — a `.gsloth.config.js` module config hands the loader an already-built
12
+ * `BaseChatModel` and legitimately has no provider string. The bare model prints; there is no
13
+ * `(unknown)` and no empty `()`.
14
+ * - **No model** — the label is dropped entirely and the line ends after the command. A provider
15
+ * name on its own would sit exactly where a model name sits and read as one, and a misidentified
16
+ * model in a review someone later quotes is worse than an absent one. That is the same
17
+ * drop-rather-than-mislead rule the banner applies to a version that does not fit.
18
+ */
19
+ export declare function reviewHeadingBlock(command: 'pr' | 'review', model?: string, provider?: string): string;
@@ -0,0 +1,60 @@
1
+ /**
2
+ * @module reviewHeading
3
+ * REL-12 — the line every `gth review` / `gth pr` run opens with, so a reader of the output knows
4
+ * whose review it is and what produced it.
5
+ *
6
+ * User-facing CLI feedback, so it is governed by `maintenance/ux-guidelines.md` § The run header,
7
+ * and it serves three of the Design Language principles: **DL-4** (transparency — a
8
+ * reader of the output knows what produced it), **DL-6** (cross-surface consistency — it renders
9
+ * through the shared `runHeaderLine`, so the review document and the agent's own run header are one
10
+ * string and not two that happen to match), and **DL-7** (graceful degradation — drop the label
11
+ * rather than mislead, exactly as the banner drops a version that will not fit).
12
+ *
13
+ * ## Why the product emits this and not the workflow
14
+ *
15
+ * The review is usually read somewhere the command is not visible: a PR comment posted by a bot
16
+ * account, a `review.md` attached to a ticket, a CI log. Stripped of the invocation, an unlabelled
17
+ * AI review on a pull request is simply assumed to be whichever AI reviewer the reader already
18
+ * knows. Putting the attribution in the CLI's own output means any workflow that runs `gth` and
19
+ * posts what it produced carries it, with no wiring of its own.
20
+ *
21
+ * ## Why it names the command, and why that is one line and no more
22
+ *
23
+ * The word after the product name is the command the USER typed — `review` or `pr` — which is the
24
+ * one thing a reader of a detached review cannot recover for themselves. It is the same header
25
+ * every other command opens with, so a reader who has seen one has seen all of them.
26
+ *
27
+ * A review is read for its findings. Everything above the first finding pushes that finding down,
28
+ * so the header is one line: no markdown heading, no rule, no box, no timestamp, no version, no
29
+ * restated repo/branch/PR. The cost of a bigger banner is paid by every reader of every review.
30
+ *
31
+ * It is not, however, unconditional. `output.header: none` drops it (the gate is at the emission
32
+ * site in `reviewModule.ts`, not here — this module builds the line and does not decide whether it
33
+ * is shown), because a caller piping a review into their own template needs a byte-clean stream.
34
+ * That rung is opt-in precisely so the reasoning above survives for everyone who does not set it.
35
+ */
36
+ import { modelProviderLabel } from '@gaunt-sloth/core/core/modelLabel.js';
37
+ import { runHeaderLine } from '@gaunt-sloth/core/core/runHeader.js';
38
+ /**
39
+ * Build the review's opening header: `Gaunt Sloth · <command> · <model> (<provider>)`, plus a
40
+ * trailing newline so the review body does not start flush against it.
41
+ *
42
+ * The line is assembled by the SHARED {@link runHeaderLine}, and the model half by the SHARED
43
+ * `model (provider)` spelling ({@link modelProviderLabel}) — the same two the agent's own run
44
+ * header uses, rather than a second spelling of the same facts.
45
+ *
46
+ * Both halves of the model are allowed to be missing, and neither prints a placeholder:
47
+ *
48
+ * - **No provider** — a `.gsloth.config.js` module config hands the loader an already-built
49
+ * `BaseChatModel` and legitimately has no provider string. The bare model prints; there is no
50
+ * `(unknown)` and no empty `()`.
51
+ * - **No model** — the label is dropped entirely and the line ends after the command. A provider
52
+ * name on its own would sit exactly where a model name sits and read as one, and a misidentified
53
+ * model in a review someone later quotes is worse than an absent one. That is the same
54
+ * drop-rather-than-mislead rule the banner applies to a version that does not fit.
55
+ */
56
+ export function reviewHeadingBlock(command, model, provider) {
57
+ const label = model?.trim() ? modelProviderLabel(model, provider) : undefined;
58
+ return `${runHeaderLine(command, label)}\n`;
59
+ }
60
+ //# sourceMappingURL=reviewHeading.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reviewHeading.js","sourceRoot":"","sources":["../../src/modules/reviewHeading.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,OAAO,EAAE,kBAAkB,EAAE,MAAM,sCAAsC,CAAC;AAC1E,OAAO,EAAE,aAAa,EAAE,MAAM,qCAAqC,CAAC;AAEpE;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,UAAU,kBAAkB,CAChC,OAAwB,EACxB,KAAc,EACd,QAAiB;IAEjB,MAAM,KAAK,GAAG,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,kBAAkB,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IAC9E,OAAO,GAAG,aAAa,CAAC,OAAO,EAAE,KAAK,CAAC,IAAI,CAAC;AAC9C,CAAC"}
@@ -5,4 +5,16 @@ export interface ReviewContext {
5
5
  /** PR number under review; undefined in `gth pr` discovery mode (current branch's PR). */
6
6
  prId?: string;
7
7
  }
8
- export declare function review(source: string, preamble: string, diff: string, config: GthConfig, command?: 'pr' | 'review', resolvers?: AgentResolvers, reviewContext?: ReviewContext): Promise<void>;
8
+ /**
9
+ * Run a review of `diff` and print the verdict.
10
+ *
11
+ * @param source - Source label, used for the output file name.
12
+ * @param _preamble - Ignored (GS2-79); see the body comment. Retained positionally so existing
13
+ * callers need no change, exactly as `runSingleShot` retains its own.
14
+ * @param diff - The content under review.
15
+ * @param config - The resolved config.
16
+ * @param command - `review` or `pr`; selects the command config and the agent's mode prompt.
17
+ * @param resolvers - Optional agent resolvers (tools/middleware).
18
+ * @param reviewContext - Extra review context (binds GitHub-only tools to the PR under review).
19
+ */
20
+ export declare function review(source: string, _preamble: string, diff: string, config: GthConfig, command?: 'pr' | 'review', resolvers?: AgentResolvers, reviewContext?: ReviewContext): Promise<void>;
@@ -1,73 +1,152 @@
1
- import { defaultStatusCallback, displayDebug, displayError, displayInfo, displaySuccess, displayWarning, flushSessionLog, initSessionLogging, stopSessionLogging, } from '@gaunt-sloth/core/utils/consoleUtils.js';
1
+ import { isGhReadFileToolEnabled } from '@gaunt-sloth/core/config.js';
2
+ import { defaultStatusCallback, display, displayDebug, displayError, displayInfo, displaySuccess, displayWarning, flushSessionLog, initSessionLogging, stopSessionLogging, } from '@gaunt-sloth/core/utils/consoleUtils.js';
3
+ import { reviewHeadingBlock } from '#src/modules/reviewHeading.js';
2
4
  import { getCommandOutputFilePath } from '#src/utils/fileUtils.js';
3
- import { HumanMessage, SystemMessage } from '@langchain/core/messages';
5
+ import { HumanMessage } from '@langchain/core/messages';
4
6
  import { GthAgentRunner } from '@gaunt-sloth/core/core/GthAgentRunner.js';
5
7
  import { MemorySaver } from '@langchain/langgraph';
6
8
  import { ProgressIndicator } from '@gaunt-sloth/core/utils/ProgressIndicator.js';
7
9
  import { createReviewRateMiddleware, REVIEW_RATE_ARTIFACT_KEY, } from '#src/middleware/reviewRateMiddleware.js';
8
10
  import { deleteArtifact, getArtifact } from '@gaunt-sloth/core/state/artifactStore.js';
9
- import { setExitCode } from '@gaunt-sloth/core/utils/systemUtils.js';
11
+ import { setExitCode, stdout } from '@gaunt-sloth/core/utils/systemUtils.js';
12
+ import { ApprovalStopError, approvalStopRows } from '@gaunt-sloth/core/core/shell/approvalStop.js';
10
13
  import { get as getGhReadFileTool, GTH_GH_READ_FILE_TOOL_NAME } from '#src/tools/ghReadFileTool.js';
11
- export async function review(source, preamble, diff, config, command = 'review', resolvers, reviewContext) {
14
+ /**
15
+ * Run a review of `diff` and print the verdict.
16
+ *
17
+ * @param source - Source label, used for the output file name.
18
+ * @param _preamble - Ignored (GS2-79); see the body comment. Retained positionally so existing
19
+ * callers need no change, exactly as `runSingleShot` retains its own.
20
+ * @param diff - The content under review.
21
+ * @param config - The resolved config.
22
+ * @param command - `review` or `pr`; selects the command config and the agent's mode prompt.
23
+ * @param resolvers - Optional agent resolvers (tools/middleware).
24
+ * @param reviewContext - Extra review context (binds GitHub-only tools to the PR under review).
25
+ */
26
+ export async function review(source,
27
+ // GS2-79: `_preamble` is retained for signature stability but is NO LONGER injected as a leading
28
+ // SystemMessage. Both agent backends COMPOSE the full system prompt themselves — backstory +
29
+ // guidelines + the per-command mode prompt + system prompt — and hand it to createAgent as
30
+ // `systemPrompt`; for `review`/`pr` that mode prompt IS the review instructions (core's
31
+ // `readModePrompt`). Passing this preamble as well produced TWO system messages, which
32
+ // `@langchain/anthropic` rejects outright ("System messages are only permitted as the first
33
+ // passed message"), breaking every `gth review` and `gth pr` run on Anthropic on both backends
34
+ // (Google/OpenAI silently merged them, so only Anthropic showed it). The same removal was made in
35
+ // `runSingleShot` and `conversation` for the same reason.
36
+ //
37
+ // Dropping it is content-preserving ONLY because the agent selects the review instructions for
38
+ // `review`/`pr` itself; composing the CHAT prompt there and removing the preamble would silently
39
+ // turn a review into a chat. `GthModePromptSelection.spec.ts` pins that.
40
+ _preamble, diff, config, command = 'review', resolvers, reviewContext) {
12
41
  const progressIndicator = config.streamOutput ? undefined : new ProgressIndicator('Reviewing.');
13
- const messages = [new SystemMessage(preamble), new HumanMessage(diff)];
14
- // REL-2: optionally give the review agent a `gh api` file-read tool so it can fetch the FULL
15
- // contents of a file when the PR diff truncates large changes. Only added in a GitHub PR
16
- // context (the content source resolves to GitHub); a graceful no-op otherwise. Reads through
17
- // the GitHub API rather than the workspace filesystem, so it is safe under pull_request_target.
18
- maybeAddGhReadFileTool(config, command, reviewContext?.prId);
19
- // Prepare logging path (if enabled by config)
20
- const filePath = getCommandOutputFilePath(config, source);
21
- if (filePath) {
22
- initSessionLogging(filePath, config.streamSessionInferenceLog);
23
- }
24
- const rateConfig = config.commands?.[command]?.rating;
25
- if (rateConfig && rateConfig.enabled !== false) {
26
- const confMiddleware = config.middleware || [];
27
- const middlewareWithoutReviewRate = confMiddleware.filter((mw) => {
28
- return !(typeof mw === 'object' &&
29
- mw !== null &&
30
- 'name' in mw &&
31
- mw.name === 'review-rate');
32
- });
33
- // Resolve review-rate middleware directly rather than going through the registry
34
- const reviewRateMiddleware = await createReviewRateMiddleware(rateConfig, config);
35
- config.middleware = [...middlewareWithoutReviewRate, reviewRateMiddleware];
36
- }
37
- // When no resolvers are provided (e.g. standalone review CLI, without @gaunt-sloth/agent's
38
- // resolvers), supply a minimal middleware resolver that passes through already-resolved
39
- // middleware. The full `gaunt-sloth` CLI injects @gaunt-sloth/agent's resolvers instead.
40
- const effectiveResolvers = resolvers ?? {
41
- resolveMiddleware: async (middleware) => middleware ?? [],
42
- };
43
- const runner = new GthAgentRunner(defaultStatusCallback, effectiveResolvers);
44
42
  try {
45
- await runner.init(command, config, new MemorySaver());
46
- await runner.processMessages(messages);
47
- }
48
- catch (error) {
49
- displayDebug(error instanceof Error ? error : String(error));
50
- const reason = error instanceof Error ? error.message : String(error);
51
- displayError(reason ? `Failed to run review with agent.\n\n${reason}` : 'Failed to run review with agent.');
52
- }
53
- finally {
54
- await runner.cleanup();
55
- }
56
- progressIndicator?.stop();
57
- handleRatingResult(rateConfig, command);
58
- // Close the file AFTER rating is written
59
- if (filePath) {
43
+ // Only the human turn: the agent supplies the system prompt via `createAgent({ systemPrompt })`.
44
+ const messages = [new HumanMessage(diff)];
45
+ // REL-2: optionally give the review agent a `gh api` file-read tool so it can fetch the FULL
46
+ // contents of a file when the PR diff truncates large changes. Only added in a GitHub PR
47
+ // context (the content source resolves to GitHub); a graceful no-op otherwise. Reads through
48
+ // the GitHub API rather than the workspace filesystem, so it is safe under pull_request_target.
49
+ maybeAddGhReadFileTool(config, command, reviewContext?.prId);
50
+ // Prepare logging path (if enabled by config)
51
+ const filePath = getCommandOutputFilePath(config, source);
52
+ if (filePath) {
53
+ initSessionLogging(filePath, config.streamSessionInferenceLog);
54
+ }
55
+ // REL-12: head the review with its attribution. It sits AFTER `initSessionLogging` on purpose —
56
+ // the session log is a capture of console output, so this ordering is the whole reason one
57
+ // emission covers both surfaces: the terminal AND the `writeOutputToFile` report a workflow
58
+ // reads back and posts. Emitted BEFORE the agent runs so it is the first thing in both.
59
+ //
60
+ // Through the ordinary `display` helper, never `headerStatus`: this is not the agent's
61
+ // technical preamble but the first line of the review document, so it survives the `compact`
62
+ // rung that strips the preamble — and on that rung it IS the run header, which is why the agent
63
+ // emits none of its own for `review`/`pr` (GS2-95: both render the same line, so a second
64
+ // emission would print the header twice on one screen).
65
+ //
66
+ // GS2-93: `none` is the one rung that reaches it, and it silences the block outright. That
67
+ // deliberately reverses REL-12 for a user who asks for it: a caller piping a review into their
68
+ // own template or diffing captured stdout needs a byte-clean stream, and nobody loses
69
+ // attribution without setting this key.
70
+ if (config.output?.header !== 'none') {
71
+ display(reviewHeadingBlock(command, config.modelDisplayName, config.modelProviderType));
72
+ }
73
+ const rateConfig = config.commands?.[command]?.rating;
74
+ if (rateConfig && rateConfig.enabled !== false) {
75
+ const confMiddleware = config.middleware || [];
76
+ const middlewareWithoutReviewRate = confMiddleware.filter((mw) => {
77
+ return !(typeof mw === 'object' &&
78
+ mw !== null &&
79
+ 'name' in mw &&
80
+ mw.name === 'review-rate');
81
+ });
82
+ // Resolve review-rate middleware directly rather than going through the registry
83
+ const reviewRateMiddleware = await createReviewRateMiddleware(rateConfig, config);
84
+ config.middleware = [...middlewareWithoutReviewRate, reviewRateMiddleware];
85
+ }
86
+ // When no resolvers are provided (e.g. standalone review CLI, without @gaunt-sloth/agent's
87
+ // resolvers), supply a minimal middleware resolver that passes through already-resolved
88
+ // middleware. The full `gaunt-sloth` CLI injects @gaunt-sloth/agent's resolvers instead.
89
+ const effectiveResolvers = resolvers ?? {
90
+ resolveMiddleware: async (middleware) => middleware ?? [],
91
+ };
92
+ // GS2-81: no backend factory, so `review`/`pr` run the runner's built-in lean agent —
93
+ // `@gaunt-sloth/review` is a leaf package that does not depend on `@gaunt-sloth/agent`. That is
94
+ // the same agent every other command resolves to, so the layering costs nothing today; giving
95
+ // this package a dependency on the agent package to reach a backend seam would invert it.
96
+ const runner = new GthAgentRunner(defaultStatusCallback, effectiveResolvers);
60
97
  try {
61
- flushSessionLog();
62
- stopSessionLogging();
63
- displaySuccess(`\n\nThis report can be found in ${filePath}`);
98
+ await runner.init(command, config, new MemorySaver());
99
+ await runner.processMessages(messages);
64
100
  }
65
101
  catch (error) {
66
102
  displayDebug(error instanceof Error ? error : String(error));
67
- displayError(`Failed to write review to file: ${filePath}`);
103
+ // [[TUI-C71]] `review` and `pr` wire no tool-approval callback, so an escalation here is
104
+ // always the §6.2 error and an `attack` verdict is always the halt: the untrusted text this
105
+ // catch prints is model-authored by construction. It goes through the SAME framed renderer
106
+ // as every other surface — one row per line, each inside the gutter — instead of being
107
+ // interpolated into a line the terminal is free to wrap back to column 0.
108
+ if (error instanceof ApprovalStopError) {
109
+ displayError('Failed to run review with agent.\n');
110
+ for (const row of approvalStopRows(error.parts, { columns: stdout.columns })) {
111
+ displayError(row);
112
+ }
113
+ }
114
+ else {
115
+ const reason = error instanceof Error ? error.message : String(error);
116
+ displayError(reason
117
+ ? `Failed to run review with agent.\n\n${reason}`
118
+ : 'Failed to run review with agent.');
119
+ }
120
+ }
121
+ finally {
122
+ await runner.cleanup();
68
123
  }
124
+ progressIndicator?.stop();
125
+ handleRatingResult(rateConfig, command);
126
+ // Close the file AFTER rating is written
127
+ if (filePath) {
128
+ try {
129
+ flushSessionLog();
130
+ stopSessionLogging();
131
+ displaySuccess(`\n\nThis report can be found in ${filePath}`);
132
+ }
133
+ catch (error) {
134
+ displayDebug(error instanceof Error ? error : String(error));
135
+ displayError(`Failed to write review to file: ${filePath}`);
136
+ }
137
+ }
138
+ deleteArtifact(REVIEW_RATE_ARTIFACT_KEY);
139
+ }
140
+ finally {
141
+ // EXT-53: the indicator owns a 1s setInterval — an active libuv handle that keeps Node's event
142
+ // loop from ever draining, so leaking it hangs the CLI forever after the work is done. The
143
+ // `stop()` above sits where it does for output ordering (before the rating result / the
144
+ // "report can be found in …" line); this `finally` guarantees the handle is also released when
145
+ // anything above throws — notably `createReviewRateMiddleware()`, which is awaited outside any
146
+ // catch, and `runner.cleanup()`, whose own `finally` rethrows straight past the `stop()`.
147
+ // `stop()` is idempotent, so the normal path's second call is a no-op.
148
+ progressIndicator?.stop();
69
149
  }
70
- deleteArtifact(REVIEW_RATE_ARTIFACT_KEY);
71
150
  }
72
151
  /**
73
152
  * REL-2: conditionally inject the optional `gh api` file-read tool into the review agent's tools.
@@ -78,6 +157,9 @@ export async function review(source, preamble, diff, config, command = 'review',
78
157
  *
79
158
  * The tool reads file contents via the GitHub API (`gh api`), never the workspace filesystem, so
80
159
  * it remains safe under `pull_request_target` CI where the untrusted PR head is not checked out.
160
+ *
161
+ * CFG-52 — it is also gated on the unified `builtInTools` registry, resolved per-command first and
162
+ * then root by {@link isGhReadFileToolEnabled}. Absence means enabled, so this stays opt-OUT.
81
163
  */
82
164
  function maybeAddGhReadFileTool(config, command, prId) {
83
165
  const commandConfig = config.commands?.[command];
@@ -85,6 +167,9 @@ function maybeAddGhReadFileTool(config, command, prId) {
85
167
  if (contentSource !== 'github') {
86
168
  return;
87
169
  }
170
+ if (!isGhReadFileToolEnabled(config, command)) {
171
+ return;
172
+ }
88
173
  // config.tools is a union (StructuredToolInterface[] | BaseToolkit[] | ServerTool[]); the
89
174
  // gh read-file tool is a StructuredToolInterface, so we only append into a structured-tool list.
90
175
  const existingTools = (Array.isArray(config.tools) ? config.tools : []);
@@ -93,7 +178,7 @@ function maybeAddGhReadFileTool(config, command, prId) {
93
178
  if (alreadyPresent) {
94
179
  return;
95
180
  }
96
- config.tools = [...existingTools, getGhReadFileTool(config, prId)];
181
+ config.tools = [...existingTools, getGhReadFileTool(config, prId, command)];
97
182
  }
98
183
  function handleRatingResult(rateConfig, command) {
99
184
  if (!rateConfig || rateConfig.enabled === false) {
@@ -1 +1 @@
1
- {"version":3,"file":"reviewModule.js","sourceRoot":"","sources":["../../src/modules/reviewModule.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"reviewModule.js","sourceRoot":"","sources":["../../src/modules/reviewModule.ts"],"names":[],"mappings":"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"}
@@ -21,16 +21,16 @@ export async function get(_, issueId) {
21
21
  return null;
22
22
  }
23
23
  const issueLabel = /^\d+$/.test(issueId) ? `#${issueId}` : issueId;
24
+ // EXT-53: the indicator is constructed OUTSIDE the try and cleared in a `finally`. It owns a 1s
25
+ // setInterval — an active libuv handle — so a `stop()` reachable only on the success path leaves
26
+ // the event loop un-drainable and the process never exits. This is especially nasty here because
27
+ // a failed issue fetch is a SOFT failure (returns null, the review proceeds), so the hang is
28
+ // invisible: the command succeeds and then simply never returns.
29
+ const progress = new ProgressIndicator(`Fetching GitHub issue ${issueLabel}`);
30
+ let issueContent;
24
31
  try {
25
32
  // Use the GitHub CLI to fetch issue details
26
- const progress = new ProgressIndicator(`Fetching GitHub issue ${issueLabel}`);
27
- const issueContent = await execAsync(`gh issue view ${issueId}`);
28
- progress.stop();
29
- if (!issueContent) {
30
- displayWarning(`No content found for GitHub issue ${issueLabel}`);
31
- return null;
32
- }
33
- return `GitHub Issue: ${issueLabel}\n\n${issueContent}`;
33
+ issueContent = await execAsync(`gh issue view ${issueId}`);
34
34
  }
35
35
  catch (error) {
36
36
  displayWarning(`
@@ -39,5 +39,13 @@ Consider checking if gh cli (https://cli.github.com/) is installed and authentic
39
39
  `);
40
40
  return null;
41
41
  }
42
+ finally {
43
+ progress.stop();
44
+ }
45
+ if (!issueContent) {
46
+ displayWarning(`No content found for GitHub issue ${issueLabel}`);
47
+ return null;
48
+ }
49
+ return `GitHub Issue: ${issueLabel}\n\n${issueContent}`;
42
50
  }
43
51
  //# sourceMappingURL=ghIssueSource.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ghIssueSource.js","sourceRoot":"","sources":["../../src/sources/ghIssueSource.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,yCAAyC,CAAC;AAEzE,OAAO,EAAE,SAAS,EAAE,MAAM,wCAAwC,CAAC;AACnE,OAAO,EAAE,iBAAiB,EAAE,MAAM,8CAA8C,CAAC;AAEjF,6FAA6F;AAC7F,sFAAsF;AACtF,kFAAkF;AAClF,MAAM,iBAAiB,GAAG,gEAAgE,CAAC;AAE3F;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,GAAG,CACvB,CAAwB,EACxB,OAA2B;IAE3B,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,cAAc,CAAC,iCAAiC,CAAC,CAAC;QAClD,OAAO,IAAI,CAAC;IACd,CAAC;IAED,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;QACrC,cAAc,CACZ,mCAAmC,OAAO,8FAA8F,CACzI,CAAC;QACF,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,OAAO,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC;IAEnE,IAAI,CAAC;QACH,4CAA4C;QAC5C,MAAM,QAAQ,GAAG,IAAI,iBAAiB,CAAC,yBAAyB,UAAU,EAAE,CAAC,CAAC;QAC9E,MAAM,YAAY,GAAG,MAAM,SAAS,CAAC,iBAAiB,OAAO,EAAE,CAAC,CAAC;QACjE,QAAQ,CAAC,IAAI,EAAE,CAAC;QAEhB,IAAI,CAAC,YAAY,EAAE,CAAC;YAClB,cAAc,CAAC,qCAAqC,UAAU,EAAE,CAAC,CAAC;YAClE,OAAO,IAAI,CAAC;QACd,CAAC;QAED,OAAO,iBAAiB,UAAU,OAAO,YAAY,EAAE,CAAC;IAC1D,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,cAAc,CAAC;6BACU,UAAU,KAAK,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC;;KAE7F,CAAC,CAAC;QACH,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"ghIssueSource.js","sourceRoot":"","sources":["../../src/sources/ghIssueSource.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,yCAAyC,CAAC;AAEzE,OAAO,EAAE,SAAS,EAAE,MAAM,wCAAwC,CAAC;AACnE,OAAO,EAAE,iBAAiB,EAAE,MAAM,8CAA8C,CAAC;AAEjF,6FAA6F;AAC7F,sFAAsF;AACtF,kFAAkF;AAClF,MAAM,iBAAiB,GAAG,gEAAgE,CAAC;AAE3F;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,GAAG,CACvB,CAAwB,EACxB,OAA2B;IAE3B,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,cAAc,CAAC,iCAAiC,CAAC,CAAC;QAClD,OAAO,IAAI,CAAC;IACd,CAAC;IAED,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;QACrC,cAAc,CACZ,mCAAmC,OAAO,8FAA8F,CACzI,CAAC;QACF,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,OAAO,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC;IAEnE,gGAAgG;IAChG,iGAAiG;IACjG,iGAAiG;IACjG,6FAA6F;IAC7F,iEAAiE;IACjE,MAAM,QAAQ,GAAG,IAAI,iBAAiB,CAAC,yBAAyB,UAAU,EAAE,CAAC,CAAC;IAC9E,IAAI,YAAoB,CAAC;IACzB,IAAI,CAAC;QACH,4CAA4C;QAC5C,YAAY,GAAG,MAAM,SAAS,CAAC,iBAAiB,OAAO,EAAE,CAAC,CAAC;IAC7D,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,cAAc,CAAC;6BACU,UAAU,KAAK,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC;;KAE7F,CAAC,CAAC;QACH,OAAO,IAAI,CAAC;IACd,CAAC;YAAS,CAAC;QACT,QAAQ,CAAC,IAAI,EAAE,CAAC;IAClB,CAAC;IAED,IAAI,CAAC,YAAY,EAAE,CAAC;QAClB,cAAc,CAAC,qCAAqC,UAAU,EAAE,CAAC,CAAC;QAClE,OAAO,IAAI,CAAC;IACd,CAAC;IAED,OAAO,iBAAiB,UAAU,OAAO,YAAY,EAAE,CAAC;AAC1D,CAAC"}
@@ -0,0 +1,12 @@
1
+ import type { ProviderConfig } from './types.js';
2
+ /**
3
+ * Gets a local diff via `git --no-pager diff [refRange]` — review the working tree (or a
4
+ * ref range) without GitHub and without piping a diff through stdin.
5
+ *
6
+ * @param _ config (unused in this source)
7
+ * @param refRange optional revision selection passed to `git diff`, e.g. `origin/main...HEAD`
8
+ * or `HEAD~3`. When omitted, diffs the working tree against the index (plain `git diff`).
9
+ * @returns the diff content; throws with a clear message outside a git repository, on a bad
10
+ * ref, or when the diff is empty (an empty review would otherwise run against no content).
11
+ */
12
+ export declare function get(_: ProviderConfig | null, refRange: string | undefined): Promise<string | null>;
@@ -0,0 +1,75 @@
1
+ import { ProgressIndicator } from '@gaunt-sloth/core/utils/ProgressIndicator.js';
2
+ /**
3
+ * `git diff` can emit multi-megabyte output for large changes; the node default (1 MiB)
4
+ * would truncate-and-fail such runs.
5
+ */
6
+ const MAX_DIFF_BUFFER = 32 * 1024 * 1024;
7
+ /**
8
+ * Gets a local diff via `git --no-pager diff [refRange]` — review the working tree (or a
9
+ * ref range) without GitHub and without piping a diff through stdin.
10
+ *
11
+ * @param _ config (unused in this source)
12
+ * @param refRange optional revision selection passed to `git diff`, e.g. `origin/main...HEAD`
13
+ * or `HEAD~3`. When omitted, diffs the working tree against the index (plain `git diff`).
14
+ * @returns the diff content; throws with a clear message outside a git repository, on a bad
15
+ * ref, or when the diff is empty (an empty review would otherwise run against no content).
16
+ */
17
+ export async function get(_, refRange) {
18
+ // Args go to execFile (no shell), so shell metacharacters are inert; still reject
19
+ // option-shaped input so an id can never become a git flag (e.g. `--output=<file>`).
20
+ if (refRange && refRange.startsWith('-')) {
21
+ throw new Error(`Invalid git diff argument "${refRange}"; expected a ref or ref range (e.g. "origin/main...HEAD"), not an option.`);
22
+ }
23
+ const label = refRange ? `for "${refRange}"` : 'for the working tree';
24
+ const gitArgs = ['--no-pager', 'diff', ...(refRange ? [refRange] : [])];
25
+ const progress = new ProgressIndicator(`Getting local git diff ${label}`);
26
+ try {
27
+ const diffContent = await runGit(gitArgs);
28
+ progress.stop();
29
+ if (!diffContent.trim()) {
30
+ throw new Error(`No changes found in git diff ${label}; nothing to review.`);
31
+ }
32
+ return `Local git diff ${label}\n\n${diffContent}`;
33
+ }
34
+ catch (error) {
35
+ progress.stop();
36
+ const reason = error instanceof Error ? error.message : String(error);
37
+ if (reason.startsWith('No changes found')) {
38
+ throw new Error(reason);
39
+ }
40
+ throw new Error(`Failed to get git diff ${label}: ${reason}\nConsider checking that you are inside a git repository and the ref range is valid.`);
41
+ }
42
+ }
43
+ /**
44
+ * Run git with an args array (execFile, no shell). Rejects on a non-zero exit or spawn
45
+ * failure with git's stderr as the reason; benign stderr chatter on a zero exit is ignored
46
+ * (unlike systemUtils.execAsync, which rejects on any stderr output).
47
+ */
48
+ async function runGit(args) {
49
+ const { execFile } = await import('node:child_process');
50
+ return new Promise((resolve, reject) => {
51
+ execFile('git', args, { maxBuffer: MAX_DIFF_BUFFER }, (error, stdout, stderr) => {
52
+ if (error) {
53
+ reject(new Error(extractGitError(stderr ?? '', error.message)));
54
+ return;
55
+ }
56
+ resolve(stdout);
57
+ });
58
+ });
59
+ }
60
+ /**
61
+ * Reduce git's stderr to the one meaningful line. Outside a repository `git diff` appends
62
+ * its entire `--no-index` usage screen after the warning line; that wall of text is noise
63
+ * in a CLI error. Prefer the `fatal:` line when present, else the first non-empty line.
64
+ */
65
+ function extractGitError(stderr, fallback) {
66
+ const lines = stderr
67
+ .split('\n')
68
+ .map((line) => line.trim())
69
+ .filter(Boolean);
70
+ if (lines.length === 0) {
71
+ return fallback;
72
+ }
73
+ return lines.find((line) => line.startsWith('fatal:')) ?? lines[0];
74
+ }
75
+ //# sourceMappingURL=gitDiffSource.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gitDiffSource.js","sourceRoot":"","sources":["../../src/sources/gitDiffSource.ts"],"names":[],"mappings":"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"}
@@ -2,7 +2,8 @@ import type { JiraConfig } from './types.js';
2
2
  /**
3
3
  * Gets Jira issue using Atlassian REST API v3 with Personal Access Token
4
4
  *
5
- * TODO we need to figure out how would this work with public jira.
5
+ * Requires an authenticated Atlassian Cloud instance (Cloud ID + API token); anonymous
6
+ * access to a public Jira instance is not supported.
6
7
  *
7
8
  * @param config Jira configuration
8
9
  * @param issueId Jira issue ID
@@ -3,7 +3,8 @@ import { getJiraCredentials, jiraRequest, } from '#src/helpers/jira/jiraClient.j
3
3
  /**
4
4
  * Gets Jira issue using Atlassian REST API v3 with Personal Access Token
5
5
  *
6
- * TODO we need to figure out how would this work with public jira.
6
+ * Requires an authenticated Atlassian Cloud instance (Cloud ID + API token); anonymous
7
+ * access to a public Jira instance is not supported.
7
8
  *
8
9
  * @param config Jira configuration
9
10
  * @param issueId Jira issue ID
@@ -1 +1 @@
1
- {"version":3,"file":"jiraIssueSource.js","sourceRoot":"","sources":["../../src/sources/jiraIssueSource.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,yCAAyC,CAAC;AAEhG,OAAO,EACL,kBAAkB,EAClB,WAAW,GAEZ,MAAM,iCAAiC,CAAC;AAYzC;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,GAAG,CACvB,MAAkC,EAClC,OAA2B;IAE3B,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,cAAc,CAAC,yBAAyB,CAAC,CAAC;QAC1C,OAAO,IAAI,CAAC;IACd,CAAC;IACD,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,cAAc,CAAC,sBAAsB,CAAC,CAAC;QACvC,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,WAAW,GAAG,kBAAkB,CAAC,MAAM,CAAC,CAAC;IAE/C,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,MAAM,YAAY,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;QACvD,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,OAAO,IAAI,CAAC;QACd,CAAC;QAED,MAAM,OAAO,GAAG,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC;QACrC,MAAM,WAAW,GAAG,KAAK,CAAC,MAAM,CAAC,WAAW,CAAC;QAE7C,OAAO,eAAe,OAAO,cAAc,OAAO,qBAAqB,WAAW,EAAE,CAAC;IACvF,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,YAAY,CACV,6BAA6B,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CACtF,CAAC;QACF,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED;;;;;;;;;GASG;AACH,KAAK,UAAU,YAAY,CACzB,WAAoC,EACpC,OAAe;IAEf,sGAAsG;IAEtG,0KAA0K;IAC1K,sHAAsH;IACtH,8CAA8C;IAC9C,qHAAqH;IACrH,qGAAqG;IAErG,IAAI,WAAW,CAAC,UAAU,EAAE,CAAC;QAC3B,OAAO,CAAC,sBAAsB,WAAW,CAAC,UAAU,GAAG,OAAO,EAAE,CAAC,CAAC;IACpE,CAAC;IAED,wFAAwF;IACxF,MAAM,OAAO,GAAG,6BAA6B,CAAC,CAAC,wCAAwC;IAEvF,OAAO,WAAW,CAAoB,WAAW,EAAE,qBAAqB,OAAO,GAAG,OAAO,EAAE,EAAE;QAC3F,MAAM,EAAE,KAAK;KACd,CAAC,CAAC;AACL,CAAC"}
1
+ {"version":3,"file":"jiraIssueSource.js","sourceRoot":"","sources":["../../src/sources/jiraIssueSource.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,yCAAyC,CAAC;AAEhG,OAAO,EACL,kBAAkB,EAClB,WAAW,GAEZ,MAAM,iCAAiC,CAAC;AAYzC;;;;;;;;;GASG;AACH,MAAM,CAAC,KAAK,UAAU,GAAG,CACvB,MAAkC,EAClC,OAA2B;IAE3B,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,cAAc,CAAC,yBAAyB,CAAC,CAAC;QAC1C,OAAO,IAAI,CAAC;IACd,CAAC;IACD,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,cAAc,CAAC,sBAAsB,CAAC,CAAC;QACvC,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,WAAW,GAAG,kBAAkB,CAAC,MAAM,CAAC,CAAC;IAE/C,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,MAAM,YAAY,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;QACvD,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,OAAO,IAAI,CAAC;QACd,CAAC;QAED,MAAM,OAAO,GAAG,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC;QACrC,MAAM,WAAW,GAAG,KAAK,CAAC,MAAM,CAAC,WAAW,CAAC;QAE7C,OAAO,eAAe,OAAO,cAAc,OAAO,qBAAqB,WAAW,EAAE,CAAC;IACvF,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,YAAY,CACV,6BAA6B,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CACtF,CAAC;QACF,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED;;;;;;;;;GASG;AACH,KAAK,UAAU,YAAY,CACzB,WAAoC,EACpC,OAAe;IAEf,sGAAsG;IAEtG,0KAA0K;IAC1K,sHAAsH;IACtH,8CAA8C;IAC9C,qHAAqH;IACrH,qGAAqG;IAErG,IAAI,WAAW,CAAC,UAAU,EAAE,CAAC;QAC3B,OAAO,CAAC,sBAAsB,WAAW,CAAC,UAAU,GAAG,OAAO,EAAE,CAAC,CAAC;IACpE,CAAC;IAED,wFAAwF;IACxF,MAAM,OAAO,GAAG,6BAA6B,CAAC,CAAC,wCAAwC;IAEvF,OAAO,WAAW,CAAoB,WAAW,EAAE,qBAAqB,OAAO,GAAG,OAAO,EAAE,EAAE;QAC3F,MAAM,EAAE,KAAK;KACd,CAAC,CAAC;AACL,CAAC"}
@@ -1,6 +1,7 @@
1
1
  import { z } from 'zod';
2
2
  import type { StructuredToolInterface } from '@langchain/core/tools';
3
3
  import type { GthConfig } from '@gaunt-sloth/core/config.js';
4
+ import { type GhReadFileCommand } from '@gaunt-sloth/core/config.js';
4
5
  declare const toolSchema: z.ZodObject<{
5
6
  path: z.ZodString;
6
7
  }, z.core.$strip>;
@@ -22,15 +23,23 @@ export declare function resolvePrRepoContext(prId: string | undefined): Promise<
22
23
  * Fetches the full contents of a single file from GitHub via the `gh api` CLI and decodes it.
23
24
  * Returns the decoded text, or a human/agent-readable explanation string on any failure
24
25
  * (graceful skip — never throws). owner/repo/ref come from the resolved PR context, not the LLM.
26
+ *
27
+ * CFG-52 — the decoded text is capped at `maxBytes`. Over the cap it is truncated rather than
28
+ * refused (a truncated file still reviews better than no file), and the result says so in its
29
+ * heading AND in a trailing marker naming the tool and the cap, so the model cannot silently
30
+ * reason about a file it only half received.
25
31
  */
26
- export declare function ghReadFileImpl(args: GhReadFileArgs, context: PrRepoContext): Promise<string>;
32
+ export declare function ghReadFileImpl(args: GhReadFileArgs, context: PrRepoContext, maxBytes?: number): Promise<string>;
27
33
  /**
28
34
  * Built-in tool factory matching the repo's `get(config)` tool idiom (see gthWebFetchTool).
29
35
  *
30
36
  * `prId` identifies the PR under review (undefined in `gth pr` discovery mode → current branch).
31
37
  * The owner/repo/ref are resolved from it once, lazily, and memoised for the run, so the agent
32
38
  * cannot read files from any repo other than the one being reviewed.
39
+ *
40
+ * CFG-52 — `command` selects which `builtInTools` registry the byte cap is read from, so
41
+ * `commands.pr` and `commands.review` each configure their own run.
33
42
  */
34
- export declare function get(_: GthConfig, prId?: string): StructuredToolInterface;
43
+ export declare function get(config: GthConfig, prId?: string, command?: GhReadFileCommand): StructuredToolInterface;
35
44
  export declare const GTH_GH_READ_FILE_TOOL_NAME = "gth_gh_read_file";
36
45
  export {};
@@ -1,5 +1,6 @@
1
1
  import { z } from 'zod';
2
2
  import { tool } from '@langchain/core/tools';
3
+ import { GH_READ_FILE_DEFAULT_MAX_BYTES, GH_READ_FILE_TOOL_NAME, getGhReadFileMaxBytes, } from '@gaunt-sloth/core/config.js';
3
4
  import { execAsync } from '@gaunt-sloth/core/utils/systemUtils.js';
4
5
  import { debugLog } from '@gaunt-sloth/core/utils/debugUtils.js';
5
6
  /**
@@ -21,6 +22,10 @@ import { debugLog } from '@gaunt-sloth/core/utils/debugUtils.js';
21
22
  * into the review. Binding them to the PR context also closes the footgun of reading arbitrary
22
23
  * files from any public repo the model can name.
23
24
  *
25
+ * It is configured through the unified `builtInTools` registry (CFG-52) and is **opt-out**: absent
26
+ * from the registry it is ON, `{ "gth_gh_read_file": false }` turns it off, and
27
+ * `{ "gth_gh_read_file": { "maxBytes": N } }` sets the ceiling on the decoded text it returns.
28
+ *
24
29
  * The tool is OPTIONAL and self-guarding:
25
30
  * - The path is strictly validated so nothing LLM-supplied can inject shell metacharacters
26
31
  * into the `gh api` invocation; the resolved owner/repo/ref are validated too.
@@ -28,7 +33,7 @@ import { debugLog } from '@gaunt-sloth/core/utils/debugUtils.js';
28
33
  * gracefully and returns an explanatory string instead of throwing, so the agent can simply
29
34
  * continue with the (truncated) diff it already has.
30
35
  */
31
- const TOOL_NAME = 'gth_gh_read_file';
36
+ const TOOL_NAME = GH_READ_FILE_TOOL_NAME;
32
37
  // PR ids reach us from the CLI; keep strict so nothing but a number can reach execAsync.
33
38
  const PR_ID_PATTERN = /^\d+$/;
34
39
  // owner/repo segments: GitHub login/repo naming, conservative allow-list (no shell metachars).
@@ -39,6 +44,19 @@ const REF_PATTERN = /^[A-Za-z0-9-_./]+$/;
39
44
  // Repo-relative path. Allow slashes and common filename characters, but no shell metachars,
40
45
  // no whitespace, and no parent-directory traversal.
41
46
  const PATH_PATTERN = /^[A-Za-z0-9-_./]+$/;
47
+ /**
48
+ * CFG-52 — cut `text` down to at most `maxBytes` UTF-8 bytes without leaving a split multi-byte
49
+ * character at the end. Slicing a Buffer at an arbitrary byte boundary can land mid-sequence, and
50
+ * decoding that produces a replacement character that is itself WIDER than the bytes it replaced —
51
+ * so the result is re-measured and trimmed until it genuinely fits the cap.
52
+ */
53
+ function truncateToBytes(text, maxBytes) {
54
+ let out = Buffer.from(text, 'utf8').subarray(0, maxBytes).toString('utf8');
55
+ while (out.length > 0 && Buffer.byteLength(out, 'utf8') > maxBytes) {
56
+ out = out.slice(0, -1);
57
+ }
58
+ return out;
59
+ }
42
60
  const toolSchema = z.object({
43
61
  path: z
44
62
  .string()
@@ -88,8 +106,13 @@ export async function resolvePrRepoContext(prId) {
88
106
  * Fetches the full contents of a single file from GitHub via the `gh api` CLI and decodes it.
89
107
  * Returns the decoded text, or a human/agent-readable explanation string on any failure
90
108
  * (graceful skip — never throws). owner/repo/ref come from the resolved PR context, not the LLM.
109
+ *
110
+ * CFG-52 — the decoded text is capped at `maxBytes`. Over the cap it is truncated rather than
111
+ * refused (a truncated file still reviews better than no file), and the result says so in its
112
+ * heading AND in a trailing marker naming the tool and the cap, so the model cannot silently
113
+ * reason about a file it only half received.
91
114
  */
92
- export async function ghReadFileImpl(args, context) {
115
+ export async function ghReadFileImpl(args, context, maxBytes = GH_READ_FILE_DEFAULT_MAX_BYTES) {
93
116
  const { path } = args;
94
117
  const { owner, repo, ref } = context;
95
118
  if (!PATH_PATTERN.test(path) || path.includes('..')) {
@@ -133,7 +156,12 @@ export async function ghReadFileImpl(args, context) {
133
156
  }
134
157
  const decoded = Buffer.from(parsed.content, 'base64').toString('utf8');
135
158
  const label = `${owner}/${repo}/${path}${ref ? `@${ref}` : ''}`;
136
- return `Full contents of ${label}:\n\n${decoded}`;
159
+ if (Buffer.byteLength(decoded, 'utf8') <= maxBytes) {
160
+ return `Full contents of ${label}:\n\n${decoded}`;
161
+ }
162
+ return (`Partial contents of ${label} (truncated):\n\n${truncateToBytes(decoded, maxBytes)}` +
163
+ `\n... [${TOOL_NAME}: file truncated at the ${maxBytes}-byte cap ` +
164
+ `(builtInTools.${TOOL_NAME}.maxBytes) — this file is INCOMPLETE, the rest was not returned] ...`);
137
165
  }
138
166
  catch (error) {
139
167
  // Graceful skip: gh missing/unauthenticated, file not found, or no GitHub context.
@@ -149,8 +177,12 @@ export async function ghReadFileImpl(args, context) {
149
177
  * `prId` identifies the PR under review (undefined in `gth pr` discovery mode → current branch).
150
178
  * The owner/repo/ref are resolved from it once, lazily, and memoised for the run, so the agent
151
179
  * cannot read files from any repo other than the one being reviewed.
180
+ *
181
+ * CFG-52 — `command` selects which `builtInTools` registry the byte cap is read from, so
182
+ * `commands.pr` and `commands.review` each configure their own run.
152
183
  */
153
- export function get(_, prId) {
184
+ export function get(config, prId, command = 'pr') {
185
+ const maxBytes = getGhReadFileMaxBytes(config, command);
154
186
  let contextPromise;
155
187
  const getContext = () => {
156
188
  if (!contextPromise) {
@@ -163,14 +195,16 @@ export function get(_, prId) {
163
195
  if (typeof context === 'string') {
164
196
  return context; // resolution failed — return the explanation as a graceful skip.
165
197
  }
166
- return ghReadFileImpl(args, context);
198
+ return ghReadFileImpl(args, context, maxBytes);
167
199
  }, {
168
200
  name: TOOL_NAME,
169
201
  description: 'Read the FULL contents of a single file from the pull request under review via the ' +
170
202
  'GitHub API. Use this when the PR diff is truncated and you need to see the complete ' +
171
203
  'file. Supply only the repository-relative path; the repository and ref are bound to ' +
172
204
  'the PR automatically. Reads through the GitHub API, not the local filesystem, so it is ' +
173
- 'safe in pull_request_target CI.',
205
+ `safe in pull_request_target CI. At most ${maxBytes} bytes of file text are returned: a ` +
206
+ 'larger file may come back truncated, and says so in its heading and in a trailing marker ' +
207
+ 'when it does. A very large file may not be readable through this endpoint at all.',
174
208
  schema: toolSchema,
175
209
  });
176
210
  }
@@ -1 +1 @@
1
- {"version":3,"file":"ghReadFileTool.js","sourceRoot":"","sources":["../../src/tools/ghReadFileTool.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ghReadFileTool.js","sourceRoot":"","sources":["../../src/tools/ghReadFileTool.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,IAAI,EAAE,MAAM,uBAAuB,CAAC;AAG7C,OAAO,EACL,8BAA8B,EAC9B,sBAAsB,EACtB,qBAAqB,GAEtB,MAAM,6BAA6B,CAAC;AACrC,OAAO,EAAE,SAAS,EAAE,MAAM,wCAAwC,CAAC;AACnE,OAAO,EAAE,QAAQ,EAAE,MAAM,uCAAuC,CAAC;AAEjE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAEH,MAAM,SAAS,GAAG,sBAAsB,CAAC;AAEzC,yFAAyF;AACzF,MAAM,aAAa,GAAG,OAAO,CAAC;AAC9B,+FAA+F;AAC/F,MAAM,aAAa,GAAG,mBAAmB,CAAC;AAC1C,MAAM,YAAY,GAAG,mBAAmB,CAAC;AACzC,wFAAwF;AACxF,MAAM,WAAW,GAAG,oBAAoB,CAAC;AACzC,4FAA4F;AAC5F,oDAAoD;AACpD,MAAM,YAAY,GAAG,oBAAoB,CAAC;AAE1C;;;;;GAKG;AACH,SAAS,eAAe,CAAC,IAAY,EAAE,QAAgB;IACrD,IAAI,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IAC3E,OAAO,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,MAAM,CAAC,UAAU,CAAC,GAAG,EAAE,MAAM,CAAC,GAAG,QAAQ,EAAE,CAAC;QACnE,GAAG,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IACzB,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,MAAM,UAAU,GAAG,CAAC,CAAC,MAAM,CAAC;IAC1B,IAAI,EAAE,CAAC;SACJ,MAAM,EAAE;SACR,QAAQ,CAAC,8EAA8E,CAAC;CAC5F,CAAC,CAAC;AAyBH;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,IAAwB;IAExB,IAAI,IAAI,KAAK,SAAS,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QACpD,OAAO,4BAA4B,IAAI,+BAA+B,CAAC;IACzE,CAAC;IAED,MAAM,UAAU,GAAG,IAAI,CAAC,CAAC,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IAC1C,MAAM,SAAS,GAAG,aAAa,UAAU,wDAAwD,CAAC;IAElG,IAAI,GAAW,CAAC;IAChB,IAAI,CAAC;QACH,GAAG,GAAG,MAAM,SAAS,CAAC,SAAS,CAAC,CAAC;IACnC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,MAAM,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACtE,OAAO,CACL,qEAAqE,MAAM,IAAI;YAC/E,mGAAmG;YACnG,sDAAsD,CACvD,CAAC;IACJ,CAAC;IAED,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,OAAO,oCAAoC,IAAI,CAAC,CAAC,CAAC,SAAS,IAAI,EAAE,CAAC,CAAC,CAAC,yBAAyB,GAAG,CAAC;IACnG,CAAC;IAED,IAAI,MAA4B,CAAC;IACjC,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAyB,CAAC;IACnD,CAAC;IAAC,OAAO,UAAU,EAAE,CAAC;QACpB,QAAQ,CAAC,+CAA+C,GAAG,EAAE,CAAC,CAAC;QAC/D,OAAO,uCACL,UAAU,YAAY,KAAK,CAAC,CAAC,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,UAAU,CACtE,EAAE,CAAC;IACL,CAAC;IAED,MAAM,KAAK,GAAG,MAAM,CAAC,mBAAmB,EAAE,KAAK,CAAC;IAChD,MAAM,IAAI,GAAG,MAAM,CAAC,cAAc,EAAE,IAAI,CAAC;IACzC,IAAI,CAAC,KAAK,IAAI,CAAC,IAAI,EAAE,CAAC;QACpB,OAAO,mGAAmG,CAAC;IAC7G,CAAC;IAED,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,EAAE,MAAM,CAAC,WAAW,EAAE,CAAC;AAClD,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,IAAoB,EACpB,OAAsB,EACtB,QAAQ,GAAW,8BAA8B;IAEjD,MAAM,EAAE,IAAI,EAAE,GAAG,IAAI,CAAC;IACtB,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC;IAErC,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;QACpD,OAAO,sBAAsB,IAAI,2DAA2D,CAAC;IAC/F,CAAC;IACD,0FAA0F;IAC1F,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QAC/B,OAAO,6BAA6B,KAAK,+CAA+C,CAAC;IAC3F,CAAC;IACD,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QAC7B,OAAO,4BAA4B,IAAI,IAAI,CAAC;IAC9C,CAAC;IACD,IAAI,GAAG,KAAK,SAAS,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;QAChD,OAAO,oBAAoB,GAAG,IAAI,CAAC;IACrC,CAAC;IAED,6FAA6F;IAC7F,MAAM,QAAQ,GAAG,GAAG,CAAC,CAAC,CAAC,QAAQ,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IAC1C,MAAM,SAAS,GAAG,iBAAiB,KAAK,IAAI,IAAI,aAAa,IAAI,GAAG,QAAQ,EAAE,CAAC;IAE/E,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,SAAS,CAAC,SAAS,CAAC,CAAC;QACvC,IAAI,CAAC,GAAG,EAAE,CAAC;YACT,OAAO,2BAA2B,KAAK,IAAI,IAAI,IAAI,IAAI,GAAG,GAAG,CAAC,CAAC,CAAC,IAAI,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC;QACpF,CAAC;QAED,IAAI,MAA0B,CAAC;QAC/B,IAAI,CAAC;YACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAuB,CAAC;QACjD,CAAC;QAAC,OAAO,UAAU,EAAE,CAAC;YACpB,QAAQ,CAAC,oDAAoD,GAAG,EAAE,CAAC,CAAC;YACpE,OAAO,2CAA2C,KAAK,IAAI,IAAI,IAAI,IAAI,KACrE,UAAU,YAAY,KAAK,CAAC,CAAC,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,UAAU,CACtE,EAAE,CAAC;QACL,CAAC;QAED,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;YAC1B,OAAO,IAAI,IAAI,gEAAgE,CAAC;QAClF,CAAC;QACD,IAAI,MAAM,CAAC,IAAI,IAAI,MAAM,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;YAC1C,OAAO,IAAI,IAAI,kCAAkC,MAAM,CAAC,IAAI,IAAI,CAAC;QACnE,CAAC;QACD,IAAI,MAAM,CAAC,QAAQ,KAAK,QAAQ,IAAI,OAAO,MAAM,CAAC,OAAO,KAAK,QAAQ,EAAE,CAAC;YACvE,uEAAuE;YACvE,OAAO,oDAAoD,IAAI,4DAA4D,CAAC;QAC9H,CAAC;QAED,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;QACvE,MAAM,KAAK,GAAG,GAAG,KAAK,IAAI,IAAI,IAAI,IAAI,GAAG,GAAG,CAAC,CAAC,CAAC,IAAI,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;QAChE,IAAI,MAAM,CAAC,UAAU,CAAC,OAAO,EAAE,MAAM,CAAC,IAAI,QAAQ,EAAE,CAAC;YACnD,OAAO,oBAAoB,KAAK,QAAQ,OAAO,EAAE,CAAC;QACpD,CAAC;QACD,OAAO,CACL,uBAAuB,KAAK,oBAAoB,eAAe,CAAC,OAAO,EAAE,QAAQ,CAAC,EAAE;YACpF,UAAU,SAAS,2BAA2B,QAAQ,YAAY;YAClE,iBAAiB,SAAS,sEAAsE,CACjG,CAAC;IACJ,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,mFAAmF;QACnF,MAAM,MAAM,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACtE,OAAO,CACL,mBAAmB,KAAK,IAAI,IAAI,IAAI,IAAI,yBAAyB,MAAM,IAAI;YAC3E,mGAAmG;YACnG,sDAAsD,CACvD,CAAC;IACJ,CAAC;AACH,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,GAAG,CACjB,MAAiB,EACjB,IAAa,EACb,OAAO,GAAsB,IAAI;IAEjC,MAAM,QAAQ,GAAG,qBAAqB,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACxD,IAAI,cAA2D,CAAC;IAChE,MAAM,UAAU,GAAG,GAAoC,EAAE;QACvD,IAAI,CAAC,cAAc,EAAE,CAAC;YACpB,cAAc,GAAG,oBAAoB,CAAC,IAAI,CAAC,CAAC;QAC9C,CAAC;QACD,OAAO,cAAc,CAAC;IACxB,CAAC,CAAC;IAEF,OAAO,IAAI,CACT,KAAK,EAAE,IAAoB,EAAE,EAAE;QAC7B,MAAM,OAAO,GAAG,MAAM,UAAU,EAAE,CAAC;QACnC,IAAI,OAAO,OAAO,KAAK,QAAQ,EAAE,CAAC;YAChC,OAAO,OAAO,CAAC,CAAC,iEAAiE;QACnF,CAAC;QACD,OAAO,cAAc,CAAC,IAAI,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC;IACjD,CAAC,EACD;QACE,IAAI,EAAE,SAAS;QACf,WAAW,EACT,qFAAqF;YACrF,sFAAsF;YACtF,sFAAsF;YACtF,yFAAyF;YACzF,2CAA2C,QAAQ,sCAAsC;YACzF,2FAA2F;YAC3F,mFAAmF;QACrF,MAAM,EAAE,UAAU;KACnB,CACyB,CAAC;AAC/B,CAAC;AAED,MAAM,CAAC,MAAM,0BAA0B,GAAG,SAAS,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gaunt-sloth/review",
3
- "version": "2.0.0-alpha.9",
3
+ "version": "2.0.0-beta.0",
4
4
  "description": "Review functionality for Gaunt Sloth",
5
5
  "license": "MIT",
6
6
  "author": "Andrew Kondratev",
@@ -34,18 +34,18 @@
34
34
  "#src/*.js": "./dist/*.js"
35
35
  },
36
36
  "dependencies": {
37
- "@langchain/core": "^1.2.1",
38
- "@langchain/langgraph": "^1.4.7",
39
- "langchain": "^1.5.2",
40
- "zod": "^4.0.0",
41
- "@gaunt-sloth/core": "2.0.0-alpha.9"
37
+ "@langchain/core": "^1.2.5",
38
+ "@langchain/langgraph": "^1.4.9",
39
+ "langchain": "^1.5.5",
40
+ "zod": "^4.4.3",
41
+ "@gaunt-sloth/core": "2.0.0-beta.0"
42
42
  },
43
43
  "files": [
44
44
  "./dist/*",
45
45
  "cli.js"
46
46
  ],
47
47
  "publishConfig": {
48
- "tag": "alpha"
48
+ "tag": "beta"
49
49
  },
50
50
  "scripts": {
51
51
  "build": "tsc"