@zentao-hub/cli 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 +21 -0
- package/README.md +175 -0
- package/README.zh-CN.md +192 -0
- package/dist/agents.d.ts +62 -0
- package/dist/agents.js +202 -0
- package/dist/agents.js.map +1 -0
- package/dist/commands/init.d.ts +9 -0
- package/dist/commands/init.js +94 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/install-claude-md.d.ts +12 -0
- package/dist/commands/install-claude-md.js +25 -0
- package/dist/commands/install-claude-md.js.map +1 -0
- package/dist/commands/install-commands.d.ts +8 -0
- package/dist/commands/install-commands.js +27 -0
- package/dist/commands/install-commands.js.map +1 -0
- package/dist/commands/install-hooks.d.ts +7 -0
- package/dist/commands/install-hooks.js +31 -0
- package/dist/commands/install-hooks.js.map +1 -0
- package/dist/commands/install-mcp.d.ts +20 -0
- package/dist/commands/install-mcp.js +203 -0
- package/dist/commands/install-mcp.js.map +1 -0
- package/dist/commands/login.d.ts +17 -0
- package/dist/commands/login.js +110 -0
- package/dist/commands/login.js.map +1 -0
- package/dist/commands/logout.d.ts +7 -0
- package/dist/commands/logout.js +59 -0
- package/dist/commands/logout.js.map +1 -0
- package/dist/commands/profiles.d.ts +5 -0
- package/dist/commands/profiles.js +26 -0
- package/dist/commands/profiles.js.map +1 -0
- package/dist/commands/register.d.ts +12 -0
- package/dist/commands/register.js +26 -0
- package/dist/commands/register.js.map +1 -0
- package/dist/commands/repos.d.ts +8 -0
- package/dist/commands/repos.js +28 -0
- package/dist/commands/repos.js.map +1 -0
- package/dist/commands/uninstall-claude-md.d.ts +6 -0
- package/dist/commands/uninstall-claude-md.js +13 -0
- package/dist/commands/uninstall-claude-md.js.map +1 -0
- package/dist/commands/uninstall-commands.d.ts +7 -0
- package/dist/commands/uninstall-commands.js +21 -0
- package/dist/commands/uninstall-commands.js.map +1 -0
- package/dist/commands/uninstall-hooks.d.ts +6 -0
- package/dist/commands/uninstall-hooks.js +36 -0
- package/dist/commands/uninstall-hooks.js.map +1 -0
- package/dist/commands/uninstall-mcp.d.ts +8 -0
- package/dist/commands/uninstall-mcp.js +99 -0
- package/dist/commands/uninstall-mcp.js.map +1 -0
- package/dist/commands/uninstall.d.ts +14 -0
- package/dist/commands/uninstall.js +28 -0
- package/dist/commands/uninstall.js.map +1 -0
- package/dist/commands/use.d.ts +6 -0
- package/dist/commands/use.js +20 -0
- package/dist/commands/use.js.map +1 -0
- package/dist/commands/whoami.d.ts +6 -0
- package/dist/commands/whoami.js +48 -0
- package/dist/commands/whoami.js.map +1 -0
- package/dist/commands/workspace.d.ts +8 -0
- package/dist/commands/workspace.js +28 -0
- package/dist/commands/workspace.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +496 -0
- package/dist/index.js.map +1 -0
- package/dist/paths.d.ts +16 -0
- package/dist/paths.js +36 -0
- package/dist/paths.js.map +1 -0
- package/dist/prompt.d.ts +7 -0
- package/dist/prompt.js +43 -0
- package/dist/prompt.js.map +1 -0
- package/dist/registry.d.ts +16 -0
- package/dist/registry.js +42 -0
- package/dist/registry.js.map +1 -0
- package/dist/util.d.ts +27 -0
- package/dist/util.js +70 -0
- package/dist/util.js.map +1 -0
- package/package.json +57 -0
- package/templates/.claude/commands/bug-analyze.md +46 -0
- package/templates/.claude/commands/fix-bug.md +193 -0
- package/templates/.githooks/commit-msg +47 -0
- package/templates/CLAUDE.md +86 -0
- package/templates/repos.yaml.example +26 -0
package/dist/registry.js
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import { existsSync } from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import yaml from "js-yaml";
|
|
5
|
+
import { resolveWorkspace } from "./paths.js";
|
|
6
|
+
export function registryPath(profile, workspace) {
|
|
7
|
+
return path.join(resolveWorkspace(profile, workspace), "repos.yaml");
|
|
8
|
+
}
|
|
9
|
+
export async function loadRegistry(profile, workspace) {
|
|
10
|
+
const file = registryPath(profile, workspace);
|
|
11
|
+
if (!existsSync(file))
|
|
12
|
+
return { products: {} };
|
|
13
|
+
const text = await fs.readFile(file, "utf8");
|
|
14
|
+
const parsed = (yaml.load(text) ?? {});
|
|
15
|
+
return { products: parsed.products ?? {} };
|
|
16
|
+
}
|
|
17
|
+
export async function saveRegistry(reg, profile, workspace) {
|
|
18
|
+
const file = registryPath(profile, workspace);
|
|
19
|
+
await fs.mkdir(path.dirname(file), { recursive: true });
|
|
20
|
+
const text = yaml.dump(reg, { sortKeys: false, lineWidth: 120 });
|
|
21
|
+
await fs.writeFile(file, text, "utf8");
|
|
22
|
+
}
|
|
23
|
+
export function listEntries(reg, productId) {
|
|
24
|
+
return reg.products[String(productId)] ?? [];
|
|
25
|
+
}
|
|
26
|
+
export function addEntry(reg, productId, entry) {
|
|
27
|
+
const key = String(productId);
|
|
28
|
+
const entries = reg.products[key] ?? [];
|
|
29
|
+
const normalized = {
|
|
30
|
+
path: path.resolve(entry.path),
|
|
31
|
+
...(entry.name ? { name: entry.name } : {}),
|
|
32
|
+
...(entry.tags && entry.tags.length ? { tags: entry.tags } : {}),
|
|
33
|
+
};
|
|
34
|
+
if (entries.some((e) => path.resolve(e.path) === normalized.path)) {
|
|
35
|
+
reg.products[key] = entries;
|
|
36
|
+
return { added: false, entries };
|
|
37
|
+
}
|
|
38
|
+
const next = [...entries, normalized];
|
|
39
|
+
reg.products[key] = next;
|
|
40
|
+
return { added: true, entries: next };
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=registry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"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"}
|
package/dist/util.d.ts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export declare function copyFile(src: string, dest: string, opts?: {
|
|
2
|
+
overwrite?: boolean;
|
|
3
|
+
}): Promise<"copied" | "skipped">;
|
|
4
|
+
/**
|
|
5
|
+
* Delete a file if it exists. Returns "removed" / "missing" so callers can
|
|
6
|
+
* print symmetric output to install-style commands.
|
|
7
|
+
*/
|
|
8
|
+
export declare function removeFile(filePath: string): Promise<"removed" | "missing">;
|
|
9
|
+
/**
|
|
10
|
+
* Remove a directory if it exists and is empty. Used by uninstall-* commands
|
|
11
|
+
* to tidy up empty `.githooks/` once the last hook is gone, without ever
|
|
12
|
+
* touching directories that still hold user files.
|
|
13
|
+
*/
|
|
14
|
+
export declare function removeIfEmpty(dirPath: string): Promise<boolean>;
|
|
15
|
+
export declare function ensureExecutable(filePath: string): Promise<void>;
|
|
16
|
+
export declare function isGitRepo(repoPath: string): boolean;
|
|
17
|
+
export declare function git(repoPath: string, args: string[]): {
|
|
18
|
+
status: number;
|
|
19
|
+
stdout: string;
|
|
20
|
+
stderr: string;
|
|
21
|
+
};
|
|
22
|
+
export declare class CliError extends Error {
|
|
23
|
+
readonly exitCode: number;
|
|
24
|
+
constructor(message: string, exitCode?: number);
|
|
25
|
+
}
|
|
26
|
+
export declare function info(msg: string): void;
|
|
27
|
+
export declare function warn(msg: string): void;
|
package/dist/util.js
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import { existsSync } from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { spawnSync } from "node:child_process";
|
|
5
|
+
export async function copyFile(src, dest, opts = {}) {
|
|
6
|
+
await fs.mkdir(path.dirname(dest), { recursive: true });
|
|
7
|
+
if (existsSync(dest) && !opts.overwrite)
|
|
8
|
+
return "skipped";
|
|
9
|
+
await fs.copyFile(src, dest);
|
|
10
|
+
return "copied";
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Delete a file if it exists. Returns "removed" / "missing" so callers can
|
|
14
|
+
* print symmetric output to install-style commands.
|
|
15
|
+
*/
|
|
16
|
+
export async function removeFile(filePath) {
|
|
17
|
+
if (!existsSync(filePath))
|
|
18
|
+
return "missing";
|
|
19
|
+
await fs.unlink(filePath);
|
|
20
|
+
return "removed";
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Remove a directory if it exists and is empty. Used by uninstall-* commands
|
|
24
|
+
* to tidy up empty `.githooks/` once the last hook is gone, without ever
|
|
25
|
+
* touching directories that still hold user files.
|
|
26
|
+
*/
|
|
27
|
+
export async function removeIfEmpty(dirPath) {
|
|
28
|
+
if (!existsSync(dirPath))
|
|
29
|
+
return false;
|
|
30
|
+
try {
|
|
31
|
+
await fs.rmdir(dirPath);
|
|
32
|
+
return true;
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
export async function ensureExecutable(filePath) {
|
|
39
|
+
try {
|
|
40
|
+
await fs.chmod(filePath, 0o755);
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
// best-effort; on Windows chmod is a no-op
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
export function isGitRepo(repoPath) {
|
|
47
|
+
const r = spawnSync("git", ["-C", repoPath, "rev-parse", "--git-dir"], {
|
|
48
|
+
encoding: "utf8",
|
|
49
|
+
});
|
|
50
|
+
return r.status === 0;
|
|
51
|
+
}
|
|
52
|
+
export function git(repoPath, args) {
|
|
53
|
+
const r = spawnSync("git", ["-C", repoPath, ...args], { encoding: "utf8" });
|
|
54
|
+
return { status: r.status ?? -1, stdout: r.stdout ?? "", stderr: r.stderr ?? "" };
|
|
55
|
+
}
|
|
56
|
+
export class CliError extends Error {
|
|
57
|
+
exitCode;
|
|
58
|
+
constructor(message, exitCode = 1) {
|
|
59
|
+
super(message);
|
|
60
|
+
this.exitCode = exitCode;
|
|
61
|
+
this.name = "CliError";
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
export function info(msg) {
|
|
65
|
+
process.stdout.write(`${msg}\n`);
|
|
66
|
+
}
|
|
67
|
+
export function warn(msg) {
|
|
68
|
+
process.stderr.write(`[warn] ${msg}\n`);
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=util.js.map
|
package/dist/util.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"util.js","sourceRoot":"","sources":["../src/util.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@zentao-hub/cli",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Helper CLI for @zentao-hub/mcp — registers the MCP server, installs slash commands / prompts (Claude Code, GitHub Copilot, Codex CLI), installs a commit-msg hook, and manages the bug hub + repos.yaml registry.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "Deven Liu <iliudonghui@gmail.com>",
|
|
7
|
+
"keywords": [
|
|
8
|
+
"zentao",
|
|
9
|
+
"禅道",
|
|
10
|
+
"mcp",
|
|
11
|
+
"claude",
|
|
12
|
+
"claude-code",
|
|
13
|
+
"github-copilot",
|
|
14
|
+
"copilot-cli",
|
|
15
|
+
"codex",
|
|
16
|
+
"cli",
|
|
17
|
+
"slash-command",
|
|
18
|
+
"commit-msg-hook"
|
|
19
|
+
],
|
|
20
|
+
"homepage": "https://github.com/devenliu/zentao-hub/tree/main/packages/cli#readme",
|
|
21
|
+
"repository": {
|
|
22
|
+
"type": "git",
|
|
23
|
+
"url": "git+https://github.com/devenliu/zentao-hub.git",
|
|
24
|
+
"directory": "packages/cli"
|
|
25
|
+
},
|
|
26
|
+
"bugs": {
|
|
27
|
+
"url": "https://github.com/devenliu/zentao-hub/issues"
|
|
28
|
+
},
|
|
29
|
+
"type": "module",
|
|
30
|
+
"bin": {
|
|
31
|
+
"zentao-hub": "dist/index.js"
|
|
32
|
+
},
|
|
33
|
+
"files": [
|
|
34
|
+
"dist",
|
|
35
|
+
"templates",
|
|
36
|
+
"README.md"
|
|
37
|
+
],
|
|
38
|
+
"engines": {
|
|
39
|
+
"node": ">=18"
|
|
40
|
+
},
|
|
41
|
+
"publishConfig": {
|
|
42
|
+
"access": "public"
|
|
43
|
+
},
|
|
44
|
+
"dependencies": {
|
|
45
|
+
"js-yaml": "^4.1.0",
|
|
46
|
+
"@zentao-hub/sdk": "0.1.0"
|
|
47
|
+
},
|
|
48
|
+
"devDependencies": {
|
|
49
|
+
"@types/js-yaml": "^4.0.9",
|
|
50
|
+
"@types/node": "^20.14.0"
|
|
51
|
+
},
|
|
52
|
+
"scripts": {
|
|
53
|
+
"build": "tsc -b",
|
|
54
|
+
"dev": "tsc -b --watch",
|
|
55
|
+
"clean": "tsc -b --clean && rm -rf dist tsconfig.tsbuildinfo"
|
|
56
|
+
}
|
|
57
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Pull and analyze the context of a single ZenTao bug (no worktree, no changes to any repo). Useful for sizing / scheduling / reading screenshots.
|
|
3
|
+
argument-hint: <bugId>
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# ZenTao bug analysis (read-only)
|
|
7
|
+
|
|
8
|
+
Runs only Phase 1 of /fix-bug.
|
|
9
|
+
|
|
10
|
+
## Variables
|
|
11
|
+
|
|
12
|
+
- `$WS` = `zentao-hub workspace ${ZENTAO_PROFILE:+--profile "$ZENTAO_PROFILE"} --print-path` — the CLI handles the per-profile layout. **Only pass `--profile` when `$ZENTAO_PROFILE` is set**; when it's unset we deliberately omit the flag so the CLI falls back to the `"default"` field of `~/.zentao-hub/credentials.json` (otherwise the literal string `default` would override whatever profile the user configured as default).
|
|
13
|
+
Fallback when `zentao-hub` isn't on `$PATH`: `${ZENTAO_HUB:-$HOME/.zentao-hub}/<profile>`, where `<profile>` is `$ZENTAO_PROFILE` if set, else the `"default"` field of `credentials.json` (`python3 -c 'import json,os; print(json.load(open(os.path.expanduser("~/.zentao-hub/credentials.json")))["default"])' 2>/dev/null`), else literal `default`.
|
|
14
|
+
- `$BUG_ID` = `$1` (required)
|
|
15
|
+
- `$BUG_DIR` = `$WS/bugs/$BUG_ID`
|
|
16
|
+
|
|
17
|
+
## Steps
|
|
18
|
+
|
|
19
|
+
### 1. Prep
|
|
20
|
+
- `mkdir -p $WS/bugs`
|
|
21
|
+
|
|
22
|
+
### 2. Pull context into `$BUG_DIR`
|
|
23
|
+
- `mkdir -p $BUG_DIR/attachments`
|
|
24
|
+
- `mcp__zentao__get_bug(bugId=$BUG_ID)` → write `$BUG_DIR/raw.json`, and produce a `description.md` summary (title / priority / severity / reporter / assignedTo / reproduction steps). For HTML→markdown rules, see [section 1.3 in fix-bug.md](fix-bug.md#bugsteps-html--markdown-conversion-rules).
|
|
25
|
+
- `mcp__zentao__get_bug_history(bugId=$BUG_ID)` → write `history.md`
|
|
26
|
+
- `mcp__zentao__download_bug_attachments(bugId=$BUG_ID, destDir="$BUG_DIR/attachments", includeInline=true)`. If `errors[]` is non-empty, tell the user.
|
|
27
|
+
|
|
28
|
+
### 3. Analyze
|
|
29
|
+
Read every local file (use Read directly on screenshots as images). Produce:
|
|
30
|
+
|
|
31
|
+
- **Symptom**: what the user sees / repro conditions / blast radius
|
|
32
|
+
- **Likely root cause** (at the business-logic level)
|
|
33
|
+
- **Code leads**: keywords / file names / function names that future-you should grep for when actually fixing
|
|
34
|
+
- **Unknowns / things to clarify**: list them explicitly
|
|
35
|
+
- **Difficulty estimate**: a rough S / M / L plus a guess at which repos / modules will need to change
|
|
36
|
+
|
|
37
|
+
Write the analysis to `$BUG_DIR/analysis.md` and print a summary to the user.
|
|
38
|
+
|
|
39
|
+
### 4. Wrap up
|
|
40
|
+
- No worktree creation, no repository changes
|
|
41
|
+
- Do NOT call `mcp__zentao__resolve_bug`
|
|
42
|
+
- Keep `$BUG_DIR` around — when you later run `/fix-bug $productId $BUG_ID` it will be reused (avoids re-downloading)
|
|
43
|
+
|
|
44
|
+
## When to upgrade to /fix-bug
|
|
45
|
+
|
|
46
|
+
If you decide to fix it: run `/fix-bug <productId> $BUG_ID`. Phase 1 detects that `$BUG_DIR` already exists, skips the download, and continues from the analysis step.
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Pull a ZenTao bug, analyze in the workspace, confirm with the user, pick repositories, then open a worktree per repo to fix and squash-merge.
|
|
3
|
+
argument-hint: [productId] [bugId?]
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# ZenTao bug-fix flow (workspace + multi-repo + worktree)
|
|
7
|
+
|
|
8
|
+
Run in the three phases below. At every ⏸ point **stop and wait for user confirmation**. Announce every git / file write command before running it.
|
|
9
|
+
|
|
10
|
+
## Variables
|
|
11
|
+
|
|
12
|
+
Resolve before running:
|
|
13
|
+
|
|
14
|
+
- `$WS` = `zentao-hub workspace ${ZENTAO_PROFILE:+--profile "$ZENTAO_PROFILE"} --print-path` — the CLI handles the per-profile layout. **Only pass `--profile` when `$ZENTAO_PROFILE` is set** (check with `printenv ZENTAO_PROFILE`); when it's unset we deliberately omit the flag so the CLI falls back to the `"default"` field of `~/.zentao-hub/credentials.json` (otherwise the literal string `default` would override whatever profile the user configured as default).
|
|
15
|
+
Fallback when `zentao-hub` isn't on `$PATH`: `${ZENTAO_HUB:-$HOME/.zentao-hub}/<profile>`, where `<profile>` is `$ZENTAO_PROFILE` if set, else the `"default"` field of `credentials.json` (`python3 -c 'import json,os; print(json.load(open(os.path.expanduser("~/.zentao-hub/credentials.json")))["default"])' 2>/dev/null`), else literal `default`.
|
|
16
|
+
- `$BUG_ID` = chosen bug id
|
|
17
|
+
- `$BUG_DIR` = `$WS/bugs/$BUG_ID`
|
|
18
|
+
- `$REGISTRY` = `$WS/repos.yaml`
|
|
19
|
+
- For each chosen repository:
|
|
20
|
+
- `$REPO` = repository root (absolute path)
|
|
21
|
+
- `$WORKTREE` = `$(dirname $REPO)/$(basename $REPO)-bug-$BUG_ID`
|
|
22
|
+
- `$BRANCH` = `fix/bug-$BUG_ID`
|
|
23
|
+
- `$MAIN` = main branch of that repo (detect via `git -C $REPO symbolic-ref --short refs/remotes/origin/HEAD`; fall back to `main` / `master` / `trunk`)
|
|
24
|
+
|
|
25
|
+
Always pass `-C <repo|worktree>` to git so the non-persistent Bash cwd never causes confusion.
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## Phase 1 — Bug context (does not depend on any repository)
|
|
30
|
+
|
|
31
|
+
### 1.1 Prepare the workspace
|
|
32
|
+
- `mkdir -p $WS/bugs`
|
|
33
|
+
- Make sure `$WS` exists; create if missing.
|
|
34
|
+
|
|
35
|
+
### 1.2 Pick a bug
|
|
36
|
+
- If `$2` is not provided: `mcp__zentao__list_my_bugs(productId=$1, status=active, limit=20)`, display the top 10 sorted by `pri` ascending, and let the user pick.
|
|
37
|
+
- If `$2` is provided: `$BUG_ID = $2`.
|
|
38
|
+
|
|
39
|
+
### 1.3 Pull context into `$BUG_DIR` (idempotent)
|
|
40
|
+
|
|
41
|
+
If `$BUG_DIR/raw.json` already exists (e.g. a previous `/bug-analyze` ran), tell the user and ask ⏸: reuse, or re-download? Default: reuse and skip to 1.4.
|
|
42
|
+
|
|
43
|
+
Otherwise:
|
|
44
|
+
- `mkdir -p $BUG_DIR/attachments`
|
|
45
|
+
- `mcp__zentao__get_bug(bugId=$BUG_ID)` → write `$BUG_DIR/raw.json`; also produce `$BUG_DIR/description.md` containing: title, priority, severity, reporter, assignedTo, plus a "Reproduction steps" section converted from `bug.steps` HTML using the rules below.
|
|
46
|
+
- `mcp__zentao__get_bug_history(bugId=$BUG_ID)` → write `$BUG_DIR/history.md` (comments and status changes ordered by time).
|
|
47
|
+
- `mcp__zentao__download_bug_attachments(bugId=$BUG_ID, destDir="$BUG_DIR/attachments", includeInline=true)`. Report any failed files honestly to the user.
|
|
48
|
+
|
|
49
|
+
#### `bug.steps` HTML → markdown conversion rules
|
|
50
|
+
|
|
51
|
+
Apply these rules so the result is reliably readable:
|
|
52
|
+
|
|
53
|
+
- `<img src=".../file-read-{id}.{ext}" ...>` → `` (path aligned with how download_bug_attachments persists files; N follows in-text order)
|
|
54
|
+
- `<br>` / `<br/>` → newline `\n`
|
|
55
|
+
- `</p>`, `</div>`, `</li>` → newline `\n`
|
|
56
|
+
- `<li ...>` → start-of-line `- ` (unordered list item)
|
|
57
|
+
- `<li>` inside `<ol>` → start-of-line `1. `, etc. (ordered list item)
|
|
58
|
+
- All other HTML tags → strip
|
|
59
|
+
- HTML entities → decoded: ` ` → space, `&` → `&`, `<` → `<`, `>` → `>`, `"` → `"`
|
|
60
|
+
- 3+ consecutive newlines → collapse to 2
|
|
61
|
+
- Trim leading / trailing whitespace
|
|
62
|
+
|
|
63
|
+
After this, the result should read as clean markdown with no residual tags.
|
|
64
|
+
|
|
65
|
+
### 1.4 Analyze ⏸
|
|
66
|
+
Read `description.md` + `history.md` + every attachment (Read screenshots directly as images). **Note: at this point you may not know yet which repository to change.** Focus the analysis on the bug itself:
|
|
67
|
+
|
|
68
|
+
- **Symptom**: what the user sees / repro conditions / blast radius
|
|
69
|
+
- **Likely root cause** (code-agnostic): business-logic step, data / config / API
|
|
70
|
+
- **Code leads**: keywords / function names / file-name hints, to help match repositories in Phase 2
|
|
71
|
+
- **Unknowns**: list them explicitly
|
|
72
|
+
|
|
73
|
+
Write the analysis to `$BUG_DIR/analysis.md`.
|
|
74
|
+
|
|
75
|
+
⏸ **Ask the user**: does the analysis look right? Anything to add?
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
79
|
+
## Phase 2 — Pick repositories
|
|
80
|
+
|
|
81
|
+
### 2.1 Look up the registry
|
|
82
|
+
- If `$REGISTRY` doesn't exist: either `cp $WS/repos.yaml.example $REGISTRY`, or create a minimal skeleton `{products: {}}` (the user may not have copied the example — be tolerant).
|
|
83
|
+
- Parse the yaml with `python3 -c` (no extra deps):
|
|
84
|
+
```bash
|
|
85
|
+
python3 -c "
|
|
86
|
+
import yaml, sys
|
|
87
|
+
with open('$REGISTRY') as f: data = yaml.safe_load(f) or {}
|
|
88
|
+
entries = (data.get('products') or {}).get($1) or []
|
|
89
|
+
for e in entries: print(e.get('name', ''), e.get('path', ''), ','.join(e.get('tags', [])))
|
|
90
|
+
"
|
|
91
|
+
```
|
|
92
|
+
- List the candidate repositories for this productId (name / path / tags).
|
|
93
|
+
|
|
94
|
+
### 2.2 Let the user pick ⏸
|
|
95
|
+
- If the registry has candidates → list them and let the user pick (**multi-select** — cross-repo bugs are common, e.g. frontend + backend).
|
|
96
|
+
- No candidates / wants to add a new one → prompt for an absolute path.
|
|
97
|
+
- For each selected path:
|
|
98
|
+
- Verify it's a git repo: `git -C <path> rev-parse --git-dir`
|
|
99
|
+
- Verify the working tree is clean: `git -C <path> status --porcelain` should be empty
|
|
100
|
+
- If either fails, stop and let the user clean up.
|
|
101
|
+
- Write any newly-entered repos back to `$REGISTRY`:
|
|
102
|
+
```bash
|
|
103
|
+
python3 << 'PYEOF'
|
|
104
|
+
import yaml
|
|
105
|
+
with open('$REGISTRY') as f: data = yaml.safe_load(f) or {}
|
|
106
|
+
data.setdefault('products', {}).setdefault($1, [])
|
|
107
|
+
# Append entries, dedup by path
|
|
108
|
+
existing = {e['path'] for e in data['products'][$1]}
|
|
109
|
+
for new_path in [...]: # user-supplied
|
|
110
|
+
if new_path not in existing:
|
|
111
|
+
data['products'][$1].append({'path': new_path})
|
|
112
|
+
with open('$REGISTRY', 'w') as f: yaml.safe_dump(data, f, allow_unicode=True, sort_keys=False)
|
|
113
|
+
PYEOF
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
⏸ **Confirm the repository list** before moving to Phase 3.
|
|
117
|
+
|
|
118
|
+
---
|
|
119
|
+
|
|
120
|
+
## Phase 3 — Worktree + fix (loop per repository)
|
|
121
|
+
|
|
122
|
+
For **each** chosen `$REPO`:
|
|
123
|
+
|
|
124
|
+
### 3.1 Create the worktree
|
|
125
|
+
- `git -C $REPO fetch origin` (skip if there is no origin)
|
|
126
|
+
- Detect `$MAIN` and tell the user. If `$MAIN` is behind origin, suggest fast-forwarding first.
|
|
127
|
+
- Check whether `$BRANCH` already exists; if so, stop and ask: reuse, delete and recreate, or rename.
|
|
128
|
+
- `git -C $REPO worktree add -b $BRANCH $WORKTREE $MAIN`
|
|
129
|
+
|
|
130
|
+
### 3.2 Fix plan ⏸
|
|
131
|
+
Based on `$BUG_DIR/analysis.md` + the code in this repo (use Grep / Read), produce a plan specific to **this repository**:
|
|
132
|
+
|
|
133
|
+
- File list to change + the approach
|
|
134
|
+
- Blast radius / test coverage / risk
|
|
135
|
+
- Cross-repo dependencies (e.g. frontend depends on a backend release first)
|
|
136
|
+
|
|
137
|
+
⏸ **Ask the user**: does the plan look right?
|
|
138
|
+
|
|
139
|
+
### 3.3 Apply changes (cwd = `$WORKTREE`)
|
|
140
|
+
- Edit / Write the changes
|
|
141
|
+
- Run tests and type checks (use the project's own commands)
|
|
142
|
+
- After tests pass: `git -C $WORKTREE add -A && git -C $WORKTREE commit -m "<wip msg>"`. Multiple commits are fine.
|
|
143
|
+
|
|
144
|
+
### 3.4 Squash merge ⏸
|
|
145
|
+
- `git -C $REPO checkout $MAIN`
|
|
146
|
+
- `git -C $REPO merge --squash $BRANCH`
|
|
147
|
+
- On conflict, stop and let the user resolve manually; **do not auto-reset**.
|
|
148
|
+
- Draft a commit message, **show it to the user, and only after confirmation submit via heredoc**:
|
|
149
|
+
|
|
150
|
+
```
|
|
151
|
+
fix: #$BUG_ID <original bug title>
|
|
152
|
+
|
|
153
|
+
<one-line root cause>
|
|
154
|
+
<one-line fix>
|
|
155
|
+
|
|
156
|
+
Closes ZenTao bug $BUG_ID
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
- After committing, record the commit hash and repo name for wrap-up.
|
|
160
|
+
|
|
161
|
+
---
|
|
162
|
+
|
|
163
|
+
## Phase 4 — Wrap-up suggestions (**suggest only, do not auto-run**)
|
|
164
|
+
|
|
165
|
+
Summarize the commit hash for every repository:
|
|
166
|
+
|
|
167
|
+
```
|
|
168
|
+
- venus-frontend: abc1234
|
|
169
|
+
- venus-backend: def5678
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
Based on user preference, optionally:
|
|
173
|
+
|
|
174
|
+
- Push each repo: `git -C $REPO push origin $MAIN`
|
|
175
|
+
- Mark the bug resolved in ZenTao:
|
|
176
|
+
```
|
|
177
|
+
mcp__zentao__resolve_bug(
|
|
178
|
+
bugId=$BUG_ID,
|
|
179
|
+
resolution="fixed",
|
|
180
|
+
comment="Multi-repo fix:\n- venus-frontend: abc1234\n- venus-backend: def5678",
|
|
181
|
+
resolvedBuild="...",
|
|
182
|
+
)
|
|
183
|
+
```
|
|
184
|
+
- Remove each worktree: `git -C $REPO worktree remove $WORKTREE`
|
|
185
|
+
- Delete the fix branch: `git -C $REPO branch -D $BRANCH`
|
|
186
|
+
- Leave the bug context in `$BUG_DIR` (kept by default for future traceability); remove with `rm -rf $BUG_DIR` if desired.
|
|
187
|
+
|
|
188
|
+
## Failure / cancel paths
|
|
189
|
+
|
|
190
|
+
- Phase 1/2, user decides not to fix: optionally clean up an empty `$BUG_DIR`; touch no repositories.
|
|
191
|
+
- Phase 3, tests cannot be made to pass in one repo: stop at that worktree, hand it back to the user; do not roll back the other repos already completed.
|
|
192
|
+
- pre-commit hook fails: fix the underlying problem per the hook's message, then commit again; **do not** `--amend`, do not `--no-verify`.
|
|
193
|
+
- squash merge conflict: stop, ask the user to resolve in the main checkout manually; once they say "continue", proceed with the commit step.
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Block AI signatures and validate the Conventional Commits header.
|
|
3
|
+
# Enable with: git config core.hooksPath .githooks
|
|
4
|
+
# (zentao install-hooks does this automatically)
|
|
5
|
+
|
|
6
|
+
msg_file="$1"
|
|
7
|
+
msg=$(cat "$msg_file")
|
|
8
|
+
|
|
9
|
+
# 1) Block AI signatures / auto-generated markers
|
|
10
|
+
if echo "$msg" | grep -qiE "(generated with claude|co-authored-by:[[:space:]]*claude|co-authored-by:[[:space:]]*.*noreply@anthropic|🤖|generated by ai|claude assisted|ai assisted)"; then
|
|
11
|
+
echo "ERROR: commit message contains an AI signature, blocked."
|
|
12
|
+
echo
|
|
13
|
+
echo "Matched fragment:"
|
|
14
|
+
echo "$msg" | grep -iE "(generated with claude|co-authored-by|🤖|AI|claude)" | head -3
|
|
15
|
+
echo
|
|
16
|
+
echo "Remove it and re-commit."
|
|
17
|
+
exit 1
|
|
18
|
+
fi
|
|
19
|
+
|
|
20
|
+
# 2) Conventional Commits header: <type>(<scope>)?: <subject>
|
|
21
|
+
first_line=$(echo "$msg" | head -n 1)
|
|
22
|
+
|
|
23
|
+
# Let Merge / Revert pass through
|
|
24
|
+
if echo "$first_line" | grep -qE "^(Merge|Revert)"; then
|
|
25
|
+
exit 0
|
|
26
|
+
fi
|
|
27
|
+
|
|
28
|
+
if ! echo "$first_line" | grep -qE "^(feat|fix|docs|style|refactor|test|chore|perf|build|ci)(\([a-z0-9_-]+\))?!?: .+"; then
|
|
29
|
+
echo "ERROR: commit header does not match Conventional Commits."
|
|
30
|
+
echo
|
|
31
|
+
echo "Current: $first_line"
|
|
32
|
+
echo
|
|
33
|
+
echo "Expected: <type>(<scope>): <subject>"
|
|
34
|
+
echo "Allowed types: feat, fix, docs, style, refactor, test, chore, perf, build, ci"
|
|
35
|
+
echo "Example: fix(auth): handle expired login token"
|
|
36
|
+
exit 1
|
|
37
|
+
fi
|
|
38
|
+
|
|
39
|
+
# 3) Optional: require every commit to carry a ZenTao bug reference [ZT#<id>].
|
|
40
|
+
# Disabled by default; uncomment to enforce.
|
|
41
|
+
# if ! echo "$msg" | grep -qE "\[ZT#[0-9]+\]"; then
|
|
42
|
+
# echo "ERROR: commit message is missing the ZenTao bug reference [ZT#<id>]"
|
|
43
|
+
# echo " Suggested placement: end of subject or in the body, e.g. fix(auth): handle expired login token [ZT#12345]"
|
|
44
|
+
# exit 1
|
|
45
|
+
# fi
|
|
46
|
+
|
|
47
|
+
exit 0
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# Project AI collaboration rules
|
|
2
|
+
|
|
3
|
+
> Copied here by `zentao-hub install claude-md`, sitting next to `package.json` / `pyproject.toml` / etc.
|
|
4
|
+
> Claude Code reads this file on startup as the collaboration contract for this repository.
|
|
5
|
+
> Before relying on it, please fill in the "Code style" section with the conventions specific to this repo.
|
|
6
|
+
|
|
7
|
+
## Bug-fixing workflow
|
|
8
|
+
|
|
9
|
+
This project fixes ZenTao bugs through the slash commands from [zentao-hub](https://github.com/devenliu/zentao-hub) (installed globally, or via a local path). **Do not commit / push manually on the main branch.**
|
|
10
|
+
|
|
11
|
+
Entry points:
|
|
12
|
+
|
|
13
|
+
- `/bug-analyze <bugId>` — analyze only, no fix. Bug context lands in `<workspace>/bugs/<id>/`, where the workspace is the per-profile dir under `$ZENTAO_HUB` (or `~/.zentao-hub`).
|
|
14
|
+
- `/fix-bug <productId> [bugId]` — full flow. **Automatically creates a worktree**, edits inside the worktree, then squash-merges back to the main branch.
|
|
15
|
+
|
|
16
|
+
See the [@zentao-hub/cli README](https://github.com/devenliu/zentao-hub/blob/main/packages/cli/README.md) for the full design.
|
|
17
|
+
|
|
18
|
+
## Commit conventions (enforced by `.githooks/commit-msg`)
|
|
19
|
+
|
|
20
|
+
Follow [Conventional Commits](https://www.conventionalcommits.org/):
|
|
21
|
+
|
|
22
|
+
```
|
|
23
|
+
<type>(<scope>): <subject>
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
- **type**: `feat` / `fix` / `docs` / `style` / `refactor` / `test` / `chore` / `perf` / `build` / `ci`
|
|
27
|
+
- **scope**: module name (optional)
|
|
28
|
+
- **subject**: imperative mood, no trailing period, kept short
|
|
29
|
+
|
|
30
|
+
When fixing a bug, append a ZenTao reference in the body so it stays traceable:
|
|
31
|
+
|
|
32
|
+
```
|
|
33
|
+
fix(auth): handle expired login token
|
|
34
|
+
|
|
35
|
+
Root cause: the interceptor did not handle 401.
|
|
36
|
+
Fix: add refresh + retry in the response interceptor.
|
|
37
|
+
|
|
38
|
+
Closes ZenTao bug 12345
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
> If the team wants every commit to carry a `[ZT#<id>]` marker, uncomment section 3 of `.githooks/commit-msg`.
|
|
42
|
+
|
|
43
|
+
## Hard prohibitions (the hook will block these)
|
|
44
|
+
|
|
45
|
+
- Do NOT add AI signatures to commit messages — `Co-Authored-By: Claude`, `🤖 Generated with`, or any equivalent.
|
|
46
|
+
- Do NOT append phrases like "Generated by AI", "Claude-assisted", "AI-assisted".
|
|
47
|
+
- Do NOT use `--no-verify` to bypass the hook (unless explicitly authorized with a clear reason).
|
|
48
|
+
- Do NOT use `--amend` on commits that have already been pushed.
|
|
49
|
+
|
|
50
|
+
## Test and commit cadence
|
|
51
|
+
|
|
52
|
+
- Code changes must pass this repository's standard test / type-check commands (see "Test commands" below) before commit.
|
|
53
|
+
- One commit, one clear goal — split into multiple commits if multiple goals are in play.
|
|
54
|
+
- If a pre-commit hook fails: **fix the underlying problem and re-commit**, do not use `--no-verify` and do not `--amend`.
|
|
55
|
+
|
|
56
|
+
## Test commands
|
|
57
|
+
|
|
58
|
+
> Fill in based on this repository. Examples:
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
# Node.js
|
|
62
|
+
npm test
|
|
63
|
+
npm run typecheck
|
|
64
|
+
|
|
65
|
+
# Python
|
|
66
|
+
pytest
|
|
67
|
+
mypy .
|
|
68
|
+
|
|
69
|
+
# Go
|
|
70
|
+
go test ./...
|
|
71
|
+
|
|
72
|
+
# Rust
|
|
73
|
+
cargo test
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Code style
|
|
77
|
+
|
|
78
|
+
> Fill in based on this repository. Typical contents:
|
|
79
|
+
>
|
|
80
|
+
> - Location and conventions for ESLint / Prettier (or equivalent)
|
|
81
|
+
> - Naming conventions (camelCase / snake_case / package structure)
|
|
82
|
+
> - Test requirements (coverage thresholds, required test types)
|
|
83
|
+
> - Module boundaries and dependency-injection conventions
|
|
84
|
+
> - i18n and error-handling style
|
|
85
|
+
|
|
86
|
+
(Delete the `>` quote block and replace with the real rules for this repo.)
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# zentao-hub repository registry — example
|
|
2
|
+
#
|
|
3
|
+
# Real file path: <HUB>/repos.yaml (gitignored).
|
|
4
|
+
# Auto-created on the first /fix-bug run if missing, then appended as the user adds entries.
|
|
5
|
+
# You can also edit it by hand — the structure is intentionally simple.
|
|
6
|
+
#
|
|
7
|
+
# Top level is productId (numeric) → list of repositories backing that product.
|
|
8
|
+
# Per-entry fields:
|
|
9
|
+
# path (required) — absolute path to the repository root
|
|
10
|
+
# name (optional) — friendly name for the list UI; defaults to basename(path)
|
|
11
|
+
# tags (optional) — array of tags for matching bug content (frontend / backend / db / ...)
|
|
12
|
+
|
|
13
|
+
products:
|
|
14
|
+
# Example: Venus2.0 (productId comes from list_products)
|
|
15
|
+
2:
|
|
16
|
+
- name: venus-frontend
|
|
17
|
+
path: /Users/you/projects/venus-fe
|
|
18
|
+
tags: [frontend, ui, electron]
|
|
19
|
+
- name: venus-backend
|
|
20
|
+
path: /Users/you/projects/venus-be
|
|
21
|
+
tags: [backend, api, python]
|
|
22
|
+
|
|
23
|
+
# Example: Guochuang
|
|
24
|
+
20:
|
|
25
|
+
- path: /Users/you/projects/guochuang
|
|
26
|
+
tags: [fullstack]
|