claude-teammate 0.1.20 → 0.1.22
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/README.md +19 -2
- package/package.json +1 -1
- package/src/cli.js +37 -10
- package/src/commands/start.js +14 -10
- package/src/commands/status.js +5 -2
- package/src/commands/stop.js +5 -2
- package/src/config.js +13 -1
package/README.md
CHANGED
|
@@ -91,14 +91,18 @@ memory/
|
|
|
91
91
|
|
|
92
92
|
## Quickstart
|
|
93
93
|
|
|
94
|
-
**Requirements:**
|
|
94
|
+
**Requirements:**
|
|
95
|
+
- Node.js 20+
|
|
96
|
+
- [Claude CLI](https://docs.anthropic.com/en/docs/claude-code) installed and authenticated
|
|
97
|
+
- A Jira API token
|
|
98
|
+
- A GitHub PAT with `repo` + PR permissions
|
|
95
99
|
|
|
96
100
|
```bash
|
|
97
101
|
npm install -g claude-teammate
|
|
98
102
|
tm8 start
|
|
99
103
|
```
|
|
100
104
|
|
|
101
|
-
`start` creates `.env` on the first run,
|
|
105
|
+
`start` creates `.env` on the first run, then launches a background worker for the current project. If `.env` already exists with the required values, setup is skipped and your credentials are not prompted for again.
|
|
102
106
|
|
|
103
107
|
To upgrade to latest version
|
|
104
108
|
|
|
@@ -163,6 +167,19 @@ It reads the actual repository before writing anything - structure, conventions,
|
|
|
163
167
|
|
|
164
168
|
<br/>
|
|
165
169
|
|
|
170
|
+
## Roadmap
|
|
171
|
+
|
|
172
|
+
- GitLab support
|
|
173
|
+
- Bitbucket support
|
|
174
|
+
- Ensure no conflicts for multiple concurrent claude-teammate agents
|
|
175
|
+
- Slack and Teams notifications when PRs are opened or merged
|
|
176
|
+
- Automatic PR description generation from commit history and issue context
|
|
177
|
+
- Branch-naming convention enforcement per epic or project config
|
|
178
|
+
- Stale-issue nudges when tickets sit unactioned past a configurable threshold
|
|
179
|
+
- Test-coverage gating before issue closure
|
|
180
|
+
|
|
181
|
+
<br/>
|
|
182
|
+
|
|
166
183
|
## Contributing
|
|
167
184
|
|
|
168
185
|
Contributions are welcome. Open an issue to discuss before submitting large changes.
|
package/package.json
CHANGED
package/src/cli.js
CHANGED
|
@@ -5,22 +5,28 @@ import { runStatusCommand } from "./commands/status.js";
|
|
|
5
5
|
import { runStopCommand } from "./commands/stop.js";
|
|
6
6
|
import { runWorkerCommand } from "./commands/worker.js";
|
|
7
7
|
|
|
8
|
+
const LOCAL_FLAG = "--local";
|
|
9
|
+
const COMMANDS_WITH_LOCAL_FLAG = new Set(["start", "stop", "status"]);
|
|
10
|
+
|
|
8
11
|
const HELP_TEXT = `claude-teammate
|
|
9
12
|
|
|
10
13
|
Usage:
|
|
11
|
-
claude-teammate start
|
|
12
|
-
claude-teammate stop
|
|
13
|
-
claude-teammate status
|
|
14
|
+
claude-teammate start [${LOCAL_FLAG}]
|
|
15
|
+
claude-teammate stop [${LOCAL_FLAG}]
|
|
16
|
+
claude-teammate status [${LOCAL_FLAG}]
|
|
14
17
|
claude-teammate --help
|
|
15
18
|
|
|
16
19
|
Commands:
|
|
17
|
-
start Start Claude Teammate in the background
|
|
18
|
-
stop Stop the background worker
|
|
20
|
+
start Start Claude Teammate in the background (default: global mode using ~/.tm8)
|
|
21
|
+
stop Stop the background worker
|
|
19
22
|
status Show worker status, logs, and last Jira poll state
|
|
23
|
+
|
|
24
|
+
Flags:
|
|
25
|
+
${LOCAL_FLAG} Use the current project directory instead of the global ~/.tm8 directory
|
|
20
26
|
`;
|
|
21
27
|
|
|
22
28
|
export async function runCli(args) {
|
|
23
|
-
const [command] = args;
|
|
29
|
+
const [command, ...remainingArgs] = args;
|
|
24
30
|
const projectRoot = process.cwd();
|
|
25
31
|
const entrypointPath = process.argv[1];
|
|
26
32
|
|
|
@@ -29,23 +35,30 @@ export async function runCli(args) {
|
|
|
29
35
|
return;
|
|
30
36
|
}
|
|
31
37
|
|
|
38
|
+
const invalidFlag = findInvalidFlag(command, remainingArgs);
|
|
39
|
+
if (invalidFlag) {
|
|
40
|
+
process.stderr.write(`Unknown flag for ${command}: ${invalidFlag}\n\n${HELP_TEXT}\n`);
|
|
41
|
+
process.exitCode = 1;
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
|
|
32
45
|
if (command === "start") {
|
|
33
|
-
await runStartCommand({ projectRoot, entrypointPath });
|
|
46
|
+
await runStartCommand({ projectRoot, entrypointPath, args: remainingArgs });
|
|
34
47
|
return;
|
|
35
48
|
}
|
|
36
49
|
|
|
37
50
|
if (command === "stop") {
|
|
38
|
-
await runStopCommand({ projectRoot });
|
|
51
|
+
await runStopCommand({ projectRoot, args: remainingArgs });
|
|
39
52
|
return;
|
|
40
53
|
}
|
|
41
54
|
|
|
42
55
|
if (command === "status") {
|
|
43
|
-
await runStatusCommand({ projectRoot });
|
|
56
|
+
await runStatusCommand({ projectRoot, args: remainingArgs });
|
|
44
57
|
return;
|
|
45
58
|
}
|
|
46
59
|
|
|
47
60
|
if (command === "run-worker") {
|
|
48
|
-
const workerProjectRoot =
|
|
61
|
+
const workerProjectRoot = remainingArgs[0];
|
|
49
62
|
if (!workerProjectRoot) {
|
|
50
63
|
throw new Error("Missing project root for run-worker.");
|
|
51
64
|
}
|
|
@@ -57,3 +70,17 @@ export async function runCli(args) {
|
|
|
57
70
|
process.stderr.write(`Unknown command: ${command}\n\n${HELP_TEXT}\n`);
|
|
58
71
|
process.exitCode = 1;
|
|
59
72
|
}
|
|
73
|
+
|
|
74
|
+
export function findInvalidFlag(command, args) {
|
|
75
|
+
if (!COMMANDS_WITH_LOCAL_FLAG.has(command)) {
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
for (const arg of args) {
|
|
80
|
+
if (arg.startsWith("-") && arg !== LOCAL_FLAG) {
|
|
81
|
+
return arg;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return null;
|
|
86
|
+
}
|
package/src/commands/start.js
CHANGED
|
@@ -6,17 +6,21 @@ import process from "node:process";
|
|
|
6
6
|
import { ensurePlaywrightMcpAvailable, verifyClaudeCli } from "../claude.js";
|
|
7
7
|
import {
|
|
8
8
|
REQUIRED_FIELDS,
|
|
9
|
+
ensureGlobalConfigDir,
|
|
9
10
|
getMissingFields,
|
|
10
11
|
loadProjectEnv,
|
|
11
12
|
persistEnvFile
|
|
12
13
|
} from "../config.js";
|
|
13
14
|
import { ensureRuntimeDir, isProcessRunning, readPid, removePid } from "../runtime.js";
|
|
14
15
|
|
|
15
|
-
export async function runStartCommand({ projectRoot, entrypointPath }) {
|
|
16
|
-
const
|
|
16
|
+
export async function runStartCommand({ projectRoot, entrypointPath, args = [] }) {
|
|
17
|
+
const isLocal = args.includes("--local");
|
|
18
|
+
const configRoot = isLocal ? projectRoot : await ensureGlobalConfigDir();
|
|
19
|
+
|
|
20
|
+
const { values } = await loadProjectEnv(configRoot);
|
|
17
21
|
|
|
18
22
|
if (Object.keys(values).length === 0) {
|
|
19
|
-
await runSetupWizard(
|
|
23
|
+
await runSetupWizard(configRoot);
|
|
20
24
|
} else {
|
|
21
25
|
const missingFields = getMissingFields(values);
|
|
22
26
|
|
|
@@ -25,7 +29,7 @@ export async function runStartCommand({ projectRoot, entrypointPath }) {
|
|
|
25
29
|
}
|
|
26
30
|
}
|
|
27
31
|
|
|
28
|
-
const runtimePaths = await ensureRuntimeDir(
|
|
32
|
+
const runtimePaths = await ensureRuntimeDir(configRoot);
|
|
29
33
|
const existingPid = await readPid(runtimePaths.pidFile);
|
|
30
34
|
|
|
31
35
|
if (existingPid && isProcessRunning(existingPid)) {
|
|
@@ -40,12 +44,12 @@ export async function runStartCommand({ projectRoot, entrypointPath }) {
|
|
|
40
44
|
|
|
41
45
|
process.stdout.write("Checking Claude CLI...\n");
|
|
42
46
|
await verifyClaudeCli({
|
|
43
|
-
cwd:
|
|
47
|
+
cwd: configRoot,
|
|
44
48
|
timeoutMs: 15_000
|
|
45
49
|
});
|
|
46
50
|
process.stdout.write("Checking Playwright MCP...\n");
|
|
47
51
|
const playwrightMcp = await ensurePlaywrightMcpAvailable({
|
|
48
|
-
cwd:
|
|
52
|
+
cwd: configRoot,
|
|
49
53
|
scope: "user",
|
|
50
54
|
timeoutMs: 15_000,
|
|
51
55
|
installTimeoutMs: 60_000
|
|
@@ -57,8 +61,8 @@ export async function runStartCommand({ projectRoot, entrypointPath }) {
|
|
|
57
61
|
}
|
|
58
62
|
|
|
59
63
|
const logFd = openSync(runtimePaths.logFile, "a");
|
|
60
|
-
const child = spawn(process.execPath, [entrypointPath, "run-worker",
|
|
61
|
-
cwd:
|
|
64
|
+
const child = spawn(process.execPath, [entrypointPath, "run-worker", configRoot], {
|
|
65
|
+
cwd: configRoot,
|
|
62
66
|
detached: true,
|
|
63
67
|
stdio: ["ignore", logFd, logFd]
|
|
64
68
|
});
|
|
@@ -70,7 +74,7 @@ export async function runStartCommand({ projectRoot, entrypointPath }) {
|
|
|
70
74
|
process.stdout.write(`Logs: ${runtimePaths.logFile}\n`);
|
|
71
75
|
}
|
|
72
76
|
|
|
73
|
-
async function runSetupWizard(
|
|
77
|
+
async function runSetupWizard(configRoot) {
|
|
74
78
|
if (!process.stdin.isTTY || !process.stdout.isTTY) {
|
|
75
79
|
throw new Error("The start wizard requires an interactive terminal.");
|
|
76
80
|
}
|
|
@@ -88,7 +92,7 @@ async function runSetupWizard(projectRoot) {
|
|
|
88
92
|
values[field.key] = await promptForValue(rl, field);
|
|
89
93
|
}
|
|
90
94
|
|
|
91
|
-
await persistEnvFile(
|
|
95
|
+
await persistEnvFile(configRoot, values);
|
|
92
96
|
process.stdout.write("\nSaved configuration to .env.\n");
|
|
93
97
|
} finally {
|
|
94
98
|
rl.close();
|
package/src/commands/status.js
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import process from "node:process";
|
|
2
2
|
|
|
3
|
+
import { ensureGlobalConfigDir } from "../config.js";
|
|
3
4
|
import { ensureRuntimeDir, isProcessRunning, readPid, readState, removePid } from "../runtime.js";
|
|
4
5
|
|
|
5
|
-
export async function runStatusCommand({ projectRoot }) {
|
|
6
|
-
const
|
|
6
|
+
export async function runStatusCommand({ projectRoot, args = [] }) {
|
|
7
|
+
const isLocal = args.includes("--local");
|
|
8
|
+
const configRoot = isLocal ? projectRoot : await ensureGlobalConfigDir();
|
|
9
|
+
const runtimePaths = await ensureRuntimeDir(configRoot);
|
|
7
10
|
const pid = await readPid(runtimePaths.pidFile);
|
|
8
11
|
const state = await readState(runtimePaths.stateFile);
|
|
9
12
|
|
package/src/commands/stop.js
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import process from "node:process";
|
|
2
2
|
|
|
3
|
+
import { ensureGlobalConfigDir } from "../config.js";
|
|
3
4
|
import { ensureRuntimeDir, isProcessRunning, readPid, removePid } from "../runtime.js";
|
|
4
5
|
|
|
5
|
-
export async function runStopCommand({ projectRoot }) {
|
|
6
|
-
const
|
|
6
|
+
export async function runStopCommand({ projectRoot, args = [] }) {
|
|
7
|
+
const isLocal = args.includes("--local");
|
|
8
|
+
const configRoot = isLocal ? projectRoot : await ensureGlobalConfigDir();
|
|
9
|
+
const runtimePaths = await ensureRuntimeDir(configRoot);
|
|
7
10
|
const pid = await readPid(runtimePaths.pidFile);
|
|
8
11
|
|
|
9
12
|
if (!pid) {
|
package/src/config.js
CHANGED
|
@@ -1,8 +1,20 @@
|
|
|
1
|
-
import { access, readFile, writeFile } from "node:fs/promises";
|
|
1
|
+
import { access, mkdir, readFile, writeFile } from "node:fs/promises";
|
|
2
2
|
import { constants as fsConstants } from "node:fs";
|
|
3
|
+
import os from "node:os";
|
|
3
4
|
import path from "node:path";
|
|
4
5
|
|
|
5
6
|
export const ENV_FILE_NAME = ".env";
|
|
7
|
+
|
|
8
|
+
export function getGlobalConfigDir() {
|
|
9
|
+
return path.join(os.homedir(), ".tm8");
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export async function ensureGlobalConfigDir() {
|
|
13
|
+
const dir = getGlobalConfigDir();
|
|
14
|
+
await mkdir(dir, { recursive: true });
|
|
15
|
+
return dir;
|
|
16
|
+
}
|
|
17
|
+
|
|
6
18
|
export const REQUIRED_FIELDS = [
|
|
7
19
|
{
|
|
8
20
|
key: "JIRA_BASE_URL",
|