agentsys 5.8.6 → 5.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +15 -4
- package/.claude-plugin/plugin.json +1 -1
- package/AGENTS.md +1 -1
- package/CHANGELOG.md +11 -0
- package/README.md +2 -2
- package/lib/binary/version.js +1 -1
- package/lib/collectors/analyzer-queries.js +341 -0
- package/lib/collectors/documentation.js +1 -2
- package/lib/collectors/github.js +3 -5
- package/lib/collectors/index.js +12 -1
- package/lib/cross-platform/index.js +6 -5
- package/lib/enhance/cross-file-analyzer.js +2 -3
- package/lib/enhance/cross-file-patterns.js +2 -3
- package/lib/enhance/docs-patterns.js +4 -6
- package/lib/index.js +0 -2
- package/lib/package.json +0 -34
- package/lib/patterns/cli-enhancers.js +2 -6
- package/lib/patterns/pipeline.js +3 -7
- package/lib/patterns/slop-analyzers.js +1 -3
- package/lib/platform/detect-platform.js +1 -2
- package/lib/repo-intel/queries.js +186 -337
- package/lib/sources/policy-questions.js +3 -4
- package/lib/state/workflow-state.js +106 -173
- package/package.json +1 -1
- package/scripts/generate-docs.js +23 -11
- package/scripts/plugins.txt +1 -0
- package/site/content.json +4 -4
- package/site/index.html +5 -5
- package/site/ux-spec.md +7 -7
- package/lib/repo-intel/index.js +0 -21
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agentsys",
|
|
3
|
-
"description": "
|
|
4
|
-
"version": "5.
|
|
3
|
+
"description": "20 specialized plugins for AI workflow automation - task orchestration, PR workflow, slop detection, code review, drift detection, enhancement analysis, documentation sync, unified static analysis, perf investigations, topic research, agent config linting, cross-tool AI consultation, structured AI debate, workflow pattern learning, codebase onboarding, contributor guidance, and Zig language support",
|
|
4
|
+
"version": "5.9.1",
|
|
5
5
|
"owner": {
|
|
6
6
|
"name": "Avi Fenesh",
|
|
7
7
|
"url": "https://github.com/avifenesh"
|
|
@@ -160,8 +160,8 @@
|
|
|
160
160
|
"source": "url",
|
|
161
161
|
"url": "https://github.com/agent-sh/agnix.git"
|
|
162
162
|
},
|
|
163
|
-
"description": "Lint agent configuration files (SKILL.md, CLAUDE.md, hooks, MCP) against
|
|
164
|
-
"version": "1.
|
|
163
|
+
"description": "Lint agent configuration files (SKILL.md, CLAUDE.md, hooks, MCP) against 414 rules across 10+ AI tools",
|
|
164
|
+
"version": "1.1.0",
|
|
165
165
|
"category": "development",
|
|
166
166
|
"homepage": "https://github.com/agent-sh/agnix"
|
|
167
167
|
},
|
|
@@ -230,6 +230,17 @@
|
|
|
230
230
|
"version": "0.1.0",
|
|
231
231
|
"category": "productivity",
|
|
232
232
|
"homepage": "https://github.com/agent-sh/can-i-help"
|
|
233
|
+
},
|
|
234
|
+
{
|
|
235
|
+
"name": "zig-lsp",
|
|
236
|
+
"source": {
|
|
237
|
+
"source": "url",
|
|
238
|
+
"url": "https://github.com/agent-sh/zig-lsp.git"
|
|
239
|
+
},
|
|
240
|
+
"description": "Zig language server for Claude Code via ZLS - automatic diagnostics after every edit, jump-to-definition, find-references, and hover. Requires zls in PATH",
|
|
241
|
+
"version": "0.1.0",
|
|
242
|
+
"category": "development",
|
|
243
|
+
"homepage": "https://github.com/agent-sh/zig-lsp"
|
|
233
244
|
}
|
|
234
245
|
]
|
|
235
246
|
}
|
package/AGENTS.md
CHANGED
|
@@ -152,7 +152,7 @@ agentsys # Run installer
|
|
|
152
152
|
<agents>
|
|
153
153
|
## Agents
|
|
154
154
|
|
|
155
|
-
|
|
155
|
+
49 agents across 20 plugins (18 have agents; gate-and-ship is commands-only; zig-lsp is config-only with no commands or agents). Key agents by model:
|
|
156
156
|
|
|
157
157
|
| Model | Agents | Use Case |
|
|
158
158
|
|-------|--------|----------|
|
package/CHANGELOG.md
CHANGED
|
@@ -9,6 +9,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
9
9
|
|
|
10
10
|
## [Unreleased]
|
|
11
11
|
|
|
12
|
+
## [5.9.1] - 2026-04-26
|
|
13
|
+
|
|
14
|
+
### Changed
|
|
15
|
+
- **agnix marketplace entry** - bumped from 1.0.0 to 1.1.0 and updated description from "385 rules" to "414 rules" to reflect agnix v0.22.0 (414 validation rules, additive `schema --fix` and `tools check/detect` subcommands). Updated `site/content.json` version highlight to match.
|
|
16
|
+
|
|
17
|
+
## [5.9.0] - 2026-04-25
|
|
18
|
+
|
|
19
|
+
### Added
|
|
20
|
+
- **`zig-lsp` plugin** - Zig language server (ZLS) integration for Claude Code's `LSP` tool. Maps `.zig` and `.zon` to language `zig`; enables `enable_build_on_save` so post-edit diagnostics surface real type errors (not just parser errors); 30 s startup timeout, restart-on-crash with cap. Plugin is config-only - no slash commands, no agents, no skills - the harness's built-in `LSP` tool dispatches automatically once `zls` is on `PATH`. Marketplace entry under category `development`. Source: https://github.com/agent-sh/zig-lsp
|
|
21
|
+
- Marketplace plugin count 19 -> 20 in `.claude-plugin/marketplace.json` description, `scripts/plugins.txt`, and `site/content.json` stats.
|
|
22
|
+
|
|
12
23
|
## [5.8.6] - 2026-04-23
|
|
13
24
|
|
|
14
25
|
### Added
|
package/README.md
CHANGED
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
</p>
|
|
20
20
|
|
|
21
21
|
<p align="center">
|
|
22
|
-
<b>
|
|
22
|
+
<b>20 plugins · 49 agents · 41 skills (across all repos) · 30k lines of lib code · 3,507 tests · 5 platforms</b><br>
|
|
23
23
|
<em>Plugins distributed as standalone repos under <a href="https://github.com/agent-sh">agent-sh</a> org - agentsys is the marketplace & installer</em>
|
|
24
24
|
</p>
|
|
25
25
|
|
|
@@ -45,7 +45,7 @@ AI models can write code. That's not the hard part anymore. The hard part is eve
|
|
|
45
45
|
|
|
46
46
|
## What This Is
|
|
47
47
|
|
|
48
|
-
An agent orchestration system -
|
|
48
|
+
An agent orchestration system - 20 plugins, 49 agents (39 file-based + 10 role-based specialists in audit-project), and 41 skills that compose into structured pipelines for software development. Each plugin lives in its own standalone repo under the [agent-sh](https://github.com/agent-sh) org. agentsys is the marketplace and installer that ties them together.
|
|
49
49
|
|
|
50
50
|
Each agent has a single responsibility, a specific model assignment, and defined inputs/outputs. Pipelines enforce phase gates so agents can't skip steps. State persists across sessions so work survives interruptions.
|
|
51
51
|
|
package/lib/binary/version.js
CHANGED
|
@@ -0,0 +1,341 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Analyzer Queries Collector
|
|
3
|
+
*
|
|
4
|
+
* Runs the agent-analyzer `repo-intel query ...` commands we consume and
|
|
5
|
+
* returns them as a single indexed bundle. Replaces the per-file regex +
|
|
6
|
+
* git-show path in docs-patterns.js with the analyzer's symbol table and
|
|
7
|
+
* slop-fix outputs, both of which are deterministic and cross-language.
|
|
8
|
+
*
|
|
9
|
+
* All queries degrade gracefully: if the binary is missing or the map
|
|
10
|
+
* file isn't present, each field is `null` and the reason is recorded on
|
|
11
|
+
* the bundle. Callers should treat `null` as "signal unavailable" and
|
|
12
|
+
* skip the corresponding checks rather than invent fallback data.
|
|
13
|
+
*
|
|
14
|
+
* @module lib/collectors/analyzer-queries
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
'use strict';
|
|
18
|
+
|
|
19
|
+
const fs = require('fs');
|
|
20
|
+
const path = require('path');
|
|
21
|
+
|
|
22
|
+
const DEFAULT_OPTIONS = {
|
|
23
|
+
cwd: process.cwd()
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
// Docs that `doc-drift` flags as uncoupled by design. These are never
|
|
27
|
+
// expected to co-change with code (Docusaurus snapshots, test fixtures,
|
|
28
|
+
// generated rule pages, CHANGELOG is append-only). Matching these is
|
|
29
|
+
// not evidence of drift; they're just not part of the live doc surface.
|
|
30
|
+
const DEFAULT_DOC_DRIFT_IGNORE = [
|
|
31
|
+
/(^|\/)versioned_docs\//,
|
|
32
|
+
/(^|\/)versioned_sidebars\//,
|
|
33
|
+
/(^|\/)tests\/fixtures\//,
|
|
34
|
+
/(^|\/)__fixtures__\//,
|
|
35
|
+
/(^|\/)generated\//,
|
|
36
|
+
/\.generated\.md$/,
|
|
37
|
+
/(^|\/)CHANGELOG\.md$/i,
|
|
38
|
+
/(^|\/)node_modules\//,
|
|
39
|
+
/(^|\/)target\//,
|
|
40
|
+
/(^|\/)dist\//,
|
|
41
|
+
/(^|\/)build\//
|
|
42
|
+
];
|
|
43
|
+
|
|
44
|
+
function resolveStateDir(cwd) {
|
|
45
|
+
for (const dir of ['.claude', '.opencode', '.codex']) {
|
|
46
|
+
if (fs.existsSync(path.join(cwd, dir))) {
|
|
47
|
+
return dir;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return '.claude';
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function resolveMapFile(cwd) {
|
|
54
|
+
return path.join(cwd, resolveStateDir(cwd), 'repo-intel.json');
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Get the agent-analyzer binary runner. On main the binary is vendored
|
|
59
|
+
* at `lib/binary`; once the agentsys resolver PR lands this will switch
|
|
60
|
+
* to `../agentsys`. Returns `null` when unavailable — callers should
|
|
61
|
+
* treat the bundle as empty rather than erroring out.
|
|
62
|
+
*/
|
|
63
|
+
function getBinary() {
|
|
64
|
+
try {
|
|
65
|
+
// Prefer the agentsys resolver when present (post PR #21)
|
|
66
|
+
const { binary } = require('../agentsys').get();
|
|
67
|
+
if (binary) return binary;
|
|
68
|
+
} catch {
|
|
69
|
+
// agentsys resolver not available; fall through to vendored binary
|
|
70
|
+
}
|
|
71
|
+
try {
|
|
72
|
+
return require('../binary');
|
|
73
|
+
} catch {
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function runJson(binary, args) {
|
|
79
|
+
try {
|
|
80
|
+
const out = binary.runAnalyzer(args);
|
|
81
|
+
return JSON.parse(out);
|
|
82
|
+
} catch {
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Normalize a repo-relative path: backslashes to forward slashes,
|
|
89
|
+
* empty/nullish input returns `''`. Centralized so every index-key
|
|
90
|
+
* construction in this module uses the same shape.
|
|
91
|
+
*/
|
|
92
|
+
function normalizePath(p) {
|
|
93
|
+
return (p || '').replace(/\\/g, '/');
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Coerce a parsed analyzer result into an array. Nearly every query
|
|
98
|
+
* returns a JSON array, but if the analyzer hands back a non-array
|
|
99
|
+
* (error object, bare null, malformed output) we must not let the
|
|
100
|
+
* value flow into a for-of loop where it would throw. Returns `[]`
|
|
101
|
+
* for anything non-iterable.
|
|
102
|
+
*/
|
|
103
|
+
function asArray(v) {
|
|
104
|
+
return Array.isArray(v) ? v : [];
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Run all analyzer queries sync-docs consumes and return an indexed
|
|
109
|
+
* bundle. See `DEFAULT_DOC_DRIFT_IGNORE` for the docs filtered from
|
|
110
|
+
* `docDrift`. A raw `docDriftAll` field is included so callers that
|
|
111
|
+
* need the unfiltered list (e.g. explicit `--include-versioned-docs`)
|
|
112
|
+
* can recover it without a second binary call.
|
|
113
|
+
*
|
|
114
|
+
* @param {Object} options
|
|
115
|
+
* @param {string} [options.cwd]
|
|
116
|
+
* @param {RegExp[]} [options.docDriftIgnore] - Override default ignore globs
|
|
117
|
+
* @param {number} [options.docDriftTop=50]
|
|
118
|
+
* @param {number} [options.staleDocsTop=500]
|
|
119
|
+
* @returns {{
|
|
120
|
+
* available: boolean,
|
|
121
|
+
* reason: string|null,
|
|
122
|
+
* mapFile: string,
|
|
123
|
+
* staleDocs: Array|null,
|
|
124
|
+
* staleDocsByKey: Map<string, Object>|null,
|
|
125
|
+
* docDrift: Array|null,
|
|
126
|
+
* docDriftAll: Array|null,
|
|
127
|
+
* entryPoints: Array|null,
|
|
128
|
+
* entryPointSet: Set<string>|null,
|
|
129
|
+
* slopFixes: Array|null,
|
|
130
|
+
* orphanExports: Array|null,
|
|
131
|
+
* passthroughWrappers: Array|null,
|
|
132
|
+
* alwaysTrueConditions: Array|null,
|
|
133
|
+
* commentedOutCode: Array|null,
|
|
134
|
+
* staleSuppressions: Array|null
|
|
135
|
+
* }}
|
|
136
|
+
*/
|
|
137
|
+
function collect(options = {}) {
|
|
138
|
+
const opts = { ...DEFAULT_OPTIONS, ...options };
|
|
139
|
+
const cwd = opts.cwd;
|
|
140
|
+
const mapFile = resolveMapFile(cwd);
|
|
141
|
+
|
|
142
|
+
// Return shape is identical whether analyzer succeeded or not; the
|
|
143
|
+
// `available` flag + `reason` tell callers which it is. Keeping the
|
|
144
|
+
// shape stable means consumers can use `?.` uniformly without having
|
|
145
|
+
// to guard per-field.
|
|
146
|
+
const empty = {
|
|
147
|
+
available: false,
|
|
148
|
+
reason: null,
|
|
149
|
+
queryErrors: [],
|
|
150
|
+
mapFile,
|
|
151
|
+
staleDocs: null,
|
|
152
|
+
staleDocsByKey: null,
|
|
153
|
+
staleDocsByDoc: null,
|
|
154
|
+
docDrift: null,
|
|
155
|
+
docDriftAll: null,
|
|
156
|
+
entryPoints: null,
|
|
157
|
+
entryPointSet: null,
|
|
158
|
+
entryPointSymbols: null,
|
|
159
|
+
slopFixes: null,
|
|
160
|
+
orphanExports: null,
|
|
161
|
+
passthroughWrappers: null,
|
|
162
|
+
alwaysTrueConditions: null,
|
|
163
|
+
commentedOutCode: null,
|
|
164
|
+
staleSuppressions: null
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
const binary = getBinary();
|
|
168
|
+
if (!binary) {
|
|
169
|
+
return { ...empty, reason: 'analyzer-binary-unavailable' };
|
|
170
|
+
}
|
|
171
|
+
if (!fs.existsSync(mapFile)) {
|
|
172
|
+
return { ...empty, reason: 'repo-intel-map-missing' };
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const staleTop = opts.staleDocsTop ?? 500;
|
|
176
|
+
const driftTop = opts.docDriftTop ?? 50;
|
|
177
|
+
|
|
178
|
+
// Query failure tracking: if runJson returns null for any query
|
|
179
|
+
// (binary exec failure, JSON parse failure), we record it in
|
|
180
|
+
// `queryErrors` so the bundle can signal partial-success rather
|
|
181
|
+
// than pretending the empty result was "successfully empty".
|
|
182
|
+
// Callers who only care about happy-path use the category arrays
|
|
183
|
+
// directly; callers who want to reason about reliability can
|
|
184
|
+
// check `queryErrors`.
|
|
185
|
+
const queryErrors = [];
|
|
186
|
+
const runSafe = (queryName, args) => {
|
|
187
|
+
const raw = runJson(binary, args);
|
|
188
|
+
if (raw === null) {
|
|
189
|
+
queryErrors.push(queryName);
|
|
190
|
+
}
|
|
191
|
+
return raw;
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
const staleDocs = asArray(runSafe('stale-docs', [
|
|
195
|
+
'repo-intel', 'query', 'stale-docs',
|
|
196
|
+
'--top', String(staleTop),
|
|
197
|
+
'--map-file', mapFile,
|
|
198
|
+
cwd
|
|
199
|
+
]));
|
|
200
|
+
|
|
201
|
+
const docDriftAll = asArray(runSafe('doc-drift', [
|
|
202
|
+
'repo-intel', 'query', 'doc-drift',
|
|
203
|
+
'--top', String(driftTop),
|
|
204
|
+
'--map-file', mapFile,
|
|
205
|
+
cwd
|
|
206
|
+
]));
|
|
207
|
+
|
|
208
|
+
const entryPoints = asArray(runSafe('entry-points', [
|
|
209
|
+
'repo-intel', 'query', 'entry-points',
|
|
210
|
+
'--map-file', mapFile,
|
|
211
|
+
cwd
|
|
212
|
+
]));
|
|
213
|
+
|
|
214
|
+
const slopRaw = runSafe('slop-fixes', [
|
|
215
|
+
'repo-intel', 'query', 'slop-fixes',
|
|
216
|
+
'--map-file', mapFile,
|
|
217
|
+
cwd
|
|
218
|
+
]);
|
|
219
|
+
// slop-fixes returns either a bare array or `{fixes, by_file}`.
|
|
220
|
+
const slopFixes = Array.isArray(slopRaw)
|
|
221
|
+
? slopRaw
|
|
222
|
+
: asArray(slopRaw?.fixes);
|
|
223
|
+
|
|
224
|
+
// Index stale-docs by "doc:line:reference" for O(1) lookup during
|
|
225
|
+
// per-file issue analysis. Also keep a second index by doc path.
|
|
226
|
+
// Both keys normalize backslashes to forward slashes so Windows-path
|
|
227
|
+
// output from the analyzer matches the normalized paths call sites
|
|
228
|
+
// construct in docs-patterns.js.
|
|
229
|
+
const staleDocsByKey = new Map();
|
|
230
|
+
const staleDocsByDoc = new Map();
|
|
231
|
+
for (const entry of staleDocs) {
|
|
232
|
+
const normalizedDoc = normalizePath(entry.doc);
|
|
233
|
+
entry.doc = normalizedDoc;
|
|
234
|
+
const key = `${normalizedDoc}:${entry.line}:${entry.reference}`;
|
|
235
|
+
staleDocsByKey.set(key, entry);
|
|
236
|
+
if (!staleDocsByDoc.has(normalizedDoc)) {
|
|
237
|
+
staleDocsByDoc.set(normalizedDoc, []);
|
|
238
|
+
}
|
|
239
|
+
staleDocsByDoc.get(normalizedDoc).push(entry);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// Entry-point sets are keyed on normalized paths so lookups from
|
|
243
|
+
// `findUndocumentedExports` (which normalizes the file path before
|
|
244
|
+
// lookup) succeed on Windows. `entryPointSymbols` is `path:name`; it
|
|
245
|
+
// MUST use the same normalization as the set to keep lookups
|
|
246
|
+
// consistent.
|
|
247
|
+
const entryPointSet = new Set();
|
|
248
|
+
const entryPointSymbols = new Set();
|
|
249
|
+
for (const ep of entryPoints) {
|
|
250
|
+
const normalizedPath = normalizePath(ep.path);
|
|
251
|
+
if (normalizedPath) entryPointSet.add(normalizedPath);
|
|
252
|
+
if (ep.name && normalizedPath) {
|
|
253
|
+
entryPointSymbols.add(`${normalizedPath}:${ep.name}`);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// Filter doc-drift using ignore globs. The unfiltered list stays on
|
|
258
|
+
// `docDriftAll` so callers can opt back into it.
|
|
259
|
+
const ignore = opts.docDriftIgnore || DEFAULT_DOC_DRIFT_IGNORE;
|
|
260
|
+
const docDrift = docDriftAll.filter((entry) => {
|
|
261
|
+
const p = normalizePath(entry.path);
|
|
262
|
+
return !ignore.some((re) => re.test(p));
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
// Partition slop-fixes by category so consumers don't re-scan.
|
|
266
|
+
// Single-pass loop - each category string lives in exactly one
|
|
267
|
+
// place (SLOP_CATEGORY_MAP), and we walk the fixes once instead
|
|
268
|
+
// of once per category. Covers the four categories plus
|
|
269
|
+
// staleSuppression added in agent-analyzer v0.7.
|
|
270
|
+
const SLOP_CATEGORY_MAP = {
|
|
271
|
+
'orphan-export': 'orphanExports',
|
|
272
|
+
'passthrough-wrapper': 'passthroughWrappers',
|
|
273
|
+
'always-true-condition': 'alwaysTrueConditions',
|
|
274
|
+
'commented-out-code': 'commentedOutCode',
|
|
275
|
+
'stale-suppression': 'staleSuppressions'
|
|
276
|
+
};
|
|
277
|
+
const orphanExports = [];
|
|
278
|
+
const passthroughWrappers = [];
|
|
279
|
+
const alwaysTrueConditions = [];
|
|
280
|
+
const commentedOutCode = [];
|
|
281
|
+
const staleSuppressions = [];
|
|
282
|
+
const bucketByKey = {
|
|
283
|
+
orphanExports,
|
|
284
|
+
passthroughWrappers,
|
|
285
|
+
alwaysTrueConditions,
|
|
286
|
+
commentedOutCode,
|
|
287
|
+
staleSuppressions
|
|
288
|
+
};
|
|
289
|
+
for (const fix of slopFixes) {
|
|
290
|
+
const key = SLOP_CATEGORY_MAP[fix.category];
|
|
291
|
+
if (key) bucketByKey[key].push(fix);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
// `available` is true when at least one query succeeded and we
|
|
295
|
+
// have a usable bundle. `queryErrors` lists which specific queries
|
|
296
|
+
// failed so callers can distinguish "binary missing entirely"
|
|
297
|
+
// (empty arrays from the unavailable branch, reason set) from
|
|
298
|
+
// "some queries succeeded, some failed" (partial bundle).
|
|
299
|
+
const available = queryErrors.length < 4;
|
|
300
|
+
return {
|
|
301
|
+
available,
|
|
302
|
+
reason: available ? null : 'all-queries-failed',
|
|
303
|
+
queryErrors,
|
|
304
|
+
mapFile,
|
|
305
|
+
staleDocs,
|
|
306
|
+
staleDocsByKey,
|
|
307
|
+
staleDocsByDoc,
|
|
308
|
+
docDrift,
|
|
309
|
+
docDriftAll,
|
|
310
|
+
entryPoints,
|
|
311
|
+
entryPointSet,
|
|
312
|
+
entryPointSymbols,
|
|
313
|
+
slopFixes,
|
|
314
|
+
orphanExports,
|
|
315
|
+
passthroughWrappers,
|
|
316
|
+
alwaysTrueConditions,
|
|
317
|
+
commentedOutCode,
|
|
318
|
+
staleSuppressions
|
|
319
|
+
};
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
/**
|
|
323
|
+
* Check whether a given (path, symbol) pair is an entry point. Used by
|
|
324
|
+
* undocumented-export filtering to skip `main()`, CLI commands, and
|
|
325
|
+
* framework-loaded config files that don't need prose docs.
|
|
326
|
+
*/
|
|
327
|
+
function isEntryPointSymbol(bundle, filePath, symbolName) {
|
|
328
|
+
if (!bundle?.entryPointSymbols) return false;
|
|
329
|
+
const normalized = normalizePath(filePath);
|
|
330
|
+
return bundle.entryPointSymbols.has(`${normalized}:${symbolName}`)
|
|
331
|
+
|| bundle.entryPointSet.has(normalized);
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
module.exports = {
|
|
335
|
+
DEFAULT_OPTIONS,
|
|
336
|
+
DEFAULT_DOC_DRIFT_IGNORE,
|
|
337
|
+
collect,
|
|
338
|
+
isEntryPointSymbol,
|
|
339
|
+
resolveMapFile,
|
|
340
|
+
resolveStateDir
|
|
341
|
+
};
|
|
@@ -8,7 +8,6 @@
|
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
'use strict';
|
|
11
|
-
const { truncate } = require('../cross-platform');
|
|
12
11
|
|
|
13
12
|
const fs = require('fs');
|
|
14
13
|
const path = require('path');
|
|
@@ -109,7 +108,7 @@ function extractPlans(result, content) {
|
|
|
109
108
|
for (const pattern of planPatterns) {
|
|
110
109
|
let match;
|
|
111
110
|
while ((match = pattern.exec(content)) !== null && result.plans.length < 15) {
|
|
112
|
-
const plan =
|
|
111
|
+
const plan = (match[1] || match[0]).slice(0, 100);
|
|
113
112
|
result.plans.push(plan);
|
|
114
113
|
}
|
|
115
114
|
}
|
package/lib/collectors/github.js
CHANGED
|
@@ -9,8 +9,6 @@
|
|
|
9
9
|
|
|
10
10
|
'use strict';
|
|
11
11
|
|
|
12
|
-
const { truncate } = require('../cross-platform');
|
|
13
|
-
|
|
14
12
|
const { execFileSync } = require('child_process');
|
|
15
13
|
|
|
16
14
|
const DEFAULT_OPTIONS = {
|
|
@@ -49,7 +47,7 @@ function execGhWithResult(args, options = {}) {
|
|
|
49
47
|
error: {
|
|
50
48
|
type: 'parse',
|
|
51
49
|
message: `Failed to parse gh output as JSON: ${error.message}`,
|
|
52
|
-
raw:
|
|
50
|
+
raw: output.slice(0, 500)
|
|
53
51
|
}
|
|
54
52
|
};
|
|
55
53
|
}
|
|
@@ -96,7 +94,7 @@ function summarizeIssue(item) {
|
|
|
96
94
|
milestone: item.milestone?.title || item.milestone || null,
|
|
97
95
|
createdAt: item.createdAt,
|
|
98
96
|
updatedAt: item.updatedAt,
|
|
99
|
-
snippet: item.body ?
|
|
97
|
+
snippet: item.body ? item.body.slice(0, 200).replace(/\n/g, ' ').trim() + (item.body.length > 200 ? '...' : '') : ''
|
|
100
98
|
};
|
|
101
99
|
}
|
|
102
100
|
|
|
@@ -114,7 +112,7 @@ function summarizePR(item) {
|
|
|
114
112
|
createdAt: item.createdAt,
|
|
115
113
|
updatedAt: item.updatedAt,
|
|
116
114
|
files: item.files || [],
|
|
117
|
-
snippet: item.body ?
|
|
115
|
+
snippet: item.body ? item.body.slice(0, 150).replace(/\n/g, ' ').trim() + (item.body.length > 150 ? '...' : '') : ''
|
|
118
116
|
};
|
|
119
117
|
}
|
|
120
118
|
|
package/lib/collectors/index.js
CHANGED
|
@@ -14,6 +14,7 @@ const documentation = require('./documentation');
|
|
|
14
14
|
const codebase = require('./codebase');
|
|
15
15
|
const docsPatterns = require('./docs-patterns');
|
|
16
16
|
const git = require('./git');
|
|
17
|
+
const analyzerQueries = require('./analyzer-queries');
|
|
17
18
|
|
|
18
19
|
const DEFAULT_OPTIONS = {
|
|
19
20
|
collectors: ['github', 'docs', 'code'],
|
|
@@ -52,9 +53,18 @@ function collect(options = {}) {
|
|
|
52
53
|
docs: null,
|
|
53
54
|
code: null,
|
|
54
55
|
docsPatterns: null,
|
|
55
|
-
git: null
|
|
56
|
+
git: null,
|
|
57
|
+
analyzer: null
|
|
56
58
|
};
|
|
57
59
|
|
|
60
|
+
// Analyzer signals are batched first so downstream collectors can
|
|
61
|
+
// consume the bundle via `opts.analyzer` (docs-patterns reads it
|
|
62
|
+
// for stale-docs lookup + entry-point filtering).
|
|
63
|
+
if (collectors.includes('analyzer')) {
|
|
64
|
+
data.analyzer = analyzerQueries.collect(opts);
|
|
65
|
+
opts.analyzer = data.analyzer;
|
|
66
|
+
}
|
|
67
|
+
|
|
58
68
|
// Collect from each enabled collector
|
|
59
69
|
if (collectors.includes('github')) {
|
|
60
70
|
data.github = github.scanGitHubState(opts);
|
|
@@ -110,6 +120,7 @@ module.exports = {
|
|
|
110
120
|
codebase,
|
|
111
121
|
docsPatterns,
|
|
112
122
|
git,
|
|
123
|
+
analyzerQueries,
|
|
113
124
|
|
|
114
125
|
// Re-export commonly used functions for convenience
|
|
115
126
|
scanGitHubState: github.scanGitHubState,
|
|
@@ -310,11 +310,12 @@ function formatSection(title, content) {
|
|
|
310
310
|
* @returns {string} Truncated text
|
|
311
311
|
*/
|
|
312
312
|
function truncate(text, maxLength) {
|
|
313
|
-
//
|
|
314
|
-
if (maxLength
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
313
|
+
// Negative or zero maxLength: return original text unchanged
|
|
314
|
+
if (maxLength <= 0) return text;
|
|
315
|
+
// Use Array.from to iterate over code points (handles emoji/surrogate pairs)
|
|
316
|
+
const codePoints = Array.from(text);
|
|
317
|
+
if (codePoints.length <= maxLength) return text;
|
|
318
|
+
return codePoints.slice(0, maxLength - 3).join('') + '...';
|
|
318
319
|
}
|
|
319
320
|
|
|
320
321
|
/**
|
|
@@ -12,7 +12,6 @@ const fs = require('fs');
|
|
|
12
12
|
const path = require('path');
|
|
13
13
|
const { parseMarkdownFrontmatter } = require('./agent-analyzer');
|
|
14
14
|
const { crossFilePatterns, loadKnownTools } = require('./cross-file-patterns');
|
|
15
|
-
const { truncate } = require('../cross-platform');
|
|
16
15
|
|
|
17
16
|
// ============================================
|
|
18
17
|
// CONSTANTS
|
|
@@ -651,9 +650,9 @@ function analyzePromptConsistency(agents) {
|
|
|
651
650
|
// Extract action keywords
|
|
652
651
|
let action;
|
|
653
652
|
if (isAlways) {
|
|
654
|
-
action =
|
|
653
|
+
action = line.replace(/.*\bALWAYS\b\s*/i, '').substring(0, ACTION_COMPARISON_LENGTH);
|
|
655
654
|
} else {
|
|
656
|
-
action =
|
|
655
|
+
action = line.replace(/.*\b(?:NEVER|DO NOT)\b\s*/i, '').substring(0, ACTION_COMPARISON_LENGTH);
|
|
657
656
|
}
|
|
658
657
|
|
|
659
658
|
// Extract significant keywords from action
|
|
@@ -11,7 +11,6 @@
|
|
|
11
11
|
const fs = require('fs');
|
|
12
12
|
const path = require('path');
|
|
13
13
|
const { getStateDir, getPlatformName } = require('../platform/state-dir');
|
|
14
|
-
const { truncate } = require('../cross-platform');
|
|
15
14
|
|
|
16
15
|
/**
|
|
17
16
|
* Platform-specific default tools
|
|
@@ -215,7 +214,7 @@ const crossFilePatterns = {
|
|
|
215
214
|
if (!instruction || !files || files.length < 2) return null;
|
|
216
215
|
|
|
217
216
|
return {
|
|
218
|
-
issue: `Duplicate instruction found in ${files.length} files: "${
|
|
217
|
+
issue: `Duplicate instruction found in ${files.length} files: "${instruction.substring(0, 50)}..."`,
|
|
219
218
|
fix: `Extract shared instruction to a common include or ensure intentional duplication`
|
|
220
219
|
};
|
|
221
220
|
}
|
|
@@ -235,7 +234,7 @@ const crossFilePatterns = {
|
|
|
235
234
|
if (!rule1 || !rule2) return null;
|
|
236
235
|
|
|
237
236
|
return {
|
|
238
|
-
issue: `Contradictory rules: "${
|
|
237
|
+
issue: `Contradictory rules: "${rule1.substring(0, 40)}..." vs "${rule2.substring(0, 40)}..."`,
|
|
239
238
|
fix: `Resolve conflict between ${file1} and ${file2}`
|
|
240
239
|
};
|
|
241
240
|
}
|
|
@@ -4,8 +4,6 @@
|
|
|
4
4
|
* @license MIT
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
const { truncate } = require('../cross-platform');
|
|
8
|
-
|
|
9
7
|
function estimateTokens(text) {
|
|
10
8
|
if (!text || typeof text !== 'string') return 0;
|
|
11
9
|
return Math.ceil(text.length / 4);
|
|
@@ -161,7 +159,7 @@ const docsPatterns = {
|
|
|
161
159
|
const tokens = estimateTokens(section);
|
|
162
160
|
if (tokens > 1000) {
|
|
163
161
|
// Get section title (first line)
|
|
164
|
-
const title =
|
|
162
|
+
const title = section.split('\n')[0].trim().slice(0, 50);
|
|
165
163
|
longSections.push({ title, tokens });
|
|
166
164
|
}
|
|
167
165
|
}
|
|
@@ -373,7 +371,7 @@ const docsPatterns = {
|
|
|
373
371
|
|
|
374
372
|
// Check if starts with dangling reference
|
|
375
373
|
if (/^(?:It|This|These|Those|They|The above|As mentioned)\s/i.test(firstLine)) {
|
|
376
|
-
const title =
|
|
374
|
+
const title = lines[0].slice(0, 30);
|
|
377
375
|
issues.push(title);
|
|
378
376
|
}
|
|
379
377
|
}
|
|
@@ -463,7 +461,7 @@ const docsPatterns = {
|
|
|
463
461
|
const tokens = estimateTokens(cleanPart);
|
|
464
462
|
|
|
465
463
|
if (tokens > 500) {
|
|
466
|
-
const preview =
|
|
464
|
+
const preview = cleanPart.trim().split('\n')[0].slice(0, 50);
|
|
467
465
|
longBlocks.push({ tokens, preview });
|
|
468
466
|
}
|
|
469
467
|
}
|
|
@@ -504,7 +502,7 @@ const docsPatterns = {
|
|
|
504
502
|
for (let i = lateThreshold; i < totalLines; i++) {
|
|
505
503
|
for (const pattern of criticalKeywords) {
|
|
506
504
|
if (pattern.test(lines[i])) {
|
|
507
|
-
lateImportantLines.push(
|
|
505
|
+
lateImportantLines.push(lines[i].trim().slice(0, 50));
|
|
508
506
|
break;
|
|
509
507
|
}
|
|
510
508
|
}
|
package/lib/index.js
CHANGED
|
@@ -30,7 +30,6 @@ const perf = require('./perf');
|
|
|
30
30
|
const collectors = require('./collectors');
|
|
31
31
|
const discoveryModule = require('./discovery');
|
|
32
32
|
const binary = require('./binary');
|
|
33
|
-
const repoIntel = require('./repo-intel');
|
|
34
33
|
|
|
35
34
|
/**
|
|
36
35
|
* Platform detection and verification utilities
|
|
@@ -256,7 +255,6 @@ module.exports = {
|
|
|
256
255
|
collectors,
|
|
257
256
|
discovery,
|
|
258
257
|
binary,
|
|
259
|
-
repoIntel,
|
|
260
258
|
|
|
261
259
|
// Direct module access for backward compatibility
|
|
262
260
|
detectPlatform,
|