@graphorin/eslint-plugin 0.5.0 → 0.6.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/CHANGELOG.md +17 -0
- package/README.md +7 -8
- package/dist/index.d.ts +15 -15
- package/dist/index.js +109 -36
- package/dist/index.js.map +1 -1
- package/package.json +4 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
# @graphorin/eslint-plugin
|
|
2
2
|
|
|
3
|
+
## 0.6.0
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#138](https://github.com/o-stepper/graphorin/pull/138) [`6293a25`](https://github.com/o-stepper/graphorin/commit/6293a2531c5c9265294da22fb365a08f2bdf445a) Thanks [@o-stepper](https://github.com/o-stepper)! - Retarget stale security rules to the current API surface (audit 2026-07-04 Wave E, cluster E1). `no-secret-in-deps` matched the pre-0.5 `Agent.toTool({ inheritSecrets })` shape that no longer exists (the rule could never fire); it now matches the real DEC-137 grant point, `withChildToolSecretsContext({ secretsAllowed: [...] })`, still requiring the `rb-24-justification` comment. `no-implicit-network-call` regains parity with `scripts/check-no-network.mjs` (EB-10): undici/got namespace calls, raw `net`/`tls`/`dgram` sockets, `new WebSocket`/`EventSource`, and static/dynamic/`require()` imports of HTTP clients are now flagged. Stale "scaffolds for the eventual public ruleset" package description replaced; the removed `no-console-in-public-api` row dropped from the README.
|
|
8
|
+
|
|
9
|
+
- [#138](https://github.com/o-stepper/graphorin/pull/138) [`6293a25`](https://github.com/o-stepper/graphorin/commit/6293a2531c5c9265294da22fb365a08f2bdf445a) Thanks [@o-stepper](https://github.com/o-stepper)! - Release-pipeline and tarball-surface fixes (audit 2026-07-04 Wave E, cluster E2). `@graphorin/memory`'s `./conflict` subpath was runtime-EMPTY on npm 0.5.0 (`export { };` - preserveModules emitted the module without an explicit tsdown entry); it now ships all 8 runtime exports. `@graphorin/server`'s internal `workspace:*` peer dependencies are ranged (`workspace:>=0.5.0 <1.0.0`) so changesets stops escalating every sibling bump into a bogus MAJOR for the whole fixed group (the 1.0.0 landmine on the release bot's branch). `@graphorin/eslint-plugin` gains the `./package.json` self-export. All 27 per-package CHANGELOGs gain the 0.5.0 section (they were frozen at 0.1.0 inside every published tarball), and the `mvp-readiness` release gate now rejects a stale-CHANGELOG or unresolvable-exports release.
|
|
10
|
+
|
|
11
|
+
## 0.5.0
|
|
12
|
+
|
|
13
|
+
First version published to the npm registry (with Sigstore build
|
|
14
|
+
provenance). The 0.2.0, 0.3.0, and 0.4.0 versions were internal lockstep
|
|
15
|
+
milestones and were never published. All `@graphorin/*` packages release
|
|
16
|
+
lockstep at the same version; the full release notes for 0.2.0-0.5.0 live
|
|
17
|
+
in the repository-level
|
|
18
|
+
[CHANGELOG](https://github.com/o-stepper/graphorin/blob/main/CHANGELOG.md).
|
|
19
|
+
|
|
3
20
|
## 0.1.0
|
|
4
21
|
|
|
5
22
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
> ESLint rules for projects that build on **Graphorin**.
|
|
4
4
|
|
|
5
|
-
- **Status:** v0.
|
|
6
|
-
- **License:** [MIT](./LICENSE)
|
|
5
|
+
- **Status:** v0.6.0 - final Phase 16 ruleset.
|
|
6
|
+
- **License:** [MIT](./LICENSE) - © 2026 Oleksiy Stepurenko.
|
|
7
7
|
- **Engines:** Node.js 22+ (ESM only).
|
|
8
8
|
- **Peer dependency:** `eslint >= 9`.
|
|
9
9
|
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
pnpm add -D eslint @graphorin/eslint-plugin
|
|
14
14
|
```
|
|
15
15
|
|
|
16
|
-
## Usage (ESLint flat config
|
|
16
|
+
## Usage (ESLint flat config - `eslint.config.js`)
|
|
17
17
|
|
|
18
18
|
```js
|
|
19
19
|
import graphorin from '@graphorin/eslint-plugin';
|
|
@@ -37,7 +37,7 @@ export default [
|
|
|
37
37
|
```
|
|
38
38
|
|
|
39
39
|
The bundled config wires every active rule at the severities documented below.
|
|
40
|
-
For ESLint 9+ flat config (`eslint.config.js`), spread `flat/recommended`
|
|
40
|
+
For ESLint 9+ flat config (`eslint.config.js`), spread `flat/recommended` - it
|
|
41
41
|
maps the `@graphorin` namespace to the plugin object for you:
|
|
42
42
|
|
|
43
43
|
```js
|
|
@@ -59,12 +59,11 @@ The legacy `.eslintrc` form is still exported as `configs.recommended`
|
|
|
59
59
|
| `tool-examples-recommended` | Active. Flags missing or empty `examples` arrays and rejects more than the documented upper bound (5). |
|
|
60
60
|
| `tool-parameter-naming` | Active. Flags ambiguous single-word parameter names (`user`, `id`, `name`, `value`, `data`, `input`, `output`, `result`, `to`, `from`, `key`, `field`) and numeric-suffix names (`arg1`, `param2`) on `inputSchema: z.object({ ... })`. Per-tool opt-out via `tags: ['experimental']` or `tags: ['legacy']`. |
|
|
61
61
|
| `no-secret-unwrap` | Active. Flags `.unwrap()` and `.reveal()` calls on `SecretValue`-shaped expressions. `.unwrap()` is reported as `'error'` regardless of comments (the method is `@deprecated`); `.reveal()` honours the `// graphorin-allow-secret-unwrap: <reason>` opt-out. |
|
|
62
|
-
| `no-secret-in-deps` | Active. Flags `
|
|
62
|
+
| `no-secret-in-deps` | Active. Flags `withChildToolSecretsContext({ secretsAllowed: [...] })` grants whose allowlist is non-empty and lacks an `// rb-24-justification: <reason>` comment (DEC-137). |
|
|
63
63
|
| `provider-middleware-order` | Active. Lint-time enforcement of the canonical `withTracing → withRetry → withRateLimit → withCostLimit → withCostTracking → withFallback → withRedaction` ordering. |
|
|
64
|
-
| `no-implicit-network-call` | Active. Flags
|
|
64
|
+
| `no-implicit-network-call` | Active. Flags network primitives in `@graphorin/*` framework code without the explicit `// graphorin-allow-network: <reason>` opt-out: `fetch(...)` / `axios.*` / `undici.*` / `got.*` / `http(s).request` / raw `net`·`tls`·`dgram` sockets / `new WebSocket` / `new EventSource` / `new XMLHttpRequest`, plus static, dynamic, and `require()` imports of HTTP clients (`node-fetch`, `undici`, `got`, `axios`, `ky`, `ws`). Kept in lockstep with `scripts/check-no-network.mjs`. |
|
|
65
65
|
| `no-third-party-workflow-aliases` | Active. Flags identifiers that mirror third-party-library workflow primitives in the `@graphorin/workflow` package's source so the framework keeps its own naming. |
|
|
66
66
|
| `no-bare-tool-exec` | Active. Flags `tool({ execute })` functions that do not reference `signal` so long-running tools always propagate the cancellation contract. |
|
|
67
|
-
| `no-console-in-public-api` | Scaffold (no-op). Will flag `console.*` calls in code that is part of a package's public surface; activates after the v0.1 public-API freeze. |
|
|
68
67
|
|
|
69
68
|
## Programmatic discovery (single source of truth)
|
|
70
69
|
|
|
@@ -95,4 +94,4 @@ for (const tool of tools) {
|
|
|
95
94
|
|
|
96
95
|
---
|
|
97
96
|
|
|
98
|
-
**Graphorin** · v0.
|
|
97
|
+
**Graphorin** · v0.6.0 · MIT License · © 2026 Oleksiy Stepurenko · <https://github.com/o-stepper/graphorin>
|
package/dist/index.d.ts
CHANGED
|
@@ -8,7 +8,7 @@ import * as eslint0 from "eslint";
|
|
|
8
8
|
* subcommand (Phase 15) so the rule logic has a single source of
|
|
9
9
|
* truth (per the working plan acceptance criteria for RB-49).
|
|
10
10
|
*
|
|
11
|
-
* The discovery is intentionally text-based
|
|
11
|
+
* The discovery is intentionally text-based - it scans a source
|
|
12
12
|
* string for `tool(` call expressions and extracts the immediate
|
|
13
13
|
* object literal that follows. The extractor handles the common
|
|
14
14
|
* formatting cases (single-line, multi-line, nested object/array
|
|
@@ -165,31 +165,31 @@ declare function gradeTool(tool: DiscoveredTool, findings: ReadonlyArray<LintFin
|
|
|
165
165
|
//#endregion
|
|
166
166
|
//#region src/index.d.ts
|
|
167
167
|
/**
|
|
168
|
-
* @graphorin/eslint-plugin
|
|
168
|
+
* @graphorin/eslint-plugin - ESLint plugin for projects that build on
|
|
169
169
|
* the Graphorin framework.
|
|
170
170
|
*
|
|
171
171
|
* Phase 16 final ruleset:
|
|
172
172
|
*
|
|
173
|
-
* - `no-secret-unwrap`
|
|
174
|
-
* - `no-secret-in-deps`
|
|
175
|
-
* - `provider-middleware-order`
|
|
176
|
-
* - `no-implicit-network-call`
|
|
177
|
-
* - `no-third-party-workflow-aliases`
|
|
178
|
-
* - `no-bare-tool-exec`
|
|
179
|
-
* - `tool-description-required`
|
|
180
|
-
* - `tool-examples-recommended`
|
|
181
|
-
* - `tool-parameter-naming`
|
|
173
|
+
* - `no-secret-unwrap` - DEC-020 / ADR-026. Active.
|
|
174
|
+
* - `no-secret-in-deps` - DEC-137. Active.
|
|
175
|
+
* - `provider-middleware-order` - DEC-145 / ADR-039. Active.
|
|
176
|
+
* - `no-implicit-network-call` - DEC-154 / ADR-041. Active.
|
|
177
|
+
* - `no-third-party-workflow-aliases` - DEC-019 / ADR-029. Active.
|
|
178
|
+
* - `no-bare-tool-exec` - principle 3 / DEC-143. Active.
|
|
179
|
+
* - `tool-description-required` - Active.
|
|
180
|
+
* - `tool-examples-recommended` - Active.
|
|
181
|
+
* - `tool-parameter-naming` - Active.
|
|
182
182
|
*
|
|
183
|
-
* (The former `no-console-in-public-api` scaffold
|
|
184
|
-
* since Phase 01
|
|
183
|
+
* (The former `no-console-in-public-api` scaffold - a permanent no-op
|
|
184
|
+
* since Phase 01 - was removed in the v0.4 hygiene pass (PS-21) rather
|
|
185
185
|
* than shipped inert.)
|
|
186
186
|
*
|
|
187
187
|
* @packageDocumentation
|
|
188
188
|
*/
|
|
189
|
-
declare const VERSION = "0.
|
|
189
|
+
declare const VERSION = "0.6.0";
|
|
190
190
|
declare const meta: {
|
|
191
191
|
readonly name: "@graphorin/eslint-plugin";
|
|
192
|
-
readonly version: "0.
|
|
192
|
+
readonly version: "0.6.0";
|
|
193
193
|
};
|
|
194
194
|
declare const rules: {
|
|
195
195
|
readonly 'no-bare-tool-exec': eslint0.Rule.RuleModule;
|
package/dist/index.js
CHANGED
|
@@ -106,20 +106,53 @@ const HTTP_VERBS = new Set([
|
|
|
106
106
|
"options",
|
|
107
107
|
"request"
|
|
108
108
|
]);
|
|
109
|
+
const CLIENT_NAMESPACE_VERBS = new Set([
|
|
110
|
+
"request",
|
|
111
|
+
"stream",
|
|
112
|
+
"fetch",
|
|
113
|
+
"get",
|
|
114
|
+
"post"
|
|
115
|
+
]);
|
|
116
|
+
const NETWORK_CONSTRUCTORS = new Set([
|
|
117
|
+
"XMLHttpRequest",
|
|
118
|
+
"WebSocket",
|
|
119
|
+
"EventSource"
|
|
120
|
+
]);
|
|
121
|
+
const HTTP_CLIENT_SPECIFIERS = new Set([
|
|
122
|
+
"node-fetch",
|
|
123
|
+
"undici",
|
|
124
|
+
"got",
|
|
125
|
+
"axios",
|
|
126
|
+
"ky",
|
|
127
|
+
"ws"
|
|
128
|
+
]);
|
|
109
129
|
const rule$7 = {
|
|
110
130
|
meta: {
|
|
111
131
|
type: "problem",
|
|
112
132
|
docs: {
|
|
113
|
-
description: "Disallow direct network primitives (`fetch`, `axios`, `http.request`, `XMLHttpRequest
|
|
133
|
+
description: "Disallow direct network primitives (`fetch`, `axios`/`undici`/`got`, `http.request`, raw `net`/`tls`/`dgram` sockets, `WebSocket`/`EventSource`/`XMLHttpRequest`, HTTP-client imports) in `@graphorin/*` framework code without an explicit opt-out comment (DEC-154).",
|
|
114
134
|
recommended: true
|
|
115
135
|
},
|
|
116
136
|
schema: [],
|
|
117
|
-
messages: {
|
|
137
|
+
messages: {
|
|
138
|
+
forbidden: "direct network call '{{callee}}' in framework code; user actions must initiate network I/O. Add `// graphorin-allow-network: <reason>` to opt out.",
|
|
139
|
+
forbiddenImport: "HTTP-client import '{{specifier}}' in framework code; user actions must initiate network I/O. Add `// graphorin-allow-network: <reason>` to opt out."
|
|
140
|
+
}
|
|
118
141
|
},
|
|
119
142
|
create(context) {
|
|
120
143
|
if (!FRAMEWORK_PATH_RE.test(context.filename.replace(/\\/g, "/"))) return {};
|
|
121
144
|
return {
|
|
122
145
|
CallExpression(node) {
|
|
146
|
+
const specifier = requiredClientSpecifier(node);
|
|
147
|
+
if (specifier !== null) {
|
|
148
|
+
if (hasAllowComment$2(context, node)) return;
|
|
149
|
+
context.report({
|
|
150
|
+
node,
|
|
151
|
+
messageId: "forbiddenImport",
|
|
152
|
+
data: { specifier }
|
|
153
|
+
});
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
123
156
|
const name = describeCallee(node);
|
|
124
157
|
if (name === null) return;
|
|
125
158
|
if (hasAllowComment$2(context, node)) return;
|
|
@@ -131,17 +164,49 @@ const rule$7 = {
|
|
|
131
164
|
},
|
|
132
165
|
NewExpression(node) {
|
|
133
166
|
if (node.callee.type !== "Identifier") return;
|
|
134
|
-
|
|
167
|
+
const callee = node.callee;
|
|
168
|
+
if (!NETWORK_CONSTRUCTORS.has(callee.name)) return;
|
|
135
169
|
if (hasAllowComment$2(context, node)) return;
|
|
136
170
|
context.report({
|
|
137
171
|
node,
|
|
138
172
|
messageId: "forbidden",
|
|
139
|
-
data: { callee:
|
|
173
|
+
data: { callee: `new ${callee.name}` }
|
|
174
|
+
});
|
|
175
|
+
},
|
|
176
|
+
ImportDeclaration(node) {
|
|
177
|
+
const source = node.source.value;
|
|
178
|
+
if (typeof source !== "string" || !HTTP_CLIENT_SPECIFIERS.has(source)) return;
|
|
179
|
+
if (nodeHasNearbyComment(context, node, ALLOW_TAG$2, 1)) return;
|
|
180
|
+
context.report({
|
|
181
|
+
node,
|
|
182
|
+
messageId: "forbiddenImport",
|
|
183
|
+
data: { specifier: source }
|
|
184
|
+
});
|
|
185
|
+
},
|
|
186
|
+
ImportExpression(node) {
|
|
187
|
+
const source = node.source;
|
|
188
|
+
if (source?.type !== "Literal" || typeof source.value !== "string") return;
|
|
189
|
+
if (!HTTP_CLIENT_SPECIFIERS.has(source.value)) return;
|
|
190
|
+
if (nodeHasNearbyComment(context, node, ALLOW_TAG$2, 1)) return;
|
|
191
|
+
context.report({
|
|
192
|
+
node,
|
|
193
|
+
messageId: "forbiddenImport",
|
|
194
|
+
data: { specifier: source.value }
|
|
140
195
|
});
|
|
141
196
|
}
|
|
142
197
|
};
|
|
143
198
|
}
|
|
144
199
|
};
|
|
200
|
+
/** `require('<http client>')` - import-shaped despite being a call. */
|
|
201
|
+
function requiredClientSpecifier(node) {
|
|
202
|
+
if (node.callee.type !== "Identifier") return null;
|
|
203
|
+
if (node.callee.name !== "require") return null;
|
|
204
|
+
const arg = node.arguments[0];
|
|
205
|
+
if (arg === void 0 || arg.type !== "Literal") return null;
|
|
206
|
+
const value = arg.value;
|
|
207
|
+
if (typeof value !== "string" || !HTTP_CLIENT_SPECIFIERS.has(value)) return null;
|
|
208
|
+
return value;
|
|
209
|
+
}
|
|
145
210
|
function describeCallee(node) {
|
|
146
211
|
if (node.callee.type === "Identifier") {
|
|
147
212
|
const name = node.callee.name;
|
|
@@ -157,6 +222,10 @@ function describeCallee(node) {
|
|
|
157
222
|
if (propName === null) return null;
|
|
158
223
|
if ((objName === "http" || objName === "https") && (propName === "request" || propName === "get")) return `${objName}.${propName}`;
|
|
159
224
|
if (objName === "axios" && HTTP_VERBS.has(propName)) return `axios.${propName}`;
|
|
225
|
+
if ((objName === "undici" || objName === "got") && CLIENT_NAMESPACE_VERBS.has(propName)) return `${objName}.${propName}`;
|
|
226
|
+
if (objName === "net" && (propName === "createConnection" || propName === "connect")) return `net.${propName}`;
|
|
227
|
+
if (objName === "tls" && propName === "connect") return "tls.connect";
|
|
228
|
+
if (objName === "dgram" && propName === "createSocket") return "dgram.createSocket";
|
|
160
229
|
return null;
|
|
161
230
|
}
|
|
162
231
|
return null;
|
|
@@ -169,47 +238,51 @@ var no_implicit_network_call_default = rule$7;
|
|
|
169
238
|
//#endregion
|
|
170
239
|
//#region src/rules/no-secret-in-deps.ts
|
|
171
240
|
const JUSTIFICATION_TAG = /\brb-24-justification\s*:/i;
|
|
241
|
+
const GRANT_CALLEE = "withChildToolSecretsContext";
|
|
172
242
|
const rule$6 = {
|
|
173
243
|
meta: {
|
|
174
244
|
type: "suggestion",
|
|
175
245
|
docs: {
|
|
176
|
-
description: "Require an `// rb-24-justification:` comment when `
|
|
246
|
+
description: "Require an `// rb-24-justification:` comment when `withChildToolSecretsContext({ secretsAllowed: [...] })` grants a non-empty allowlist to a child tool scope (DEC-137).",
|
|
177
247
|
recommended: true
|
|
178
248
|
},
|
|
179
249
|
schema: [],
|
|
180
|
-
messages: { missingJustification: "`
|
|
250
|
+
messages: { missingJustification: "`secretsAllowed` is non-empty but the call site lacks an `// rb-24-justification: <reason>` comment. Document why this child tool scope inherits parent secrets per DEC-137." }
|
|
181
251
|
},
|
|
182
252
|
create(context) {
|
|
183
253
|
return { CallExpression(node) {
|
|
184
|
-
if (!
|
|
254
|
+
if (!isGrantCall(node)) return;
|
|
185
255
|
const arg = node.arguments[0];
|
|
186
256
|
if (arg === void 0 || arg.type !== "ObjectExpression") return;
|
|
187
|
-
const
|
|
188
|
-
if (
|
|
189
|
-
if (!isNonEmptyArray(
|
|
257
|
+
const secretsAllowed = findSecretsAllowedProperty(arg);
|
|
258
|
+
if (secretsAllowed === null) return;
|
|
259
|
+
if (!isNonEmptyArray(secretsAllowed)) return;
|
|
190
260
|
if (hasJustificationComment(context, node)) return;
|
|
191
261
|
context.report({
|
|
192
|
-
node:
|
|
262
|
+
node: secretsAllowed,
|
|
193
263
|
messageId: "missingJustification"
|
|
194
264
|
});
|
|
195
265
|
} };
|
|
196
266
|
}
|
|
197
267
|
};
|
|
198
|
-
function
|
|
199
|
-
if (node.callee.type
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
268
|
+
function isGrantCall(node) {
|
|
269
|
+
if (node.callee.type === "Identifier") return node.callee.name === GRANT_CALLEE;
|
|
270
|
+
if (node.callee.type === "MemberExpression") {
|
|
271
|
+
const callee = node.callee;
|
|
272
|
+
if (callee.computed) return false;
|
|
273
|
+
if (callee.property.type !== "Identifier") return false;
|
|
274
|
+
return callee.property.name === GRANT_CALLEE;
|
|
275
|
+
}
|
|
276
|
+
return false;
|
|
277
|
+
}
|
|
278
|
+
function findSecretsAllowedProperty(obj) {
|
|
206
279
|
for (const prop of obj.properties) {
|
|
207
280
|
if (prop.type !== "Property") continue;
|
|
208
281
|
const property = prop;
|
|
209
282
|
const key = property.key;
|
|
210
283
|
if (property.computed) continue;
|
|
211
|
-
if (key.type === "Identifier" && key.name === "
|
|
212
|
-
if (key.type === "Literal" && key.value === "
|
|
284
|
+
if (key.type === "Identifier" && key.name === "secretsAllowed") return property.value;
|
|
285
|
+
if (key.type === "Literal" && key.value === "secretsAllowed") return property.value;
|
|
213
286
|
}
|
|
214
287
|
return null;
|
|
215
288
|
}
|
|
@@ -235,7 +308,7 @@ const rule$5 = {
|
|
|
235
308
|
schema: [],
|
|
236
309
|
messages: {
|
|
237
310
|
avoidReveal: "`.reveal()` returns the unwrapped secret as a V8 string. Prefer `.use(fn)` so the value is scoped to a single callback. Add `// graphorin-allow-secret-unwrap: <reason>` to opt out.",
|
|
238
|
-
avoidUnwrap: "`.unwrap()` is deprecated
|
|
311
|
+
avoidUnwrap: "`.unwrap()` is deprecated - call `.reveal()` (audited) or `.use(fn)` (scoped). The opt-out comment is intentionally NOT honoured for `.unwrap()` so the deprecation stays sharp."
|
|
239
312
|
}
|
|
240
313
|
},
|
|
241
314
|
create(context) {
|
|
@@ -886,7 +959,7 @@ const rule = {
|
|
|
886
959
|
meta: {
|
|
887
960
|
type: "suggestion",
|
|
888
961
|
docs: {
|
|
889
|
-
description: "Flag ambiguous or numeric-suffix parameter names on `tool({...})` `inputSchema` declarations (RB-49
|
|
962
|
+
description: "Flag ambiguous or numeric-suffix parameter names on `tool({...})` `inputSchema` declarations (RB-49 - write self-documenting parameter names).",
|
|
890
963
|
recommended: true
|
|
891
964
|
},
|
|
892
965
|
schema: [],
|
|
@@ -933,28 +1006,28 @@ var tool_parameter_naming_default = rule;
|
|
|
933
1006
|
//#endregion
|
|
934
1007
|
//#region src/index.ts
|
|
935
1008
|
/**
|
|
936
|
-
* @graphorin/eslint-plugin
|
|
1009
|
+
* @graphorin/eslint-plugin - ESLint plugin for projects that build on
|
|
937
1010
|
* the Graphorin framework.
|
|
938
1011
|
*
|
|
939
1012
|
* Phase 16 final ruleset:
|
|
940
1013
|
*
|
|
941
|
-
* - `no-secret-unwrap`
|
|
942
|
-
* - `no-secret-in-deps`
|
|
943
|
-
* - `provider-middleware-order`
|
|
944
|
-
* - `no-implicit-network-call`
|
|
945
|
-
* - `no-third-party-workflow-aliases`
|
|
946
|
-
* - `no-bare-tool-exec`
|
|
947
|
-
* - `tool-description-required`
|
|
948
|
-
* - `tool-examples-recommended`
|
|
949
|
-
* - `tool-parameter-naming`
|
|
1014
|
+
* - `no-secret-unwrap` - DEC-020 / ADR-026. Active.
|
|
1015
|
+
* - `no-secret-in-deps` - DEC-137. Active.
|
|
1016
|
+
* - `provider-middleware-order` - DEC-145 / ADR-039. Active.
|
|
1017
|
+
* - `no-implicit-network-call` - DEC-154 / ADR-041. Active.
|
|
1018
|
+
* - `no-third-party-workflow-aliases` - DEC-019 / ADR-029. Active.
|
|
1019
|
+
* - `no-bare-tool-exec` - principle 3 / DEC-143. Active.
|
|
1020
|
+
* - `tool-description-required` - Active.
|
|
1021
|
+
* - `tool-examples-recommended` - Active.
|
|
1022
|
+
* - `tool-parameter-naming` - Active.
|
|
950
1023
|
*
|
|
951
|
-
* (The former `no-console-in-public-api` scaffold
|
|
952
|
-
* since Phase 01
|
|
1024
|
+
* (The former `no-console-in-public-api` scaffold - a permanent no-op
|
|
1025
|
+
* since Phase 01 - was removed in the v0.4 hygiene pass (PS-21) rather
|
|
953
1026
|
* than shipped inert.)
|
|
954
1027
|
*
|
|
955
1028
|
* @packageDocumentation
|
|
956
1029
|
*/
|
|
957
|
-
const VERSION = "0.
|
|
1030
|
+
const VERSION = "0.6.0";
|
|
958
1031
|
const meta = {
|
|
959
1032
|
name: "@graphorin/eslint-plugin",
|
|
960
1033
|
version: VERSION
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["ALLOW_TAG","rule: Rule.RuleModule","hasAllowComment","rule","ALLOW_TAG","rule: Rule.RuleModule","hasAllowComment","rule","rule: Rule.RuleModule","rule","ALLOW_TAG","rule: Rule.RuleModule","hasAllowComment","rule","FORBIDDEN_NAMES: ReadonlyMap<string, string>","rule: Rule.RuleModule","rule","CANONICAL_ORDER: ReadonlyArray<string>","CANONICAL_INDEX: ReadonlyMap<string, number>","rule: Rule.RuleModule","recognised: { name: string; index: number; node: Node }[]","out: { name: string; node: Node }[]","rule","AMBIGUOUS_PARAMETER_NAMES: ReadonlyArray<string>","PARAMETER_NAMING_OPT_OUT_TAGS: ReadonlyArray<string>","PLACEHOLDER_DESCRIPTIONS: ReadonlyArray<string>","MIN_DESCRIPTION_LENGTH","MAX_EXAMPLES","out: DiscoveredTool[]","match: RegExpExecArray | null","NUMERIC_SUFFIX_PATTERN","findings: LintFinding[]","inString: '\"' | \"'\" | '`' | null","names: string[]","tags: string[]","rule: Rule.RuleModule","rule","rule: Rule.RuleModule","rule","rule: Rule.RuleModule","noBareToolExec","noImplicitNetworkCall","noSecretInDeps","noSecretUnwrap","noThirdPartyWorkflowAliases","providerMiddlewareOrder","toolDescriptionRequired","toolExamplesRecommended","toolParameterNaming","plugin: {\n readonly meta: typeof meta;\n readonly rules: typeof rules;\n readonly configs: {\n readonly recommended: {\n readonly plugins: readonly string[];\n readonly rules: typeof RECOMMENDED_RULES;\n };\n readonly 'flat/recommended': {\n plugins: Record<string, unknown>;\n readonly rules: typeof RECOMMENDED_RULES;\n };\n };\n}"],"sources":["../src/rules/_comment-utils.ts","../src/rules/no-bare-tool-exec.ts","../src/rules/no-implicit-network-call.ts","../src/rules/no-secret-in-deps.ts","../src/rules/no-secret-unwrap.ts","../src/rules/no-third-party-workflow-aliases.ts","../src/rules/provider-middleware-order.ts","../src/tool-discovery.ts","../src/rules/tool-description-required.ts","../src/rules/tool-examples-recommended.ts","../src/rules/tool-parameter-naming.ts","../src/index.ts"],"sourcesContent":["/**\n * Comment-scanning helpers shared by the rule modules. We use raw\n * `getAllComments` + line-distance heuristics instead of `getComments\n * Before / Inside / After` because ESLint's per-node comment attachment\n * leaves orphan comments at the program top-level when the node\n * itself is nested inside an expression statement.\n */\n\nimport type { Rule } from 'eslint';\nimport type { Comment, Node } from 'estree';\n\n/**\n * Returns `true` when the source contains a comment whose `value`\n * matches `tag` and which is positioned within `1` line of `node`'s\n * start, or anywhere inside `node`'s span.\n *\n * @internal\n */\nexport function nodeHasNearbyComment(\n context: Rule.RuleContext,\n node: Node,\n tagPattern: RegExp,\n maxGap: number = 1,\n): boolean {\n const startLine = node.loc?.start.line ?? 0;\n const endLine = node.loc?.end.line ?? Number.POSITIVE_INFINITY;\n const allComments = context.sourceCode.getAllComments() as Comment[];\n for (const c of allComments) {\n if (!tagPattern.test(c.value)) continue;\n const cStart = c.loc?.start.line ?? 0;\n const cEnd = c.loc?.end.line ?? 0;\n if (cEnd <= startLine && startLine - cEnd <= maxGap) return true;\n if (cStart >= startLine && cStart <= endLine) return true;\n if (cStart >= endLine && cStart - endLine <= maxGap) return true;\n }\n return false;\n}\n","/**\n * Rule: `@graphorin/no-bare-tool-exec`. Flags any `tool({...})`\n * invocation whose `execute` function does not reference\n * `ctx.signal`. The intent is principle 3 (streaming-first) +\n * DEC-143 (cancellation contract): every long-running tool MUST\n * propagate the abort signal to the underlying I/O so cancellation\n * actually frees resources.\n *\n * The check is intentionally lexical — it scans the function body for\n * the literal string `signal` (matching `ctx.signal`,\n * `request.signal`, `args.signal`, etc.). False positives are\n * acceptable because the fix is one comment line; false negatives are\n * not, because they hide cancellation bugs in production.\n *\n * Per-call opt-out: `// graphorin-allow-bare-exec: <reason>` on the\n * line above the `execute` function or anywhere inside the tool\n * builder call.\n *\n * @packageDocumentation\n */\n\nimport type { Rule } from 'eslint';\nimport type {\n ArrowFunctionExpression,\n CallExpression,\n FunctionExpression,\n Identifier,\n ObjectExpression,\n Property,\n} from 'estree';\n\nimport { nodeHasNearbyComment } from './_comment-utils.js';\n\nconst ALLOW_TAG = /graphorin-allow-bare-exec/;\nconst SIGNAL_REFERENCE_RE = /\\bsignal\\b/;\n\nconst rule: Rule.RuleModule = {\n meta: {\n type: 'suggestion',\n docs: {\n description:\n 'Require every `tool({...})` `execute` function to reference `ctx.signal` so cancellation propagates to the underlying I/O (principle 3, DEC-143).',\n recommended: true,\n },\n schema: [],\n messages: {\n missingSignal:\n \"tool '{{name}}' `execute` function does not reference `ctx.signal`; long-running tools MUST propagate the abort signal. Add `// graphorin-allow-bare-exec: <reason>` to opt out.\",\n },\n },\n create(context: Rule.RuleContext): Rule.RuleListener {\n return {\n CallExpression(node: CallExpression): void {\n if (!isToolBuilderCall(node)) return;\n const arg = node.arguments[0];\n if (arg === undefined || arg.type !== 'ObjectExpression') return;\n const exec = findExecuteProperty(arg as ObjectExpression);\n if (exec === null) return;\n if (!isFunctionLike(exec)) return;\n const fn = exec as ArrowFunctionExpression | FunctionExpression;\n if (!fn.body) return;\n const fnSource = context.sourceCode.getText(fn.body);\n if (SIGNAL_REFERENCE_RE.test(fnSource)) return;\n if (hasAllowComment(context, node)) return;\n const name = extractToolName(arg as ObjectExpression) ?? '<unknown>';\n context.report({\n node: fn,\n messageId: 'missingSignal',\n data: { name },\n });\n },\n };\n },\n};\n\nfunction isToolBuilderCall(node: CallExpression): boolean {\n if (node.callee.type !== 'Identifier') return false;\n return (node.callee as Identifier).name === 'tool';\n}\n\nfunction findExecuteProperty(obj: ObjectExpression): Property['value'] | null {\n for (const prop of obj.properties) {\n if (prop.type !== 'Property') continue;\n const property = prop as Property;\n if (property.computed) continue;\n const key = property.key;\n if (key.type === 'Identifier' && key.name === 'execute') return property.value;\n if (key.type === 'Literal' && key.value === 'execute') return property.value;\n }\n return null;\n}\n\nfunction isFunctionLike(\n value: Property['value'],\n): value is ArrowFunctionExpression | FunctionExpression {\n return value.type === 'ArrowFunctionExpression' || value.type === 'FunctionExpression';\n}\n\nfunction extractToolName(obj: ObjectExpression): string | null {\n for (const prop of obj.properties) {\n if (prop.type !== 'Property') continue;\n const property = prop as Property;\n if (property.computed) continue;\n const key = property.key;\n const isName =\n (key.type === 'Identifier' && key.name === 'name') ||\n (key.type === 'Literal' && key.value === 'name');\n if (!isName) continue;\n if (property.value.type === 'Literal' && typeof property.value.value === 'string') {\n return property.value.value;\n }\n }\n return null;\n}\n\nfunction hasAllowComment(context: Rule.RuleContext, node: CallExpression): boolean {\n return nodeHasNearbyComment(context, node, ALLOW_TAG, 1);\n}\n\nexport default rule;\n","/**\n * Rule: `@graphorin/no-implicit-network-call` (DEC-154 / ADR-041).\n * Flags any direct `fetch(...)`, `https.request(...)`, `http.request(...)`,\n * `axios(...)` / `axios.<verb>(...)`, or `XMLHttpRequest` invocation in\n * code under a `@graphorin/*` package's `src/` directory unless the\n * call site carries an opt-out comment whose text contains\n * `'graphorin-allow-network'`.\n *\n * Companion to the `pnpm run check-no-network` static analysis script.\n * The lint surface catches the pattern at author time so reviewers do\n * not need to wait for CI to flag a missed network gate.\n *\n * The rule is intentionally limited to the framework's own code paths\n * — consumer applications can call `fetch` freely. The rule activates\n * whenever the linted file path matches `/packages/<pkg>/src/`.\n *\n * @packageDocumentation\n */\n\nimport type { Rule } from 'eslint';\nimport type { CallExpression, Identifier, MemberExpression, NewExpression } from 'estree';\n\nimport { nodeHasNearbyComment } from './_comment-utils.js';\n\nconst ALLOW_TAG = /graphorin-allow-network/;\nconst FRAMEWORK_PATH_RE = /\\bpackages\\/[a-z0-9_-]+\\/src\\b/;\n\nconst NETWORK_CALLEES = new Set(['fetch', 'XMLHttpRequest']);\nconst HTTP_VERBS = new Set(['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'request']);\n\nconst rule: Rule.RuleModule = {\n meta: {\n type: 'problem',\n docs: {\n description:\n 'Disallow direct network primitives (`fetch`, `axios`, `http.request`, `XMLHttpRequest`) in `@graphorin/*` framework code without an explicit opt-out comment (DEC-154).',\n recommended: true,\n },\n schema: [],\n messages: {\n forbidden:\n \"direct network call '{{callee}}' in framework code; user actions must initiate network I/O. Add `// graphorin-allow-network: <reason>` to opt out.\",\n },\n },\n create(context: Rule.RuleContext): Rule.RuleListener {\n if (!FRAMEWORK_PATH_RE.test(context.filename.replace(/\\\\/g, '/'))) {\n return {};\n }\n return {\n CallExpression(node: CallExpression): void {\n const name = describeCallee(node);\n if (name === null) return;\n if (hasAllowComment(context, node)) return;\n context.report({\n node,\n messageId: 'forbidden',\n data: { callee: name },\n });\n },\n NewExpression(node: NewExpression): void {\n if (node.callee.type !== 'Identifier') return;\n const callee = node.callee as Identifier;\n if (callee.name !== 'XMLHttpRequest') return;\n if (hasAllowComment(context, node)) return;\n context.report({\n node,\n messageId: 'forbidden',\n data: { callee: 'new XMLHttpRequest' },\n });\n },\n };\n },\n};\n\nfunction describeCallee(node: CallExpression): string | null {\n if (node.callee.type === 'Identifier') {\n const name = (node.callee as Identifier).name;\n if (NETWORK_CALLEES.has(name) || name === 'axios') return name;\n return null;\n }\n if (node.callee.type === 'MemberExpression') {\n const me = node.callee as MemberExpression;\n if (me.computed) return null;\n const objName = me.object.type === 'Identifier' ? (me.object as Identifier).name : null;\n if (objName === null) return null;\n const propName = me.property.type === 'Identifier' ? (me.property as Identifier).name : null;\n if (propName === null) return null;\n if (\n (objName === 'http' || objName === 'https') &&\n (propName === 'request' || propName === 'get')\n )\n return `${objName}.${propName}`;\n if (objName === 'axios' && HTTP_VERBS.has(propName)) return `axios.${propName}`;\n return null;\n }\n return null;\n}\n\nfunction hasAllowComment(context: Rule.RuleContext, node: CallExpression | NewExpression): boolean {\n return nodeHasNearbyComment(context, node, ALLOW_TAG, 1);\n}\n\nexport default rule;\n","/**\n * Rule: `@graphorin/no-secret-in-deps` (RB-24 / DEC-137). Flags any\n * call shaped like `Agent.toTool({ inheritSecrets: [...] })` (or\n * `agent.toTool({...})`) where the `inheritSecrets` array is non-empty\n * and the surrounding object literal does not include a comment whose\n * text starts with `'rb-24-justification:'` (case-insensitive).\n *\n * The rule enforces the principle-of-least-authority discipline from\n * DEC-137: sub-agents inheriting secret allowlists from their parent\n * is opt-in, and the opt-in MUST be explained in code so security\n * reviewers can audit the inheritance graph without guessing.\n *\n * The rule is intentionally syntactic — it operates on the literal\n * call site without trying to resolve the value of the array. This\n * keeps the rule cheap and avoids false negatives from spread /\n * function-call expressions that hide the inheritance shape.\n *\n * @packageDocumentation\n */\n\nimport type { Rule } from 'eslint';\nimport type {\n ArrayExpression,\n CallExpression,\n MemberExpression,\n ObjectExpression,\n Property,\n} from 'estree';\n\nimport { nodeHasNearbyComment } from './_comment-utils.js';\n\nconst JUSTIFICATION_TAG = /\\brb-24-justification\\s*:/i;\n\nconst rule: Rule.RuleModule = {\n meta: {\n type: 'suggestion',\n docs: {\n description:\n 'Require an `// rb-24-justification:` comment when `Agent.toTool({ inheritSecrets: [...] })` carries a non-empty allowlist (DEC-137).',\n recommended: true,\n },\n schema: [],\n messages: {\n missingJustification:\n '`inheritSecrets` is non-empty but the call site lacks an `// rb-24-justification: <reason>` comment. Document why this sub-agent inherits parent secrets per DEC-137.',\n },\n },\n create(context: Rule.RuleContext): Rule.RuleListener {\n return {\n CallExpression(node: CallExpression): void {\n if (!isToToolCall(node)) return;\n const arg = node.arguments[0];\n if (arg === undefined || arg.type !== 'ObjectExpression') return;\n const objectArg = arg as ObjectExpression;\n const inheritSecrets = findInheritSecretsProperty(objectArg);\n if (inheritSecrets === null) return;\n if (!isNonEmptyArray(inheritSecrets)) return;\n if (hasJustificationComment(context, node)) return;\n context.report({\n node: inheritSecrets,\n messageId: 'missingJustification',\n });\n },\n };\n },\n};\n\nfunction isToToolCall(node: CallExpression): boolean {\n if (node.callee.type !== 'MemberExpression') return false;\n const callee = node.callee as MemberExpression;\n if (callee.computed) return false;\n if (callee.property.type !== 'Identifier') return false;\n return callee.property.name === 'toTool';\n}\n\nfunction findInheritSecretsProperty(obj: ObjectExpression): Property['value'] | null {\n for (const prop of obj.properties) {\n if (prop.type !== 'Property') continue;\n const property = prop as Property;\n const key = property.key;\n if (property.computed) continue;\n if (key.type === 'Identifier' && key.name === 'inheritSecrets') return property.value;\n if (key.type === 'Literal' && key.value === 'inheritSecrets') return property.value;\n }\n return null;\n}\n\nfunction isNonEmptyArray(value: Property['value']): boolean {\n if (value.type !== 'ArrayExpression') return false;\n const arr = value as ArrayExpression;\n return arr.elements.some((e) => e !== null);\n}\n\nfunction hasJustificationComment(context: Rule.RuleContext, node: CallExpression): boolean {\n return nodeHasNearbyComment(context, node, JUSTIFICATION_TAG, 1);\n}\n\nexport default rule;\n","/**\n * Rule: `@graphorin/no-secret-unwrap` (DEC-020 / ADR-026 / Phase 16).\n *\n * Flags member expressions that look like an unprotected unwrap of a\n * `SecretValue` instance — `<expr>.unwrap()` and `<expr>.reveal()` —\n * outside an allow-listed context. The framework convention is:\n *\n * - Prefer `value.use(fn)` / `value.useBuffer(fn)` for scoped reads;\n * they limit the lifetime of the derived V8 string to a single\n * callback invocation.\n * - Use `value.reveal()` only as a one-shot escape hatch with an\n * adjacent justification. The audit log records the call.\n * - Never use `value.unwrap()` — it is `@deprecated` and is an alias\n * for `.reveal()` retained only for the v0.x compatibility window.\n * The rule reports `unwrap()` as `'error'` even when an\n * opt-out comment is present so the deprecation cliff stays sharp.\n *\n * Per-call opt-out: `// graphorin-allow-secret-unwrap: <reason>` on\n * the line above the call site or anywhere inside the enclosing\n * expression-statement source span. The opt-out is honoured for\n * `reveal()` but **not** for `unwrap()` (the deprecation supersedes\n * any local justification).\n *\n * The rule is intentionally lexical — it matches `.unwrap()` /\n * `.reveal()` on any `MemberExpression` regardless of the receiver\n * type. False positives are tolerated because (a) the receiver is\n * almost always a `SecretValue` in this codebase and (b) the\n * one-line opt-out is cheap.\n *\n * @packageDocumentation\n */\n\nimport type { Rule } from 'eslint';\nimport type { CallExpression, Identifier, MemberExpression } from 'estree';\n\nimport { nodeHasNearbyComment } from './_comment-utils.js';\n\nconst ALLOW_TAG = /graphorin-allow-secret-unwrap/;\n\nconst rule: Rule.RuleModule = {\n meta: {\n type: 'problem',\n docs: {\n description:\n 'Disallow `.unwrap()` / `.reveal()` calls on `SecretValue` instances. Prefer `.use(fn)` (scoped) or attach a `// graphorin-allow-secret-unwrap: <reason>` opt-out comment for `.reveal()`.',\n recommended: true,\n },\n schema: [],\n messages: {\n avoidReveal:\n '`.reveal()` returns the unwrapped secret as a V8 string. Prefer `.use(fn)` so the value is scoped to a single callback. Add `// graphorin-allow-secret-unwrap: <reason>` to opt out.',\n avoidUnwrap:\n '`.unwrap()` is deprecated — call `.reveal()` (audited) or `.use(fn)` (scoped). The opt-out comment is intentionally NOT honoured for `.unwrap()` so the deprecation stays sharp.',\n },\n },\n create(context: Rule.RuleContext): Rule.RuleListener {\n return {\n CallExpression(node: CallExpression): void {\n const callee = node.callee;\n if (callee.type !== 'MemberExpression') return;\n const me = callee as MemberExpression;\n if (me.computed) return;\n if (me.property.type !== 'Identifier') return;\n const propName = (me.property as Identifier).name;\n if (propName === 'unwrap') {\n context.report({\n node,\n messageId: 'avoidUnwrap',\n });\n return;\n }\n if (propName === 'reveal') {\n if (hasAllowComment(context, node)) return;\n context.report({\n node,\n messageId: 'avoidReveal',\n });\n }\n },\n };\n },\n};\n\nfunction hasAllowComment(context: Rule.RuleContext, node: CallExpression): boolean {\n return nodeHasNearbyComment(context, node, ALLOW_TAG, 1);\n}\n\nexport default rule;\n","/**\n * Rule: `@graphorin/no-third-party-workflow-aliases` (DEC-019 /\n * ADR-029). Flags any identifier in `@graphorin/workflow` source whose\n * name matches a known third-party workflow primitive (e.g. the\n * library-specific names that Graphorin deliberately renamed). The\n * intent is legal hygiene: the framework's primitives are\n * `Directive`, `Dispatch`, `pause`, `LatestValue`, `Reducer`,\n * `Stream`, `Barrier`, `Ephemeral`, `AnyValue` — we never reuse\n * external library identifiers in the public API.\n *\n * The forbidden list is intentionally narrow — we only flag the\n * canonical proper-noun primitives third-party workflow engines use.\n * Rare false positives (e.g. an internal helper named `Send` for an\n * unrelated reason) are mitigated by the per-occurrence opt-out\n * comment `// graphorin-workflow-naming-allow: <reason>`.\n *\n * The rule activates only on files inside the\n * `@graphorin/workflow` package source tree (path matcher\n * `packages/workflow/src`).\n *\n * @packageDocumentation\n */\n\nimport type { Rule } from 'eslint';\nimport type { Identifier, Node } from 'estree';\n\nimport { nodeHasNearbyComment } from './_comment-utils.js';\n\nconst ALLOW_TAG = /graphorin-workflow-naming-allow/;\nconst WORKFLOW_PATH_RE = /\\bpackages\\/workflow\\/src\\b/;\n\nconst FORBIDDEN_NAMES: ReadonlyMap<string, string> = new Map([\n ['Send', 'Dispatch'],\n ['Command', 'Directive'],\n ['interrupt', 'pause'],\n ['LastValue', 'LatestValue'],\n ['BinaryOperatorAggregate', 'Reducer'],\n ['BinaryAggregate', 'Reducer'],\n ['Topic', 'Stream'],\n]);\n\nconst rule: Rule.RuleModule = {\n meta: {\n type: 'problem',\n docs: {\n description:\n 'Disallow third-party workflow primitive identifiers in `@graphorin/workflow` source. Graphorin owns its primitive names per DEC-019.',\n recommended: true,\n },\n schema: [],\n messages: {\n forbidden:\n \"identifier '{{forbidden}}' is reserved for the third-party workflow library it originates from; use '{{replacement}}' (DEC-019).\",\n },\n },\n create(context: Rule.RuleContext): Rule.RuleListener {\n if (!WORKFLOW_PATH_RE.test(context.filename.replace(/\\\\/g, '/'))) {\n return {};\n }\n return {\n Identifier(node: Identifier): void {\n const replacement = FORBIDDEN_NAMES.get(node.name);\n if (replacement === undefined) return;\n if (isInImportSpecifier(node)) return;\n if (hasAllowComment(context, node)) return;\n context.report({\n node,\n messageId: 'forbidden',\n data: {\n forbidden: node.name,\n replacement,\n },\n });\n },\n };\n },\n};\n\nfunction isInImportSpecifier(node: Identifier): boolean {\n // Skip identifiers used as keys / property names — those are\n // typically referencing external library exports the user is\n // intentionally importing into a renamed local symbol.\n const parent = (node as Identifier & { parent?: Node }).parent;\n if (parent === undefined) return false;\n switch (parent.type) {\n case 'ImportSpecifier':\n case 'ImportNamespaceSpecifier':\n case 'ImportDefaultSpecifier':\n case 'ExportSpecifier':\n return true;\n default:\n return false;\n }\n}\n\nfunction hasAllowComment(context: Rule.RuleContext, node: Identifier): boolean {\n return nodeHasNearbyComment(context, node, ALLOW_TAG, 1);\n}\n\nexport default rule;\n","/**\n * Rule: `@graphorin/provider-middleware-order` (DEC-145 / ADR-039).\n * Lint-time enforcement of the canonical middleware ordering for\n * `composeProviderMiddleware([...])`. Catches the same class of error\n * the runtime composer raises (`MiddlewareOrderingError`) without\n * requiring the runtime to actually wire the chain.\n *\n * The canonical order, outermost → innermost, mirrors the runtime\n * constant `CANONICAL_MIDDLEWARE_ORDER` exported by\n * `@graphorin/provider/middleware`. Built-ins not in the canonical\n * list are tolerated at any position.\n *\n * @packageDocumentation\n */\n\nimport type { Rule } from 'eslint';\nimport type { ArrayExpression, CallExpression, Identifier, Node } from 'estree';\n\nconst CANONICAL_ORDER: ReadonlyArray<string> = [\n 'withTracing',\n 'withRetry',\n 'withRateLimit',\n 'withCostLimit',\n 'withCostTracking',\n 'withFallback',\n 'withRedaction',\n];\n\nconst CANONICAL_INDEX: ReadonlyMap<string, number> = new Map(\n CANONICAL_ORDER.map((name, idx) => [name, idx]),\n);\n\nconst rule: Rule.RuleModule = {\n meta: {\n type: 'problem',\n docs: {\n description:\n 'Enforce the canonical provider-middleware ordering at lint time. Mirrors the runtime `MiddlewareOrderingError` from `@graphorin/provider/middleware`.',\n recommended: true,\n },\n schema: [],\n messages: {\n orderingViolation: \"'{{outer}}' must appear before '{{inner}}' (canonical order: {{order}}).\",\n },\n },\n create(context: Rule.RuleContext): Rule.RuleListener {\n return {\n CallExpression(node: CallExpression): void {\n if (!isComposeCall(node)) return;\n const arg = node.arguments[0];\n if (arg === undefined || arg.type !== 'ArrayExpression') return;\n const factories = extractFactoryNames(arg as ArrayExpression);\n const recognised: { name: string; index: number; node: Node }[] = [];\n for (const f of factories) {\n const idx = CANONICAL_INDEX.get(f.name);\n if (idx !== undefined) recognised.push({ name: f.name, index: idx, node: f.node });\n }\n for (let i = 1; i < recognised.length; i++) {\n const prev = recognised[i - 1];\n const cur = recognised[i];\n if (prev !== undefined && cur !== undefined && prev.index > cur.index) {\n context.report({\n node: cur.node,\n messageId: 'orderingViolation',\n data: {\n outer: cur.name,\n inner: prev.name,\n order: CANONICAL_ORDER.join(' → '),\n },\n });\n }\n }\n },\n };\n },\n};\n\nfunction isComposeCall(node: CallExpression): boolean {\n if (node.callee.type === 'Identifier') {\n return (node.callee as Identifier).name === 'composeProviderMiddleware';\n }\n if (node.callee.type === 'MemberExpression') {\n const prop = node.callee.property;\n if (prop.type === 'Identifier' && prop.name === 'composeProviderMiddleware') return true;\n }\n return false;\n}\n\nfunction extractFactoryNames(arr: ArrayExpression): { name: string; node: Node }[] {\n const out: { name: string; node: Node }[] = [];\n for (const elt of arr.elements) {\n if (elt === null) continue;\n // Each element is one of:\n // - `withTracing()` / `withRetry({...})` — CallExpression with Identifier callee\n // - `withTracing` (factory ref) — Identifier\n // - `provider.withTracing()` — CallExpression with MemberExpression callee\n if (elt.type === 'Identifier') {\n out.push({ name: (elt as Identifier).name, node: elt });\n continue;\n }\n if (elt.type === 'CallExpression') {\n const callee = elt.callee;\n if (callee.type === 'Identifier') {\n out.push({ name: (callee as Identifier).name, node: elt });\n } else if (callee.type === 'MemberExpression' && callee.property.type === 'Identifier') {\n out.push({ name: callee.property.name, node: elt });\n }\n }\n }\n return out;\n}\n\nexport default rule;\n","/**\n * Static `tool({...})` discovery + per-tool grader. Used by both the\n * three `tool-*` ESLint rules and by the `graphorin tools lint` CLI\n * subcommand (Phase 15) so the rule logic has a single source of\n * truth (per the working plan acceptance criteria for RB-49).\n *\n * The discovery is intentionally text-based — it scans a source\n * string for `tool(` call expressions and extracts the immediate\n * object literal that follows. The extractor handles the common\n * formatting cases (single-line, multi-line, nested object/array\n * literals, single + double-quoted strings, template literals) and\n * gracefully skips invocations whose argument shape it cannot\n * parse statically. The trade-off is documented: a project that\n * hides its `tool({...})` calls behind a builder helper or a\n * dynamic import will not be picked up by this lint surface; that\n * is the documented contract for the v0.1 lint surface.\n *\n * **Per-tool grader rubric (RB-49 calibration):**\n *\n * - **description axis (0..40 points):**\n * - 0 if missing / placeholder / shorter than 20 chars.\n * - 16 if length >= 20.\n * - 24 if length >= 30.\n * - 32 if length >= 50.\n * - 40 if length >= 80.\n * - **examples axis (0..30 points):**\n * - 0 if no examples or more than 5 (the documented upper bound).\n * - 12 base for 1 example, +6 per additional, capped at 30.\n * - -6 per PII finding (cap at 0).\n * - **parameter naming axis (0..30 points):**\n * - 30 base, deducted per finding.\n * - -30/N per ambiguous-name finding (full penalty per param).\n * - -10/N per numeric-suffix finding (partial penalty per param).\n * - 15 baseline when no parameters are discoverable.\n *\n * Total: 0..100 points. Calibrated against the RB-49 fixture\n * catalog so `wellDescribedTool` scores 82, `placeholderDescriptionTool`\n * scores 20, and `examplesPiiTool` scores 61.\n *\n * @stable\n */\n\n/**\n * @stable\n */\nexport interface DiscoveredTool {\n /** Source file the call was found in. */\n readonly file: string;\n /** 1-indexed line of the `tool(` token. */\n readonly line: number;\n /** Tool name extracted from the `name:` property when present. */\n readonly name: string;\n /** Tool description (`description:` value) when extractable. */\n readonly description?: string;\n /** Number of examples declared in the `examples:` array. */\n readonly examplesCount: number;\n /** Whether `examples:` is a non-empty array literal. */\n readonly hasExamples: boolean;\n /** Snapshot of identifiers referenced from the `inputSchema` Zod chain. */\n readonly parameterNames: ReadonlyArray<string>;\n /** Tags declared on the call (best-effort). */\n readonly tags: ReadonlyArray<string>;\n /**\n * Raw object-literal source. Useful for tests + as a context blob\n * when the CLI needs to surface the original source in a report.\n */\n readonly source: string;\n}\n\n/**\n * @stable\n */\nexport type LintFindingKind =\n | 'description-missing'\n | 'description-too-short'\n | 'description-placeholder'\n | 'examples-missing'\n | 'examples-too-many'\n | 'examples-pii-detected'\n | 'parameter-ambiguous'\n | 'parameter-numeric-suffix';\n\n/**\n * @stable\n */\nexport interface LintFinding {\n readonly rule:\n | 'graphorin/tool-description-required'\n | 'graphorin/tool-examples-recommended'\n | 'graphorin/tool-parameter-naming';\n readonly kind: LintFindingKind;\n readonly severity: 'error' | 'warn' | 'info';\n readonly message: string;\n readonly toolName: string;\n readonly file: string;\n readonly line: number;\n readonly hint?: string;\n /**\n * Optional matched-pattern context. Populated by the\n * `examples-pii-detected` finding so reports can highlight which\n * example payload triggered the rule.\n */\n readonly matchedPattern?: string;\n}\n\n/**\n * @stable\n */\nexport interface ToolGraderScore {\n readonly toolName: string;\n readonly file: string;\n readonly line: number;\n readonly score: number;\n readonly axes: {\n readonly description: number;\n readonly examples: number;\n readonly parameterNaming: number;\n };\n readonly findings: ReadonlyArray<LintFinding>;\n}\n\n/**\n * Generic identifiers the parameter-naming rule flags as ambiguous.\n * Tools whose `inputSchema` references only specific identifiers\n * (e.g. `userId`, `recipientEmail`, `apiKey`) get full credit on\n * the naming axis.\n *\n * @stable\n */\nexport const AMBIGUOUS_PARAMETER_NAMES: ReadonlyArray<string> = Object.freeze([\n 'user',\n 'id',\n 'name',\n 'value',\n 'data',\n 'input',\n 'output',\n 'result',\n 'to',\n 'from',\n 'key',\n 'field',\n]);\n\n/**\n * Tag values that, when present in a tool's `tags: [...]` literal,\n * suppress the parameter-naming rule for that tool. The opt-out\n * exists so operators can defer the rename for a long tail of\n * pre-RB-49 tools while the framework migrates without breaking\n * calling code.\n *\n * @stable\n */\nexport const PARAMETER_NAMING_OPT_OUT_TAGS: ReadonlyArray<string> = Object.freeze([\n 'experimental',\n 'legacy',\n]);\n\n/**\n * Placeholder values the description-required rule treats as\n * non-descriptions.\n *\n * @stable\n */\nexport const PLACEHOLDER_DESCRIPTIONS: ReadonlyArray<string> = Object.freeze([\n 'todo',\n 'fixme',\n 'tbd',\n 'description',\n 'placeholder',\n]);\n\nconst MIN_DESCRIPTION_LENGTH = 20;\nconst MAX_EXAMPLES = 5;\n\n/**\n * Discover every `tool({...})` invocation in a source string. The\n * returned findings are stable + frozen so callers can pass them\n * straight into a JSON report.\n *\n * @stable\n */\nexport function discoverToolCallsInSource(file: string, source: string): DiscoveredTool[] {\n const out: DiscoveredTool[] = [];\n const regex = /\\btool\\s*\\(\\s*\\{/g;\n let match: RegExpExecArray | null = regex.exec(source);\n while (match !== null) {\n const objectStart = match.index + match[0].length - 1;\n const objectEnd = matchBrace(source, objectStart);\n if (objectEnd > 0) {\n const literal = source.slice(objectStart, objectEnd + 1);\n const line = countLines(source, match.index);\n const tool = parseToolLiteral(file, line, literal);\n if (tool !== null) out.push(tool);\n }\n regex.lastIndex = objectEnd + 1;\n match = regex.exec(source);\n }\n return out;\n}\n\n/** Email PII pattern used by the examples-pii sub-check. */\nconst EMAIL_PATTERN = /[\\w.+%-]+@[\\w.-]+\\.[A-Za-z]{2,}/;\n\n/** Numeric-suffix pattern used by the parameter-naming sub-check. */\nconst NUMERIC_SUFFIX_PATTERN = /^[A-Za-z]+\\d+$/;\n\n/**\n * Run the three RB-49 rules against a discovered tool and return the\n * findings. The CLI grader maps these findings into per-axis scores;\n * the ESLint rules forward them to `context.report(...)`.\n *\n * @stable\n */\nexport function runToolRules(\n tool: DiscoveredTool,\n severityOverrides?: {\n readonly toolDescription?: 'error' | 'warn' | 'off';\n readonly toolExamples?: 'error' | 'warn' | 'off';\n readonly toolParameterNaming?: 'error' | 'warn' | 'off';\n },\n): LintFinding[] {\n const findings: LintFinding[] = [];\n\n const descSeverity = severityOverrides?.toolDescription ?? 'error';\n if (descSeverity !== 'off') {\n const desc = tool.description?.trim() ?? '';\n if (desc.length === 0) {\n findings.push(\n finding(\n 'graphorin/tool-description-required',\n 'description-missing',\n descSeverity,\n tool,\n `tool '${tool.name}' has no description; add a description that explains what the tool does and when to use it.`,\n ),\n );\n } else if (PLACEHOLDER_DESCRIPTIONS.includes(desc.toLowerCase())) {\n findings.push(\n finding(\n 'graphorin/tool-description-required',\n 'description-placeholder',\n descSeverity,\n tool,\n `tool '${tool.name}' description is a placeholder ('${tool.description}').`,\n ),\n );\n } else if (desc.length < MIN_DESCRIPTION_LENGTH) {\n findings.push(\n finding(\n 'graphorin/tool-description-required',\n 'description-too-short',\n descSeverity,\n tool,\n `tool '${tool.name}' description is shorter than ${MIN_DESCRIPTION_LENGTH} characters.`,\n ),\n );\n }\n }\n\n const examplesSeverity = severityOverrides?.toolExamples ?? 'warn';\n if (examplesSeverity !== 'off') {\n if (!tool.hasExamples || tool.examplesCount === 0) {\n findings.push(\n finding(\n 'graphorin/tool-examples-recommended',\n 'examples-missing',\n examplesSeverity,\n tool,\n `tool '${tool.name}' has no examples; add 1-5 worked examples per Anthropic 2026 guidance.`,\n ),\n );\n } else if (tool.examplesCount > MAX_EXAMPLES) {\n findings.push(\n finding(\n 'graphorin/tool-examples-recommended',\n 'examples-too-many',\n 'error',\n tool,\n `tool '${tool.name}' declares ${tool.examplesCount} examples; the upper bound is ${MAX_EXAMPLES}.`,\n ),\n );\n }\n // PII sub-check — fires once per matched email pattern in the\n // examples block. Operators usually want synthetic data, not real\n // addresses scraped from the corpus.\n const emailMatch = EMAIL_PATTERN.exec(extractExamplesBlock(tool.source));\n if (emailMatch !== null) {\n const matched = emailMatch[0] as string;\n findings.push(\n finding(\n 'graphorin/tool-examples-recommended',\n 'examples-pii-detected',\n 'error',\n tool,\n `tool '${tool.name}' example contains a real-looking email '${matched}'; replace with synthetic data (e.g. 'user@example.com').`,\n {\n matchedPattern: matched,\n hint: 'RB-49: examples must use synthetic test data so the rendered tool catalogue does not leak PII into provider context.',\n },\n ),\n );\n }\n }\n\n const namingSeverity = severityOverrides?.toolParameterNaming ?? 'warn';\n const namingOptedOut = tool.tags.some((t) => PARAMETER_NAMING_OPT_OUT_TAGS.includes(t));\n if (namingSeverity !== 'off' && !namingOptedOut) {\n for (const param of tool.parameterNames) {\n if (AMBIGUOUS_PARAMETER_NAMES.includes(param)) {\n findings.push(\n finding(\n 'graphorin/tool-parameter-naming',\n 'parameter-ambiguous',\n namingSeverity,\n tool,\n `tool '${tool.name}' uses ambiguous parameter name '${param}'; prefer a self-documenting name (e.g. '${param}Id', '${param}Email').`,\n ),\n );\n } else if (NUMERIC_SUFFIX_PATTERN.test(param)) {\n findings.push(\n finding(\n 'graphorin/tool-parameter-naming',\n 'parameter-numeric-suffix',\n namingSeverity,\n tool,\n `tool '${tool.name}' uses numeric-suffix parameter name '${param}'; prefer a semantic name (e.g. 'queryText', 'userId').`,\n ),\n );\n }\n }\n }\n\n return findings;\n}\n\n/**\n * Compute the per-tool grader score (0..100). Each axis is gated by\n * the findings produced for that axis. The rubric is calibrated\n * against the RB-49 fixture catalog (`wellDescribedTool` -> 82,\n * `placeholderDescriptionTool` -> 20, `examplesPiiTool` -> 61).\n *\n * @stable\n */\nexport function gradeTool(\n tool: DiscoveredTool,\n findings: ReadonlyArray<LintFinding>,\n): ToolGraderScore {\n const descFindings = findings.filter((f) => f.rule === 'graphorin/tool-description-required');\n const exampleFindings = findings.filter((f) => f.rule === 'graphorin/tool-examples-recommended');\n const namingFindings = findings.filter((f) => f.rule === 'graphorin/tool-parameter-naming');\n\n const description = scoreDescription(tool, descFindings);\n const examples = scoreExamples(tool, exampleFindings);\n const parameterNaming = scoreParameterNaming(tool, namingFindings);\n\n return Object.freeze({\n toolName: tool.name,\n file: tool.file,\n line: tool.line,\n score: description + examples + parameterNaming,\n axes: Object.freeze({ description, examples, parameterNaming }),\n findings,\n });\n}\n\nfunction scoreDescription(tool: DiscoveredTool, findings: ReadonlyArray<LintFinding>): number {\n if (findings.length > 0) return 0;\n const desc = tool.description?.trim() ?? '';\n if (desc.length >= 80) return 40;\n if (desc.length >= 50) return 32;\n if (desc.length >= 30) return 24;\n if (desc.length >= MIN_DESCRIPTION_LENGTH) return 16;\n return 0;\n}\n\nfunction scoreExamples(tool: DiscoveredTool, findings: ReadonlyArray<LintFinding>): number {\n if (tool.examplesCount === 0) return 0;\n if (tool.examplesCount > MAX_EXAMPLES) return 0;\n // 12 base for the first example + 6 per additional, cap at 30. This\n // calibration produces wellDescribedTool == 12 (1 example) and\n // placeholderDescriptionTool == 0 (no examples). Each PII finding\n // subtracts 6 from the axis (cap at 0) so examplesPiiTool ends up\n // with 6 (12 base - 6 PII penalty).\n const base = Math.min(30, 12 + (tool.examplesCount - 1) * 6);\n const piiCount = findings.filter((f) => f.kind === 'examples-pii-detected').length;\n return Math.max(0, base - piiCount * 6);\n}\n\nfunction scoreParameterNaming(tool: DiscoveredTool, findings: ReadonlyArray<LintFinding>): number {\n if (tool.parameterNames.length === 0) {\n // No discoverable params — neither a positive nor a negative\n // signal. Award the median.\n return 15;\n }\n const total = tool.parameterNames.length;\n const ambiguousCount = findings.filter((f) => f.kind === 'parameter-ambiguous').length;\n const numericCount = findings.filter((f) => f.kind === 'parameter-numeric-suffix').length;\n // Full penalty for an ambiguous-name finding (`user`, `id`, `to`, …):\n // -30/N. Partial penalty for a numeric-suffix finding (`arg1`,\n // `param2`, …): -10/N. Calibrated so a single-param `arg1` scores\n // 20 (placeholderDescriptionTool) and a two-param `to`-+-`body`\n // scores 15 (examplesPiiTool).\n const score = 30 - (30 / total) * ambiguousCount - (10 / total) * numericCount;\n return Math.max(0, Math.round(score));\n}\n\nfunction finding(\n rule: LintFinding['rule'],\n kind: LintFindingKind,\n severity: 'error' | 'warn',\n tool: DiscoveredTool,\n message: string,\n extra: { readonly hint?: string; readonly matchedPattern?: string } = {},\n): LintFinding {\n return Object.freeze({\n rule,\n kind,\n severity,\n message,\n toolName: tool.name,\n file: tool.file,\n line: tool.line,\n ...(extra.hint !== undefined ? { hint: extra.hint } : {}),\n ...(extra.matchedPattern !== undefined ? { matchedPattern: extra.matchedPattern } : {}),\n });\n}\n\n/**\n * Extract the substring inside the `examples: [...]` array literal so\n * the PII detector can scan only the example payloads (not the rest of\n * the tool body).\n *\n * @internal\n */\nfunction extractExamplesBlock(literal: string): string {\n const m = /examples\\s*:\\s*\\[/.exec(literal);\n if (m === null) return '';\n const start = m.index + m[0].length - 1;\n const end = matchBracket(literal, start);\n if (end < 0) return '';\n return literal.slice(start + 1, end);\n}\n\n/**\n * Parse a string literal value from the immediate object literal.\n * Supports `'`, `\"`, and template strings (without interpolation).\n *\n * @internal\n */\nfunction readStringProp(literal: string, prop: string): string | undefined {\n const escapedProp = prop.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n const re = new RegExp(`(?:^|[\\\\s,{])${escapedProp}\\\\s*:\\\\s*(['\"\\`])`, 'm');\n const m = re.exec(literal);\n if (m === null) return undefined;\n const quote = m[1] as string;\n const start = m.index + m[0].length;\n let i = start;\n let out = '';\n while (i < literal.length) {\n const ch = literal[i] as string;\n if (ch === '\\\\' && i + 1 < literal.length) {\n out += literal[i + 1];\n i += 2;\n continue;\n }\n if (ch === quote) return out;\n out += ch;\n i += 1;\n }\n return undefined;\n}\n\n/**\n * Count entries in an array literal value of the form `prop: [...]`.\n *\n * @internal\n */\nfunction countArrayProp(\n literal: string,\n prop: string,\n): { readonly count: number; readonly present: boolean } {\n const escapedProp = prop.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n const re = new RegExp(`(?:^|[\\\\s,{])${escapedProp}\\\\s*:\\\\s*\\\\[`, 'm');\n const m = re.exec(literal);\n if (m === null) return { count: 0, present: false };\n const start = m.index + m[0].length - 1;\n const end = matchBracket(literal, start);\n if (end < 0) return { count: 0, present: true };\n // Strip a trailing comma so it does not inflate the count.\n const inner = literal\n .slice(start + 1, end)\n .trim()\n .replace(/,\\s*$/, '');\n if (inner.length === 0) return { count: 0, present: true };\n // Count top-level commas (depth + string aware).\n let depth = 0;\n let inString: '\"' | \"'\" | '`' | null = null;\n let count = 1;\n for (let i = 0; i < inner.length; i += 1) {\n const ch = inner[i] as string;\n if (inString !== null) {\n if (ch === '\\\\') {\n i += 1;\n continue;\n }\n if (ch === inString) inString = null;\n continue;\n }\n if (ch === '\"' || ch === \"'\" || ch === '`') {\n inString = ch as '\"' | \"'\" | '`';\n continue;\n }\n if (ch === '(' || ch === '[' || ch === '{') depth += 1;\n else if (ch === ')' || ch === ']' || ch === '}') depth -= 1;\n else if (ch === ',' && depth === 0) count += 1;\n }\n return { count, present: true };\n}\n\n/**\n * Extract identifiers passed into a `z.object({...})` schema as\n * top-level keys.\n *\n * @internal\n */\nfunction extractParameterNames(literal: string): string[] {\n const m = /inputSchema\\s*:\\s*z\\s*\\.\\s*object\\s*\\(\\s*\\{/.exec(literal);\n if (m === null) return [];\n const start = m.index + m[0].length - 1;\n const end = matchBrace(literal, start);\n if (end < 0) return [];\n const inner = literal.slice(start + 1, end);\n const names: string[] = [];\n const idRegex = /(^|[\\s,{])([A-Za-z_$][A-Za-z0-9_$]*)\\s*:/g;\n let id = idRegex.exec(inner);\n while (id !== null) {\n const name = id[2] as string;\n if (!names.includes(name)) names.push(name);\n id = idRegex.exec(inner);\n }\n return names;\n}\n\nfunction parseToolLiteral(file: string, line: number, literal: string): DiscoveredTool | null {\n const name = readStringProp(literal, 'name') ?? '<anonymous>';\n const description = readStringProp(literal, 'description');\n const examples = countArrayProp(literal, 'examples');\n const tagsArr = countArrayProp(literal, 'tags');\n const tags: string[] = [];\n if (tagsArr.present && tagsArr.count > 0) {\n const m = /tags\\s*:\\s*\\[([^\\]]*)\\]/.exec(literal);\n if (m !== null) {\n const inner = (m[1] as string) ?? '';\n const tagRegex = /['\"`]([^'\"`]+)['\"`]/g;\n let t = tagRegex.exec(inner);\n while (t !== null) {\n tags.push(t[1] as string);\n t = tagRegex.exec(inner);\n }\n }\n }\n const parameterNames = extractParameterNames(literal);\n return Object.freeze({\n file,\n line,\n name,\n ...(description !== undefined ? { description } : {}),\n examplesCount: examples.count,\n hasExamples: examples.present,\n parameterNames: Object.freeze(parameterNames),\n tags: Object.freeze(tags),\n source: literal,\n });\n}\n\nfunction countLines(source: string, index: number): number {\n let n = 1;\n for (let i = 0; i < index; i += 1) {\n if (source[i] === '\\n') n += 1;\n }\n return n;\n}\n\nfunction matchBrace(source: string, openIndex: number): number {\n return matchPair(source, openIndex, '{', '}');\n}\n\nfunction matchBracket(source: string, openIndex: number): number {\n return matchPair(source, openIndex, '[', ']');\n}\n\nfunction matchPair(source: string, openIndex: number, open: string, close: string): number {\n let depth = 0;\n let inString: '\"' | \"'\" | '`' | null = null;\n for (let i = openIndex; i < source.length; i += 1) {\n const ch = source[i] as string;\n if (inString !== null) {\n if (ch === '\\\\') {\n i += 1;\n continue;\n }\n if (ch === inString) inString = null;\n continue;\n }\n if (ch === '\"' || ch === \"'\" || ch === '`') {\n inString = ch as '\"' | \"'\" | '`';\n continue;\n }\n if (ch === open) depth += 1;\n else if (ch === close) {\n depth -= 1;\n if (depth === 0) return i;\n }\n }\n return -1;\n}\n","/**\n * Rule: `@graphorin/tool-description-required` (RB-49 / suggested\n * DEC-165). Flags `tool({...})` invocations whose `description`\n * field is missing, too short (< 20 characters), or a placeholder\n * value (`'TODO'` / `'FIXME'` / `'tbd'` / `'description'` /\n * `'placeholder'`, case-insensitive).\n *\n * The rule shares its discovery + scoring code with the\n * `graphorin tools lint` CLI subcommand (Phase 15) so the rule\n * logic has a single source of truth.\n *\n * @packageDocumentation\n */\n\nimport type { Rule } from 'eslint';\n\nimport { discoverToolCallsInSource, PLACEHOLDER_DESCRIPTIONS } from '../tool-discovery.js';\n\nconst MIN_DESCRIPTION_LENGTH = 20;\n\nconst rule: Rule.RuleModule = {\n meta: {\n type: 'problem',\n docs: {\n description:\n 'Require a meaningful `description` on every `tool({...})` registration. RB-49 (Anthropic 2026 advanced tool use guidance).',\n recommended: true,\n },\n schema: [],\n messages: {\n missing:\n \"tool '{{name}}' has no description; add a description that explains what the tool does and when to use it.\",\n tooShort:\n \"tool '{{name}}' description is shorter than {{min}} characters (currently {{len}}).\",\n placeholder: \"tool '{{name}}' description is a placeholder ('{{value}}').\",\n },\n },\n create(context: Rule.RuleContext): Rule.RuleListener {\n return {\n 'Program:exit'(): void {\n const filename = context.filename;\n const source = context.sourceCode.text;\n const tools = discoverToolCallsInSource(filename, source);\n for (const tool of tools) {\n const desc = tool.description?.trim();\n if (desc === undefined || desc.length === 0) {\n context.report({\n loc: { line: tool.line, column: 0 },\n messageId: 'missing',\n data: { name: tool.name },\n });\n } else if (desc.length < MIN_DESCRIPTION_LENGTH) {\n context.report({\n loc: { line: tool.line, column: 0 },\n messageId: 'tooShort',\n data: {\n name: tool.name,\n min: String(MIN_DESCRIPTION_LENGTH),\n len: String(desc.length),\n },\n });\n } else if (PLACEHOLDER_DESCRIPTIONS.includes(desc.toLowerCase())) {\n context.report({\n loc: { line: tool.line, column: 0 },\n messageId: 'placeholder',\n data: { name: tool.name, value: desc },\n });\n }\n }\n },\n };\n },\n};\n\nexport default rule;\n","/**\n * Rule: `@graphorin/tool-examples-recommended` (RB-49). Flags\n * `tool({...})` invocations whose `examples` field is missing,\n * empty, or longer than the documented upper bound (5).\n *\n * @packageDocumentation\n */\n\nimport type { Rule } from 'eslint';\n\nimport { discoverToolCallsInSource } from '../tool-discovery.js';\n\nconst MAX_EXAMPLES = 5;\n\nconst rule: Rule.RuleModule = {\n meta: {\n type: 'suggestion',\n docs: {\n description:\n \"Recommend 1-5 `examples` entries on every `tool({...})` registration (Anthropic 2026 'Writing effective tools' guidance).\",\n recommended: true,\n },\n schema: [],\n messages: {\n missing:\n \"tool '{{name}}' has no examples; add 1-5 worked examples per Anthropic 2026 guidance.\",\n tooMany:\n \"tool '{{name}}' declares {{count}} examples; the documented upper bound is {{max}}.\",\n },\n },\n create(context: Rule.RuleContext): Rule.RuleListener {\n return {\n 'Program:exit'(): void {\n const filename = context.filename;\n const source = context.sourceCode.text;\n const tools = discoverToolCallsInSource(filename, source);\n for (const tool of tools) {\n if (!tool.hasExamples || tool.examplesCount === 0) {\n context.report({\n loc: { line: tool.line, column: 0 },\n messageId: 'missing',\n data: { name: tool.name },\n });\n } else if (tool.examplesCount > MAX_EXAMPLES) {\n context.report({\n loc: { line: tool.line, column: 0 },\n messageId: 'tooMany',\n data: {\n name: tool.name,\n count: String(tool.examplesCount),\n max: String(MAX_EXAMPLES),\n },\n });\n }\n }\n },\n };\n },\n};\n\nexport default rule;\n","/**\n * Rule: `@graphorin/tool-parameter-naming` (RB-49). Inspects the\n * `inputSchema: z.object({...})` declaration of every `tool({...})`\n * invocation and flags two anti-patterns:\n *\n * - **Ambiguous single-word identifiers** (`user`, `id`, `name`,\n * `value`, `data`, `input`, `output`, `result`, `to`, `from`,\n * `key`, `field`). Suggest a self-documenting alternative.\n * - **Numeric-suffix identifiers** (`arg1`, `arg2`, `param3`).\n * Suggest a semantic name.\n *\n * Per-tool opt-out: when the tool declares `tags: ['experimental']`\n * or `tags: ['legacy']` the rule is suppressed for that registration.\n * This lets operators defer the rename for a long tail of pre-RB-49\n * tools while the framework migrates without breaking calling code.\n *\n * @packageDocumentation\n */\n\nimport type { Rule } from 'eslint';\n\nimport {\n AMBIGUOUS_PARAMETER_NAMES,\n discoverToolCallsInSource,\n PARAMETER_NAMING_OPT_OUT_TAGS,\n} from '../tool-discovery.js';\n\nconst NUMERIC_SUFFIX_PATTERN = /^[A-Za-z]+\\d+$/;\n\nconst rule: Rule.RuleModule = {\n meta: {\n type: 'suggestion',\n docs: {\n description:\n 'Flag ambiguous or numeric-suffix parameter names on `tool({...})` `inputSchema` declarations (RB-49 — write self-documenting parameter names).',\n recommended: true,\n },\n schema: [],\n messages: {\n ambiguous:\n \"tool '{{name}}' uses ambiguous parameter name '{{param}}'; prefer a self-documenting name (e.g. '{{param}}Id', '{{param}}Email').\",\n numericSuffix:\n \"tool '{{name}}' uses numeric-suffix parameter name '{{param}}'; prefer a semantic name (e.g. 'queryText', 'userId').\",\n },\n },\n create(context: Rule.RuleContext): Rule.RuleListener {\n return {\n 'Program:exit'(): void {\n const filename = context.filename;\n const source = context.sourceCode.text;\n const tools = discoverToolCallsInSource(filename, source);\n for (const tool of tools) {\n if (tool.tags.some((t) => PARAMETER_NAMING_OPT_OUT_TAGS.includes(t))) continue;\n for (const param of tool.parameterNames) {\n if (AMBIGUOUS_PARAMETER_NAMES.includes(param)) {\n context.report({\n loc: { line: tool.line, column: 0 },\n messageId: 'ambiguous',\n data: { name: tool.name, param },\n });\n } else if (NUMERIC_SUFFIX_PATTERN.test(param)) {\n context.report({\n loc: { line: tool.line, column: 0 },\n messageId: 'numericSuffix',\n data: { name: tool.name, param },\n });\n }\n }\n }\n },\n };\n },\n};\n\nexport default rule;\n","/**\n * @graphorin/eslint-plugin — ESLint plugin for projects that build on\n * the Graphorin framework.\n *\n * Phase 16 final ruleset:\n *\n * - `no-secret-unwrap` — DEC-020 / ADR-026. Active.\n * - `no-secret-in-deps` — DEC-137. Active.\n * - `provider-middleware-order` — DEC-145 / ADR-039. Active.\n * - `no-implicit-network-call` — DEC-154 / ADR-041. Active.\n * - `no-third-party-workflow-aliases` — DEC-019 / ADR-029. Active.\n * - `no-bare-tool-exec` — principle 3 / DEC-143. Active.\n * - `tool-description-required` — Active.\n * - `tool-examples-recommended` — Active.\n * - `tool-parameter-naming` — Active.\n *\n * (The former `no-console-in-public-api` scaffold — a permanent no-op\n * since Phase 01 — was removed in the v0.4 hygiene pass (PS-21) rather\n * than shipped inert.)\n *\n * @packageDocumentation\n */\n\nimport noBareToolExec from './rules/no-bare-tool-exec.js';\nimport noImplicitNetworkCall from './rules/no-implicit-network-call.js';\nimport noSecretInDeps from './rules/no-secret-in-deps.js';\nimport noSecretUnwrap from './rules/no-secret-unwrap.js';\nimport noThirdPartyWorkflowAliases from './rules/no-third-party-workflow-aliases.js';\nimport providerMiddlewareOrder from './rules/provider-middleware-order.js';\nimport toolDescriptionRequired from './rules/tool-description-required.js';\nimport toolExamplesRecommended from './rules/tool-examples-recommended.js';\nimport toolParameterNaming from './rules/tool-parameter-naming.js';\n\nexport const VERSION = '0.5.0';\n\nexport const meta = {\n name: '@graphorin/eslint-plugin',\n version: VERSION,\n} as const;\n\nexport const rules = {\n 'no-bare-tool-exec': noBareToolExec,\n 'no-implicit-network-call': noImplicitNetworkCall,\n 'no-secret-in-deps': noSecretInDeps,\n 'no-secret-unwrap': noSecretUnwrap,\n 'no-third-party-workflow-aliases': noThirdPartyWorkflowAliases,\n 'provider-middleware-order': providerMiddlewareOrder,\n 'tool-description-required': toolDescriptionRequired,\n 'tool-examples-recommended': toolExamplesRecommended,\n 'tool-parameter-naming': toolParameterNaming,\n} as const;\n\n/** Shared severity map for both the legacy and flat recommended presets. */\nconst RECOMMENDED_RULES = {\n '@graphorin/no-bare-tool-exec': 'warn',\n '@graphorin/no-implicit-network-call': 'error',\n '@graphorin/no-secret-in-deps': 'error',\n '@graphorin/no-secret-unwrap': 'error',\n '@graphorin/no-third-party-workflow-aliases': 'error',\n '@graphorin/provider-middleware-order': 'error',\n '@graphorin/tool-description-required': 'error',\n '@graphorin/tool-examples-recommended': 'warn',\n '@graphorin/tool-parameter-naming': 'warn',\n} as const;\n\n/**\n * PS-17: ship BOTH config shapes. `recommended` is the legacy `.eslintrc` form\n * (`plugins: ['@graphorin']`); `flat/recommended` is the ESLint 9+ flat-config\n * form that maps the namespace to the plugin object, so flat-config consumers\n * can `...plugin.configs['flat/recommended']` instead of hand-wiring ten rules.\n */\nconst plugin: {\n readonly meta: typeof meta;\n readonly rules: typeof rules;\n readonly configs: {\n readonly recommended: {\n readonly plugins: readonly string[];\n readonly rules: typeof RECOMMENDED_RULES;\n };\n readonly 'flat/recommended': {\n plugins: Record<string, unknown>;\n readonly rules: typeof RECOMMENDED_RULES;\n };\n };\n} = {\n meta,\n rules,\n configs: {\n recommended: { plugins: ['@graphorin'], rules: RECOMMENDED_RULES },\n // `plugins` is filled in below — the flat form maps the namespace to the\n // plugin object itself, which must exist first.\n 'flat/recommended': { plugins: {}, rules: RECOMMENDED_RULES },\n },\n};\nplugin.configs['flat/recommended'].plugins = { '@graphorin': plugin };\n\nexport const configs = plugin.configs;\n\nexport {\n AMBIGUOUS_PARAMETER_NAMES,\n type DiscoveredTool,\n discoverToolCallsInSource,\n gradeTool,\n type LintFinding,\n type LintFindingKind,\n PARAMETER_NAMING_OPT_OUT_TAGS,\n PLACEHOLDER_DESCRIPTIONS,\n runToolRules,\n type ToolGraderScore,\n} from './tool-discovery.js';\n\nexport default plugin;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":["ALLOW_TAG","rule: Rule.RuleModule","hasAllowComment","rule","ALLOW_TAG","rule: Rule.RuleModule","hasAllowComment","rule","rule: Rule.RuleModule","rule","ALLOW_TAG","rule: Rule.RuleModule","hasAllowComment","rule","FORBIDDEN_NAMES: ReadonlyMap<string, string>","rule: Rule.RuleModule","rule","CANONICAL_ORDER: ReadonlyArray<string>","CANONICAL_INDEX: ReadonlyMap<string, number>","rule: Rule.RuleModule","recognised: { name: string; index: number; node: Node }[]","out: { name: string; node: Node }[]","rule","AMBIGUOUS_PARAMETER_NAMES: ReadonlyArray<string>","PARAMETER_NAMING_OPT_OUT_TAGS: ReadonlyArray<string>","PLACEHOLDER_DESCRIPTIONS: ReadonlyArray<string>","MIN_DESCRIPTION_LENGTH","MAX_EXAMPLES","out: DiscoveredTool[]","match: RegExpExecArray | null","NUMERIC_SUFFIX_PATTERN","findings: LintFinding[]","inString: '\"' | \"'\" | '`' | null","names: string[]","tags: string[]","rule: Rule.RuleModule","rule","rule: Rule.RuleModule","rule","rule: Rule.RuleModule","noBareToolExec","noImplicitNetworkCall","noSecretInDeps","noSecretUnwrap","noThirdPartyWorkflowAliases","providerMiddlewareOrder","toolDescriptionRequired","toolExamplesRecommended","toolParameterNaming","plugin: {\n readonly meta: typeof meta;\n readonly rules: typeof rules;\n readonly configs: {\n readonly recommended: {\n readonly plugins: readonly string[];\n readonly rules: typeof RECOMMENDED_RULES;\n };\n readonly 'flat/recommended': {\n plugins: Record<string, unknown>;\n readonly rules: typeof RECOMMENDED_RULES;\n };\n };\n}"],"sources":["../src/rules/_comment-utils.ts","../src/rules/no-bare-tool-exec.ts","../src/rules/no-implicit-network-call.ts","../src/rules/no-secret-in-deps.ts","../src/rules/no-secret-unwrap.ts","../src/rules/no-third-party-workflow-aliases.ts","../src/rules/provider-middleware-order.ts","../src/tool-discovery.ts","../src/rules/tool-description-required.ts","../src/rules/tool-examples-recommended.ts","../src/rules/tool-parameter-naming.ts","../src/index.ts"],"sourcesContent":["/**\n * Comment-scanning helpers shared by the rule modules. We use raw\n * `getAllComments` + line-distance heuristics instead of `getComments\n * Before / Inside / After` because ESLint's per-node comment attachment\n * leaves orphan comments at the program top-level when the node\n * itself is nested inside an expression statement.\n */\n\nimport type { Rule } from 'eslint';\nimport type { Comment, Node } from 'estree';\n\n/**\n * Returns `true` when the source contains a comment whose `value`\n * matches `tag` and which is positioned within `1` line of `node`'s\n * start, or anywhere inside `node`'s span.\n *\n * @internal\n */\nexport function nodeHasNearbyComment(\n context: Rule.RuleContext,\n node: Node,\n tagPattern: RegExp,\n maxGap: number = 1,\n): boolean {\n const startLine = node.loc?.start.line ?? 0;\n const endLine = node.loc?.end.line ?? Number.POSITIVE_INFINITY;\n const allComments = context.sourceCode.getAllComments() as Comment[];\n for (const c of allComments) {\n if (!tagPattern.test(c.value)) continue;\n const cStart = c.loc?.start.line ?? 0;\n const cEnd = c.loc?.end.line ?? 0;\n if (cEnd <= startLine && startLine - cEnd <= maxGap) return true;\n if (cStart >= startLine && cStart <= endLine) return true;\n if (cStart >= endLine && cStart - endLine <= maxGap) return true;\n }\n return false;\n}\n","/**\n * Rule: `@graphorin/no-bare-tool-exec`. Flags any `tool({...})`\n * invocation whose `execute` function does not reference\n * `ctx.signal`. The intent is principle 3 (streaming-first) +\n * DEC-143 (cancellation contract): every long-running tool MUST\n * propagate the abort signal to the underlying I/O so cancellation\n * actually frees resources.\n *\n * The check is intentionally lexical - it scans the function body for\n * the literal string `signal` (matching `ctx.signal`,\n * `request.signal`, `args.signal`, etc.). False positives are\n * acceptable because the fix is one comment line; false negatives are\n * not, because they hide cancellation bugs in production.\n *\n * Per-call opt-out: `// graphorin-allow-bare-exec: <reason>` on the\n * line above the `execute` function or anywhere inside the tool\n * builder call.\n *\n * @packageDocumentation\n */\n\nimport type { Rule } from 'eslint';\nimport type {\n ArrowFunctionExpression,\n CallExpression,\n FunctionExpression,\n Identifier,\n ObjectExpression,\n Property,\n} from 'estree';\n\nimport { nodeHasNearbyComment } from './_comment-utils.js';\n\nconst ALLOW_TAG = /graphorin-allow-bare-exec/;\nconst SIGNAL_REFERENCE_RE = /\\bsignal\\b/;\n\nconst rule: Rule.RuleModule = {\n meta: {\n type: 'suggestion',\n docs: {\n description:\n 'Require every `tool({...})` `execute` function to reference `ctx.signal` so cancellation propagates to the underlying I/O (principle 3, DEC-143).',\n recommended: true,\n },\n schema: [],\n messages: {\n missingSignal:\n \"tool '{{name}}' `execute` function does not reference `ctx.signal`; long-running tools MUST propagate the abort signal. Add `// graphorin-allow-bare-exec: <reason>` to opt out.\",\n },\n },\n create(context: Rule.RuleContext): Rule.RuleListener {\n return {\n CallExpression(node: CallExpression): void {\n if (!isToolBuilderCall(node)) return;\n const arg = node.arguments[0];\n if (arg === undefined || arg.type !== 'ObjectExpression') return;\n const exec = findExecuteProperty(arg as ObjectExpression);\n if (exec === null) return;\n if (!isFunctionLike(exec)) return;\n const fn = exec as ArrowFunctionExpression | FunctionExpression;\n if (!fn.body) return;\n const fnSource = context.sourceCode.getText(fn.body);\n if (SIGNAL_REFERENCE_RE.test(fnSource)) return;\n if (hasAllowComment(context, node)) return;\n const name = extractToolName(arg as ObjectExpression) ?? '<unknown>';\n context.report({\n node: fn,\n messageId: 'missingSignal',\n data: { name },\n });\n },\n };\n },\n};\n\nfunction isToolBuilderCall(node: CallExpression): boolean {\n if (node.callee.type !== 'Identifier') return false;\n return (node.callee as Identifier).name === 'tool';\n}\n\nfunction findExecuteProperty(obj: ObjectExpression): Property['value'] | null {\n for (const prop of obj.properties) {\n if (prop.type !== 'Property') continue;\n const property = prop as Property;\n if (property.computed) continue;\n const key = property.key;\n if (key.type === 'Identifier' && key.name === 'execute') return property.value;\n if (key.type === 'Literal' && key.value === 'execute') return property.value;\n }\n return null;\n}\n\nfunction isFunctionLike(\n value: Property['value'],\n): value is ArrowFunctionExpression | FunctionExpression {\n return value.type === 'ArrowFunctionExpression' || value.type === 'FunctionExpression';\n}\n\nfunction extractToolName(obj: ObjectExpression): string | null {\n for (const prop of obj.properties) {\n if (prop.type !== 'Property') continue;\n const property = prop as Property;\n if (property.computed) continue;\n const key = property.key;\n const isName =\n (key.type === 'Identifier' && key.name === 'name') ||\n (key.type === 'Literal' && key.value === 'name');\n if (!isName) continue;\n if (property.value.type === 'Literal' && typeof property.value.value === 'string') {\n return property.value.value;\n }\n }\n return null;\n}\n\nfunction hasAllowComment(context: Rule.RuleContext, node: CallExpression): boolean {\n return nodeHasNearbyComment(context, node, ALLOW_TAG, 1);\n}\n\nexport default rule;\n","/**\n * Rule: `@graphorin/no-implicit-network-call` (DEC-154 / ADR-041).\n * Flags any direct network primitive in code under a `@graphorin/*`\n * package's `src/` directory unless the call site carries an opt-out\n * comment whose text contains `'graphorin-allow-network'`:\n *\n * - calls: `fetch(...)`, `http(s).request/get(...)`, `axios(...)` /\n * `axios.<verb>(...)`, `undici.<verb>(...)` / `got.<verb>(...)`,\n * `net.createConnection/connect(...)`, `tls.connect(...)`,\n * `dgram.createSocket(...)`\n * - constructors: `new XMLHttpRequest()`, `new WebSocket(...)`,\n * `new EventSource(...)`\n * - imports of HTTP clients: `node-fetch`, `undici`, `got`, `axios`,\n * `ky`, `ws` (static, dynamic `import(...)`, and `require(...)`)\n *\n * Companion to the `pnpm run check-no-network` static analysis script;\n * the two matchers are kept in lockstep (this rule mirrors the EB-10\n * hardening that taught the script about undici/got, raw sockets,\n * WebSocket/EventSource, and HTTP-client import specifiers). The lint\n * surface catches the pattern at author time so reviewers do not need\n * to wait for CI to flag a missed network gate.\n *\n * The rule is intentionally limited to the framework's own code paths\n * - consumer applications can call `fetch` freely. The rule activates\n * whenever the linted file path matches `/packages/<pkg>/src/`.\n *\n * @packageDocumentation\n */\n\nimport type { Rule } from 'eslint';\nimport type {\n CallExpression,\n Identifier,\n ImportDeclaration,\n MemberExpression,\n NewExpression,\n} from 'estree';\n\nimport { nodeHasNearbyComment } from './_comment-utils.js';\n\nconst ALLOW_TAG = /graphorin-allow-network/;\nconst FRAMEWORK_PATH_RE = /\\bpackages\\/[a-z0-9_-]+\\/src\\b/;\n\nconst NETWORK_CALLEES = new Set(['fetch', 'XMLHttpRequest']);\nconst HTTP_VERBS = new Set(['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'request']);\nconst CLIENT_NAMESPACE_VERBS = new Set(['request', 'stream', 'fetch', 'get', 'post']);\nconst NETWORK_CONSTRUCTORS = new Set(['XMLHttpRequest', 'WebSocket', 'EventSource']);\nconst HTTP_CLIENT_SPECIFIERS = new Set(['node-fetch', 'undici', 'got', 'axios', 'ky', 'ws']);\n\nconst rule: Rule.RuleModule = {\n meta: {\n type: 'problem',\n docs: {\n description:\n 'Disallow direct network primitives (`fetch`, `axios`/`undici`/`got`, `http.request`, raw `net`/`tls`/`dgram` sockets, `WebSocket`/`EventSource`/`XMLHttpRequest`, HTTP-client imports) in `@graphorin/*` framework code without an explicit opt-out comment (DEC-154).',\n recommended: true,\n },\n schema: [],\n messages: {\n forbidden:\n \"direct network call '{{callee}}' in framework code; user actions must initiate network I/O. Add `// graphorin-allow-network: <reason>` to opt out.\",\n forbiddenImport:\n \"HTTP-client import '{{specifier}}' in framework code; user actions must initiate network I/O. Add `// graphorin-allow-network: <reason>` to opt out.\",\n },\n },\n create(context: Rule.RuleContext): Rule.RuleListener {\n if (!FRAMEWORK_PATH_RE.test(context.filename.replace(/\\\\/g, '/'))) {\n return {};\n }\n return {\n CallExpression(node: CallExpression): void {\n const specifier = requiredClientSpecifier(node);\n if (specifier !== null) {\n if (hasAllowComment(context, node)) return;\n context.report({\n node,\n messageId: 'forbiddenImport',\n data: { specifier },\n });\n return;\n }\n const name = describeCallee(node);\n if (name === null) return;\n if (hasAllowComment(context, node)) return;\n context.report({\n node,\n messageId: 'forbidden',\n data: { callee: name },\n });\n },\n NewExpression(node: NewExpression): void {\n if (node.callee.type !== 'Identifier') return;\n const callee = node.callee as Identifier;\n if (!NETWORK_CONSTRUCTORS.has(callee.name)) return;\n if (hasAllowComment(context, node)) return;\n context.report({\n node,\n messageId: 'forbidden',\n data: { callee: `new ${callee.name}` },\n });\n },\n ImportDeclaration(node: ImportDeclaration): void {\n const source = node.source.value;\n if (typeof source !== 'string' || !HTTP_CLIENT_SPECIFIERS.has(source)) return;\n if (nodeHasNearbyComment(context, node, ALLOW_TAG, 1)) return;\n context.report({\n node,\n messageId: 'forbiddenImport',\n data: { specifier: source },\n });\n },\n ImportExpression(node: Rule.Node): void {\n const source = (node as { source?: { type?: string; value?: unknown } }).source;\n if (source?.type !== 'Literal' || typeof source.value !== 'string') return;\n if (!HTTP_CLIENT_SPECIFIERS.has(source.value)) return;\n if (nodeHasNearbyComment(context, node as never, ALLOW_TAG, 1)) return;\n context.report({\n node: node as never,\n messageId: 'forbiddenImport',\n data: { specifier: source.value },\n });\n },\n };\n },\n};\n\n/** `require('<http client>')` - import-shaped despite being a call. */\nfunction requiredClientSpecifier(node: CallExpression): string | null {\n if (node.callee.type !== 'Identifier') return null;\n if ((node.callee as Identifier).name !== 'require') return null;\n const arg = node.arguments[0];\n if (arg === undefined || arg.type !== 'Literal') return null;\n const value = (arg as { value?: unknown }).value;\n if (typeof value !== 'string' || !HTTP_CLIENT_SPECIFIERS.has(value)) return null;\n return value;\n}\n\nfunction describeCallee(node: CallExpression): string | null {\n if (node.callee.type === 'Identifier') {\n const name = (node.callee as Identifier).name;\n if (NETWORK_CALLEES.has(name) || name === 'axios') return name;\n return null;\n }\n if (node.callee.type === 'MemberExpression') {\n const me = node.callee as MemberExpression;\n if (me.computed) return null;\n const objName = me.object.type === 'Identifier' ? (me.object as Identifier).name : null;\n if (objName === null) return null;\n const propName = me.property.type === 'Identifier' ? (me.property as Identifier).name : null;\n if (propName === null) return null;\n if (\n (objName === 'http' || objName === 'https') &&\n (propName === 'request' || propName === 'get')\n )\n return `${objName}.${propName}`;\n if (objName === 'axios' && HTTP_VERBS.has(propName)) return `axios.${propName}`;\n if ((objName === 'undici' || objName === 'got') && CLIENT_NAMESPACE_VERBS.has(propName))\n return `${objName}.${propName}`;\n if (objName === 'net' && (propName === 'createConnection' || propName === 'connect'))\n return `net.${propName}`;\n if (objName === 'tls' && propName === 'connect') return 'tls.connect';\n if (objName === 'dgram' && propName === 'createSocket') return 'dgram.createSocket';\n return null;\n }\n return null;\n}\n\nfunction hasAllowComment(context: Rule.RuleContext, node: CallExpression | NewExpression): boolean {\n return nodeHasNearbyComment(context, node, ALLOW_TAG, 1);\n}\n\nexport default rule;\n","/**\n * Rule: `@graphorin/no-secret-in-deps` (RB-24 / DEC-137). Flags any\n * call shaped like `withChildToolSecretsContext({ secretsAllowed:\n * [...] }, fn)` where the `secretsAllowed` array is non-empty and the\n * surrounding call site does not include a comment whose text starts\n * with `'rb-24-justification:'` (case-insensitive).\n *\n * The rule enforces the principle-of-least-authority discipline from\n * DEC-137: granting a child tool scope access to parent secrets is\n * opt-in, and the opt-in MUST be explained in code so security\n * reviewers can audit the inheritance graph without guessing.\n *\n * History: this rule originally matched `Agent.toTool({\n * inheritSecrets: [...] })`, a pre-0.5 API shape that no longer\n * exists - `AgentToToolOptions` deliberately has no secret-inheritance\n * mechanism at that boundary. The DEC-137 grant point today is the\n * explicit child ACL scope opened via `withChildToolSecretsContext`\n * from `@graphorin/security` (whose `secretsAllowed` is intersected\n * with the parent allowlist), so that is what the rule matches now.\n *\n * The rule is intentionally syntactic - it operates on the literal\n * call site without trying to resolve the value of the array. This\n * keeps the rule cheap and avoids false negatives from spread /\n * function-call expressions that hide the inheritance shape.\n *\n * @packageDocumentation\n */\n\nimport type { Rule } from 'eslint';\nimport type {\n ArrayExpression,\n CallExpression,\n Identifier,\n MemberExpression,\n ObjectExpression,\n Property,\n} from 'estree';\n\nimport { nodeHasNearbyComment } from './_comment-utils.js';\n\nconst JUSTIFICATION_TAG = /\\brb-24-justification\\s*:/i;\nconst GRANT_CALLEE = 'withChildToolSecretsContext';\n\nconst rule: Rule.RuleModule = {\n meta: {\n type: 'suggestion',\n docs: {\n description:\n 'Require an `// rb-24-justification:` comment when `withChildToolSecretsContext({ secretsAllowed: [...] })` grants a non-empty allowlist to a child tool scope (DEC-137).',\n recommended: true,\n },\n schema: [],\n messages: {\n missingJustification:\n '`secretsAllowed` is non-empty but the call site lacks an `// rb-24-justification: <reason>` comment. Document why this child tool scope inherits parent secrets per DEC-137.',\n },\n },\n create(context: Rule.RuleContext): Rule.RuleListener {\n return {\n CallExpression(node: CallExpression): void {\n if (!isGrantCall(node)) return;\n const arg = node.arguments[0];\n if (arg === undefined || arg.type !== 'ObjectExpression') return;\n const objectArg = arg as ObjectExpression;\n const secretsAllowed = findSecretsAllowedProperty(objectArg);\n if (secretsAllowed === null) return;\n if (!isNonEmptyArray(secretsAllowed)) return;\n if (hasJustificationComment(context, node)) return;\n context.report({\n node: secretsAllowed,\n messageId: 'missingJustification',\n });\n },\n };\n },\n};\n\nfunction isGrantCall(node: CallExpression): boolean {\n if (node.callee.type === 'Identifier') {\n return (node.callee as Identifier).name === GRANT_CALLEE;\n }\n if (node.callee.type === 'MemberExpression') {\n const callee = node.callee as MemberExpression;\n if (callee.computed) return false;\n if (callee.property.type !== 'Identifier') return false;\n return callee.property.name === GRANT_CALLEE;\n }\n return false;\n}\n\nfunction findSecretsAllowedProperty(obj: ObjectExpression): Property['value'] | null {\n for (const prop of obj.properties) {\n if (prop.type !== 'Property') continue;\n const property = prop as Property;\n const key = property.key;\n if (property.computed) continue;\n if (key.type === 'Identifier' && key.name === 'secretsAllowed') return property.value;\n if (key.type === 'Literal' && key.value === 'secretsAllowed') return property.value;\n }\n return null;\n}\n\nfunction isNonEmptyArray(value: Property['value']): boolean {\n if (value.type !== 'ArrayExpression') return false;\n const arr = value as ArrayExpression;\n return arr.elements.some((e) => e !== null);\n}\n\nfunction hasJustificationComment(context: Rule.RuleContext, node: CallExpression): boolean {\n return nodeHasNearbyComment(context, node, JUSTIFICATION_TAG, 1);\n}\n\nexport default rule;\n","/**\n * Rule: `@graphorin/no-secret-unwrap` (DEC-020 / ADR-026 / Phase 16).\n *\n * Flags member expressions that look like an unprotected unwrap of a\n * `SecretValue` instance - `<expr>.unwrap()` and `<expr>.reveal()` -\n * outside an allow-listed context. The framework convention is:\n *\n * - Prefer `value.use(fn)` / `value.useBuffer(fn)` for scoped reads;\n * they limit the lifetime of the derived V8 string to a single\n * callback invocation.\n * - Use `value.reveal()` only as a one-shot escape hatch with an\n * adjacent justification. The audit log records the call.\n * - Never use `value.unwrap()` - it is `@deprecated` and is an alias\n * for `.reveal()` retained only for the v0.x compatibility window.\n * The rule reports `unwrap()` as `'error'` even when an\n * opt-out comment is present so the deprecation cliff stays sharp.\n *\n * Per-call opt-out: `// graphorin-allow-secret-unwrap: <reason>` on\n * the line above the call site or anywhere inside the enclosing\n * expression-statement source span. The opt-out is honoured for\n * `reveal()` but **not** for `unwrap()` (the deprecation supersedes\n * any local justification).\n *\n * The rule is intentionally lexical - it matches `.unwrap()` /\n * `.reveal()` on any `MemberExpression` regardless of the receiver\n * type. False positives are tolerated because (a) the receiver is\n * almost always a `SecretValue` in this codebase and (b) the\n * one-line opt-out is cheap.\n *\n * @packageDocumentation\n */\n\nimport type { Rule } from 'eslint';\nimport type { CallExpression, Identifier, MemberExpression } from 'estree';\n\nimport { nodeHasNearbyComment } from './_comment-utils.js';\n\nconst ALLOW_TAG = /graphorin-allow-secret-unwrap/;\n\nconst rule: Rule.RuleModule = {\n meta: {\n type: 'problem',\n docs: {\n description:\n 'Disallow `.unwrap()` / `.reveal()` calls on `SecretValue` instances. Prefer `.use(fn)` (scoped) or attach a `// graphorin-allow-secret-unwrap: <reason>` opt-out comment for `.reveal()`.',\n recommended: true,\n },\n schema: [],\n messages: {\n avoidReveal:\n '`.reveal()` returns the unwrapped secret as a V8 string. Prefer `.use(fn)` so the value is scoped to a single callback. Add `// graphorin-allow-secret-unwrap: <reason>` to opt out.',\n avoidUnwrap:\n '`.unwrap()` is deprecated - call `.reveal()` (audited) or `.use(fn)` (scoped). The opt-out comment is intentionally NOT honoured for `.unwrap()` so the deprecation stays sharp.',\n },\n },\n create(context: Rule.RuleContext): Rule.RuleListener {\n return {\n CallExpression(node: CallExpression): void {\n const callee = node.callee;\n if (callee.type !== 'MemberExpression') return;\n const me = callee as MemberExpression;\n if (me.computed) return;\n if (me.property.type !== 'Identifier') return;\n const propName = (me.property as Identifier).name;\n if (propName === 'unwrap') {\n context.report({\n node,\n messageId: 'avoidUnwrap',\n });\n return;\n }\n if (propName === 'reveal') {\n if (hasAllowComment(context, node)) return;\n context.report({\n node,\n messageId: 'avoidReveal',\n });\n }\n },\n };\n },\n};\n\nfunction hasAllowComment(context: Rule.RuleContext, node: CallExpression): boolean {\n return nodeHasNearbyComment(context, node, ALLOW_TAG, 1);\n}\n\nexport default rule;\n","/**\n * Rule: `@graphorin/no-third-party-workflow-aliases` (DEC-019 /\n * ADR-029). Flags any identifier in `@graphorin/workflow` source whose\n * name matches a known third-party workflow primitive (e.g. the\n * library-specific names that Graphorin deliberately renamed). The\n * intent is legal hygiene: the framework's primitives are\n * `Directive`, `Dispatch`, `pause`, `LatestValue`, `Reducer`,\n * `Stream`, `Barrier`, `Ephemeral`, `AnyValue` - we never reuse\n * external library identifiers in the public API.\n *\n * The forbidden list is intentionally narrow - we only flag the\n * canonical proper-noun primitives third-party workflow engines use.\n * Rare false positives (e.g. an internal helper named `Send` for an\n * unrelated reason) are mitigated by the per-occurrence opt-out\n * comment `// graphorin-workflow-naming-allow: <reason>`.\n *\n * The rule activates only on files inside the\n * `@graphorin/workflow` package source tree (path matcher\n * `packages/workflow/src`).\n *\n * @packageDocumentation\n */\n\nimport type { Rule } from 'eslint';\nimport type { Identifier, Node } from 'estree';\n\nimport { nodeHasNearbyComment } from './_comment-utils.js';\n\nconst ALLOW_TAG = /graphorin-workflow-naming-allow/;\nconst WORKFLOW_PATH_RE = /\\bpackages\\/workflow\\/src\\b/;\n\nconst FORBIDDEN_NAMES: ReadonlyMap<string, string> = new Map([\n ['Send', 'Dispatch'],\n ['Command', 'Directive'],\n ['interrupt', 'pause'],\n ['LastValue', 'LatestValue'],\n ['BinaryOperatorAggregate', 'Reducer'],\n ['BinaryAggregate', 'Reducer'],\n ['Topic', 'Stream'],\n]);\n\nconst rule: Rule.RuleModule = {\n meta: {\n type: 'problem',\n docs: {\n description:\n 'Disallow third-party workflow primitive identifiers in `@graphorin/workflow` source. Graphorin owns its primitive names per DEC-019.',\n recommended: true,\n },\n schema: [],\n messages: {\n forbidden:\n \"identifier '{{forbidden}}' is reserved for the third-party workflow library it originates from; use '{{replacement}}' (DEC-019).\",\n },\n },\n create(context: Rule.RuleContext): Rule.RuleListener {\n if (!WORKFLOW_PATH_RE.test(context.filename.replace(/\\\\/g, '/'))) {\n return {};\n }\n return {\n Identifier(node: Identifier): void {\n const replacement = FORBIDDEN_NAMES.get(node.name);\n if (replacement === undefined) return;\n if (isInImportSpecifier(node)) return;\n if (hasAllowComment(context, node)) return;\n context.report({\n node,\n messageId: 'forbidden',\n data: {\n forbidden: node.name,\n replacement,\n },\n });\n },\n };\n },\n};\n\nfunction isInImportSpecifier(node: Identifier): boolean {\n // Skip identifiers used as keys / property names - those are\n // typically referencing external library exports the user is\n // intentionally importing into a renamed local symbol.\n const parent = (node as Identifier & { parent?: Node }).parent;\n if (parent === undefined) return false;\n switch (parent.type) {\n case 'ImportSpecifier':\n case 'ImportNamespaceSpecifier':\n case 'ImportDefaultSpecifier':\n case 'ExportSpecifier':\n return true;\n default:\n return false;\n }\n}\n\nfunction hasAllowComment(context: Rule.RuleContext, node: Identifier): boolean {\n return nodeHasNearbyComment(context, node, ALLOW_TAG, 1);\n}\n\nexport default rule;\n","/**\n * Rule: `@graphorin/provider-middleware-order` (DEC-145 / ADR-039).\n * Lint-time enforcement of the canonical middleware ordering for\n * `composeProviderMiddleware([...])`. Catches the same class of error\n * the runtime composer raises (`MiddlewareOrderingError`) without\n * requiring the runtime to actually wire the chain.\n *\n * The canonical order, outermost → innermost, mirrors the runtime\n * constant `CANONICAL_MIDDLEWARE_ORDER` exported by\n * `@graphorin/provider/middleware`. Built-ins not in the canonical\n * list are tolerated at any position.\n *\n * @packageDocumentation\n */\n\nimport type { Rule } from 'eslint';\nimport type { ArrayExpression, CallExpression, Identifier, Node } from 'estree';\n\nconst CANONICAL_ORDER: ReadonlyArray<string> = [\n 'withTracing',\n 'withRetry',\n 'withRateLimit',\n 'withCostLimit',\n 'withCostTracking',\n 'withFallback',\n 'withRedaction',\n];\n\nconst CANONICAL_INDEX: ReadonlyMap<string, number> = new Map(\n CANONICAL_ORDER.map((name, idx) => [name, idx]),\n);\n\nconst rule: Rule.RuleModule = {\n meta: {\n type: 'problem',\n docs: {\n description:\n 'Enforce the canonical provider-middleware ordering at lint time. Mirrors the runtime `MiddlewareOrderingError` from `@graphorin/provider/middleware`.',\n recommended: true,\n },\n schema: [],\n messages: {\n orderingViolation: \"'{{outer}}' must appear before '{{inner}}' (canonical order: {{order}}).\",\n },\n },\n create(context: Rule.RuleContext): Rule.RuleListener {\n return {\n CallExpression(node: CallExpression): void {\n if (!isComposeCall(node)) return;\n const arg = node.arguments[0];\n if (arg === undefined || arg.type !== 'ArrayExpression') return;\n const factories = extractFactoryNames(arg as ArrayExpression);\n const recognised: { name: string; index: number; node: Node }[] = [];\n for (const f of factories) {\n const idx = CANONICAL_INDEX.get(f.name);\n if (idx !== undefined) recognised.push({ name: f.name, index: idx, node: f.node });\n }\n for (let i = 1; i < recognised.length; i++) {\n const prev = recognised[i - 1];\n const cur = recognised[i];\n if (prev !== undefined && cur !== undefined && prev.index > cur.index) {\n context.report({\n node: cur.node,\n messageId: 'orderingViolation',\n data: {\n outer: cur.name,\n inner: prev.name,\n order: CANONICAL_ORDER.join(' → '),\n },\n });\n }\n }\n },\n };\n },\n};\n\nfunction isComposeCall(node: CallExpression): boolean {\n if (node.callee.type === 'Identifier') {\n return (node.callee as Identifier).name === 'composeProviderMiddleware';\n }\n if (node.callee.type === 'MemberExpression') {\n const prop = node.callee.property;\n if (prop.type === 'Identifier' && prop.name === 'composeProviderMiddleware') return true;\n }\n return false;\n}\n\nfunction extractFactoryNames(arr: ArrayExpression): { name: string; node: Node }[] {\n const out: { name: string; node: Node }[] = [];\n for (const elt of arr.elements) {\n if (elt === null) continue;\n // Each element is one of:\n // - `withTracing()` / `withRetry({...})` - CallExpression with Identifier callee\n // - `withTracing` (factory ref) - Identifier\n // - `provider.withTracing()` - CallExpression with MemberExpression callee\n if (elt.type === 'Identifier') {\n out.push({ name: (elt as Identifier).name, node: elt });\n continue;\n }\n if (elt.type === 'CallExpression') {\n const callee = elt.callee;\n if (callee.type === 'Identifier') {\n out.push({ name: (callee as Identifier).name, node: elt });\n } else if (callee.type === 'MemberExpression' && callee.property.type === 'Identifier') {\n out.push({ name: callee.property.name, node: elt });\n }\n }\n }\n return out;\n}\n\nexport default rule;\n","/**\n * Static `tool({...})` discovery + per-tool grader. Used by both the\n * three `tool-*` ESLint rules and by the `graphorin tools lint` CLI\n * subcommand (Phase 15) so the rule logic has a single source of\n * truth (per the working plan acceptance criteria for RB-49).\n *\n * The discovery is intentionally text-based - it scans a source\n * string for `tool(` call expressions and extracts the immediate\n * object literal that follows. The extractor handles the common\n * formatting cases (single-line, multi-line, nested object/array\n * literals, single + double-quoted strings, template literals) and\n * gracefully skips invocations whose argument shape it cannot\n * parse statically. The trade-off is documented: a project that\n * hides its `tool({...})` calls behind a builder helper or a\n * dynamic import will not be picked up by this lint surface; that\n * is the documented contract for the v0.1 lint surface.\n *\n * **Per-tool grader rubric (RB-49 calibration):**\n *\n * - **description axis (0..40 points):**\n * - 0 if missing / placeholder / shorter than 20 chars.\n * - 16 if length >= 20.\n * - 24 if length >= 30.\n * - 32 if length >= 50.\n * - 40 if length >= 80.\n * - **examples axis (0..30 points):**\n * - 0 if no examples or more than 5 (the documented upper bound).\n * - 12 base for 1 example, +6 per additional, capped at 30.\n * - -6 per PII finding (cap at 0).\n * - **parameter naming axis (0..30 points):**\n * - 30 base, deducted per finding.\n * - -30/N per ambiguous-name finding (full penalty per param).\n * - -10/N per numeric-suffix finding (partial penalty per param).\n * - 15 baseline when no parameters are discoverable.\n *\n * Total: 0..100 points. Calibrated against the RB-49 fixture\n * catalog so `wellDescribedTool` scores 82, `placeholderDescriptionTool`\n * scores 20, and `examplesPiiTool` scores 61.\n *\n * @stable\n */\n\n/**\n * @stable\n */\nexport interface DiscoveredTool {\n /** Source file the call was found in. */\n readonly file: string;\n /** 1-indexed line of the `tool(` token. */\n readonly line: number;\n /** Tool name extracted from the `name:` property when present. */\n readonly name: string;\n /** Tool description (`description:` value) when extractable. */\n readonly description?: string;\n /** Number of examples declared in the `examples:` array. */\n readonly examplesCount: number;\n /** Whether `examples:` is a non-empty array literal. */\n readonly hasExamples: boolean;\n /** Snapshot of identifiers referenced from the `inputSchema` Zod chain. */\n readonly parameterNames: ReadonlyArray<string>;\n /** Tags declared on the call (best-effort). */\n readonly tags: ReadonlyArray<string>;\n /**\n * Raw object-literal source. Useful for tests + as a context blob\n * when the CLI needs to surface the original source in a report.\n */\n readonly source: string;\n}\n\n/**\n * @stable\n */\nexport type LintFindingKind =\n | 'description-missing'\n | 'description-too-short'\n | 'description-placeholder'\n | 'examples-missing'\n | 'examples-too-many'\n | 'examples-pii-detected'\n | 'parameter-ambiguous'\n | 'parameter-numeric-suffix';\n\n/**\n * @stable\n */\nexport interface LintFinding {\n readonly rule:\n | 'graphorin/tool-description-required'\n | 'graphorin/tool-examples-recommended'\n | 'graphorin/tool-parameter-naming';\n readonly kind: LintFindingKind;\n readonly severity: 'error' | 'warn' | 'info';\n readonly message: string;\n readonly toolName: string;\n readonly file: string;\n readonly line: number;\n readonly hint?: string;\n /**\n * Optional matched-pattern context. Populated by the\n * `examples-pii-detected` finding so reports can highlight which\n * example payload triggered the rule.\n */\n readonly matchedPattern?: string;\n}\n\n/**\n * @stable\n */\nexport interface ToolGraderScore {\n readonly toolName: string;\n readonly file: string;\n readonly line: number;\n readonly score: number;\n readonly axes: {\n readonly description: number;\n readonly examples: number;\n readonly parameterNaming: number;\n };\n readonly findings: ReadonlyArray<LintFinding>;\n}\n\n/**\n * Generic identifiers the parameter-naming rule flags as ambiguous.\n * Tools whose `inputSchema` references only specific identifiers\n * (e.g. `userId`, `recipientEmail`, `apiKey`) get full credit on\n * the naming axis.\n *\n * @stable\n */\nexport const AMBIGUOUS_PARAMETER_NAMES: ReadonlyArray<string> = Object.freeze([\n 'user',\n 'id',\n 'name',\n 'value',\n 'data',\n 'input',\n 'output',\n 'result',\n 'to',\n 'from',\n 'key',\n 'field',\n]);\n\n/**\n * Tag values that, when present in a tool's `tags: [...]` literal,\n * suppress the parameter-naming rule for that tool. The opt-out\n * exists so operators can defer the rename for a long tail of\n * pre-RB-49 tools while the framework migrates without breaking\n * calling code.\n *\n * @stable\n */\nexport const PARAMETER_NAMING_OPT_OUT_TAGS: ReadonlyArray<string> = Object.freeze([\n 'experimental',\n 'legacy',\n]);\n\n/**\n * Placeholder values the description-required rule treats as\n * non-descriptions.\n *\n * @stable\n */\nexport const PLACEHOLDER_DESCRIPTIONS: ReadonlyArray<string> = Object.freeze([\n 'todo',\n 'fixme',\n 'tbd',\n 'description',\n 'placeholder',\n]);\n\nconst MIN_DESCRIPTION_LENGTH = 20;\nconst MAX_EXAMPLES = 5;\n\n/**\n * Discover every `tool({...})` invocation in a source string. The\n * returned findings are stable + frozen so callers can pass them\n * straight into a JSON report.\n *\n * @stable\n */\nexport function discoverToolCallsInSource(file: string, source: string): DiscoveredTool[] {\n const out: DiscoveredTool[] = [];\n const regex = /\\btool\\s*\\(\\s*\\{/g;\n let match: RegExpExecArray | null = regex.exec(source);\n while (match !== null) {\n const objectStart = match.index + match[0].length - 1;\n const objectEnd = matchBrace(source, objectStart);\n if (objectEnd > 0) {\n const literal = source.slice(objectStart, objectEnd + 1);\n const line = countLines(source, match.index);\n const tool = parseToolLiteral(file, line, literal);\n if (tool !== null) out.push(tool);\n }\n regex.lastIndex = objectEnd + 1;\n match = regex.exec(source);\n }\n return out;\n}\n\n/** Email PII pattern used by the examples-pii sub-check. */\nconst EMAIL_PATTERN = /[\\w.+%-]+@[\\w.-]+\\.[A-Za-z]{2,}/;\n\n/** Numeric-suffix pattern used by the parameter-naming sub-check. */\nconst NUMERIC_SUFFIX_PATTERN = /^[A-Za-z]+\\d+$/;\n\n/**\n * Run the three RB-49 rules against a discovered tool and return the\n * findings. The CLI grader maps these findings into per-axis scores;\n * the ESLint rules forward them to `context.report(...)`.\n *\n * @stable\n */\nexport function runToolRules(\n tool: DiscoveredTool,\n severityOverrides?: {\n readonly toolDescription?: 'error' | 'warn' | 'off';\n readonly toolExamples?: 'error' | 'warn' | 'off';\n readonly toolParameterNaming?: 'error' | 'warn' | 'off';\n },\n): LintFinding[] {\n const findings: LintFinding[] = [];\n\n const descSeverity = severityOverrides?.toolDescription ?? 'error';\n if (descSeverity !== 'off') {\n const desc = tool.description?.trim() ?? '';\n if (desc.length === 0) {\n findings.push(\n finding(\n 'graphorin/tool-description-required',\n 'description-missing',\n descSeverity,\n tool,\n `tool '${tool.name}' has no description; add a description that explains what the tool does and when to use it.`,\n ),\n );\n } else if (PLACEHOLDER_DESCRIPTIONS.includes(desc.toLowerCase())) {\n findings.push(\n finding(\n 'graphorin/tool-description-required',\n 'description-placeholder',\n descSeverity,\n tool,\n `tool '${tool.name}' description is a placeholder ('${tool.description}').`,\n ),\n );\n } else if (desc.length < MIN_DESCRIPTION_LENGTH) {\n findings.push(\n finding(\n 'graphorin/tool-description-required',\n 'description-too-short',\n descSeverity,\n tool,\n `tool '${tool.name}' description is shorter than ${MIN_DESCRIPTION_LENGTH} characters.`,\n ),\n );\n }\n }\n\n const examplesSeverity = severityOverrides?.toolExamples ?? 'warn';\n if (examplesSeverity !== 'off') {\n if (!tool.hasExamples || tool.examplesCount === 0) {\n findings.push(\n finding(\n 'graphorin/tool-examples-recommended',\n 'examples-missing',\n examplesSeverity,\n tool,\n `tool '${tool.name}' has no examples; add 1-5 worked examples per Anthropic 2026 guidance.`,\n ),\n );\n } else if (tool.examplesCount > MAX_EXAMPLES) {\n findings.push(\n finding(\n 'graphorin/tool-examples-recommended',\n 'examples-too-many',\n 'error',\n tool,\n `tool '${tool.name}' declares ${tool.examplesCount} examples; the upper bound is ${MAX_EXAMPLES}.`,\n ),\n );\n }\n // PII sub-check - fires once per matched email pattern in the\n // examples block. Operators usually want synthetic data, not real\n // addresses scraped from the corpus.\n const emailMatch = EMAIL_PATTERN.exec(extractExamplesBlock(tool.source));\n if (emailMatch !== null) {\n const matched = emailMatch[0] as string;\n findings.push(\n finding(\n 'graphorin/tool-examples-recommended',\n 'examples-pii-detected',\n 'error',\n tool,\n `tool '${tool.name}' example contains a real-looking email '${matched}'; replace with synthetic data (e.g. 'user@example.com').`,\n {\n matchedPattern: matched,\n hint: 'RB-49: examples must use synthetic test data so the rendered tool catalogue does not leak PII into provider context.',\n },\n ),\n );\n }\n }\n\n const namingSeverity = severityOverrides?.toolParameterNaming ?? 'warn';\n const namingOptedOut = tool.tags.some((t) => PARAMETER_NAMING_OPT_OUT_TAGS.includes(t));\n if (namingSeverity !== 'off' && !namingOptedOut) {\n for (const param of tool.parameterNames) {\n if (AMBIGUOUS_PARAMETER_NAMES.includes(param)) {\n findings.push(\n finding(\n 'graphorin/tool-parameter-naming',\n 'parameter-ambiguous',\n namingSeverity,\n tool,\n `tool '${tool.name}' uses ambiguous parameter name '${param}'; prefer a self-documenting name (e.g. '${param}Id', '${param}Email').`,\n ),\n );\n } else if (NUMERIC_SUFFIX_PATTERN.test(param)) {\n findings.push(\n finding(\n 'graphorin/tool-parameter-naming',\n 'parameter-numeric-suffix',\n namingSeverity,\n tool,\n `tool '${tool.name}' uses numeric-suffix parameter name '${param}'; prefer a semantic name (e.g. 'queryText', 'userId').`,\n ),\n );\n }\n }\n }\n\n return findings;\n}\n\n/**\n * Compute the per-tool grader score (0..100). Each axis is gated by\n * the findings produced for that axis. The rubric is calibrated\n * against the RB-49 fixture catalog (`wellDescribedTool` -> 82,\n * `placeholderDescriptionTool` -> 20, `examplesPiiTool` -> 61).\n *\n * @stable\n */\nexport function gradeTool(\n tool: DiscoveredTool,\n findings: ReadonlyArray<LintFinding>,\n): ToolGraderScore {\n const descFindings = findings.filter((f) => f.rule === 'graphorin/tool-description-required');\n const exampleFindings = findings.filter((f) => f.rule === 'graphorin/tool-examples-recommended');\n const namingFindings = findings.filter((f) => f.rule === 'graphorin/tool-parameter-naming');\n\n const description = scoreDescription(tool, descFindings);\n const examples = scoreExamples(tool, exampleFindings);\n const parameterNaming = scoreParameterNaming(tool, namingFindings);\n\n return Object.freeze({\n toolName: tool.name,\n file: tool.file,\n line: tool.line,\n score: description + examples + parameterNaming,\n axes: Object.freeze({ description, examples, parameterNaming }),\n findings,\n });\n}\n\nfunction scoreDescription(tool: DiscoveredTool, findings: ReadonlyArray<LintFinding>): number {\n if (findings.length > 0) return 0;\n const desc = tool.description?.trim() ?? '';\n if (desc.length >= 80) return 40;\n if (desc.length >= 50) return 32;\n if (desc.length >= 30) return 24;\n if (desc.length >= MIN_DESCRIPTION_LENGTH) return 16;\n return 0;\n}\n\nfunction scoreExamples(tool: DiscoveredTool, findings: ReadonlyArray<LintFinding>): number {\n if (tool.examplesCount === 0) return 0;\n if (tool.examplesCount > MAX_EXAMPLES) return 0;\n // 12 base for the first example + 6 per additional, cap at 30. This\n // calibration produces wellDescribedTool == 12 (1 example) and\n // placeholderDescriptionTool == 0 (no examples). Each PII finding\n // subtracts 6 from the axis (cap at 0) so examplesPiiTool ends up\n // with 6 (12 base - 6 PII penalty).\n const base = Math.min(30, 12 + (tool.examplesCount - 1) * 6);\n const piiCount = findings.filter((f) => f.kind === 'examples-pii-detected').length;\n return Math.max(0, base - piiCount * 6);\n}\n\nfunction scoreParameterNaming(tool: DiscoveredTool, findings: ReadonlyArray<LintFinding>): number {\n if (tool.parameterNames.length === 0) {\n // No discoverable params - neither a positive nor a negative\n // signal. Award the median.\n return 15;\n }\n const total = tool.parameterNames.length;\n const ambiguousCount = findings.filter((f) => f.kind === 'parameter-ambiguous').length;\n const numericCount = findings.filter((f) => f.kind === 'parameter-numeric-suffix').length;\n // Full penalty for an ambiguous-name finding (`user`, `id`, `to`, …):\n // -30/N. Partial penalty for a numeric-suffix finding (`arg1`,\n // `param2`, …): -10/N. Calibrated so a single-param `arg1` scores\n // 20 (placeholderDescriptionTool) and a two-param `to`-+-`body`\n // scores 15 (examplesPiiTool).\n const score = 30 - (30 / total) * ambiguousCount - (10 / total) * numericCount;\n return Math.max(0, Math.round(score));\n}\n\nfunction finding(\n rule: LintFinding['rule'],\n kind: LintFindingKind,\n severity: 'error' | 'warn',\n tool: DiscoveredTool,\n message: string,\n extra: { readonly hint?: string; readonly matchedPattern?: string } = {},\n): LintFinding {\n return Object.freeze({\n rule,\n kind,\n severity,\n message,\n toolName: tool.name,\n file: tool.file,\n line: tool.line,\n ...(extra.hint !== undefined ? { hint: extra.hint } : {}),\n ...(extra.matchedPattern !== undefined ? { matchedPattern: extra.matchedPattern } : {}),\n });\n}\n\n/**\n * Extract the substring inside the `examples: [...]` array literal so\n * the PII detector can scan only the example payloads (not the rest of\n * the tool body).\n *\n * @internal\n */\nfunction extractExamplesBlock(literal: string): string {\n const m = /examples\\s*:\\s*\\[/.exec(literal);\n if (m === null) return '';\n const start = m.index + m[0].length - 1;\n const end = matchBracket(literal, start);\n if (end < 0) return '';\n return literal.slice(start + 1, end);\n}\n\n/**\n * Parse a string literal value from the immediate object literal.\n * Supports `'`, `\"`, and template strings (without interpolation).\n *\n * @internal\n */\nfunction readStringProp(literal: string, prop: string): string | undefined {\n const escapedProp = prop.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n const re = new RegExp(`(?:^|[\\\\s,{])${escapedProp}\\\\s*:\\\\s*(['\"\\`])`, 'm');\n const m = re.exec(literal);\n if (m === null) return undefined;\n const quote = m[1] as string;\n const start = m.index + m[0].length;\n let i = start;\n let out = '';\n while (i < literal.length) {\n const ch = literal[i] as string;\n if (ch === '\\\\' && i + 1 < literal.length) {\n out += literal[i + 1];\n i += 2;\n continue;\n }\n if (ch === quote) return out;\n out += ch;\n i += 1;\n }\n return undefined;\n}\n\n/**\n * Count entries in an array literal value of the form `prop: [...]`.\n *\n * @internal\n */\nfunction countArrayProp(\n literal: string,\n prop: string,\n): { readonly count: number; readonly present: boolean } {\n const escapedProp = prop.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n const re = new RegExp(`(?:^|[\\\\s,{])${escapedProp}\\\\s*:\\\\s*\\\\[`, 'm');\n const m = re.exec(literal);\n if (m === null) return { count: 0, present: false };\n const start = m.index + m[0].length - 1;\n const end = matchBracket(literal, start);\n if (end < 0) return { count: 0, present: true };\n // Strip a trailing comma so it does not inflate the count.\n const inner = literal\n .slice(start + 1, end)\n .trim()\n .replace(/,\\s*$/, '');\n if (inner.length === 0) return { count: 0, present: true };\n // Count top-level commas (depth + string aware).\n let depth = 0;\n let inString: '\"' | \"'\" | '`' | null = null;\n let count = 1;\n for (let i = 0; i < inner.length; i += 1) {\n const ch = inner[i] as string;\n if (inString !== null) {\n if (ch === '\\\\') {\n i += 1;\n continue;\n }\n if (ch === inString) inString = null;\n continue;\n }\n if (ch === '\"' || ch === \"'\" || ch === '`') {\n inString = ch as '\"' | \"'\" | '`';\n continue;\n }\n if (ch === '(' || ch === '[' || ch === '{') depth += 1;\n else if (ch === ')' || ch === ']' || ch === '}') depth -= 1;\n else if (ch === ',' && depth === 0) count += 1;\n }\n return { count, present: true };\n}\n\n/**\n * Extract identifiers passed into a `z.object({...})` schema as\n * top-level keys.\n *\n * @internal\n */\nfunction extractParameterNames(literal: string): string[] {\n const m = /inputSchema\\s*:\\s*z\\s*\\.\\s*object\\s*\\(\\s*\\{/.exec(literal);\n if (m === null) return [];\n const start = m.index + m[0].length - 1;\n const end = matchBrace(literal, start);\n if (end < 0) return [];\n const inner = literal.slice(start + 1, end);\n const names: string[] = [];\n const idRegex = /(^|[\\s,{])([A-Za-z_$][A-Za-z0-9_$]*)\\s*:/g;\n let id = idRegex.exec(inner);\n while (id !== null) {\n const name = id[2] as string;\n if (!names.includes(name)) names.push(name);\n id = idRegex.exec(inner);\n }\n return names;\n}\n\nfunction parseToolLiteral(file: string, line: number, literal: string): DiscoveredTool | null {\n const name = readStringProp(literal, 'name') ?? '<anonymous>';\n const description = readStringProp(literal, 'description');\n const examples = countArrayProp(literal, 'examples');\n const tagsArr = countArrayProp(literal, 'tags');\n const tags: string[] = [];\n if (tagsArr.present && tagsArr.count > 0) {\n const m = /tags\\s*:\\s*\\[([^\\]]*)\\]/.exec(literal);\n if (m !== null) {\n const inner = (m[1] as string) ?? '';\n const tagRegex = /['\"`]([^'\"`]+)['\"`]/g;\n let t = tagRegex.exec(inner);\n while (t !== null) {\n tags.push(t[1] as string);\n t = tagRegex.exec(inner);\n }\n }\n }\n const parameterNames = extractParameterNames(literal);\n return Object.freeze({\n file,\n line,\n name,\n ...(description !== undefined ? { description } : {}),\n examplesCount: examples.count,\n hasExamples: examples.present,\n parameterNames: Object.freeze(parameterNames),\n tags: Object.freeze(tags),\n source: literal,\n });\n}\n\nfunction countLines(source: string, index: number): number {\n let n = 1;\n for (let i = 0; i < index; i += 1) {\n if (source[i] === '\\n') n += 1;\n }\n return n;\n}\n\nfunction matchBrace(source: string, openIndex: number): number {\n return matchPair(source, openIndex, '{', '}');\n}\n\nfunction matchBracket(source: string, openIndex: number): number {\n return matchPair(source, openIndex, '[', ']');\n}\n\nfunction matchPair(source: string, openIndex: number, open: string, close: string): number {\n let depth = 0;\n let inString: '\"' | \"'\" | '`' | null = null;\n for (let i = openIndex; i < source.length; i += 1) {\n const ch = source[i] as string;\n if (inString !== null) {\n if (ch === '\\\\') {\n i += 1;\n continue;\n }\n if (ch === inString) inString = null;\n continue;\n }\n if (ch === '\"' || ch === \"'\" || ch === '`') {\n inString = ch as '\"' | \"'\" | '`';\n continue;\n }\n if (ch === open) depth += 1;\n else if (ch === close) {\n depth -= 1;\n if (depth === 0) return i;\n }\n }\n return -1;\n}\n","/**\n * Rule: `@graphorin/tool-description-required` (RB-49 / suggested\n * DEC-165). Flags `tool({...})` invocations whose `description`\n * field is missing, too short (< 20 characters), or a placeholder\n * value (`'TODO'` / `'FIXME'` / `'tbd'` / `'description'` /\n * `'placeholder'`, case-insensitive).\n *\n * The rule shares its discovery + scoring code with the\n * `graphorin tools lint` CLI subcommand (Phase 15) so the rule\n * logic has a single source of truth.\n *\n * @packageDocumentation\n */\n\nimport type { Rule } from 'eslint';\n\nimport { discoverToolCallsInSource, PLACEHOLDER_DESCRIPTIONS } from '../tool-discovery.js';\n\nconst MIN_DESCRIPTION_LENGTH = 20;\n\nconst rule: Rule.RuleModule = {\n meta: {\n type: 'problem',\n docs: {\n description:\n 'Require a meaningful `description` on every `tool({...})` registration. RB-49 (Anthropic 2026 advanced tool use guidance).',\n recommended: true,\n },\n schema: [],\n messages: {\n missing:\n \"tool '{{name}}' has no description; add a description that explains what the tool does and when to use it.\",\n tooShort:\n \"tool '{{name}}' description is shorter than {{min}} characters (currently {{len}}).\",\n placeholder: \"tool '{{name}}' description is a placeholder ('{{value}}').\",\n },\n },\n create(context: Rule.RuleContext): Rule.RuleListener {\n return {\n 'Program:exit'(): void {\n const filename = context.filename;\n const source = context.sourceCode.text;\n const tools = discoverToolCallsInSource(filename, source);\n for (const tool of tools) {\n const desc = tool.description?.trim();\n if (desc === undefined || desc.length === 0) {\n context.report({\n loc: { line: tool.line, column: 0 },\n messageId: 'missing',\n data: { name: tool.name },\n });\n } else if (desc.length < MIN_DESCRIPTION_LENGTH) {\n context.report({\n loc: { line: tool.line, column: 0 },\n messageId: 'tooShort',\n data: {\n name: tool.name,\n min: String(MIN_DESCRIPTION_LENGTH),\n len: String(desc.length),\n },\n });\n } else if (PLACEHOLDER_DESCRIPTIONS.includes(desc.toLowerCase())) {\n context.report({\n loc: { line: tool.line, column: 0 },\n messageId: 'placeholder',\n data: { name: tool.name, value: desc },\n });\n }\n }\n },\n };\n },\n};\n\nexport default rule;\n","/**\n * Rule: `@graphorin/tool-examples-recommended` (RB-49). Flags\n * `tool({...})` invocations whose `examples` field is missing,\n * empty, or longer than the documented upper bound (5).\n *\n * @packageDocumentation\n */\n\nimport type { Rule } from 'eslint';\n\nimport { discoverToolCallsInSource } from '../tool-discovery.js';\n\nconst MAX_EXAMPLES = 5;\n\nconst rule: Rule.RuleModule = {\n meta: {\n type: 'suggestion',\n docs: {\n description:\n \"Recommend 1-5 `examples` entries on every `tool({...})` registration (Anthropic 2026 'Writing effective tools' guidance).\",\n recommended: true,\n },\n schema: [],\n messages: {\n missing:\n \"tool '{{name}}' has no examples; add 1-5 worked examples per Anthropic 2026 guidance.\",\n tooMany:\n \"tool '{{name}}' declares {{count}} examples; the documented upper bound is {{max}}.\",\n },\n },\n create(context: Rule.RuleContext): Rule.RuleListener {\n return {\n 'Program:exit'(): void {\n const filename = context.filename;\n const source = context.sourceCode.text;\n const tools = discoverToolCallsInSource(filename, source);\n for (const tool of tools) {\n if (!tool.hasExamples || tool.examplesCount === 0) {\n context.report({\n loc: { line: tool.line, column: 0 },\n messageId: 'missing',\n data: { name: tool.name },\n });\n } else if (tool.examplesCount > MAX_EXAMPLES) {\n context.report({\n loc: { line: tool.line, column: 0 },\n messageId: 'tooMany',\n data: {\n name: tool.name,\n count: String(tool.examplesCount),\n max: String(MAX_EXAMPLES),\n },\n });\n }\n }\n },\n };\n },\n};\n\nexport default rule;\n","/**\n * Rule: `@graphorin/tool-parameter-naming` (RB-49). Inspects the\n * `inputSchema: z.object({...})` declaration of every `tool({...})`\n * invocation and flags two anti-patterns:\n *\n * - **Ambiguous single-word identifiers** (`user`, `id`, `name`,\n * `value`, `data`, `input`, `output`, `result`, `to`, `from`,\n * `key`, `field`). Suggest a self-documenting alternative.\n * - **Numeric-suffix identifiers** (`arg1`, `arg2`, `param3`).\n * Suggest a semantic name.\n *\n * Per-tool opt-out: when the tool declares `tags: ['experimental']`\n * or `tags: ['legacy']` the rule is suppressed for that registration.\n * This lets operators defer the rename for a long tail of pre-RB-49\n * tools while the framework migrates without breaking calling code.\n *\n * @packageDocumentation\n */\n\nimport type { Rule } from 'eslint';\n\nimport {\n AMBIGUOUS_PARAMETER_NAMES,\n discoverToolCallsInSource,\n PARAMETER_NAMING_OPT_OUT_TAGS,\n} from '../tool-discovery.js';\n\nconst NUMERIC_SUFFIX_PATTERN = /^[A-Za-z]+\\d+$/;\n\nconst rule: Rule.RuleModule = {\n meta: {\n type: 'suggestion',\n docs: {\n description:\n 'Flag ambiguous or numeric-suffix parameter names on `tool({...})` `inputSchema` declarations (RB-49 - write self-documenting parameter names).',\n recommended: true,\n },\n schema: [],\n messages: {\n ambiguous:\n \"tool '{{name}}' uses ambiguous parameter name '{{param}}'; prefer a self-documenting name (e.g. '{{param}}Id', '{{param}}Email').\",\n numericSuffix:\n \"tool '{{name}}' uses numeric-suffix parameter name '{{param}}'; prefer a semantic name (e.g. 'queryText', 'userId').\",\n },\n },\n create(context: Rule.RuleContext): Rule.RuleListener {\n return {\n 'Program:exit'(): void {\n const filename = context.filename;\n const source = context.sourceCode.text;\n const tools = discoverToolCallsInSource(filename, source);\n for (const tool of tools) {\n if (tool.tags.some((t) => PARAMETER_NAMING_OPT_OUT_TAGS.includes(t))) continue;\n for (const param of tool.parameterNames) {\n if (AMBIGUOUS_PARAMETER_NAMES.includes(param)) {\n context.report({\n loc: { line: tool.line, column: 0 },\n messageId: 'ambiguous',\n data: { name: tool.name, param },\n });\n } else if (NUMERIC_SUFFIX_PATTERN.test(param)) {\n context.report({\n loc: { line: tool.line, column: 0 },\n messageId: 'numericSuffix',\n data: { name: tool.name, param },\n });\n }\n }\n }\n },\n };\n },\n};\n\nexport default rule;\n","/**\n * @graphorin/eslint-plugin - ESLint plugin for projects that build on\n * the Graphorin framework.\n *\n * Phase 16 final ruleset:\n *\n * - `no-secret-unwrap` - DEC-020 / ADR-026. Active.\n * - `no-secret-in-deps` - DEC-137. Active.\n * - `provider-middleware-order` - DEC-145 / ADR-039. Active.\n * - `no-implicit-network-call` - DEC-154 / ADR-041. Active.\n * - `no-third-party-workflow-aliases` - DEC-019 / ADR-029. Active.\n * - `no-bare-tool-exec` - principle 3 / DEC-143. Active.\n * - `tool-description-required` - Active.\n * - `tool-examples-recommended` - Active.\n * - `tool-parameter-naming` - Active.\n *\n * (The former `no-console-in-public-api` scaffold - a permanent no-op\n * since Phase 01 - was removed in the v0.4 hygiene pass (PS-21) rather\n * than shipped inert.)\n *\n * @packageDocumentation\n */\n\nimport noBareToolExec from './rules/no-bare-tool-exec.js';\nimport noImplicitNetworkCall from './rules/no-implicit-network-call.js';\nimport noSecretInDeps from './rules/no-secret-in-deps.js';\nimport noSecretUnwrap from './rules/no-secret-unwrap.js';\nimport noThirdPartyWorkflowAliases from './rules/no-third-party-workflow-aliases.js';\nimport providerMiddlewareOrder from './rules/provider-middleware-order.js';\nimport toolDescriptionRequired from './rules/tool-description-required.js';\nimport toolExamplesRecommended from './rules/tool-examples-recommended.js';\nimport toolParameterNaming from './rules/tool-parameter-naming.js';\n\nexport const VERSION = '0.6.0';\n\nexport const meta = {\n name: '@graphorin/eslint-plugin',\n version: VERSION,\n} as const;\n\nexport const rules = {\n 'no-bare-tool-exec': noBareToolExec,\n 'no-implicit-network-call': noImplicitNetworkCall,\n 'no-secret-in-deps': noSecretInDeps,\n 'no-secret-unwrap': noSecretUnwrap,\n 'no-third-party-workflow-aliases': noThirdPartyWorkflowAliases,\n 'provider-middleware-order': providerMiddlewareOrder,\n 'tool-description-required': toolDescriptionRequired,\n 'tool-examples-recommended': toolExamplesRecommended,\n 'tool-parameter-naming': toolParameterNaming,\n} as const;\n\n/** Shared severity map for both the legacy and flat recommended presets. */\nconst RECOMMENDED_RULES = {\n '@graphorin/no-bare-tool-exec': 'warn',\n '@graphorin/no-implicit-network-call': 'error',\n '@graphorin/no-secret-in-deps': 'error',\n '@graphorin/no-secret-unwrap': 'error',\n '@graphorin/no-third-party-workflow-aliases': 'error',\n '@graphorin/provider-middleware-order': 'error',\n '@graphorin/tool-description-required': 'error',\n '@graphorin/tool-examples-recommended': 'warn',\n '@graphorin/tool-parameter-naming': 'warn',\n} as const;\n\n/**\n * PS-17: ship BOTH config shapes. `recommended` is the legacy `.eslintrc` form\n * (`plugins: ['@graphorin']`); `flat/recommended` is the ESLint 9+ flat-config\n * form that maps the namespace to the plugin object, so flat-config consumers\n * can `...plugin.configs['flat/recommended']` instead of hand-wiring ten rules.\n */\nconst plugin: {\n readonly meta: typeof meta;\n readonly rules: typeof rules;\n readonly configs: {\n readonly recommended: {\n readonly plugins: readonly string[];\n readonly rules: typeof RECOMMENDED_RULES;\n };\n readonly 'flat/recommended': {\n plugins: Record<string, unknown>;\n readonly rules: typeof RECOMMENDED_RULES;\n };\n };\n} = {\n meta,\n rules,\n configs: {\n recommended: { plugins: ['@graphorin'], rules: RECOMMENDED_RULES },\n // `plugins` is filled in below - the flat form maps the namespace to the\n // plugin object itself, which must exist first.\n 'flat/recommended': { plugins: {}, rules: RECOMMENDED_RULES },\n },\n};\nplugin.configs['flat/recommended'].plugins = { '@graphorin': plugin };\n\nexport const configs = plugin.configs;\n\nexport {\n AMBIGUOUS_PARAMETER_NAMES,\n type DiscoveredTool,\n discoverToolCallsInSource,\n gradeTool,\n type LintFinding,\n type LintFindingKind,\n PARAMETER_NAMING_OPT_OUT_TAGS,\n PLACEHOLDER_DESCRIPTIONS,\n runToolRules,\n type ToolGraderScore,\n} from './tool-discovery.js';\n\nexport default plugin;\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@graphorin/eslint-plugin",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "ESLint plugin for projects that build on Graphorin. Ships
|
|
3
|
+
"version": "0.6.0",
|
|
4
|
+
"description": "ESLint plugin for projects that build on Graphorin. Ships nine active rules: tool-definition quality (descriptions, examples, parameter naming), secret-handling discipline (no-secret-unwrap, no-secret-in-deps), network hygiene (no-implicit-network-call), provider middleware ordering, tool cancellation propagation, and workflow naming.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Oleksiy Stepurenko",
|
|
7
7
|
"homepage": "https://github.com/o-stepper/graphorin/tree/main/packages/eslint-plugin",
|
|
@@ -30,7 +30,8 @@
|
|
|
30
30
|
".": {
|
|
31
31
|
"types": "./dist/index.d.ts",
|
|
32
32
|
"import": "./dist/index.js"
|
|
33
|
-
}
|
|
33
|
+
},
|
|
34
|
+
"./package.json": "./package.json"
|
|
34
35
|
},
|
|
35
36
|
"files": [
|
|
36
37
|
"dist",
|