@skrupellose/code-helper 0.1.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/README.md ADDED
@@ -0,0 +1,64 @@
1
+ # code-helper
2
+
3
+ `code-helper` 是一个通用 agent 协作工作流 CLI,用于提高项目中多个 agent 协作时的规范度和上下文持久化能力。
4
+
5
+ ## 使用方式
6
+
7
+ ```bash
8
+ npx @skrupellose/code-helper
9
+ ```
10
+
11
+ 交互菜单支持方向键移动,空格或回车确认。功能开关页面支持方向键移动、空格切换、回车保存。不支持按键交互的终端会回退为数字菜单,输入 `0` 返回上一级。菜单动作会打印开始和完成状态,并在 TTY 模式下等待回车后再返回菜单,避免执行结果一闪而过。
12
+
13
+ 也可以使用非交互命令:
14
+
15
+ ```bash
16
+ npx @skrupellose/code-helper init
17
+ npx @skrupellose/code-helper check
18
+ npx @skrupellose/code-helper features list
19
+ npx @skrupellose/code-helper plan docs/订单管理需求.md 订单管理升级
20
+ npx @skrupellose/code-helper manual-test 订单管理升级
21
+ npx @skrupellose/code-helper archive 订单管理升级
22
+ npx @skrupellose/code-helper tasks
23
+ npx @skrupellose/code-helper skills register
24
+ ```
25
+
26
+ 在交互式“项目计划优化”里,可以直接把需求文档拖到终端;工具会识别引号、`file://`、反斜杠转义空格和项目内绝对路径。
27
+
28
+ ## 默认工作区
29
+
30
+ 初始化后会创建:
31
+
32
+ - `.code-helper/`:工具配置、内置 skills 模板、hook sample 和检查结果
33
+ - `.agents/skills/`:Codex 项目级 skills 注册目录,当前项目需要 Codex 时注册 code-helper skills
34
+ - `.claude/skills/`:Claude Code 项目级 skills 注册目录,当前项目需要 Claude Code 时注册 code-helper skills
35
+ - `code-helper-docs/user-rules/`:长期专题规则
36
+ - `code-helper-docs/plan-doc/`:项目计划
37
+ - `code-helper-docs/result-doc/`:执行结果和手工测试文档
38
+ - `code-helper-docs/status-doc/`:当前状态记录
39
+ - `code-helper-docs/*/archive/`:已完成或已结束任务的归档文档
40
+
41
+ ## 默认策略
42
+
43
+ - 默认维护 `AGENTS.md`,检测到或配置启用后可同步 `CLAUDE.md`。
44
+ - 初始化不会覆盖已有专题规则。
45
+ - `.code-helper/skills/` 只是内置 skills 模板源,不会被 Codex 或 Claude Code 默认识别。
46
+ - `npx @skrupellose/code-helper init` 会根据初始化前的入口文档注册项目级 skills:只有 `AGENTS.md` 时只注册 Codex,只有 `CLAUDE.md` 时只注册 Claude Code,两者都存在时注册两套;两个入口文档都不存在的新项目默认注册两套。
47
+ - `npx @skrupellose/code-helper skills register` 不带 target 时按当前项目已有 `AGENTS.md` / `CLAUDE.md` 自动选择目标;传 `all` 时强制注册两套。
48
+ - 入口文档只更新 `<!-- code-helper:start -->` 和 `<!-- code-helper:end -->` 之间的受控区块。
49
+ - 计划、结果、状态和测试文档必须使用中文命名与中文总结,例如 `code-helper-docs/plan-doc/订单管理升级.md`、`code-helper-docs/result-doc/订单管理升级/实施记录.md`、`code-helper-docs/status-doc/订单管理升级-状态.md`。
50
+ - 页面相关测试只生成严格手工测试文档。
51
+ - 工具自己只执行纯逻辑测试,例如函数单元测试或非浏览器集成测试。
52
+ - 功能完成后可以执行 `npx @skrupellose/code-helper archive <中文功能名>` 归档文档。
53
+ - 用户手动移动到 `archive/` 的任务会被识别为已结束任务。
54
+ - Git hooks 默认关闭,只生成 sample 模板。
55
+
56
+ ## 功能开关
57
+
58
+ ```bash
59
+ npx @skrupellose/code-helper features list
60
+ npx @skrupellose/code-helper features disable testingPolicy
61
+ npx @skrupellose/code-helper features enable gitHooks
62
+ ```
63
+
64
+ 功能关闭后,菜单和检查会跳过对应能力。修改开关后可重新执行 `npx @skrupellose/code-helper init` 刷新入口索引和模板。
@@ -0,0 +1,26 @@
1
+ import type { OperationResult } from "./types.js";
2
+ /**
3
+ * 任务文档的状态。
4
+ * active 表示仍在顶层工作目录,archived 表示已经进入 archive,mixed 表示两边都存在,需要人工确认。
5
+ */
6
+ export type TaskStatus = "active" | "archived" | "mixed";
7
+ /**
8
+ * 单个任务在 plan/result/status 三类文档中的分布。
9
+ * CLI 会用该结构展示“哪些任务仍在进行,哪些已经结束”。
10
+ */
11
+ export interface TaskRecord {
12
+ featureName: string;
13
+ status: TaskStatus;
14
+ activeArtifacts: string[];
15
+ archivedArtifacts: string[];
16
+ }
17
+ /**
18
+ * 执行任务文档归档。
19
+ * 归档目标固定为各文档目录下的 archive 子目录,避免已结束任务继续影响当前任务判断。
20
+ */
21
+ export declare function archiveFeature(projectRoot: string, rawFeatureName: string): Promise<OperationResult[]>;
22
+ /**
23
+ * 扫描当前项目任务。
24
+ * 如果用户手动把文件移动到 archive 子目录,这里也会识别为 archived。
25
+ */
26
+ export declare function listTasks(projectRoot: string): Promise<TaskRecord[]>;
@@ -0,0 +1,289 @@
1
+ import { mkdir, readdir, rename, stat } from "node:fs/promises";
2
+ import { dirname, join } from "node:path";
3
+ import { loadConfig } from "./config.js";
4
+ import { ensureDirectory, portablePath, projectPath, writeText } from "./fs-utils.js";
5
+ import { normalizeDocumentName, normalizeFeatureName } from "./workflows.js";
6
+ /**
7
+ * 执行任务文档归档。
8
+ * 归档目标固定为各文档目录下的 archive 子目录,避免已结束任务继续影响当前任务判断。
9
+ */
10
+ export async function archiveFeature(projectRoot, rawFeatureName) {
11
+ const config = await loadConfig(projectRoot);
12
+ if (!config.features.documentArchive.enabled) {
13
+ throw new Error("文档归档功能已关闭,请先执行 `code-helper features enable documentArchive`。");
14
+ }
15
+ const featureNames = getArchiveFeatureNameCandidates(rawFeatureName);
16
+ const recordFeatureName = featureNames[0];
17
+ const operations = [];
18
+ const moves = featureNames.flatMap((featureName) => getArchiveMoves(config, featureName));
19
+ for (const move of moves) {
20
+ operations.push(await movePathIfExists(projectRoot, move.from, move.to));
21
+ }
22
+ if (!operations.some((operation) => isKnownArchivedOperation(operation))) {
23
+ throw new Error(`未找到功能文档:${rawFeatureName}。请确认 plan-doc、result-doc 或 status-doc 中存在对应任务。`);
24
+ }
25
+ operations.push(await writeArchiveRecord(projectRoot, config, recordFeatureName, operations));
26
+ return operations;
27
+ }
28
+ /**
29
+ * 生成归档任务名候选。
30
+ * 新文档强制中文命名;旧项目可能仍有英文 feature 文档,因此归档时保留旧英文兼容。
31
+ */
32
+ function getArchiveFeatureNameCandidates(rawFeatureName) {
33
+ const legacyName = normalizeFeatureName(rawFeatureName);
34
+ const chineseName = normalizeDocumentName(rawFeatureName, "功能文档");
35
+ const orderedNames = containsChinese(rawFeatureName)
36
+ ? [chineseName, legacyName]
37
+ : [legacyName, chineseName];
38
+ return [...new Set(orderedNames)];
39
+ }
40
+ /**
41
+ * 判断输入是否包含中文。
42
+ * 归档命令用它决定中文新规则和英文旧规则的匹配优先级。
43
+ */
44
+ function containsChinese(value) {
45
+ return /\p{Script=Han}/u.test(value);
46
+ }
47
+ /**
48
+ * 扫描当前项目任务。
49
+ * 如果用户手动把文件移动到 archive 子目录,这里也会识别为 archived。
50
+ */
51
+ export async function listTasks(projectRoot) {
52
+ const config = await loadConfig(projectRoot);
53
+ const tasks = new Map();
54
+ await collectPlanDocuments(projectRoot, config, tasks, false);
55
+ await collectPlanDocuments(projectRoot, config, tasks, true);
56
+ await collectResultDocuments(projectRoot, config, tasks, false);
57
+ await collectResultDocuments(projectRoot, config, tasks, true);
58
+ await collectStatusDocuments(projectRoot, config, tasks, false);
59
+ await collectStatusDocuments(projectRoot, config, tasks, true);
60
+ return [...tasks.values()]
61
+ .map((task) => ({
62
+ ...task,
63
+ status: resolveTaskStatus(task)
64
+ }))
65
+ .sort((left, right) => left.featureName.localeCompare(right.featureName));
66
+ }
67
+ /**
68
+ * 根据任务名生成三类文档的归档移动计划。
69
+ * plan/status 是单文件,result 是目录,三者互相独立,允许部分存在。
70
+ */
71
+ function getArchiveMoves(config, featureName) {
72
+ return [
73
+ {
74
+ from: portablePath(config.directories.planDoc, `${featureName}.md`),
75
+ to: portablePath(config.directories.planDoc, "archive", `${featureName}.md`)
76
+ },
77
+ {
78
+ from: portablePath(config.directories.resultDoc, featureName),
79
+ to: portablePath(config.directories.resultDoc, "archive", featureName)
80
+ },
81
+ {
82
+ from: portablePath(config.directories.statusDoc, `${featureName}-状态.md`),
83
+ to: portablePath(config.directories.statusDoc, "archive", `${featureName}-状态.md`)
84
+ },
85
+ {
86
+ from: portablePath(config.directories.statusDoc, `${featureName}-status.md`),
87
+ to: portablePath(config.directories.statusDoc, "archive", `${featureName}-status.md`)
88
+ }
89
+ ];
90
+ }
91
+ /**
92
+ * 文件或目录存在时移动到 archive。
93
+ * 目标已存在时不覆盖,避免破坏用户手动归档后的内容。
94
+ */
95
+ async function movePathIfExists(projectRoot, fromRelativePath, toRelativePath) {
96
+ const fromPath = projectPath(projectRoot, fromRelativePath);
97
+ const toPath = projectPath(projectRoot, toRelativePath);
98
+ const sourceExists = await pathExists(fromPath);
99
+ const targetExists = await pathExists(toPath);
100
+ if (!sourceExists && targetExists) {
101
+ return {
102
+ path: toPath,
103
+ action: "skipped",
104
+ message: "已在归档目录中,识别为已结束任务"
105
+ };
106
+ }
107
+ if (!sourceExists) {
108
+ return {
109
+ path: fromPath,
110
+ action: "skipped",
111
+ message: "源文档不存在,跳过归档"
112
+ };
113
+ }
114
+ if (targetExists) {
115
+ return {
116
+ path: toPath,
117
+ action: "skipped",
118
+ message: "归档目标已存在,为避免覆盖已跳过"
119
+ };
120
+ }
121
+ await mkdir(dirname(toPath), { recursive: true });
122
+ await rename(fromPath, toPath);
123
+ return {
124
+ path: toPath,
125
+ action: "updated",
126
+ message: "已移动到归档目录"
127
+ };
128
+ }
129
+ /**
130
+ * 写入归档记录。
131
+ * 该记录只描述 code-helper 的归档动作,不替代业务 result-doc。
132
+ */
133
+ async function writeArchiveRecord(projectRoot, config, featureName, operations) {
134
+ const archiveDirectory = projectPath(projectRoot, join(config.directories.workspace, "archives"));
135
+ const targetPath = join(archiveDirectory, `${featureName}.json`);
136
+ const archivedPaths = operations
137
+ .filter((operation) => isKnownArchivedOperation(operation))
138
+ .map((operation) => operation.path);
139
+ await ensureDirectory(archiveDirectory);
140
+ await writeText(targetPath, `${JSON.stringify({
141
+ featureName,
142
+ archivedAt: new Date().toISOString(),
143
+ archivedPaths,
144
+ note: "此记录由 code-helper archive 生成。任务文档在 archive 目录中时视为已结束。"
145
+ }, null, 2)}\n`);
146
+ return {
147
+ path: targetPath,
148
+ action: "updated",
149
+ message: "已写入归档记录"
150
+ };
151
+ }
152
+ /**
153
+ * 判断操作是否证明该任务已经处于归档状态。
154
+ * 源文档不存在不算已归档,避免生成空归档记录。
155
+ */
156
+ function isKnownArchivedOperation(operation) {
157
+ return operation.message === "已移动到归档目录" || operation.message === "已在归档目录中,识别为已结束任务";
158
+ }
159
+ /**
160
+ * 收集 plan-doc 中的活动或归档计划文档。
161
+ */
162
+ async function collectPlanDocuments(projectRoot, config, tasks, archived) {
163
+ const directory = archived
164
+ ? portablePath(config.directories.planDoc, "archive")
165
+ : config.directories.planDoc;
166
+ const files = await safeReadDirectory(projectPath(projectRoot, directory));
167
+ for (const file of files) {
168
+ if (!file.endsWith(".md")) {
169
+ continue;
170
+ }
171
+ const featureName = file.slice(0, -".md".length);
172
+ addArtifact(tasks, featureName, portablePath(directory, file), archived);
173
+ }
174
+ }
175
+ /**
176
+ * 收集 result-doc 中的活动或归档执行结果目录。
177
+ */
178
+ async function collectResultDocuments(projectRoot, config, tasks, archived) {
179
+ const directory = archived
180
+ ? portablePath(config.directories.resultDoc, "archive")
181
+ : config.directories.resultDoc;
182
+ const entries = await safeReadDirectory(projectPath(projectRoot, directory));
183
+ for (const entry of entries) {
184
+ if (entry === "archive" || entry.startsWith(".")) {
185
+ continue;
186
+ }
187
+ const absolutePath = projectPath(projectRoot, portablePath(directory, entry));
188
+ if (!(await isDirectory(absolutePath))) {
189
+ continue;
190
+ }
191
+ addArtifact(tasks, entry, portablePath(directory, entry), archived);
192
+ }
193
+ }
194
+ /**
195
+ * 收集 status-doc 中的活动或归档状态文件。
196
+ */
197
+ async function collectStatusDocuments(projectRoot, config, tasks, archived) {
198
+ const directory = archived
199
+ ? portablePath(config.directories.statusDoc, "archive")
200
+ : config.directories.statusDoc;
201
+ const files = await safeReadDirectory(projectPath(projectRoot, directory));
202
+ for (const file of files) {
203
+ const featureName = extractStatusFeatureName(file);
204
+ if (featureName === undefined) {
205
+ continue;
206
+ }
207
+ addArtifact(tasks, featureName, portablePath(directory, file), archived);
208
+ }
209
+ }
210
+ /**
211
+ * 从状态文档文件名中提取任务名。
212
+ * 新文档使用中文 `-状态.md`,旧文档的 `-status.md` 仍兼容识别。
213
+ */
214
+ function extractStatusFeatureName(file) {
215
+ if (file.endsWith("-状态.md")) {
216
+ return file.slice(0, -"-状态.md".length);
217
+ }
218
+ if (file.endsWith("-status.md")) {
219
+ return file.slice(0, -"-status.md".length);
220
+ }
221
+ return undefined;
222
+ }
223
+ /**
224
+ * 把一个文档路径加入任务记录。
225
+ * 统一在这里初始化 TaskRecord,避免各收集函数重复逻辑。
226
+ */
227
+ function addArtifact(tasks, featureName, relativePath, archived) {
228
+ const existing = tasks.get(featureName) ?? {
229
+ featureName,
230
+ status: "active",
231
+ activeArtifacts: [],
232
+ archivedArtifacts: []
233
+ };
234
+ if (archived) {
235
+ existing.archivedArtifacts.push(relativePath);
236
+ }
237
+ else {
238
+ existing.activeArtifacts.push(relativePath);
239
+ }
240
+ tasks.set(featureName, existing);
241
+ }
242
+ /**
243
+ * 根据活动文档和归档文档分布推导任务状态。
244
+ */
245
+ function resolveTaskStatus(task) {
246
+ if (task.activeArtifacts.length > 0 && task.archivedArtifacts.length > 0) {
247
+ return "mixed";
248
+ }
249
+ if (task.archivedArtifacts.length > 0) {
250
+ return "archived";
251
+ }
252
+ return "active";
253
+ }
254
+ /**
255
+ * 判断路径是否存在。
256
+ */
257
+ async function pathExists(path) {
258
+ try {
259
+ await stat(path);
260
+ return true;
261
+ }
262
+ catch {
263
+ return false;
264
+ }
265
+ }
266
+ /**
267
+ * 判断路径是否为目录。
268
+ */
269
+ async function isDirectory(path) {
270
+ try {
271
+ return (await stat(path)).isDirectory();
272
+ }
273
+ catch {
274
+ return false;
275
+ }
276
+ }
277
+ /**
278
+ * 安全读取目录。
279
+ * 不存在时返回空数组,方便新项目或未使用过的 archive 目录扫描。
280
+ */
281
+ async function safeReadDirectory(path) {
282
+ try {
283
+ return await readdir(path);
284
+ }
285
+ catch {
286
+ return [];
287
+ }
288
+ }
289
+ //# sourceMappingURL=archive.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"archive.js","sourceRoot":"","sources":["../src/archive.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ import type { CheckIssue } from "./types.js";
2
+ /**
3
+ * 运行项目协作规则检查。
4
+ * 检查只读项目结构,并把结果写入 `.code-helper/checks/latest.json`。
5
+ */
6
+ export declare function runChecks(projectRoot: string): Promise<CheckIssue[]>;