adspecs 0.1.23 → 0.1.25

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.
@@ -0,0 +1,15 @@
1
+ {
2
+ "$schema": "adspecs session logger configuration",
3
+ "version": 1,
4
+ "trigger_keywords": ["保存会话", "记录会话", "save session", "log session"],
5
+ "auto_triggers": {
6
+ "pre_compact": true,
7
+ "session_end": true,
8
+ "long_session_minutes": 30
9
+ },
10
+ "summary_max_chars": 200,
11
+ "max_artifacts_listed": 20,
12
+ "log_extension": ".log",
13
+ "filename_pattern": "YYYYMMDD-HHmmss_{module}_ai-session.log",
14
+ "recovery_suffix": "-recovery"
15
+ }
@@ -10,7 +10,7 @@
10
10
  {
11
11
  "name": "adspecs",
12
12
  "description": "AI Coding研发协同开发插件",
13
- "version": "0.1.23",
13
+ "version": "0.1.25",
14
14
  "source": "./",
15
15
  "author": {
16
16
  "name": "Qingwen Chen",
@@ -2,7 +2,7 @@
2
2
  "name": "adspecs",
3
3
  "displayName": "adspecs AI Plugin",
4
4
  "description": "AI first工程规范驱动开发插件",
5
- "version": "0.1.23",
5
+ "version": "0.1.25",
6
6
  "author": {
7
7
  "name": "Qingwen Chen",
8
8
  "email": "cqinwn@qq.com",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "adspecs",
3
3
  "displayName": "adspecs AI 工程规范驱动开发插件",
4
- "version": "0.1.23",
4
+ "version": "0.1.25",
5
5
  "description": "AI-first specification-driven development plugin for ECP platform full-lifecycle development",
6
6
  "descriptionZh": "AI优先的工程规范驱动开发插件,覆盖需求分析、PRD、系统设计、任务拆解、TDD、代码评审、Wiki同步全流程",
7
7
  "author": {
package/CLAUDE.md CHANGED
@@ -23,8 +23,9 @@ adspecs/
23
23
  │ ├── scripts/ # Helper scripts (bash, PowerShell, Python)
24
24
  │ ├── feature.json # Default feature tracking template (JSON, primary)
25
25
  │ ├── feature.yml # Feature tracking human-readable view (YAML, synced from JSON)
26
+ │ ├── session-logger.json # AI session logger trigger configuration (keywords, auto-triggers)
26
27
  │ └── project.json # Default project metadata template
27
- ├── skills/ # 19 skill definitions (each with SKILL.md)
28
+ ├── skills/ # 20 skill definitions (each with SKILL.md)
28
29
  │ ├── project-init/ # Project scaffolding
29
30
  │ ├── adspecs-biz-analysis/ # 7-step business requirement analysis
30
31
  │ ├── adspecs-new-requirement/ # Deep business requirement analysis + mock review
@@ -43,6 +44,7 @@ adspecs/
43
44
  │ ├── adspecs-utest/ # JUnit 5 + Mockito unit test code + reports
44
45
  │ ├── adspecs-export-word/ # Export Markdown to Word (.docx) with cover page + TOC
45
46
  │ ├── adspecs-update-status/ # Feature status update + tracking matrix output
47
+ │ ├── adspecs-save-session/ # Save AI session log to docs/80-development/800-log/
46
48
  │ ├── sie-front-code-review/ # React+TS+AntDesign code review
47
49
  │ └── wiki-update/ # Sync docs/ to wiki/ (full + incremental modes)
48
50
  ├── references/ # Reference projects & conventions copied during `/project-init`
@@ -101,12 +103,15 @@ Hooks are configured in `hooks/hooks.json` and run as Node.js scripts:
101
103
  | `SessionStart` | `session-start.js` | Injects `skills/using-saeplugin/SKILL.md` as agent context (no-op if skill doesn't exist) |
102
104
  | `SessionStart` | `wiki-queue.js init` | Initialize wiki update queue file in `.claude/` |
103
105
  | `SessionStart` | `commit-queue.js init` | Initialize commit candidates queue file in `.claude/` |
106
+ | `SessionStart` | `session-logger.js init` | Detect unsaved previous session → inject recovery reminder or normal guidance |
107
+ | `PreCompact` | `session-logger.js mark` | Context compaction imminent → inject urgent save reminder for Claude to write log |
104
108
  | `PostToolUse` (Write/Edit on code/doc files) | `wiki-queue.js enqueue` | Track changed files for wiki sync |
105
109
  | `PostToolUse` (Write on `pipeline-manifest.json`) | `commit-queue.js enqueue` | Track completed features for commit |
106
110
  | `Stop` | `wiki-queue.js report` | Suggest wiki sync at session end |
107
111
  | `Stop` | `commit-queue.js report` | Suggest commit at session end |
112
+ | `Stop` | `session-logger.js report`| Mark session as missed if log not saved → recovery at next SessionStart |
108
113
 
109
- Hook scripts use `__dirname` for path resolution (not CWD). Queue files stored in `.claude/` (`.commit-candidates`, `.wiki-update-queue`).
114
+ Hook scripts use `__dirname` for path resolution (not CWD). Queue files stored in `.claude/` (`.commit-candidates`, `.wiki-update-queue`, `.session-save-marker`).
110
115
 
111
116
  ## Path Configuration
112
117
 
@@ -126,6 +131,7 @@ Skills support configurable output directories via `.adspecs/paths.json` in the
126
131
  "prd_dir": "docs/20-prd",
127
132
  "design_dir": "docs/30-system-design",
128
133
  "test_report_dir": "docs/80-development",
134
+ "session_log_dir": "docs/80-development/800-log",
129
135
  "export_dir": "docs/90-export",
130
136
  "wiki_dir": "wiki"
131
137
  }
@@ -143,7 +149,8 @@ Skills support configurable output directories via `.adspecs/paths.json` in the
143
149
  | `prd_dir` | `docs/20-prd` | adspecs-prd, adspecs-plan (input), adspecs-front-spec (input), wiki-update (source) |
144
150
  | `design_dir` | `docs/30-system-design` | adspecs-plan (output), adspecs-tasks, adspecs-front-spec (output), adspecs-front-tasks, adspecs-utest (input), wiki-update (source) |
145
151
  | `test_report_dir` | `docs/80-development` | adspecs-utest (test reports) |
146
- | `export_dir` | `docs/90-export` | adspecs-export-word (Word 导出输出) |
152
+ | `session_log_dir` | `docs/80-development/800-log` | session-logger (AI session logs, triggered by keyword / PreCompact / session end) |
153
+ | `export_dir` | `docs/90-export` | adspecs-export-word (Word 导出输出) |
147
154
  | `wiki_dir` | `wiki` | wiki-update (output target) |
148
155
 
149
156
  `/project-init` generates this file automatically with default values. Users can edit it to customize output paths for their project structure.
@@ -178,7 +185,7 @@ Projects initialized by this plugin follow this layout:
178
185
  | ------------------------------ | -------------------------------------------------- |
179
186
  | `docs/20-prd/` | Requirements specs (PRD) |
180
187
  | `docs/30-system-design/` | System design + SQL + frontend design + task lists |
181
- | `docs/40-project_conventions/` | ECP coding standards |
188
+ | `docs/40-project_conventions/` | coding standards |
182
189
  | `.adspecs/templates/` | Standard document templates |
183
190
  | `web-ui/` | Frontend production project |
184
191
  | `service/` | Backend Java service modules |
@@ -192,6 +199,13 @@ This plugin requires the **superpowers** plugin for coding-phase skills:
192
199
  - `verification-before-completion` — Pre-completion verification
193
200
  - `finishing-a-development-branch` — Branch cleanup / PR creation
194
201
 
202
+ ## publish
203
+
204
+ ```bash
205
+ npm version patch --no-git-tag-version
206
+ npm publish --registry=https://registry.npmjs.org
207
+ ```
208
+
195
209
  ## Installation & Distribution
196
210
 
197
211
  ```bash
package/hooks/hooks.json CHANGED
@@ -14,6 +14,20 @@
14
14
  {
15
15
  "type": "command",
16
16
  "command": "node ${CLAUDE_PLUGIN_ROOT}/hooks/commit-queue.js init"
17
+ },
18
+ {
19
+ "type": "command",
20
+ "command": "node ${CLAUDE_PLUGIN_ROOT}/hooks/session-logger.js init"
21
+ }
22
+ ]
23
+ }
24
+ ],
25
+ "PreCompact": [
26
+ {
27
+ "hooks": [
28
+ {
29
+ "type": "command",
30
+ "command": "node ${CLAUDE_PLUGIN_ROOT}/hooks/session-logger.js mark"
17
31
  }
18
32
  ]
19
33
  }
@@ -55,6 +69,10 @@
55
69
  {
56
70
  "type": "command",
57
71
  "command": "node ${CLAUDE_PLUGIN_ROOT}/hooks/commit-queue.js report"
72
+ },
73
+ {
74
+ "type": "command",
75
+ "command": "node ${CLAUDE_PLUGIN_ROOT}/hooks/session-logger.js report"
58
76
  }
59
77
  ]
60
78
  }
@@ -0,0 +1,349 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Session Logger — AI 会话日志自动保存 Hook
5
+ *
6
+ * 在 SessionStart / PreCompact / Stop 三个事件中管理会话保存状态,
7
+ * 通过 additionalContext 引导 Claude 自行生成并写入结构化日志文件。
8
+ *
9
+ * 用法:
10
+ * node hooks/session-logger.js init — SessionStart: 检测恢复 / 注入引导
11
+ * node hooks/session-logger.js mark — PreCompact: 紧急保存提醒
12
+ * node hooks/session-logger.js report — Stop: 标记未保存 / 清理
13
+ */
14
+
15
+ const fs = require('fs');
16
+ const path = require('path');
17
+
18
+ // ── 路径解析 ──────────────────────────────────────────────
19
+ // 使用 process.cwd() 定位目标项目(不用 __dirname,因为 __dirname 指向插件目录)
20
+ const PROJECT_ROOT = process.cwd();
21
+ const CLAUDE_DIR = path.join(PROJECT_ROOT, '.claude');
22
+ const MARKER_FILE = path.join(CLAUDE_DIR, '.session-save-marker');
23
+ const CONFIG_FILE = path.join(PROJECT_ROOT, '.adspecs', 'session-logger.json');
24
+ const PATHS_FILE = path.join(PROJECT_ROOT, '.adspecs', 'paths.json');
25
+ const FEATURE_FILE = path.join(PROJECT_ROOT, '.adspecs', 'feature.json');
26
+
27
+ // ── 默认配置 ──────────────────────────────────────────────
28
+ const DEFAULT_CONFIG = {
29
+ trigger_keywords: ['保存会话', '记录会话', 'save session', 'log session'],
30
+ summary_max_chars: 200,
31
+ max_artifacts_listed: 20,
32
+ log_extension: '.log',
33
+ recovery_suffix: '-recovery',
34
+ };
35
+
36
+ const DEFAULT_LOG_DIR = 'docs/80-development/800-log';
37
+
38
+ // ── 工具函数 ──────────────────────────────────────────────
39
+
40
+ /** 安全读取 JSON 文件,解析失败或不存在返回 fallback */
41
+ function loadJson(filePath, fallback) {
42
+ if (!fs.existsSync(filePath)) {
43
+ return fallback;
44
+ }
45
+ try {
46
+ var raw = fs.readFileSync(filePath, 'utf8');
47
+ return JSON.parse(raw);
48
+ } catch (_err) {
49
+ return fallback;
50
+ }
51
+ }
52
+
53
+ /** 从 feature.json 的 active 字段提取模块 ID */
54
+ function resolveModuleId() {
55
+ var feature = loadJson(FEATURE_FILE, {});
56
+ var active = feature.active || '';
57
+
58
+ if (!active) {
59
+ return { id: 'GEN', name: 'generic' };
60
+ }
61
+
62
+ // 取路径末段(如 docs/20-prd/003-user-auth -> 003-user-auth)
63
+ var segments = active.split(/[\\/]/).filter(Boolean);
64
+ var lastSegment = segments.length > 0 ? segments[segments.length - 1] : '';
65
+
66
+ if (!lastSegment) {
67
+ return { id: 'GEN', name: 'generic' };
68
+ }
69
+
70
+ // 提取编号前缀(如 003-user-auth -> 003-user-auth)
71
+ var match = lastSegment.match(/^([a-z0-9]+(?:-[a-z0-9]+)*)/i);
72
+ if (match && match[1]) {
73
+ return { id: match[1], name: lastSegment };
74
+ }
75
+
76
+ return { id: lastSegment, name: lastSegment };
77
+ }
78
+
79
+ /** 读取日志输出目录 */
80
+ function resolveLogDir() {
81
+ var pathsConfig = loadJson(PATHS_FILE, {});
82
+ var paths = pathsConfig.paths || {};
83
+ return paths.session_log_dir || DEFAULT_LOG_DIR;
84
+ }
85
+
86
+ /** 读取用户配置 */
87
+ function loadConfig() {
88
+ var userConfig = loadJson(CONFIG_FILE, {});
89
+ // 合并默认配置(用户配置覆盖默认)
90
+ var merged = {};
91
+ Object.keys(DEFAULT_CONFIG).forEach(function (key) {
92
+ merged[key] = userConfig[key] !== undefined ? userConfig[key] : DEFAULT_CONFIG[key];
93
+ });
94
+ return merged;
95
+ }
96
+
97
+ /** 读取 marker 文件 */
98
+ function readMarker() {
99
+ if (!fs.existsSync(MARKER_FILE)) {
100
+ return null;
101
+ }
102
+ try {
103
+ var raw = fs.readFileSync(MARKER_FILE, 'utf8').trim();
104
+ if (!raw) return null;
105
+ // JSONL 格式,取最后一行(最新态)
106
+ var lines = raw.split(/\r?\n/).filter(Boolean);
107
+ return JSON.parse(lines[lines.length - 1]);
108
+ } catch (_err) {
109
+ return null;
110
+ }
111
+ }
112
+
113
+ /** 写入 marker 文件(覆盖写,单行最新态) */
114
+ function writeMarker(data) {
115
+ fs.mkdirSync(CLAUDE_DIR, { recursive: true });
116
+ fs.writeFileSync(MARKER_FILE, JSON.stringify(data) + '\n', 'utf8');
117
+ }
118
+
119
+ /** 清空 marker 文件 */
120
+ function clearMarker() {
121
+ if (fs.existsSync(MARKER_FILE)) {
122
+ fs.unlinkSync(MARKER_FILE);
123
+ }
124
+ }
125
+
126
+ /** 生成时间戳字符串 YYYYMMDD-HHmmss */
127
+ function formatTimestamp(date) {
128
+ var d = date || new Date();
129
+ var yyyy = d.getFullYear();
130
+ var MM = String(d.getMonth() + 1).padStart(2, '0');
131
+ var dd = String(d.getDate()).padStart(2, '0');
132
+ var HH = String(d.getHours()).padStart(2, '0');
133
+ var mm = String(d.getMinutes()).padStart(2, '0');
134
+ var ss = String(d.getSeconds()).padStart(2, '0');
135
+ return yyyy + MM + dd + '-' + HH + mm + ss;
136
+ }
137
+
138
+ /** 输出 additionalContext 到 stdout(供 Claude 读取) */
139
+ function outputContext(eventName, contextText) {
140
+ var output = {
141
+ hookSpecificOutput: {
142
+ hookEventName: eventName,
143
+ additionalContext: contextText,
144
+ },
145
+ };
146
+ process.stdout.write(JSON.stringify(output));
147
+ }
148
+
149
+ /** 计算两个 ISO 时间戳之间的分钟数 */
150
+ function elapsedMinutes(startISO) {
151
+ if (!startISO) return 0;
152
+ var start = new Date(startISO);
153
+ var now = new Date();
154
+ var diffMs = now - start;
155
+ return Math.round(diffMs / 60000);
156
+ }
157
+
158
+ // ── 子命令:init(SessionStart) ─────────────────────────
159
+
160
+ function cmdInit() {
161
+ var marker = readMarker();
162
+ var mod = resolveModuleId();
163
+ var logDir = resolveLogDir();
164
+ var config = loadConfig();
165
+
166
+ // 检测是否需要恢复
167
+ if (marker && marker.type === 'session_missed') {
168
+ var elapsed = elapsedMinutes(marker.startedAt);
169
+ var recoveryText = [
170
+ '<session-logger>',
171
+ '## ⚠️ 上次会话日志未保存',
172
+ '',
173
+ '上次会话:' + (marker.startedAt || '未知') + ' 启动,模块 ' + (marker.module || 'GEN') + ',已运行约 ' + elapsed + ' 分钟',
174
+ '日志未保存原因:会话异常结束或用户执行了 /clear',
175
+ '',
176
+ '### 恢复选项',
177
+ '- 确认恢复:我会生成 ' + logDir + '/' + formatTimestamp() + '_' + (marker.module || 'GEN') + '_ai-session' + config.recovery_suffix + config.log_extension,
178
+ '- 跳过恢复:回复"跳过恢复"或"skip recovery"',
179
+ '',
180
+ '恢复日志基于用户描述的上次会话要点生成,质量依赖用户输入。',
181
+ '</session-logger>',
182
+ ].join('\n');
183
+
184
+ // 写新 marker(覆盖旧的 session_missed)
185
+ writeMarker({
186
+ type: 'session_started',
187
+ module: mod.id,
188
+ moduleName: mod.name,
189
+ timestamp: new Date().toISOString(),
190
+ startedAt: new Date().toISOString(),
191
+ saved: false,
192
+ saveCount: 0,
193
+ compactRequestedAt: null,
194
+ missedAt: marker.missedAt || null,
195
+ recoveryFrom: marker,
196
+ });
197
+
198
+ outputContext('SessionStart', recoveryText);
199
+ console.log('[session-logger] Recovery reminder injected for module ' + (marker.module || 'GEN'));
200
+ return;
201
+ }
202
+
203
+ // 常规启动:注入引导
204
+ var keywords = config.trigger_keywords.join(' / ');
205
+ var guideText = [
206
+ '<session-logger>',
207
+ '## AI 会话日志保存指南',
208
+ '',
209
+ '当前模块:' + mod.id + ' (' + mod.name + ')',
210
+ '日志目录:' + logDir + '/',
211
+ '',
212
+ '### 何时保存(按优先级)',
213
+ '1. **关键词触发**(最高优先级):用户输入包含以下任一词时 → 立即保存:',
214
+ ' ' + keywords,
215
+ '2. **会话即将结束**:任务完成、用户表示结束 → 主动保存',
216
+ '3. **长会话节点**:每完成一个重要阶段(如系统设计完成、任务清单生成)→ 主动保存',
217
+ '4. **/clear 前**:用户表示要 /clear 时 → 建议先保存',
218
+ '',
219
+ '### 如何保存',
220
+ '使用 Write 工具写入:' + logDir + '/{YYYYMMDD-HHmmss}_' + mod.id + '_ai-session' + config.log_extension,
221
+ '内容遵循 adspecs-save-session SKILL.md 中的模板。',
222
+ '',
223
+ '### 注意',
224
+ '- Summary 必须实质性,禁止"本次会话讨论了需求"等空洞描述',
225
+ '- Key Artifacts 必须是真实存在的文件路径',
226
+ '- 同一会话多次保存产生多个文件(时间戳区分),第 N 次的 Summary 聚焦上次保存后的进展',
227
+ '- /clear 是 CLI 内建命令,不会触发 Stop hook,请在用户提到 /clear 时主动提醒保存',
228
+ '</session-logger>',
229
+ ].join('\n');
230
+
231
+ // 写新 marker
232
+ writeMarker({
233
+ type: 'session_started',
234
+ module: mod.id,
235
+ moduleName: mod.name,
236
+ timestamp: new Date().toISOString(),
237
+ startedAt: new Date().toISOString(),
238
+ saved: false,
239
+ saveCount: 0,
240
+ compactRequestedAt: null,
241
+ missedAt: null,
242
+ });
243
+
244
+ outputContext('SessionStart', guideText);
245
+ console.log('[session-logger] Session started for module ' + mod.id);
246
+ }
247
+
248
+ // ── 子命令:mark(PreCompact) ───────────────────────────
249
+
250
+ function cmdMark() {
251
+ var marker = readMarker();
252
+ var mod = resolveModuleId();
253
+ var logDir = resolveLogDir();
254
+ var config = loadConfig();
255
+ var now = new Date();
256
+ var timestamp = formatTimestamp(now);
257
+
258
+ // 更新 marker
259
+ var newMarker = {
260
+ type: 'compact_pending',
261
+ module: mod.id,
262
+ moduleName: mod.name,
263
+ timestamp: now.toISOString(),
264
+ startedAt: (marker && marker.startedAt) || now.toISOString(),
265
+ saved: false,
266
+ saveCount: (marker && marker.saveCount) || 0,
267
+ compactRequestedAt: now.toISOString(),
268
+ missedAt: null,
269
+ };
270
+ writeMarker(newMarker);
271
+
272
+ // 注入紧急提醒
273
+ var filename = timestamp + '_' + mod.id + '_ai-session' + config.log_extension;
274
+ var filePath = logDir + '/' + filename;
275
+
276
+ var urgentText = [
277
+ '<session-logger>',
278
+ '## 🚨 上下文即将压缩 — 立即保存会话日志',
279
+ '',
280
+ '这是**最高优先级操作**,请在继续其他任务之前完成:',
281
+ '',
282
+ '1. 使用 Write 工具写入:' + filePath,
283
+ '2. 内容遵循 adspecs-save-session SKILL.md 中的模板',
284
+ '3. Summary 聚焦' + (newMarker.saveCount > 0 ? '上次保存后的进展(第 ' + (newMarker.saveCount + 1) + ' 次保存)' : '本次会话的关键内容'),
285
+ '',
286
+ '完成后,marker 会自动更新。',
287
+ '</session-logger>',
288
+ ].join('\n');
289
+
290
+ outputContext('PreCompact', urgentText);
291
+ console.log('[session-logger] PreCompact: urgent save requested for ' + filename);
292
+ }
293
+
294
+ // ── 子命令:report(Stop) ───────────────────────────────
295
+
296
+ function cmdReport() {
297
+ var marker = readMarker();
298
+
299
+ if (!marker) {
300
+ // 无 marker,静默退出
301
+ return;
302
+ }
303
+
304
+ // 已保存 → 清空 marker
305
+ if (marker.saved === true) {
306
+ clearMarker();
307
+ console.log('[session-logger] Session log saved, marker cleared.');
308
+ return;
309
+ }
310
+
311
+ // 未保存 → 标记 session_missed
312
+ if (marker.type === 'session_started' || marker.type === 'compact_pending') {
313
+ marker.type = 'session_missed';
314
+ marker.missedAt = new Date().toISOString();
315
+ marker.timestamp = new Date().toISOString();
316
+ writeMarker(marker);
317
+
318
+ console.log('');
319
+ console.log('═══ [session-logger] ══════════════════════════════════');
320
+ console.log('[session-logger] WARNING: session log NOT saved');
321
+ console.log('[session-logger] Module: ' + (marker.module || 'GEN'));
322
+ console.log('[session-logger] Next SessionStart will offer recovery');
323
+ console.log('══════════════════════════════════════════════════════');
324
+ console.log('');
325
+ }
326
+ }
327
+
328
+ // ── 主入口 ────────────────────────────────────────────────
329
+
330
+ var action = process.argv[2];
331
+
332
+ switch (action) {
333
+ case 'init':
334
+ cmdInit();
335
+ break;
336
+
337
+ case 'mark':
338
+ cmdMark();
339
+ break;
340
+
341
+ case 'report':
342
+ cmdReport();
343
+ break;
344
+
345
+ default:
346
+ console.error('[session-logger] Unknown action: ' + (action || '(none)'));
347
+ console.error('[session-logger] Usage: node hooks/session-logger.js init|mark|report');
348
+ process.exit(1);
349
+ }
package/package.json CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "name": "adspecs",
3
- "version": "0.1.23",
3
+ "version": "0.1.25",
4
4
  "description": "AI first 工程规范驱动开发插件 — 支持 npm 安装和 CLI 初始化",
5
5
  "bin": {
6
- "adspecs": "./bin/adspecs.js"
6
+ "adspecs": "bin/adspecs.js"
7
7
  },
8
8
  "main": "./src/utils.js",
9
9
  "files": [