@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.
package/AGENTS.md CHANGED
@@ -348,7 +348,7 @@ Security expert. Use for OWASP Top 10, CVE analysis, security audits, penetratio
348
348
 
349
349
  ### `seo-specialist`
350
350
 
351
- Search engine optimization specialist. Trigger words: SEO, search engine, meta tags, structured data, Core Web Vitals, sitemap, robots.txt, schema.org
351
+ Search engine + generative engine optimization specialist. Trigger words: SEO, GEO, AEO, search engine, meta tags, structured data, Core Web Vitals, sitemap, robots.txt, schema.org, llms.txt, ChatGPT visibility, Claude citation, Perplexity ranking, AI Overviews
352
352
 
353
353
  **Tools:** `Read, Write, Edit, Bash, Grep, Glob`
354
354
 
@@ -436,7 +436,7 @@ Specialized agent personas — apply their expertise for relevant tasks:
436
436
  - **search-specialist**: Information retrieval and search optimization specialist. Trigger words: search, query, semantic search, information retrieval, relevance, ranking, search optimization
437
437
  - **security-architect**: Proactive security design expert. Use for Threat Modeling, architecture security reviews, and designing secure systems (AuthN/AuthZ, Crypto).
438
438
  - **security-auditor**: Security expert. Use for OWASP Top 10, CVE analysis, security audits, penetration testing, vulnerability assessment, hardening. Triggers: security, owasp, cve, vulnerability, audit, hardening, penetration, pentest, injection test, api security.
439
- - **seo-specialist**: Search engine optimization specialist. Trigger words: SEO, search engine, meta tags, structured data, Core Web Vitals, sitemap, robots.txt, schema.org
439
+ - **seo-specialist**: Search engine + generative engine optimization specialist. Trigger words: SEO, GEO, AEO, search engine, meta tags, structured data, Core Web Vitals, sitemap, robots.txt, schema.org, llms.txt, ChatGPT visibility, Claude citation, Perplexity ranking, AI Overviews
440
440
  - **system-governor**: The Guardian of the Constitution. Validates all evolutionary changes and enforces immutable rules. Has VETO power.
441
441
  - **tech-lead**: Technical authority for code quality, architecture patterns, and stack decisions. Use for code reviews, technological disputes, and standards enforcement.
442
442
  - **technical-researcher**: Deep technical investigation and multi-source research synthesis specialist. Trigger words: technical research, feasibility study, root cause analysis, API investigation, compatibility research, comparison matrix, synthesize, aggregate, report, executive summary, gap analysis, findings, multi-source, cross-reference
@@ -454,7 +454,7 @@ Skills are invocable commands or auto-loaded knowledge sources:
454
454
  - **architecture-audit**: Explore codebase organically for architectural friction, discover shallow modules, and propose module-deepening refactors as GitHub issue RFCs using parallel sub-agent interface designs. Use when user wants to improve architecture, find shallow modules, deepen modules, or reduce coupling. Codex-adapted: uses native subagents and plan tracking.
455
455
  - **architecture-decision**: Architecture decision making via trade-off analysis in RFC/RFD/ADR format: context, constraints, 3+ options, comparison, recommendation. Triggers: architecture decision, ADR, RFC, RFD, trade-offs, options comparison, design choice, pick between, should we use, evaluate approach. Load when weighing 2+ architectural options or writing decision records.
456
456
  - **biz-scan**: Scans codebase for business opportunities by analyzing database schemas, API endpoints, tracking events, and feature flags to surface underutilized capabilities, missing KPIs, and monetization gaps. Use when the user asks about revenue opportunities, business metrics, KPI coverage, analytics gaps, or monetization analysis of a codebase.
457
- - **brand-voice**: Loaded when writing documentation, content, README, or user-facing text. Prevents generic LLM rhetoric and enforces direct, technical voice.
457
+ - **brand-voice**: Loaded when writing documentation, content, README, or user-facing text, AND when an output-mode is set for conversational responses. Prevents generic LLM rhetoric, enforces direct technical voice, and supplies optional concise/strict response modes.
458
458
  - **briefing**: Generate an executive daily briefing that aggregates reports from all agents into a short, decision-focused summary. Use when the user asks for a status update across the whole system — not for one-agent activity reports.
459
459
  - **build**: Build the project with auto-detected toolchain (npm, poetry, cargo, go, flutter, Docker). Use when the user asks to compile, bundle, or produce artifacts — not to run tests or deploy.
460
460
  - **chaos**: Inject controlled faults for resilience testing on non-production targets. Use when the user explicitly asks for a chaos experiment, latency injection, or dependency kill — never implicit.
