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.
Files changed (47) hide show
  1. package/LICENSE.txt +21 -0
  2. package/README.md +147 -0
  3. package/dist/cli.d.ts +3 -0
  4. package/dist/cli.d.ts.map +1 -0
  5. package/dist/cli.js +164 -0
  6. package/dist/cli.js.map +1 -0
  7. package/dist/config/loader.d.ts +29 -0
  8. package/dist/config/loader.d.ts.map +1 -0
  9. package/dist/config/loader.js +81 -0
  10. package/dist/config/loader.js.map +1 -0
  11. package/dist/config/schema.d.ts +84 -0
  12. package/dist/config/schema.d.ts.map +1 -0
  13. package/dist/config/schema.js +104 -0
  14. package/dist/config/schema.js.map +1 -0
  15. package/dist/git/history.d.ts +55 -0
  16. package/dist/git/history.d.ts.map +1 -0
  17. package/dist/git/history.js +376 -0
  18. package/dist/git/history.js.map +1 -0
  19. package/dist/hooks/file-matcher.d.ts +22 -0
  20. package/dist/hooks/file-matcher.d.ts.map +1 -0
  21. package/dist/hooks/file-matcher.js +86 -0
  22. package/dist/hooks/file-matcher.js.map +1 -0
  23. package/dist/hooks/index.d.ts +4 -0
  24. package/dist/hooks/index.d.ts.map +1 -0
  25. package/dist/hooks/index.js +4 -0
  26. package/dist/hooks/index.js.map +1 -0
  27. package/dist/hooks/pre-tool-use.d.ts +35 -0
  28. package/dist/hooks/pre-tool-use.d.ts.map +1 -0
  29. package/dist/hooks/pre-tool-use.js +132 -0
  30. package/dist/hooks/pre-tool-use.js.map +1 -0
  31. package/dist/hooks/system-prompt.d.ts +9 -0
  32. package/dist/hooks/system-prompt.d.ts.map +1 -0
  33. package/dist/hooks/system-prompt.js +88 -0
  34. package/dist/hooks/system-prompt.js.map +1 -0
  35. package/dist/index.d.ts +8 -0
  36. package/dist/index.d.ts.map +1 -0
  37. package/dist/index.js +13 -0
  38. package/dist/index.js.map +1 -0
  39. package/dist/init.d.ts +25 -0
  40. package/dist/init.d.ts.map +1 -0
  41. package/dist/init.js +274 -0
  42. package/dist/init.js.map +1 -0
  43. package/dist/stats.d.ts +40 -0
  44. package/dist/stats.d.ts.map +1 -0
  45. package/dist/stats.js +146 -0
  46. package/dist/stats.js.map +1 -0
  47. 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,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=cli.d.ts.map
@@ -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
@@ -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