@softspark/ai-toolkit 3.1.1 → 3.2.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/AGENTS.md +3 -3
- package/CHANGELOG.md +30 -0
- package/README.md +10 -8
- package/app/.claude-plugin/plugin.json +1 -1
- package/app/ARCHITECTURE.md +47 -0
- package/app/agents/seo-specialist.md +86 -3
- package/app/hooks/ai-toolkit-statusline.sh +201 -0
- package/app/hooks.json +5 -0
- package/app/skills/brand-voice/SKILL.md +38 -2
- package/app/skills/brand-voice/modes/concise.md +67 -0
- package/app/skills/brand-voice/modes/strict.md +91 -0
- package/app/skills/brand-voice/scripts/measure.py +246 -0
- package/app/skills/briefing/SKILL.md +61 -0
- package/app/skills/swarm/SKILL.md +83 -1
- package/benchmarks/ecosystem-doctor-snapshot.json +17 -13
- package/bin/ai-toolkit.js +2 -0
- package/kb/history/completed/f2-mcp-trim-spike-20260504.md +117 -0
- package/kb/history/completed/output-token-discipline-plan-20260504.md +261 -0
- package/kb/planning/mcp-context-trim-v4-prd.md +158 -0
- package/kb/procedures/release-verification-sop.md +8 -5
- package/kb/reference/architecture-overview.md +2 -2
- package/kb/reference/skills-catalog.md +3 -3
- package/llms-full.txt +569 -12
- package/llms.txt +3 -0
- package/manifest.json +1 -1
- package/package.json +1 -1
- package/scripts/install_steps/hooks.py +33 -0
- package/scripts/merge-hooks.py +17 -0
- package/scripts/pack_codebase.py +362 -0
- package/scripts/session_token_stats.py +264 -0
|
@@ -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-
|
|
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": "
|
|
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": "
|
|
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.
|
|
107
|
+
"version": "2.1.126 (Claude Code)"
|
|
106
108
|
},
|
|
107
109
|
"cline": {
|
|
108
|
-
"docs_hash": "
|
|
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": "
|
|
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
|
|
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": "
|
|
193
|
+
"docs_hash": "4c3678f1353e012f",
|
|
191
194
|
"headings": [],
|
|
192
195
|
"markers": {
|
|
193
196
|
".cursor/rules": false,
|
|
194
197
|
"AGENTS.md": false,
|
|
195
|
-
"Agent Mode":
|
|
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": "
|
|
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": "
|
|
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": "
|
|
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": "
|
|
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',
|