agentsys 5.14.0 → 6.0.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.
Files changed (39) hide show
  1. package/.claude-plugin/marketplace.json +1 -27
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/.codex-plugin/plugin.json +2 -3
  4. package/AGENTS.md +4 -6
  5. package/CHANGELOG.md +13 -0
  6. package/README.md +5 -115
  7. package/lib/binary/index.js +8 -2
  8. package/lib/binary/shared-helpers.js +160 -0
  9. package/lib/collectors/codebase.js +7 -2
  10. package/lib/collectors/documentation.js +8 -2
  11. package/lib/enhance/agent-patterns.js +17 -4
  12. package/lib/enhance/auto-suppression.js +19 -7
  13. package/lib/enhance/cross-file-analyzer.js +11 -4
  14. package/lib/enhance/docs-patterns.js +6 -2
  15. package/lib/enhance/fixer.js +22 -5
  16. package/lib/enhance/skill-patterns.js +5 -5
  17. package/lib/index.js +2 -0
  18. package/lib/repo-intel/cache.js +171 -0
  19. package/lib/repo-intel/converter.js +130 -0
  20. package/lib/repo-intel/embed/binary.js +242 -0
  21. package/lib/repo-intel/embed/index.js +26 -0
  22. package/lib/repo-intel/embed/orchestrator.js +239 -0
  23. package/lib/repo-intel/embed/preference.js +136 -0
  24. package/lib/repo-intel/enrich.js +198 -0
  25. package/lib/repo-intel/index.js +370 -0
  26. package/lib/repo-intel/installer.js +78 -0
  27. package/lib/repo-intel/queries.js +213 -13
  28. package/lib/repo-intel/updater.js +104 -0
  29. package/lib/repo-map/index.js +19 -254
  30. package/package.json +1 -1
  31. package/scripts/generate-docs.js +2 -13
  32. package/scripts/plugins.txt +0 -2
  33. package/site/assets/js/main.js +5 -13
  34. package/site/content.json +7 -24
  35. package/site/index.html +26 -74
  36. package/site/ux-spec.md +6 -6
  37. package/.kiro/agents/web-session.json +0 -12
  38. package/.kiro/skills/web-auth/SKILL.md +0 -177
  39. package/.kiro/skills/web-browse/SKILL.md +0 -516
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "agentsys",
3
3
  "description": "26 specialized plugins for AI workflow automation - task orchestration, PR workflow, slop detection, code review, drift detection, enhancement analysis, documentation sync, unified static analysis, durable memory, negative behavior memory, skill and system prompt curation, perf investigations, topic research, agent config linting, cross-tool AI consultation, structured AI debate, workflow pattern learning, codebase onboarding, contributor guidance, Zig language support, Mojo language support, and Ada/SPARK language support",
4
- "version": "5.14.0",
4
+ "version": "6.0.0",
5
5
  "owner": {
6
6
  "name": "Avi Fenesh",
7
7
  "url": "https://github.com/avifenesh"
@@ -198,19 +198,6 @@
198
198
  "category": "productivity",
199
199
  "homepage": "https://github.com/agent-sh/learn"
200
200
  },
