@softspark/ai-toolkit 3.1.1 → 3.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,91 @@
1
+ # Strict Mode
2
+
3
+ Active when project sets `output-mode: strict` or user invokes `/brand-voice strict`. More aggressive than `concise`. Use for long sessions, expensive models, or batch operations where every token costs.
4
+
5
+ ## Targets
6
+
7
+ - Token output ≤40% of default
8
+ - No prose blocks — only lists, tables, code, exact strings
9
+ - No response longer than 8 lines unless data requires it
10
+
11
+ ## Hard Rules
12
+
13
+ 1. **No prose paragraphs.** Replace prose with bullet lists or tables.
14
+ 2. **No connective tissue.** Drop "because", "so", "therefore" unless the causal link is the answer.
15
+ 3. **Sentence fragments allowed** for non-data answers: "Done." "Yes." "Already exists at <path>." are valid responses.
16
+ 4. **Tables for any comparison ≥2 items.** Two-column min: `key | value`.
17
+ 5. **No examples unless asked.** Strict mode assumes the user knows what good output looks like.
18
+ 6. **No qualifiers.** Drop "approximately", "roughly", "around", "about" — give exact numbers or say "unknown".
19
+ 7. **No second-person framing.** "You should X" → "Do X." or "X is required."
20
+
21
+ ## Mandatory Cuts
22
+
23
+ | Pattern | Replacement |
24
+ |---------|-------------|
25
+ | Any paragraph >2 sentences | Bullet list |
26
+ | "There are N reasons..." | Numbered list directly |
27
+ | "Let me know if..." | (delete) |
28
+ | "Hope this helps" | (delete) |
29
+ | "Feel free to..." | (delete) |
30
+ | Repeating the question | (delete) |
31
+ | Re-explaining what was just done | (delete) |
32
+ | "Now that..." / "After that..." | (delete) |
33
+ | "It's important to note..." | State the fact, drop the framing |
34
+ | "In other words..." | Pick one phrasing, drop the other |
35
+
36
+ ## What Stays Full-Length
37
+
38
+ - Code blocks (never elide)
39
+ - File paths, error messages, stack traces (never truncate)
40
+ - Command output the user must see
41
+ - Test failure listings
42
+ - Security findings (CVE IDs, severity, file:line)
43
+ - Diff context lines
44
+
45
+ ## Format Skeleton
46
+
47
+ For most strict-mode responses, use this skeleton:
48
+
49
+ ```
50
+ <one-line answer or status>
51
+
52
+ <table or bullets if data>
53
+
54
+ <next action if applicable, one line>
55
+ ```
56
+
57
+ That's the whole response. No intro, no outro, no transitions.
58
+
59
+ ## Examples
60
+
61
+ **User asks: "is the test passing?"**
62
+
63
+ Default: ~40 tokens of explanation. Strict: `Yes. tests/test_brand_voice.bats: 5/5 passing in 0.3s.`
64
+
65
+ **User asks: "what changed in this commit?"**
66
+
67
+ Default: prose summary + diff highlights. Strict:
68
+
69
+ ```
70
+ Changed: app/skills/brand-voice/SKILL.md (+23 lines)
71
+ Added: app/skills/brand-voice/modes/concise.md
72
+ Added: app/skills/brand-voice/modes/strict.md
73
+ ```
74
+
75
+ **User asks: "should I use Postgres or MySQL?"**
76
+
77
+ Default: paragraph weighing options. Strict:
78
+
79
+ | Factor | Postgres | MySQL |
80
+ |--------|----------|-------|
81
+ | JSONB | yes | partial |
82
+ | Replication | logical+phys | logical+phys |
83
+ | Default for this stack | yes | no |
84
+
85
+ Recommend: Postgres.
86
+
87
+ ## Boundary Behavior
88
+
89
+ - Strict mode does NOT mean wrong. If a fact requires 5 lines to be correct, write 5 lines. Cut framing, never substance.
90
+ - Strict mode does NOT mean rude. Drop pleasantries, not respect.
91
+ - If the user explicitly asks for explanation, switch to concise mode for that response. Strict is the default, not a gag.
@@ -0,0 +1,246 @@
1
+ #!/usr/bin/env python3
2
+ """Measure token reduction of brand-voice output modes against fixtures.
3
+
4
+ Reads each fixture directory containing default.md, concise.md, strict.md,
5
+ counts tokens with a deterministic whitespace heuristic, and reports per-fixture
6
+ and aggregate ratios. Asserts:
7
+
8
+ concise <= CONCISE_BUDGET (default 0.60)
9
+ strict <= STRICT_BUDGET (default 0.40)
10
+
11
+ Also asserts fact preservation: every file path, identifier, line:number, and
12
+ fenced code block from default.md must appear in concise.md and strict.md.
13
+
14
+ Usage:
15
+ python3 app/skills/brand-voice/scripts/measure.py \\
16
+ --fixtures tests/fixtures/output-modes/
17
+
18
+ Exit codes:
19
+ 0 all fixtures pass budgets and fact preservation
20
+ 1 one or more fixtures violate budget or drop facts
21
+ 2 invalid fixtures dir or missing files
22
+ """
23
+ from __future__ import annotations
24
+
25
+ import argparse
26
+ import json
27
+ import re
28
+ import sys
29
+ from pathlib import Path
30
+
31
+ CONCISE_BUDGET_DEFAULT = 0.60
32
+ STRICT_BUDGET_DEFAULT = 0.40
33
+
34
+ PATH_RE = re.compile(r"\b[\w/.-]+\.[a-zA-Z]{1,5}\b")
35
+ LINE_REF_SUFFIX_RE = re.compile(r":\d+$")
36
+ CODE_BLOCK_RE = re.compile(r"```[\w]*\n.*?```", re.DOTALL)
37
+ NUMBER_WITH_UNIT_RE = re.compile(r"\b\d+(?:\.\d+)?(?:ms|s|kb|mb|gb|%)\b", re.IGNORECASE)
38
+ IDENT_BACKTICK_RE = re.compile(r"`([^`\n]+)`")
39
+
40
+
41
+ def count_tokens(text: str) -> int:
42
+ """Whitespace-based token estimate. Deterministic and offline."""
43
+ return len(text.split())
44
+
45
+
46
+ def normalize_path(value: str) -> str:
47
+ """Strip `:NN` line-number suffix so `foo.ts` and `foo.ts:42` compare equal."""
48
+ return LINE_REF_SUFFIX_RE.sub("", value).strip("`")
49
+
50
+
51
+ def extract_facts(text: str) -> set[str]:
52
+ """Extract load-bearing facts: file paths, numbers with units, backtick identifiers.
53
+
54
+ Does NOT extract code-block bodies. A mode response legitimately shows
55
+ different code (e.g. the fix) than the default response (e.g. the bug),
56
+ and treating each code line as a required fact produces false positives.
57
+ """
58
+ text_no_code = CODE_BLOCK_RE.sub("", text)
59
+ facts: set[str] = set()
60
+
61
+ for match in PATH_RE.findall(text_no_code):
62
+ normalized = normalize_path(match)
63
+ if "/" in normalized:
64
+ facts.add(normalized)
65
+
66
+ for match in IDENT_BACKTICK_RE.findall(text_no_code):
67
+ cleaned = match.strip()
68
+ if len(cleaned) >= 3 and not cleaned.startswith("```"):
69
+ facts.add(normalize_path(cleaned))
70
+
71
+ for match in NUMBER_WITH_UNIT_RE.findall(text_no_code):
72
+ facts.add(match.lower())
73
+
74
+ return facts
75
+
76
+
77
+ def load_must_contain(fixture_dir: Path) -> list[str]:
78
+ """Optional explicit must-contain assertions, one per line."""
79
+ must_contain_file = fixture_dir / "must_contain.txt"
80
+ if not must_contain_file.exists():
81
+ return []
82
+ return [
83
+ line.strip()
84
+ for line in must_contain_file.read_text().splitlines()
85
+ if line.strip() and not line.strip().startswith("#")
86
+ ]
87
+
88
+
89
+ def evaluate_fixture(
90
+ fixture_dir: Path,
91
+ concise_budget: float,
92
+ strict_budget: float,
93
+ ) -> dict:
94
+ default_file = fixture_dir / "default.md"
95
+ concise_file = fixture_dir / "concise.md"
96
+ strict_file = fixture_dir / "strict.md"
97
+
98
+ for path in (default_file, concise_file, strict_file):
99
+ if not path.exists():
100
+ return {
101
+ "fixture": fixture_dir.name,
102
+ "ok": False,
103
+ "error": f"missing file: {path.name}",
104
+ }
105
+
106
+ default_text = default_file.read_text()
107
+ concise_text = concise_file.read_text()
108
+ strict_text = strict_file.read_text()
109
+
110
+ default_tokens = count_tokens(default_text)
111
+ concise_tokens = count_tokens(concise_text)
112
+ strict_tokens = count_tokens(strict_text)
113
+
114
+ if default_tokens == 0:
115
+ return {
116
+ "fixture": fixture_dir.name,
117
+ "ok": False,
118
+ "error": "default.md empty",
119
+ }
120
+
121
+ concise_ratio = concise_tokens / default_tokens
122
+ strict_ratio = strict_tokens / default_tokens
123
+
124
+ default_facts = extract_facts(default_text)
125
+ concise_facts = extract_facts(concise_text)
126
+ strict_facts = extract_facts(strict_text)
127
+
128
+ concise_missing = sorted(default_facts - concise_facts)
129
+ strict_missing = sorted(default_facts - strict_facts)
130
+
131
+ must_contain = load_must_contain(fixture_dir)
132
+ concise_required_missing = [
133
+ item for item in must_contain if item not in concise_text
134
+ ]
135
+ strict_required_missing = [
136
+ item for item in must_contain if item not in strict_text
137
+ ]
138
+
139
+ budget_ok_concise = concise_ratio <= concise_budget
140
+ budget_ok_strict = strict_ratio <= strict_budget
141
+ facts_ok_concise = not concise_required_missing
142
+ facts_ok_strict = not strict_required_missing
143
+
144
+ return {
145
+ "fixture": fixture_dir.name,
146
+ "ok": budget_ok_concise
147
+ and budget_ok_strict
148
+ and facts_ok_concise
149
+ and facts_ok_strict,
150
+ "default_tokens": default_tokens,
151
+ "concise_tokens": concise_tokens,
152
+ "strict_tokens": strict_tokens,
153
+ "concise_ratio": round(concise_ratio, 3),
154
+ "strict_ratio": round(strict_ratio, 3),
155
+ "concise_budget_ok": budget_ok_concise,
156
+ "strict_budget_ok": budget_ok_strict,
157
+ "concise_advisory_missing": concise_missing,
158
+ "strict_advisory_missing": strict_missing,
159
+ "concise_required_missing": concise_required_missing,
160
+ "strict_required_missing": strict_required_missing,
161
+ }
162
+
163
+
164
+ def main() -> int:
165
+ parser = argparse.ArgumentParser(description=__doc__)
166
+ parser.add_argument(
167
+ "--fixtures",
168
+ type=Path,
169
+ default=Path("tests/fixtures/output-modes"),
170
+ help="Path to fixtures directory",
171
+ )
172
+ parser.add_argument(
173
+ "--concise-budget",
174
+ type=float,
175
+ default=CONCISE_BUDGET_DEFAULT,
176
+ help=f"Max concise/default token ratio (default {CONCISE_BUDGET_DEFAULT})",
177
+ )
178
+ parser.add_argument(
179
+ "--strict-budget",
180
+ type=float,
181
+ default=STRICT_BUDGET_DEFAULT,
182
+ help=f"Max strict/default token ratio (default {STRICT_BUDGET_DEFAULT})",
183
+ )
184
+ parser.add_argument(
185
+ "--json",
186
+ action="store_true",
187
+ help="Emit JSON report instead of human format",
188
+ )
189
+ args = parser.parse_args()
190
+
191
+ if not args.fixtures.is_dir():
192
+ print(f"error: fixtures dir not found: {args.fixtures}", file=sys.stderr)
193
+ return 2
194
+
195
+ fixture_dirs = sorted(
196
+ d for d in args.fixtures.iterdir() if d.is_dir() and (d / "default.md").exists()
197
+ )
198
+
199
+ if not fixture_dirs:
200
+ print(f"error: no fixtures found under {args.fixtures}", file=sys.stderr)
201
+ return 2
202
+
203
+ results = [
204
+ evaluate_fixture(d, args.concise_budget, args.strict_budget)
205
+ for d in fixture_dirs
206
+ ]
207
+
208
+ total_default = sum(r.get("default_tokens", 0) for r in results)
209
+ total_concise = sum(r.get("concise_tokens", 0) for r in results)
210
+ total_strict = sum(r.get("strict_tokens", 0) for r in results)
211
+ aggregate = {
212
+ "concise_ratio": round(total_concise / total_default, 3) if total_default else 0,
213
+ "strict_ratio": round(total_strict / total_default, 3) if total_default else 0,
214
+ "fixtures": len(results),
215
+ "passed": sum(1 for r in results if r.get("ok")),
216
+ }
217
+
218
+ if args.json:
219
+ print(json.dumps({"results": results, "aggregate": aggregate}, indent=2))
220
+ else:
221
+ print(f"{'fixture':<28} {'default':>8} {'concise':>8} {'strict':>8} {'c%':>6} {'s%':>6} {'ok':>4}")
222
+ print("-" * 76)
223
+ for r in results:
224
+ if "error" in r:
225
+ print(f"{r['fixture']:<28} ERROR: {r['error']}")
226
+ continue
227
+ mark = "yes" if r["ok"] else "NO"
228
+ print(
229
+ f"{r['fixture']:<28} {r['default_tokens']:>8} "
230
+ f"{r['concise_tokens']:>8} {r['strict_tokens']:>8} "
231
+ f"{int(r['concise_ratio'] * 100):>5}% "
232
+ f"{int(r['strict_ratio'] * 100):>5}% {mark:>4}"
233
+ )
234
+ print("-" * 76)
235
+ print(
236
+ f"aggregate: concise={int(aggregate['concise_ratio'] * 100)}% "
237
+ f"strict={int(aggregate['strict_ratio'] * 100)}% "
238
+ f"passed={aggregate['passed']}/{aggregate['fixtures']}"
239
+ )
240
+
241
+ all_ok = all(r.get("ok") for r in results)
242
+ return 0 if all_ok else 1
243
+
244
+
245
+ if __name__ == "__main__":
246
+ sys.exit(main())
@@ -18,6 +18,8 @@ Triggers the Chief of Staff to generate an executive summary.
18
18
  /briefing [period]
