Skip to content

Commit 3fcb6e9

Browse files
authored
fix: Update hook formats and improve plugin skills (#59) (#60)
- Claude Code: Replace deprecated {"decision":"allow"} with hookSpecificOutput format - Cursor: Switch from beforeShellExecution to preToolUse for command rewriting support - Cursor: Use updated_input instead of updatedCommand per Cursor docs - All skills: Expand description for better triggering, add missing flags, troubleshooting - Bump plugin version to 1.0.3
1 parent a1723d8 commit 3fcb6e9

File tree

9 files changed

+239
-152
lines changed

9 files changed

+239
-152
lines changed

Sources/Install/Templates/CursorTemplates.swift

Lines changed: 15 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ enum CursorTemplates {
88
{
99
"version": 1,
1010
"hooks": {
11-
"beforeShellExecution": [
11+
"preToolUse": [
1212
{
1313
"command": "./.cursor/hooks/pre-xcsift.sh"
1414
}
@@ -22,7 +22,7 @@ enum CursorTemplates {
2222
{
2323
"version": 1,
2424
"hooks": {
25-
"beforeShellExecution": [
25+
"preToolUse": [
2626
{
2727
"command": "~/.cursor/hooks/pre-xcsift.sh"
2828
}
@@ -34,29 +34,27 @@ enum CursorTemplates {
3434
/// The pre-xcsift.sh hook script content
3535
static let hookScript = """
3636
#!/bin/bash
37-
# xcsift pre-shell hook for Cursor
37+
# xcsift pre-tool hook for Cursor
3838
# Intercepts xcodebuild and swift build/test commands and pipes through xcsift
3939
40-
# Read the command from environment or argument
41-
COMMAND="${CURSOR_SHELL_COMMAND:-$1}"
40+
ALLOW='{"permission":"allow"}'
41+
42+
# Read tool input from stdin (JSON with tool_input field)
43+
INPUT=$(cat)
44+
45+
# Extract the command from the tool input
46+
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty')
4247
4348
if [ -z "$COMMAND" ]; then
44-
# No command, allow execution
45-
echo '{"permission": "allow"}'
49+
# No command field, allow as-is
50+
echo "$ALLOW"
4651
exit 0
4752
fi
4853
4954
# Check if xcsift is available
5055
if ! command -v xcsift &> /dev/null; then
5156
# xcsift not installed, allow command as-is
52-
echo '{"permission": "allow"}'
53-
exit 0
54-
fi
55-
56-
# Check if jq is available (required for JSON output)
57-
if ! command -v jq &> /dev/null; then
58-
# jq not installed, allow command as-is
59-
echo '{"permission": "allow"}'
57+
echo "$ALLOW"
6058
exit 0
6159
fi
6260
@@ -75,10 +73,10 @@ enum CursorTemplates {
7573
MODIFIED_COMMAND="$COMMAND | xcsift -f toon"
7674
7775
# Return modified command
78-
jq -n --arg cmd "$MODIFIED_COMMAND" '{"permission": "allow", "updatedCommand": $cmd}'
76+
jq -n --arg cmd "$MODIFIED_COMMAND" '{"permission":"allow","updated_input":{"command":$cmd}}'
7977
else
8078
# Not a build command, allow as-is
81-
echo '{"permission": "allow"}'
79+
echo "$ALLOW"
8280
fi
8381
"""
8482

Sources/Install/Templates/SharedTemplates.swift

Lines changed: 66 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -7,95 +7,119 @@ enum SharedTemplates {
77
static let skillMarkdown = """
88
---
99
name: xcsift
10-
description: Use xcsift for Swift and Xcode build output. Activate when running swift build, swift test, or xcodebuild commands. Pipe output through xcsift for structured TOON format optimized for LLM consumption.
10+
description: Use xcsift for all Swift and Xcode build commands. Pipe xcodebuild build, xcodebuild test, swift build, swift test through xcsift for structured TOON output with compile errors, warnings, test failures, code coverage, slow tests, and build timing. Essential for parsing Xcode build output, diagnosing compile errors, analyzing test results, and measuring code coverage in any Swift or Xcode project.
1111
---
1212
1313
# xcsift - Xcode Build Output Formatter
1414
15-
xcsift is a Swift command-line tool that parses and formats xcodebuild/SPM output for coding agents. It transforms verbose Xcode build output into token-efficient TOON format optimized for machine readability.
15+
xcsift parses and formats xcodebuild/SPM output into token-efficient TOON format optimized for LLM consumption. It extracts compile errors, warnings, test failures, code coverage, and build timing from verbose Xcode output.
1616
1717
## When to Use
1818
19-
Use xcsift when running Swift or Xcode build commands:
20-
- `xcodebuild build`
21-
- `xcodebuild test`
22-
- `swift build`
23-
- `swift test`
19+
**Always** pipe through xcsift when running:
20+
- `xcodebuild build` / `xcodebuild test`
21+
- `swift build` / `swift test`
22+
- Any command that produces Xcode/SPM build output
2423
2524
## Usage Pattern
2625
27-
Always pipe build output through xcsift with stderr redirected:
26+
Always redirect stderr and use TOON format:
2827
2928
```bash
30-
# Basic usage
29+
# Build
3130
xcodebuild build 2>&1 | xcsift -f toon
3231
swift build 2>&1 | xcsift -f toon
3332
33+
# Test
34+
swift test 2>&1 | xcsift -f toon
35+
xcodebuild test 2>&1 | xcsift -f toon
36+
3437
# With warnings
3538
xcodebuild build 2>&1 | xcsift -f toon -w
3639
3740
# With code coverage
3841
swift test --enable-code-coverage 2>&1 | xcsift -f toon -c
3942
xcodebuild test -enableCodeCoverage YES 2>&1 | xcsift -f toon -c
4043
44+
# With detailed per-file coverage
45+
swift test --enable-code-coverage 2>&1 | xcsift -f toon -c --coverage-details
46+
4147
# With executable targets
4248
xcodebuild build 2>&1 | xcsift -f toon -e
4349
44-
# Strict CI mode (fail on warnings)
50+
# Strict CI mode (fail on warnings or errors)
4551
xcodebuild build 2>&1 | xcsift -f toon -W -E
52+
53+
# Slow test detection
54+
swift test 2>&1 | xcsift -f toon --slow-threshold 1.0
55+
56+
# Build info (per-target phases, timing, dependencies)
57+
xcodebuild build 2>&1 | xcsift -f toon --build-info
4658
```
4759
4860
## Key Flags
4961
5062
| Flag | Description |
5163
|------|-------------|
52-
| `-f toon` | TOON format (30-60% fewer tokens) |
53-
| `-w` | Show detailed warnings |
54-
| `-W` | Treat warnings as errors |
55-
| `-q` | Quiet mode (no output on success) |
56-
| `-c` | Include code coverage |
64+
| `-f toon` | TOON format (30-60% fewer tokens than JSON) |
65+
| `-w` | Show detailed warnings list |
66+
| `-W` | Treat warnings as errors (Werror) |
67+
| `-q` | Quiet mode (no output on clean success) |
68+
| `-c` | Include code coverage summary |
69+
| `--coverage-details` | Per-file coverage breakdown (use with `-c`) |
5770
| `-e` | Include executable targets |
58-
| `-E` | Exit with failure on build failure |
59-
| `--build-info` | Per-target phases and timing |
71+
| `-E` | Exit with non-zero code on build failure |
72+
| `--build-info` | Per-target phases, timing, and dependencies |
73+
| `--slow-threshold N` | Flag tests slower than N seconds |
74+
| `--config PATH` | Use custom config file (default: `.xcsift.toml`) |
75+
| `--init` | Generate `.xcsift.toml` template in current directory |
6076
61-
## Output Format
77+
## Interpreting TOON Output
6278
63-
TOON format provides structured output optimized for LLMs:
79+
TOON uses indentation-based structure with tabular arrays:
6480
6581
```toon
6682
status: failed
6783
summary:
6884
errors: 1
6985
warnings: 3
86+
failed_tests: 2
87+
passed_tests: 10
88+
build_time: 12.4s
89+
test_time: 5.2s
7090
errors[1]{file,line,message}:
71-
main.swift,15,"use of undeclared identifier"
72-
warnings[3]{file,line,message}:
73-
Parser.swift,20,"unused variable"
91+
main.swift,15,"use of undeclared identifier 'foo'"
92+
warnings[3]{file,line,message,type}:
93+
Parser.swift,20,"unused variable 'result'",compile
94+
View.swift,42,"Publishing changes from background threads",swiftui
95+
Util.swift,10,"Custom warning message",runtime
96+
failed_tests[2]{suite,test,file,line,message,duration}:
97+
MyTests,testExample,MyTests.swift,25,"XCTAssertEqual failed",0.123
98+
MyTests,testOther,MyTests.swift,30,"XCTAssertTrue failed",0.456
7499
```
75100
76-
## Important Notes
77-
78-
- Always use `2>&1` to capture stderr (where compiler errors are written)
79-
- TOON format reduces tokens by 30-60% compared to raw xcodebuild output
80-
- When running build commands, always add `| xcsift -f toon` to the end
81-
82-
## Example Workflow
101+
**Key patterns:**
102+
- `status`: `succeeded` or `failed`
103+
- `errors[N]{columns}:` — tabular array with N items, column names in braces
104+
- `warnings` have `type`: `compile`, `runtime`, or `swiftui`
105+
- `failed_tests` include file/line for navigation and duration
106+
- `null` values mean the data wasn't available
83107
84-
1. Build the project:
85-
```bash
86-
swift build 2>&1 | xcsift -f toon
87-
```
108+
## Troubleshooting
88109
89-
2. If build fails, analyze the structured error output
110+
| Problem | Solution |
111+
|---------|----------|
112+
| No errors shown but build failed | Add `2>&1` to capture stderr |
113+
| Coverage shows `null` | Add `--enable-code-coverage` (SPM) or `-enableCodeCoverage YES` (xcodebuild) |
114+
| "xcsift not found" | Install: `brew install xcsift` or build from source |
115+
| Coverage for wrong target | xcsift auto-filters to tested target; use `--coverage-path` to override |
116+
| Config not loading | Check `.xcsift.toml` in CWD or `~/.config/xcsift/config.toml` |
90117
91-
3. Run tests with coverage:
92-
```bash
93-
swift test --enable-code-coverage 2>&1 | xcsift -f toon -c
94-
```
118+
## Important
95119
96-
4. For CI pipelines, use strict mode:
97-
```bash
98-
xcodebuild build 2>&1 | xcsift -f toon -W -E
99-
```
120+
- **Always use `2>&1`** to capture stderr (compiler errors and warnings go to stderr)
121+
- TOON format reduces tokens by 30-60% compared to raw xcodebuild output
122+
- When running build commands, always add `| xcsift -f toon` to the end
123+
- Flaky test detection is automatic (no flag needed) — detects tests that both pass and fail
100124
"""
101125
}

Tests/InstallTests.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -331,15 +331,15 @@ final class CursorInstallerTests: XCTestCase {
331331
let template = CursorTemplates.projectHooksJSON
332332

333333
XCTAssertTrue(template.contains("\"version\": 1"))
334-
XCTAssertTrue(template.contains("beforeShellExecution"))
334+
XCTAssertTrue(template.contains("preToolUse"))
335335
XCTAssertTrue(template.contains("./.cursor/hooks/pre-xcsift.sh"))
336336
}
337337

338338
func testGlobalHooksJSONTemplate() {
339339
let template = CursorTemplates.globalHooksJSON
340340

341341
XCTAssertTrue(template.contains("\"version\": 1"))
342-
XCTAssertTrue(template.contains("beforeShellExecution"))
342+
XCTAssertTrue(template.contains("preToolUse"))
343343
XCTAssertTrue(template.contains("~/.cursor/hooks/pre-xcsift.sh"))
344344
}
345345

@@ -355,7 +355,7 @@ final class CursorInstallerTests: XCTestCase {
355355
XCTAssertTrue(template.contains("xcsift"))
356356
XCTAssertTrue(template.contains("permission"))
357357
XCTAssertTrue(template.contains("allow"))
358-
XCTAssertTrue(template.contains("updatedCommand"))
358+
XCTAssertTrue(template.contains("updated_input"))
359359
}
360360

361361
func testSkillMarkdownTemplate() {
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "xcsift",
3-
"version": "1.0.2",
3+
"version": "1.0.3",
44
"description": "Automatically pipe xcodebuild and swift build output through xcsift for structured TOON format optimized for LLM consumption",
55
"skills": "./skills"
66
}

plugins/claude-code/scripts/pre-xcsift.sh

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
# xcsift pre-tool hook for Claude Code
33
# Intercepts xcodebuild and swift build/test commands and pipes through xcsift
44

5-
set -e
5+
ALLOW='{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"allow"}}'
66

77
# Read tool input from stdin (JSON with tool_input field)
88
INPUT=$(cat)
@@ -12,14 +12,14 @@ COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty')
1212

1313
if [ -z "$COMMAND" ]; then
1414
# No command field, allow as-is
15-
echo '{"decision": "allow"}'
15+
echo "$ALLOW"
1616
exit 0
1717
fi
1818

1919
# Check if xcsift is available
2020
if ! command -v xcsift &> /dev/null; then
2121
# xcsift not installed, allow command as-is
22-
echo '{"decision": "allow"}'
22+
echo "$ALLOW"
2323
exit 0
2424
fi
2525

@@ -37,9 +37,9 @@ if echo "$COMMAND" | grep -qE '^\s*(xcodebuild|swift\s+(build|test))\b' && \
3737
# Pipe through xcsift with TOON format
3838
MODIFIED_COMMAND="$COMMAND | xcsift -f toon"
3939

40-
# Return modified command
41-
jq -n --arg cmd "$MODIFIED_COMMAND" '{"decision": "allow", "updatedCommand": $cmd}'
40+
# Return modified command with rewritten input
41+
jq -n --arg cmd "$MODIFIED_COMMAND" '{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"allow","updatedInput":{"command":$cmd}}}'
4242
else
4343
# Not a build command, allow as-is
44-
echo '{"decision": "allow"}'
44+
echo "$ALLOW"
4545
fi

0 commit comments

Comments
 (0)