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,159 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* RAG Skill Loader - 动态技能感知加载器
|
|
4
|
+
*
|
|
5
|
+
* 基于用户任务内容,自动检测并加载相关技能
|
|
6
|
+
* 无感知运行,零打扰
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const fs = require('fs');
|
|
10
|
+
const path = require('path');
|
|
11
|
+
|
|
12
|
+
const PROJECT_DIR = process.env.CLAUDE_PROJECT_DIR || process.cwd();
|
|
13
|
+
const RAG_DIR = path.join(PROJECT_DIR, '.claude/rag');
|
|
14
|
+
const SKILL_INDEX_FILE = path.join(RAG_DIR, 'skill-index.json');
|
|
15
|
+
const SKILLS_DIR = path.join(PROJECT_DIR, '.claude/skills');
|
|
16
|
+
|
|
17
|
+
// 技能关键词匹配权重
|
|
18
|
+
const KEYWORD_WEIGHTS = {
|
|
19
|
+
exact: 1.0, // 完全匹配
|
|
20
|
+
partial: 0.8, // 部分匹配
|
|
21
|
+
related: 0.5 // 相关匹配
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
// 加载技能索引
|
|
25
|
+
function loadSkillIndex() {
|
|
26
|
+
if (!fs.existsSync(SKILL_INDEX_FILE)) {
|
|
27
|
+
return { skills: [], auto_load: { enabled: false } };
|
|
28
|
+
}
|
|
29
|
+
try {
|
|
30
|
+
return JSON.parse(fs.readFileSync(SKILL_INDEX_FILE, 'utf-8'));
|
|
31
|
+
} catch (e) {
|
|
32
|
+
return { skills: [], auto_load: { enabled: false } };
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// 分析用户输入,提取关键词
|
|
37
|
+
function analyzeInput(input) {
|
|
38
|
+
const toolName = process.env.CLAUDE_TOOL_NAME || '';
|
|
39
|
+
const lowerInput = input.toLowerCase();
|
|
40
|
+
|
|
41
|
+
// 关键词提取
|
|
42
|
+
const keywords = [
|
|
43
|
+
// 前端相关
|
|
44
|
+
'frontend', 'ui', 'react', 'vue', 'component', 'design', 'styling', 'css', 'tailwind',
|
|
45
|
+
// 文档相关
|
|
46
|
+
'document', 'docx', 'word', 'pdf', 'ppt', 'slide', 'spreadsheet', 'excel', 'xlsx',
|
|
47
|
+
// 艺术相关
|
|
48
|
+
'art', 'design', 'creative', 'poster', 'generative', 'algorithmic', 'canvas',
|
|
49
|
+
// 测试相关
|
|
50
|
+
'test', 'testing', 'e2e', 'automation', 'browser',
|
|
51
|
+
// Agent 相关
|
|
52
|
+
'agent', 'orchestration', 'workflow', 'multi-agent',
|
|
53
|
+
// 工具相关
|
|
54
|
+
'mcp', 'api', 'server', 'integration', 'cli'
|
|
55
|
+
];
|
|
56
|
+
|
|
57
|
+
const found = keywords.filter(k => lowerInput.includes(k));
|
|
58
|
+
return {
|
|
59
|
+
keywords: found,
|
|
60
|
+
toolName,
|
|
61
|
+
inputLength: input.length
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// 匹配技能
|
|
66
|
+
function matchSkills(analysis, skillIndex) {
|
|
67
|
+
const { keywords, toolName } = analysis;
|
|
68
|
+
const matches = [];
|
|
69
|
+
|
|
70
|
+
for (const skill of skillIndex.skills) {
|
|
71
|
+
let score = 0;
|
|
72
|
+
let matchedKeywords = [];
|
|
73
|
+
|
|
74
|
+
for (const keyword of keywords) {
|
|
75
|
+
for (const skillKeyword of skill.keywords) {
|
|
76
|
+
if (skillKeyword === keyword) {
|
|
77
|
+
score += KEYWORD_WEIGHTS.exact;
|
|
78
|
+
matchedKeywords.push(keyword);
|
|
79
|
+
} else if (skillKeyword.includes(keyword) || keyword.includes(skillKeyword)) {
|
|
80
|
+
score += KEYWORD_WEIGHTS.partial;
|
|
81
|
+
matchedKeywords.push(keyword);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// 工具名称匹配
|
|
87
|
+
if (toolName && skill.keywords.some(k => toolName.toLowerCase().includes(k))) {
|
|
88
|
+
score += KEYWORD_WEIGHTS.related;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (score > 0) {
|
|
92
|
+
matches.push({
|
|
93
|
+
name: skill.name,
|
|
94
|
+
score: score,
|
|
95
|
+
keywords: matchedKeywords,
|
|
96
|
+
description: skill.description
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// 按分数排序
|
|
102
|
+
return matches.sort((a, b) => b.score - a.score);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// 生成技能加载提示
|
|
106
|
+
function generateSkillLoadHint(matches) {
|
|
107
|
+
if (matches.length === 0) {
|
|
108
|
+
return '';
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const topMatches = matches.slice(0, 3);
|
|
112
|
+
const skillNames = topMatches.map(m => m.name).join(', ');
|
|
113
|
+
|
|
114
|
+
return `
|
|
115
|
+
🧠 [RAG] 检测到相关技能: ${skillNames}
|
|
116
|
+
|
|
117
|
+
使用方式: openskills read <skill-name>
|
|
118
|
+
|
|
119
|
+
匹配详情:
|
|
120
|
+
${topMatches.map(m => ` - ${m.name}: ${(m.score).toFixed(1)} (${m.keywords.join(', ')})`).join('\n')}
|
|
121
|
+
`;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// 主函数 - 完全静默,输出到文件
|
|
125
|
+
function main() {
|
|
126
|
+
try {
|
|
127
|
+
const skillIndex = loadSkillIndex();
|
|
128
|
+
if (!skillIndex.auto_load?.enabled) {
|
|
129
|
+
process.exit(0);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const toolInput = process.env.CLAUDE_TOOL_INPUT || '';
|
|
133
|
+
if (!toolInput || toolInput.length < 10) {
|
|
134
|
+
process.exit(0);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const analysis = analyzeInput(toolInput);
|
|
138
|
+
if (analysis.keywords.length === 0) {
|
|
139
|
+
process.exit(0);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const matches = matchSkills(analysis, skillIndex);
|
|
143
|
+
if (matches.length === 0) {
|
|
144
|
+
process.exit(0);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// 将提示写入文件供 AI 阅读
|
|
148
|
+
const hint = generateSkillLoadHint(matches);
|
|
149
|
+
const hintFile = path.join(RAG_DIR, '.skill-hint.txt');
|
|
150
|
+
fs.writeFileSync(hintFile, hint + '\n');
|
|
151
|
+
|
|
152
|
+
} catch (e) {
|
|
153
|
+
// 完全静默
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
process.exit(0);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
main();
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# 会话结束 Hook - 自动生成对话摘要并同步到 PROJECT_LOG.md
|
|
3
|
+
#
|
|
4
|
+
# 用法:在 shell 中 source 此文件
|
|
5
|
+
# source .claude/hooks/session-end.sh
|
|
6
|
+
#
|
|
7
|
+
# 或者添加到 ~/.zshrc 或 ~/.bashrc:
|
|
8
|
+
# source /path/to/project/.claude/hooks/session-end.sh
|
|
9
|
+
|
|
10
|
+
# 检测是否在项目目录中
|
|
11
|
+
CLAUDE_PROJECT_DIR="${CLAUDE_PROJECT_DIR:-$(pwd)}"
|
|
12
|
+
|
|
13
|
+
# 会话数据文件
|
|
14
|
+
THINKING_DIR="$CLAUDE_PROJECT_DIR/.claude/thinking-routes"
|
|
15
|
+
ACTIVITY_FILE="$THINKING_DIR/.conversation-flow.json"
|
|
16
|
+
PROJECT_LOG="$CLAUDE_PROJECT_DIR/.claude/PROJECT_LOG.md"
|
|
17
|
+
|
|
18
|
+
# 生成会话摘要并追加到 PROJECT_LOG.md
|
|
19
|
+
save_session_summary() {
|
|
20
|
+
# 只在项目目录内执行
|
|
21
|
+
if [[ ! -f "$ACTIVITY_FILE" ]]; then
|
|
22
|
+
return
|
|
23
|
+
fi
|
|
24
|
+
|
|
25
|
+
local today=$(date +%Y-%m-%d)
|
|
26
|
+
local time=$(date +%H:%M:%S)
|
|
27
|
+
local turn_count=0
|
|
28
|
+
|
|
29
|
+
# 读取对话流数据
|
|
30
|
+
if [[ -f "$ACTIVITY_FILE" ]]; then
|
|
31
|
+
turn_count=$(node -e "
|
|
32
|
+
const fs = require('fs');
|
|
33
|
+
try {
|
|
34
|
+
const flow = JSON.parse(fs.readFileSync('$ACTIVITY_FILE', 'utf-8'));
|
|
35
|
+
console.log(flow.turns ? flow.turns.length : 0);
|
|
36
|
+
} catch(e) {
|
|
37
|
+
console.log(0);
|
|
38
|
+
}
|
|
39
|
+
" 2>/dev/null || echo "0")
|
|
40
|
+
fi
|
|
41
|
+
|
|
42
|
+
# 只在有对话记录时保存
|
|
43
|
+
if [[ "$turn_count" -gt 0 ]]; then
|
|
44
|
+
# 追加到 PROJECT_LOG.md
|
|
45
|
+
cat >> "$PROJECT_LOG" << EOF
|
|
46
|
+
|
|
47
|
+
## $today $time - 会话结束
|
|
48
|
+
- 对话轮次: $turn_count
|
|
49
|
+
- 会话文件: .claude/thinking-routes/.conversation-flow.json
|
|
50
|
+
- 查看摘要: 运行 \`.claude/hooks/tl-summary.sh\`
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
EOF
|
|
54
|
+
fi
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
# 注册退出时的处理
|
|
58
|
+
trap 'save_session_summary' EXIT INT TERM
|
|
59
|
+
|
|
60
|
+
# 导出函数供手动调用
|
|
61
|
+
export -f save_session_summary
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# 对话摘要同步脚本 - 将对话摘要追加到 PROJECT_LOG.md
|
|
3
|
+
# 用法: .claude/hooks/sync-to-log.sh
|
|
4
|
+
|
|
5
|
+
set -e
|
|
6
|
+
|
|
7
|
+
# 自动检测项目目录
|
|
8
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
9
|
+
CLAUDE_PROJECT_DIR="${CLAUDE_PROJECT_DIR:-$(cd "$SCRIPT_DIR/../.." && pwd)}"
|
|
10
|
+
|
|
11
|
+
THINKING_DIR="$CLAUDE_PROJECT_DIR/.claude/thinking-routes"
|
|
12
|
+
ACTIVITY_FILE="$THINKING_DIR/.conversation-flow.json"
|
|
13
|
+
PROJECT_LOG="$CLAUDE_PROJECT_DIR/.claude/PROJECT_LOG.md"
|
|
14
|
+
SESSION_FILE="$THINKING_DIR/.current-session"
|
|
15
|
+
|
|
16
|
+
# 确保目录存在
|
|
17
|
+
mkdir -p "$THINKING_DIR"
|
|
18
|
+
|
|
19
|
+
# 获取对话数据
|
|
20
|
+
if [[ ! -f "$ACTIVITY_FILE" ]]; then
|
|
21
|
+
echo "❌ 没有对话记录"
|
|
22
|
+
exit 0
|
|
23
|
+
fi
|
|
24
|
+
|
|
25
|
+
# 生成摘要
|
|
26
|
+
SUMMARY=$(node -e "
|
|
27
|
+
const fs = require('fs');
|
|
28
|
+
const flow = JSON.parse(fs.readFileSync('$ACTIVITY_FILE', 'utf-8'));
|
|
29
|
+
|
|
30
|
+
const turns = flow.turns || [];
|
|
31
|
+
const today = new Date().toISOString().split('T')[0];
|
|
32
|
+
|
|
33
|
+
// 统计今天的对话
|
|
34
|
+
const todayTurns = turns.filter(t => t.time && t.time.startsWith(today));
|
|
35
|
+
|
|
36
|
+
// 统计工具使用
|
|
37
|
+
const toolCounts = {};
|
|
38
|
+
todayTurns.forEach(t => {
|
|
39
|
+
const tool = t.toolName || 'unknown';
|
|
40
|
+
toolCounts[tool] = (toolCounts[tool] || 0) + 1;
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
// 生成工具使用统计
|
|
44
|
+
const toolStats = Object.entries(toolCounts)
|
|
45
|
+
.sort((a, b) => b[1] - a[1])
|
|
46
|
+
.slice(0, 5)
|
|
47
|
+
.map(([tool, count]) => \` - \${tool}: \${count}\`)
|
|
48
|
+
.join('\\n');
|
|
49
|
+
|
|
50
|
+
// 获取最近活动
|
|
51
|
+
const recent = todayTurns.slice(-3).map(t => {
|
|
52
|
+
const time = t.time ? t.time.split('T')[1].split(':')[0] + ':' + t.time.split('T')[1].split(':')[1] : '??:??';
|
|
53
|
+
return \` - \${time} \${t.toolName || 'unknown'}\`;
|
|
54
|
+
}).join('\\n');
|
|
55
|
+
|
|
56
|
+
console.log('## ' + today + ' - 对话摘要');
|
|
57
|
+
console.log('');
|
|
58
|
+
console.log('### 统计');
|
|
59
|
+
console.log('- 对话轮次: ' + todayTurns.length);
|
|
60
|
+
console.log('- 会话 ID: ' + (flow.sessionId || 'unknown'));
|
|
61
|
+
console.log('');
|
|
62
|
+
console.log('### 工具使用');
|
|
63
|
+
console.log(toolStats || ' (无)');
|
|
64
|
+
console.log('');
|
|
65
|
+
console.log('### 最近活动');
|
|
66
|
+
console.log(recent || ' (无)');
|
|
67
|
+
console.log('');
|
|
68
|
+
console.log('---');
|
|
69
|
+
" 2>/dev/null || echo "生成摘要失败")
|
|
70
|
+
|
|
71
|
+
# 追加到 PROJECT_LOG.md
|
|
72
|
+
if [[ -n "$SUMMARY" ]]; then
|
|
73
|
+
# 检查今天是否已有记录
|
|
74
|
+
today=$(date +%Y-%m-%d)
|
|
75
|
+
if grep -q "^## $today" "$PROJECT_LOG" 2>/dev/null; then
|
|
76
|
+
# 更新今天的记录(删除旧记录,追加新的)
|
|
77
|
+
# 简单处理:直接追加
|
|
78
|
+
echo "$SUMMARY" >> "$PROJECT_LOG"
|
|
79
|
+
else
|
|
80
|
+
echo "$SUMMARY" >> "$PROJECT_LOG"
|
|
81
|
+
fi
|
|
82
|
+
echo "✅ 已同步到 PROJECT_LOG.md"
|
|
83
|
+
fi
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* ThinkingLens 无感知追踪
|
|
4
|
+
*
|
|
5
|
+
* 特点:
|
|
6
|
+
* - 完全静默,0 输出
|
|
7
|
+
* - 自动追踪对话流
|
|
8
|
+
* - 智能判断重要时刻
|
|
9
|
+
* - 后台运行,不打扰
|
|
10
|
+
* - 定期同步到 PROJECT_LOG.md(每 20 轮)
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
const fs = require('fs');
|
|
14
|
+
const path = require('path');
|
|
15
|
+
const { spawn } = require('child_process');
|
|
16
|
+
|
|
17
|
+
const THINKING_DIR = path.join(process.env.CLAUDE_PROJECT_DIR, '.claude/thinking-routes');
|
|
18
|
+
const ACTIVITY_FILE = path.join(THINKING_DIR, '.conversation-flow.json');
|
|
19
|
+
const SESSION_FILE = path.join(THINKING_DIR, '.current-session');
|
|
20
|
+
const SYNC_MARKER_FILE = path.join(THINKING_DIR, '.last-sync');
|
|
21
|
+
const SYNC_INTERVAL = 20; // 每 20 轮同步一次
|
|
22
|
+
|
|
23
|
+
// 确保目录存在
|
|
24
|
+
try { fs.mkdirSync(THINKING_DIR, { recursive: true }); } catch (e) {}
|
|
25
|
+
|
|
26
|
+
// 获取当前会话
|
|
27
|
+
function getSession() {
|
|
28
|
+
let sessionId = fs.existsSync(SESSION_FILE)
|
|
29
|
+
? fs.readFileSync(SESSION_FILE, 'utf-8').trim()
|
|
30
|
+
: null;
|
|
31
|
+
|
|
32
|
+
if (!sessionId) {
|
|
33
|
+
const date = new Date().toISOString().split('T')[0].replace(/-/g, '');
|
|
34
|
+
const project = path.basename(process.env.CLAUDE_PROJECT_DIR).slice(0, 10);
|
|
35
|
+
sessionId = `s-${date}-${project}`;
|
|
36
|
+
fs.writeFileSync(SESSION_FILE, sessionId);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return sessionId;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// 获取对话流数据
|
|
43
|
+
function getConversationFlow() {
|
|
44
|
+
if (!fs.existsSync(ACTIVITY_FILE)) {
|
|
45
|
+
return { sessionId: getSession(), turns: [], startTime: new Date().toISOString() };
|
|
46
|
+
}
|
|
47
|
+
try {
|
|
48
|
+
return JSON.parse(fs.readFileSync(ACTIVITY_FILE, 'utf-8'));
|
|
49
|
+
} catch (e) {
|
|
50
|
+
return { sessionId: getSession(), turns: [], startTime: new Date().toISOString() };
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// 保存对话流
|
|
55
|
+
function saveConversationFlow(data) {
|
|
56
|
+
// 只保留最近 100 条
|
|
57
|
+
if (data.turns.length > 100) {
|
|
58
|
+
data.turns = data.turns.slice(-100);
|
|
59
|
+
}
|
|
60
|
+
fs.writeFileSync(ACTIVITY_FILE, JSON.stringify(data, null, 2));
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// 检查是否需要同步到日志
|
|
64
|
+
function shouldSync(turnCount) {
|
|
65
|
+
const lastSync = fs.existsSync(SYNC_MARKER_FILE)
|
|
66
|
+
? parseInt(fs.readFileSync(SYNC_MARKER_FILE, 'utf-8'), 10)
|
|
67
|
+
: 0;
|
|
68
|
+
return turnCount - lastSync >= SYNC_INTERVAL;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// 标记已同步
|
|
72
|
+
function markSynced(turnCount) {
|
|
73
|
+
fs.writeFileSync(SYNC_MARKER_FILE, turnCount.toString());
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// 静默同步到 PROJECT_LOG.md
|
|
77
|
+
function syncToLog() {
|
|
78
|
+
const syncScript = path.join(process.env.CLAUDE_PROJECT_DIR, '.claude/hooks/sync-to-log.sh');
|
|
79
|
+
if (fs.existsSync(syncScript)) {
|
|
80
|
+
try {
|
|
81
|
+
// 使用 spawn 静默执行,不输出任何内容
|
|
82
|
+
spawn('bash', [syncScript], {
|
|
83
|
+
stdio: 'ignore',
|
|
84
|
+
detached: true
|
|
85
|
+
}).unref();
|
|
86
|
+
} catch (e) {
|
|
87
|
+
// 静默忽略错误
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// 检测是否是关键操作
|
|
93
|
+
function detectKeyOperation(toolInput, toolName) {
|
|
94
|
+
const keywords = [
|
|
95
|
+
'完成', '实现', '创建', '添加', '修复', '更新',
|
|
96
|
+
'完成', '测试', '部署', '推送', '设计', '决定'
|
|
97
|
+
];
|
|
98
|
+
|
|
99
|
+
// 如果包含关键词,可能是关键操作
|
|
100
|
+
if (keywords.some(k => toolInput.includes(k))) {
|
|
101
|
+
return 'potential-action';
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// 如果是代码编辑
|
|
105
|
+
if (toolName === 'Edit' || toolName === 'Write') {
|
|
106
|
+
return 'code-edit';
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return 'normal';
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// 主函数 - 完全静默
|
|
113
|
+
function main() {
|
|
114
|
+
try {
|
|
115
|
+
const sessionId = getSession();
|
|
116
|
+
const flow = getConversationFlow();
|
|
117
|
+
const toolInput = process.env.CLAUDE_TOOL_INPUT || '';
|
|
118
|
+
const toolName = process.env.CLAUDE_TOOL_NAME || '';
|
|
119
|
+
|
|
120
|
+
// 添加新的对话轮次
|
|
121
|
+
flow.turns.push({
|
|
122
|
+
time: new Date().toISOString(),
|
|
123
|
+
toolName,
|
|
124
|
+
inputLength: toolInput.length,
|
|
125
|
+
type: detectKeyOperation(toolInput, toolName),
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
saveConversationFlow(flow);
|
|
129
|
+
|
|
130
|
+
// 检查是否需要同步到日志
|
|
131
|
+
const turnCount = flow.turns.length;
|
|
132
|
+
if (shouldSync(turnCount)) {
|
|
133
|
+
syncToLog();
|
|
134
|
+
markSynced(turnCount);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
} catch (e) {
|
|
138
|
+
// 完全静默,忽略任何错误
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// 永远不输出任何内容
|
|
142
|
+
process.exit(0);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
main();
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# tl-summary - 查看今日对话摘要
|
|
3
|
+
#
|
|
4
|
+
# 用法:
|
|
5
|
+
# .claude/hooks/tl-summary.sh
|
|
6
|
+
|
|
7
|
+
# 自动检测项目目录
|
|
8
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
9
|
+
CLAUDE_PROJECT_DIR="${CLAUDE_PROJECT_DIR:-$(cd "$SCRIPT_DIR/../.." && pwd)}"
|
|
10
|
+
|
|
11
|
+
THINKING_DIR="$CLAUDE_PROJECT_DIR/.claude/thinking-routes"
|
|
12
|
+
FLOW_FILE="$THINKING_DIR/.conversation-flow.json"
|
|
13
|
+
|
|
14
|
+
if [[ ! -f "$FLOW_FILE" ]]; then
|
|
15
|
+
echo "📭 暂无对话记录"
|
|
16
|
+
exit 0
|
|
17
|
+
fi
|
|
18
|
+
|
|
19
|
+
# 解析 JSON(使用 Node.js)
|
|
20
|
+
node -e "
|
|
21
|
+
const fs = require('fs');
|
|
22
|
+
const flow = JSON.parse(fs.readFileSync('$FLOW_FILE', 'utf-8'));
|
|
23
|
+
|
|
24
|
+
console.log('');
|
|
25
|
+
console.log('📅 今日对话摘要');
|
|
26
|
+
console.log('─'.repeat(40));
|
|
27
|
+
console.log('会话 ID: ' + flow.sessionId);
|
|
28
|
+
console.log('开始时间: ' + new Date(flow.startTime).toLocaleString('zh-CN'));
|
|
29
|
+
console.log('对话轮次: ' + flow.turns.length);
|
|
30
|
+
console.log('');
|
|
31
|
+
|
|
32
|
+
// 统计操作类型
|
|
33
|
+
const types = {};
|
|
34
|
+
flow.turns.forEach(t => {
|
|
35
|
+
types[t.type] = (types[t.type] || 0) + 1;
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
console.log('📊 操作统计:');
|
|
39
|
+
Object.entries(types).forEach(([type, count]) => {
|
|
40
|
+
const emoji = type === 'potential-action' ? '⚡' :
|
|
41
|
+
type === 'code-edit' ? '📝' : '💬';
|
|
42
|
+
console.log(' ' + emoji + ' ' + type + ': ' + count);
|
|
43
|
+
});
|
|
44
|
+
console.log('');
|
|
45
|
+
|
|
46
|
+
// 显示最近 5 个操作
|
|
47
|
+
console.log('🕐 最近活动:');
|
|
48
|
+
flow.turns.slice(-5).reverse().forEach(t => {
|
|
49
|
+
const time = new Date(t.time).toLocaleTimeString('zh-CN', { hour: '2-digit', minute: '2-digit' });
|
|
50
|
+
const emoji = t.type === 'potential-action' ? '⚡' :
|
|
51
|
+
t.type === 'code-edit' ? '📝' : '💬';
|
|
52
|
+
console.log(' ' + time + ' ' + emoji + ' ' + t.toolName);
|
|
53
|
+
});
|
|
54
|
+
"
|