@zhuan-ai/zhuanspec 2.17.0 → 2.17.2
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/dist/core/hooks/knowledge-usage.d.ts +29 -0
- package/dist/core/hooks/knowledge-usage.js +160 -11
- package/dist/core/init.js +1 -1
- package/dist/core/metrics/knowledge-funnel.d.ts +2 -0
- package/dist/core/metrics/knowledge-funnel.js +9 -0
- package/dist/core/templates/codex-hooks-template.js +1 -1
- package/dist/core/templates/slash-command-templates.js +18 -1
- package/package.json +20 -22
|
@@ -14,6 +14,7 @@ interface UsageCounter {
|
|
|
14
14
|
refCount: number;
|
|
15
15
|
lastRefAt: string;
|
|
16
16
|
}
|
|
17
|
+
type ProjectWikiRefSource = 'file' | 'llmwiki' | string;
|
|
17
18
|
/** usage.json 落点结构:zhuanspec/knowledge/.metrics/usage.json */
|
|
18
19
|
export interface KnowledgeUsageStats {
|
|
19
20
|
schemaVersion: 1;
|
|
@@ -26,6 +27,10 @@ export interface KnowledgeUsageStats {
|
|
|
26
27
|
byFile: Record<string, UsageCounter>;
|
|
27
28
|
/** 键 = 相对 ZhuanSpec 根的 ProjectWiki 路径,如 service/.project-wiki/project.md */
|
|
28
29
|
byProjectWikiFile: Record<string, UsageCounter>;
|
|
30
|
+
/** 键 = ProjectWiki 引用来源,如 file / llmwiki */
|
|
31
|
+
projectWikiRefsBySource: Record<string, UsageCounter>;
|
|
32
|
+
/** 键 = ProjectWiki 文件路径,值 = 该文件按来源拆分的引用计数 */
|
|
33
|
+
byProjectWikiFileSource: Record<string, Record<string, UsageCounter>>;
|
|
29
34
|
lastUpdatedAt: string;
|
|
30
35
|
}
|
|
31
36
|
/** 单次上报输入(清晰可复用的上报接口契约) */
|
|
@@ -35,6 +40,22 @@ export interface KnowledgeRefEvent {
|
|
|
35
40
|
/** 引用时间,缺省取 getBeijingTime() */
|
|
36
41
|
refAt?: string;
|
|
37
42
|
}
|
|
43
|
+
export interface ProjectWikiEffectiveRef {
|
|
44
|
+
/** llmwiki 返回的 ProjectWiki 文档路径,支持绝对路径或相对 ZhuanSpec 根路径 */
|
|
45
|
+
path: string;
|
|
46
|
+
/** llmwiki 召回分数,仅用于后续分析,不参与是否计数的判定 */
|
|
47
|
+
score?: number;
|
|
48
|
+
/** 调用方判定该文档为有效引用的简要原因 */
|
|
49
|
+
reason?: string;
|
|
50
|
+
}
|
|
51
|
+
export interface ProjectWikiRefEvent {
|
|
52
|
+
source?: ProjectWikiRefSource;
|
|
53
|
+
query?: string;
|
|
54
|
+
project?: string;
|
|
55
|
+
effectiveRefs: ProjectWikiEffectiveRef[];
|
|
56
|
+
/** 引用时间,缺省取 getBeijingTime() */
|
|
57
|
+
refAt?: string;
|
|
58
|
+
}
|
|
38
59
|
interface KnowledgeUsageOptions {
|
|
39
60
|
json?: boolean;
|
|
40
61
|
file?: string;
|
|
@@ -43,6 +64,14 @@ interface KnowledgeUsageOptions {
|
|
|
43
64
|
export declare function getUsagePath(knowledgeDir: string): string;
|
|
44
65
|
/** 读取 usage.json,缺失/损坏均返回空结构(容错,不抛) */
|
|
45
66
|
export declare function readUsageStats(knowledgeDir: string): Promise<KnowledgeUsageStats>;
|
|
67
|
+
export declare function extractLlmwikiEffectiveRefsFromHookInput(stdinData: Record<string, unknown>): ProjectWikiEffectiveRef[];
|
|
68
|
+
/**
|
|
69
|
+
* 上报 llmwiki 召回后被实际使用的 ProjectWiki 文档。
|
|
70
|
+
* 只统计调用方传入的 effectiveRefs,不统计全部召回结果;同一次事件内同一路径去重。
|
|
71
|
+
*
|
|
72
|
+
* @returns 实际累加的唯一文档数
|
|
73
|
+
*/
|
|
74
|
+
export declare function reportProjectWikiRefs(knowledgeDir: string, event: ProjectWikiRefEvent): Promise<number>;
|
|
46
75
|
/**
|
|
47
76
|
* 清晰可复用的上报接口:累加一次引用。
|
|
48
77
|
* - filePath 未命中 knowledge/ 三类目录 → no-op 返回 false。
|
|
@@ -28,6 +28,8 @@ function emptyUsage() {
|
|
|
28
28
|
byId: {},
|
|
29
29
|
byFile: {},
|
|
30
30
|
byProjectWikiFile: {},
|
|
31
|
+
projectWikiRefsBySource: {},
|
|
32
|
+
byProjectWikiFileSource: {},
|
|
31
33
|
lastUpdatedAt: '',
|
|
32
34
|
};
|
|
33
35
|
}
|
|
@@ -48,6 +50,12 @@ export async function readUsageStats(knowledgeDir) {
|
|
|
48
50
|
byProjectWikiFile: parsed.byProjectWikiFile && typeof parsed.byProjectWikiFile === 'object'
|
|
49
51
|
? parsed.byProjectWikiFile
|
|
50
52
|
: {},
|
|
53
|
+
projectWikiRefsBySource: parsed.projectWikiRefsBySource && typeof parsed.projectWikiRefsBySource === 'object'
|
|
54
|
+
? parsed.projectWikiRefsBySource
|
|
55
|
+
: {},
|
|
56
|
+
byProjectWikiFileSource: parsed.byProjectWikiFileSource && typeof parsed.byProjectWikiFileSource === 'object'
|
|
57
|
+
? parsed.byProjectWikiFileSource
|
|
58
|
+
: {},
|
|
51
59
|
lastUpdatedAt: typeof parsed.lastUpdatedAt === 'string' ? parsed.lastUpdatedAt : '',
|
|
52
60
|
};
|
|
53
61
|
}
|
|
@@ -95,6 +103,143 @@ function matchProjectWikiFile(repoRoot, filePath) {
|
|
|
95
103
|
return null;
|
|
96
104
|
return segments.join('/');
|
|
97
105
|
}
|
|
106
|
+
function normalizeProjectWikiFile(repoRoot, filePath) {
|
|
107
|
+
if (!filePath)
|
|
108
|
+
return null;
|
|
109
|
+
if (path.isAbsolute(filePath)) {
|
|
110
|
+
return matchProjectWikiFile(repoRoot, filePath);
|
|
111
|
+
}
|
|
112
|
+
return matchProjectWikiFile(repoRoot, path.join(repoRoot, filePath));
|
|
113
|
+
}
|
|
114
|
+
function bump(counter, refAt) {
|
|
115
|
+
return {
|
|
116
|
+
refCount: (counter?.refCount ?? 0) + 1,
|
|
117
|
+
lastRefAt: refAt,
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
function bumpProjectWikiRef(usage, relPath, source, refAt) {
|
|
121
|
+
usage.byProjectWikiFile[relPath] = bump(usage.byProjectWikiFile[relPath], refAt);
|
|
122
|
+
usage.projectWikiRefsBySource[source] = bump(usage.projectWikiRefsBySource[source], refAt);
|
|
123
|
+
usage.byProjectWikiFileSource[relPath] = usage.byProjectWikiFileSource[relPath] || {};
|
|
124
|
+
usage.byProjectWikiFileSource[relPath][source] = bump(usage.byProjectWikiFileSource[relPath][source], refAt);
|
|
125
|
+
usage.projectWikiTotalRefs += 1;
|
|
126
|
+
}
|
|
127
|
+
function isObject(value) {
|
|
128
|
+
return !!value && typeof value === 'object' && !Array.isArray(value);
|
|
129
|
+
}
|
|
130
|
+
function toEffectiveRefs(value) {
|
|
131
|
+
if (!Array.isArray(value))
|
|
132
|
+
return [];
|
|
133
|
+
const refs = [];
|
|
134
|
+
for (const item of value) {
|
|
135
|
+
if (typeof item === 'string') {
|
|
136
|
+
refs.push({ path: item });
|
|
137
|
+
continue;
|
|
138
|
+
}
|
|
139
|
+
if (!isObject(item))
|
|
140
|
+
continue;
|
|
141
|
+
const refPath = typeof item.path === 'string'
|
|
142
|
+
? item.path
|
|
143
|
+
: typeof item.file_path === 'string'
|
|
144
|
+
? item.file_path
|
|
145
|
+
: '';
|
|
146
|
+
if (!refPath)
|
|
147
|
+
continue;
|
|
148
|
+
refs.push({
|
|
149
|
+
path: refPath,
|
|
150
|
+
score: typeof item.score === 'number' ? item.score : undefined,
|
|
151
|
+
reason: typeof item.reason === 'string' ? item.reason : undefined,
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
return refs;
|
|
155
|
+
}
|
|
156
|
+
function extractProjectWikiRefEvent(stdinData, toolInput) {
|
|
157
|
+
const candidates = [stdinData, toolInput].filter(Boolean);
|
|
158
|
+
for (const candidate of candidates) {
|
|
159
|
+
const refs = toEffectiveRefs(candidate.projectWikiEffectiveRefs
|
|
160
|
+
?? candidate.project_wiki_effective_refs
|
|
161
|
+
?? candidate.effectiveRefs
|
|
162
|
+
?? candidate.effective_refs);
|
|
163
|
+
if (refs.length === 0)
|
|
164
|
+
continue;
|
|
165
|
+
return {
|
|
166
|
+
source: typeof candidate.source === 'string' ? candidate.source : 'llmwiki',
|
|
167
|
+
query: typeof candidate.query === 'string' ? candidate.query : undefined,
|
|
168
|
+
project: typeof candidate.project === 'string' ? candidate.project : undefined,
|
|
169
|
+
effectiveRefs: refs,
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
return null;
|
|
173
|
+
}
|
|
174
|
+
function collectStringValues(value, output = []) {
|
|
175
|
+
if (typeof value === 'string') {
|
|
176
|
+
output.push(value);
|
|
177
|
+
return output;
|
|
178
|
+
}
|
|
179
|
+
if (Array.isArray(value)) {
|
|
180
|
+
for (const item of value)
|
|
181
|
+
collectStringValues(item, output);
|
|
182
|
+
return output;
|
|
183
|
+
}
|
|
184
|
+
if (isObject(value)) {
|
|
185
|
+
for (const item of Object.values(value))
|
|
186
|
+
collectStringValues(item, output);
|
|
187
|
+
}
|
|
188
|
+
return output;
|
|
189
|
+
}
|
|
190
|
+
function extractProjectWikiPaths(text) {
|
|
191
|
+
const matches = text.match(/[^\s"'`<>]*\.project-wiki\/[^\s"'`<>]*?\.md/g) || [];
|
|
192
|
+
return matches
|
|
193
|
+
.map((item) => item.replace(/[),.;:,。;:]+$/g, ''))
|
|
194
|
+
.filter(Boolean);
|
|
195
|
+
}
|
|
196
|
+
export function extractLlmwikiEffectiveRefsFromHookInput(stdinData) {
|
|
197
|
+
const toolInput = stdinData.tool_input;
|
|
198
|
+
const command = typeof toolInput?.command === 'string' ? toolInput.command : '';
|
|
199
|
+
if (!/\bllmwiki\b/.test(command))
|
|
200
|
+
return [];
|
|
201
|
+
const toolResult = stdinData.tool_result;
|
|
202
|
+
const searchText = [
|
|
203
|
+
command,
|
|
204
|
+
...collectStringValues(toolResult),
|
|
205
|
+
].join('\n');
|
|
206
|
+
return Array.from(new Set(extractProjectWikiPaths(searchText))).map((refPath) => ({
|
|
207
|
+
path: refPath,
|
|
208
|
+
reason: 'llmwiki query result referenced by Bash output',
|
|
209
|
+
}));
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* 上报 llmwiki 召回后被实际使用的 ProjectWiki 文档。
|
|
213
|
+
* 只统计调用方传入的 effectiveRefs,不统计全部召回结果;同一次事件内同一路径去重。
|
|
214
|
+
*
|
|
215
|
+
* @returns 实际累加的唯一文档数
|
|
216
|
+
*/
|
|
217
|
+
export async function reportProjectWikiRefs(knowledgeDir, event) {
|
|
218
|
+
const repoRoot = path.dirname(path.dirname(path.resolve(knowledgeDir)));
|
|
219
|
+
const source = event.source || 'llmwiki';
|
|
220
|
+
const refAt = event.refAt || getBeijingTime();
|
|
221
|
+
const uniqueRefs = new Set();
|
|
222
|
+
for (const ref of event.effectiveRefs || []) {
|
|
223
|
+
const relPath = normalizeProjectWikiFile(repoRoot, ref.path);
|
|
224
|
+
if (relPath)
|
|
225
|
+
uniqueRefs.add(relPath);
|
|
226
|
+
}
|
|
227
|
+
if (uniqueRefs.size === 0)
|
|
228
|
+
return 0;
|
|
229
|
+
try {
|
|
230
|
+
const usage = await readUsageStats(knowledgeDir);
|
|
231
|
+
for (const relPath of uniqueRefs) {
|
|
232
|
+
bumpProjectWikiRef(usage, relPath, source, refAt);
|
|
233
|
+
}
|
|
234
|
+
usage.lastUpdatedAt = refAt;
|
|
235
|
+
await FileSystemUtils.createDirectory(path.dirname(getUsagePath(knowledgeDir)));
|
|
236
|
+
await atomicWriteJson(getUsagePath(knowledgeDir), usage);
|
|
237
|
+
return uniqueRefs.size;
|
|
238
|
+
}
|
|
239
|
+
catch {
|
|
240
|
+
return 0;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
98
243
|
/**
|
|
99
244
|
* 清晰可复用的上报接口:累加一次引用。
|
|
100
245
|
* - filePath 未命中 knowledge/ 三类目录 → no-op 返回 false。
|
|
@@ -106,7 +251,7 @@ function matchProjectWikiFile(repoRoot, filePath) {
|
|
|
106
251
|
export async function reportKnowledgeRef(knowledgeDir, event) {
|
|
107
252
|
const relPath = matchKnowledgeFile(knowledgeDir, event.filePath);
|
|
108
253
|
const repoRoot = path.dirname(path.dirname(path.resolve(knowledgeDir)));
|
|
109
|
-
const projectWikiRelPath =
|
|
254
|
+
const projectWikiRelPath = normalizeProjectWikiFile(repoRoot, event.filePath);
|
|
110
255
|
if (!relPath && !projectWikiRelPath)
|
|
111
256
|
return false;
|
|
112
257
|
const refAt = event.refAt || getBeijingTime();
|
|
@@ -124,19 +269,14 @@ export async function reportKnowledgeRef(knowledgeDir, event) {
|
|
|
124
269
|
}
|
|
125
270
|
try {
|
|
126
271
|
const usage = await readUsageStats(knowledgeDir);
|
|
127
|
-
const bump = (counter) => ({
|
|
128
|
-
refCount: (counter?.refCount ?? 0) + 1,
|
|
129
|
-
lastRefAt: refAt,
|
|
130
|
-
});
|
|
131
272
|
if (relPath) {
|
|
132
|
-
usage.byFile[relPath] = bump(usage.byFile[relPath]);
|
|
273
|
+
usage.byFile[relPath] = bump(usage.byFile[relPath], refAt);
|
|
133
274
|
if (id)
|
|
134
|
-
usage.byId[id] = bump(usage.byId[id]);
|
|
275
|
+
usage.byId[id] = bump(usage.byId[id], refAt);
|
|
135
276
|
usage.totalRefs += 1;
|
|
136
277
|
}
|
|
137
278
|
if (projectWikiRelPath) {
|
|
138
|
-
usage
|
|
139
|
-
usage.projectWikiTotalRefs += 1;
|
|
279
|
+
bumpProjectWikiRef(usage, projectWikiRelPath, 'file', refAt);
|
|
140
280
|
}
|
|
141
281
|
usage.lastUpdatedAt = refAt;
|
|
142
282
|
await FileSystemUtils.createDirectory(path.dirname(getUsagePath(knowledgeDir)));
|
|
@@ -173,11 +313,20 @@ export async function knowledgeUsageHook(options) {
|
|
|
173
313
|
}
|
|
174
314
|
const toolInput = stdinData.tool_input;
|
|
175
315
|
const filePath = toolInput?.file_path || toolInput?.path || options.file || '';
|
|
176
|
-
|
|
316
|
+
const projectWikiRefEvent = extractProjectWikiRefEvent(stdinData, toolInput) || {
|
|
317
|
+
source: 'llmwiki',
|
|
318
|
+
effectiveRefs: extractLlmwikiEffectiveRefsFromHookInput(stdinData),
|
|
319
|
+
};
|
|
320
|
+
if (filePath || projectWikiRefEvent.effectiveRefs.length > 0) {
|
|
177
321
|
try {
|
|
178
322
|
const repoRoot = resolveZhuanSpecRoot(process.cwd());
|
|
179
323
|
const knowledgeDir = path.join(repoRoot, 'zhuanspec', 'knowledge');
|
|
180
|
-
|
|
324
|
+
if (projectWikiRefEvent.effectiveRefs.length > 0) {
|
|
325
|
+
await reportProjectWikiRefs(knowledgeDir, projectWikiRefEvent);
|
|
326
|
+
}
|
|
327
|
+
if (filePath) {
|
|
328
|
+
await reportKnowledgeRef(knowledgeDir, { filePath });
|
|
329
|
+
}
|
|
181
330
|
}
|
|
182
331
|
catch {
|
|
183
332
|
// 旁路 hook 永不抛错
|
package/dist/core/init.js
CHANGED
|
@@ -30,6 +30,8 @@ export interface FunnelStats {
|
|
|
30
30
|
projectWikiReferencedRefs: number;
|
|
31
31
|
/** ProjectWiki 被引用数按 service 聚合 */
|
|
32
32
|
projectWikiReferencedRefsByService: Record<string, number>;
|
|
33
|
+
/** ProjectWiki 被引用数按来源聚合,如 file / llmwiki */
|
|
34
|
+
projectWikiReferencedRefsBySource: Record<string, number>;
|
|
33
35
|
/** 五跳转化率(0~1,分母为 0 记 0) */
|
|
34
36
|
conversions: {
|
|
35
37
|
signalToAsked: number;
|
|
@@ -104,6 +104,13 @@ function aggregateProjectWikiRefsByService(byProjectWikiFile) {
|
|
|
104
104
|
}
|
|
105
105
|
return Object.fromEntries(Object.entries(byService).sort(([a], [b]) => a.localeCompare(b)));
|
|
106
106
|
}
|
|
107
|
+
function aggregateCounters(counters) {
|
|
108
|
+
const result = {};
|
|
109
|
+
for (const [key, counter] of Object.entries(counters)) {
|
|
110
|
+
result[key] = counter.refCount ?? 0;
|
|
111
|
+
}
|
|
112
|
+
return Object.fromEntries(Object.entries(result).sort(([a], [b]) => a.localeCompare(b)));
|
|
113
|
+
}
|
|
107
114
|
/**
|
|
108
115
|
* 顶层聚合:拼装 FunnelStats。
|
|
109
116
|
* @param archivedEntries 落库数(由调用方传入 stats.total,避免重复 scanKnowledgeEntries)
|
|
@@ -114,6 +121,7 @@ export async function computeFunnelStats(zhuanspecDir, knowledgeDir, archivedEnt
|
|
|
114
121
|
const referencedRefs = usage.totalRefs;
|
|
115
122
|
const projectWikiReferencedRefs = usage.projectWikiTotalRefs;
|
|
116
123
|
const projectWikiReferencedRefsByService = aggregateProjectWikiRefsByService(usage.byProjectWikiFile);
|
|
124
|
+
const projectWikiReferencedRefsBySource = aggregateCounters(usage.projectWikiRefsBySource);
|
|
117
125
|
const { correctionSignals, askedPitfalls, scannedChanges } = changeScan;
|
|
118
126
|
return {
|
|
119
127
|
correctionSignals,
|
|
@@ -122,6 +130,7 @@ export async function computeFunnelStats(zhuanspecDir, knowledgeDir, archivedEnt
|
|
|
122
130
|
referencedRefs,
|
|
123
131
|
projectWikiReferencedRefs,
|
|
124
132
|
projectWikiReferencedRefsByService,
|
|
133
|
+
projectWikiReferencedRefsBySource,
|
|
125
134
|
conversions: {
|
|
126
135
|
signalToAsked: rate(askedPitfalls, correctionSignals),
|
|
127
136
|
askedToArchived: rate(archivedEntries, askedPitfalls),
|
|
@@ -893,9 +893,26 @@ const designSteps = `**步骤**
|
|
|
893
893
|
* 在 progress.json 的 events 字段追加:\`{ "event": "test-case-skipped", "timestamp": "<ISO>" }\`
|
|
894
894
|
* 直接进入步骤 8
|
|
895
895
|
|
|
896
|
+
7.6. **涉及工程清单输出(阶段 6 定稿产物,必须生成)**:
|
|
897
|
+
- 在技术方案定稿时,从 Skill 生成的 \`tech-spec.md\`、\`matched_services\` 和改动点定位结果中提取涉及工程
|
|
898
|
+
- 输出到与技术方案同目录:
|
|
899
|
+
\`zhuanspec/changes/{change-id}/techDesign/affected-projects.md\`
|
|
900
|
+
- 只记录分析出的工程/服务名称,不展开改动原因、证据、置信度或实现细节
|
|
901
|
+
- 固定使用 Markdown 列表:
|
|
902
|
+
\`\`\`markdown
|
|
903
|
+
# 涉及工程清单
|
|
904
|
+
|
|
905
|
+
- user-service
|
|
906
|
+
- order-service
|
|
907
|
+
\`\`\`
|
|
908
|
+
- 工程/服务必须来自项目知识定位或实际代码检索证据,禁止凭空补全
|
|
909
|
+
- 如果无法定位任何工程,也必须生成 \`affected-projects.md\`,并写明"未定位到明确工程"
|
|
910
|
+
- 该文件供后续 proposal/tasks/apply 阶段快速判断工程边界,禁止创建 proposal.md 或 tasks.md 来替代它
|
|
911
|
+
|
|
896
912
|
8. **输出摘要**:
|
|
897
913
|
- 告知用户生成的文档路径和实际调用的 Skill 名称
|
|
898
|
-
-
|
|
914
|
+
- 告知用户涉及工程清单路径:\`zhuanspec/changes/{change-id}/techDesign/affected-projects.md\`
|
|
915
|
+
- **明确说明**:techDesign 阶段仅保存进度数据(progress.json)、技术方案(含外部依赖矩阵)和涉及工程清单,不创建 proposal.md 和 tasks.md
|
|
899
916
|
- 如果步骤 7.5 生成了测试 case,额外提示:「✅ 已生成测试 case 源文件,后续 Propose 阶段将自动启用 TDD 模式并调用 tdd-testcase-generator 转换为研发 TDD testcase」
|
|
900
917
|
- 提示 phase=techDesign
|
|
901
918
|
- 提示下一步可以使用 \`/zhuanspec:proposal\` 创建变更提案(复用目录)`;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zhuan-ai/zhuanspec",
|
|
3
|
-
"version": "2.17.
|
|
3
|
+
"version": "2.17.2",
|
|
4
4
|
"description": "AI-native system for spec-driven development",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"zhuanspec",
|
|
@@ -39,26 +39,6 @@
|
|
|
39
39
|
"!dist/**/__tests__",
|
|
40
40
|
"!dist/**/*.map"
|
|
41
41
|
],
|
|
42
|
-
"scripts": {
|
|
43
|
-
"lint": "eslint src/",
|
|
44
|
-
"build": "node build.js",
|
|
45
|
-
"dev": "tsc --watch",
|
|
46
|
-
"dev:cli": "pnpm build && node bin/zhuanspec.js",
|
|
47
|
-
"test": "vitest run",
|
|
48
|
-
"test:watch": "vitest",
|
|
49
|
-
"test:ui": "vitest --ui",
|
|
50
|
-
"test:coverage": "vitest --coverage",
|
|
51
|
-
"test:postinstall": "node scripts/postinstall.js",
|
|
52
|
-
"prepare": "npm run build",
|
|
53
|
-
"prepublishOnly": "npm run build",
|
|
54
|
-
"postinstall": "node scripts/postinstall.js",
|
|
55
|
-
"check:pack-version": "node scripts/pack-version-check.mjs",
|
|
56
|
-
"diagnose:cursor": "node scripts/diagnose-cursor-commands.js",
|
|
57
|
-
"release": "pnpm run release:ci",
|
|
58
|
-
"release:ci": "pnpm run check:pack-version && pnpm exec changeset publish",
|
|
59
|
-
"release:local": "pnpm exec changeset version && pnpm run check:pack-version && pnpm exec changeset publish",
|
|
60
|
-
"changeset": "changeset"
|
|
61
|
-
},
|
|
62
42
|
"engines": {
|
|
63
43
|
"node": ">=20.19.0"
|
|
64
44
|
},
|
|
@@ -80,5 +60,23 @@
|
|
|
80
60
|
"ora": "^8.2.0",
|
|
81
61
|
"yaml": "^2.8.2",
|
|
82
62
|
"zod": "^4.0.17"
|
|
63
|
+
},
|
|
64
|
+
"scripts": {
|
|
65
|
+
"lint": "eslint src/",
|
|
66
|
+
"build": "node build.js",
|
|
67
|
+
"dev": "tsc --watch",
|
|
68
|
+
"dev:cli": "pnpm build && node bin/zhuanspec.js",
|
|
69
|
+
"test": "vitest run",
|
|
70
|
+
"test:watch": "vitest",
|
|
71
|
+
"test:ui": "vitest --ui",
|
|
72
|
+
"test:coverage": "vitest --coverage",
|
|
73
|
+
"test:postinstall": "node scripts/postinstall.js",
|
|
74
|
+
"postinstall": "node scripts/postinstall.js",
|
|
75
|
+
"check:pack-version": "node scripts/pack-version-check.mjs",
|
|
76
|
+
"diagnose:cursor": "node scripts/diagnose-cursor-commands.js",
|
|
77
|
+
"release": "pnpm run release:ci",
|
|
78
|
+
"release:ci": "pnpm run check:pack-version && pnpm exec changeset publish",
|
|
79
|
+
"release:local": "pnpm exec changeset version && pnpm run check:pack-version && pnpm exec changeset publish",
|
|
80
|
+
"changeset": "changeset"
|
|
83
81
|
}
|
|
84
|
-
}
|
|
82
|
+
}
|