claude-spp 0.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/LICENSE.txt +21 -0
- package/README.md +147 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +164 -0
- package/dist/cli.js.map +1 -0
- package/dist/config/loader.d.ts +29 -0
- package/dist/config/loader.d.ts.map +1 -0
- package/dist/config/loader.js +81 -0
- package/dist/config/loader.js.map +1 -0
- package/dist/config/schema.d.ts +84 -0
- package/dist/config/schema.d.ts.map +1 -0
- package/dist/config/schema.js +104 -0
- package/dist/config/schema.js.map +1 -0
- package/dist/git/history.d.ts +55 -0
- package/dist/git/history.d.ts.map +1 -0
- package/dist/git/history.js +376 -0
- package/dist/git/history.js.map +1 -0
- package/dist/hooks/file-matcher.d.ts +22 -0
- package/dist/hooks/file-matcher.d.ts.map +1 -0
- package/dist/hooks/file-matcher.js +86 -0
- package/dist/hooks/file-matcher.js.map +1 -0
- package/dist/hooks/index.d.ts +4 -0
- package/dist/hooks/index.d.ts.map +1 -0
- package/dist/hooks/index.js +4 -0
- package/dist/hooks/index.js.map +1 -0
- package/dist/hooks/pre-tool-use.d.ts +35 -0
- package/dist/hooks/pre-tool-use.d.ts.map +1 -0
- package/dist/hooks/pre-tool-use.js +132 -0
- package/dist/hooks/pre-tool-use.js.map +1 -0
- package/dist/hooks/system-prompt.d.ts +9 -0
- package/dist/hooks/system-prompt.d.ts.map +1 -0
- package/dist/hooks/system-prompt.js +88 -0
- package/dist/hooks/system-prompt.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +13 -0
- package/dist/index.js.map +1 -0
- package/dist/init.d.ts +25 -0
- package/dist/init.d.ts.map +1 -0
- package/dist/init.js +274 -0
- package/dist/init.js.map +1 -0
- package/dist/stats.d.ts +40 -0
- package/dist/stats.d.ts.map +1 -0
- package/dist/stats.js +146 -0
- package/dist/stats.js.map +1 -0
- package/package.json +41 -0
package/LICENSE.txt
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Matthew Olson
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
# Simian Programmer Plugin (SPP) for Claude
|
|
2
|
+
|
|
3
|
+
> If you’re learning how to play the guitar, you can watch as many YouTube videos as you want, you’re not going to learn the guitar. You have to put your fingers on the strings to actually learn the motions. I think there is a parallel here to programming, where programming has to be learned in part by the actual typing.
|
|
4
|
+
|
|
5
|
+
-[DHH on Lex Friedman](youtube.com/watch?v=vagyIcmIGOQ&t=5408)
|
|
6
|
+
|
|
7
|
+
## How it works
|
|
8
|
+
|
|
9
|
+
SPP is a Claude plugin that encourages you to keep writing code. It is configured per-project, and provides 5 modes:
|
|
10
|
+
|
|
11
|
+
| Mode | Description |
|
|
12
|
+
|------|-------------|
|
|
13
|
+
| Lazy monkey | 100% AI coding |
|
|
14
|
+
| Curious monkey | 10% human / 90% AI |
|
|
15
|
+
| Clever monkey | 25% human / 75% AI |
|
|
16
|
+
| Wise monkey | 50% human / 50% AI |
|
|
17
|
+
| Crazy monkey | 100% human coding |
|
|
18
|
+
|
|
19
|
+
### Code ratio tracking
|
|
20
|
+
The human/AI coding ratio is calculated by looking at commits that you have authored. Commits with "**Co-Authored-By: Claude**" in the message are counted as Claude commits, those without are counted as human commits.
|
|
21
|
+
|
|
22
|
+
There is an option to track by lines of code (added+deleted) if you wish, but I find that tracking by commit and practicing good git hygeine and using small, well scoped commits works best.
|
|
23
|
+
|
|
24
|
+
Tracking starts from the latest commit when the plugin is initialized, so that only new work is counted. It looks back over a time window (default 7 days).
|
|
25
|
+
|
|
26
|
+
## Ratio enforcement and the help human code skill
|
|
27
|
+
|
|
28
|
+
When the ratio of human/AI commits becomes too low, Claude is **blocked** from writing code. Instead, it uses a "help human code" skill to help **you** write the code, and then reviews your work. This skill instructs Claude to provide you with a high level over view of the task, code pointers, and test instructions. Example:
|
|
29
|
+
|
|
30
|
+
```
|
|
31
|
+
❯ Some tests are failing, can you fix them?
|
|
32
|
+
|
|
33
|
+
⏺ I'd be happy to help, but I'm currently blocked from writing code due to SPP restrictions -
|
|
34
|
+
your human coding ratio is below the 100% target. Let me use the spp-human-task skill to guide
|
|
35
|
+
you through fixing the tests yourself.
|
|
36
|
+
...
|
|
37
|
+
---
|
|
38
|
+
High Level Goal
|
|
39
|
+
|
|
40
|
+
Fix 15 failing tests across 3 test files. The issues are:
|
|
41
|
+
|
|
42
|
+
1. tests/config.test.ts (2 failures) - Test expectations don't match the actual MODES array
|
|
43
|
+
2. tests/hooks.test.ts (6 failures) - Tests use wrong interface for calling preToolUseHook
|
|
44
|
+
3. tests/init.test.ts (7 failures) - Tests timeout because initializeStp prompts for user input via stdin
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
File 1: tests/config.test.ts
|
|
48
|
+
|
|
49
|
+
Problem: The tests expect 6 modes with different ratios, but the actual MODES array only has 5 modes with different values.
|
|
50
|
+
|
|
51
|
+
Lines 97-109 need to be updated:
|
|
52
|
+
|
|
53
|
+
Fix 1: Line 98-99 - "returns custom ratio when set"
|
|
54
|
+
|
|
55
|
+
The test tries to set humanWorkRatio on the config, but Config doesn't have a humanWorkRatio field. The getEffectiveRatio function only looks at the mode. This test is invalid - you should either:
|
|
56
|
+
- Remove this test entirely, OR
|
|
57
|
+
- Change it to test setting a different mode
|
|
58
|
+
|
|
59
|
+
(etc...)
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
Test Your Changes
|
|
63
|
+
|
|
64
|
+
After making the fixes, run:
|
|
65
|
+
npm test
|
|
66
|
+
|
|
67
|
+
All tests should pass.
|
|
68
|
+
|
|
69
|
+
---
|
|
70
|
+
Would you like more detailed guidance on any of these fixes,
|
|
71
|
+
or do you want me to review your code after you make the changes?
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
After you make the fix, you can ask claude to review and commit your changes.
|
|
75
|
+
|
|
76
|
+
### Pausing, resuming, and reseting
|
|
77
|
+
|
|
78
|
+
Sometimes you gotta do what you gotta do.
|
|
79
|
+
|
|
80
|
+
You may **pause** SPP to allow Claude to freely write code (`spp pause`). This pause expires after 24 hours, or you can unpause with `spp resume`.
|
|
81
|
+
|
|
82
|
+
If you get way behind on your coding and want to declare bankruptcy, you can reset tracking from the current commit using:
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
spp reset
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Don't feel bad, no one is keeping score!
|
|
89
|
+
|
|
90
|
+
### git post commit hook shows stats
|
|
91
|
+
|
|
92
|
+
After you commit, a post-commit hook prints out current stats:
|
|
93
|
+
```bash
|
|
94
|
+
% git commit -m "Adjust some wording"
|
|
95
|
+
|
|
96
|
+
✅ 🐒 Human coding on target. Current: 100% Target: 25%. Keep up the great work!
|
|
97
|
+
|
|
98
|
+
Mode Clever monkey (75% AI / 25% human)
|
|
99
|
+
Tracking Commits since a4e1e8c "remove .spp" 1/22/2026
|
|
100
|
+
|
|
101
|
+
Human 1 commits 10 lines
|
|
102
|
+
Claude 0 commits 0 lines
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
### Getting started
|
|
107
|
+
|
|
108
|
+
1. Add the plugin in claude:
|
|
109
|
+
```bash
|
|
110
|
+
# Start claude and run:
|
|
111
|
+
/plugin marketplace add mlolson/claude-spp
|
|
112
|
+
/plugin install spp@mlolson
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
2. Install the CLI globally
|
|
116
|
+
```bash
|
|
117
|
+
npm i -g git+https://github.com/mlolson/claude-spp.git
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
3. Initialize SPP in your project:
|
|
121
|
+
```bash
|
|
122
|
+
cd /path/to/your/project
|
|
123
|
+
spp init
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
4. Bonus step: Turn off AI suggestions in your IDE. (It kind of of defeats the purpose)
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
### CLI Commands
|
|
130
|
+
|
|
131
|
+
| Command | Description |
|
|
132
|
+
|---------|-------------|
|
|
133
|
+
| `spp help` | Print help message
|
|
134
|
+
| `spp init` | Initialize SPP in the current project |
|
|
135
|
+
| `spp stats` | Show current ratio and statistics |
|
|
136
|
+
| `spp modes` | List all available modes |
|
|
137
|
+
| `spp mode [n]` | Show or change the current mode (1-5) |
|
|
138
|
+
| `spp pause` | Pause enforcement for 24 hours |
|
|
139
|
+
| `spp resume` | Resume enforcement immediately |
|
|
140
|
+
| `spp reset` | Reset tracking to start from current commit |
|
|
141
|
+
|
|
142
|
+
### Claude slash commands
|
|
143
|
+
|
|
144
|
+
| Command | Description |
|
|
145
|
+
|---------|-------------|
|
|
146
|
+
| `/spp:stats` | Show current coding stats |
|
|
147
|
+
| `/spp:help` | Get help with the CLI interface |
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Command } from "commander";
|
|
3
|
+
import { runPreToolUseHook } from "./hooks/pre-tool-use.js";
|
|
4
|
+
import { generateSystemPrompt } from "./hooks/system-prompt.js";
|
|
5
|
+
import { initializeSpp, isFullyInitialized, promptUser } from "./init.js";
|
|
6
|
+
import { getHeadCommitHash } from "./git/history.js";
|
|
7
|
+
import { loadConfig, saveConfig } from "./config/loader.js";
|
|
8
|
+
import { getCurrentMode, getModeByNumber, getModeByName, MODES } from "./config/schema.js";
|
|
9
|
+
import { getStats, formatStats } from "./stats.js";
|
|
10
|
+
function getModesExplanation() {
|
|
11
|
+
const config = loadConfig(process.cwd());
|
|
12
|
+
const currentMode = getCurrentMode(config);
|
|
13
|
+
const lines = ["Coding modes:"];
|
|
14
|
+
for (const mode of MODES) {
|
|
15
|
+
const marker = mode.number === currentMode.number ? " <-- current" : "";
|
|
16
|
+
lines.push(` ${mode.number}. ${mode.name} - ${mode.description}${marker}`);
|
|
17
|
+
}
|
|
18
|
+
return lines.join("\n");
|
|
19
|
+
}
|
|
20
|
+
const program = new Command();
|
|
21
|
+
program
|
|
22
|
+
.name("spp")
|
|
23
|
+
.description("SPP CLI - Simian Programmer Plugin for Claude - https://github.com/mlolson/claude-spp")
|
|
24
|
+
.version("0.1.0");
|
|
25
|
+
// User commands
|
|
26
|
+
program
|
|
27
|
+
.command("init")
|
|
28
|
+
.argument("[mode]", "mode number 1-6")
|
|
29
|
+
.description("Initialize SPP")
|
|
30
|
+
.action(async (mode) => {
|
|
31
|
+
const modeNum = mode ? parseInt(mode, 10) : undefined;
|
|
32
|
+
const config = await initializeSpp(process.cwd(), modeNum);
|
|
33
|
+
saveConfig(process.cwd(), config);
|
|
34
|
+
const currentMode = getCurrentMode(config);
|
|
35
|
+
console.log("");
|
|
36
|
+
console.log(`✅ SPP initialized with mode ${currentMode.number}: ${currentMode.name}`);
|
|
37
|
+
console.log(`${currentMode.description}`);
|
|
38
|
+
console.log(`Install directory: .claude-spp/`);
|
|
39
|
+
console.log(`Git hook: .git/hooks/post-commit\n`);
|
|
40
|
+
console.log("Analyzing repo...");
|
|
41
|
+
const stats = getStats(process.cwd());
|
|
42
|
+
console.log(formatStats(stats));
|
|
43
|
+
});
|
|
44
|
+
program
|
|
45
|
+
.command("mode")
|
|
46
|
+
.argument("[value]", "mode number or name")
|
|
47
|
+
.description("Show or change the current mode")
|
|
48
|
+
.action(async (value) => {
|
|
49
|
+
if (!isFullyInitialized(process.cwd())) {
|
|
50
|
+
console.error("❌ SPP not initialized. Run: spp init");
|
|
51
|
+
process.exit(1);
|
|
52
|
+
}
|
|
53
|
+
const config = loadConfig(process.cwd());
|
|
54
|
+
// If no argument, show current mode
|
|
55
|
+
if (!value) {
|
|
56
|
+
const currentMode = getModeByNumber(config.mode);
|
|
57
|
+
if (!currentMode) {
|
|
58
|
+
console.log("Current mode: none");
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
console.log(`Current mode: ${currentMode.number}, ${currentMode.name}, ${currentMode.description}`);
|
|
62
|
+
}
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
// Try to parse as number first
|
|
66
|
+
let selectedMode = getModeByNumber(parseInt(value, 10));
|
|
67
|
+
// If not a number, try by name
|
|
68
|
+
if (!selectedMode) {
|
|
69
|
+
selectedMode = getModeByName(value);
|
|
70
|
+
}
|
|
71
|
+
if (!selectedMode) {
|
|
72
|
+
throw Error(`❌ Unknown mode: ${value} Use a number 1-6 or a mode name`);
|
|
73
|
+
}
|
|
74
|
+
// Update config
|
|
75
|
+
config.mode = selectedMode.number;
|
|
76
|
+
saveConfig(process.cwd(), config);
|
|
77
|
+
console.log(`✅ Mode changed to ${selectedMode.number}: ${selectedMode.name}`);
|
|
78
|
+
console.log(` ${selectedMode.description}`);
|
|
79
|
+
});
|
|
80
|
+
program
|
|
81
|
+
.command("modes")
|
|
82
|
+
.description("List available modes")
|
|
83
|
+
.action(() => {
|
|
84
|
+
if (!isFullyInitialized(process.cwd())) {
|
|
85
|
+
console.error("❌ SPP not initialized. Run: spp init");
|
|
86
|
+
process.exit(1);
|
|
87
|
+
}
|
|
88
|
+
console.log(getModesExplanation());
|
|
89
|
+
console.log("To change mode: spp mode <number>");
|
|
90
|
+
});
|
|
91
|
+
program
|
|
92
|
+
.command("stats")
|
|
93
|
+
.description("Show SPP status and statistics")
|
|
94
|
+
.action(() => {
|
|
95
|
+
const stats = getStats(process.cwd());
|
|
96
|
+
console.log(formatStats(stats));
|
|
97
|
+
});
|
|
98
|
+
program
|
|
99
|
+
.command("pause")
|
|
100
|
+
.description("Pause SPP for 24 hours (Claude writes freely)")
|
|
101
|
+
.action(() => {
|
|
102
|
+
if (!isFullyInitialized(process.cwd())) {
|
|
103
|
+
console.error("❌ SPP not initialized. Run: spp init");
|
|
104
|
+
process.exit(1);
|
|
105
|
+
}
|
|
106
|
+
const config = loadConfig(process.cwd());
|
|
107
|
+
config.enabled = false;
|
|
108
|
+
config.pausedUntil = new Date(Date.now() + 24 * 60 * 60 * 1000).toISOString();
|
|
109
|
+
saveConfig(process.cwd(), config);
|
|
110
|
+
console.log("⏸️ SPP enforcement paused for 24 hours. Claude may write code freely.");
|
|
111
|
+
console.log(" Run 'spp resume' to resume SPP enforcement immediately.");
|
|
112
|
+
});
|
|
113
|
+
program
|
|
114
|
+
.command("resume")
|
|
115
|
+
.description("Resume SPP tracking")
|
|
116
|
+
.action(() => {
|
|
117
|
+
if (!isFullyInitialized(process.cwd())) {
|
|
118
|
+
console.error("❌ SPP not initialized. Run: spp init");
|
|
119
|
+
process.exit(1);
|
|
120
|
+
}
|
|
121
|
+
const config = loadConfig(process.cwd());
|
|
122
|
+
config.enabled = true;
|
|
123
|
+
delete config.pausedUntil;
|
|
124
|
+
saveConfig(process.cwd(), config);
|
|
125
|
+
console.log("▶️ SPP resumed. Coding enforcement is active.");
|
|
126
|
+
});
|
|
127
|
+
program
|
|
128
|
+
.command("reset")
|
|
129
|
+
.description("Reset tracking to start from current commit")
|
|
130
|
+
.action(async () => {
|
|
131
|
+
if (!isFullyInitialized(process.cwd())) {
|
|
132
|
+
console.error("❌ SPP not initialized. Run: spp init");
|
|
133
|
+
process.exit(1);
|
|
134
|
+
}
|
|
135
|
+
const answer = await promptUser("This will reset tracking to start from the current commit. All previous stats will be cleared. Are you sure? (y/N): ");
|
|
136
|
+
if (answer.toLowerCase() !== "y") {
|
|
137
|
+
console.log("Reset cancelled.");
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
const headCommit = getHeadCommitHash(process.cwd());
|
|
141
|
+
if (!headCommit) {
|
|
142
|
+
console.error("❌ Could not get current commit hash.");
|
|
143
|
+
process.exit(1);
|
|
144
|
+
}
|
|
145
|
+
const config = loadConfig(process.cwd());
|
|
146
|
+
config.trackingStartCommit = headCommit;
|
|
147
|
+
saveConfig(process.cwd(), config);
|
|
148
|
+
console.log(`✅ Tracking reset. Stats will now start from commit ${headCommit.substring(0, 7)}.`);
|
|
149
|
+
});
|
|
150
|
+
// Hook commands (called by Claude Code)
|
|
151
|
+
program
|
|
152
|
+
.command("hook:pre-tool-use")
|
|
153
|
+
.description("Pre-tool-use hook (internal, reads JSON from stdin)")
|
|
154
|
+
.action(async () => {
|
|
155
|
+
await runPreToolUseHook();
|
|
156
|
+
});
|
|
157
|
+
program
|
|
158
|
+
.command("hook:system-prompt")
|
|
159
|
+
.description("Output system prompt injection (internal)")
|
|
160
|
+
.action(() => {
|
|
161
|
+
console.log(generateSystemPrompt(process.cwd()));
|
|
162
|
+
});
|
|
163
|
+
program.parseAsync();
|
|
164
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { type Config } from "./schema.js";
|
|
2
|
+
/**
|
|
3
|
+
* Get the path to the .claude-spp directory for a project
|
|
4
|
+
*/
|
|
5
|
+
export declare function getSppDir(projectPath: string): string;
|
|
6
|
+
/**
|
|
7
|
+
* Get the path to the config file
|
|
8
|
+
*/
|
|
9
|
+
export declare function getConfigPath(projectPath: string): string;
|
|
10
|
+
/**
|
|
11
|
+
* Check if SPP is initialized in the project
|
|
12
|
+
*/
|
|
13
|
+
export declare function isSppInitialized(projectPath: string): boolean;
|
|
14
|
+
/**
|
|
15
|
+
* Load and validate the config file
|
|
16
|
+
* Returns default config if file doesn't exist
|
|
17
|
+
* Throws if config is invalid
|
|
18
|
+
* Auto-unpauses if pausedUntil time has passed
|
|
19
|
+
*/
|
|
20
|
+
export declare function loadConfig(projectPath: string): Config;
|
|
21
|
+
/**
|
|
22
|
+
* Save config to file
|
|
23
|
+
*/
|
|
24
|
+
export declare function saveConfig(projectPath: string, config: Config): void;
|
|
25
|
+
/**
|
|
26
|
+
* Initialize SPP in a project with default config
|
|
27
|
+
*/
|
|
28
|
+
export declare function initializeConfig(projectPath: string): Config;
|
|
29
|
+
//# sourceMappingURL=loader.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../../src/config/loader.ts"],"names":[],"mappings":"AAEA,OAAO,EAAgC,KAAK,MAAM,EAAE,MAAM,aAAa,CAAC;AAKxE;;GAEG;AACH,wBAAgB,SAAS,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAErD;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAEzD;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAE7D;AAED;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CA6BtD;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAiBpE;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAO5D"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import * as fs from "node:fs";
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
import { ConfigSchema, DEFAULT_CONFIG } from "./schema.js";
|
|
4
|
+
const SPP_DIR = ".claude-spp";
|
|
5
|
+
const CONFIG_FILE = "config.json";
|
|
6
|
+
/**
|
|
7
|
+
* Get the path to the .claude-spp directory for a project
|
|
8
|
+
*/
|
|
9
|
+
export function getSppDir(projectPath) {
|
|
10
|
+
return path.join(projectPath, SPP_DIR);
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Get the path to the config file
|
|
14
|
+
*/
|
|
15
|
+
export function getConfigPath(projectPath) {
|
|
16
|
+
return path.join(getSppDir(projectPath), CONFIG_FILE);
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Check if SPP is initialized in the project
|
|
20
|
+
*/
|
|
21
|
+
export function isSppInitialized(projectPath) {
|
|
22
|
+
return fs.existsSync(getSppDir(projectPath));
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Load and validate the config file
|
|
26
|
+
* Returns default config if file doesn't exist
|
|
27
|
+
* Throws if config is invalid
|
|
28
|
+
* Auto-unpauses if pausedUntil time has passed
|
|
29
|
+
*/
|
|
30
|
+
export function loadConfig(projectPath) {
|
|
31
|
+
const configPath = getConfigPath(projectPath);
|
|
32
|
+
if (!fs.existsSync(configPath)) {
|
|
33
|
+
return DEFAULT_CONFIG;
|
|
34
|
+
}
|
|
35
|
+
try {
|
|
36
|
+
const raw = fs.readFileSync(configPath, "utf-8");
|
|
37
|
+
const json = JSON.parse(raw);
|
|
38
|
+
const config = ConfigSchema.parse(json);
|
|
39
|
+
// Auto-unpause if pausedUntil time has passed
|
|
40
|
+
if (config.pausedUntil) {
|
|
41
|
+
const unpauseTime = new Date(config.pausedUntil).getTime();
|
|
42
|
+
if (Date.now() > unpauseTime) {
|
|
43
|
+
config.enabled = true;
|
|
44
|
+
delete config.pausedUntil;
|
|
45
|
+
saveConfig(projectPath, config);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return config;
|
|
49
|
+
}
|
|
50
|
+
catch (error) {
|
|
51
|
+
if (error instanceof SyntaxError) {
|
|
52
|
+
throw new Error(`Invalid JSON in ${configPath}: ${error.message}`);
|
|
53
|
+
}
|
|
54
|
+
throw error;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Save config to file
|
|
59
|
+
*/
|
|
60
|
+
export function saveConfig(projectPath, config) {
|
|
61
|
+
const sppDir = getSppDir(projectPath);
|
|
62
|
+
const configPath = getConfigPath(projectPath);
|
|
63
|
+
// Ensure .claude-spp directory exists
|
|
64
|
+
if (!fs.existsSync(sppDir)) {
|
|
65
|
+
fs.mkdirSync(sppDir, { recursive: true });
|
|
66
|
+
}
|
|
67
|
+
// Validate before saving
|
|
68
|
+
const validated = ConfigSchema.parse(config);
|
|
69
|
+
fs.writeFileSync(configPath, JSON.stringify(validated, null, 2) + "\n", "utf-8");
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Initialize SPP in a project with default config
|
|
73
|
+
*/
|
|
74
|
+
export function initializeConfig(projectPath) {
|
|
75
|
+
const config = {
|
|
76
|
+
...DEFAULT_CONFIG,
|
|
77
|
+
};
|
|
78
|
+
saveConfig(projectPath, config);
|
|
79
|
+
return config;
|
|
80
|
+
}
|
|
81
|
+
//# sourceMappingURL=loader.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loader.js","sourceRoot":"","sources":["../../src/config/loader.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
/**
|
|
3
|
+
* Mode definition
|
|
4
|
+
*/
|
|
5
|
+
export interface Mode {
|
|
6
|
+
number: number;
|
|
7
|
+
name: string;
|
|
8
|
+
humanRatio: number;
|
|
9
|
+
description: string;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Available modes for work distribution
|
|
13
|
+
*/
|
|
14
|
+
export declare const MODES: Mode[];
|
|
15
|
+
/**
|
|
16
|
+
* Get mode by number
|
|
17
|
+
*/
|
|
18
|
+
export declare function getModeByNumber(num: number): Mode | undefined;
|
|
19
|
+
/**
|
|
20
|
+
* Get mode by name (case-insensitive)
|
|
21
|
+
*/
|
|
22
|
+
export declare function getModeByName(name: string): Mode | undefined;
|
|
23
|
+
/**
|
|
24
|
+
* Stats window options for filtering commit history
|
|
25
|
+
*/
|
|
26
|
+
export declare const StatsWindowSchema: z.ZodEnum<["oneDay", "oneWeek", "allTime"]>;
|
|
27
|
+
export type StatsWindow = z.infer<typeof StatsWindowSchema>;
|
|
28
|
+
/**
|
|
29
|
+
* Tracking mode options - what to count for ratio calculation
|
|
30
|
+
*/
|
|
31
|
+
export declare const TrackingModeSchema: z.ZodEnum<["commits", "lines"]>;
|
|
32
|
+
export type TrackingMode = z.infer<typeof TrackingModeSchema>;
|
|
33
|
+
/**
|
|
34
|
+
* Human-readable labels for tracking modes
|
|
35
|
+
*/
|
|
36
|
+
export declare const TRACKING_MODE_LABELS: Record<TrackingMode, string>;
|
|
37
|
+
/**
|
|
38
|
+
* Human-readable labels for stats windows
|
|
39
|
+
*/
|
|
40
|
+
export declare const STATS_WINDOW_LABELS: Record<StatsWindow, string>;
|
|
41
|
+
/**
|
|
42
|
+
* Get cutoff date for a stats window
|
|
43
|
+
* @returns Date for filtering or null for allTime (no filter)
|
|
44
|
+
*/
|
|
45
|
+
export declare function getStatsWindowCutoff(window: StatsWindow): Date | null;
|
|
46
|
+
/**
|
|
47
|
+
* Main configuration schema for .dojo/config.json
|
|
48
|
+
*/
|
|
49
|
+
export declare const ConfigSchema: z.ZodObject<{
|
|
50
|
+
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
51
|
+
mode: z.ZodDefault<z.ZodNumber>;
|
|
52
|
+
statsWindow: z.ZodDefault<z.ZodEnum<["oneDay", "oneWeek", "allTime"]>>;
|
|
53
|
+
trackingMode: z.ZodDefault<z.ZodEnum<["commits", "lines"]>>;
|
|
54
|
+
pausedUntil: z.ZodOptional<z.ZodString>;
|
|
55
|
+
trackingStartCommit: z.ZodOptional<z.ZodString>;
|
|
56
|
+
}, "strip", z.ZodTypeAny, {
|
|
57
|
+
enabled: boolean;
|
|
58
|
+
mode: number;
|
|
59
|
+
statsWindow: "oneDay" | "oneWeek" | "allTime";
|
|
60
|
+
trackingMode: "commits" | "lines";
|
|
61
|
+
pausedUntil?: string | undefined;
|
|
62
|
+
trackingStartCommit?: string | undefined;
|
|
63
|
+
}, {
|
|
64
|
+
enabled?: boolean | undefined;
|
|
65
|
+
mode?: number | undefined;
|
|
66
|
+
statsWindow?: "oneDay" | "oneWeek" | "allTime" | undefined;
|
|
67
|
+
trackingMode?: "commits" | "lines" | undefined;
|
|
68
|
+
pausedUntil?: string | undefined;
|
|
69
|
+
trackingStartCommit?: string | undefined;
|
|
70
|
+
}>;
|
|
71
|
+
export type Config = z.infer<typeof ConfigSchema>;
|
|
72
|
+
/**
|
|
73
|
+
* Default configuration values
|
|
74
|
+
*/
|
|
75
|
+
export declare const DEFAULT_CONFIG: Config;
|
|
76
|
+
/**
|
|
77
|
+
* Get the current mode from config
|
|
78
|
+
*/
|
|
79
|
+
export declare function getCurrentMode(config: Config): Mode;
|
|
80
|
+
/**
|
|
81
|
+
* Get the effective human work ratio from config
|
|
82
|
+
*/
|
|
83
|
+
export declare function getEffectiveRatio(config: Config): number;
|
|
84
|
+
//# sourceMappingURL=schema.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../src/config/schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;GAEG;AACH,MAAM,WAAW,IAAI;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;GAEG;AACH,eAAO,MAAM,KAAK,EAAE,IAAI,EAMvB,CAAC;AAEF;;GAEG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAE7D;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAE5D;AAED;;GAEG;AACH,eAAO,MAAM,iBAAiB,6CAA2C,CAAC;AAC1E,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAE5D;;GAEG;AACH,eAAO,MAAM,kBAAkB,iCAA+B,CAAC;AAC/D,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAE9D;;GAEG;AACH,eAAO,MAAM,oBAAoB,EAAE,MAAM,CAAC,YAAY,EAAE,MAAM,CAG7D,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,mBAAmB,EAAE,MAAM,CAAC,WAAW,EAAE,MAAM,CAI3D,CAAC;AAEF;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,WAAW,GAAG,IAAI,GAAG,IAAI,CAUrE;AAED;;GAEG;AACH,eAAO,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;EAoBvB,CAAC;AAEH,MAAM,MAAM,MAAM,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,YAAY,CAAC,CAAC;AAElD;;GAEG;AACH,eAAO,MAAM,cAAc,EAAE,MAK5B,CAAC;AAEF;;GAEG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAEnD;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAIxD"}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
/**
|
|
3
|
+
* Available modes for work distribution
|
|
4
|
+
*/
|
|
5
|
+
export const MODES = [
|
|
6
|
+
{ number: 1, name: "Lazy monkey", humanRatio: 0, description: "100% AI coding" },
|
|
7
|
+
{ number: 2, name: "Curious monkey", humanRatio: 0.1, description: "90% AI / 10% human" },
|
|
8
|
+
{ number: 3, name: "Clever monkey", humanRatio: 0.25, description: "75% AI / 25% human" },
|
|
9
|
+
{ number: 4, name: "Wise monkey", humanRatio: 0.5, description: "50% AI / 50% human" },
|
|
10
|
+
{ number: 5, name: "Crazy monkey", humanRatio: 1, description: "100% human coding" },
|
|
11
|
+
];
|
|
12
|
+
/**
|
|
13
|
+
* Get mode by number
|
|
14
|
+
*/
|
|
15
|
+
export function getModeByNumber(num) {
|
|
16
|
+
return MODES.find((m) => m.number === num);
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Get mode by name (case-insensitive)
|
|
20
|
+
*/
|
|
21
|
+
export function getModeByName(name) {
|
|
22
|
+
return MODES.find((m) => m.name.toLowerCase() === name.toLowerCase());
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Stats window options for filtering commit history
|
|
26
|
+
*/
|
|
27
|
+
export const StatsWindowSchema = z.enum(["oneDay", "oneWeek", "allTime"]);
|
|
28
|
+
/**
|
|
29
|
+
* Tracking mode options - what to count for ratio calculation
|
|
30
|
+
*/
|
|
31
|
+
export const TrackingModeSchema = z.enum(["commits", "lines"]);
|
|
32
|
+
/**
|
|
33
|
+
* Human-readable labels for tracking modes
|
|
34
|
+
*/
|
|
35
|
+
export const TRACKING_MODE_LABELS = {
|
|
36
|
+
commits: "Commits",
|
|
37
|
+
lines: "Lines of code",
|
|
38
|
+
};
|
|
39
|
+
/**
|
|
40
|
+
* Human-readable labels for stats windows
|
|
41
|
+
*/
|
|
42
|
+
export const STATS_WINDOW_LABELS = {
|
|
43
|
+
oneDay: "Last 24 hours",
|
|
44
|
+
oneWeek: "Last 7 days",
|
|
45
|
+
allTime: "All time",
|
|
46
|
+
};
|
|
47
|
+
/**
|
|
48
|
+
* Get cutoff date for a stats window
|
|
49
|
+
* @returns Date for filtering or null for allTime (no filter)
|
|
50
|
+
*/
|
|
51
|
+
export function getStatsWindowCutoff(window) {
|
|
52
|
+
const now = new Date();
|
|
53
|
+
switch (window) {
|
|
54
|
+
case "oneDay":
|
|
55
|
+
return new Date(now.getTime() - 24 * 60 * 60 * 1000);
|
|
56
|
+
case "oneWeek":
|
|
57
|
+
return new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000);
|
|
58
|
+
case "allTime":
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Main configuration schema for .dojo/config.json
|
|
64
|
+
*/
|
|
65
|
+
export const ConfigSchema = z.object({
|
|
66
|
+
// Whether Dojo is enabled for this project
|
|
67
|
+
enabled: z.boolean().default(true),
|
|
68
|
+
// Mode number (1-6)
|
|
69
|
+
mode: z.number().int().min(1).max(6).default(4),
|
|
70
|
+
// Stats window for filtering commit history
|
|
71
|
+
statsWindow: StatsWindowSchema.default("oneWeek"),
|
|
72
|
+
// Tracking mode - what to count for ratio calculation
|
|
73
|
+
trackingMode: TrackingModeSchema.default("commits"),
|
|
74
|
+
// ISO timestamp when SPP pause expires (set by pause command)
|
|
75
|
+
pausedUntil: z.string().optional(),
|
|
76
|
+
// Git commit hash of the last commit to exclude from tracking
|
|
77
|
+
// Set to the 10th commit hash once we reach MIN_COMMITS_FOR_TRACKING
|
|
78
|
+
// Stats only include commits after this one
|
|
79
|
+
trackingStartCommit: z.string().optional(),
|
|
80
|
+
});
|
|
81
|
+
/**
|
|
82
|
+
* Default configuration values
|
|
83
|
+
*/
|
|
84
|
+
export const DEFAULT_CONFIG = {
|
|
85
|
+
enabled: true,
|
|
86
|
+
mode: 3,
|
|
87
|
+
statsWindow: "oneWeek",
|
|
88
|
+
trackingMode: "commits",
|
|
89
|
+
};
|
|
90
|
+
/**
|
|
91
|
+
* Get the current mode from config
|
|
92
|
+
*/
|
|
93
|
+
export function getCurrentMode(config) {
|
|
94
|
+
return getModeByNumber(config.mode) ?? MODES[3]; // Default to 50-50
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Get the effective human work ratio from config
|
|
98
|
+
*/
|
|
99
|
+
export function getEffectiveRatio(config) {
|
|
100
|
+
// Use mode
|
|
101
|
+
const mode = getCurrentMode(config);
|
|
102
|
+
return mode.humanRatio;
|
|
103
|
+
}
|
|
104
|
+
//# sourceMappingURL=schema.js.map
|