19
19
  # Example: /briefing today
20
20
  # Example: /briefing week
21
+ /briefing --tokens [--since 7d|24h|30m]
22
+ # Reports real session token usage from Claude Code JSONL.
21
23
  ```
22
24
 
23
25
  ## Protocol
@@ -58,6 +60,65 @@ Triggers the Chief of Staff to generate an executive summary.
58
60
  - "Recent runs" without an explicit time bound defaults to **everything** on some log backends. Always pass `--since` or a date filter, or you will read a week into yesterday's memory.
59
61
  - Successful runs outnumber interesting runs by an order of magnitude. Aggressively filter green/noop entries — they are the signal's noise floor.
60
62
 
63
+ ## Token Receipts
64
+
65
+ The `--tokens` flag reports real token usage parsed from Claude Code session JSONL — not estimates. Useful for:
66
+
67
+ - Verifying `output-mode: concise` actually reduces tokens vs default sessions
68
+ - Spotting expensive runs before they show up on the bill
69
+ - Capturing a baseline before changing prompts or skills
70
+
71
+ Underlying script: `scripts/session_token_stats.py`.
72
+
73
+ ```bash
74
+ # Aggregate current session
75
+ python3 scripts/session_token_stats.py --json
76
+
77
+ # Statusline-friendly one-line output
78
+ python3 scripts/session_token_stats.py --statusline
79
+
80
+ # Trend vs baseline
81
+ python3 scripts/session_token_stats.py --statusline --baseline ~/.softspark/ai-toolkit/baseline.json
82
+ ```
83
+
84
+ ### Status line (installed by default in v3.2.0+)
85
+
86
+ `ai-toolkit install` wires `~/.claude/settings.json` to `app/hooks/ai-toolkit-statusline.sh`. The hook reads native Claude Code statusLine stdin (no session JSONL parsing) and renders one line:
87
+
88
+ ```
89
+ ➜ <dir> git:(branch) ✗ ████░░░░░░ 43% ↑6.5k ↓252k effort:xhigh <model>
90
+ ```
91
+
92
+ Segments left to right:
93
+
94
+ - `➜ <dir>` — current directory basename
95
+ - `git:(branch) ✗` — git branch + dirty marker
96
+ - 10-cell **progress bar** for context-window usage. Color tiers: green `<70%`, orange `70–89%`, red `≥90%`
97
+ - `↑in ↓out` — token arrows. Green up = input (upload), red down = output (download). Both cumulative across the session.
98
+ - `effort:level` — Claude Code effort level (low / medium / high / xhigh)
99
+ - model name
100
+
101
+ Custom statusLine entries you set yourself (without the `_source: ai-toolkit` tag) are preserved untouched on install.
102
+
103
+ Opt-outs (no reinstall required):
104
+
105
+ - `AI_TOOLKIT_STATUSLINE_DISABLE=1` — silence the line entirely
106
+ - `AI_TOOLKIT_STATUSLINE_NO_TOKENS=1` — hide token arrows segment
107
+ - `AI_TOOLKIT_STATUSLINE_NO_GIT=1` — hide git segment
108
+ - `AI_TOOLKIT_STATUSLINE_NO_EFFORT=1` — hide effort level segment
109
+ - `AI_TOOLKIT_STATUSLINE_NO_COLOR=1` — disable ANSI colors
110
+ - `AI_TOOLKIT_STATUSLINE_SHOW_COST=1` — append Claude Code's reported cost (`cost.total_cost_usd`)
111
+ - `AI_TOOLKIT_STATUSLINE_DUMP=1` — write received stdin to `/tmp/cc-statusline-input.json` (debug)
112
+
113
+ ### Save a baseline
114
+
115
+ ```bash
116
+ python3 scripts/session_token_stats.py --json | jq '.totals' > ~/.softspark/ai-toolkit/baseline.json
117
+ export AI_TOOLKIT_STATUSLINE_BASELINE=~/.softspark/ai-toolkit/baseline.json
118
+ ```
119
+
120
+ The statusline then renders trend arrows (↑ / ↓) against that baseline.
121
+
61
122
  ## When NOT to Use
62
123
 
63
124
  - For a specific production incident — use `/workflow incident-response`
@@ -3,7 +3,7 @@ name: swarm
3
3
  description: "Execute tasks via Map-Reduce, Consensus, or Relay swarms"
4
4
  user-invocable: true
5
5
  effort: max
6
- argument-hint: "[map-reduce|consensus|relay] [task]"
6
+ argument-hint: "[map-reduce|consensus|relay] [--with-kb] [--worktree] [task]"
7
7
  context: fork
8
8
  agent: orchestrator
9
9
  model: opus
@@ -79,3 +79,85 @@ Agent(
79
79
  2. De-duplicate identical findings
80
80
  3. Synthesize unique insights
81
81
  4. Generate final swarm report
82
+
83
+ ## KB-First Mode (`--with-kb`)
84
+
85
+ When `$ARGUMENTS` contains `--with-kb`, every spawned agent MUST receive KB context grounded in the project knowledge base.
86
+
87
+ ### Required pre-flight (run BEFORE spawning agents)
88
+
89
+ 1. Call `mcp__rag-mcp__smart_query` with the original task as `query`. Use `use_multi_hop=true` if the task spans 2+ concepts.
90
+ 2. Capture `results[*].kb_id`, `title`, `content`, and `source_documents_used`.
91
+ 3. Build a `[KB CONTEXT]` block (max 10 entries, pruned to top scores).
92
+
93
+ ### Per-agent prompt template (mandatory under `--with-kb`)
94
+
95
+ ```
96
+ [KB CONTEXT — from rag-mcp smart_query, ground all decisions in these]
97
+ - {kb_id}: {title}
98
+ {content excerpt, ≤300 chars}
99
+ - ...
100
+
101
+ [YOUR SUB-TASK]
102
+ {specific sub-task, owned files, success criteria}
103
+
104
+ [RULES]
105
+ - Cite KB entries as [PATH: kb_id] when you rely on them.
106
+ - If KB is silent on a decision, state that explicitly — do NOT invent.
107
+ - After producing your output, call mcp__rag-mcp__verify_answer with your answer + the cited kb_ids; include the verdict in your final report.
108
+ ```
109
+
110
+ ### Aggregation under `--with-kb`
111
+
112
+ The synthesis step MUST include a `## KB Coverage` section listing which `kb_id`s were actually cited and any agent that returned `verdict: unsupported`.
113
+
114
+ ### When to skip `--with-kb`
115
+
116
+ - Pure code-mechanical tasks (rename, format, dependency bump) — KB adds noise.
117
+ - Tasks already scoped to one file with no cross-cutting concerns.
118
+
119
+ ## Isolated Worktrees Mode (`--worktree`)
120
+
121
+ When `$ARGUMENTS` contains `--worktree`, every spawned agent in **Map-Reduce** mode runs in its own git worktree on a throwaway branch. Aggregation merges or copies the changes back into the lead workspace.
122
+
123
+ ### Why
124
+
125
+ - Agents touching adjacent files (same module, different functions) can race.
126
+ - Writing to disjoint paths is not enough — file-locking, formatter cache, IDE indexers, and `.git/index.lock` all leak.
127
+ - Worktrees give each agent a real filesystem-level boundary plus a named branch for review.
128
+
129
+ ### How (mandatory under `--worktree`)
130
+
131
+ Pass `isolation: "worktree"` to every `Agent` call:
132
+
133
+ ```
134
+ Agent(
135
+ subagent_type="...",
136
+ description="...",
137
+ prompt="...",
138
+ isolation="worktree"
139
+ )
140
+ ```
141
+
142
+ The Agent tool returns the worktree path and branch name on completion. **Empty worktrees are auto-cleaned** by the runtime when the agent made no changes — you don't have to.
143
+
144
+ ### Aggregation under `--worktree`
145
+
146
+ After all agents return:
147
+
148
+ 1. List the returned `(path, branch)` pairs.
149
+ 2. For each non-empty result: `cd <main repo> && git merge --no-ff <branch>` (or cherry-pick the commits if the agent didn't commit).
150
+ 3. If any merge conflicts → escalate, do NOT auto-resolve. Cite which two agents touched the same hunk.
151
+ 4. After successful merge → delete the worktree: `git worktree remove <path>` and the throwaway branch.
152
+
153
+ ### When `--worktree` is mandatory (not optional)
154
+
155
+ - Map-Reduce with N≥3 agents touching the same module tree
156
+ - Any task that runs the project formatter or codegen
157
+ - Any task that mutates lockfiles, migrations, or generated artifacts
158
+
159
+ ### When to skip `--worktree`
160
+
161
+ - Consensus mode — agents return analysis text, not file changes.
162
+ - Relay mode — sequential by design, next agent reads prior agent's commit.
163
+ - Single-agent fallback or KB-only research swarms.
@@ -1,5 +1,5 @@
1
1
  {
2
- "last_run": "2026-04-28T09:44:52Z",
2
+ "last_run": "2026-05-04T08:36:05Z",
3
3
  "schema_version": 1,
4
4
  "tools": {
5
5
  "aider": {
@@ -24,10 +24,11 @@
24
24
  }
25
25
  },
26
26
  "augment": {
27
- "docs_hash": "7e632fc04e405f60",
27
+ "docs_hash": "f6b4c7fd64936879",
28
28
  "headings": [
29
29
  "Agent",
30
30
  "Code Completions",
31
+ "Documentation Index",
31
32
  "Introduction",
32
33
  "Next Edit",
33
34
  "\u200bAuggie CLI",
@@ -57,9 +58,10 @@
57
58
  }
58
59
  },
59
60
  "claude-code": {
60
- "docs_hash": "e51d7d44c72cd522",
61
+ "docs_hash": "7d2930e5f97e4126",
61
62
  "headings": [
62
63
  "Claude Code overview",
64
+ "Documentation Index",
63
65
  "\u200bGet started",
64
66
  "\u200bNext steps",
65
67
  "\u200bUse Claude Code everywhere",
@@ -102,15 +104,16 @@
102
104
  "slash command": true,
103
105
  "sub-agent": true
104
106
  },
105
- "version": "2.1.121 (Claude Code)"
107
+ "version": "2.1.126 (Claude Code)"
106
108
  },
107
109
  "cline": {
108
- "docs_hash": "0c69984492abf906",
110
+ "docs_hash": "eec0384d180243aa",
109
111
  "headings": [
110
112
  "Cline CLI",
111
113
  "Cline Documentation",
112
114
  "Core Workflows",
113
115
  "Customization",
116
+ "Documentation Index",
114
117
  "Features",
115
118
  "Install Cline",
116
119
  "MCP Servers",
@@ -135,7 +138,7 @@
135
138
  }
136
139
  },
137
140
  "codex-cli": {
138
- "docs_hash": "9e188e7e454523b6",
141
+ "docs_hash": "5a4b2d4eeee1d675",
139
142
  "headings": [
140
143
  "About",
141
144
  "Contributing",
@@ -154,7 +157,7 @@
154
157
  "Packages 0",
155
158
  "Provide feedback",
156
159
  "Quickstart",
157
- "Releases 744",
160
+ "Releases 757",
158
161
  "Repository files navigation",
159
162
  "Resources",
160
163
  "Saved searches",
@@ -187,12 +190,12 @@
187
190
  "version": "codex-cli 0.125.0"
188
191
  },
189
192
  "cursor": {
190
- "docs_hash": "56983669e33487e2",
193
+ "docs_hash": "4c3678f1353e012f",
191
194
  "headings": [],
192
195
  "markers": {
193
196
  ".cursor/rules": false,
194
197
  "AGENTS.md": false,
195
- "Agent Mode": false,
198
+ "Agent Mode": true,
196
199
  "Composer": true,
197
200
  "cursorrules": false,
198
201
  "hooks.json": false,
@@ -203,7 +206,7 @@
203
206
  }
204
207
  },
205
208
  "gemini-cli": {
206
- "docs_hash": "fa8989b65df2bb73",
209
+ "docs_hash": "20c254199b42f18c",
207
210
  "headings": [
208
211
  "Breadcrumbs",
209
212
  "Directory actions",
@@ -242,7 +245,7 @@
242
245
  }
243
246
  },
244
247
  "github-copilot": {
245
- "docs_hash": "c2100e9dca8dce0d",
248
+ "docs_hash": "6467b14babd06b26",
246
249
  "headings": [
247
250
  "About Copilot auto model selection",
248
251
  "About Copilot integrations",
@@ -290,7 +293,7 @@
290
293
  }
291
294
  },
292
295
  "opencode": {
293
- "docs_hash": "5c0f3f9c9b031f49",
296
+ "docs_hash": "c8c233106f166444",
294
297
  "headings": [
295
298
  "Add features",
296
299
  "Ask questions",
@@ -351,12 +354,13 @@
351
354
  }
352
355
  },
353
356
  "windsurf": {
354
- "docs_hash": "9cadd867b0cec6da",
357
+ "docs_hash": "167ee1fc000f03d9",
355
358
  "headings": [
356
359
  "Advanced",
357
360
  "App Deploys",
358
361
  "Cascade",
359
362
  "Context Awareness",
363
+ "Documentation Index",
360
364
  "MCP",
361
365
  "Memories",
362
366
  "Recommended Plugins",
package/bin/ai-toolkit.js CHANGED
@@ -51,6 +51,7 @@ const SCRIPT_COMMANDS = {
51
51
  'evaluate': { script: 'evaluate_skills.py', toolkitCwd: true },
52
52
  'stats': { script: 'stats.py' },
53
53
  'compile-slm': { script: 'compile_slm.py' },
54
+ 'pack-codebase': { script: 'pack_codebase.py' },
54
55
  };
55
56
 
56
57
  // ---------------------------------------------------------------------------
@@ -106,6 +107,7 @@ const COMMANDS = {
106
107
  'opencode-json': 'Merge .mcp.json servers into opencode.json',
107
108
  'agents-md': 'Regenerate AGENTS.md from agent definitions',
108
109
  'compile-slm': 'Compile toolkit into a minimal SLM system prompt (--budget, --model-size, --dry-run)',
110
+ 'pack-codebase': 'Pack the current codebase into a single AI-friendly markdown file (--budget, --include, --exclude, --dry-run)',
109
111
  'llms-txt': 'Generate llms.txt and llms-full.txt',
110
112
  'generate-all': 'Generate all platform configs at once (agents, cursor, windsurf, copilot, gemini, cline, roo, aider, augment, antigravity, codex, opencode, llms)',
111
113
  help: 'Show this help message',