@gempack/squad-mcp 0.10.1 → 0.11.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/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +2 -2
- package/CHANGELOG.md +60 -0
- package/README.md +34 -8
- package/dist/errors.d.ts +1 -1
- package/dist/errors.js.map +1 -1
- package/dist/index.js +1 -1
- package/dist/learning/format.js +0 -0
- package/dist/learning/format.js.map +1 -1
- package/dist/learning/normalize.d.ts +28 -0
- package/dist/learning/normalize.js +54 -0
- package/dist/learning/normalize.js.map +1 -0
- package/dist/learning/store.d.ts +33 -3
- package/dist/learning/store.js +39 -3
- package/dist/learning/store.js.map +1 -1
- package/dist/tools/prune-learnings.d.ts +84 -0
- package/dist/tools/prune-learnings.js +256 -0
- package/dist/tools/prune-learnings.js.map +1 -0
- package/dist/tools/read-learnings.d.ts +37 -1
- package/dist/tools/read-learnings.js +102 -10
- package/dist/tools/read-learnings.js.map +1 -1
- package/dist/tools/record-learning.d.ts +0 -0
- package/dist/tools/record-learning.js +0 -0
- package/dist/tools/record-learning.js.map +1 -1
- package/dist/tools/registry.js +2 -0
- package/dist/tools/registry.js.map +1 -1
- package/dist/util/atomic-rewrite-jsonl.d.ts +36 -0
- package/dist/util/atomic-rewrite-jsonl.js +95 -0
- package/dist/util/atomic-rewrite-jsonl.js.map +1 -0
- package/package.json +1 -1
- package/skills/squad/SKILL.md +66 -74
- package/skills/stats/SKILL.md +1 -0
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
"repo": "ggemba/squad-mcp"
|
|
13
13
|
},
|
|
14
14
|
"description": "Squad-dev workflow: deterministic classification, risk scoring, agent selection, advisory orchestration over MCP, native subagents, plus /squad:implement and /squad:review slash commands.",
|
|
15
|
-
"version": "0.
|
|
15
|
+
"version": "0.11.0",
|
|
16
16
|
"license": "Apache-2.0",
|
|
17
17
|
"homepage": "https://github.com/ggemba/squad-mcp"
|
|
18
18
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "squad",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.0",
|
|
4
4
|
"description": "Squad-dev workflow as a Claude Code plugin: classification, risk scoring, agent selection, advisory orchestration. Bundles an MCP server, native subagents, and the /squad:implement and /squad:review slash commands.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": {
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
"mcpServers": {
|
|
40
40
|
"squad": {
|
|
41
41
|
"command": "npx",
|
|
42
|
-
"args": ["-y", "@gempack/squad-mcp@0.
|
|
42
|
+
"args": ["-y", "@gempack/squad-mcp@0.11.0"]
|
|
43
43
|
}
|
|
44
44
|
}
|
|
45
45
|
}
|
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,66 @@ this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.11.0] - 2026-05-11
|
|
11
|
+
|
|
12
|
+
Closes the **learnings loop** end-to-end. Before v0.11.0, `read_learnings` was wired into Phase 5 of the squad skill but `record_learning` was a buried manual call — the write side of the cycle never fired in practice. v0.11.0 makes the cycle automatic: after `/squad:review` consolidates findings, the skill batches them into a single Phase 12 "Save as precedents?" prompt, the user picks accept/reject per finding, and the squad stops re-raising things the team already decided. Adds lifecycle plumbing (archive + promote), agent-visible past-decision inlining, and a `prune_learnings` MCP tool for housekeeping. Backward-compatible at the journal level — additive optional fields, v0.10.x readers see new fields as unknown and strip them.
|
|
13
|
+
|
|
14
|
+
### Cycle-2 fixes (Phase 11 reject-loop)
|
|
15
|
+
|
|
16
|
+
The first implementation pass shipped with three Blockers caught in the post-impl review:
|
|
17
|
+
|
|
18
|
+
- **B1 — Promoted-first ordering was broken end-to-end.** The original `[...promoted, ...rest]` array was passed through `tailRecent.slice(-limit)` (which keeps the tail, dropping promoted at the head when journal > limit), then `formatLearningsForPrompt.reverse()` (which pushed promoted to the bottom of rendered output). Three reviewers (architect, developer, QA) independently identified the bug. Fixed by partitioning visible entries, capping promoted at `MAX_PROMOTED_IN_PROMPT = 10` (architect Major M2), applying user filters only to non-promoted, and feeding the formatter a pre-shaped input that survives its internal reverse. Test now pins `idxPromoted < idxRegular` and the `entries.length > limit` regression.
|
|
19
|
+
- **B2 — Atomic rewrite step-4 failure had no rollback.** Between `<file>→.prev` and `.tmp→<file>` renames, a failure of the second left the journal MISSING with no recovery hint surfaced. Fixed by catching the failure, attempting `<file>.prev → <file>` rollback, and emitting a new `ATOMIC_REWRITE_FAILED` SquadError. Double-failure path embeds `mv <file>.prev <file>` recovery instructions in the message. Tests mock `fs.rename` to exercise both paths.
|
|
20
|
+
- **B3 — LLM prompt injection persisted via `reason`.** `formatLearningsForPrompt` interpolated user-supplied text raw into every advisor prompt thereafter; a hostile commit to a shared `.squad/learnings.jsonl` (or a one-time paste-and-record) persistently subverted future runs. Fixed by adding render-time `sanitizeForPrompt` (strips C0/C1 control bytes, bidi-control codepoints U+202A–202E + U+2066–2069, zero-width codepoints U+200B–200D + U+FEFF) and wrapping `reason` in a Markdown blockquote. New Inviolable Rule in `skills/squad/SKILL.md` Phase 12 instructs the orchestrator to refuse `because` clauses containing instruction-shaped patterns.
|
|
21
|
+
|
|
22
|
+
Plus the surrounding Major cluster:
|
|
23
|
+
|
|
24
|
+
- **`min_recurrence: 1` was a footgun** — promoted every singleton accept, defeating the "team policy" signal. Schema now rejects via `.refine` + runtime guard in the handler (covers programmatic callers that bypass the dispatcher). Acceptable values are `0` (disable) or `>= 2`.
|
|
25
|
+
- **`unchanged_count` arithmetic was fragile** — relied on three simultaneous invariants holding. Replaced with a `Set<number>` of touched indices, robust under future refactoring.
|
|
26
|
+
- **`SafeString` hardening was incomplete** — `branch` and `scope` accepted NUL bytes despite the v0.11.0 release-note claim. Promoted both at the tool boundary AND the store schema (defense-in-depth so a future caller bypassing the tool still gets rejected).
|
|
27
|
+
- **`normalizeFindingTitle` accepted mismatched quote pairs** (`"foo'` → `foo`). Tightened to per-quote-class regex; only true pairs strip. Added `PASS ORDER IS LOAD-BEARING` block-comment to deter future reordering.
|
|
28
|
+
- **Stats panel `📚` glyph contradicted its own single-cyan rule.** Rewrote panel item 2a to use `▸` directly; dropped the example emoji that walked itself back in prose.
|
|
29
|
+
- **Orphan "Phase 14" references in `src/tools/record-learning.ts` and `skills/squad/SKILL.md:50`** updated to the current Phase 12 batched flow.
|
|
30
|
+
|
|
31
|
+
Cycle-2 also pinned coverage gaps QA called out: `learnings.enabled: false` no-op in `prune_learnings`, schema round-trip for `archived` + `promoted` through `appendLearning`, promotion tie-break on identical ts, NUL-byte rejection on branch/scope.
|
|
32
|
+
|
|
33
|
+
### Added
|
|
34
|
+
|
|
35
|
+
- **Phase 12 batched "Save as precedents?" prompt** in `skills/squad/SKILL.md` — appears at the end of every `/squad:review` after `apply_consolidation_rules` returns. Groups findings by agent + severity (Blocker / Major / Minor; Suggestions discarded), surfaces a numbered list, parses casual responses (`accept 1,2,3` / `reject 4` / `all accept` / `skip` / `because <reason>`), and calls `record_learning` for each marked finding. The single record path — Phase 14's per-finding restate flow is REMOVED to avoid double-prompting.
|
|
36
|
+
- **`prune_learnings` MCP tool** — lifecycle maintenance for `.squad/learnings.jsonl`. Two passes inside one atomic-rewrite under the file lock: (1) entries older than `max_age_days` are marked `archived: true` and suppressed from default `read_learnings`; (2) entries grouped by `normalizeFindingTitle` with ≥ `min_recurrence` accept decisions get `promoted: true` on the most-recent matching entry. Never auto-runs (`max_age_days` defaults to 0). `dry_run: true` for inspection without mutating.
|
|
37
|
+
- **`archived: boolean` and `promoted: boolean` optional fields** added to the learning entry schema at the store level (not just the tool layer — critical to prevent round-trip data loss when prune rewrites the file). Both default to undefined, both are additive — a v0.10.x reader stripping them via Zod's default `strip-unknown` semantics is a tolerated downgrade.
|
|
38
|
+
- **`src/learning/normalize.ts`** — single canonical `normalizeFindingTitle(s)` helper used by both `read_learnings` (matching live findings against past learnings) and `prune_learnings` (grouping for promotion-recurrence counting). lowercase + trim + strip trailing `.,;` + strip surrounding quotes/backticks + strip trailing parenthetical + collapse whitespace.
|
|
39
|
+
- **`src/util/atomic-rewrite-jsonl.ts`** — shared atomic-rewrite primitive (lock + write `<file>.tmp` + rename `<file>` → `<file>.prev` + rename `<file>.tmp` → `<file>`). POSIX rename atomicity means concurrent readers see either the pre-rewrite or post-rewrite file in full, never a torn write. `.prev` is the rollback snapshot.
|
|
40
|
+
- **⭐ PROMOTED tag** rendered inline by `formatLearningsForPrompt` for entries with `promoted: true`. Advisors are instructed in Phase 5 to treat promoted entries as team policy, not ordinary precedent.
|
|
41
|
+
- **Stats panel learnings line** (`skills/stats/SKILL.md` §Panel order item 2a) — `learnings: <total> total · <promoted> promoted · <archived> archived` under the trend sparkline. Fetched via `read_learnings({limit: 0, include_archived: true, include_summary: true, include_rendered: false})` — the `limit: 0` short-circuits entry rendering and returns just the summary object. Omitted entirely when `total === 0`.
|
|
42
|
+
- **`include_archived` and `include_summary` flags on `read_learnings`** — default false. `include_archived: true` opts back in to archived rows (debug / audit). `include_summary: true` returns a `summary` object with `{total, active, archived, promoted}` counts computed over the FULL file before any agent / decision / scope filter. Used by the stats panel and by future "you haven't pruned in a while" prompts.
|
|
43
|
+
- **Past-decision interlock for advisors** — Phase 5 advisory prompt template (`skills/squad/SKILL.md`) gains an explicit section telling agents to honor ⭐ PROMOTED entries as binding and to use `normalizeFindingTitle`-style matching when judging whether a live finding restates a past learning. The advisor sees the rendered learning block inline, not via a separate tool call.
|
|
44
|
+
|
|
45
|
+
### Changed
|
|
46
|
+
|
|
47
|
+
- **`read_learnings` auto-filters archived rows by default.** Set `include_archived: true` to surface them. Promoted entries surface FIRST in the rendered markdown block regardless of `tailRecent` ordering — they represent crystallised team policy and always belong at the top.
|
|
48
|
+
- **`read_learnings.limit` widened from `positive()` to `nonnegative().max(200)`.** `limit: 0` is a valid sentinel meaning "summary-only, no entries" — used by `/squad:stats` to fetch counts without paging the file. Hard cap 200 unchanged.
|
|
49
|
+
- **Phase 14 (per-finding learning restate) REMOVED** from `skills/squad/SKILL.md`. The Phase 12 batched prompt is the single record path. Eliminates the double-prompt that would have asked the user about the same finding twice.
|
|
50
|
+
- **`record_learning.finding` and `record_learning.reason` hardened with `SafeString`** — NUL-byte rejection at the tool boundary. `reason` flows verbatim from the user's natural-language Phase 12 response ("accept 1 because we have CSRF at the gateway"), so closing the injection surface at the schema edge is cheap defense in depth.
|
|
51
|
+
|
|
52
|
+
### Schema migration
|
|
53
|
+
|
|
54
|
+
- **Additive only.** New optional fields: `archived?: boolean`, `promoted?: boolean`. No schema_version bump. v0.10.x readers strip the unknown fields silently via Zod default behaviour and continue. v0.11.0 readers consume both fields. Recurrence count is NOT stored on the row (would create a write-while-write race when parallel advisors record); promotion is computed lazily inside `prune_learnings` under the lock.
|
|
55
|
+
|
|
56
|
+
### Tests
|
|
57
|
+
|
|
58
|
+
- `tests/learning-normalize.test.ts` (new) — `normalizeFindingTitle` cases: case folding, whitespace collapse, trailing punctuation, surrounding quotes/backticks, parenthetical line-numbers, idempotency, equivalence of common decoration variants.
|
|
59
|
+
- `tests/atomic-rewrite-jsonl.test.ts` (new) — round-trip with shaped data, parent-dir creation, empty-rows edge case, `.prev` snapshot semantics, no stale `.tmp`, concurrent rewrites serialised under lock.
|
|
60
|
+
- `tests/prune-learnings.test.ts` (new) — empty / no-op cases, age cutoff archival, idempotent re-runs (no double-archive, no double-promote), promotion grouping by normalised title, reject decisions ignored when counting, archived entries excluded from recurrence count, `dry_run` doesn't mutate, archived rows hidden from default read path, full-field preservation on rewrite (no data loss), backward compat with v0.10.x rows.
|
|
61
|
+
- `tests/read-learnings-tool.test.ts` (new) — `include_summary` count correctness, `include_summary: false` omission, `limit: 0` short-circuit returning summary, `include_archived` default-off / opt-in, promoted-first ordering in entries array AND rendered block (⭐ PROMOTED tag emitted), backward compat with v0.10.x rows.
|
|
62
|
+
- `tests/dispatch-tool.test.ts` — `record_learning` rejects NUL byte in `finding` and `reason` at the SafeString boundary; `prune_learnings` is registered and accepts a default-args call.
|
|
63
|
+
- `tests/integration/server-lifecycle.test.ts` — tool list updated to include `prune_learnings` (25 tools total).
|
|
64
|
+
|
|
65
|
+
### Known issues
|
|
66
|
+
|
|
67
|
+
- The Phase 12 prompt is executed by the host LLM following the SKILL spec — there is no automated test that intercepts a real `/squad:review` response and verifies the batched parse produced the right `record_learning` calls. We rely on the LLM following the parsing grammar in `skills/squad/SKILL.md`. Same trust boundary as v0.10.0's debug / question / brainstorm telemetry hooks.
|
|
68
|
+
- `prune_learnings` is never invoked automatically. Users who want regular housekeeping run `prune_learnings({max_age_days: 180, min_recurrence: 3})` themselves or wire it into a cron / pre-commit hook. The default no-op was a deliberate trade-off — surprising diff churn on repos that commit `.squad/learnings.jsonl` is worse than letting old entries sit there.
|
|
69
|
+
|
|
10
70
|
## [0.10.1] - 2026-05-11
|
|
11
71
|
|
|
12
72
|
Patch release: pays the technical debt accumulated across v0.9.0 + v0.10.0. No new features, no schema change, no user-visible behavior change beyond `/squad:stats` now showing `/squad:question` and `/squad:brainstorm` invocations (they were in the enum but the SKILL files never emitted journal rows).
|
package/README.md
CHANGED
|
@@ -127,6 +127,7 @@ Stuck? Check `INSTALL.md` → Troubleshooting. The most common failures (`Failed
|
|
|
127
127
|
| `read_squad_config` | Read and resolve `.squad.yaml` (or `.squad.yml`) at workspace_root. Returns effective weights, threshold, min_score, skip_paths, disable_agents. |
|
|
128
128
|
| `read_learnings` | Load past accept/reject decisions from `.squad/learnings.jsonl`. Filters by agent / decision / changed-file scope. Returns entries plus a markdown block ready to inject into agent or consolidator prompts. |
|
|
129
129
|
| `record_learning` | Append one accept/reject decision to `.squad/learnings.jsonl`. Side-effecting; the skill (or CLI) is responsible for per-finding user authorisation. |
|
|
130
|
+
| `prune_learnings` | Lifecycle maintenance (v0.11.0+): mark entries older than `max_age_days` as `archived` and entries with ≥ `min_recurrence` accepts on the same canonicalised finding as `promoted`. Atomic rewrite under file lock. Never auto-runs. |
|
|
130
131
|
| `compose_prd_parse` | Build a prompt + JSON schema for the host LLM to decompose a PRD into atomic tasks. Pure-MCP: server does NO LLM calls. Caller (skill) feeds the prompt to its model, then calls `record_tasks` after user confirmation. |
|
|
131
132
|
| `list_tasks` | Read tasks from `.squad/tasks.json`. Filters: status, agent (matches `agent_hints`), changed_files (glob match against task `scope`). |
|
|
132
133
|
| `next_task` | Pick the next ready task: candidate status (default pending), all dependencies done, optional agent / changed_files filter. Tiebreak priority then id. Returns null + reason when none ready. |
|
|
@@ -240,17 +241,20 @@ Each time the team accepts or rejects an advisory finding, the decision can be a
|
|
|
240
241
|
|
|
241
242
|
The file lives in git. Decisions are auditable in PR diffs.
|
|
242
243
|
|
|
243
|
-
### Recording decisions
|
|
244
|
+
### Recording decisions (v0.11.0+ Phase 12 prompt)
|
|
244
245
|
|
|
245
|
-
|
|
246
|
+
After `/squad:review` consolidates findings, the skill surfaces a single batched prompt at the end of the report. It groups the findings by agent + severity (Suggestion-level findings excluded) and asks one question:
|
|
246
247
|
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
248
|
+
> Save which findings as precedents? Reply: `accept N1,N2,N3` / `reject N4` / `all accept` / `skip` / `because <reason>` to attach a rationale.
|
|
249
|
+
|
|
250
|
+
Each affirmative pick fires one `record_learning` call. Examples:
|
|
251
|
+
|
|
252
|
+
- `accept 1,2 because we ship this pattern across services`
|
|
253
|
+
- `reject 3` (records the rejection without a reason; the squad will still suppress the same finding next run)
|
|
254
|
+
- `all accept` (accepts every Blocker / Major / Minor in the report)
|
|
255
|
+
- `skip` or empty response (records nothing)
|
|
252
256
|
|
|
253
|
-
|
|
257
|
+
**Per-finding authorisation is required** — silence or "thanks" is not authorisation. The skill never invents a reason; the text after `because` flows verbatim to `record_learning.reason`.
|
|
254
258
|
|
|
255
259
|
For non-MCP environments, use the CLI helper:
|
|
256
260
|
|
|
@@ -269,6 +273,28 @@ In Phase 5 (per-agent advisory) the skill calls `read_learnings(workspace_root,
|
|
|
269
273
|
|
|
270
274
|
Each agent is told: when a current finding matches a previously **rejected** decision (similar agent + similar finding text + matching scope), suppress or downgrade severity unless the diff materially changes the rationale. When a finding contradicts a previously **accepted** decision, flag the contradiction explicitly.
|
|
271
275
|
|
|
276
|
+
### Lifecycle (v0.11.0+): archive + promote
|
|
277
|
+
|
|
278
|
+
Two new optional fields on each entry let the journal age gracefully without manual surgery:
|
|
279
|
+
|
|
280
|
+
- `archived: true` — the entry is past the team's age cutoff and is hidden from default `read_learnings` injection. The row stays on disk for forensics.
|
|
281
|
+
- `promoted: true` — the same finding (matched by canonicalised title) has been accepted ≥ N times and now surfaces FIRST in the rendered block as `⭐ PROMOTED`. Advisors are instructed to treat promoted entries as team policy, not ordinary precedent.
|
|
282
|
+
|
|
283
|
+
Both flags are set by the **`prune_learnings`** MCP tool:
|
|
284
|
+
|
|
285
|
+
```
|
|
286
|
+
prune_learnings({
|
|
287
|
+
workspace_root: <repo>,
|
|
288
|
+
max_age_days: 180, // entries older than this get archived: true
|
|
289
|
+
min_recurrence: 3, // accept-decisions on the same finding ≥ 3× get promoted: true
|
|
290
|
+
dry_run: false // set true to inspect counts without mutating
|
|
291
|
+
})
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
`prune_learnings` **never auto-runs.** The defaults are `max_age_days: 0` (= disabled) and `min_recurrence: 3` — invoking with no arguments is a safe no-op. Wire it into a cron or pre-commit hook if you want regular housekeeping. Each non-no-op run produces an atomic rewrite of `.squad/learnings.jsonl` under the same file lock used by `record_learning`; concurrent readers either see the pre-rewrite or post-rewrite file in full, never a torn write. A `.prev` snapshot is kept alongside the file as the rollback point.
|
|
295
|
+
|
|
296
|
+
The v0.11.0 schema is **additive and backward-compatible** — a v0.10.x reader strips the unknown `archived` / `promoted` fields silently and continues. No `schema_version` bump.
|
|
297
|
+
|
|
272
298
|
### Configuration
|
|
273
299
|
|
|
274
300
|
Override defaults via `.squad.yaml`:
|
package/dist/errors.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type SquadErrorCode = "PATH_TRAVERSAL_DENIED" | "PATH_REQUIRES_WORKSPACE" | "PATH_INVALID" | "AGENT_DIR_MISSING" | "UNKNOWN_AGENT" | "OVERRIDE_REJECTED" | "INVALID_INPUT" | "INTERNAL_ERROR" | "GIT_EXEC_DENIED" | "GIT_EXEC_TIMEOUT" | "GIT_NOT_FOUND" | "GIT_OUTPUT_TOO_LARGE" | "GIT_NOT_A_REPO" | "CONFIG_READ_FAILED" | "RECORD_TOO_LARGE";
|
|
1
|
+
export type SquadErrorCode = "PATH_TRAVERSAL_DENIED" | "PATH_REQUIRES_WORKSPACE" | "PATH_INVALID" | "AGENT_DIR_MISSING" | "UNKNOWN_AGENT" | "OVERRIDE_REJECTED" | "INVALID_INPUT" | "INTERNAL_ERROR" | "GIT_EXEC_DENIED" | "GIT_EXEC_TIMEOUT" | "GIT_NOT_FOUND" | "GIT_OUTPUT_TOO_LARGE" | "GIT_NOT_A_REPO" | "CONFIG_READ_FAILED" | "RECORD_TOO_LARGE" | "ATOMIC_REWRITE_FAILED";
|
|
2
2
|
export declare class SquadError extends Error {
|
|
3
3
|
readonly code: SquadErrorCode;
|
|
4
4
|
readonly details?: Record<string, unknown>;
|
package/dist/errors.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AA6BA,MAAM,OAAO,UAAW,SAAQ,KAAK;IAC1B,IAAI,CAAiB;IACrB,OAAO,CAA2B;IAE3C,YAAY,IAAoB,EAAE,OAAe,EAAE,OAAiC;QAClF,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,YAAY,CAAC;QACzB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;IACzB,CAAC;CACF;AAED,MAAM,UAAU,YAAY,CAAC,GAAY;IACvC,OAAO,GAAG,YAAY,UAAU,CAAC;AACnC,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -7,7 +7,7 @@ import { listResources, readResource } from "./resources/registry.js";
|
|
|
7
7
|
import { listPrompts, getPrompt } from "./prompts/registry.js";
|
|
8
8
|
import { logger, setupProcessHandlers } from "./observability/logger.js";
|
|
9
9
|
setupProcessHandlers();
|
|
10
|
-
const SERVER_VERSION = "0.
|
|
10
|
+
const SERVER_VERSION = "0.11.0";
|
|
11
11
|
const server = new Server({
|
|
12
12
|
name: "squad-mcp",
|
|
13
13
|
version: SERVER_VERSION,
|
package/dist/learning/format.js
CHANGED
|
Binary file
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"format.js","sourceRoot":"","sources":["../../src/learning/format.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAsBtD,MAAM,UAAU,GAAG,GAAG,CAAC;AACvB,MAAM,aAAa,GAAG,EAAE,CAAC;AACzB,MAAM,eAAe,GAAG,wBAAwB,CAAC;AAEjD;;;;;;;;GAQG;AACH,MAAM,UAAU,wBAAwB,CACtC,OAAwB,EACxB,UAAkC,EAAE;IAEpC,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,KAAK,IAAI,aAAa,EAAE,UAAU,CAAC,CAAC;IACnE,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,IAAI,eAAe,CAAC;IAEnD,4EAA4E;IAC5E,sEAAsE;IACtE,uCAAuC;IACvC,IAAI,QAAQ,GAAG,OAAO,CAAC;IACvB,IAAI,OAAO,CAAC,YAAY,IAAI,OAAO,CAAC,YAAY,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC5D,QAAQ,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE;YAC9B,IAAI,CAAC,CAAC,CAAC,KAAK;gBAAE,OAAO,IAAI,CAAC;YAC1B,OAAO,OAAO,CAAC,YAAa,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,WAAW,CAAC,CAAC,CAAC,KAAM,EAAE,CAAC,CAAC,CAAC,CAAC;QACrE,CAAC,CAAC,CAAC;IACL,CAAC;IAED,uEAAuE;IACvE,MAAM,IAAI,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC;IACpC,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IAEjC,wDAAwD;IACxD,MAAM,OAAO,GAAG,CAAC,GAAG,IAAI,CAAC,CAAC,OAAO,EAAE,CAAC;IAEpC,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACpB,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACf,KAAK,CAAC,IAAI,CACR,yCAAyC,OAAO,CAAC,MAAM,OAAO,OAAO,CAAC,MAAM,0RAA0R,CACvW,CAAC;IACF,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAEf,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;QACvB,MAAM,GAAG,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QACvF,MAAM,OAAO,GAAG,CAAC,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,UAAU,CAAC;QAClE,MAAM,GAAG,GAAG,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,QAAQ,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;QACjD,MAAM,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,
|
|
1
|
+
{"version":3,"file":"format.js","sourceRoot":"","sources":["../../src/learning/format.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Canonical title normalisation for matching learnings against live findings
|
|
3
|
+
* and for grouping during promotion-recurrence counting.
|
|
4
|
+
*
|
|
5
|
+
* Single source of truth — used by:
|
|
6
|
+
* - `read_learnings` rendering: when a live finding's title normalises to
|
|
7
|
+
* the same key as a past learning's, the past learning is inlined next
|
|
8
|
+
* to the relevant finding context in the advisory prompt (v0.11.0+).
|
|
9
|
+
* - `prune_learnings`: groups entries by normalised title to count
|
|
10
|
+
* accept-decisions and mark the most-recent matching entry as
|
|
11
|
+
* `promoted: true` when count ≥ `min_recurrence`.
|
|
12
|
+
*
|
|
13
|
+
* Strategy:
|
|
14
|
+
* - Lowercase for case-insensitivity ("CSRF Token Missing" ≡ "csrf token missing").
|
|
15
|
+
* - Strip leading/trailing whitespace.
|
|
16
|
+
* - Collapse runs of internal whitespace to a single space.
|
|
17
|
+
* - Strip a small set of common punctuation that's noise-vs-signal at
|
|
18
|
+
* finding granularity: trailing `.`, surrounding `"` `'` ``` `` `, and
|
|
19
|
+
* parenthetical suffixes (often line numbers like ` (line 42)` or
|
|
20
|
+
* ` (#1234)`). Inner punctuation stays — `auth.middleware.ts` matters
|
|
21
|
+
* for disambiguation.
|
|
22
|
+
*
|
|
23
|
+
* Conservative on purpose: this is exact-match-after-normalisation, NOT
|
|
24
|
+
* fuzzy similarity. Plan v2 deferred TF-IDF / embedding-based similarity to
|
|
25
|
+
* a future release. If the team wants "this finding is the same as past
|
|
26
|
+
* one with different wording", they record it explicitly.
|
|
27
|
+
*/
|
|
28
|
+
export declare function normalizeFindingTitle(s: string): string;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Canonical title normalisation for matching learnings against live findings
|
|
3
|
+
* and for grouping during promotion-recurrence counting.
|
|
4
|
+
*
|
|
5
|
+
* Single source of truth — used by:
|
|
6
|
+
* - `read_learnings` rendering: when a live finding's title normalises to
|
|
7
|
+
* the same key as a past learning's, the past learning is inlined next
|
|
8
|
+
* to the relevant finding context in the advisory prompt (v0.11.0+).
|
|
9
|
+
* - `prune_learnings`: groups entries by normalised title to count
|
|
10
|
+
* accept-decisions and mark the most-recent matching entry as
|
|
11
|
+
* `promoted: true` when count ≥ `min_recurrence`.
|
|
12
|
+
*
|
|
13
|
+
* Strategy:
|
|
14
|
+
* - Lowercase for case-insensitivity ("CSRF Token Missing" ≡ "csrf token missing").
|
|
15
|
+
* - Strip leading/trailing whitespace.
|
|
16
|
+
* - Collapse runs of internal whitespace to a single space.
|
|
17
|
+
* - Strip a small set of common punctuation that's noise-vs-signal at
|
|
18
|
+
* finding granularity: trailing `.`, surrounding `"` `'` ``` `` `, and
|
|
19
|
+
* parenthetical suffixes (often line numbers like ` (line 42)` or
|
|
20
|
+
* ` (#1234)`). Inner punctuation stays — `auth.middleware.ts` matters
|
|
21
|
+
* for disambiguation.
|
|
22
|
+
*
|
|
23
|
+
* Conservative on purpose: this is exact-match-after-normalisation, NOT
|
|
24
|
+
* fuzzy similarity. Plan v2 deferred TF-IDF / embedding-based similarity to
|
|
25
|
+
* a future release. If the team wants "this finding is the same as past
|
|
26
|
+
* one with different wording", they record it explicitly.
|
|
27
|
+
*/
|
|
28
|
+
export function normalizeFindingTitle(s) {
|
|
29
|
+
if (typeof s !== "string")
|
|
30
|
+
return "";
|
|
31
|
+
// PASS ORDER IS LOAD-BEARING. Changing the order changes the equivalence
|
|
32
|
+
// class that `prune_learnings` uses for promotion grouping — invalidating
|
|
33
|
+
// every promoted entry's recurrence count on the next prune. If you need
|
|
34
|
+
// to tweak this function, do it via additive passes, not reorderings.
|
|
35
|
+
let out = s.toLowerCase().trim();
|
|
36
|
+
// Strip a single trailing `.` / `,` / `;`.
|
|
37
|
+
out = out.replace(/[.,;]+$/, "");
|
|
38
|
+
// Strip surrounding quotes / backticks — only when BOTH sides match the
|
|
39
|
+
// same quote character. v0.11.0 cycle-2 (developer Major M6) fix: the
|
|
40
|
+
// earlier `^[`'"](.+)[`'"]$` accepted mismatched pairs (e.g. `"foo'`)
|
|
41
|
+
// which collapsed to `foo`, surprising future maintainers. Apply each
|
|
42
|
+
// quote class independently so only true pairs strip.
|
|
43
|
+
out = out.replace(/^`(.+)`$/, "$1");
|
|
44
|
+
out = out.replace(/^'(.+)'$/, "$1");
|
|
45
|
+
out = out.replace(/^"(.+)"$/, "$1");
|
|
46
|
+
// Strip a single trailing parenthetical suffix `(...)`. Line numbers,
|
|
47
|
+
// PR refs, agent attributions all get noise-suppressed.
|
|
48
|
+
out = out.replace(/\s*\([^)]*\)\s*$/, "");
|
|
49
|
+
// Collapse internal whitespace to single space.
|
|
50
|
+
out = out.replace(/\s+/g, " ");
|
|
51
|
+
// Trim again in case the parenthetical strip exposed trailing whitespace.
|
|
52
|
+
return out.trim();
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=normalize.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"normalize.js","sourceRoot":"","sources":["../../src/learning/normalize.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,MAAM,UAAU,qBAAqB,CAAC,CAAS;IAC7C,IAAI,OAAO,CAAC,KAAK,QAAQ;QAAE,OAAO,EAAE,CAAC;IACrC,yEAAyE;IACzE,0EAA0E;IAC1E,yEAAyE;IACzE,sEAAsE;IACtE,IAAI,GAAG,GAAG,CAAC,CAAC,WAAW,EAAE,CAAC,IAAI,EAAE,CAAC;IACjC,2CAA2C;IAC3C,GAAG,GAAG,GAAG,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC;IACjC,wEAAwE;IACxE,sEAAsE;IACtE,sEAAsE;IACtE,sEAAsE;IACtE,sDAAsD;IACtD,GAAG,GAAG,GAAG,CAAC,OAAO,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC;IACpC,GAAG,GAAG,GAAG,CAAC,OAAO,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC;IACpC,GAAG,GAAG,GAAG,CAAC,OAAO,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC;IACpC,sEAAsE;IACtE,wDAAwD;IACxD,GAAG,GAAG,GAAG,CAAC,OAAO,CAAC,kBAAkB,EAAE,EAAE,CAAC,CAAC;IAC1C,gDAAgD;IAChD,GAAG,GAAG,GAAG,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC/B,0EAA0E;IAC1E,OAAO,GAAG,CAAC,IAAI,EAAE,CAAC;AACpB,CAAC"}
|
package/dist/learning/store.d.ts
CHANGED
|
@@ -11,8 +11,13 @@ declare const learningEntrySchema: z.ZodObject<{
|
|
|
11
11
|
ts: z.ZodString;
|
|
12
12
|
/** PR number when recorded from `/squad:review #N`; optional otherwise. */
|
|
13
13
|
pr: z.ZodOptional<z.ZodNumber>;
|
|
14
|
-
/**
|
|
15
|
-
|
|
14
|
+
/**
|
|
15
|
+
* Branch name when recorded from a local review (no PR ref). v0.11.0
|
|
16
|
+
* cycle-2 (security Major M2): NUL-byte rejection mirrors the tool-edge
|
|
17
|
+
* `SafeString` so the store schema rejects a hostile row even if a
|
|
18
|
+
* future caller bypasses the tool.
|
|
19
|
+
*/
|
|
20
|
+
branch: z.ZodOptional<z.ZodEffects<z.ZodString, string, string>>;
|
|
16
21
|
/** Which agent's finding this decision concerns. */
|
|
17
22
|
agent: z.ZodEnum<[AgentName, ...AgentName[]]>;
|
|
18
23
|
/** Severity at the time of the decision (Blocker / Major / Minor / Suggestion). */
|
|
@@ -28,7 +33,28 @@ declare const learningEntrySchema: z.ZodObject<{
|
|
|
28
33
|
* absent, the decision applies repo-wide. Used by the formatter to filter
|
|
29
34
|
* learnings down to those relevant to the current diff.
|
|
30
35
|
*/
|
|
31
|
-
scope: z.ZodOptional<z.ZodString
|
|
36
|
+
scope: z.ZodOptional<z.ZodEffects<z.ZodString, string, string>>;
|
|
37
|
+
/**
|
|
38
|
+
* v0.11.0+ lifecycle fields. All optional and additive — a v0.10.x reader
|
|
39
|
+
* encountering these fields silently strips them (Zod default), so the
|
|
40
|
+
* journal can be read by older clients without crashing. A v0.11.0+ reader
|
|
41
|
+
* uses them to suppress (archived) or surface (promoted) entries.
|
|
42
|
+
*
|
|
43
|
+
* - `archived: true` — entry is past `max_age_days` and is hidden from
|
|
44
|
+
* default `readLearnings` output. Set by `prune_learnings`. The entry
|
|
45
|
+
* stays on disk for forensics; not deleted.
|
|
46
|
+
* - `promoted: true` — entry has been accepted ≥ `min_recurrence` times
|
|
47
|
+
* (across all agents, matched by `normalizeFindingTitle`) and is surfaced
|
|
48
|
+
* FIRST in the rendered learnings block regardless of scope match.
|
|
49
|
+
* Promoted entries represent crystallised team policy.
|
|
50
|
+
*
|
|
51
|
+
* Note: `recurrence_count` is NOT stored (planner cycle-1 Blocker B3 —
|
|
52
|
+
* storing it creates a write-while-write race when parallel advisors
|
|
53
|
+
* record). Promotion logic counts lazily inside `prune_learnings` and
|
|
54
|
+
* `read_learnings` rendering instead.
|
|
55
|
+
*/
|
|
56
|
+
archived: z.ZodOptional<z.ZodBoolean>;
|
|
57
|
+
promoted: z.ZodOptional<z.ZodBoolean>;
|
|
32
58
|
}, "strip", z.ZodTypeAny, {
|
|
33
59
|
agent: AgentName;
|
|
34
60
|
ts: string;
|
|
@@ -39,6 +65,8 @@ declare const learningEntrySchema: z.ZodObject<{
|
|
|
39
65
|
pr?: number | undefined;
|
|
40
66
|
branch?: string | undefined;
|
|
41
67
|
scope?: string | undefined;
|
|
68
|
+
archived?: boolean | undefined;
|
|
69
|
+
promoted?: boolean | undefined;
|
|
42
70
|
}, {
|
|
43
71
|
agent: AgentName;
|
|
44
72
|
ts: string;
|
|
@@ -49,6 +77,8 @@ declare const learningEntrySchema: z.ZodObject<{
|
|
|
49
77
|
pr?: number | undefined;
|
|
50
78
|
branch?: string | undefined;
|
|
51
79
|
scope?: string | undefined;
|
|
80
|
+
archived?: boolean | undefined;
|
|
81
|
+
promoted?: boolean | undefined;
|
|
52
82
|
}>;
|
|
53
83
|
export type LearningEntry = z.infer<typeof learningEntrySchema>;
|
|
54
84
|
/**
|
package/dist/learning/store.js
CHANGED
|
@@ -23,8 +23,18 @@ const learningEntrySchema = z.object({
|
|
|
23
23
|
ts: z.string().min(1).max(40),
|
|
24
24
|
/** PR number when recorded from `/squad:review #N`; optional otherwise. */
|
|
25
25
|
pr: z.number().int().positive().optional(),
|
|
26
|
-
/**
|
|
27
|
-
|
|
26
|
+
/**
|
|
27
|
+
* Branch name when recorded from a local review (no PR ref). v0.11.0
|
|
28
|
+
* cycle-2 (security Major M2): NUL-byte rejection mirrors the tool-edge
|
|
29
|
+
* `SafeString` so the store schema rejects a hostile row even if a
|
|
30
|
+
* future caller bypasses the tool.
|
|
31
|
+
*/
|
|
32
|
+
branch: z
|
|
33
|
+
.string()
|
|
34
|
+
.min(1)
|
|
35
|
+
.max(255)
|
|
36
|
+
.refine((v) => v.indexOf("\0") === -1, "must not contain NUL byte")
|
|
37
|
+
.optional(),
|
|
28
38
|
/** Which agent's finding this decision concerns. */
|
|
29
39
|
agent: z.enum(AGENT_NAMES_TUPLE),
|
|
30
40
|
/** Severity at the time of the decision (Blocker / Major / Minor / Suggestion). */
|
|
@@ -40,7 +50,33 @@ const learningEntrySchema = z.object({
|
|
|
40
50
|
* absent, the decision applies repo-wide. Used by the formatter to filter
|
|
41
51
|
* learnings down to those relevant to the current diff.
|
|
42
52
|
*/
|
|
43
|
-
scope: z
|
|
53
|
+
scope: z
|
|
54
|
+
.string()
|
|
55
|
+
.min(1)
|
|
56
|
+
.max(512)
|
|
57
|
+
.refine((v) => v.indexOf("\0") === -1, "must not contain NUL byte")
|
|
58
|
+
.optional(),
|
|
59
|
+
/**
|
|
60
|
+
* v0.11.0+ lifecycle fields. All optional and additive — a v0.10.x reader
|
|
61
|
+
* encountering these fields silently strips them (Zod default), so the
|
|
62
|
+
* journal can be read by older clients without crashing. A v0.11.0+ reader
|
|
63
|
+
* uses them to suppress (archived) or surface (promoted) entries.
|
|
64
|
+
*
|
|
65
|
+
* - `archived: true` — entry is past `max_age_days` and is hidden from
|
|
66
|
+
* default `readLearnings` output. Set by `prune_learnings`. The entry
|
|
67
|
+
* stays on disk for forensics; not deleted.
|
|
68
|
+
* - `promoted: true` — entry has been accepted ≥ `min_recurrence` times
|
|
69
|
+
* (across all agents, matched by `normalizeFindingTitle`) and is surfaced
|
|
70
|
+
* FIRST in the rendered learnings block regardless of scope match.
|
|
71
|
+
* Promoted entries represent crystallised team policy.
|
|
72
|
+
*
|
|
73
|
+
* Note: `recurrence_count` is NOT stored (planner cycle-1 Blocker B3 —
|
|
74
|
+
* storing it creates a write-while-write race when parallel advisors
|
|
75
|
+
* record). Promotion logic counts lazily inside `prune_learnings` and
|
|
76
|
+
* `read_learnings` rendering instead.
|
|
77
|
+
*/
|
|
78
|
+
archived: z.boolean().optional(),
|
|
79
|
+
promoted: z.boolean().optional(),
|
|
44
80
|
});
|
|
45
81
|
/**
|
|
46
82
|
* Default location for the JSONL file, relative to workspace_root. Repo-versioned
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"store.js","sourceRoot":"","sources":["../../src/learning/store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,IAAI,EAAE,EAAE,MAAM,SAAS,CAAC;AACzC,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,iBAAiB,EAAkB,MAAM,+BAA+B,CAAC;AAClF,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,wBAAwB,EAAE,MAAM,wBAAwB,CAAC;AAClE,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACpD,OAAO,EAAE,MAAM,EAAE,MAAM,4BAA4B,CAAC;AAEpD;;;;GAIG;AACH,MAAM,eAAe,GAAG,KAAK,CAAC;AAE9B;;;;;GAKG;AACH,MAAM,mBAAmB,GAAG,CAAC,CAAC,MAAM,CAAC;IACnC,iDAAiD;IACjD,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC;IAC7B,2EAA2E;IAC3E,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;IAC1C,
|
|
1
|
+
{"version":3,"file":"store.js","sourceRoot":"","sources":["../../src/learning/store.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import type { ToolDef } from "./registry.js";
|
|
3
|
+
/**
|
|
4
|
+
* Lifecycle maintenance for `.squad/learnings.jsonl` (v0.11.0+).
|
|
5
|
+
*
|
|
6
|
+
* Two passes, both running inside the same atomic-rewrite (lock + rename-
|
|
7
|
+
* rename) under `withFileLock`:
|
|
8
|
+
*
|
|
9
|
+
* 1. **Age cutoff** — when `max_age_days > 0`, entries whose `ts` is older
|
|
10
|
+
* than `now - max_age_days` are marked `archived: true`. Archived
|
|
11
|
+
* entries stay on disk for forensics; they are suppressed from the
|
|
12
|
+
* default `read_learnings` read path (use `include_archived: true` to
|
|
13
|
+
* surface them).
|
|
14
|
+
*
|
|
15
|
+
* 2. **Promotion** — entries are grouped by `normalizeFindingTitle(finding)`.
|
|
16
|
+
* For each group, the number of `decision: "accept"` entries that are
|
|
17
|
+
* NOT already archived is counted. When the count is ≥ `min_recurrence`,
|
|
18
|
+
* the most-recent accepted entry in the group is marked `promoted: true`
|
|
19
|
+
* (the rest stay un-promoted to avoid noisy duplicates in the rendered
|
|
20
|
+
* block). Promoted entries surface FIRST in `read_learnings` output
|
|
21
|
+
* regardless of scope match — they represent crystallised team policy.
|
|
22
|
+
*
|
|
23
|
+
* **Never auto-runs.** The default `max_age_days` is `0` (= disabled) so
|
|
24
|
+
* invoking the tool with no input is a safe no-op. Users who want regular
|
|
25
|
+
* housekeeping run it themselves (or wire a cron / git pre-commit hook).
|
|
26
|
+
*
|
|
27
|
+
* **Auditability**: pruning produces a diff on the on-disk journal. Repos
|
|
28
|
+
* that commit `.squad/learnings.jsonl` should expect a non-trivial diff
|
|
29
|
+
* after a non-no-op run. The `.prev` snapshot from the atomic rewrite is
|
|
30
|
+
* the rollback point.
|
|
31
|
+
*
|
|
32
|
+
* **Race safety**: the entire read-modify-write happens under the file
|
|
33
|
+
* lock used by `appendLearning`. Concurrent appenders block until the
|
|
34
|
+
* prune finishes; concurrent readers (no lock by design) see either the
|
|
35
|
+
* pre-prune or post-prune file in full because rename-into-place is
|
|
36
|
+
* atomic on POSIX same-filesystem renames.
|
|
37
|
+
*/
|
|
38
|
+
declare const schema: z.ZodObject<{
|
|
39
|
+
workspace_root: z.ZodString;
|
|
40
|
+
/**
|
|
41
|
+
* Age cutoff in days. Entries older than `now - max_age_days` are marked
|
|
42
|
+
* archived. Default `0` (= disabled — explicit user opt-in required).
|
|
43
|
+
* The plan v2 default departs from the plan v1 default of 180 to avoid
|
|
44
|
+
* surprising diff churn on repos that commit the journal.
|
|
45
|
+
*/
|
|
46
|
+
max_age_days: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
|
|
47
|
+
/**
|
|
48
|
+
* Promotion threshold. A finding-title group with ≥ this many accept
|
|
49
|
+
* decisions (across all agents, after archival) earns a `promoted: true`
|
|
50
|
+
* flag on its most-recent entry. Default 3 (1× = anecdote; 2× = pattern;
|
|
51
|
+
* 3× = team policy). Set 0 to disable promotion entirely. `1` is rejected
|
|
52
|
+
* because it would mark every singleton accept as promoted — defeating the
|
|
53
|
+
* "this is team policy" signal (developer cycle-2 Major M5).
|
|
54
|
+
*/
|
|
55
|
+
min_recurrence: z.ZodEffects<z.ZodDefault<z.ZodOptional<z.ZodNumber>>, number, number | undefined>;
|
|
56
|
+
/**
|
|
57
|
+
* When true, compute counts without writing. Useful for "what would this
|
|
58
|
+
* do?" inspection before running the destructive pass.
|
|
59
|
+
*/
|
|
60
|
+
dry_run: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
|
|
61
|
+
}, "strip", z.ZodTypeAny, {
|
|
62
|
+
workspace_root: string;
|
|
63
|
+
max_age_days: number;
|
|
64
|
+
min_recurrence: number;
|
|
65
|
+
dry_run: boolean;
|
|
66
|
+
}, {
|
|
67
|
+
workspace_root: string;
|
|
68
|
+
max_age_days?: number | undefined;
|
|
69
|
+
min_recurrence?: number | undefined;
|
|
70
|
+
dry_run?: boolean | undefined;
|
|
71
|
+
}>;
|
|
72
|
+
type Input = z.infer<typeof schema>;
|
|
73
|
+
export interface PruneLearningsOutput {
|
|
74
|
+
ok: true;
|
|
75
|
+
file: string;
|
|
76
|
+
total: number;
|
|
77
|
+
archived_count: number;
|
|
78
|
+
promoted_count: number;
|
|
79
|
+
unchanged_count: number;
|
|
80
|
+
dry_run: boolean;
|
|
81
|
+
}
|
|
82
|
+
export declare function pruneLearningsTool(input: Input): Promise<PruneLearningsOutput>;
|
|
83
|
+
export declare const pruneLearningsToolDef: ToolDef<typeof schema>;
|
|
84
|
+
export {};
|