claude-kanban 0.2.1 → 0.3.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/bin/cli.js +133 -11
- package/dist/bin/cli.js.map +1 -1
- package/dist/server/index.js +48 -8
- package/dist/server/index.js.map +1 -1
- package/package.json +1 -1
package/dist/bin/cli.js
CHANGED
|
@@ -16,7 +16,7 @@ import { existsSync as existsSync3 } from "fs";
|
|
|
16
16
|
// src/server/services/executor.ts
|
|
17
17
|
import { spawn, execSync } from "child_process";
|
|
18
18
|
import { join as join4 } from "path";
|
|
19
|
-
import { writeFileSync as writeFileSync4, unlinkSync, mkdirSync as mkdirSync2, existsSync as existsSync2, appendFileSync as appendFileSync2, readFileSync as readFileSync4, rmSync } from "fs";
|
|
19
|
+
import { writeFileSync as writeFileSync4, unlinkSync, mkdirSync as mkdirSync2, existsSync as existsSync2, appendFileSync as appendFileSync2, readFileSync as readFileSync4, rmSync, symlinkSync } from "fs";
|
|
20
20
|
import { EventEmitter } from "events";
|
|
21
21
|
|
|
22
22
|
// src/server/services/project.ts
|
|
@@ -36,7 +36,39 @@ function detectPackageManager(projectPath) {
|
|
|
36
36
|
if (existsSync(join(projectPath, "bun.lockb"))) return "bun";
|
|
37
37
|
return "npm";
|
|
38
38
|
}
|
|
39
|
+
function detectProjectType(projectPath) {
|
|
40
|
+
if (existsSync(join(projectPath, "package.json"))) return "node";
|
|
41
|
+
if (existsSync(join(projectPath, "pyproject.toml")) || existsSync(join(projectPath, "requirements.txt")) || existsSync(join(projectPath, "setup.py"))) return "python";
|
|
42
|
+
if (existsSync(join(projectPath, "composer.json"))) return "php";
|
|
43
|
+
if (existsSync(join(projectPath, "Gemfile"))) return "ruby";
|
|
44
|
+
if (existsSync(join(projectPath, "go.mod"))) return "go";
|
|
45
|
+
if (existsSync(join(projectPath, "Cargo.toml"))) return "rust";
|
|
46
|
+
return "unknown";
|
|
47
|
+
}
|
|
39
48
|
function detectProjectCommands(projectPath) {
|
|
49
|
+
const projectType = detectProjectType(projectPath);
|
|
50
|
+
switch (projectType) {
|
|
51
|
+
case "node":
|
|
52
|
+
return detectNodeCommands(projectPath);
|
|
53
|
+
case "python":
|
|
54
|
+
return detectPythonCommands(projectPath);
|
|
55
|
+
case "php":
|
|
56
|
+
return detectPHPCommands(projectPath);
|
|
57
|
+
case "ruby":
|
|
58
|
+
return detectRubyCommands(projectPath);
|
|
59
|
+
case "go":
|
|
60
|
+
return detectGoCommands(projectPath);
|
|
61
|
+
case "rust":
|
|
62
|
+
return detectRustCommands(projectPath);
|
|
63
|
+
default:
|
|
64
|
+
return {
|
|
65
|
+
testCommand: "",
|
|
66
|
+
typecheckCommand: "",
|
|
67
|
+
buildCommand: ""
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
function detectNodeCommands(projectPath) {
|
|
40
72
|
const pm = detectPackageManager(projectPath);
|
|
41
73
|
const run = pm === "npm" ? "npm run" : pm;
|
|
42
74
|
const packageJsonPath = join(projectPath, "package.json");
|
|
@@ -48,11 +80,61 @@ function detectProjectCommands(projectPath) {
|
|
|
48
80
|
} catch {
|
|
49
81
|
}
|
|
50
82
|
}
|
|
51
|
-
const testCommand = scripts.test ? `${run} test` :
|
|
52
|
-
const typecheckCommand = scripts.typecheck ? `${run} typecheck` : scripts["type-check"] ? `${run} type-check` : existsSync(join(projectPath, "tsconfig.json")) ? `${run === "npm run" ? "npx" : pm} tsc --noEmit` :
|
|
53
|
-
const buildCommand = scripts.build ? `${run} build` :
|
|
83
|
+
const testCommand = scripts.test ? `${run} test` : "";
|
|
84
|
+
const typecheckCommand = scripts.typecheck ? `${run} typecheck` : scripts["type-check"] ? `${run} type-check` : existsSync(join(projectPath, "tsconfig.json")) ? `${run === "npm run" ? "npx" : pm} tsc --noEmit` : "";
|
|
85
|
+
const buildCommand = scripts.build ? `${run} build` : "";
|
|
54
86
|
return { testCommand, typecheckCommand, buildCommand };
|
|
55
87
|
}
|
|
88
|
+
function detectPythonCommands(projectPath) {
|
|
89
|
+
const hasPytest = existsSync(join(projectPath, "pytest.ini")) || existsSync(join(projectPath, "pyproject.toml")) || existsSync(join(projectPath, "tests"));
|
|
90
|
+
const hasMypy = existsSync(join(projectPath, "mypy.ini")) || existsSync(join(projectPath, ".mypy.ini"));
|
|
91
|
+
return {
|
|
92
|
+
testCommand: hasPytest ? "pytest" : "",
|
|
93
|
+
typecheckCommand: hasMypy ? "mypy ." : "",
|
|
94
|
+
buildCommand: ""
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
function detectPHPCommands(projectPath) {
|
|
98
|
+
const hasPhpunit = existsSync(join(projectPath, "phpunit.xml")) || existsSync(join(projectPath, "phpunit.xml.dist"));
|
|
99
|
+
const hasPest = existsSync(join(projectPath, "tests", "Pest.php"));
|
|
100
|
+
const hasPhpstan = existsSync(join(projectPath, "phpstan.neon")) || existsSync(join(projectPath, "phpstan.neon.dist"));
|
|
101
|
+
let testCommand = "";
|
|
102
|
+
if (hasPest) {
|
|
103
|
+
testCommand = "./vendor/bin/pest";
|
|
104
|
+
} else if (hasPhpunit) {
|
|
105
|
+
testCommand = "./vendor/bin/phpunit";
|
|
106
|
+
}
|
|
107
|
+
return {
|
|
108
|
+
testCommand,
|
|
109
|
+
typecheckCommand: hasPhpstan ? "./vendor/bin/phpstan analyse" : "",
|
|
110
|
+
buildCommand: ""
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
function detectRubyCommands(projectPath) {
|
|
114
|
+
const hasRspec = existsSync(join(projectPath, "spec")) || existsSync(join(projectPath, ".rspec"));
|
|
115
|
+
const hasMinitest = existsSync(join(projectPath, "test"));
|
|
116
|
+
return {
|
|
117
|
+
testCommand: hasRspec ? "bundle exec rspec" : hasMinitest ? "bundle exec rake test" : "",
|
|
118
|
+
typecheckCommand: "",
|
|
119
|
+
// Ruby doesn't have built-in typechecking (sorbet is optional)
|
|
120
|
+
buildCommand: ""
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
function detectGoCommands(_projectPath) {
|
|
124
|
+
return {
|
|
125
|
+
testCommand: "go test ./...",
|
|
126
|
+
typecheckCommand: "go build ./...",
|
|
127
|
+
// Go's compiler is the typechecker
|
|
128
|
+
buildCommand: "go build"
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
function detectRustCommands(_projectPath) {
|
|
132
|
+
return {
|
|
133
|
+
testCommand: "cargo test",
|
|
134
|
+
typecheckCommand: "cargo check",
|
|
135
|
+
buildCommand: "cargo build"
|
|
136
|
+
};
|
|
137
|
+
}
|
|
56
138
|
function createInitialPRD(projectName) {
|
|
57
139
|
return {
|
|
58
140
|
version: "1.0",
|
|
@@ -617,6 +699,7 @@ ${summary}
|
|
|
617
699
|
cwd: this.projectPath,
|
|
618
700
|
stdio: "pipe"
|
|
619
701
|
});
|
|
702
|
+
this.symlinkDependencies(worktreePath);
|
|
620
703
|
console.log(`[executor] Created worktree at ${worktreePath} on branch ${branchName}`);
|
|
621
704
|
return { worktreePath, branchName };
|
|
622
705
|
} catch (error) {
|
|
@@ -624,6 +707,38 @@ ${summary}
|
|
|
624
707
|
return null;
|
|
625
708
|
}
|
|
626
709
|
}
|
|
710
|
+
/**
|
|
711
|
+
* Symlink dependency directories from main project to worktree
|
|
712
|
+
*/
|
|
713
|
+
symlinkDependencies(worktreePath) {
|
|
714
|
+
const depDirs = [
|
|
715
|
+
"node_modules",
|
|
716
|
+
".pnpm",
|
|
717
|
+
// pnpm store
|
|
718
|
+
".yarn",
|
|
719
|
+
// yarn cache
|
|
720
|
+
"vendor",
|
|
721
|
+
// PHP/Ruby deps
|
|
722
|
+
"__pycache__",
|
|
723
|
+
// Python cache
|
|
724
|
+
".venv",
|
|
725
|
+
// Python virtual env
|
|
726
|
+
"venv"
|
|
727
|
+
// Python virtual env
|
|
728
|
+
];
|
|
729
|
+
for (const dir of depDirs) {
|
|
730
|
+
const sourcePath = join4(this.projectPath, dir);
|
|
731
|
+
const targetPath = join4(worktreePath, dir);
|
|
732
|
+
if (existsSync2(sourcePath) && !existsSync2(targetPath)) {
|
|
733
|
+
try {
|
|
734
|
+
symlinkSync(sourcePath, targetPath, "junction");
|
|
735
|
+
console.log(`[executor] Symlinked ${dir} to worktree`);
|
|
736
|
+
} catch (error) {
|
|
737
|
+
console.log(`[executor] Could not symlink ${dir}:`, error);
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
}
|
|
627
742
|
/**
|
|
628
743
|
* Remove a git worktree
|
|
629
744
|
*/
|
|
@@ -716,6 +831,17 @@ ${summary}
|
|
|
716
831
|
const stepsText = task.steps.length > 0 ? `
|
|
717
832
|
Verification steps:
|
|
718
833
|
${task.steps.map((s, i) => `${i + 1}. ${s}`).join("\n")}` : "";
|
|
834
|
+
const verifySteps = [];
|
|
835
|
+
if (config.project.typecheckCommand) {
|
|
836
|
+
verifySteps.push(`Run typecheck: ${config.project.typecheckCommand}`);
|
|
837
|
+
}
|
|
838
|
+
if (config.project.testCommand) {
|
|
839
|
+
verifySteps.push(`Run tests: ${config.project.testCommand}`);
|
|
840
|
+
}
|
|
841
|
+
const verifySection = verifySteps.length > 0 ? `2. Verify your work:
|
|
842
|
+
${verifySteps.map((s) => ` - ${s}`).join("\n")}
|
|
843
|
+
|
|
844
|
+
` : "";
|
|
719
845
|
return `You are an AI coding agent. Complete the following task:
|
|
720
846
|
|
|
721
847
|
## TASK
|
|
@@ -729,23 +855,19 @@ ${stepsText}
|
|
|
729
855
|
## INSTRUCTIONS
|
|
730
856
|
1. Implement this task as described above.
|
|
731
857
|
|
|
732
|
-
2.
|
|
733
|
-
- Run typecheck: ${config.project.typecheckCommand}
|
|
734
|
-
- Run tests: ${config.project.testCommand}
|
|
735
|
-
|
|
736
|
-
3. When complete, update the task in ${prdPath}:
|
|
858
|
+
${verifySection}${verifySteps.length > 0 ? "3" : "2"}. When complete, update the task in ${prdPath}:
|
|
737
859
|
- Find the task with id "${task.id}"
|
|
738
860
|
- Set "passes": true
|
|
739
861
|
- Set "status": "completed"
|
|
740
862
|
|
|
741
|
-
4. Document your work in ${progressPath}:
|
|
863
|
+
${verifySteps.length > 0 ? "4" : "3"}. Document your work in ${progressPath}:
|
|
742
864
|
- What you implemented and files changed
|
|
743
865
|
- Key decisions made and why
|
|
744
866
|
- Gotchas, edge cases, or tricky parts discovered
|
|
745
867
|
- Useful patterns or approaches that worked well
|
|
746
868
|
- Anything a future agent should know about this area of the codebase
|
|
747
869
|
|
|
748
|
-
5. Make a git commit with a descriptive message.
|
|
870
|
+
${verifySteps.length > 0 ? "5" : "4"}. Make a git commit with a descriptive message.
|
|
749
871
|
|
|
750
872
|
Focus only on this task. When successfully complete, output: <promise>COMPLETE</promise>`;
|
|
751
873
|
}
|