package/CHANGELOG.md CHANGED
@@ -7,6 +7,46 @@ Versioning follows [Semantic Versioning](https://semver.org/).
7
7
 
8
8
  ---
9
9
 
10
+ ## v3.2.1 — Statusline shellcheck cleanup (2026-05-04)
11
+
12
+ Patch release. Lint-only fix in the default status line hook — no behavior change.
13
+
14
+ ### Fixed
15
+
16
+ - **`app/hooks/ai-toolkit-statusline.sh` shellcheck warnings** — removed the unused `C_GRAY` color variable (SC2034) and replaced the deprecated `[ p -o q ]` form with `{ [ p ] || [ q ]; }` (SC2166). CI shellcheck gate at `--severity=warning` now passes clean across `app/hooks/*.sh`.
17
+
18
+ ---
19
+
20
+ ## v3.2.0 — Output discipline & token-aware status line (2026-05-04)
21
+
22
+ Minor release. Two coordinated additions: per-response output discipline and a real-token status line installed by default.
23
+
24
+ ### Added
25
+
26
+ - **`brand-voice` output modes** — `concise` (≤60% tokens) and `strict` (≤40% tokens) extend the existing brand-voice skill with response-length governance. Activate via `output-mode: concise` in project `CLAUDE.md` or `/brand-voice concise`. Mode rules in `app/skills/brand-voice/modes/`.
27
+ - **`measure.py` token evaluator** — fixture-driven before/after comparison at `app/skills/brand-voice/scripts/measure.py`. Asserts budget compliance and load-bearing fact preservation via optional `must_contain.txt` per fixture. JSON and human report formats.
28
+ - **`session_token_stats.py`** — stdlib-only Claude Code session JSONL parser at `scripts/session_token_stats.py`. Reports real input/output/cache token counts per session, supports `--since`, `--baseline`, `--statusline`, and `--json` modes.
29
+ - **Comprehensive default status line** — new `app/hooks/ai-toolkit-statusline.sh` renders a single line with cwd, git branch + dirty marker, context-window as a 10-cell progress bar (green <70% / orange 70–89% / red ≥90%), upload arrow `↑` (input tokens, green) + download arrow `↓` (output tokens, red), effort level, and model name. All token + cost data is read directly from Claude Code's statusLine stdin (`context_window.total_input_tokens`, `total_output_tokens`, `cost.total_cost_usd`, `effort.level`) — no JSONL parsing in the hot path, ~50ms cold start. Installed automatically into `~/.claude/settings.json` by `ai-toolkit install` (and `update`). User-customized statusLine entries (without the ai-toolkit `_source` tag) are preserved untouched. Cost-estimate segment is opt-in via `AI_TOOLKIT_STATUSLINE_SHOW_COST=1`.
30
+ - **`merge-hooks.py` statusLine support** — `inject` writes the toolkit statusLine only when the target has none or has a stale toolkit-installed one. `strip` removes only toolkit-installed entries.
31
+ - **`briefing` skill `--tokens` mode** — `/briefing --tokens [--since 7d]` for explicit reporting and baseline capture. Includes wire-up docs for the status-line hook and `AI_TOOLKIT_STATUSLINE_*` opt-out env vars.
32
+ - **Hook runtime scripts deployed alongside hooks** — `scripts/install_steps/hooks.py` now copies a curated allowlist of Python helpers (`session_token_stats.py`, `version_check.py`) to `~/.softspark/ai-toolkit/scripts/`, so hooks work on a fresh install before the npm-global package catches up. Hook resolution order: `AI_TOOLKIT_DIR` → `~/.softspark/ai-toolkit/` → npm global → walk up from script (dev fallback).
33
+
34
+ ### Opt-out
35
+
36
+ - `AI_TOOLKIT_STATUSLINE_DISABLE=1` silences output entirely
37
+ - `AI_TOOLKIT_STATUSLINE_NO_TOKENS=1` hides token arrows segment
38
+ - `AI_TOOLKIT_STATUSLINE_NO_GIT=1` hides git segment
39
+ - `AI_TOOLKIT_STATUSLINE_NO_EFFORT=1` hides effort level segment
40
+ - `AI_TOOLKIT_STATUSLINE_NO_COLOR=1` disables ANSI colors
41
+ - `AI_TOOLKIT_STATUSLINE_SHOW_COST=1` appends Claude Code's reported `cost.total_cost_usd` (off by default — long Opus sessions show alarming numbers)
42
+ - `AI_TOOLKIT_STATUSLINE_DUMP=1` writes received stdin to `/tmp/cc-statusline-input.json` (debug only)
43
+
44
+ ### Tests
45
+
46
+ - 38 new bats tests across `tests/test_brand_voice.bats`, `tests/test_session_token_stats.bats`, `tests/test_statusline_hook.bats`, `tests/test_merge_hooks_statusline.bats`, plus install-step assertion in `tests/test_install.bats`.
47
+
48
+ ---
49
+
10
50
  ## v3.1.1 — Windows install fix (2026-04-29)
11
51
 
12
52
  Patch release. `ai-toolkit install --local` crashed on Windows before doing any work because `scripts/install_steps/project_registry.py` imported `fcntl` (POSIX-only) at module load, raising `ModuleNotFoundError: No module named 'fcntl'`.
package/README.md CHANGED
@@ -6,7 +6,7 @@
6
6
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
7
7
  [![Skills](https://img.shields.io/badge/skills-112-brightgreen)](app/skills/)
8
8
  [![Agents](https://img.shields.io/badge/agents-44-blue)](app/agents/)
9
- [![Tests](https://img.shields.io/badge/tests-974%20passing-success)](tests/)
9
+ [![Tests](https://img.shields.io/badge/tests-1038%20passing-success)](tests/)
10
10
 
11
11
  <p align="center">
12
12
  <img src="assets/ai-toolkit-readme-hero.png" alt="ai-toolkit 3 README hero showing one toolkit for 12 AI editors" width="900">
@@ -14,12 +14,11 @@
14
14
 
15
15
  ---
16
16
 
17
- ## What's New in v3.1.1
17
+ ## What's New in v3.2.1
18
18
 
19
- Patch release fixing the Windows install crash introduced when project registration started using a POSIX-only file lock.
19
+ Patch release. Shellcheck cleanup of the default status line hook no behavior change.
20
20
 
21
- - **Windows install no longer crashes** — `ai-toolkit install --local` on Windows hit `ModuleNotFoundError: No module named 'fcntl'` before doing any work. The project registry now selects `fcntl` (POSIX) or `msvcrt.locking` (Windows) at runtime; POSIX semantics are unchanged.
22
- - **Regression test on POSIX CI** — `tests/test_windows_support.bats` simulates the missing-`fcntl` scenario and exercises the Windows lock branch via a stubbed `msvcrt`, so this regression is caught without a Windows runner.
21
+ - **Statusline lint cleanup** — `app/hooks/ai-toolkit-statusline.sh` now passes `shellcheck --severity=warning`: removed the unused `C_GRAY` color variable (SC2034) and replaced the deprecated `[ p -o q ]` form with `{ [ p ] || [ q ]; }` (SC2166).
23
22
 
24
23
  See [CHANGELOG.md](CHANGELOG.md) for full history.
25
24
 
@@ -126,8 +125,8 @@ See [CLI Reference](kb/reference/cli-reference.md) for all commands and options.
126
125
  | Component | Count | Description |
127
126
  |-----------|-------|-------------|
128
127
  | `skills/` (task) | 32 | Slash commands: `/commit`, `/build`, `/deploy`, `/test`, `/mcp-builder`, ... |
129
- | `skills/` (hybrid) | 31 | Slash commands with agent knowledge base |
130
- | `skills/` (knowledge) | 49 | Domain knowledge auto-loaded by agents (includes 13 `<lang>-rules` skills) |
128
+ | `skills/` (hybrid) | 32 | Slash commands with agent knowledge base |
129
+ | `skills/` (knowledge) | 48 | Domain knowledge auto-loaded by agents (includes 13 `<lang>-rules` skills) |
131
130
  | `agents/` | 44 | Specialized agents across 10 categories |
132
131
  | `hooks/` | 21 global + 5 skill-scoped | Quality gates, path safety, prompt governance, session lifecycle |
133
132
  | `plugins/` | 11 packs | Opt-in domain bundles (security, research, frontend, enterprise, 6 language packs) |
@@ -152,7 +151,7 @@ ai-toolkit/
152
151
  │ └── ARCHITECTURE.md # Full system design
153
152
  ├── kb/ # Reference docs, procedures, plans
154
153
  ├── scripts/ # Validation, install, evaluation scripts
155
- ├── tests/ # Bats test suite (974 tests)
154
+ ├── tests/ # Bats test suite (1038 tests)
156
155
  └── CHANGELOG.md
157
156
  ```
158
157
 
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "ai-toolkit",
3
3
  "description": "Professional-grade Claude Code toolkit with persona presets, skill security auditor, expanded lifecycle hooks, experimental opt-in plugin packs, benchmark harvesting, and multi-tool support.",
4
- "version": "3.1.1",
4
+ "version": "3.2.1",
5
5
  "author": {
6
6
  "name": "SoftSpark",
7
7
  "url": "https://github.com/softspark"
@@ -12,6 +12,53 @@ Universal multi-agent system for software development. Works across all reposito
12
12
 
13
13
  ---
14
14
 
15
+ ## Layered Model (the five-layer mental model)
16
+
17
+ The toolkit is organised as five stacked layers. Higher layers depend on lower layers, never the other way around. Treat this as the canonical mental model when adding features — pick the lowest layer that fits.
18
+
19
+ ```
20
+ ┌─────────────────────────────────────────────────────────────┐
21
+ │ Layer 5 — Plugins (distribution) │
22
+ │ Bundles for npm / marketplace / ai-toolkit eject │
23
+ │ Files: app/plugins/, plugin.json, package.json │
24
+ ├─────────────────────────────────────────────────────────────┤
25
+ │ Layer 4 — Subagents (delegation) │
26
+ │ Specialised personas spawned via Agent / Teams │
27
+ │ Files: app/agents/, .claude/agents/ │
28
+ ├─────────────────────────────────────────────────────────────┤
29
+ │ Layer 3 — Hooks (the guardrail layer) │
30
+ │ Lifecycle events: SessionStart, PreToolUse, Stop, … │
31
+ │ Files: app/hooks/*.sh, settings.json hooks block │
32
+ ├─────────────────────────────────────────────────────────────┤
33
+ │ Layer 2 — Skills (the knowledge layer) │
34
+ │ Slash-commands, knowledge skills, hybrid skills │
35
+ │ Files: app/skills/<name>/SKILL.md │
36
+ ├─────────────────────────────────────────────────────────────┤
37
+ │ Layer 1 — CLAUDE.md (the memory layer) │
38
+ │ Persistent rules + auto-memory loaded into every session │
39
+ │ Files: CLAUDE.md (global / project / .claude) │
40
+ └─────────────────────────────────────────────────────────────┘
41
+ ```
42
+
43
+ ### Where does my new feature go?
44
+
45
+ | If the feature is… | Add it at | Examples |
46
+ |---|---|---|
47
+ | A persistent rule the model must always know | Layer 1 (CLAUDE.md) | "always sync_tasks first", commit conventions |
48
+ | A reusable workflow / domain knowledge | Layer 2 (Skills) | `/debug`, `/review`, `python-rules` |
49
+ | Behavior the harness must enforce automatically | Layer 3 (Hooks) | block destructive `rm -rf`, run lint on Stop |
50
+ | A specialised role for delegation | Layer 4 (Subagents) | `security-auditor`, `database-architect` |
51
+ | A shippable bundle for other repos | Layer 5 (Plugins) | `app/plugins/python-stack/` |
52
+
53
+ ### Cross-cutting rules
54
+
55
+ - A skill MAY trigger an agent (`agent: <name>` frontmatter) — that's Layer 2 calling Layer 4.
56
+ - A hook MUST NOT call a skill — hooks are deterministic shell, skills are LLM-driven.
57
+ - A plugin MAY bundle skills + agents + hooks, but never the inverse.
58
+ - CLAUDE.md is read by every layer; it is the only layer everyone reads.
59
+
60
+ ---
61
+
15
62
  ## Agents
16
63
 
17
64
  ### Orchestration & Planning (4)
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  name: seo-specialist
3
- description: "Search engine optimization specialist. Trigger words: SEO, search engine, meta tags, structured data, Core Web Vitals, sitemap, robots.txt, schema.org"
3
+ description: "Search engine + generative engine optimization specialist. Trigger words: SEO, GEO, AEO, search engine, meta tags, structured data, Core Web Vitals, sitemap, robots.txt, schema.org, llms.txt, ChatGPT visibility, Claude citation, Perplexity ranking, AI Overviews"
4
4
  tools: Read, Write, Edit, Bash, Grep, Glob
5
5
  model: sonnet
6
6
  color: cyan
7
7
  skills: clean-code, seo-validate
8
8
  ---
9
9
 
10
- # SEO Specialist
10
+ # SEO + GEO Specialist
11
11
 
12
- Search engine optimization specialist.
12
+ Optimization for both classical search engines AND generative engines (ChatGPT, Claude, Perplexity, Google AI Overviews, Gemini).
13
13
 
14
14
  ## Expertise
15
15
  - Technical SEO
@@ -17,6 +17,7 @@ Search engine optimization specialist.
17
17
  - Core Web Vitals
18
18
  - Structured data (Schema.org)
19
19
  - SEO auditing
20
+ - **Generative Engine Optimization (GEO)** — being cited by LLM-based answer engines
20
21
 
21
22
  ## Responsibilities
22
23
 
@@ -108,4 +109,86 @@ Sitemap: https://example.com/sitemap.xml
108
109
  ```python
109
110
  smart_query("SEO optimization patterns")
110
111
  hybrid_search_kb("technical SEO checklist")
112
+ smart_query("GEO generative engine optimization")
111
113
  ```
114
+
115
+ ---
116
+
117
+ ## Generative Engine Optimization (GEO)
118
+
119
+ Goal: get cited by ChatGPT, Claude, Perplexity, Gemini, and Google AI Overviews — not just rank in classical SERPs.
120
+
121
+ ### When to use SEO vs GEO
122
+ - **SEO**: a user types a query and clicks a link → optimise for click-through.
123
+ - **GEO**: a user asks an LLM and reads the synthesis → optimise for **inclusion in the answer + named source attribution**.
124
+ - They overlap on technical foundations (crawlability, structured data, authority) and diverge on content shape and citability.
125
+
126
+ ### Citability checklist (the 12 things LLM crawlers reward)
127
+ 1. **One claim per sentence.** LLMs extract sentence-level snippets — long compound sentences get dropped.
128
+ 2. **Lead with the answer.** Inverted-pyramid structure — definition / number / decision in the first sentence of each section.
129
+ 3. **Stable, descriptive H2/H3 headings phrased as questions.** Mirrors how users prompt LLMs.
130
+ 4. **Unique data, numbers, dates, version strings.** Verifiable facts get cited; opinion does not.
131
+ 5. **Named author + author bio with credentials.** Authority signal that survives synthesis.
132
+ 6. **Original quotes from named experts.** Quotation blocks are over-represented in LLM citations.
133
+ 7. **Comparison tables with explicit labels.** Tables are extracted whole; vague prose is summarised away.
134
+ 8. **Schema.org `Article`, `FAQPage`, `HowTo`, `QAPage`, `Person`.** Mandatory for entity disambiguation.
135
+ 9. **`llms.txt` at site root.** Curated index of canonical pages for LLM crawlers (Anthropic-proposed convention).
136
+ 10. **Stable URLs with semantic slugs.** LLMs cache citations — URL changes erase your authority overnight.
137
+ 11. **Cross-linking with descriptive anchors.** "this article" is dead anchor text; use the actual claim.
138
+ 12. **Date stamps on every page.** Recency is a top-3 ranking factor for AI Overviews and Perplexity.
139
+
140
+ ### `llms.txt` template
141
+ ```
142
+ # Brand Name
143
+ > One-sentence description of what the site is and who it's for.
144
+
145
+ ## Core docs
146
+ - [Title](/url): one-line summary the LLM should remember
147
+ - [Title](/url): one-line summary
148
+
149
+ ## API reference
150
+ - [Endpoint](/url): purpose, auth, response shape
151
+
152
+ ## Optional
153
+ - [Changelog](/changelog): recent updates
154
+ ```
155
+
156
+ ### GEO audit checklist
157
+ - [ ] First sentence of every page answers the page's title as a question
158
+ - [ ] At least one comparison table OR numbered list per long-form page
159
+ - [ ] Author block with name + role + LinkedIn / GitHub / ORCID
160
+ - [ ] Schema.org `Article` with `author`, `datePublished`, `dateModified`
161
+ - [ ] Schema.org `FAQPage` for any page with Q/A structure
162
+ - [ ] `llms.txt` at site root with curated canonical URLs
163
+ - [ ] Page contains at least 3 unique data points (numbers, dates, version strings)
164
+ - [ ] Date visible to the user, not just in metadata
165
+ - [ ] No paywall / login wall on indexable content
166
+ - [ ] No JavaScript-only content for primary value (LLM crawlers often skip JS)
167
+ - [ ] Internal links use claim-as-anchor, not "click here"
168
+ - [ ] Brand and product names are consistent across the entire site
169
+
170
+ ### Measuring GEO outcomes
171
+ GEO has no Search Console equivalent. Substitutes:
172
+ - **Manual prompt testing**: query each target prompt monthly in ChatGPT / Claude / Perplexity / AI Overviews; record whether your domain appears in the citation footer.
173
+ - **Referrer analysis**: filter analytics by `Referer` containing `chat.openai.com`, `perplexity.ai`, `claude.ai`, `gemini.google.com`.
174
+ - **Brand-mention monitoring**: tools that scrape LLM outputs for mentions (Otterly, Profound, AthenaHQ).
175
+ - **Server logs**: identify and welcome crawler user-agents (`GPTBot`, `ClaudeBot`, `PerplexityBot`, `Google-Extended`, `OAI-SearchBot`).
176
+
177
+ ### `robots.txt` for AI crawlers (allow by default)
178
+ ```
179
+ User-agent: GPTBot
180
+ Allow: /
181
+
182
+ User-agent: ClaudeBot
183
+ Allow: /
184
+
185
+ User-agent: PerplexityBot
186
+ Allow: /
187
+
188
+ User-agent: Google-Extended
189
+ Allow: /
190
+
191
+ User-agent: OAI-SearchBot
192
+ Allow: /
193
+ ```
194
+ Disallow only what you would disallow for Googlebot. Blocking AI crawlers wholesale erases you from generative answers — the trade-off is rarely worth it for content sites.
@@ -0,0 +1,200 @@
1
+ #!/usr/bin/env bash
2
+ # ai-toolkit-statusline.sh — comprehensive Claude Code status line.
3
+ #
4
+ # Renders one line. Left side: cwd, git, ctx%, i/o token split.
5
+ # Right side (right-aligned): effort level, model name.
6
+ # Optional cost segment is opt-in via AI_TOOLKIT_STATUSLINE_SHOW_COST=1.
7
+ #
8
+ # All token + cost data is read directly from Claude Code's statusLine stdin
9
+ # (context_window.total_input_tokens, total_output_tokens, cost.total_cost_usd,
10
+ # effort.level). No JSONL parsing in the hot path.
11
+ #
12
+ # Wire-up: ai-toolkit installer adds this as default `statusLine` in
13
+ # ~/.claude/settings.json. Manual config:
14
+ # {
15
+ # "statusLine": {
16
+ # "type": "command",
17
+ # "command": "bash ~/.softspark/ai-toolkit/hooks/ai-toolkit-statusline.sh"
18
+ # }
19
+ # }
20
+ #
21
+ # Env vars (all optional):
22
+ # AI_TOOLKIT_DIR — toolkit install location (auto-detected)
23
+ # AI_TOOLKIT_STATUSLINE_DISABLE — "1" silences output entirely
24
+ # AI_TOOLKIT_STATUSLINE_NO_COLOR — "1" disables ANSI colors
25
+ # AI_TOOLKIT_STATUSLINE_NO_TOKENS — "1" hides i/o segment
26
+ # AI_TOOLKIT_STATUSLINE_NO_GIT — "1" hides git segment
27
+ # AI_TOOLKIT_STATUSLINE_NO_EFFORT — "1" hides effort segment
28
+ # AI_TOOLKIT_STATUSLINE_SHOW_COST — "1" appends Claude Code's reported cost
29
+ # AI_TOOLKIT_STATUSLINE_DUMP — "1" writes stdin to /tmp/cc-statusline-input.json
30
+ #
31
+ # Performance: ~50ms (single python3 parse of stdin, one git invocation).
32
+
33
+ set -u
34
+
35
+ [ "${AI_TOOLKIT_STATUSLINE_DISABLE:-0}" = "1" ] && exit 0
36
+
37
+ # ── Colors ───────────────────────────────────────────────────────────────────
38
+ # Claude Code captures stdout via pipe but interprets ANSI escapes when it
39
+ # renders the statusline. So always emit colors unless explicitly disabled.
40
+ if [ "${AI_TOOLKIT_STATUSLINE_NO_COLOR:-0}" = "1" ]; then
41
+ C_RESET="" C_GREEN="" C_BOLD_GREEN="" C_CYAN="" C_BLUE="" C_RED=""
42
+ C_YELLOW="" C_MAGENTA="" C_DIM="" C_BOLD_YELLOW="" C_ORANGE=""
43
+ else
44
+ C_RESET=$'\033[0m'
45
+ C_GREEN=$'\033[0;32m'
46
+ C_BOLD_GREEN=$'\033[1;32m'
47
+ C_CYAN=$'\033[0;36m'
48
+ C_BLUE=$'\033[1;34m'
49
+ C_RED=$'\033[0;31m'
50
+ C_YELLOW=$'\033[0;33m'
51
+ C_BOLD_YELLOW=$'\033[1;33m'
52
+ C_MAGENTA=$'\033[0;35m'
53
+ C_DIM=$'\033[2m'
54
+ C_ORANGE=$'\033[38;5;208m'
55
+ fi
56
+
57
+ # ── Parse stdin in one python3 invocation ────────────────────────────────────
58
+ INPUT="$(cat 2>/dev/null || true)"
59
+
60
+ if [ "${AI_TOOLKIT_STATUSLINE_DUMP:-0}" = "1" ] && [ -n "$INPUT" ]; then
61
+ printf '%s' "$INPUT" > /tmp/cc-statusline-input.json 2>/dev/null
62
+ fi
63
+
64
+ PARSED="$(printf '%s' "$INPUT" | python3 -c '
65
+ import json, sys
66
+ def get(d, path):
67
+ for k in path.split("."):
68
+ d = d.get(k) if isinstance(d, dict) else None
69
+ if d is None:
70
+ return ""
71
+ return d if d is not None else ""
72
+ def fmt_tokens(n):
73
+ try:
74
+ n = int(n)
75
+ except (TypeError, ValueError):
76
+ return "0"
77
+ if n >= 1000:
78
+ return f"{n/1000:.1f}k"
79
+ return str(n)
80
+ try:
81
+ d = json.loads(sys.stdin.read() or "{}")
82
+ except Exception:
83
+ d = {}
84
+ cwd = get(d, "cwd") or ""
85
+ model = get(d, "model.display_name") or get(d, "model.id") or ""
86
+ ctx_pct = get(d, "context_window.used_percentage")
87
+ in_tok = fmt_tokens(get(d, "context_window.total_input_tokens"))
88
+ out_tok = fmt_tokens(get(d, "context_window.total_output_tokens"))
89
+ cost = get(d, "cost.total_cost_usd")
90
+ try:
91
+ cost_str = f"{float(cost):.2f}" if cost != "" else ""
92
+ except (TypeError, ValueError):
93
+ cost_str = ""
94
+ effort = get(d, "effort.level") or ""
95
+ print(f"{cwd}\t{model}\t{ctx_pct}\t{in_tok}\t{out_tok}\t{cost_str}\t{effort}")
96
+ ' 2>/dev/null)"
97
+
98
+ IFS=$'\t' read -r CWD MODEL_NAME CTX_USED IN_TOK OUT_TOK COST EFFORT <<< "$PARSED"
99
+ [ -z "$CWD" ] && CWD="$PWD"
100
+
101
+ # ── Segment: prompt + dir ────────────────────────────────────────────────────
102
+ DIR_BASENAME="$(basename "$CWD" 2>/dev/null || echo '~')"
103
+ SEG_PROMPT="${C_BOLD_GREEN}\xe2\x9e\x9c${C_RESET} ${C_CYAN}${DIR_BASENAME}${C_RESET}"
104
+
105
+ # ── Segment: git ────────────────────────────────────────────────────────────
106
+ SEG_GIT=""
107
+ if [ "${AI_TOOLKIT_STATUSLINE_NO_GIT:-0}" != "1" ] && \
108
+ git -C "$CWD" rev-parse --git-dir >/dev/null 2>&1; then
109
+ BRANCH="$(git -C "$CWD" -c core.fsmonitor=false symbolic-ref --short HEAD 2>/dev/null)"
110
+ if [ -n "$BRANCH" ]; then
111
+ DIRTY=""
112
+ if git -C "$CWD" -c core.fsmonitor=false status --porcelain 2>/dev/null | grep -q .; then
113
+ DIRTY=" ${C_RED}\xe2\x9c\x97${C_RESET}"
114
+ fi
115
+ SEG_GIT=" ${C_BLUE}git:(${C_RED}${BRANCH}${C_BLUE})${C_RESET}${DIRTY}"
116
+ fi
117
+ fi
118
+
119
+ # ── Segment: context window as progress bar ────────────────────────────────
120
+ # 10-cell bar with usage-shaded color: green <50%, yellow 50-79%, red ≥80%.
121
+ build_ctx_bar() {
122
+ local pct=$1
123
+ local cells=10
124
+ local filled=$(( (pct * cells + 50) / 100 ))
125
+ [ "$filled" -gt "$cells" ] && filled=$cells
126
+ [ "$filled" -lt 0 ] && filled=0
127
+ local color
128
+ if [ "$pct" -ge 90 ]; then
129
+ color="$C_RED"
130
+ elif [ "$pct" -ge 70 ]; then
131
+ color="$C_ORANGE"
132
+ else
133
+ color="$C_GREEN"
134
+ fi
135
+ local bar=""
136
+ local i=0
137
+ while [ "$i" -lt "$filled" ]; do
138
+ bar+="\xe2\x96\x88" # █ FULL BLOCK
139
+ i=$((i+1))
140
+ done
141
+ local empty=""
142
+ while [ "$i" -lt "$cells" ]; do
143
+ empty+="\xe2\x96\x91" # ░ LIGHT SHADE
144
+ i=$((i+1))
145
+ done
146
+ printf '%b' "${color}${bar}${C_DIM}${empty}${C_RESET}"
147
+ }
148
+
149
+ SEG_CTX=""
150
+ if [ -n "$CTX_USED" ]; then
151
+ CTX_INT="$(printf '%.0f' "$CTX_USED" 2>/dev/null || echo "$CTX_USED")"
152
+ case "$CTX_INT" in
153
+ ''|*[!0-9]*) CTX_INT=0 ;;
154
+ esac
155
+ SEG_CTX=" $(build_ctx_bar "$CTX_INT") ${C_DIM}${CTX_INT}%${C_RESET}"
156
+ fi
157
+
158
+ # ── Segment: tokens with up/down arrows ─────────────────────────────────────
159
+ # ↑ green = upload (input tokens sent to model)
160
+ # ↓ red = download (output tokens received from model)
161
+ SEG_TOKENS=""
162
+ if [ "${AI_TOOLKIT_STATUSLINE_NO_TOKENS:-0}" != "1" ] && [ -n "$IN_TOK" ] && \
163
+ { [ "$IN_TOK" != "0" ] || [ "$OUT_TOK" != "0" ]; }; then
164
+ SEG_TOKENS=" ${C_BOLD_GREEN}\xe2\x86\x91${IN_TOK}${C_RESET} ${C_RED}\xe2\x86\x93${OUT_TOK}${C_RESET}"
165
+ if [ "${AI_TOOLKIT_STATUSLINE_SHOW_COST:-0}" = "1" ] && [ -n "$COST" ]; then
166
+ SEG_TOKENS+=" ${C_BOLD_GREEN}\$${COST}${C_RESET}"
167
+ fi
168
+ fi
169
+
170
+ # ── Right-aligned segments: effort + model ───────────────────────────────────
171
+ SEG_EFFORT=""
172
+ if [ "${AI_TOOLKIT_STATUSLINE_NO_EFFORT:-0}" != "1" ] && [ -n "$EFFORT" ]; then
173
+ case "$EFFORT" in
174
+ xhigh|high) E_COLOR="$C_BOLD_YELLOW" ;;
175
+ medium) E_COLOR="$C_YELLOW" ;;
176
+ *) E_COLOR="$C_DIM" ;;
177
+ esac
178
+ SEG_EFFORT="${C_DIM}effort:${C_RESET}${E_COLOR}${EFFORT}${C_RESET}"
179
+ fi
180
+
181
+ SEG_MODEL=""
182
+ if [ -n "$MODEL_NAME" ]; then
183
+ SEG_MODEL="${C_MAGENTA}${MODEL_NAME}${C_RESET}"
184
+ fi
185
+
186
+ # ── Render (left-aligned, single line) ───────────────────────────────────────
187
+ # Right-align was attempted earlier but Claude Code reserves variable-width
188
+ # space on the right of the rendered statusline for its own UI overlay,
189
+ # truncating any content right-padded near COLUMNS. Left-align with effort
190
+ # and model as trailing segments avoids the truncation.
191
+ SEG_TRAILER=""
192
+ [ -n "$SEG_EFFORT" ] && SEG_TRAILER+=" $SEG_EFFORT"
193
+ [ -n "$SEG_MODEL" ] && SEG_TRAILER+=" $SEG_MODEL"
194
+
195
+ printf "%b%b%b%b%b\n" \
196
+ "$SEG_PROMPT" \
197
+ "${SEG_GIT:-}" \
198
+ "${SEG_CTX:-}" \
199
+ "${SEG_TOKENS:-}" \
200
+ "${SEG_TRAILER}"
package/app/hooks.json CHANGED
@@ -234,5 +234,10 @@
234
234
  ]
235
235
  }
236
236
  ]
237
+ },
238
+ "statusLine": {
239
+ "_source": "ai-toolkit",
240
+ "type": "command",
241
+ "command": "bash \"$HOME/.softspark/ai-toolkit/hooks/ai-toolkit-statusline.sh\""
237
242
  }
238
243
  }
@@ -1,8 +1,8 @@
1
1
  ---
2
2
  name: brand-voice
3
- description: "Loaded when writing documentation, content, README, or user-facing text. Prevents generic LLM rhetoric and enforces direct, technical voice."
3
+ description: "Loaded when writing documentation, content, README, or user-facing text, AND when an output-mode is set for conversational responses. Prevents generic LLM rhetoric, enforces direct technical voice, and supplies optional concise/strict response modes."
4
4
  effort: medium
5
- user-invocable: false
5
+ user-invocable: true
6
6
  allowed-tools: Read
7
7
  ---
8
8
 
@@ -10,6 +10,8 @@ allowed-tools: Read
10
10
 
11
11
  Auto-loaded when writing documentation, content, or user-facing text. Enforces consistent, direct voice and eliminates LLM rhetoric.
12
12
 
13
+ Also loaded when a project sets `output-mode: concise` or `output-mode: strict` in `CLAUDE.md`, to govern conversational response length and structure (see [Output Modes](#output-modes)).
14
+
13
15
  ## Anti-Trope List (Banned Phrases)
14
16
 
15
17
  ### Opening Tropes (Never Start With)
@@ -119,6 +121,40 @@ install takes under 30 seconds on a local disk.
119
121
  - **CRITICAL**: one idea per sentence. If you write "and" linking two distinct ideas, split the sentence
120
122
  - **MANDATORY**: technical claims include a concrete number, name, or example — never assert quality without evidence
121
123
 
124
+ ## Output Modes
125
+
126
+ Three modes govern conversational response length. Default applies always; `concise` and `strict` activate per-project or per-session.
127
+
128
+ | Mode | Token target vs default | Used when |
129
+ |------|-------------------------|-----------|
130
+ | `default` | 100% (no extra constraints) | No `output-mode` configured |
131
+ | `concise` | ≤60% | Daily work, short Q&A, code edits, short reviews |
132
+ | `strict` | ≤40% | Long sessions, expensive models, batch operations, code-only tasks |
133
+
134
+ Mode rules live in this skill's `modes/` directory. Read the file matching the active mode:
135
+
136
+ - `modes/concise.md` — bullet-first, no preamble, max 3-sentence prose blocks
137
+ - `modes/strict.md` — telegraphic, no prose blocks, only lists/tables/code
138
+
139
+ ### Activation
140
+
141
+ 1. **Project-level (preferred):** add `output-mode: concise` to project `CLAUDE.md` frontmatter or `.claude/settings.json` under `aiToolkit.outputMode`
142
+ 2. **Session-level:** user types `/brand-voice concise` or `/brand-voice strict` to switch for the current session
143
+ 3. **Reset:** `/brand-voice default` or remove the project setting
144
+
145
+ ### What modes do NOT change
146
+
147
+ - Code blocks (always full, never truncated)
148
+ - File paths, line numbers, error messages (always exact)
149
+ - Lists of items the user must see (file lists, test failures, security findings)
150
+ - Plan documents and ADRs (always full structure)
151
+
152
+ Modes change *prose*, not *data*. If you cut a fact to fit a length budget, you have failed the mode, not honored it.
153
+
154
+ ### Measurement
155
+
156
+ Run `python3 app/skills/brand-voice/scripts/measure.py --fixtures tests/fixtures/output-modes/` to compare baseline vs mode tokens on the fixture set. Report shows per-fixture deltas and an aggregate ratio.
157
+
122
158
  ## When NOT to Load
123
159
 
124
160
  - For code or technical specs with no user-facing prose — the voice rules do not apply
@@ -0,0 +1,67 @@
1
+ # Concise Mode
2
+
3
+ Active when project sets `output-mode: concise` or user invokes `/brand-voice concise`.
4
+
5
+ ## Targets
6
+
7
+ - Token output ≤60% of default
8
+ - No response longer than 12 lines unless data requires it (file list, test failures, code)
9
+ - No response shorter than 1 sentence — never reply with just "Done." or "OK"
10
+
11
+ ## Hard Rules
12
+
13
+ 1. **No preamble.** Start with the answer or the action result, not "I'll now..." or "Let me explain..."
14
+ 2. **No closing summary.** Don't restate what you just did unless asked.
15
+ 3. **Bullets over prose** for any list ≥3 items. Each bullet = one fact, ≤80 chars.
16
+ 4. **One paragraph max** per topic. If you need two, split into bullets.
17
+ 5. **No filler transitions:** drop "Additionally," "Moreover," "It's worth noting," "Importantly."
18
+ 6. **No hedging stacks:** "I think we could potentially maybe try" → "Try X."
19
+ 7. **Quote facts, not framing:** show file paths, line numbers, exact error text. Cut the explanation around them if the user can read them.
20
+
21
+ ## What to Cut First
22
+
23
+ | Pattern | Action |
24
+ |---------|--------|
25
+ | "Let me check..." / "I'll look at..." | Delete — just do the action |
26
+ | "It looks like..." / "It seems..." | Delete and state the fact directly |
27
+ | "As you can see..." | Delete — the user can see |
28
+ | "Now that we've ... let's ..." | Delete — start the next action |
29
+ | "Great question!" / "Good point!" | Delete always |
30
+ | Restating the user's question | Delete — answer it |
31
+ | "In summary," / "Overall," | Delete the summary entirely |
32
+
33
+ ## What to Keep
34
+
35
+ - Concrete file paths, line numbers, command output
36
+ - Exact error messages and stack frames
37
+ - Trade-offs when the user has to decide
38
+ - Warnings about destructive actions
39
+ - Numbers (timings, sizes, counts, percentages)
40
+
41
+ ## Examples
42
+
43
+ **Bad (verbose):**
44
+
45
+ ```
46
+ Let me check the brand-voice skill for you. I'll read the SKILL.md file
47
+ to understand its current structure and then look at how it integrates
48
+ with the rest of the system. Now that I've examined it, I can see that
49
+ the skill currently has 130 lines and uses YAML frontmatter. As you can
50
+ see, it has a clear structure with sections for anti-tropes, voice
51
+ principles, and rules. In summary, the skill is well-organized.
52
+ ```
53
+
54
+ **Good (concise):**
55
+
56
+ ```
57
+ brand-voice/SKILL.md: 130 lines, YAML frontmatter, sections for anti-tropes,
58
+ voice principles, rules. Structure is clean.
59
+ ```
60
+
61
+ Token ratio: 76 → 25 = 33%.
62
+
63
+ ## Boundary Behavior
64
+
65
+ - If a question genuinely needs 30 lines to answer, write 30. Don't truncate facts to hit the budget.
66
+ - If the user asks "explain", give one solid paragraph, not a one-liner.
67
+ - If unsure whether the user wants depth, ask. One question is cheaper than a wrong-length response.