agentsys 5.8.6 → 5.9.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.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "agentsys",
3
- "description": "19 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, and contributor guidance",
4
- "version": "5.8.6",
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.0",
5
5
  "owner": {
6
6
  "name": "Avi Fenesh",
7
7
  "url": "https://github.com/avifenesh"
@@ -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
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentsys",
3
- "version": "5.8.6",
3
+ "version": "5.9.0",
4
4
  "description": "Professional-grade slash commands for Claude Code with cross-platform support",
5
5
  "keywords": [
6
6
  "workflow",
package/AGENTS.md CHANGED
@@ -152,7 +152,7 @@ agentsys # Run installer
152
152
  <agents>
153
153
  ## Agents
154
154
 
155
- 47 agents across 19 plugins. Key agents by model:
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,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
9
9
 
10
10
  ## [Unreleased]
11
11
 
12
+ ## [5.9.0] - 2026-04-25
13
+
14
+ ### Added
15
+ - **`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
16
+ - Marketplace plugin count 19 -> 20 in `.claude-plugin/marketplace.json` description, `scripts/plugins.txt`, and `site/content.json` stats.
17
+
12
18
  ## [5.8.6] - 2026-04-23
13
19
 
14
20
  ### Added
package/README.md CHANGED
@@ -19,7 +19,7 @@
19
19
  </p>
20
20
 
21
21
  <p align="center">
22
- <b>19 plugins · 49 agents · 41 skills (across all repos) · 30k lines of lib code · 3,507 tests · 5 platforms</b><br>
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 &amp; 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 - 19 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.
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
 
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  // Minimum binary version required by this version of agent-core
4
- const ANALYZER_MIN_VERSION = '0.4.0';
4
+ const ANALYZER_MIN_VERSION = '0.3.0';
5
5
 
6
6
  // Binary name
7
7
  const BINARY_NAME = 'agent-analyzer';
@@ -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 = truncate(match[1] || match[0], 100);
111
+ const plan = (match[1] || match[0]).slice(0, 100);
113
112
  result.plans.push(plan);
114
113
  }
115
114
  }
@@ -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: truncate(output, 500)
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 ? truncate(item.body.replace(/\n/g, ' '), 200) : ''
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 ? truncate(item.body.replace(/\n/g, ' '), 150) : ''
115
+ snippet: item.body ? item.body.slice(0, 150).replace(/\n/g, ' ').trim() + (item.body.length > 150 ? '...' : '') : ''
118
116
  };
119
117
  }
120
118
 
@@ -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
- // Code-point safe: uses spread operator to avoid splitting surrogate pairs (emoji, flags, CJK).
314
- if (maxLength < 0) return text;
315
- const chars = [...text];
316
- if (chars.length <= maxLength) return text;
317
- return chars.slice(0, maxLength - 3).join('') + '...';
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 = truncate(line.replace(/.*\bALWAYS\b\s*/i, ''), ACTION_COMPARISON_LENGTH);
653
+ action = line.replace(/.*\bALWAYS\b\s*/i, '').substring(0, ACTION_COMPARISON_LENGTH);
655
654
  } else {
656
- action = truncate(line.replace(/.*\b(?:NEVER|DO NOT)\b\s*/i, ''), ACTION_COMPARISON_LENGTH);
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: "${truncate(instruction, 50)}"`,
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: "${truncate(rule1, 40)}" vs "${truncate(rule2, 40)}"`,
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 = truncate(section.split('\n')[0].trim(), 50);
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 = truncate(lines[0], 30);
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 = truncate(cleanPart.trim().split('\n')[0], 50);
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(truncate(lines[i].trim(), 50));
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,