201
- {
202
- "name": "axiom",
203
- "source": {
204
- "source": "url",
205
- "url": "https://github.com/agent-sh/axiom.git",
206
- "ref": "v0.6.2",
207
- "commit": "e3f3fabfcb19c140a38cbd5c5129ac69ca50b359"
208
- },
209
- "description": "Personal agent-native knowledge base: load thin context, query durable memories, create project scaffolds, and propose approved records",
210
- "version": "0.6.2",
211
- "category": "productivity",
212
- "homepage": "https://github.com/agent-sh/axiom"
213
- },
214
201
  {
215
202
  "name": "banthis",
216
203
  "source": {
@@ -262,19 +249,6 @@
262
249
  "category": "productivity",
263
250
  "homepage": "https://github.com/agent-sh/debate"
264
251
  },
265
- {
266
- "name": "web-ctl",
267
- "source": {
268
- "source": "url",
269
- "url": "https://github.com/agent-sh/web-ctl.git",
270
- "commit": "345e44bc8a7b373728afce6c0d94ef067b5abc82",
271
- "ref": "v1.1.0"
272
- },
273
- "description": "Browser automation and web testing toolkit for AI agents - headless browser control, persistent sessions, auth handoff, and prompt injection defense",
274
- "version": "1.1.0",
275
- "category": "automation",
276
- "homepage": "https://github.com/agent-sh/web-ctl"
277
- },
278
252
  {
279
253
  "name": "skillers",
280
254
  "source": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentsys",
3
- "version": "5.14.0",
3
+ "version": "6.0.0",
4
4
  "description": "Professional-grade slash commands for Claude Code with cross-platform support",
5
5
  "keywords": [
6
6
  "workflow",
@@ -4,8 +4,8 @@
4
4
  "skills": "./adapters/codex/skills",
5
5
  "interface": {
6
6
  "displayName": "agentsys",
7
- "shortDescription": "AI agent orchestration with 45 skills and 50 agents",
8
- "longDescription": "Professional-grade slash commands for AI-powered development workflows. Includes /next-task (task discovery to production), /axiom (durable agent memory), /banthis (negative behavior memory), /skill-curator and /system-prompt-curator (agent configuration curation), /ship (commit to deploy), /audit-project (multi-agent code review), /deslop (AI slop cleanup), /perf (performance investigation), /enhance (config analysis), /consult (cross-tool AI consultation), and more.",
7
+ "shortDescription": "AI agent orchestration with 44 skills and 49 agents",
8
+ "longDescription": "Professional-grade slash commands for AI-powered development workflows. Includes /next-task (task discovery to production), /banthis (negative behavior memory), /skill-curator and /system-prompt-curator (agent configuration curation), /ship (commit to deploy), /audit-project (multi-agent code review), /deslop (AI slop cleanup), /perf (performance investigation), /enhance (config analysis), /consult (cross-tool AI consultation), and more.",
9
9
  "developerName": "Avi Fenesh",
10
10
  "category": "developer-tools",
11
11
  "capabilities": [
@@ -20,7 +20,6 @@
20
20
  "websiteUrl": "https://agent-sh.github.io/agent-sh.dev/",
21
21
  "defaultPrompt": [
22
22
  "What should I work on next?",
23
- "Load my durable Axiom context",
24
23
  "Review this codebase"
25
24
  ]
26
25
  }
package/AGENTS.md CHANGED
@@ -76,7 +76,7 @@
76
76
  <!-- GEN:START:claude-architecture -->
77
77
  ```
78
78
  lib/ → Shared library (vendored to plugins)
79
- plugins/ → 26 plugins, 50 agents (40 file-based + 10 role-based), 47 skills
79
+ plugins/ → 24 plugins, 49 agents (39 file-based + 10 role-based), 44 skills
80
80
  adapters/ → Platform adapters (opencode-plugin/, opencode/, codex/)
81
81
  checklists/ → Action checklists (9 files)
82
82
  bin/cli.js → npm CLI installer
@@ -94,14 +94,12 @@ bin/cli.js → npm CLI installer
94
94
  | enhance | 8 | 9 | Code quality analyzers |
95
95
  | sync-docs | 1 | 1 | Documentation sync |
96
96
  | repo-intel | 3 | 1 | Unified static analysis |
97
- | axiom | 0 | 1 | Durable agent-native memory |
98
97
  | banthis | 0 | 1 | Durable negative behavior memory |
99
98
  | perf | 6 | 8 | Performance investigation |
100
99
  | learn | 1 | 1 | Topic research and learning guides |
101
100
  | agnix | 0 | 1 | Agent config linting |
102
101
  | consult | 1 | 1 | Cross-tool AI consultation |
103
102
  | debate | 1 | 1 | Multi-perspective debate analysis |
104
- | web-ctl | 1 | 2 | Browser automation for AI agents |
105
103
  | skill-curator | 0 | 1 | Skill authoring and review |
106
104
  | system-prompt-curator | 0 | 1 | System prompt curation |
107
105
  | skillers | 2 | 2 | Workflow pattern learning |
@@ -178,7 +176,7 @@ agentsys # Run installer
178
176
  <agents>
179
177
  ## Agents
180
178
 
181
- 50 agents across 26 plugins (17 have agents; gate-and-ship is commands-only; axiom, banthis, skill-curator, system-prompt-curator, and agnix are skill/command-only; zig-lsp is config-only with no commands or agents; mojo and ada-spark are skill-only). Key agents by model:
179
+ 49 agents across 24 plugins (16 have agents; gate-and-ship is commands-only; banthis, skill-curator, system-prompt-curator, and agnix are skill/command-only; zig-lsp is config-only with no commands or agents; mojo and ada-spark are skill-only). Key agents by model:
182
180
 
183
181
  | Model | Agents | Use Case |
184
182
  |-------|--------|----------|
@@ -192,7 +190,7 @@ See [README.md](./README.md#command-details) and [docs/reference/AGENTS.md](./do
192
190
  <skills>
193
191
  ## Skills
194
192
 
195
- 45 skills across plugins. Agents invoke skills for reusable implementation.
193
+ 44 skills across plugins. Agents invoke skills for reusable implementation.
196
194
 
197
195
  | Category | Key Skills |
198
196
  |----------|------------|
@@ -200,7 +198,7 @@ See [README.md](./README.md#command-details) and [docs/reference/AGENTS.md](./do
200
198
  | Enhancement | `enhance-*` (9 skills for plugins, agents, docs, prompts, hooks), `skill-curator`, `system-prompt-curator` |
201
199
  | Performance | `baseline`, `benchmark`, `profile`, `theory-tester` |
202
200
  | Cleanup | `deslop`, `sync-docs`, `drift-analysis`, `repo-intel` |
203
- | Memory | `axiom`, `banthis` |
201
+ | Memory | `banthis` |
204
202
 
205
203
  See [README.md](./README.md#skills) for full skill list.
206
204
  </skills>
package/CHANGELOG.md CHANGED
@@ -9,6 +9,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
9
9
 
10
10
  ## [Unreleased]
11
11
 
12
+ ## [6.0.0] - 2026-05-29
13
+
14
+ ### Removed
15
+ - **BREAKING: Dropped the `axiom` and `web-ctl` plugins from the marketplace** (#365). Both repositories were retired; their marketplace entries, `plugins.txt` rows, `.kiro` mirrors (`web-auth`, `web-browse`, `web-session`), and all site/docs references were scrubbed. Installable plugin count is now 24 (was 26). Users who previously installed `axiom` or `web-ctl` from the marketplace must remove them; they will no longer resolve.
16
+
17
+ ### Added
18
+ - **Unified `repo-intel` core library** synced from agent-core (#373). The vendored `lib/repo-intel/` now exposes a single surface that folds the former `repo-map` lifecycle and the embedder into one module: `init`/`update`/`status`/`load`/`loadRaw`/`exists`, typed `queries.*`, LLM-augmentation write-path (`applyDescriptors`/`applySummary`), and an opt-in `embed` submodule (orchestrator, preference, binary resolver) for semantic search and duplicate detection. `lib/repo-map` remains as a deprecated compatibility shim. `lib/index.js` now exports `repoIntel` alongside `repoMap`.
19
+
20
+ ### Fixed
21
+ - `skill-patterns` `side_effect_without_disable` now accepts both the YAML boolean `true` and the quoted string `"true"` for `disable-model-invocation`, so a frontmatter value of `"true"` is no longer wrongly re-flagged.
22
+ - Bounded 19 polynomial-ReDoS regexes and closed 2 prototype-pollution sinks across the synced `collectors`/`enhance` lib (match semantics preserved; verified by equivalence testing).
23
+ - Removed the stale `aiRatio` query test - the analyzer dropped AI-authorship attribution and the query no longer exists (#372).
24
+
12
25
  ## [5.14.0] - 2026-05-21
13
26
 
14
27
  ### Added
package/README.md CHANGED
@@ -19,7 +19,7 @@
19
19
  </p>
20
20
 
21
21
  <p align="center">
22
- <b>26 plugins · 50 agents · 47 skills (across all repos) · 30k lines of lib code · 3,518 tests · 5 platforms</b><br>
22
+ <b>24 plugins · 49 agents · 44 skills (across all repos) · 30k lines of lib code · 3,518 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 - 26 plugins, 50 agents (40 file-based + 10 role-based specialists in audit-project), and 47 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 - 24 plugins, 49 agents (39 file-based + 10 role-based specialists in audit-project), and 44 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
 
@@ -118,7 +118,6 @@ The investment shifts from model spend to pipeline design. Better prompts, riche
118
118
  | [`/next-task`](#next-task) | Task workflow: discovery, implementation, PR, merge |
119
119
  | [`/prepare-delivery`](#prepare-delivery) | Pre-ship quality gates: deslop, review, validation, docs sync |
120
120
  | [`/gate-and-ship`](#gate-and-ship) | Quality gates then ship (/prepare-delivery + /ship) |
121
- | [`/axiom`](#axiom) | Durable memory: load, query, list, bootstrap projects, and record approved knowledge |
122
121
  | [`/banthis`](#banthis) | Durable negative memory: persist banned agent behaviors |
123
122
  | [`/agnix`](#agnix) | Lint agent configurations (423 rules) |
124
123
  | [`/ship`](#ship) | PR creation, CI monitoring, merge |
@@ -132,7 +131,6 @@ The investment shifts from model spend to pipeline design. Better prompts, riche
132
131
  | [`/learn`](#learn) | Research topics, create learning guides |
133
132
  | [`/consult`](#consult) | Cross-tool AI consultation |
134
133
  | [`/debate`](#debate) | Structured debate between AI tools |
135
- | [`/web-ctl`](#web-ctl) | Browser automation for AI agents |
136
134
  | [`/release`](#release) | Versioned release with ecosystem detection |
137
135
  | [`/skillers`](#skillers) | Workflow pattern learning and automation |
138
136
  | [`/skill-curator`](#skill-curator) | Create and improve reliable SKILL.md files |
@@ -146,7 +144,7 @@ Each command works standalone. Together, they compose into end-to-end pipelines.
146
144
 
147
145
  ## Skills
148
146
 
149
- 47 skills included across the plugins:
147
+ 44 skills included across the plugins:
150
148
 
151
149
  | Category | Skills |
152
150
  |----------|--------|
@@ -158,10 +156,9 @@ Each command works standalone. Together, they compose into end-to-end pipelines.
158
156
  | **Code Review** | `audit-project` |
159
157
  | **AI Collaboration** | `consult`, `debate`, `learn`, `recommend`, `skillers-compact` |
160
158
  | **Onboarding** | `can-i-help`, `onboard` |
161
- | **Web** | `web-auth`, `web-browse` |
162
159
  | **Release** | `release` |
163
160
  | **Analysis** | `drift-analysis`, `repo-intel` |
164
- | **Memory** | `axiom`, `banthis` |
161
+ | **Memory** | `banthis` |
165
162
  | **Linting** | `agnix` |
166
163
 
167
164
  **External skill plugins** (standalone repos, installed separately):
@@ -183,7 +180,7 @@ Skills are the reusable implementation units. Agents invoke skills; commands orc
183
180
  | [The Approach](#the-approach) | Why it's built this way |
184
181
  | [Benchmarks](#benchmarks) | Sonnet + agentsys vs raw Opus |
185
182
  | [Commands](#commands) | All 24 commands overview |
186
- | [Skills](#skills) | 47 skills across plugins |
183
+ | [Skills](#skills) | 44 skills across plugins |
187
184
  | [Skill-Only Plugins](#skill-only-plugins) | glide-mq and other non-command plugins |
188
185
  | [Command Details](#command-details) | Deep dive into each command |
189
186
  | [How Commands Work Together](#how-commands-work-together) | Standalone vs integrated |
@@ -314,38 +311,6 @@ Each piece runs independently - use `/prepare-delivery` alone to review before d
314
311
 
315
312
  ---
316
313
 
317
- ### /axiom
318
-
319
- **Purpose:** Durable, queryable memory for agents. Load the smallest useful context, query project or global knowledge, and propose new records without bloating `AGENTS.md`.
320
-
321
- **[axiom](https://github.com/agent-sh/axiom)** is a standalone plugin and CLI. It creates a private `axiom-based` knowledge repo after explicit approval, keeps only thin context loaded automatically, and stores durable decisions, memories, preferences, and project notes in queryable files.
322
-
323
- **Auto-loading at session start (v0.6.2+):** The plugin ships a `SessionStart` hook that runs `axiom before-any --auto-project --detect-only --quiet` automatically in Claude Code and Codex (Codex requires `[features].plugin_hooks = true` in `~/.codex/config.toml`). The hook never mutates state - it emits `## Axiom Setup` when `~/.axiom` is missing or `## Project Detection` when the current git project has no scaffold, then the agent asks the user before running `axiom init` or `axiom project <slug>`. An OpenCode plugin scaffold (`opencode-plugin/axiom.mjs`) hooks `session.created` and pre-warms `~/.axiom/.session-context.md`; full session-start context injection waits on [sst/opencode#5409](https://github.com/sst/opencode/issues/5409). On Cursor / Cline / Aider / Gemini CLI the agent invokes `before-any` via the skill instead.
324
-
325
- **What it does:**
326
-
327
- | Command | Use |
328
- |---------|-----|
329
- | `axiom before-any --quiet` | Load global thin context at the start of meaningful work |
330
- | `axiom before-any --auto-project --detect-only --quiet` | Read-only auto-load used by the SessionStart hook |
331
- | `axiom before-any --project <slug>` | Load project context and create missing project scaffolds |
332
- | `axiom query "<keyword>" --project <slug>` | Retrieve focused, source-backed project knowledge |
333
- | `axiom list --topics --project <slug>` | Explore what knowledge exists before querying |
334
- | `axiom record ...` | Propose a durable record through a temp clone, diff, and human approval |
335
-
336
- **Usage:**
337
-
338
- ```bash
339
- /axiom before-any --quiet
340
- /axiom before-any --project flowfabric
341
- /axiom query "lease based" --project flowfabric
342
- /axiom record --project flowfabric --kind decision "Lease-based claiming v2" "We switched because it gives stronger safety during restarts."
343
- ```
344
-
345
- **External tool:** Requires the [axiom CLI](https://github.com/agent-sh/axiom) from the plugin package.
346
-
347
- ---
348
-
349
314
  ### /banthis
350
315
 
351
316
  **Purpose:** Durable negative memory for repeated agent mistakes. Turn a user's "stop doing this" correction into a persistent rule in `CLAUDE.md` or `AGENTS.md`.
@@ -900,81 +865,6 @@ agent-knowledge/
900
865
 
901
866
  **Agent:** debate-orchestrator (opus model for orchestration)
902
867
 
903
- ### /web-ctl
904
-
905
- **Purpose:** Browser automation for AI agents - navigate, authenticate, and interact with web pages.
906
-
907
- **How it works:**
908
-
909
- Each invocation is a single Node.js process using Playwright. No daemon, no MCP server. Session state persists via Chrome's userDataDir with AES-256-GCM encrypted storage.
910
-
911
- ```
912
- Agent calls skill -> node scripts/web-ctl.js <args> -> Playwright API -> JSON result
913
- ```
914
-
915
- **Session lifecycle:**
916
-
917
- 1. `session start <name>` - Create session (encrypted profile directory)
918
- 2. `session auth <name> --url <login-url>` - Opens headed Chrome for human login (2FA, CAPTCHAs). Polls for success URL/selector, encrypts cookies on completion
919
- 3. `run <name> <action>` - Headless actions using persisted cookies
920
- 4. `session end <name>` - Cleanup
921
-
922
- **Actions:**
923
-
924
- | Action | Description | Key flag |
925
- |--------|-------------|----------|
926
- | `goto <url>` | Navigate to URL | |
927
- | `snapshot` | Get accessibility tree (primary page inspection) | |
928
- | `click <sel>` | Click element | `--wait-stable` |
929
- | `click-wait <sel>` | Click and wait for DOM + network stability | `--timeout <ms>` |
930
- | `type <sel> <text>` | Type with human-like delays | |
931
- | `read <sel>` | Read element text content | |
932
- | `fill <sel> <value>` | Clear field and set value | |
933
- | `wait <sel>` | Wait for element to appear | `--timeout <ms>` |
934
- | `evaluate <js>` | Execute JS in page context | `--allow-evaluate` |
935
- | `screenshot` | Full-page screenshot | `--path <file>` |
936
- | `network` | Capture network requests | `--filter <pattern>` |
937
- | `checkpoint` | Open headed browser for user (CAPTCHAs) | `--timeout <sec>` |
938
-
939
- `click-wait` waits for network idle + no DOM mutations for 500ms before returning. Cuts SPA interactions from multiple agent turns to one.
940
-
941
- **Error handling:**
942
-
943
- All errors return classified codes with actionable recovery suggestions:
944
-
945
- | Code | Recovery suggestion |
946
- |------|-------------------|
947
- | `element_not_found` | Snapshot included in response for selector discovery |
948
- | `timeout` | Increase `--timeout` |
949
- | `browser_closed` | `session start <name>` |
950
- | `network_error` | Check URL; verify cookies with `session status` |
951
- | `no_display` | Use `--vnc` flag |
952
- | `session_expired` | Re-authenticate |
953
-
954
- **Security:** Output sanitization (cookies/tokens redacted), prompt injection defense (`[PAGE_CONTENT: ...]` delimiters), AES-256-GCM encryption at rest, anti-bot measures (`webdriver=false`, random delays), read-only agent (no Write/Edit tools).
955
-
956
- **Selector syntax:** `role=button[name='Submit']`, `css=div.class`, `text=Click here`, `#id`
957
-
958
- **Usage:**
959
-
960
- ```bash
961
- /web-ctl goto https://example.com
962
- /web-ctl auth twitter --url https://x.com/i/flow/login
963
- /web-ctl # describe what you want to do, agent orchestrates it
964
- ```
965
-
966
- **Install:**
967
-
968
- ```bash
969
- agentsys install web-ctl
970
- npm install playwright
971
- npx playwright install chromium
972
- ```
973
-
974
- **Agent:** web-session (sonnet model)
975
-
976
- **Skills:** web-auth (human-in-the-loop auth), web-browse (headless actions)
977
-
978
868
  ### /release
979
869
 
980
870
  > Versioned release with automatic ecosystem and tooling detection
@@ -48,6 +48,12 @@ const { promisify } = require('util');
48
48
 
49
49
  const execFileAsync = promisify(cp.execFile);
50
50
 
51
+ // repo-intel artifacts grow with history: a mature repo's JSON can exceed 20 MB
52
+ // (agnix measured ~21 MB). Node's execFile default maxBuffer is 1 MB, which
53
+ // silently fails init/update/query on any real repo with "stdout maxBuffer length
54
+ // exceeded". Cap generously; callers can override via options.maxBuffer.
55
+ const ANALYZER_MAX_BUFFER = 256 * 1024 * 1024;
56
+
51
57
  const { ANALYZER_MIN_VERSION, BINARY_NAME, GITHUB_REPO } = require('./version');
52
58
 
53
59
  const PLATFORM_MAP = {
@@ -957,7 +963,7 @@ function ensureBinarySync(options) {
957
963
  */
958
964
  function runAnalyzer(args, options) {
959
965
  const binPath = ensureBinarySync();
960
- const opts = Object.assign({ encoding: 'utf8', windowsHide: true }, options);
966
+ const opts = Object.assign({ encoding: 'utf8', windowsHide: true, maxBuffer: ANALYZER_MAX_BUFFER }, options);
961
967
  if (!opts.stdio) opts.stdio = ['pipe', 'pipe', 'pipe'];
962
968
  const result = cp.execFileSync(binPath, args, opts);
963
969
  return typeof result === 'string' ? result : result.toString('utf8');
@@ -971,7 +977,7 @@ function runAnalyzer(args, options) {
971
977
  */
972
978
  async function runAnalyzerAsync(args, options) {
973
979
  const binPath = await ensureBinary();
974
- const opts = Object.assign({ encoding: 'utf8', windowsHide: true }, options);
980
+ const opts = Object.assign({ encoding: 'utf8', windowsHide: true, maxBuffer: ANALYZER_MAX_BUFFER }, options);
975
981
  const result = await execFileAsync(binPath, args, opts);
976
982
  return result.stdout;
977
983
  }
@@ -0,0 +1,160 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Shared HTTP + archive helpers used by both binary resolvers
5
+ * (`lib/binary/index.js` for `agent-analyzer`, `lib/embed/binary.js`
6
+ * for `agent-analyzer-embed`).
7
+ *
8
+ * Extracted to keep the two resolvers from drifting on HTTP redirect
9
+ * handling, GitHub auth, and archive extraction details — a single
10
+ * fix to e.g. the timeout policy or the redirect cap lands once and
11
+ * applies to both binaries.
12
+ *
13
+ * @module lib/binary/shared-helpers
14
+ */
15
+
16
+ const fs = require('fs');
17
+ const path = require('path');
18
+ const os = require('os');
19
+ const https = require('https');
20
+ const cp = require('child_process');
21
+
22
+ const DEFAULT_DOWNLOAD_TIMEOUT_MS = 30000;
23
+ const MAX_REDIRECTS = 5;
24
+
25
+ /**
26
+ * Fetch a URL into an in-memory Buffer following up to 5 redirects.
27
+ *
28
+ * Honors `GITHUB_TOKEN` / `GH_TOKEN` for authenticated requests
29
+ * (raises rate limit, lets private-repo asset URLs work). Stalled
30
+ * connections are killed by the per-request timeout — without this
31
+ * a stuck socket would hang the process indefinitely.
32
+ *
33
+ * @param {string} url
34
+ * @param {Object} [options]
35
+ * @param {string} [options.userAgent='agent-sh/binary-resolver']
36
+ * @param {number} [options.timeoutMs=30000] - per-request timeout
37
+ * @returns {Promise<Buffer>}
38
+ */
39
+ function downloadToBuffer(url, options) {
40
+ const opts = options || {};
41
+ const userAgent = opts.userAgent || 'agent-sh/binary-resolver';
42
+ const timeoutMs = opts.timeoutMs || DEFAULT_DOWNLOAD_TIMEOUT_MS;
43
+
44
+ return new Promise(function (resolve, reject) {
45
+ const ghToken = process.env.GITHUB_TOKEN || process.env.GH_TOKEN;
46
+
47
+ function request(reqUrl, redirectCount) {
48
+ if (redirectCount > MAX_REDIRECTS) {
49
+ reject(new Error('Too many redirects fetching from ' + url));
50
+ return;
51
+ }
52
+ const headers = {
53
+ 'User-Agent': userAgent,
54
+ 'Accept': 'application/octet-stream'
55
+ };
56
+ if (ghToken) headers['Authorization'] = 'Bearer ' + ghToken;
57
+
58
+ const req = https.get(reqUrl, { headers: headers, timeout: timeoutMs }, function (res) {
59
+ const sc = res.statusCode;
60
+ if (sc === 301 || sc === 302 || sc === 307 || sc === 308) {
61
+ res.resume();
62
+ var loc = res.headers.location;
63
+ if (loc && !loc.startsWith('https://')) {
64
+ reject(new Error('Refusing non-HTTPS redirect to ' + loc));
65
+ return;
66
+ }
67
+ request(loc, redirectCount + 1);
68
+ return;
69
+ }
70
+ if (sc !== 200) {
71
+ res.resume();
72
+ const hint = sc === 403 ? ' (rate limited - set GITHUB_TOKEN env var)' : '';
73
+ reject(new Error('HTTP ' + sc + hint + ' fetching ' + reqUrl));
74
+ return;
75
+ }
76
+ const chunks = [];
77
+ res.on('data', function (chunk) { chunks.push(chunk); });
78
+ res.on('end', function () { resolve(Buffer.concat(chunks)); });
79
+ res.on('error', reject);
80
+ });
81
+ req.on('error', reject);
82
+ req.on('timeout', function () {
83
+ req.destroy();
84
+ reject(new Error('Timeout (' + timeoutMs + 'ms) fetching ' + reqUrl));
85
+ });
86
+ }
87
+
88
+ request(url, 0);
89
+ });
90
+ }
91
+
92
+ /**
93
+ * Extract a `.tar.gz` Buffer into `destDir` using the system `tar`.
94
+ * Available on Linux, macOS, and Windows (built into recent Win10/11).
95
+ *
96
+ * @param {Buffer} buf
97
+ * @param {string} destDir
98
+ * @returns {Promise<void>}
99
+ */
100
+ function extractTarGz(buf, destDir) {
101
+ return new Promise(function (resolve, reject) {
102
+ const tarDest = process.platform === 'win32' ? destDir.replace(/\\/g, '/') : destDir;
103
+ const tar = cp.spawn('tar', ['xz', '-C', tarDest], {
104
+ stdio: ['pipe', 'pipe', 'pipe']
105
+ });
106
+ let stderr = '';
107
+ tar.stderr.on('data', function (d) { stderr += d; });
108
+ tar.stdin.write(buf);
109
+ tar.stdin.end();
110
+ tar.on('close', function (code) {
111
+ if (code !== 0) {
112
+ reject(new Error('tar extraction failed (code ' + code + '): ' + stderr));
113
+ } else {
114
+ resolve();
115
+ }
116
+ });
117
+ tar.on('error', reject);
118
+ });
119
+ }
120
+
121
+ /**
122
+ * Extract a `.zip` Buffer into `destDir` using PowerShell's
123
+ * `Expand-Archive` (Windows-only).
124
+ *
125
+ * @param {Buffer} buf
126
+ * @param {string} destDir
127
+ * @param {string} binaryName - used as the temp-dir prefix
128
+ * @returns {Promise<void>}
129
+ */
130
+ function extractZip(buf, destDir, binaryName) {
131
+ return new Promise(function (resolve, reject) {
132
+ var tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), binaryName + '-'));
133
+ var tmpZip = path.join(tmpDir, 'archive.zip');
134
+ fs.writeFileSync(tmpZip, buf);
135
+ var ps = cp.spawn(
136
+ 'powershell',
137
+ ['-NoProfile', '-NonInteractive', '-Command',
138
+ 'Expand-Archive', '-Path', tmpZip, '-DestinationPath', destDir, '-Force'],
139
+ { stdio: ['ignore', 'pipe', 'pipe'] }
140
+ );
141
+ var stderr = '';
142
+ ps.stderr.on('data', function (d) { stderr += d; });
143
+ ps.on('close', function (code) {
144
+ try { fs.rmSync(tmpDir, { recursive: true, force: true }); } catch (e) { /* ignore */ }
145
+ if (code !== 0) {
146
+ reject(new Error('zip extraction failed (code ' + code + '): ' + stderr));
147
+ } else {
148
+ resolve();
149
+ }
150
+ });
151
+ ps.on('error', reject);
152
+ });
153
+ }
154
+
155
+ module.exports = {
156
+ downloadToBuffer,
157
+ extractTarGz,
158
+ extractZip,
159
+ DEFAULT_DOWNLOAD_TIMEOUT_MS
160
+ };
@@ -126,7 +126,10 @@ function extractSymbols(content) {
126
126
  symbols.functions.push(match[1]);
127
127
  }
128
128
 
129
- const arrowPattern = /(?:const|let)\s+([a-zA-Z_$][a-zA-Z0-9_$]*)\s*=\s*(?:async\s*)?\([^)]*\)\s*=>/g;
129
+ // ReDoS fix: bound the unbounded \s* / async runs and the parameter list so the
130
+ // matcher cannot backtrack polynomially on pathological input. Bounds are large
131
+ // enough that all realistic source matches identically to the prior \s*/[^)]* form.
132
+ const arrowPattern = /(?:const|let)\s{1,1000}([a-zA-Z_$][a-zA-Z0-9_$]*)\s{0,1000}=\s{0,1000}(?:async\s{0,1000})?\([^)]{0,2000}\)\s{0,1000}=>/g;
130
133
  while ((match = arrowPattern.exec(content)) !== null) {
131
134
  symbols.functions.push(match[1]);
132
135
  }
@@ -141,7 +144,9 @@ function extractSymbols(content) {
141
144
  symbols.exports.push(match[1]);
142
145
  }
143
146
 
144
- const moduleExportsPattern = /module\.exports\s*=\s*\{([^}]+)\}/;
147
+ // ReDoS fix: bound the \s* runs and capture length so the matcher stays linear;
148
+ // bounds exceed any realistic module.exports declaration so matches are unchanged.
149
+ const moduleExportsPattern = /module\.exports\s{0,1000}=\s{0,1000}\{([^}]{1,100000})\}/;
145
150
  const moduleMatch = content.match(moduleExportsPattern);
146
151
  if (moduleMatch) {
147
152
  const keys = moduleMatch[1].split(',').map(k => k.trim().split(':')[0].trim());
@@ -50,7 +50,9 @@ function safeReadFile(filePath, basePath) {
50
50
  * Analyze a single markdown file
51
51
  */
52
52
  function analyzeMarkdownFile(content, filePath) {
53
- const sectionMatches = content.match(/^##\s+(.+)$/gm) || [];
53
+ // ReDoS fix: bound the \s+ run after the ## marker; line-anchored (.+) cannot
54
+ // cross newlines so this matches the same headings as before.
55
+ const sectionMatches = content.match(/^##\s{1,1000}(.+)$/gm) || [];
54
56
  const sections = sectionMatches.slice(0, 10).map(s => s.replace(/^##\s+/, ''));
55
57
  const sectionLower = sections.map(s => s.toLowerCase()).join(' ');
56
58
 
@@ -83,7 +85,11 @@ function extractCheckboxes(result, content) {
83
85
  * Extract documented features
84
86
  */
85
87
  function extractFeatures(result, content) {
86
- const featurePattern = /^[-*]\s+\*{0,2}(.+?)\*{0,2}(?:\s*[-–]\s*(.+))?$/gm;
88
+ // ReDoS fix: bound the \s+ run and the line-content quantifiers so the lazy
89
+ // (.+?) / optional trailing (.+) pair cannot backtrack polynomially. Using
90
+ // [^\n] is equivalent to . here (. never matches newline), and the bounds far
91
+ // exceed the 80-char feature cap applied below, so matches are unchanged.
92
+ const featurePattern = /^[-*]\s{1,100}\*{0,2}([^\n]{1,2000}?)\*{0,2}(?:\s{0,100}[-–]\s{0,100}([^\n]{1,2000}))?$/gm;
87
93
  let match;
88
94
 
89
95
  while ((match = featurePattern.exec(content)) !== null && result.features.length < 20) {
@@ -439,8 +439,17 @@ const agentPatterns = {
439
439
 
440
440
  // Look for hardcoded .claude/ references
441
441
  const hasHardcoded = /\.claude\//.test(content);
442
- // Exclude if using AI_STATE_DIR
443
- const usesEnvVar = /AI_STATE_DIR|\$\{.*STATE.*\}/i.test(content);
442
+ // Exclude if using AI_STATE_DIR or a ${...STATE...} env expression.
443
+ // ReDoS fix: the old /\$\{.*STATE.*\}/ (and the [^}]*STATE[^}]* rewrite)
444
+ // has two ambiguous quantifier runs -> polynomial backtrack. Instead
445
+ // scan each ${...} group with a single bounded [^}] run, then substring-
446
+ // test for STATE. Linear, and matches STATE in ANY ${...} like before.
447
+ let usesEnvVar = /AI_STATE_DIR/i.test(content);
448
+ if (!usesEnvVar) {
449
+ for (const m of content.matchAll(/\$\{([^}]{0,1000})\}/g)) {
450
+ if (/STATE/i.test(m[1])) { usesEnvVar = true; break; }
451
+ }
452
+ }
444
453
 
445
454
  if (hasHardcoded && !usesEnvVar) {
446
455
  return {
@@ -494,8 +503,12 @@ const agentPatterns = {
494
503
 
495
504
  // Check if has code blocks or lists but no XML
496
505
  const hasCodeBlocks = /```[\s\S]+?```/.test(content);
497
- const hasLists = /^[-*]\s+.+$/m.test(content);
498
- const hasXML = /<\w+>[\s\S]*?<\/\w+>/.test(content);
506
+ // ReDoS fix: bound the \s+ and line-content runs; line-anchored so this still
507
+ // detects any "- item" / "* item" list line as before.
508
+ const hasLists = /^[-*]\s{1,1000}[^\n]{1,2000}$/m.test(content);
509
+ // ReDoS fix: bound the unbounded [\s\S]*? so an unterminated <tag> cannot
510
+ // drive polynomial backtracking; 50k chars covers any realistic XML block.
511
+ const hasXML = /<\w+>[\s\S]{0,50000}?<\/\w+>/.test(content);
499
512
  const sectionCount = (content.match(/^##\s+/gm) || []).length;
500
513
 
501
514
  // Complex content without XML