sumulige-claude 1.2.0 → 1.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/.claude/.kickoff-hint.txt +3 -2
- package/.claude/AGENTS.md +6 -6
- package/.claude/CLAUDE.md +138 -0
- package/.claude/README.md +234 -43
- package/.claude/USAGE.md +175 -0
- package/.claude/boris-optimizations.md +167 -0
- package/.claude/commands/fix.md +83 -0
- package/.claude/commands/plan.md +88 -0
- package/.claude/commands/refactor.md +102 -0
- package/.claude/commands/todos.md +6 -41
- package/.claude/hooks/code-formatter.cjs +2 -7
- package/.claude/hooks/conversation-logger.cjs +222 -0
- package/.claude/hooks/multi-session.cjs +3 -9
- package/.claude/hooks/pre-push.cjs +3 -2
- package/.claude/hooks/project-kickoff.cjs +198 -20
- package/.claude/hooks/rag-skill-loader.cjs +0 -7
- package/.claude/hooks/session-restore.cjs +0 -0
- package/.claude/hooks/session-save.cjs +0 -0
- package/.claude/hooks/thinking-silent.cjs +3 -9
- package/.claude/hooks/todo-manager.cjs +142 -269
- package/.claude/hooks/verify-work.cjs +4 -10
- package/.claude/rag/skill-index.json +147 -8
- package/.claude/rules/coding-style.md +119 -0
- package/.claude/rules/hooks.md +288 -0
- package/.claude/rules/performance.md +78 -0
- package/.claude/rules/security.md +56 -0
- package/.claude/rules/testing.md +89 -0
- package/.claude/settings.json +115 -0
- package/.claude/settings.local.json +19 -1
- package/.claude/skills/SKILLS.md +145 -0
- package/.claude/skills/design-brain/SKILL.md +190 -0
- package/.claude/skills/design-brain/metadata.yaml +26 -0
- package/.claude/skills/examples/README.md +47 -0
- package/.claude/skills/examples/basic-task.md +67 -0
- package/.claude/skills/examples/bug-fix-workflow.md +92 -0
- package/.claude/skills/examples/feature-development.md +81 -0
- package/.claude/skills/manus-kickoff/SKILL.md +128 -0
- package/.claude/skills/manus-kickoff/examples/basic.md +84 -0
- package/.claude/skills/manus-kickoff/metadata.yaml +33 -0
- package/.claude/skills/manus-kickoff/templates/PROJECT_KICKOFF.md +89 -0
- package/.claude/skills/manus-kickoff/templates/PROJECT_PROPOSAL.md +227 -0
- package/.claude/skills/manus-kickoff/templates/TASK_PLAN.md +121 -0
- package/.claude/skills/quality-guard/SKILL.md +138 -0
- package/.claude/skills/quality-guard/metadata.yaml +27 -0
- package/.claude/skills/quick-fix/SKILL.md +138 -0
- package/.claude/skills/quick-fix/metadata.yaml +26 -0
- package/.claude/skills/test-master/SKILL.md +186 -0
- package/.claude/skills/test-master/metadata.yaml +29 -0
- package/.claude/templates/PROJECT_KICKOFF.md +89 -0
- package/.claude/templates/PROJECT_PROPOSAL.md +227 -0
- package/.claude/templates/TASK_PLAN.md +121 -0
- package/.claude-plugin/marketplace.json +2 -2
- package/AGENTS.md +49 -7
- package/CHANGELOG.md +56 -2
- package/CLAUDE-template.md +114 -0
- package/README.md +73 -1
- package/config/official-skills.json +2 -2
- package/development/knowledge-base/.index.clean.json +1 -0
- package/jest.config.js +3 -1
- package/lib/commands.js +1626 -1207
- package/lib/marketplace.js +1 -0
- package/package.json +1 -1
- package/project-paradigm.md +313 -0
- package/prompts/how-to-find.md +163 -0
- package/tests/commands.test.js +940 -17
- package/tests/config-schema.test.js +425 -0
- package/tests/marketplace.test.js +330 -214
- package/tests/sync-external.test.js +214 -0
- package/tests/update-registry.test.js +251 -0
- package/tests/utils.test.js +12 -8
- package/tests/web-search.test.js +392 -0
- package/thinkinglens-silent.md +138 -0
- package/.claude/skills/api-tester/SKILL.md +0 -90
- package/.claude/skills/api-tester/examples/basic.md +0 -3
- package/.claude/skills/api-tester/metadata.yaml +0 -30
- package/.claude/skills/api-tester/templates/default.md +0 -3
- package/.claude/skills/template/SKILL.md +0 -6
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Conversation Logger - 增强版对话记录器
|
|
4
|
+
*
|
|
5
|
+
* 自动记录每次对话到 DAILY_CONVERSATION.md
|
|
6
|
+
* 按日期分组,无需手动触发
|
|
7
|
+
*
|
|
8
|
+
* 功能:
|
|
9
|
+
* - 捕获用户输入和 AI 输出
|
|
10
|
+
* - 记录工具使用
|
|
11
|
+
* - 按日期自动分组
|
|
12
|
+
* - 实时追加到日志
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
const fs = require('fs');
|
|
16
|
+
const path = require('path');
|
|
17
|
+
|
|
18
|
+
const PROJECT_DIR = process.env.CLAUDE_PROJECT_DIR || process.cwd();
|
|
19
|
+
const CONVERSATION_LOG = path.join(PROJECT_DIR, '.claude', 'DAILY_CONVERSATION.md');
|
|
20
|
+
const THINKING_DIR = path.join(PROJECT_DIR, '.claude', 'thinking-routes');
|
|
21
|
+
const FLOW_FILE = path.join(THINKING_DIR, '.conversation-flow.json');
|
|
22
|
+
|
|
23
|
+
// 确保目录存在
|
|
24
|
+
try { fs.mkdirSync(THINKING_DIR, { recursive: true }); } catch (e) {}
|
|
25
|
+
|
|
26
|
+
// 获取当前日期字符串 (YYYY-MM-DD)
|
|
27
|
+
function getDateStr() {
|
|
28
|
+
const now = new Date();
|
|
29
|
+
const year = now.getFullYear();
|
|
30
|
+
const month = String(now.getMonth() + 1).padStart(2, '0');
|
|
31
|
+
const day = String(now.getDate()).padStart(2, '0');
|
|
32
|
+
return `${year}-${month}-${day}`;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// 获取当前时间字符串 (HH:MM)
|
|
36
|
+
function getTimeStr() {
|
|
37
|
+
const now = new Date();
|
|
38
|
+
const hours = String(now.getHours()).padStart(2, '0');
|
|
39
|
+
const minutes = String(now.getMinutes()).padStart(2, '0');
|
|
40
|
+
return `${hours}:${minutes}`;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// 转义 Markdown 特殊字符
|
|
44
|
+
function escapeMarkdown(text) {
|
|
45
|
+
if (!text) return '';
|
|
46
|
+
return text
|
|
47
|
+
.replace(/\|/g, '\\|')
|
|
48
|
+
.replace(/\n/g, ' ')
|
|
49
|
+
.slice(0, 500); // 限制长度
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// 初始化日志文件
|
|
53
|
+
function initLogFile() {
|
|
54
|
+
if (!fs.existsSync(CONVERSATION_LOG)) {
|
|
55
|
+
const header = `# 每日对话记录
|
|
56
|
+
|
|
57
|
+
> 此文件由 conversation-logger.cjs 自动生成
|
|
58
|
+
> 记录与 AI 的每次对话
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
`;
|
|
62
|
+
fs.writeFileSync(CONVERSATION_LOG, header, 'utf-8');
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// 追加对话到日志
|
|
67
|
+
function appendConversation(role, content, toolInfo = '') {
|
|
68
|
+
initLogFile();
|
|
69
|
+
|
|
70
|
+
const dateStr = getDateStr();
|
|
71
|
+
const timeStr = getTimeStr();
|
|
72
|
+
const roleLabel = role === 'user' ? '👤 用户' : role === 'assistant' ? '🤖 AI' : '🔧 工具';
|
|
73
|
+
const safeContent = escapeMarkdown(content);
|
|
74
|
+
|
|
75
|
+
const entry = `### ${timeStr} - ${roleLabel}
|
|
76
|
+
${safeContent}${toolInfo ? `\n> ${toolInfo}` : ''}
|
|
77
|
+
|
|
78
|
+
`;
|
|
79
|
+
|
|
80
|
+
// 读取现有内容
|
|
81
|
+
let logContent = fs.readFileSync(CONVERSATION_LOG, 'utf-8');
|
|
82
|
+
|
|
83
|
+
// 检查今天是否已有日期头
|
|
84
|
+
const todayHeader = `## ${dateStr}`;
|
|
85
|
+
const todayIndex = logContent.indexOf(todayHeader);
|
|
86
|
+
|
|
87
|
+
if (todayIndex === -1) {
|
|
88
|
+
// 今天还没有记录,添加新的日期头
|
|
89
|
+
const todaySection = `
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
## ${dateStr}
|
|
93
|
+
|
|
94
|
+
${entry}
|
|
95
|
+
`;
|
|
96
|
+
logContent += todaySection;
|
|
97
|
+
} else {
|
|
98
|
+
// 找到今天的记录位置,在末尾追加
|
|
99
|
+
// 使用正则确保只匹配 ## 开头的日期行,而不是 ### 开头的条目
|
|
100
|
+
const afterToday = logContent.slice(todayIndex + todayHeader.length);
|
|
101
|
+
const dateMatch = afterToday.match(/^## \d{4}-\d{2}-\d{2}/m);
|
|
102
|
+
const nextDateIndex = dateMatch ? todayIndex + todayHeader.length + dateMatch.index : -1;
|
|
103
|
+
|
|
104
|
+
if (nextDateIndex === -1) {
|
|
105
|
+
// 今天是最后一天,直接追加到文件末尾
|
|
106
|
+
logContent += entry;
|
|
107
|
+
} else {
|
|
108
|
+
// 在下一天之前插入
|
|
109
|
+
logContent = logContent.slice(0, nextDateIndex) + entry + logContent.slice(nextDateIndex);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// 写回文件
|
|
114
|
+
fs.writeFileSync(CONVERSATION_LOG, logContent, 'utf-8');
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// 从思维轨迹读取并记录完整对话
|
|
118
|
+
function syncFromFlowFile() {
|
|
119
|
+
if (!fs.existsSync(FLOW_FILE)) {
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
try {
|
|
124
|
+
const flow = JSON.parse(fs.readFileSync(FLOW_FILE, 'utf-8'));
|
|
125
|
+
const turns = flow.turns || [];
|
|
126
|
+
|
|
127
|
+
if (turns.length === 0) return;
|
|
128
|
+
|
|
129
|
+
// 读取现有日志,记录已处理的 turn
|
|
130
|
+
const logContent = fs.existsSync(CONVERSATION_LOG)
|
|
131
|
+
? fs.readFileSync(CONVERSATION_LOG, 'utf-8')
|
|
132
|
+
: '';
|
|
133
|
+
initLogFile();
|
|
134
|
+
|
|
135
|
+
// 简单的追踪:只记录最新的几次交互
|
|
136
|
+
const recentTurns = turns.slice(-5);
|
|
137
|
+
|
|
138
|
+
recentTurns.forEach(turn => {
|
|
139
|
+
const time = turn.time ? new Date(turn.time).toISOString() : '';
|
|
140
|
+
const toolName = turn.toolName || '';
|
|
141
|
+
const type = turn.type || '';
|
|
142
|
+
|
|
143
|
+
// 根据 turn 类型记录
|
|
144
|
+
if (type === 'potential-action' && toolName) {
|
|
145
|
+
appendConversation('tool', `[${toolName}]`, turn.description || '');
|
|
146
|
+
}
|
|
147
|
+
});
|
|
148
|
+
} catch (e) {
|
|
149
|
+
// 静默处理错误
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// 主函数
|
|
154
|
+
function main() {
|
|
155
|
+
try {
|
|
156
|
+
const eventType = process.env.CLAUDE_EVENT_TYPE || '';
|
|
157
|
+
const toolName = process.env.CLAUDE_TOOL_NAME || '';
|
|
158
|
+
const toolInput = process.env.CLAUDE_TOOL_INPUT || '';
|
|
159
|
+
const textOutput = process.env.CLAUDE_TEXT_OUTPUT || '';
|
|
160
|
+
|
|
161
|
+
// 根据事件类型记录
|
|
162
|
+
switch (eventType) {
|
|
163
|
+
case 'UserPromptSubmit':
|
|
164
|
+
// 用户提交输入
|
|
165
|
+
if (toolInput) {
|
|
166
|
+
appendConversation('user', toolInput);
|
|
167
|
+
}
|
|
168
|
+
break;
|
|
169
|
+
|
|
170
|
+
case 'TextOutput':
|
|
171
|
+
// AI 输出文本
|
|
172
|
+
if (textOutput) {
|
|
173
|
+
appendConversation('assistant', textOutput);
|
|
174
|
+
}
|
|
175
|
+
break;
|
|
176
|
+
|
|
177
|
+
case 'PostToolUse':
|
|
178
|
+
// 工具使用后
|
|
179
|
+
if (toolName) {
|
|
180
|
+
appendConversation('tool', toolInput, `使用工具: ${toolName}`);
|
|
181
|
+
}
|
|
182
|
+
break;
|
|
183
|
+
|
|
184
|
+
case 'AgentStop':
|
|
185
|
+
// 会话结束,同步思维轨迹
|
|
186
|
+
syncFromFlowFile();
|
|
187
|
+
break;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
} catch (e) {
|
|
191
|
+
// 静默处理错误,不干扰正常对话
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
process.exit(0);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// 如果直接运行,执行同步
|
|
198
|
+
if (require.main === module) {
|
|
199
|
+
if (process.argv.includes('--sync')) {
|
|
200
|
+
syncFromFlowFile();
|
|
201
|
+
console.log('✅ 已同步对话记录');
|
|
202
|
+
} else if (process.argv.includes('--view')) {
|
|
203
|
+
// 查看今日对话
|
|
204
|
+
if (fs.existsSync(CONVERSATION_LOG)) {
|
|
205
|
+
const content = fs.readFileSync(CONVERSATION_LOG, 'utf-8');
|
|
206
|
+
const today = getDateStr();
|
|
207
|
+
const start = content.indexOf(`## ${today}`);
|
|
208
|
+
if (start !== -1) {
|
|
209
|
+
const end = content.indexOf('## ', start + 10);
|
|
210
|
+
console.log(content.slice(start, end === -1 ? undefined : end));
|
|
211
|
+
} else {
|
|
212
|
+
console.log('📭 今天暂无对话记录');
|
|
213
|
+
}
|
|
214
|
+
} else {
|
|
215
|
+
console.log('📭 暂无对话记录');
|
|
216
|
+
}
|
|
217
|
+
} else {
|
|
218
|
+
main();
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
module.exports = { main, syncFromFlowFile, appendConversation };
|
|
@@ -16,12 +16,6 @@ const path = require('path');
|
|
|
16
16
|
const os = require('os');
|
|
17
17
|
|
|
18
18
|
const PROJECT_DIR = process.env.CLAUDE_PROJECT_DIR || process.cwd();
|
|
19
|
-
|
|
20
|
-
// 如果不在 Claude Code 环境中运行,静默退出
|
|
21
|
-
if (!process.env.CLAUDE_PROJECT_DIR) {
|
|
22
|
-
process.exit(0);
|
|
23
|
-
}
|
|
24
|
-
|
|
25
19
|
const SESSIONS_DIR = path.join(PROJECT_DIR, '.claude', 'sessions');
|
|
26
20
|
const ACTIVE_SESSIONS_FILE = path.join(SESSIONS_DIR, 'active-sessions.json');
|
|
27
21
|
const SESSION_LOCK_FILE = path.join(SESSIONS_DIR, '.session-lock');
|
|
@@ -129,13 +123,13 @@ function displaySessionStatus() {
|
|
|
129
123
|
console.log(` 主机: ${getSessionIdentifier()}`);
|
|
130
124
|
|
|
131
125
|
if (sessions.length > 1) {
|
|
132
|
-
console.
|
|
126
|
+
console.log(`\n 其他活跃会话:`);
|
|
133
127
|
sessions
|
|
134
128
|
.filter(s => s.id !== currentId)
|
|
135
129
|
.forEach(s => {
|
|
136
130
|
const number = s.number || '?';
|
|
137
131
|
const duration = Math.round((Date.now() - s.startedAt) / 1000 / 60);
|
|
138
|
-
console.
|
|
132
|
+
console.log(` - 会话 #${number}: ${s.identifier} (${duration}分钟前启动)`);
|
|
139
133
|
});
|
|
140
134
|
}
|
|
141
135
|
|
|
@@ -156,7 +150,7 @@ function main() {
|
|
|
156
150
|
if (eventType === 'UserPromptSubmit' && toolInput.length > 20) {
|
|
157
151
|
// 只在较长的用户输入时显示,避免噪音
|
|
158
152
|
if (sessions.length > 1 || process.env.SHOW_SESSION_STATUS) {
|
|
159
|
-
console.
|
|
153
|
+
console.log(`\n🔄 [会话 #${sessionNumber}] 活跃会话: ${sessions.length}\n`);
|
|
160
154
|
}
|
|
161
155
|
}
|
|
162
156
|
|
|
@@ -41,7 +41,8 @@ async function main() {
|
|
|
41
41
|
stdio: 'pipe'
|
|
42
42
|
}).trim() || 'origin/main';
|
|
43
43
|
|
|
44
|
-
|
|
44
|
+
// Use --diff-filter=ACMR to exclude deleted files (D)
|
|
45
|
+
const output = execSync(`git diff --name-only --diff-filter=ACMR ${upstream}...HEAD`, {
|
|
45
46
|
encoding: 'utf-8',
|
|
46
47
|
stdio: 'pipe'
|
|
47
48
|
});
|
|
@@ -85,7 +86,7 @@ async function main() {
|
|
|
85
86
|
|
|
86
87
|
const result = await gate.check({
|
|
87
88
|
files: checkable.map(f => path.join(projectDir, f)),
|
|
88
|
-
severity: '
|
|
89
|
+
severity: 'error' // Only block on errors, not warnings
|
|
89
90
|
});
|
|
90
91
|
|
|
91
92
|
if (!result.passed) {
|
|
@@ -9,23 +9,28 @@
|
|
|
9
9
|
* 功能:
|
|
10
10
|
* - 检查项目是否已启动
|
|
11
11
|
* - 如果未启动,提示 AI 进行项目规划
|
|
12
|
+
* - 生成规划文档后,自动创建任务到 backlog/
|
|
12
13
|
*/
|
|
13
14
|
|
|
14
15
|
const fs = require('fs');
|
|
15
16
|
const path = require('path');
|
|
17
|
+
const { execSync } = require('child_process');
|
|
16
18
|
|
|
17
19
|
const PROJECT_DIR = process.env.CLAUDE_PROJECT_DIR || process.cwd();
|
|
18
|
-
|
|
19
|
-
// 如果不在 Claude Code 环境中运行,静默退出
|
|
20
|
-
if (!process.env.CLAUDE_PROJECT_DIR) {
|
|
21
|
-
process.exit(0);
|
|
22
|
-
}
|
|
23
|
-
|
|
24
20
|
const TEMPLATES_DIR = path.join(PROJECT_DIR, '.claude/templates');
|
|
25
21
|
const KICKOFF_FILE = path.join(PROJECT_DIR, 'PROJECT_KICKOFF.md');
|
|
26
22
|
const PLAN_FILE = path.join(PROJECT_DIR, 'TASK_PLAN.md');
|
|
27
23
|
const PROPOSAL_FILE = path.join(PROJECT_DIR, 'PROJECT_PROPOSAL.md');
|
|
28
24
|
const HINT_FILE = path.join(PROJECT_DIR, '.claude/.kickoff-hint.txt');
|
|
25
|
+
const TODOS_DIR = path.join(PROJECT_DIR, 'development/todos');
|
|
26
|
+
const BACKLOG_DIR = path.join(TODOS_DIR, 'backlog');
|
|
27
|
+
|
|
28
|
+
// 任务类型图标
|
|
29
|
+
const TASK_TYPES = {
|
|
30
|
+
research: { icon: '📊', dir: 'research', name: 'Research' },
|
|
31
|
+
develop: { icon: '💻', dir: 'develop', name: 'Develop' },
|
|
32
|
+
test: { icon: '🧪', dir: 'test', name: 'Test' }
|
|
33
|
+
};
|
|
29
34
|
|
|
30
35
|
// 检查项目是否已启动
|
|
31
36
|
function isProjectStarted() {
|
|
@@ -41,6 +46,169 @@ function isForceKickoff() {
|
|
|
41
46
|
toolInput.includes('重新规划');
|
|
42
47
|
}
|
|
43
48
|
|
|
49
|
+
// 检查任务规划文档是否存在
|
|
50
|
+
function hasTaskPlan() {
|
|
51
|
+
return fs.existsSync(PLAN_FILE);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// 解析 TASK_PLAN.md 中的任务
|
|
55
|
+
function parseTasksFromPlan() {
|
|
56
|
+
if (!fs.existsSync(PLAN_FILE)) {
|
|
57
|
+
return { research: [], develop: [], test: [] };
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const content = fs.readFileSync(PLAN_FILE, 'utf-8');
|
|
61
|
+
const tasks = { research: [], develop: [], test: [] };
|
|
62
|
+
|
|
63
|
+
// 匹配任务项 - 支持 - [ ] 和 - [x] 格式
|
|
64
|
+
const taskRegex = /-\s*\[[ x]?\]\s*(?:\[([P0-3])\])?\s*(.+)/g;
|
|
65
|
+
let match;
|
|
66
|
+
|
|
67
|
+
// 当前任务类型上下文
|
|
68
|
+
let currentType = 'develop'; // 默认为开发任务
|
|
69
|
+
|
|
70
|
+
// 检测章节标题来确定任务类型
|
|
71
|
+
const lines = content.split('\n');
|
|
72
|
+
lines.forEach(line => {
|
|
73
|
+
const trimmed = line.trim();
|
|
74
|
+
|
|
75
|
+
// 检测章节标题
|
|
76
|
+
if (trimmed.includes('研究') || trimmed.includes('Research') || trimmed.includes('调研')) {
|
|
77
|
+
currentType = 'research';
|
|
78
|
+
} else if (trimmed.includes('开发') || trimmed.includes('Develop') || trimmed.includes('实现')) {
|
|
79
|
+
currentType = 'develop';
|
|
80
|
+
} else if (trimmed.includes('测试') || trimmed.includes('Test') || trimmed.includes('验证')) {
|
|
81
|
+
currentType = 'test';
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// 匹配任务项
|
|
85
|
+
const taskMatch = trimmed.match(/^-\s*\[([ x])\]\s*(?:\[([P0-3])\])?\s*(.+)/);
|
|
86
|
+
if (taskMatch) {
|
|
87
|
+
const [, checked, priority, title] = taskMatch;
|
|
88
|
+
tasks[currentType].push({
|
|
89
|
+
title: title.trim(),
|
|
90
|
+
priority: priority || 'P2',
|
|
91
|
+
checked: checked === 'x'
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
return tasks;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// 创建任务文件
|
|
100
|
+
function createTaskFile(task, type, index) {
|
|
101
|
+
const typeConfig = TASK_TYPES[type];
|
|
102
|
+
const slug = titleToSlug(task.title);
|
|
103
|
+
const fileName = `${index}-${slug}.md`;
|
|
104
|
+
const filePath = path.join(BACKLOG_DIR, typeConfig.dir, fileName);
|
|
105
|
+
const now = new Date().toISOString().split('T')[0];
|
|
106
|
+
|
|
107
|
+
// 读取对应模板
|
|
108
|
+
const templatePath = path.join(__dirname, '../../../development/todos/_templates', `${typeConfig.dir}.md`);
|
|
109
|
+
let template = '';
|
|
110
|
+
|
|
111
|
+
if (fs.existsSync(templatePath)) {
|
|
112
|
+
template = fs.readFileSync(templatePath, 'utf-8');
|
|
113
|
+
} else {
|
|
114
|
+
// 简化模板
|
|
115
|
+
template = `# ${task.title}
|
|
116
|
+
|
|
117
|
+
> **类型**: ${typeConfig.icon} ${typeConfig.name} | ${getTypeDescription(type)}
|
|
118
|
+
> **状态**: 待规划
|
|
119
|
+
> **优先级**: ${task.priority}
|
|
120
|
+
> **创建时间**: ${now}
|
|
121
|
+
> **来源**: PROJECT_KICKOFF
|
|
122
|
+
|
|
123
|
+
---
|
|
124
|
+
|
|
125
|
+
## 📋 任务描述
|
|
126
|
+
|
|
127
|
+
${task.title}
|
|
128
|
+
|
|
129
|
+
---
|
|
130
|
+
|
|
131
|
+
## 🎯 验收标准
|
|
132
|
+
|
|
133
|
+
- [ ] 验收标准 1
|
|
134
|
+
- [ ] 验收标准 2
|
|
135
|
+
|
|
136
|
+
---
|
|
137
|
+
|
|
138
|
+
## 📝 备注
|
|
139
|
+
|
|
140
|
+
来自项目启动规划。
|
|
141
|
+
`;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// 创建目录
|
|
145
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
146
|
+
|
|
147
|
+
// 写入任务文件
|
|
148
|
+
fs.writeFileSync(filePath, template);
|
|
149
|
+
|
|
150
|
+
return fileName;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// 将标题转换为文件名友好的 slug
|
|
154
|
+
function titleToSlug(title) {
|
|
155
|
+
return title
|
|
156
|
+
.toLowerCase()
|
|
157
|
+
.replace(/[^\u4e00-\u9fa5a-z0-9]+/g, '-')
|
|
158
|
+
.replace(/^-+|-+$/g, '')
|
|
159
|
+
.substring(0, 50);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// 获取任务类型描述
|
|
163
|
+
function getTypeDescription(type) {
|
|
164
|
+
const descriptions = {
|
|
165
|
+
research: '调研/设计/探索',
|
|
166
|
+
develop: '实现/编码/重构',
|
|
167
|
+
test: '测试/验证/QA'
|
|
168
|
+
};
|
|
169
|
+
return descriptions[type] || '';
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// 从规划创建任务到 backlog
|
|
173
|
+
function createTasksFromPlan() {
|
|
174
|
+
const tasks = parseTasksFromPlan();
|
|
175
|
+
let createdCount = 0;
|
|
176
|
+
|
|
177
|
+
// 确保目录存在
|
|
178
|
+
Object.values(TASK_TYPES).forEach(type => {
|
|
179
|
+
fs.mkdirSync(path.join(BACKLOG_DIR, type.dir), { recursive: true });
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
// 创建各类任务
|
|
183
|
+
Object.entries(tasks).forEach(([type, taskList]) => {
|
|
184
|
+
taskList.forEach((task, index) => {
|
|
185
|
+
if (!task.checked) { // 跳过已完成任务
|
|
186
|
+
const fileName = createTaskFile(task, type, index + 1);
|
|
187
|
+
createdCount++;
|
|
188
|
+
console.log(` ✅ 创建 ${type} 任务: ${fileName}`);
|
|
189
|
+
}
|
|
190
|
+
});
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
return createdCount;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// 刷新任务索引
|
|
197
|
+
function refreshTaskIndex() {
|
|
198
|
+
const todoManagerPath = path.join(PROJECT_DIR, '.claude/hooks/todo-manager.cjs');
|
|
199
|
+
if (fs.existsSync(todoManagerPath)) {
|
|
200
|
+
try {
|
|
201
|
+
execSync(`node "${todoManagerPath}" --force`, {
|
|
202
|
+
cwd: PROJECT_DIR,
|
|
203
|
+
stdio: 'pipe'
|
|
204
|
+
});
|
|
205
|
+
console.log(' ✅ 任务索引已更新');
|
|
206
|
+
} catch (e) {
|
|
207
|
+
// 忽略错误
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
44
212
|
// 生成启动提示
|
|
45
213
|
function generateKickoffHint() {
|
|
46
214
|
const now = new Date().toISOString().split('T')[0];
|
|
@@ -89,7 +257,8 @@ function generateKickoffHint() {
|
|
|
89
257
|
✅ 生成 PROJECT_KICKOFF.md
|
|
90
258
|
✅ 生成 TASK_PLAN.md
|
|
91
259
|
✅ 生成 PROJECT_PROPOSAL.md
|
|
92
|
-
✅
|
|
260
|
+
✅ 🆕 自动创建任务到 development/todos/backlog/
|
|
261
|
+
✅ 🆕 刷新任务索引
|
|
93
262
|
|
|
94
263
|
═══════════════════════════════════════════════════════════════════════
|
|
95
264
|
|
|
@@ -101,24 +270,33 @@ function generateKickoffHint() {
|
|
|
101
270
|
|
|
102
271
|
// 主函数
|
|
103
272
|
function main() {
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
}
|
|
273
|
+
// 如果项目已启动且不是强制模式,静默退出
|
|
274
|
+
if (isProjectStarted() && !isForceKickoff()) {
|
|
275
|
+
process.exit(0);
|
|
276
|
+
}
|
|
109
277
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
fs.writeFileSync(HINT_FILE, hint);
|
|
278
|
+
// 如果已经有任务规划,自动创建任务
|
|
279
|
+
if (hasTaskPlan()) {
|
|
280
|
+
console.log('📋 检测到 TASK_PLAN.md,正在创建任务...');
|
|
114
281
|
|
|
115
|
-
|
|
116
|
-
console.log(hint);
|
|
282
|
+
const createdCount = createTasksFromPlan();
|
|
117
283
|
|
|
118
|
-
|
|
119
|
-
|
|
284
|
+
if (createdCount > 0) {
|
|
285
|
+
console.log(`✅ 已创建 ${createdCount} 个任务到 backlog/`);
|
|
286
|
+
refreshTaskIndex();
|
|
287
|
+
} else {
|
|
288
|
+
console.log('ℹ️ 所有任务已完成或为空');
|
|
289
|
+
}
|
|
120
290
|
}
|
|
121
291
|
|
|
292
|
+
// 生成提示文件
|
|
293
|
+
const hint = generateKickoffHint();
|
|
294
|
+
fs.mkdirSync(path.dirname(HINT_FILE), { recursive: true });
|
|
295
|
+
fs.writeFileSync(HINT_FILE, hint);
|
|
296
|
+
|
|
297
|
+
// 同时输出到 stdout (供 AI 直接读取)
|
|
298
|
+
console.log(hint);
|
|
299
|
+
|
|
122
300
|
process.exit(0);
|
|
123
301
|
}
|
|
124
302
|
|
|
@@ -10,12 +10,6 @@ const fs = require('fs');
|
|
|
10
10
|
const path = require('path');
|
|
11
11
|
|
|
12
12
|
const PROJECT_DIR = process.env.CLAUDE_PROJECT_DIR || process.cwd();
|
|
13
|
-
|
|
14
|
-
// 如果不在 Claude Code 环境中运行,静默退出
|
|
15
|
-
if (!process.env.CLAUDE_PROJECT_DIR) {
|
|
16
|
-
process.exit(0);
|
|
17
|
-
}
|
|
18
|
-
|
|
19
13
|
const RAG_DIR = path.join(PROJECT_DIR, '.claude/rag');
|
|
20
14
|
const SKILL_INDEX_FILE = path.join(RAG_DIR, 'skill-index.json');
|
|
21
15
|
const SKILLS_DIR = path.join(PROJECT_DIR, '.claude/skills');
|
|
@@ -153,7 +147,6 @@ function main() {
|
|
|
153
147
|
// 将提示写入文件供 AI 阅读
|
|
154
148
|
const hint = generateSkillLoadHint(matches);
|
|
155
149
|
const hintFile = path.join(RAG_DIR, '.skill-hint.txt');
|
|
156
|
-
fs.mkdirSync(path.dirname(hintFile), { recursive: true });
|
|
157
150
|
fs.writeFileSync(hintFile, hint + '\n');
|
|
158
151
|
|
|
159
152
|
} catch (e) {
|
|
File without changes
|
|
File without changes
|
|
@@ -14,13 +14,7 @@ const fs = require('fs');
|
|
|
14
14
|
const path = require('path');
|
|
15
15
|
const { spawn } = require('child_process');
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
const PROJECT_DIR = process.env.CLAUDE_PROJECT_DIR || process.cwd();
|
|
19
|
-
if (!process.env.CLAUDE_PROJECT_DIR) {
|
|
20
|
-
process.exit(0);
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
const THINKING_DIR = path.join(PROJECT_DIR, '.claude/thinking-routes');
|
|
17
|
+
const THINKING_DIR = path.join(process.env.CLAUDE_PROJECT_DIR, '.claude/thinking-routes');
|
|
24
18
|
const ACTIVITY_FILE = path.join(THINKING_DIR, '.conversation-flow.json');
|
|
25
19
|
const SESSION_FILE = path.join(THINKING_DIR, '.current-session');
|
|
26
20
|
const SYNC_MARKER_FILE = path.join(THINKING_DIR, '.last-sync');
|
|
@@ -37,7 +31,7 @@ function getSession() {
|
|
|
37
31
|
|
|
38
32
|
if (!sessionId) {
|
|
39
33
|
const date = new Date().toISOString().split('T')[0].replace(/-/g, '');
|
|
40
|
-
const project = path.basename(
|
|
34
|
+
const project = path.basename(process.env.CLAUDE_PROJECT_DIR).slice(0, 10);
|
|
41
35
|
sessionId = `s-${date}-${project}`;
|
|
42
36
|
fs.writeFileSync(SESSION_FILE, sessionId);
|
|
43
37
|
}
|
|
@@ -81,7 +75,7 @@ function markSynced(turnCount) {
|
|
|
81
75
|
|
|
82
76
|
// 静默同步到 PROJECT_LOG.md
|
|
83
77
|
function syncToLog() {
|
|
84
|
-
const syncScript = path.join(
|
|
78
|
+
const syncScript = path.join(process.env.CLAUDE_PROJECT_DIR, '.claude/hooks/sync-to-log.sh');
|
|
85
79
|
if (fs.existsSync(syncScript)) {
|
|
86
80
|
try {
|
|
87
81
|
// 使用 spawn 静默执行,不输出任何内容
|