sumulige-claude 1.0.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/.claude/AGENTS.md +42 -0
- package/.claude/README.md +142 -0
- package/.claude/rag/skill-index.json +15 -0
- package/.claude/settings.local.json +36 -0
- package/.claude/skills/api-tester/SKILL.md +61 -0
- package/.claude/skills/api-tester/examples/basic.md +3 -0
- package/.claude/skills/api-tester/metadata.yaml +30 -0
- package/.claude/skills/api-tester/templates/default.md +3 -0
- package/AGENTS.md +33 -0
- package/README.md +857 -0
- package/cli.js +814 -0
- package/development/todos/INDEX.md +114 -0
- package/development/todos/active/_README.md +49 -0
- package/development/todos/active/todo-system.md +37 -0
- package/development/todos/archived/_README.md +11 -0
- package/development/todos/backlog/_README.md +11 -0
- package/development/todos/backlog/mcp-integration.md +35 -0
- package/development/todos/completed/_README.md +11 -0
- package/development/todos/completed/boris-optimizations.md +39 -0
- package/package.json +35 -0
- package/template/.claude/CLAUDE-template.md +138 -0
- package/template/.claude/README.md +142 -0
- package/template/.claude/boris-optimizations.md +167 -0
- package/template/.claude/commands/commit-push-pr.md +59 -0
- package/template/.claude/commands/commit.md +53 -0
- package/template/.claude/commands/pr.md +76 -0
- package/template/.claude/commands/review.md +61 -0
- package/template/.claude/commands/sessions.md +62 -0
- package/template/.claude/commands/skill-create.md +131 -0
- package/template/.claude/commands/test.md +56 -0
- package/template/.claude/commands/todos.md +99 -0
- package/template/.claude/commands/verify-work.md +63 -0
- package/template/.claude/hooks/code-formatter.cjs +187 -0
- package/template/.claude/hooks/multi-session.cjs +181 -0
- package/template/.claude/hooks/project-kickoff.cjs +114 -0
- package/template/.claude/hooks/rag-skill-loader.cjs +159 -0
- package/template/.claude/hooks/session-end.sh +61 -0
- package/template/.claude/hooks/sync-to-log.sh +83 -0
- package/template/.claude/hooks/thinking-silent.cjs +145 -0
- package/template/.claude/hooks/tl-summary.sh +54 -0
- package/template/.claude/hooks/todo-manager.cjs +248 -0
- package/template/.claude/hooks/verify-work.cjs +134 -0
- package/template/.claude/rag/skill-index.json +135 -0
- package/template/.claude/settings.json +33 -0
- package/template/.claude/skills/SKILLS.md +145 -0
- package/template/.claude/skills/examples/README.md +47 -0
- package/template/.claude/skills/examples/basic-task.md +67 -0
- package/template/.claude/skills/examples/bug-fix-workflow.md +92 -0
- package/template/.claude/skills/examples/feature-development.md +81 -0
- package/template/.claude/skills/manus-kickoff/SKILL.md +128 -0
- package/template/.claude/skills/manus-kickoff/examples/basic.md +84 -0
- package/template/.claude/skills/manus-kickoff/metadata.yaml +33 -0
- package/template/.claude/skills/manus-kickoff/templates/PROJECT_KICKOFF.md +89 -0
- package/template/.claude/skills/manus-kickoff/templates/PROJECT_PROPOSAL.md +227 -0
- package/template/.claude/skills/manus-kickoff/templates/TASK_PLAN.md +121 -0
- package/template/.claude/skills/template/SKILL.md +61 -0
- package/template/.claude/skills/template/metadata.yaml +30 -0
- package/template/.claude/templates/PROJECT_KICKOFF.md +89 -0
- package/template/.claude/templates/PROJECT_PROPOSAL.md +227 -0
- package/template/.claude/templates/TASK_PLAN.md +121 -0
- package/template/.claude/thinking-routes/QUICKREF.md +98 -0
- package/template/CLAUDE-template.md +114 -0
- package/template/README.md +148 -0
- package/template/development/todos/INDEX.md +63 -0
- package/template/development/todos/active/_README.md +49 -0
- package/template/development/todos/archived/_README.md +11 -0
- package/template/development/todos/backlog/_README.md +11 -0
- package/template/development/todos/completed/_README.md +11 -0
- package/template/init.sh +150 -0
- package/template/project-paradigm.md +313 -0
- package/template/prompts/how-to-find.md +163 -0
- package/template/thinkinglens-silent.md +138 -0
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Manage project todos and tasks
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
Manage project tasks in the development/todos directory.
|
|
6
|
+
|
|
7
|
+
## Show Task Overview
|
|
8
|
+
|
|
9
|
+
Display the task overview:
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
cat development/todos/INDEX.md
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Task Operations
|
|
16
|
+
|
|
17
|
+
### Create a New Task
|
|
18
|
+
|
|
19
|
+
When user asks to create a task:
|
|
20
|
+
1. Create file in `development/todos/active/`
|
|
21
|
+
2. Use kebab-case for filename (e.g., `user-login.md`)
|
|
22
|
+
3. Use the template format:
|
|
23
|
+
|
|
24
|
+
```markdown
|
|
25
|
+
# [Task Name]
|
|
26
|
+
|
|
27
|
+
**状态**: 🚧 进行中
|
|
28
|
+
**优先级**: P0/P1/P2/P3
|
|
29
|
+
**负责人**: AI/Human
|
|
30
|
+
**分支**: `feature/xxx`
|
|
31
|
+
**创建时间**: YYYY-MM-DD
|
|
32
|
+
**预计完成**: YYYY-MM-DD
|
|
33
|
+
|
|
34
|
+
## 描述
|
|
35
|
+
|
|
36
|
+
[Task description]
|
|
37
|
+
|
|
38
|
+
## 子任务
|
|
39
|
+
|
|
40
|
+
- [ ] Subtask 1
|
|
41
|
+
- [ ] Subtask 2
|
|
42
|
+
|
|
43
|
+
## 依赖
|
|
44
|
+
|
|
45
|
+
- [Dependencies]
|
|
46
|
+
|
|
47
|
+
## 进度
|
|
48
|
+
|
|
49
|
+
- [x] Planning
|
|
50
|
+
- [ ] In progress
|
|
51
|
+
- [ ] Testing
|
|
52
|
+
- [ ] Review
|
|
53
|
+
|
|
54
|
+
## 备注
|
|
55
|
+
|
|
56
|
+
[Notes]
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### Update Task Status
|
|
60
|
+
|
|
61
|
+
To move a task:
|
|
62
|
+
- **Complete**: Move from `active/` to `completed/`
|
|
63
|
+
- **Backlog**: Move from `active/` to `backlog/`
|
|
64
|
+
- **Archive**: Move from `completed/` to `archived/`
|
|
65
|
+
|
|
66
|
+
### Update Task Progress
|
|
67
|
+
|
|
68
|
+
Edit the task file and update:
|
|
69
|
+
- Subtask checkboxes
|
|
70
|
+
- Progress checklist
|
|
71
|
+
- Add notes
|
|
72
|
+
|
|
73
|
+
## Refresh Task Index
|
|
74
|
+
|
|
75
|
+
After any task changes, refresh the index:
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
node .claude/hooks/todo-manager.cjs --force
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## Task Priority Levels
|
|
82
|
+
|
|
83
|
+
| Priority | Usage |
|
|
84
|
+
|----------|-------|
|
|
85
|
+
| P0 | Critical - blocks release |
|
|
86
|
+
| P1 | High - important for next milestone |
|
|
87
|
+
| P2 | Medium - normal feature work |
|
|
88
|
+
| P3 | Low - nice to have |
|
|
89
|
+
|
|
90
|
+
## Examples
|
|
91
|
+
|
|
92
|
+
User says: "Create a task for implementing user authentication"
|
|
93
|
+
→ Create `active/user-authentication.md`
|
|
94
|
+
|
|
95
|
+
User says: "Mark the login task as complete"
|
|
96
|
+
→ Move `active/login.md` to `completed/login.md`
|
|
97
|
+
|
|
98
|
+
User says: "Show all active tasks"
|
|
99
|
+
→ List all files in `development/todos/active/`
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: View and manage pending verification tasks
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
You are helping the user review and verify completed work.
|
|
6
|
+
|
|
7
|
+
## Check Pending Tasks
|
|
8
|
+
|
|
9
|
+
First, read the verification log:
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
cat .claude/verify/verify-log.md 2>/dev/null || echo "No verification log found"
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
And check pending tasks:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
cat .claude/verify/.pending-verify.json 2>/dev/null || echo "No pending tasks"
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Verification Process
|
|
22
|
+
|
|
23
|
+
For each pending task, guide the user through:
|
|
24
|
+
|
|
25
|
+
1. **What was done** - Summarize the action
|
|
26
|
+
2. **How to verify** - Run appropriate verification commands:
|
|
27
|
+
- For code changes: run tests, linters
|
|
28
|
+
- For deployments: check staging environment
|
|
29
|
+
- For commits: verify CI/CD status
|
|
30
|
+
|
|
31
|
+
## Common Verification Commands
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
# Run tests
|
|
35
|
+
npm test
|
|
36
|
+
|
|
37
|
+
# Check test coverage
|
|
38
|
+
npm run test:coverage
|
|
39
|
+
|
|
40
|
+
# Run linter
|
|
41
|
+
npm run lint
|
|
42
|
+
|
|
43
|
+
# Type check
|
|
44
|
+
npm run type-check
|
|
45
|
+
|
|
46
|
+
# Build verification
|
|
47
|
+
npm run build
|
|
48
|
+
|
|
49
|
+
# Check CI status (if using GitHub)
|
|
50
|
+
gh run list --limit 5
|
|
51
|
+
|
|
52
|
+
# Check PR status
|
|
53
|
+
gh pr status
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Mark as Complete
|
|
57
|
+
|
|
58
|
+
After verification is successful, ask the user if they want to:
|
|
59
|
+
- Mark the task as verified (remove from pending)
|
|
60
|
+
- Keep it for later verification
|
|
61
|
+
- Add additional verification steps
|
|
62
|
+
|
|
63
|
+
To mark as complete, edit `.claude/verify/.pending-verify.json` to remove the completed task.
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Code Formatter Hook - 自动代码格式化
|
|
4
|
+
*
|
|
5
|
+
* 根据 Boris Cherny 的最佳实践,Claude 通常能生成格式良好的代码,
|
|
6
|
+
* 但这个 hook 处理最后 10% 的格式问题,避免 CI 中的格式错误。
|
|
7
|
+
*
|
|
8
|
+
* 触发时机:PostToolUse 事件
|
|
9
|
+
* 支持语言:JavaScript, TypeScript, Python, Rust, Go, JSON, Markdown
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
const fs = require('fs');
|
|
13
|
+
const path = require('path');
|
|
14
|
+
const { spawn } = require('child_process');
|
|
15
|
+
|
|
16
|
+
const PROJECT_DIR = process.env.CLAUDE_PROJECT_DIR || process.cwd();
|
|
17
|
+
|
|
18
|
+
// 支持的格式化器配置
|
|
19
|
+
const FORMATTERS = {
|
|
20
|
+
// JavaScript/TypeScript
|
|
21
|
+
'.js': { cmd: 'prettier', args: ['--write', '--log-level', 'warn'] },
|
|
22
|
+
'.jsx': { cmd: 'prettier', args: ['--write', '--log-level', 'warn'] },
|
|
23
|
+
'.ts': { cmd: 'prettier', args: ['--write', '--log-level', 'warn'] },
|
|
24
|
+
'.tsx': { cmd: 'prettier', args: ['--write', '--log-level', 'warn'] },
|
|
25
|
+
'.mjs': { cmd: 'prettier', args: ['--write', '--log-level', 'warn'] },
|
|
26
|
+
'.cjs': { cmd: 'prettier', args: ['--write', '--log-level', 'warn'] },
|
|
27
|
+
|
|
28
|
+
// Python
|
|
29
|
+
'.py': { cmd: 'black', args: ['--quiet'] },
|
|
30
|
+
|
|
31
|
+
// Rust
|
|
32
|
+
'.rs': { cmd: 'rustfmt', args: [] },
|
|
33
|
+
|
|
34
|
+
// Go
|
|
35
|
+
'.go': { cmd: 'gofmt', args: ['-w'] },
|
|
36
|
+
|
|
37
|
+
// JSON
|
|
38
|
+
'.json': { cmd: 'prettier', args: ['--write', '--log-level', 'warn'] },
|
|
39
|
+
|
|
40
|
+
// Markdown
|
|
41
|
+
'.md': { cmd: 'prettier', args: ['--write', '--log-level', 'warn'] },
|
|
42
|
+
|
|
43
|
+
// CSS/SCSS
|
|
44
|
+
'.css': { cmd: 'prettier', args: ['--write', '--log-level', 'warn'] },
|
|
45
|
+
'.scss': { cmd: 'prettier', args: ['--write', '--log-level', 'warn'] },
|
|
46
|
+
'.less': { cmd: 'prettier', args: ['--write', '--log-level', 'warn'] },
|
|
47
|
+
|
|
48
|
+
// HTML
|
|
49
|
+
'.html': { cmd: 'prettier', args: ['--write', '--log-level', 'warn'] },
|
|
50
|
+
'.htm': { cmd: 'prettier', args: ['--write', '--log-level', 'warn'] },
|
|
51
|
+
|
|
52
|
+
// YAML
|
|
53
|
+
'.yaml': { cmd: 'prettier', args: ['--write', '--log-level', 'warn'] },
|
|
54
|
+
'.yml': { cmd: 'prettier', args: ['--write', '--log-level', 'warn'] },
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
// 检查命令是否可用
|
|
58
|
+
function isCommandAvailable(cmd) {
|
|
59
|
+
try {
|
|
60
|
+
// 首先尝试 which
|
|
61
|
+
spawn('which', [cmd], { stdio: 'ignore' });
|
|
62
|
+
|
|
63
|
+
// 对于 prettier,额外检查 npx
|
|
64
|
+
if (cmd === 'prettier') {
|
|
65
|
+
spawn('npx', [cmd, '--version'], { stdio: 'ignore' });
|
|
66
|
+
}
|
|
67
|
+
return true;
|
|
68
|
+
} catch (e) {
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// 获取工具修改的文件
|
|
74
|
+
function getModifiedFiles() {
|
|
75
|
+
const toolName = process.env.CLAUDE_TOOL_NAME || '';
|
|
76
|
+
const toolInput = process.env.CLAUDE_TOOL_INPUT || '';
|
|
77
|
+
|
|
78
|
+
// 从工具输入中提取文件路径
|
|
79
|
+
const filePaths = [];
|
|
80
|
+
|
|
81
|
+
// Edit 工具
|
|
82
|
+
if (toolName === 'Edit') {
|
|
83
|
+
// Edit 工具的文件路径在 file_path 参数中
|
|
84
|
+
const match = toolInput.match(/"filePath":\s*"([^"]+)"/);
|
|
85
|
+
if (match) {
|
|
86
|
+
filePaths.push(match[1]);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Write 工具
|
|
91
|
+
if (toolName === 'Write') {
|
|
92
|
+
const match = toolInput.match(/"file_path":\s*"([^"]+)"/);
|
|
93
|
+
if (match) {
|
|
94
|
+
filePaths.push(match[1]);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// NotebookEdit 工具
|
|
99
|
+
if (toolName === 'NotebookEdit') {
|
|
100
|
+
const match = toolInput.match(/"notebook_path":\s*"([^"]+)"/);
|
|
101
|
+
if (match) {
|
|
102
|
+
filePaths.push(match[1]);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return filePaths;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// 格式化文件
|
|
110
|
+
function formatFile(filePath) {
|
|
111
|
+
const ext = path.extname(filePath);
|
|
112
|
+
|
|
113
|
+
// 跳过不需要格式化的目录
|
|
114
|
+
const skipDirs = ['node_modules', '.git', 'dist', 'build', 'target', 'vendor', '.venv'];
|
|
115
|
+
if (skipDirs.some(dir => filePath.includes(`/${dir}/`) || filePath.includes(`\\${dir}\\`))) {
|
|
116
|
+
return null;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const formatter = FORMATTERS[ext];
|
|
120
|
+
if (!formatter) {
|
|
121
|
+
return null;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// 检查格式化器是否可用
|
|
125
|
+
if (!isCommandAvailable(formatter.cmd)) {
|
|
126
|
+
return null;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// 检查文件是否存在
|
|
130
|
+
if (!fs.existsSync(filePath)) {
|
|
131
|
+
return null;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
try {
|
|
135
|
+
// 对于 prettier,使用 npx 来支持本地安装
|
|
136
|
+
const cmd = formatter.cmd === 'prettier' ? 'npx' : formatter.cmd;
|
|
137
|
+
const args = formatter.cmd === 'prettier'
|
|
138
|
+
? ['prettier', ...formatter.args, filePath]
|
|
139
|
+
: [...formatter.args, filePath];
|
|
140
|
+
|
|
141
|
+
// 运行格式化器
|
|
142
|
+
spawn(cmd, args, {
|
|
143
|
+
stdio: 'ignore',
|
|
144
|
+
cwd: PROJECT_DIR,
|
|
145
|
+
detached: true
|
|
146
|
+
}).unref();
|
|
147
|
+
return { file: filePath, formatter: formatter.cmd };
|
|
148
|
+
} catch (e) {
|
|
149
|
+
return null;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// 主函数
|
|
154
|
+
function main() {
|
|
155
|
+
try {
|
|
156
|
+
const eventType = process.env.CLAUDE_EVENT_TYPE || '';
|
|
157
|
+
|
|
158
|
+
// 只在 PostToolUse 事件时运行
|
|
159
|
+
if (eventType !== 'PostToolUse') {
|
|
160
|
+
process.exit(0);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const modifiedFiles = getModifiedFiles();
|
|
164
|
+
|
|
165
|
+
if (modifiedFiles.length === 0) {
|
|
166
|
+
process.exit(0);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// 格式化所有修改的文件
|
|
170
|
+
const formatted = modifiedFiles
|
|
171
|
+
.map(formatFile)
|
|
172
|
+
.filter(result => result !== null);
|
|
173
|
+
|
|
174
|
+
// 输出格式化结果(可选,用于调试)
|
|
175
|
+
if (formatted.length > 0 && process.env.DEBUG_FORMATTER) {
|
|
176
|
+
console.log(`\n🎨 [格式化] 已格式化 ${formatted.length} 个文件:`);
|
|
177
|
+
formatted.forEach(f => console.log(` - ${f.file} (${f.formatter})`));
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
} catch (e) {
|
|
181
|
+
// 完全静默,不输出任何错误
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
process.exit(0);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
main();
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Multi-Session Manager - 并行多会话管理
|
|
4
|
+
*
|
|
5
|
+
* 根据 Boris Cherny 的实践,并行运行 5 个 Claude 可以极大提升效率。
|
|
6
|
+
* 这个 hook 帮助跟踪和管理多个并行会话。
|
|
7
|
+
*
|
|
8
|
+
* 功能:
|
|
9
|
+
* - 跟踪活跃的会话
|
|
10
|
+
* - 为每个会话分配编号
|
|
11
|
+
* - 记录会话状态和任务
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
const fs = require('fs');
|
|
15
|
+
const path = require('path');
|
|
16
|
+
const os = require('os');
|
|
17
|
+
|
|
18
|
+
const PROJECT_DIR = process.env.CLAUDE_PROJECT_DIR || process.cwd();
|
|
19
|
+
const SESSIONS_DIR = path.join(PROJECT_DIR, '.claude', 'sessions');
|
|
20
|
+
const ACTIVE_SESSIONS_FILE = path.join(SESSIONS_DIR, 'active-sessions.json');
|
|
21
|
+
const SESSION_LOCK_FILE = path.join(SESSIONS_DIR, '.session-lock');
|
|
22
|
+
|
|
23
|
+
// 确保目录存在
|
|
24
|
+
try { fs.mkdirSync(SESSIONS_DIR, { recursive: true }); } catch (e) {}
|
|
25
|
+
|
|
26
|
+
// 获取主机名和用户信息用于标识会话来源
|
|
27
|
+
function getSessionIdentifier() {
|
|
28
|
+
const hostname = os.hostname();
|
|
29
|
+
const platform = os.platform();
|
|
30
|
+
const username = process.env.USER || process.env.USERNAME || 'unknown';
|
|
31
|
+
return `${username}@${hostname} (${platform})`;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// 获取当前会话 ID
|
|
35
|
+
function getCurrentSessionId() {
|
|
36
|
+
// 从环境变量或进程信息生成唯一 ID
|
|
37
|
+
const pid = process.pid;
|
|
38
|
+
const ppid = process.ppid;
|
|
39
|
+
const timestamp = Date.now();
|
|
40
|
+
return `session-${pid}-${ppid}`;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// 获取活跃会话列表
|
|
44
|
+
function getActiveSessions() {
|
|
45
|
+
if (!fs.existsSync(ACTIVE_SESSIONS_FILE)) {
|
|
46
|
+
return [];
|
|
47
|
+
}
|
|
48
|
+
try {
|
|
49
|
+
const data = fs.readFileSync(ACTIVE_SESSIONS_FILE, 'utf-8');
|
|
50
|
+
const sessions = JSON.parse(data);
|
|
51
|
+
// 清理超过 1 小时的旧会话
|
|
52
|
+
const oneHourAgo = Date.now() - 60 * 60 * 1000;
|
|
53
|
+
return sessions.filter(s => s.lastHeartbeat > oneHourAgo);
|
|
54
|
+
} catch (e) {
|
|
55
|
+
return [];
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// 保存活跃会话列表
|
|
60
|
+
function saveActiveSessions(sessions) {
|
|
61
|
+
fs.writeFileSync(ACTIVE_SESSIONS_FILE, JSON.stringify(sessions, null, 2));
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// 更新当前会话的心跳
|
|
65
|
+
function updateHeartbeat() {
|
|
66
|
+
const sessions = getActiveSessions();
|
|
67
|
+
const currentId = getCurrentSessionId();
|
|
68
|
+
const now = Date.now();
|
|
69
|
+
|
|
70
|
+
const existingSession = sessions.find(s => s.id === currentId);
|
|
71
|
+
if (existingSession) {
|
|
72
|
+
existingSession.lastHeartbeat = now;
|
|
73
|
+
existingSession.heartbeatCount = (existingSession.heartbeatCount || 0) + 1;
|
|
74
|
+
} else {
|
|
75
|
+
sessions.push({
|
|
76
|
+
id: currentId,
|
|
77
|
+
identifier: getSessionIdentifier(),
|
|
78
|
+
startedAt: now,
|
|
79
|
+
lastHeartbeat: now,
|
|
80
|
+
heartbeatCount: 1,
|
|
81
|
+
status: 'active'
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
saveActiveSessions(sessions);
|
|
86
|
+
return sessions;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// 获取会话编号(1-5,类似 Boris 的标签页编号)
|
|
90
|
+
function getSessionNumber(sessions) {
|
|
91
|
+
const currentId = getCurrentSessionId();
|
|
92
|
+
const session = sessions.find(s => s.id === currentId);
|
|
93
|
+
if (session && session.number) {
|
|
94
|
+
return session.number;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// 分配一个可用的编号
|
|
98
|
+
const usedNumbers = new Set(sessions.filter(s => s.number).map(s => s.number));
|
|
99
|
+
for (let i = 1; i <= 10; i++) {
|
|
100
|
+
if (!usedNumbers.has(i)) {
|
|
101
|
+
// 更新会话的编号
|
|
102
|
+
const updatedSessions = getActiveSessions();
|
|
103
|
+
const targetSession = updatedSessions.find(s => s.id === currentId);
|
|
104
|
+
if (targetSession) {
|
|
105
|
+
targetSession.number = i;
|
|
106
|
+
saveActiveSessions(updatedSessions);
|
|
107
|
+
return i;
|
|
108
|
+
}
|
|
109
|
+
return i;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return sessions.length + 1;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// 显示会话状态
|
|
117
|
+
function displaySessionStatus() {
|
|
118
|
+
const sessions = getActiveSessions();
|
|
119
|
+
const currentId = getCurrentSessionId();
|
|
120
|
+
const currentNumber = getSessionNumber(sessions);
|
|
121
|
+
|
|
122
|
+
console.log(`\n🔄 [多会话] 当前会话: #${currentNumber} | 活跃会话: ${sessions.length}`);
|
|
123
|
+
console.log(` 主机: ${getSessionIdentifier()}`);
|
|
124
|
+
|
|
125
|
+
if (sessions.length > 1) {
|
|
126
|
+
console.log(`\n 其他活跃会话:`);
|
|
127
|
+
sessions
|
|
128
|
+
.filter(s => s.id !== currentId)
|
|
129
|
+
.forEach(s => {
|
|
130
|
+
const number = s.number || '?';
|
|
131
|
+
const duration = Math.round((Date.now() - s.startedAt) / 1000 / 60);
|
|
132
|
+
console.log(` - 会话 #${number}: ${s.identifier} (${duration}分钟前启动)`);
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
console.log('');
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// 主函数
|
|
140
|
+
function main() {
|
|
141
|
+
try {
|
|
142
|
+
const eventType = process.env.CLAUDE_EVENT_TYPE || '';
|
|
143
|
+
const toolInput = process.env.CLAUDE_TOOL_INPUT || '';
|
|
144
|
+
|
|
145
|
+
// 在每次交互时更新心跳
|
|
146
|
+
const sessions = updateHeartbeat();
|
|
147
|
+
const sessionNumber = getSessionNumber(sessions);
|
|
148
|
+
|
|
149
|
+
// 在会话开始或重要操作时显示状态
|
|
150
|
+
if (eventType === 'UserPromptSubmit' && toolInput.length > 20) {
|
|
151
|
+
// 只在较长的用户输入时显示,避免噪音
|
|
152
|
+
if (sessions.length > 1 || process.env.SHOW_SESSION_STATUS) {
|
|
153
|
+
console.log(`\n🔄 [会话 #${sessionNumber}] 活跃会话: ${sessions.length}\n`);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// 在会话结束时清理
|
|
158
|
+
if (eventType === 'AgentStop') {
|
|
159
|
+
const currentId = getCurrentSessionId();
|
|
160
|
+
const updatedSessions = getActiveSessions();
|
|
161
|
+
const index = updatedSessions.findIndex(s => s.id === currentId);
|
|
162
|
+
if (index !== -1) {
|
|
163
|
+
updatedSessions[index].status = 'ended';
|
|
164
|
+
updatedSessions[index].endedAt = Date.now();
|
|
165
|
+
saveActiveSessions(updatedSessions);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
} catch (e) {
|
|
170
|
+
// 静默处理错误
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
process.exit(0);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// 如果直接运行此脚本,显示会话状态
|
|
177
|
+
if (require.main === module && process.argv[2] === '--status') {
|
|
178
|
+
displaySessionStatus();
|
|
179
|
+
} else {
|
|
180
|
+
main();
|
|
181
|
+
}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Project Kickoff Hook - Manus-style project initialization
|
|
4
|
+
*
|
|
5
|
+
* 触发条件:
|
|
6
|
+
* - 检测到新项目 (缺少 PROJECT_KICKOFF.md)
|
|
7
|
+
* - 用户明确请求项目启动
|
|
8
|
+
*
|
|
9
|
+
* 功能:
|
|
10
|
+
* - 检查项目是否已启动
|
|
11
|
+
* - 如果未启动,提示 AI 进行项目规划
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
const fs = require('fs');
|
|
15
|
+
const path = require('path');
|
|
16
|
+
|
|
17
|
+
const PROJECT_DIR = process.env.CLAUDE_PROJECT_DIR || process.cwd();
|
|
18
|
+
const TEMPLATES_DIR = path.join(PROJECT_DIR, '.claude/templates');
|
|
19
|
+
const KICKOFF_FILE = path.join(PROJECT_DIR, 'PROJECT_KICKOFF.md');
|
|
20
|
+
const PLAN_FILE = path.join(PROJECT_DIR, 'TASK_PLAN.md');
|
|
21
|
+
const PROPOSAL_FILE = path.join(PROJECT_DIR, 'PROJECT_PROPOSAL.md');
|
|
22
|
+
const HINT_FILE = path.join(PROJECT_DIR, '.claude/.kickoff-hint.txt');
|
|
23
|
+
|
|
24
|
+
// 检查项目是否已启动
|
|
25
|
+
function isProjectStarted() {
|
|
26
|
+
return fs.existsSync(KICKOFF_FILE);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// 检查是否为强制启动模式
|
|
30
|
+
function isForceKickoff() {
|
|
31
|
+
const toolInput = process.env.CLAUDE_TOOL_INPUT || '';
|
|
32
|
+
return toolInput.includes('kickoff') ||
|
|
33
|
+
toolInput.includes('项目启动') ||
|
|
34
|
+
toolInput.includes('project plan') ||
|
|
35
|
+
toolInput.includes('重新规划');
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// 生成启动提示
|
|
39
|
+
function generateKickoffHint() {
|
|
40
|
+
const now = new Date().toISOString().split('T')[0];
|
|
41
|
+
|
|
42
|
+
return `
|
|
43
|
+
╔══════════════════════════════════════════════════════════════════════╗
|
|
44
|
+
║ 🚀 项目启动检测 (Project Kickoff) ║
|
|
45
|
+
╚══════════════════════════════════════════════════════════════════════╝
|
|
46
|
+
|
|
47
|
+
检测到此项目尚未完成启动流程。
|
|
48
|
+
|
|
49
|
+
根据 Manus 风格的 AI 2.0 开发范式,在开始编码前,我们需要:
|
|
50
|
+
|
|
51
|
+
═══════════════════════════════════════════════════════════════════════
|
|
52
|
+
|
|
53
|
+
📋 第一步: 项目启动清单 (PROJECT_KICKOFF.md)
|
|
54
|
+
├── 定义项目目标和成功标准
|
|
55
|
+
├── 明确技术约束和非技术约束
|
|
56
|
+
└── 划定 AI/Human 责任边界
|
|
57
|
+
|
|
58
|
+
📋 第二步: 任务执行计划 (TASK_PLAN.md)
|
|
59
|
+
├── 任务分解 (WBS)
|
|
60
|
+
├── 依赖关系分析
|
|
61
|
+
├── Agent 分配策略
|
|
62
|
+
└── 检查点设置
|
|
63
|
+
|
|
64
|
+
📋 第三步: 项目计划书 (PROJECT_PROPOSAL.md)
|
|
65
|
+
├── 技术架构设计
|
|
66
|
+
├── 功能需求分析
|
|
67
|
+
├── 开发迭代规划
|
|
68
|
+
└── 风险评估
|
|
69
|
+
|
|
70
|
+
═══════════════════════════════════════════════════════════════════════
|
|
71
|
+
|
|
72
|
+
🎯 下一步行动:
|
|
73
|
+
|
|
74
|
+
请回答以下问题,我将为你生成完整的项目规划:
|
|
75
|
+
|
|
76
|
+
1. 项目名称是什么?
|
|
77
|
+
2. 用一句话描述这个项目要解决什么问题?
|
|
78
|
+
3. 核心目标是什么?(成功标准是什么?)
|
|
79
|
+
4. 有什么技术约束或偏好?(语言/框架/部署等)
|
|
80
|
+
5. 预期的时间框架是怎样的?
|
|
81
|
+
|
|
82
|
+
回答这些问题后,我将:
|
|
83
|
+
✅ 生成 PROJECT_KICKOFF.md
|
|
84
|
+
✅ 生成 TASK_PLAN.md
|
|
85
|
+
✅ 生成 PROJECT_PROPOSAL.md
|
|
86
|
+
✅ 等待你的确认后开始执行
|
|
87
|
+
|
|
88
|
+
═══════════════════════════════════════════════════════════════════════
|
|
89
|
+
|
|
90
|
+
💡 提示: 你也可以直接说 "跳过启动" 使用传统开发模式
|
|
91
|
+
|
|
92
|
+
生成日期: ${now}
|
|
93
|
+
`;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// 主函数
|
|
97
|
+
function main() {
|
|
98
|
+
// 如果项目已启动且不是强制模式,静默退出
|
|
99
|
+
if (isProjectStarted() && !isForceKickoff()) {
|
|
100
|
+
process.exit(0);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// 生成提示文件
|
|
104
|
+
const hint = generateKickoffHint();
|
|
105
|
+
fs.mkdirSync(path.dirname(HINT_FILE), { recursive: true });
|
|
106
|
+
fs.writeFileSync(HINT_FILE, hint);
|
|
107
|
+
|
|
108
|
+
// 同时输出到 stdout (供 AI 直接读取)
|
|
109
|
+
console.log(hint);
|
|
110
|
+
|
|
111
|
+
process.exit(0);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
main();
|