@paths.design/caws-cli 8.0.0 → 8.1.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/dist/budget-derivation.d.ts +74 -0
- package/dist/budget-derivation.d.ts.map +1 -0
- package/dist/cicd-optimizer.d.ts +142 -0
- package/dist/cicd-optimizer.d.ts.map +1 -0
- package/dist/commands/archive.d.ts +51 -0
- package/dist/commands/archive.d.ts.map +1 -0
- package/dist/commands/archive.js +114 -6
- package/dist/commands/burnup.d.ts +6 -0
- package/dist/commands/burnup.d.ts.map +1 -0
- package/dist/commands/burnup.js +109 -10
- package/dist/commands/diagnose.d.ts +52 -0
- package/dist/commands/diagnose.d.ts.map +1 -0
- package/dist/commands/diagnose.js +1 -1
- package/dist/commands/evaluate.d.ts +8 -0
- package/dist/commands/evaluate.d.ts.map +1 -0
- package/dist/commands/init.d.ts +5 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/iterate.d.ts +8 -0
- package/dist/commands/iterate.d.ts.map +1 -0
- package/dist/commands/mode.d.ts +24 -0
- package/dist/commands/mode.d.ts.map +1 -0
- package/dist/commands/mode.js +24 -14
- package/dist/commands/plan.d.ts +49 -0
- package/dist/commands/plan.d.ts.map +1 -0
- package/dist/commands/provenance.d.ts +32 -0
- package/dist/commands/provenance.d.ts.map +1 -0
- package/dist/commands/provenance.js +216 -93
- package/dist/commands/quality-gates.d.ts +6 -0
- package/dist/commands/quality-gates.d.ts.map +1 -0
- package/dist/commands/quality-gates.js +82 -3
- package/dist/commands/quality-monitor.d.ts +17 -0
- package/dist/commands/quality-monitor.d.ts.map +1 -0
- package/dist/commands/specs.d.ts +71 -0
- package/dist/commands/specs.d.ts.map +1 -0
- package/dist/commands/specs.js +184 -6
- package/dist/commands/status.d.ts +44 -0
- package/dist/commands/status.d.ts.map +1 -0
- package/dist/commands/status.js +134 -10
- package/dist/commands/templates.d.ts +74 -0
- package/dist/commands/templates.d.ts.map +1 -0
- package/dist/commands/templates.js +2 -2
- package/dist/commands/tool.d.ts +13 -0
- package/dist/commands/tool.d.ts.map +1 -0
- package/dist/commands/troubleshoot.d.ts +8 -0
- package/dist/commands/troubleshoot.d.ts.map +1 -0
- package/dist/commands/tutorial.d.ts +55 -0
- package/dist/commands/tutorial.d.ts.map +1 -0
- package/dist/commands/validate.d.ts +15 -0
- package/dist/commands/validate.d.ts.map +1 -0
- package/dist/commands/waivers.d.ts +8 -0
- package/dist/commands/waivers.d.ts.map +1 -0
- package/dist/commands/workflow.d.ts +85 -0
- package/dist/commands/workflow.d.ts.map +1 -0
- package/dist/config/index.d.ts +29 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/modes.d.ts +225 -0
- package/dist/config/modes.d.ts.map +1 -0
- package/dist/constants/spec-types.d.ts +41 -0
- package/dist/constants/spec-types.d.ts.map +1 -0
- package/dist/error-handler.d.ts +164 -0
- package/dist/error-handler.d.ts.map +1 -0
- package/dist/error-handler.js +6 -98
- package/dist/generators/jest-config-generator.js +242 -0
- package/dist/generators/jest-config.d.ts +32 -0
- package/dist/generators/jest-config.d.ts.map +1 -0
- package/dist/generators/working-spec.d.ts +13 -0
- package/dist/generators/working-spec.d.ts.map +1 -0
- package/dist/index-new.d.ts +5 -0
- package/dist/index-new.d.ts.map +1 -0
- package/dist/index-new.js +317 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +14 -7
- package/dist/index.js.backup +4711 -0
- package/dist/minimal-cli.d.ts +3 -0
- package/dist/minimal-cli.d.ts.map +1 -0
- package/dist/minimal-cli.js +3 -1
- package/dist/policy/PolicyManager.d.ts +104 -0
- package/dist/policy/PolicyManager.d.ts.map +1 -0
- package/dist/scaffold/claude-hooks.js +316 -0
- package/dist/scaffold/cursor-hooks.d.ts +7 -0
- package/dist/scaffold/cursor-hooks.d.ts.map +1 -0
- package/dist/scaffold/git-hooks.d.ts +38 -0
- package/dist/scaffold/git-hooks.d.ts.map +1 -0
- package/dist/scaffold/index.d.ts +15 -0
- package/dist/scaffold/index.d.ts.map +1 -0
- package/dist/scaffold/index.js +18 -0
- package/dist/spec/SpecFileManager.d.ts +146 -0
- package/dist/spec/SpecFileManager.d.ts.map +1 -0
- package/dist/templates/.claude/README.md +190 -0
- package/dist/templates/.claude/hooks/audit.sh +96 -0
- package/dist/templates/.claude/hooks/block-dangerous.sh +90 -0
- package/dist/templates/.claude/hooks/naming-check.sh +97 -0
- package/dist/templates/.claude/hooks/quality-check.sh +68 -0
- package/dist/templates/.claude/hooks/scan-secrets.sh +85 -0
- package/dist/templates/.claude/hooks/scope-guard.sh +105 -0
- package/dist/templates/.claude/hooks/validate-spec.sh +76 -0
- package/dist/templates/.claude/settings.json +95 -0
- package/dist/test-analysis.d.ts +182 -0
- package/dist/test-analysis.d.ts.map +1 -0
- package/dist/test-analysis.js +203 -10
- package/dist/tool-interface.d.ts +236 -0
- package/dist/tool-interface.d.ts.map +1 -0
- package/dist/tool-loader.d.ts +77 -0
- package/dist/tool-loader.d.ts.map +1 -0
- package/dist/tool-validator.d.ts +72 -0
- package/dist/tool-validator.d.ts.map +1 -0
- package/dist/utils/async-utils.d.ts +73 -0
- package/dist/utils/async-utils.d.ts.map +1 -0
- package/dist/utils/command-wrapper.d.ts +66 -0
- package/dist/utils/command-wrapper.d.ts.map +1 -0
- package/dist/utils/detection.d.ts +14 -0
- package/dist/utils/detection.d.ts.map +1 -0
- package/dist/utils/error-categories.js +210 -0
- package/dist/utils/finalization.d.ts +17 -0
- package/dist/utils/finalization.d.ts.map +1 -0
- package/dist/utils/git-lock.d.ts +13 -0
- package/dist/utils/git-lock.d.ts.map +1 -0
- package/dist/utils/gitignore-updater.d.ts +39 -0
- package/dist/utils/gitignore-updater.d.ts.map +1 -0
- package/dist/utils/project-analysis.d.ts +34 -0
- package/dist/utils/project-analysis.d.ts.map +1 -0
- package/dist/utils/promise-utils.d.ts +30 -0
- package/dist/utils/promise-utils.d.ts.map +1 -0
- package/dist/utils/quality-gates-utils.js +402 -0
- package/dist/utils/quality-gates.d.ts +49 -0
- package/dist/utils/quality-gates.d.ts.map +1 -0
- package/dist/utils/spec-resolver.d.ts +80 -0
- package/dist/utils/spec-resolver.d.ts.map +1 -0
- package/dist/utils/typescript-detector.d.ts +63 -0
- package/dist/utils/typescript-detector.d.ts.map +1 -0
- package/dist/utils/typescript-detector.js +36 -90
- package/dist/utils/yaml-validation.d.ts +32 -0
- package/dist/utils/yaml-validation.d.ts.map +1 -0
- package/dist/validation/spec-validation.d.ts +43 -0
- package/dist/validation/spec-validation.d.ts.map +1 -0
- package/dist/validation/spec-validation.js +59 -6
- package/dist/waivers-manager.d.ts +167 -0
- package/dist/waivers-manager.d.ts.map +1 -0
- package/package.json +5 -3
- package/templates/.claude/README.md +190 -0
- package/templates/.claude/hooks/audit.sh +96 -0
- package/templates/.claude/hooks/block-dangerous.sh +90 -0
- package/templates/.claude/hooks/naming-check.sh +97 -0
- package/templates/.claude/hooks/quality-check.sh +68 -0
- package/templates/.claude/hooks/scan-secrets.sh +85 -0
- package/templates/.claude/hooks/scope-guard.sh +105 -0
- package/templates/.claude/hooks/validate-spec.sh +76 -0
- package/templates/.claude/settings.json +95 -0
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Spec File Manager - Handles WorkingSpec file operations and YAML conversion
|
|
3
|
+
*
|
|
4
|
+
* Features:
|
|
5
|
+
* - Bidirectional WorkingSpec ↔ YAML conversion
|
|
6
|
+
* - Temporary file support for validation workflows
|
|
7
|
+
* - Backup/restore capabilities
|
|
8
|
+
* - Automatic cleanup of old temporary files
|
|
9
|
+
*/
|
|
10
|
+
export class SpecFileManager {
|
|
11
|
+
constructor(config?: {});
|
|
12
|
+
projectRoot: any;
|
|
13
|
+
useTemporaryFiles: any;
|
|
14
|
+
tempDir: any;
|
|
15
|
+
/**
|
|
16
|
+
* Convert WorkingSpec object to YAML string
|
|
17
|
+
*
|
|
18
|
+
* @param {Object} spec - WorkingSpec to convert
|
|
19
|
+
* @returns {string} YAML string representation
|
|
20
|
+
*/
|
|
21
|
+
specToYaml(spec: any): string;
|
|
22
|
+
/**
|
|
23
|
+
* Parse YAML string to WorkingSpec object
|
|
24
|
+
*
|
|
25
|
+
* @param {string} yamlContent - YAML string to parse
|
|
26
|
+
* @returns {Object} Parsed WorkingSpec object
|
|
27
|
+
* @throws {Error} If YAML is invalid or doesn't match WorkingSpec schema
|
|
28
|
+
*/
|
|
29
|
+
yamlToSpec(yamlContent: string): any;
|
|
30
|
+
/**
|
|
31
|
+
* Get path to .caws/working-spec.yaml in project
|
|
32
|
+
*
|
|
33
|
+
* @returns {string} Absolute path to working spec file
|
|
34
|
+
*/
|
|
35
|
+
getSpecFilePath(): string;
|
|
36
|
+
/**
|
|
37
|
+
* Check if working spec file exists
|
|
38
|
+
*
|
|
39
|
+
* @returns {Promise<boolean>} True if file exists
|
|
40
|
+
*/
|
|
41
|
+
specFileExists(): Promise<boolean>;
|
|
42
|
+
/**
|
|
43
|
+
* Read working spec from .caws/working-spec.yaml
|
|
44
|
+
*
|
|
45
|
+
* @returns {Promise<Object>} Parsed WorkingSpec object
|
|
46
|
+
* @throws {Error} If file doesn't exist or is invalid
|
|
47
|
+
*/
|
|
48
|
+
readSpecFile(): Promise<any>;
|
|
49
|
+
/**
|
|
50
|
+
* Write WorkingSpec to file
|
|
51
|
+
*
|
|
52
|
+
* Writes to .caws/working-spec.yaml or a temporary file based on configuration.
|
|
53
|
+
*
|
|
54
|
+
* @param {Object} spec - WorkingSpec to write
|
|
55
|
+
* @param {Object} options - Write options
|
|
56
|
+
* @param {boolean} options.useTemp - Override temp file usage
|
|
57
|
+
* @param {boolean} options.backup - Create backup before writing
|
|
58
|
+
* @returns {Promise<Object>} Write result with file path and cleanup function
|
|
59
|
+
*/
|
|
60
|
+
writeSpecFile(spec: any, options?: {
|
|
61
|
+
useTemp: boolean;
|
|
62
|
+
backup: boolean;
|
|
63
|
+
}): Promise<any>;
|
|
64
|
+
/**
|
|
65
|
+
* Update existing working spec file
|
|
66
|
+
*
|
|
67
|
+
* Reads current spec, merges changes, and writes back.
|
|
68
|
+
*
|
|
69
|
+
* @param {Object} updates - Partial WorkingSpec with fields to update
|
|
70
|
+
* @returns {Promise<Object>} Updated WorkingSpec
|
|
71
|
+
*/
|
|
72
|
+
updateSpecFile(updates: any): Promise<any>;
|
|
73
|
+
/**
|
|
74
|
+
* Create backup of working spec
|
|
75
|
+
*
|
|
76
|
+
* @returns {Promise<string>} Path to backup file
|
|
77
|
+
*/
|
|
78
|
+
backupSpecFile(): Promise<string>;
|
|
79
|
+
/**
|
|
80
|
+
* Restore working spec from backup
|
|
81
|
+
*
|
|
82
|
+
* @param {string} backupPath - Path to backup file
|
|
83
|
+
* @returns {Promise<void>}
|
|
84
|
+
*/
|
|
85
|
+
restoreSpecFile(backupPath: string): Promise<void>;
|
|
86
|
+
/**
|
|
87
|
+
* List all backup files
|
|
88
|
+
*
|
|
89
|
+
* @returns {Promise<string[]>} Array of backup file paths
|
|
90
|
+
*/
|
|
91
|
+
listBackups(): Promise<string[]>;
|
|
92
|
+
/**
|
|
93
|
+
* Delete old backup files
|
|
94
|
+
*
|
|
95
|
+
* @param {Object} options - Cleanup options
|
|
96
|
+
* @param {number} options.maxAge - Maximum age in milliseconds (default: 7 days)
|
|
97
|
+
* @param {number} options.keep - Minimum number of backups to keep (default: 5)
|
|
98
|
+
* @returns {Promise<number>} Number of backups deleted
|
|
99
|
+
*/
|
|
100
|
+
cleanupBackups(options?: {
|
|
101
|
+
maxAge: number;
|
|
102
|
+
keep: number;
|
|
103
|
+
}): Promise<number>;
|
|
104
|
+
/**
|
|
105
|
+
* Validate spec file exists and is parseable
|
|
106
|
+
*
|
|
107
|
+
* @returns {Promise<Object>} Validation result
|
|
108
|
+
*/
|
|
109
|
+
validateSpecFile(): Promise<any>;
|
|
110
|
+
/**
|
|
111
|
+
* Clean up old temporary spec files
|
|
112
|
+
*
|
|
113
|
+
* Removes temp files older than specified age.
|
|
114
|
+
*
|
|
115
|
+
* @param {number} maxAge - Maximum age in milliseconds (default: 1 hour)
|
|
116
|
+
* @returns {Promise<number>} Number of files cleaned up
|
|
117
|
+
*/
|
|
118
|
+
cleanupTempFiles(maxAge?: number): Promise<number>;
|
|
119
|
+
/**
|
|
120
|
+
* Get spec file stats (size, modified date, etc.)
|
|
121
|
+
*
|
|
122
|
+
* @returns {Promise<Object>} File stats
|
|
123
|
+
*/
|
|
124
|
+
getSpecFileStats(): Promise<any>;
|
|
125
|
+
/**
|
|
126
|
+
* Create a new SpecFileManager instance with different configuration
|
|
127
|
+
*
|
|
128
|
+
* @param {Object} config - New configuration
|
|
129
|
+
* @returns {SpecFileManager} New instance
|
|
130
|
+
*/
|
|
131
|
+
withConfig(config: any): SpecFileManager;
|
|
132
|
+
}
|
|
133
|
+
export const defaultSpecFileManager: SpecFileManager;
|
|
134
|
+
/**
|
|
135
|
+
* Create a SpecFileManager instance with default configuration
|
|
136
|
+
*
|
|
137
|
+
* @param {string} projectRoot - Project root directory
|
|
138
|
+
* @param {Object} options - Additional options
|
|
139
|
+
* @returns {SpecFileManager} SpecFileManager instance
|
|
140
|
+
*/
|
|
141
|
+
export function createSpecFileManager(projectRoot: string, options?: any): SpecFileManager;
|
|
142
|
+
export declare function specToYaml(spec: any): string;
|
|
143
|
+
export declare function yamlToSpec(yaml: any): any;
|
|
144
|
+
export declare function readSpecFile(projectRoot: any): Promise<any>;
|
|
145
|
+
export declare function writeSpecFile(spec: any, projectRoot: any, options: any): Promise<any>;
|
|
146
|
+
//# sourceMappingURL=SpecFileManager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SpecFileManager.d.ts","sourceRoot":"","sources":["../../src/spec/SpecFileManager.js"],"names":[],"mappings":"AAaA;;;;;;;;GAQG;AACH;IACE,yBAIC;IAHC,iBAAsD;IACtD,uBAA0D;IAC1D,aAA4C;IAG9C;;;;;OAKG;IACH,uBAFa,MAAM,CASlB;IAED;;;;;;OAMG;IACH,wBAJW,MAAM,OAqBhB;IAED;;;;OAIG;IACH,mBAFa,MAAM,CAIlB;IAED;;;;OAIG;IACH,kBAFa,OAAO,CAAC,OAAO,CAAC,CAS5B;IAED;;;;;OAKG;IACH,gBAHa,OAAO,KAAQ,CAe3B;IAED;;;;;;;;;;OAUG;IACH,mCAJG;QAAyB,OAAO,EAAxB,OAAO;QACU,MAAM,EAAvB,OAAO;KACf,GAAU,OAAO,KAAQ,CA2C3B;IAED;;;;;;;OAOG;IACH,8BAFa,OAAO,KAAQ,CAa3B;IAED;;;;OAIG;IACH,kBAFa,OAAO,CAAC,MAAM,CAAC,CAS3B;IAED;;;;;OAKG;IACH,4BAHW,MAAM,GACJ,OAAO,CAAC,IAAI,CAAC,CAKzB;IAED;;;;OAIG;IACH,eAFa,OAAO,CAAC,MAAM,EAAE,CAAC,CAoB7B;IAED;;;;;;;OAOG;IACH,yBAJG;QAAwB,MAAM,EAAtB,MAAM;QACU,IAAI,EAApB,MAAM;KACd,GAAU,OAAO,CAAC,MAAM,CAAC,CA+B3B;IAED;;;;OAIG;IACH,oBAFa,OAAO,KAAQ,CAe3B;IAED;;;;;;;OAOG;IACH,0BAHW,MAAM,GACJ,OAAO,CAAC,MAAM,CAAC,CA6B3B;IAED;;;;OAIG;IACH,oBAFa,OAAO,KAAQ,CA0B3B;IAED;;;;;OAKG;IACH,yBAFa,eAAe,CAS3B;CACF;AAiBD,qDAAqD;AAfrD;;;;;;GAMG;AACH,mDAJW,MAAM,kBAEJ,eAAe,CAO3B;AAWa,sDAAiD;AACjD,mDAAiD;AAC/C,qEAKb;AACc,+FAKd"}
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
# Claude Code Integration for CAWS
|
|
2
|
+
|
|
3
|
+
This directory contains Claude Code hooks and configuration for CAWS (Coding Agent Working Standard) integration.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
CAWS hooks for Claude Code provide:
|
|
8
|
+
|
|
9
|
+
- **Safety Gates**: Block dangerous commands and scan for secrets
|
|
10
|
+
- **Quality Gates**: Run CAWS quality checks after file edits
|
|
11
|
+
- **Scope Guards**: Validate edits against the working spec's scope
|
|
12
|
+
- **Audit Logging**: Track agent actions for compliance
|
|
13
|
+
|
|
14
|
+
## Directory Structure
|
|
15
|
+
|
|
16
|
+
```
|
|
17
|
+
.claude/
|
|
18
|
+
├── settings.json # Claude Code settings with hooks configuration
|
|
19
|
+
├── hooks/ # Hook scripts
|
|
20
|
+
│ ├── audit.sh # Session and action logging
|
|
21
|
+
│ ├── block-dangerous.sh # Block destructive commands
|
|
22
|
+
│ ├── scan-secrets.sh # Warn when reading sensitive files
|
|
23
|
+
│ ├── quality-check.sh # Run CAWS quality gates
|
|
24
|
+
│ ├── validate-spec.sh # Validate spec files
|
|
25
|
+
│ ├── scope-guard.sh # Check scope boundaries
|
|
26
|
+
│ └── naming-check.sh # Validate file naming conventions
|
|
27
|
+
├── logs/ # Audit logs (gitignored)
|
|
28
|
+
└── README.md # This file
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Hook Events
|
|
32
|
+
|
|
33
|
+
### PreToolUse Hooks
|
|
34
|
+
|
|
35
|
+
Run before Claude executes a tool:
|
|
36
|
+
|
|
37
|
+
| Hook | Matcher | Purpose |
|
|
38
|
+
|------|---------|---------|
|
|
39
|
+
| `block-dangerous.sh` | `Bash` | Block destructive shell commands |
|
|
40
|
+
| `scan-secrets.sh` | `Read` | Warn when reading sensitive files |
|
|
41
|
+
| `scope-guard.sh` | `Write\|Edit` | Check scope boundaries before edits |
|
|
42
|
+
|
|
43
|
+
### PostToolUse Hooks
|
|
44
|
+
|
|
45
|
+
Run after Claude executes a tool:
|
|
46
|
+
|
|
47
|
+
| Hook | Matcher | Purpose |
|
|
48
|
+
|------|---------|---------|
|
|
49
|
+
| `quality-check.sh` | `Write\|Edit` | Run CAWS quality gates |
|
|
50
|
+
| `validate-spec.sh` | `Write\|Edit` | Validate spec file changes |
|
|
51
|
+
| `naming-check.sh` | `Write` | Check file naming conventions |
|
|
52
|
+
| `audit.sh` | `Write\|Edit\|Bash` | Log tool usage |
|
|
53
|
+
|
|
54
|
+
### Session Hooks
|
|
55
|
+
|
|
56
|
+
| Hook | Event | Purpose |
|
|
57
|
+
|------|-------|---------|
|
|
58
|
+
| `audit.sh session-start` | `SessionStart` | Log session start |
|
|
59
|
+
| `audit.sh stop` | `Stop` | Log session end |
|
|
60
|
+
|
|
61
|
+
## Configuration
|
|
62
|
+
|
|
63
|
+
### Enable/Disable Hooks
|
|
64
|
+
|
|
65
|
+
Edit `settings.json` to enable or disable specific hooks. Remove entries from the `hooks` object to disable them.
|
|
66
|
+
|
|
67
|
+
### Hook Levels
|
|
68
|
+
|
|
69
|
+
The scaffold supports four hook levels:
|
|
70
|
+
|
|
71
|
+
- **safety**: Block dangerous commands, scan for secrets
|
|
72
|
+
- **quality**: Run quality gates on file edits
|
|
73
|
+
- **scope**: Validate edits against spec scope
|
|
74
|
+
- **audit**: Log all agent actions
|
|
75
|
+
|
|
76
|
+
Run `caws init --hooks=safety,quality` to enable specific levels.
|
|
77
|
+
|
|
78
|
+
## Audit Logs
|
|
79
|
+
|
|
80
|
+
Audit logs are written to `.claude/logs/`:
|
|
81
|
+
|
|
82
|
+
- `audit.log` - All-time log (appended)
|
|
83
|
+
- `audit-YYYY-MM-DD.log` - Daily logs
|
|
84
|
+
|
|
85
|
+
Logs are JSON-formatted for easy parsing:
|
|
86
|
+
|
|
87
|
+
```json
|
|
88
|
+
{
|
|
89
|
+
"timestamp": "2024-01-15T10:30:00Z",
|
|
90
|
+
"session_id": "abc123",
|
|
91
|
+
"event": "tool_use",
|
|
92
|
+
"tool": "Write",
|
|
93
|
+
"file": "src/index.ts",
|
|
94
|
+
"cwd": "/project"
|
|
95
|
+
}
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## Customization
|
|
99
|
+
|
|
100
|
+
### Adding Custom Hooks
|
|
101
|
+
|
|
102
|
+
1. Create a new script in `.claude/hooks/`
|
|
103
|
+
2. Make it executable: `chmod +x .claude/hooks/my-hook.sh`
|
|
104
|
+
3. Add it to `settings.json`:
|
|
105
|
+
|
|
106
|
+
```json
|
|
107
|
+
{
|
|
108
|
+
"hooks": {
|
|
109
|
+
"PostToolUse": [
|
|
110
|
+
{
|
|
111
|
+
"matcher": "Write|Edit",
|
|
112
|
+
"hooks": [
|
|
113
|
+
{
|
|
114
|
+
"type": "command",
|
|
115
|
+
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/my-hook.sh",
|
|
116
|
+
"timeout": 10
|
|
117
|
+
}
|
|
118
|
+
]
|
|
119
|
+
}
|
|
120
|
+
]
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### Hook Input/Output
|
|
126
|
+
|
|
127
|
+
Hooks receive JSON input via stdin:
|
|
128
|
+
|
|
129
|
+
```json
|
|
130
|
+
{
|
|
131
|
+
"session_id": "abc123",
|
|
132
|
+
"hook_event_name": "PostToolUse",
|
|
133
|
+
"tool_name": "Write",
|
|
134
|
+
"tool_input": {
|
|
135
|
+
"file_path": "/path/to/file.ts",
|
|
136
|
+
"content": "..."
|
|
137
|
+
},
|
|
138
|
+
"tool_response": { "success": true }
|
|
139
|
+
}
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
Hooks can output JSON to control Claude's behavior:
|
|
143
|
+
|
|
144
|
+
```json
|
|
145
|
+
{
|
|
146
|
+
"decision": "block",
|
|
147
|
+
"reason": "Quality gate failed: ..."
|
|
148
|
+
}
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
Or add context:
|
|
152
|
+
|
|
153
|
+
```json
|
|
154
|
+
{
|
|
155
|
+
"hookSpecificOutput": {
|
|
156
|
+
"hookEventName": "PostToolUse",
|
|
157
|
+
"additionalContext": "Remember to update the tests."
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
## Troubleshooting
|
|
163
|
+
|
|
164
|
+
### Hooks Not Running
|
|
165
|
+
|
|
166
|
+
1. Check `settings.json` syntax: `cat .claude/settings.json | jq .`
|
|
167
|
+
2. Verify scripts are executable: `ls -la .claude/hooks/`
|
|
168
|
+
3. Test hooks manually: `echo '{}' | .claude/hooks/audit.sh`
|
|
169
|
+
|
|
170
|
+
### Permission Errors
|
|
171
|
+
|
|
172
|
+
Make all hook scripts executable:
|
|
173
|
+
|
|
174
|
+
```bash
|
|
175
|
+
chmod +x .claude/hooks/*.sh
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
### Debug Hooks
|
|
179
|
+
|
|
180
|
+
Run Claude Code with `--debug` to see hook execution details:
|
|
181
|
+
|
|
182
|
+
```bash
|
|
183
|
+
claude --debug
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
## Further Reading
|
|
187
|
+
|
|
188
|
+
- [Claude Code Hooks Documentation](https://code.claude.com/docs/en/hooks)
|
|
189
|
+
- [CAWS Quality Gates](../../docs/quality-gates.md)
|
|
190
|
+
- [CAWS Scope Management](../../docs/scope-management.md)
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# CAWS Audit Hook for Claude Code
|
|
3
|
+
# Logs agent actions for compliance and debugging
|
|
4
|
+
# @author @darianrosebrook
|
|
5
|
+
|
|
6
|
+
set -euo pipefail
|
|
7
|
+
|
|
8
|
+
# Get event type from argument or input
|
|
9
|
+
EVENT_TYPE="${1:-tool-use}"
|
|
10
|
+
|
|
11
|
+
# Read JSON input from stdin
|
|
12
|
+
INPUT=$(cat)
|
|
13
|
+
|
|
14
|
+
# Parse common fields from Claude Code hook input
|
|
15
|
+
SESSION_ID=$(echo "$INPUT" | jq -r '.session_id // "unknown"')
|
|
16
|
+
CWD=$(echo "$INPUT" | jq -r '.cwd // "."')
|
|
17
|
+
HOOK_EVENT=$(echo "$INPUT" | jq -r '.hook_event_name // "unknown"')
|
|
18
|
+
TOOL_NAME=$(echo "$INPUT" | jq -r '.tool_name // ""')
|
|
19
|
+
PERMISSION_MODE=$(echo "$INPUT" | jq -r '.permission_mode // "default"')
|
|
20
|
+
|
|
21
|
+
# Ensure log directory exists
|
|
22
|
+
LOG_DIR="${CLAUDE_PROJECT_DIR:-.}/.claude/logs"
|
|
23
|
+
mkdir -p "$LOG_DIR"
|
|
24
|
+
|
|
25
|
+
# Log file path
|
|
26
|
+
LOG_FILE="$LOG_DIR/audit.log"
|
|
27
|
+
DATE_LOG_FILE="$LOG_DIR/audit-$(date +%Y-%m-%d).log"
|
|
28
|
+
|
|
29
|
+
# Timestamp
|
|
30
|
+
TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
|
31
|
+
|
|
32
|
+
# Build log entry based on event type
|
|
33
|
+
case "$EVENT_TYPE" in
|
|
34
|
+
session-start)
|
|
35
|
+
SOURCE=$(echo "$INPUT" | jq -r '.source // "unknown"')
|
|
36
|
+
MODEL=$(echo "$INPUT" | jq -r '.model // "unknown"')
|
|
37
|
+
LOG_ENTRY=$(jq -n \
|
|
38
|
+
--arg ts "$TIMESTAMP" \
|
|
39
|
+
--arg sid "$SESSION_ID" \
|
|
40
|
+
--arg event "session_start" \
|
|
41
|
+
--arg source "$SOURCE" \
|
|
42
|
+
--arg model "$MODEL" \
|
|
43
|
+
--arg cwd "$CWD" \
|
|
44
|
+
'{timestamp: $ts, session_id: $sid, event: $event, source: $source, model: $model, cwd: $cwd}')
|
|
45
|
+
;;
|
|
46
|
+
|
|
47
|
+
stop)
|
|
48
|
+
STOP_HOOK_ACTIVE=$(echo "$INPUT" | jq -r '.stop_hook_active // false')
|
|
49
|
+
LOG_ENTRY=$(jq -n \
|
|
50
|
+
--arg ts "$TIMESTAMP" \
|
|
51
|
+
--arg sid "$SESSION_ID" \
|
|
52
|
+
--arg event "session_stop" \
|
|
53
|
+
--arg cwd "$CWD" \
|
|
54
|
+
--argjson hook_active "$STOP_HOOK_ACTIVE" \
|
|
55
|
+
'{timestamp: $ts, session_id: $sid, event: $event, cwd: $cwd, stop_hook_active: $hook_active}')
|
|
56
|
+
;;
|
|
57
|
+
|
|
58
|
+
tool-use)
|
|
59
|
+
# Extract tool-specific info
|
|
60
|
+
TOOL_INPUT=$(echo "$INPUT" | jq -c '.tool_input // {}')
|
|
61
|
+
TOOL_RESPONSE=$(echo "$INPUT" | jq -c '.tool_response // {}')
|
|
62
|
+
TOOL_USE_ID=$(echo "$INPUT" | jq -r '.tool_use_id // ""')
|
|
63
|
+
|
|
64
|
+
# For file operations, extract the path
|
|
65
|
+
FILE_PATH=$(echo "$TOOL_INPUT" | jq -r '.file_path // ""')
|
|
66
|
+
COMMAND=$(echo "$TOOL_INPUT" | jq -r '.command // ""')
|
|
67
|
+
|
|
68
|
+
LOG_ENTRY=$(jq -n \
|
|
69
|
+
--arg ts "$TIMESTAMP" \
|
|
70
|
+
--arg sid "$SESSION_ID" \
|
|
71
|
+
--arg event "tool_use" \
|
|
72
|
+
--arg tool "$TOOL_NAME" \
|
|
73
|
+
--arg file "$FILE_PATH" \
|
|
74
|
+
--arg cmd "$COMMAND" \
|
|
75
|
+
--arg cwd "$CWD" \
|
|
76
|
+
--arg mode "$PERMISSION_MODE" \
|
|
77
|
+
'{timestamp: $ts, session_id: $sid, event: $event, tool: $tool, file: $file, command: $cmd, cwd: $cwd, permission_mode: $mode}')
|
|
78
|
+
;;
|
|
79
|
+
|
|
80
|
+
*)
|
|
81
|
+
LOG_ENTRY=$(jq -n \
|
|
82
|
+
--arg ts "$TIMESTAMP" \
|
|
83
|
+
--arg sid "$SESSION_ID" \
|
|
84
|
+
--arg event "$EVENT_TYPE" \
|
|
85
|
+
--arg hook "$HOOK_EVENT" \
|
|
86
|
+
--arg cwd "$CWD" \
|
|
87
|
+
'{timestamp: $ts, session_id: $sid, event: $event, hook_event: $hook, cwd: $cwd}')
|
|
88
|
+
;;
|
|
89
|
+
esac
|
|
90
|
+
|
|
91
|
+
# Append to log files
|
|
92
|
+
echo "$LOG_ENTRY" >> "$LOG_FILE"
|
|
93
|
+
echo "$LOG_ENTRY" >> "$DATE_LOG_FILE"
|
|
94
|
+
|
|
95
|
+
# Success - allow operation to continue
|
|
96
|
+
exit 0
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# CAWS Dangerous Command Blocker for Claude Code
|
|
3
|
+
# Blocks potentially destructive shell commands
|
|
4
|
+
# @author @darianrosebrook
|
|
5
|
+
|
|
6
|
+
set -euo pipefail
|
|
7
|
+
|
|
8
|
+
# Read JSON input from Claude Code
|
|
9
|
+
INPUT=$(cat)
|
|
10
|
+
|
|
11
|
+
# Extract tool info
|
|
12
|
+
TOOL_NAME=$(echo "$INPUT" | jq -r '.tool_name // ""')
|
|
13
|
+
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // ""')
|
|
14
|
+
|
|
15
|
+
# Only check Bash tool
|
|
16
|
+
if [[ "$TOOL_NAME" != "Bash" ]] || [[ -z "$COMMAND" ]]; then
|
|
17
|
+
exit 0
|
|
18
|
+
fi
|
|
19
|
+
|
|
20
|
+
# Dangerous command patterns
|
|
21
|
+
DANGEROUS_PATTERNS=(
|
|
22
|
+
# Destructive file operations
|
|
23
|
+
'rm -rf /'
|
|
24
|
+
'rm -rf ~'
|
|
25
|
+
'rm -rf \*'
|
|
26
|
+
'rm -rf \.'
|
|
27
|
+
'rm -rf /\*'
|
|
28
|
+
'dd if=/dev/zero'
|
|
29
|
+
'dd if=/dev/random'
|
|
30
|
+
'mkfs\.'
|
|
31
|
+
'fdisk'
|
|
32
|
+
'> /dev/sd'
|
|
33
|
+
|
|
34
|
+
# Fork bombs and resource exhaustion
|
|
35
|
+
':\(\)\{:\|:\&\};:'
|
|
36
|
+
'while true.*fork'
|
|
37
|
+
|
|
38
|
+
# Credential/secret exposure
|
|
39
|
+
'cat.*\.env'
|
|
40
|
+
'cat.*/etc/passwd'
|
|
41
|
+
'cat.*/etc/shadow'
|
|
42
|
+
'cat.*id_rsa'
|
|
43
|
+
'cat.*\.ssh/'
|
|
44
|
+
'cat.*credentials'
|
|
45
|
+
'cat.*\.aws/'
|
|
46
|
+
|
|
47
|
+
# Network exfiltration
|
|
48
|
+
'curl.*\|.*sh'
|
|
49
|
+
'wget.*\|.*sh'
|
|
50
|
+
'curl.*\|.*bash'
|
|
51
|
+
'wget.*\|.*bash'
|
|
52
|
+
|
|
53
|
+
# Permission escalation
|
|
54
|
+
'chmod 777'
|
|
55
|
+
'chmod -R 777'
|
|
56
|
+
'chmod.*\+s'
|
|
57
|
+
|
|
58
|
+
# History manipulation
|
|
59
|
+
'history -c'
|
|
60
|
+
'rm.*\.bash_history'
|
|
61
|
+
'rm.*\.zsh_history'
|
|
62
|
+
|
|
63
|
+
# System modification
|
|
64
|
+
'shutdown'
|
|
65
|
+
'reboot'
|
|
66
|
+
'init 0'
|
|
67
|
+
'init 6'
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
# Check command against dangerous patterns
|
|
71
|
+
for pattern in "${DANGEROUS_PATTERNS[@]}"; do
|
|
72
|
+
if echo "$COMMAND" | grep -qiE "$pattern"; then
|
|
73
|
+
# Output to stderr for Claude to see
|
|
74
|
+
echo "BLOCKED: Command matches dangerous pattern: $pattern" >&2
|
|
75
|
+
echo "Command was: $COMMAND" >&2
|
|
76
|
+
|
|
77
|
+
# Exit code 2 blocks the tool and shows stderr to Claude
|
|
78
|
+
exit 2
|
|
79
|
+
fi
|
|
80
|
+
done
|
|
81
|
+
|
|
82
|
+
# Check for sudo without specific allowed commands
|
|
83
|
+
if echo "$COMMAND" | grep -qE '^sudo\s' && ! echo "$COMMAND" | grep -qE 'sudo (npm|yarn|pnpm|brew|apt-get|apt|dnf|yum)'; then
|
|
84
|
+
echo "BLOCKED: sudo commands require explicit approval" >&2
|
|
85
|
+
echo "If this command is safe, please run it manually in your terminal" >&2
|
|
86
|
+
exit 2
|
|
87
|
+
fi
|
|
88
|
+
|
|
89
|
+
# Allow the command
|
|
90
|
+
exit 0
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# CAWS Naming Convention Check Hook for Claude Code
|
|
3
|
+
# Validates file naming against CAWS conventions
|
|
4
|
+
# @author @darianrosebrook
|
|
5
|
+
|
|
6
|
+
set -euo pipefail
|
|
7
|
+
|
|
8
|
+
# Read JSON input from Claude Code
|
|
9
|
+
INPUT=$(cat)
|
|
10
|
+
|
|
11
|
+
# Extract file path from PostToolUse input
|
|
12
|
+
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // ""')
|
|
13
|
+
TOOL_NAME=$(echo "$INPUT" | jq -r '.tool_name // ""')
|
|
14
|
+
|
|
15
|
+
# Only check Write tool (new files)
|
|
16
|
+
if [[ "$TOOL_NAME" != "Write" ]]; then
|
|
17
|
+
exit 0
|
|
18
|
+
fi
|
|
19
|
+
|
|
20
|
+
if [[ -z "$FILE_PATH" ]]; then
|
|
21
|
+
exit 0
|
|
22
|
+
fi
|
|
23
|
+
|
|
24
|
+
# Get filename
|
|
25
|
+
FILENAME=$(basename "$FILE_PATH")
|
|
26
|
+
|
|
27
|
+
# Banned modifiers that indicate incomplete/temporary naming
|
|
28
|
+
BANNED_MODIFIERS=(
|
|
29
|
+
"enhanced"
|
|
30
|
+
"unified"
|
|
31
|
+
"simplified"
|
|
32
|
+
"better"
|
|
33
|
+
"new"
|
|
34
|
+
"next"
|
|
35
|
+
"final"
|
|
36
|
+
"copy"
|
|
37
|
+
"revamp"
|
|
38
|
+
"improved"
|
|
39
|
+
"alt"
|
|
40
|
+
"tmp"
|
|
41
|
+
"scratch"
|
|
42
|
+
"wip"
|
|
43
|
+
"test-"
|
|
44
|
+
"-test"
|
|
45
|
+
"_test"
|
|
46
|
+
"temp"
|
|
47
|
+
"old"
|
|
48
|
+
"backup"
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
# Convert filename to lowercase for checking
|
|
52
|
+
FILENAME_LOWER=$(echo "$FILENAME" | tr '[:upper:]' '[:lower:]')
|
|
53
|
+
|
|
54
|
+
# Check for banned modifiers
|
|
55
|
+
for modifier in "${BANNED_MODIFIERS[@]}"; do
|
|
56
|
+
if [[ "$FILENAME_LOWER" == *"$modifier"* ]]; then
|
|
57
|
+
# Special case: allow test files that follow conventions
|
|
58
|
+
if [[ "$modifier" == "test-" ]] || [[ "$modifier" == "-test" ]] || [[ "$modifier" == "_test" ]]; then
|
|
59
|
+
if [[ "$FILENAME_LOWER" =~ \.(test|spec)\.(js|ts|jsx|tsx|py|go|rs)$ ]]; then
|
|
60
|
+
continue
|
|
61
|
+
fi
|
|
62
|
+
fi
|
|
63
|
+
|
|
64
|
+
echo '{
|
|
65
|
+
"hookSpecificOutput": {
|
|
66
|
+
"hookEventName": "PostToolUse",
|
|
67
|
+
"additionalContext": "Warning: The filename '\'''"$FILENAME"''\'' contains the modifier '\'''"$modifier"''\'' which may indicate temporary or non-canonical naming. Consider using a more descriptive, permanent name. See CAWS naming conventions in .caws/canonical-map.yaml or run '\''caws naming check'\''."
|
|
68
|
+
}
|
|
69
|
+
}'
|
|
70
|
+
exit 0
|
|
71
|
+
fi
|
|
72
|
+
done
|
|
73
|
+
|
|
74
|
+
# Check for version suffixes (e.g., file-v2.js, file_v3.ts)
|
|
75
|
+
if [[ "$FILENAME_LOWER" =~ [-_]v[0-9]+\. ]]; then
|
|
76
|
+
echo '{
|
|
77
|
+
"hookSpecificOutput": {
|
|
78
|
+
"hookEventName": "PostToolUse",
|
|
79
|
+
"additionalContext": "Warning: The filename '\'''"$FILENAME"''\'' contains a version suffix. Version control should be handled by git, not file names. Consider removing the version suffix."
|
|
80
|
+
}
|
|
81
|
+
}'
|
|
82
|
+
exit 0
|
|
83
|
+
fi
|
|
84
|
+
|
|
85
|
+
# Check for date stamps (e.g., file-2024-01-15.js)
|
|
86
|
+
if [[ "$FILENAME_LOWER" =~ [0-9]{4}[-_][0-9]{2}[-_][0-9]{2} ]]; then
|
|
87
|
+
echo '{
|
|
88
|
+
"hookSpecificOutput": {
|
|
89
|
+
"hookEventName": "PostToolUse",
|
|
90
|
+
"additionalContext": "Warning: The filename '\'''"$FILENAME"''\'' contains a date stamp. Version control should be handled by git, not file names. Consider removing the date."
|
|
91
|
+
}
|
|
92
|
+
}'
|
|
93
|
+
exit 0
|
|
94
|
+
fi
|
|
95
|
+
|
|
96
|
+
# File naming is OK
|
|
97
|
+
exit 0
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# CAWS Quality Check Hook for Claude Code
|
|
3
|
+
# Runs CAWS quality validation after file edits
|
|
4
|
+
# @author @darianrosebrook
|
|
5
|
+
|
|
6
|
+
set -euo pipefail
|
|
7
|
+
|
|
8
|
+
# Read JSON input from Claude Code
|
|
9
|
+
INPUT=$(cat)
|
|
10
|
+
|
|
11
|
+
# Extract file info from PostToolUse input
|
|
12
|
+
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // ""')
|
|
13
|
+
TOOL_NAME=$(echo "$INPUT" | jq -r '.tool_name // ""')
|
|
14
|
+
|
|
15
|
+
# Only run on Write/Edit of source files
|
|
16
|
+
if [[ "$TOOL_NAME" != "Write" ]] && [[ "$TOOL_NAME" != "Edit" ]]; then
|
|
17
|
+
exit 0
|
|
18
|
+
fi
|
|
19
|
+
|
|
20
|
+
# Skip non-source files and node_modules/dist
|
|
21
|
+
if [[ ! "$FILE_PATH" =~ \.(js|ts|jsx|tsx|py|go|rs|java|mjs|cjs)$ ]] || \
|
|
22
|
+
[[ "$FILE_PATH" =~ node_modules ]] || \
|
|
23
|
+
[[ "$FILE_PATH" =~ dist/ ]] || \
|
|
24
|
+
[[ "$FILE_PATH" =~ build/ ]]; then
|
|
25
|
+
exit 0
|
|
26
|
+
fi
|
|
27
|
+
|
|
28
|
+
# Determine project directory
|
|
29
|
+
PROJECT_DIR="${CLAUDE_PROJECT_DIR:-.}"
|
|
30
|
+
|
|
31
|
+
# Check if we're in a CAWS project
|
|
32
|
+
if [[ ! -f "$PROJECT_DIR/.caws/working-spec.yaml" ]]; then
|
|
33
|
+
exit 0
|
|
34
|
+
fi
|
|
35
|
+
|
|
36
|
+
# Check if CAWS CLI is available
|
|
37
|
+
if ! command -v caws &> /dev/null; then
|
|
38
|
+
# Suggest installing CAWS
|
|
39
|
+
echo '{
|
|
40
|
+
"hookSpecificOutput": {
|
|
41
|
+
"hookEventName": "PostToolUse",
|
|
42
|
+
"additionalContext": "CAWS CLI not available. Consider installing with: npm install -g @caws/cli"
|
|
43
|
+
}
|
|
44
|
+
}'
|
|
45
|
+
exit 0
|
|
46
|
+
fi
|
|
47
|
+
|
|
48
|
+
# Run CAWS quality gates in quiet mode for quick feedback
|
|
49
|
+
if caws quality-gates --context=commit --quiet 2>/dev/null; then
|
|
50
|
+
# Quality check passed - provide positive feedback
|
|
51
|
+
echo '{
|
|
52
|
+
"hookSpecificOutput": {
|
|
53
|
+
"hookEventName": "PostToolUse",
|
|
54
|
+
"additionalContext": "Quality gates passed for this change."
|
|
55
|
+
}
|
|
56
|
+
}'
|
|
57
|
+
else
|
|
58
|
+
# Quality check failed - provide feedback to Claude
|
|
59
|
+
# Run again to get violations summary
|
|
60
|
+
VIOLATIONS=$(caws quality-gates --context=commit --json 2>/dev/null | jq -r '.violations[:3] | .[] | "- \(.gate): \(.message)"' 2>/dev/null || echo "Run 'caws quality-gates' for details")
|
|
61
|
+
|
|
62
|
+
echo '{
|
|
63
|
+
"decision": "block",
|
|
64
|
+
"reason": "Quality gate violations detected. Please address the following issues before continuing:\n'"$VIOLATIONS"'\n\nRun '\''caws quality-gates'\'' for full details."
|
|
65
|
+
}'
|
|
66
|
+
fi
|
|
67
|
+
|
|
68
|
+
exit 0
|