@bicorne/task-flow 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/LICENSE +21 -0
- package/README.md +252 -0
- package/SKILL.md +250 -0
- package/assets/.harnessrc +10 -0
- package/assets/PHASE-task.json.example +50 -0
- package/assets/design.md +69 -0
- package/assets/hooks.json +15 -0
- package/assets/product-requirement.md +82 -0
- package/assets/schema.json +127 -0
- package/assets/tasks.md +26 -0
- package/dist/commands/analyze.d.ts +32 -0
- package/dist/commands/analyze.js +338 -0
- package/dist/commands/archive.d.ts +11 -0
- package/dist/commands/archive.js +53 -0
- package/dist/commands/design.d.ts +38 -0
- package/dist/commands/design.js +492 -0
- package/dist/commands/extract.d.ts +31 -0
- package/dist/commands/extract.js +477 -0
- package/dist/commands/init.d.ts +24 -0
- package/dist/commands/init.js +165 -0
- package/dist/commands/merge/index.d.ts +17 -0
- package/dist/commands/merge/index.js +322 -0
- package/dist/commands/merge/merger.d.ts +18 -0
- package/dist/commands/merge/merger.js +151 -0
- package/dist/commands/merge/types.d.ts +67 -0
- package/dist/commands/merge/types.js +6 -0
- package/dist/commands/merge/validators.d.ts +14 -0
- package/dist/commands/merge/validators.js +147 -0
- package/dist/commands/merge.d.ts +7 -0
- package/dist/commands/merge.js +15 -0
- package/dist/commands/start.d.ts +32 -0
- package/dist/commands/start.js +265 -0
- package/dist/commands/status.d.ts +15 -0
- package/dist/commands/status.js +143 -0
- package/dist/commands/sync.d.ts +11 -0
- package/dist/commands/sync.js +58 -0
- package/dist/commands/tasks-gen/doc-parser.d.ts +7 -0
- package/dist/commands/tasks-gen/doc-parser.js +259 -0
- package/dist/commands/tasks-gen/generators.d.ts +33 -0
- package/dist/commands/tasks-gen/generators.js +141 -0
- package/dist/commands/tasks-gen/index.d.ts +30 -0
- package/dist/commands/tasks-gen/index.js +345 -0
- package/dist/commands/tasks-gen/parsers.d.ts +29 -0
- package/dist/commands/tasks-gen/parsers.js +272 -0
- package/dist/commands/tasks-gen/templates.d.ts +8 -0
- package/dist/commands/tasks-gen/templates.js +37 -0
- package/dist/commands/tasks-gen/types.d.ts +71 -0
- package/dist/commands/tasks-gen/types.js +17 -0
- package/dist/commands/tasks-gen/validators.d.ts +14 -0
- package/dist/commands/tasks-gen/validators.js +54 -0
- package/dist/commands/tasks.d.ts +9 -0
- package/dist/commands/tasks.js +22 -0
- package/dist/commands/worktree.d.ts +28 -0
- package/dist/commands/worktree.js +275 -0
- package/dist/hooks/check-prd-exists.d.ts +20 -0
- package/dist/hooks/check-prd-exists.js +61 -0
- package/dist/hooks/check-worktree-conflict.d.ts +34 -0
- package/dist/hooks/check-worktree-conflict.js +107 -0
- package/dist/hooks/hook-runner/executor.d.ts +18 -0
- package/dist/hooks/hook-runner/executor.js +143 -0
- package/dist/hooks/hook-runner/index.d.ts +64 -0
- package/dist/hooks/hook-runner/index.js +220 -0
- package/dist/hooks/hook-runner/loader.d.ts +23 -0
- package/dist/hooks/hook-runner/loader.js +126 -0
- package/dist/hooks/hook-runner/types.d.ts +59 -0
- package/dist/hooks/hook-runner/types.js +6 -0
- package/dist/hooks/hook-runner.d.ts +9 -0
- package/dist/hooks/hook-runner.js +30 -0
- package/dist/hooks/phase-complete-detector.d.ts +35 -0
- package/dist/hooks/phase-complete-detector.js +203 -0
- package/dist/hooks/phase-gate-validator.d.ts +76 -0
- package/dist/hooks/phase-gate-validator.js +407 -0
- package/dist/hooks/save-checkpoint.d.ts +43 -0
- package/dist/hooks/save-checkpoint.js +144 -0
- package/dist/hooks/start-mcp-servers.d.ts +50 -0
- package/dist/hooks/start-mcp-servers.js +75 -0
- package/dist/hooks/stop-mcp-servers.d.ts +40 -0
- package/dist/hooks/stop-mcp-servers.js +58 -0
- package/dist/index.d.ts +29 -0
- package/dist/index.js +238 -0
- package/dist/lib/archive.d.ts +31 -0
- package/dist/lib/archive.js +226 -0
- package/dist/lib/config.d.ts +93 -0
- package/dist/lib/config.js +251 -0
- package/dist/lib/constants.d.ts +222 -0
- package/dist/lib/constants.js +247 -0
- package/dist/lib/interactive.d.ts +31 -0
- package/dist/lib/interactive.js +166 -0
- package/dist/lib/mcp-client.d.ts +156 -0
- package/dist/lib/mcp-client.js +370 -0
- package/dist/lib/state.d.ts +119 -0
- package/dist/lib/state.js +293 -0
- package/dist/slash/executor.d.ts +22 -0
- package/dist/slash/executor.js +259 -0
- package/dist/slash/index.d.ts +11 -0
- package/dist/slash/index.js +45 -0
- package/dist/slash/parser.d.ts +24 -0
- package/dist/slash/parser.js +101 -0
- package/dist/slash/registry.d.ts +22 -0
- package/dist/slash/registry.js +155 -0
- package/dist/spec/openspec-to-task/builders.d.ts +107 -0
- package/dist/spec/openspec-to-task/builders.js +138 -0
- package/dist/spec/openspec-to-task/index.d.ts +20 -0
- package/dist/spec/openspec-to-task/index.js +182 -0
- package/dist/spec/openspec-to-task/parsers.d.ts +65 -0
- package/dist/spec/openspec-to-task/parsers.js +232 -0
- package/dist/spec/openspec-to-task/types.d.ts +49 -0
- package/dist/spec/openspec-to-task/types.js +6 -0
- package/dist/spec/sync-openspec-to-task.d.ts +7 -0
- package/dist/spec/sync-openspec-to-task.js +21 -0
- package/dist/spec/sync-task-to-openspec.d.ts +27 -0
- package/dist/spec/sync-task-to-openspec.js +288 -0
- package/dist/types/ai-context.d.ts +108 -0
- package/dist/types/ai-context.js +9 -0
- package/package.json +66 -0
- package/references/AI-CONVERSATION-TUTORIAL.md +270 -0
- package/references/CLI-TUTORIAL.md +447 -0
- package/references/GIT-WORKTREE-SOP.md +109 -0
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# 产品需求文档
|
|
2
|
+
|
|
3
|
+
## 文档基础信息(Meta Data)
|
|
4
|
+
|
|
5
|
+
| 字段 | 内容 |
|
|
6
|
+
|------|------|
|
|
7
|
+
| 项目名称 | <!-- 项目名称 --> |
|
|
8
|
+
| 版本 | v1.0.0 |
|
|
9
|
+
| 创建日期 | <!-- YYYY-MM-DD --> |
|
|
10
|
+
| 作者 | <!-- 作者 --> |
|
|
11
|
+
| 文档状态 | 草稿 |
|
|
12
|
+
| 评审人 | <!-- 评审人列表 --> |
|
|
13
|
+
|
|
14
|
+
## 项目背景与目标(Why)
|
|
15
|
+
|
|
16
|
+
### 背景
|
|
17
|
+
<!-- 描述项目背景和动机,当前存在的问题或痛点 -->
|
|
18
|
+
|
|
19
|
+
### 目标
|
|
20
|
+
<!-- 描述项目要达成的核心目标,可量化指标优先 -->
|
|
21
|
+
|
|
22
|
+
### 范围
|
|
23
|
+
<!-- 明确项目包含和不包含的内容 -->
|
|
24
|
+
|
|
25
|
+
## 用户与场景(Who and When)
|
|
26
|
+
|
|
27
|
+
### 目标用户
|
|
28
|
+
<!-- 描述目标用户群体及其特征 -->
|
|
29
|
+
|
|
30
|
+
### 用户场景
|
|
31
|
+
<!-- 描述典型使用场景和用户旅程 -->
|
|
32
|
+
|
|
33
|
+
### 用户故事
|
|
34
|
+
<!-- 以"作为[用户],我希望[功能],以便[价值]"格式描述 -->
|
|
35
|
+
|
|
36
|
+
## 全局设计与架构(How)
|
|
37
|
+
|
|
38
|
+
### 整体架构
|
|
39
|
+
<!-- 描述系统整体架构设计 -->
|
|
40
|
+
|
|
41
|
+
### 核心流程
|
|
42
|
+
<!-- 描述核心业务流程 -->
|
|
43
|
+
|
|
44
|
+
### 约束条件
|
|
45
|
+
<!-- 技术、业务或时间约束 -->
|
|
46
|
+
|
|
47
|
+
## 详细功能需求(What)
|
|
48
|
+
|
|
49
|
+
### 功能清单
|
|
50
|
+
<!-- 以表格或列表形式列出所有功能需求 -->
|
|
51
|
+
|
|
52
|
+
| 编号 | 功能名称 | 优先级 | 描述 | 验收标准 |
|
|
53
|
+
|------|----------|--------|------|----------|
|
|
54
|
+
| F-001 | <!-- 功能 --> | P0 | <!-- 描述 --> | <!-- 标准 --> |
|
|
55
|
+
|
|
56
|
+
### 功能详细说明
|
|
57
|
+
<!-- 对每个功能进行详细说明 -->
|
|
58
|
+
|
|
59
|
+
## 非功能性需求
|
|
60
|
+
|
|
61
|
+
### 性能需求
|
|
62
|
+
<!-- 响应时间、吞吐量、并发用户数等 -->
|
|
63
|
+
|
|
64
|
+
### 安全需求
|
|
65
|
+
<!-- 认证、授权、数据加密、审计等 -->
|
|
66
|
+
|
|
67
|
+
### 可用性需求
|
|
68
|
+
<!-- SLA、容灾、降级策略等 -->
|
|
69
|
+
|
|
70
|
+
### 兼容性需求
|
|
71
|
+
<!-- 浏览器、操作系统、设备兼容性 -->
|
|
72
|
+
|
|
73
|
+
## 上线与运营计划
|
|
74
|
+
|
|
75
|
+
### 上线计划
|
|
76
|
+
<!-- 上线步骤、灰度策略、回滚方案 -->
|
|
77
|
+
|
|
78
|
+
### 监控与告警
|
|
79
|
+
<!-- 关键指标监控、告警阈值 -->
|
|
80
|
+
|
|
81
|
+
### 运营计划
|
|
82
|
+
<!-- 数据埋点、A/B 测试、迭代计划 -->
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"title": "Task File Schema",
|
|
4
|
+
"description": "Task file format for task-flow PHASE-*.json files",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"required": ["id", "type", "title", "priority", "phase", "status", "spec"],
|
|
7
|
+
"properties": {
|
|
8
|
+
"id": {
|
|
9
|
+
"type": "string",
|
|
10
|
+
"pattern": "^PHASE-[0-9]+-[0-9]+-[0-9]+$",
|
|
11
|
+
"description": "Task ID in format PHASE-{phase}-{subphase}-{sequence}"
|
|
12
|
+
},
|
|
13
|
+
"type": {
|
|
14
|
+
"type": "string",
|
|
15
|
+
"enum": ["feat", "fix", "refactor", "test", "docs"],
|
|
16
|
+
"description": "Task type"
|
|
17
|
+
},
|
|
18
|
+
"title": {
|
|
19
|
+
"type": "string",
|
|
20
|
+
"minLength": 1,
|
|
21
|
+
"description": "Task title"
|
|
22
|
+
},
|
|
23
|
+
"priority": {
|
|
24
|
+
"type": "string",
|
|
25
|
+
"enum": ["high", "medium", "low"],
|
|
26
|
+
"description": "Task priority"
|
|
27
|
+
},
|
|
28
|
+
"phase": {
|
|
29
|
+
"type": "integer",
|
|
30
|
+
"minimum": 1,
|
|
31
|
+
"description": "Phase number"
|
|
32
|
+
},
|
|
33
|
+
"dependencies": {
|
|
34
|
+
"type": "array",
|
|
35
|
+
"items": {
|
|
36
|
+
"type": "string",
|
|
37
|
+
"pattern": "^PHASE-[0-9]+-[0-9]+-[0-9]+$"
|
|
38
|
+
},
|
|
39
|
+
"default": [],
|
|
40
|
+
"description": "List of task IDs this task depends on"
|
|
41
|
+
},
|
|
42
|
+
"docs_to_read": {
|
|
43
|
+
"type": "array",
|
|
44
|
+
"items": { "type": "string" },
|
|
45
|
+
"default": [],
|
|
46
|
+
"description": "Documents AI must read before implementing"
|
|
47
|
+
},
|
|
48
|
+
"spec": {
|
|
49
|
+
"type": "object",
|
|
50
|
+
"required": ["description", "files"],
|
|
51
|
+
"properties": {
|
|
52
|
+
"description": {
|
|
53
|
+
"type": "string",
|
|
54
|
+
"minLength": 1,
|
|
55
|
+
"description": "Detailed task description"
|
|
56
|
+
},
|
|
57
|
+
"files": {
|
|
58
|
+
"type": "array",
|
|
59
|
+
"items": { "type": "string" },
|
|
60
|
+
"minItems": 1,
|
|
61
|
+
"description": "Files related to this task"
|
|
62
|
+
},
|
|
63
|
+
"context": {
|
|
64
|
+
"type": "array",
|
|
65
|
+
"items": { "type": "string" },
|
|
66
|
+
"default": [],
|
|
67
|
+
"description": "Context documents"
|
|
68
|
+
}
|
|
69
|
+
},
|
|
70
|
+
"additionalProperties": true
|
|
71
|
+
},
|
|
72
|
+
"acceptance_criteria": {
|
|
73
|
+
"type": "array",
|
|
74
|
+
"items": { "type": "string" },
|
|
75
|
+
"default": [],
|
|
76
|
+
"description": "Acceptance criteria for task completion"
|
|
77
|
+
},
|
|
78
|
+
"implementation": {
|
|
79
|
+
"type": "object",
|
|
80
|
+
"properties": {
|
|
81
|
+
"steps": {
|
|
82
|
+
"type": "array",
|
|
83
|
+
"items": {
|
|
84
|
+
"type": "object",
|
|
85
|
+
"required": ["step"],
|
|
86
|
+
"properties": {
|
|
87
|
+
"step": { "type": "string" },
|
|
88
|
+
"details": { "type": "string" }
|
|
89
|
+
},
|
|
90
|
+
"additionalProperties": false
|
|
91
|
+
},
|
|
92
|
+
"default": []
|
|
93
|
+
},
|
|
94
|
+
"notes": {
|
|
95
|
+
"type": "string",
|
|
96
|
+
"default": ""
|
|
97
|
+
}
|
|
98
|
+
},
|
|
99
|
+
"additionalProperties": true
|
|
100
|
+
},
|
|
101
|
+
"validation": {
|
|
102
|
+
"type": "object",
|
|
103
|
+
"properties": {
|
|
104
|
+
"commands": {
|
|
105
|
+
"type": "array",
|
|
106
|
+
"items": { "type": "string" },
|
|
107
|
+
"default": [],
|
|
108
|
+
"description": "Commands to validate task completion"
|
|
109
|
+
},
|
|
110
|
+
"manual_checks": {
|
|
111
|
+
"type": "array",
|
|
112
|
+
"items": { "type": "string" },
|
|
113
|
+
"default": [],
|
|
114
|
+
"description": "Manual checks for task completion"
|
|
115
|
+
}
|
|
116
|
+
},
|
|
117
|
+
"additionalProperties": true
|
|
118
|
+
},
|
|
119
|
+
"status": {
|
|
120
|
+
"type": "string",
|
|
121
|
+
"enum": ["pending", "in_progress", "completed", "blocked"],
|
|
122
|
+
"default": "pending",
|
|
123
|
+
"description": "Task status"
|
|
124
|
+
}
|
|
125
|
+
},
|
|
126
|
+
"additionalProperties": true
|
|
127
|
+
}
|
package/assets/tasks.md
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# 任务清单
|
|
2
|
+
|
|
3
|
+
## 概述
|
|
4
|
+
<!-- 任务概述 -->
|
|
5
|
+
|
|
6
|
+
## 任务列表
|
|
7
|
+
|
|
8
|
+
### Phase 1: 基础实现
|
|
9
|
+
|
|
10
|
+
- [ ] Task 1.1: 任务标题
|
|
11
|
+
- 描述:任务描述
|
|
12
|
+
- 文件:src/file1.ts, src/file2.ts
|
|
13
|
+
- 验收:验收标准
|
|
14
|
+
|
|
15
|
+
### Phase 2: 扩展功能
|
|
16
|
+
|
|
17
|
+
- [ ] Task 2.1: 任务标题
|
|
18
|
+
- 描述:任务描述
|
|
19
|
+
- 文件:src/file3.ts
|
|
20
|
+
- 验收:验收标准
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## 任务依赖关系
|
|
25
|
+
|
|
26
|
+
- Task 2.1 依赖 Task 1.1
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* commands/analyze.ts
|
|
3
|
+
* 扫描宿主项目基本信息,生成 AI context JSON 的骨架
|
|
4
|
+
*
|
|
5
|
+
* AI 可以在此基础上补充自己的理解(analysis、task 等字段),
|
|
6
|
+
* 然后通过 --context-file 传给 extract/design 命令。
|
|
7
|
+
*
|
|
8
|
+
* 用法:
|
|
9
|
+
* task-flow analyze --output context.json
|
|
10
|
+
*/
|
|
11
|
+
import { AiContext } from '../types/ai-context';
|
|
12
|
+
export interface AnalyzeOptions {
|
|
13
|
+
cwd?: string;
|
|
14
|
+
output?: string;
|
|
15
|
+
}
|
|
16
|
+
export interface AnalyzeResult {
|
|
17
|
+
success: boolean;
|
|
18
|
+
reason?: string;
|
|
19
|
+
message?: string;
|
|
20
|
+
context?: AiContext;
|
|
21
|
+
outputPath?: string;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Analyze project and generate context JSON
|
|
25
|
+
*/
|
|
26
|
+
export declare function analyzeProject(options?: AnalyzeOptions): AnalyzeResult;
|
|
27
|
+
export declare function main(): void;
|
|
28
|
+
declare const _default: {
|
|
29
|
+
analyzeProject: typeof analyzeProject;
|
|
30
|
+
main: typeof main;
|
|
31
|
+
};
|
|
32
|
+
export default _default;
|
|
@@ -0,0 +1,338 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* commands/analyze.ts
|
|
4
|
+
* 扫描宿主项目基本信息,生成 AI context JSON 的骨架
|
|
5
|
+
*
|
|
6
|
+
* AI 可以在此基础上补充自己的理解(analysis、task 等字段),
|
|
7
|
+
* 然后通过 --context-file 传给 extract/design 命令。
|
|
8
|
+
*
|
|
9
|
+
* 用法:
|
|
10
|
+
* task-flow analyze --output context.json
|
|
11
|
+
*/
|
|
12
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
13
|
+
if (k2 === undefined) k2 = k;
|
|
14
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
15
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
16
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
17
|
+
}
|
|
18
|
+
Object.defineProperty(o, k2, desc);
|
|
19
|
+
}) : (function(o, m, k, k2) {
|
|
20
|
+
if (k2 === undefined) k2 = k;
|
|
21
|
+
o[k2] = m[k];
|
|
22
|
+
}));
|
|
23
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
24
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
25
|
+
}) : function(o, v) {
|
|
26
|
+
o["default"] = v;
|
|
27
|
+
});
|
|
28
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
29
|
+
var ownKeys = function(o) {
|
|
30
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
31
|
+
var ar = [];
|
|
32
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
33
|
+
return ar;
|
|
34
|
+
};
|
|
35
|
+
return ownKeys(o);
|
|
36
|
+
};
|
|
37
|
+
return function (mod) {
|
|
38
|
+
if (mod && mod.__esModule) return mod;
|
|
39
|
+
var result = {};
|
|
40
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
41
|
+
__setModuleDefault(result, mod);
|
|
42
|
+
return result;
|
|
43
|
+
};
|
|
44
|
+
})();
|
|
45
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
46
|
+
exports.analyzeProject = analyzeProject;
|
|
47
|
+
exports.main = main;
|
|
48
|
+
const fs = __importStar(require("fs"));
|
|
49
|
+
const path = __importStar(require("path"));
|
|
50
|
+
const config_1 = require("../lib/config");
|
|
51
|
+
function parseArgs(argv) {
|
|
52
|
+
const args = {};
|
|
53
|
+
for (let i = 2; i < argv.length; i += 1) {
|
|
54
|
+
const token = argv[i] || '';
|
|
55
|
+
if (!token.startsWith('--')) {
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
const key = token.slice(2);
|
|
59
|
+
const next = argv[i + 1] || '';
|
|
60
|
+
if (!next || next.startsWith('--')) {
|
|
61
|
+
args[key] = true;
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
args[key] = next;
|
|
65
|
+
i += 1;
|
|
66
|
+
}
|
|
67
|
+
return args;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Read and parse package.json if it exists
|
|
71
|
+
*/
|
|
72
|
+
function readPackageJson(projectRoot) {
|
|
73
|
+
const pkgPath = path.resolve(projectRoot, 'package.json');
|
|
74
|
+
if (!fs.existsSync(pkgPath)) {
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
try {
|
|
78
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
79
|
+
return require(pkgPath);
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Read tsconfig.json if it exists
|
|
87
|
+
*/
|
|
88
|
+
function readTsconfig(projectRoot) {
|
|
89
|
+
const tsconfigPath = path.resolve(projectRoot, 'tsconfig.json');
|
|
90
|
+
if (!fs.existsSync(tsconfigPath)) {
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
try {
|
|
94
|
+
const content = fs.readFileSync(tsconfigPath, 'utf8');
|
|
95
|
+
// Strip comments for basic parsing
|
|
96
|
+
const stripped = content.replace(/\/\/.*$/gm, '').replace(/\/\*[\s\S]*?\*\//g, '');
|
|
97
|
+
return JSON.parse(stripped);
|
|
98
|
+
}
|
|
99
|
+
catch {
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Scan top-level directory structure
|
|
105
|
+
*/
|
|
106
|
+
function scanDirectoryStructure(projectRoot) {
|
|
107
|
+
const entries = fs.readdirSync(projectRoot, { withFileTypes: true });
|
|
108
|
+
const summary = [];
|
|
109
|
+
const knownDirs = {
|
|
110
|
+
src: '源代码目录',
|
|
111
|
+
lib: '库代码目录',
|
|
112
|
+
packages: 'Monorepo 包目录',
|
|
113
|
+
apps: '应用目录',
|
|
114
|
+
test: '测试目录',
|
|
115
|
+
tests: '测试目录',
|
|
116
|
+
__tests__: '测试目录',
|
|
117
|
+
docs: '文档目录',
|
|
118
|
+
scripts: '脚本目录',
|
|
119
|
+
config: '配置目录',
|
|
120
|
+
public: '静态资源目录',
|
|
121
|
+
assets: '资源目录',
|
|
122
|
+
};
|
|
123
|
+
for (const entry of entries) {
|
|
124
|
+
if (entry.isDirectory() && !entry.name.startsWith('.') && !entry.name.startsWith('node_modules')) {
|
|
125
|
+
const dirPath = path.resolve(projectRoot, entry.name);
|
|
126
|
+
const purpose = knownDirs[entry.name] || '项目目录';
|
|
127
|
+
let fileCount;
|
|
128
|
+
try {
|
|
129
|
+
fileCount = fs.readdirSync(dirPath, { recursive: true }).filter((p) => typeof p === 'string' && !p.startsWith('.') && fs.statSync(path.resolve(dirPath, p)).isFile()).length;
|
|
130
|
+
}
|
|
131
|
+
catch {
|
|
132
|
+
// Ignore permission errors
|
|
133
|
+
}
|
|
134
|
+
summary.push({ path: entry.name, purpose, fileCount });
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
return summary;
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Detect project type from structure and package.json
|
|
141
|
+
*/
|
|
142
|
+
function detectProjectType(pkg, structure) {
|
|
143
|
+
if (pkg) {
|
|
144
|
+
const isMonorepo = pkg.workspaces !== undefined
|
|
145
|
+
|| (pkg.pnpm !== undefined && typeof pkg.pnpm === 'object')
|
|
146
|
+
|| structure.some((s) => s.path === 'packages' || s.path === 'apps');
|
|
147
|
+
if (isMonorepo) {
|
|
148
|
+
return 'monorepo';
|
|
149
|
+
}
|
|
150
|
+
const hasBin = pkg.bin !== undefined;
|
|
151
|
+
if (hasBin) {
|
|
152
|
+
return 'cli';
|
|
153
|
+
}
|
|
154
|
+
const mainType = typeof pkg.main === 'string' ? pkg.main : '';
|
|
155
|
+
if (mainType.includes('.mjs') || mainType.includes('.cjs')) {
|
|
156
|
+
return 'library';
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
if (structure.some((s) => s.path === 'src') && structure.some((s) => s.path === 'public')) {
|
|
160
|
+
return 'application';
|
|
161
|
+
}
|
|
162
|
+
return 'application';
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Infer tech stack from package.json and tsconfig
|
|
166
|
+
*/
|
|
167
|
+
function inferTechStack(pkg, tsconfig) {
|
|
168
|
+
const techStack = {};
|
|
169
|
+
if (tsconfig) {
|
|
170
|
+
techStack.language = 'TypeScript';
|
|
171
|
+
}
|
|
172
|
+
if (pkg) {
|
|
173
|
+
const deps = { ...(pkg.dependencies || {}), ...(pkg.devDependencies || {}) };
|
|
174
|
+
const depNames = Object.keys(deps);
|
|
175
|
+
// Runtime
|
|
176
|
+
if (depNames.some((d) => d.includes('next'))) {
|
|
177
|
+
techStack.runtime = 'Next.js';
|
|
178
|
+
}
|
|
179
|
+
else if (depNames.some((d) => d.includes('express'))) {
|
|
180
|
+
techStack.runtime = 'Express';
|
|
181
|
+
}
|
|
182
|
+
else if (depNames.some((d) => d.includes('fastify'))) {
|
|
183
|
+
techStack.runtime = 'Fastify';
|
|
184
|
+
}
|
|
185
|
+
else if (depNames.some((d) => d.includes('react'))) {
|
|
186
|
+
techStack.runtime = 'React';
|
|
187
|
+
}
|
|
188
|
+
else if (depNames.some((d) => d.includes('vue'))) {
|
|
189
|
+
techStack.runtime = 'Vue';
|
|
190
|
+
}
|
|
191
|
+
// Framework
|
|
192
|
+
if (depNames.includes('next')) {
|
|
193
|
+
techStack.framework = 'Next.js';
|
|
194
|
+
}
|
|
195
|
+
else if (depNames.includes('express')) {
|
|
196
|
+
techStack.framework = 'Express';
|
|
197
|
+
}
|
|
198
|
+
else if (depNames.includes('fastify')) {
|
|
199
|
+
techStack.framework = 'Fastify';
|
|
200
|
+
}
|
|
201
|
+
else if (depNames.includes('react')) {
|
|
202
|
+
techStack.framework = 'React';
|
|
203
|
+
}
|
|
204
|
+
else if (depNames.includes('vue')) {
|
|
205
|
+
techStack.framework = 'Vue';
|
|
206
|
+
}
|
|
207
|
+
// Build tool
|
|
208
|
+
if (depNames.includes('typescript') || depNames.includes('ts-node')) {
|
|
209
|
+
techStack.buildTool = 'tsc';
|
|
210
|
+
}
|
|
211
|
+
if (depNames.includes('vite')) {
|
|
212
|
+
techStack.buildTool = 'vite';
|
|
213
|
+
}
|
|
214
|
+
if (depNames.includes('webpack')) {
|
|
215
|
+
techStack.buildTool = 'webpack';
|
|
216
|
+
}
|
|
217
|
+
if (depNames.includes('esbuild')) {
|
|
218
|
+
techStack.buildTool = 'esbuild';
|
|
219
|
+
}
|
|
220
|
+
if (depNames.includes('turbo')) {
|
|
221
|
+
techStack.buildTool = 'turbo';
|
|
222
|
+
}
|
|
223
|
+
// Test framework
|
|
224
|
+
if (depNames.includes('vitest')) {
|
|
225
|
+
techStack.testFramework = 'vitest';
|
|
226
|
+
}
|
|
227
|
+
else if (depNames.includes('jest')) {
|
|
228
|
+
techStack.testFramework = 'jest';
|
|
229
|
+
}
|
|
230
|
+
else if (depNames.includes('mocha')) {
|
|
231
|
+
techStack.testFramework = 'mocha';
|
|
232
|
+
}
|
|
233
|
+
// Lint
|
|
234
|
+
if (depNames.includes('eslint')) {
|
|
235
|
+
techStack.lintTool = 'eslint';
|
|
236
|
+
}
|
|
237
|
+
else if (depNames.includes('biome')) {
|
|
238
|
+
techStack.lintTool = 'biome';
|
|
239
|
+
}
|
|
240
|
+
if (depNames.includes('prettier')) {
|
|
241
|
+
techStack.lintTool = (techStack.lintTool || '') + ' + prettier';
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
return techStack;
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* Infer dependencies info
|
|
248
|
+
*/
|
|
249
|
+
function inferDependencies(pkg) {
|
|
250
|
+
const result = {};
|
|
251
|
+
if (!pkg) {
|
|
252
|
+
return result;
|
|
253
|
+
}
|
|
254
|
+
const deps = pkg.dependencies || {};
|
|
255
|
+
const devDeps = pkg.devDependencies || {};
|
|
256
|
+
result.core = Object.keys(deps).slice(0, 10);
|
|
257
|
+
result.dev = Object.keys(devDeps).slice(0, 10);
|
|
258
|
+
return result;
|
|
259
|
+
}
|
|
260
|
+
/**
|
|
261
|
+
* Analyze project and generate context JSON
|
|
262
|
+
*/
|
|
263
|
+
function analyzeProject(options = {}) {
|
|
264
|
+
const config = (0, config_1.loadConfig)({ cwd: options.cwd });
|
|
265
|
+
const projectRoot = config.projectRoot || process.cwd();
|
|
266
|
+
const pkg = readPackageJson(projectRoot);
|
|
267
|
+
const tsconfig = readTsconfig(projectRoot);
|
|
268
|
+
const structure = scanDirectoryStructure(projectRoot);
|
|
269
|
+
const projectName = String(pkg?.name || path.basename(projectRoot));
|
|
270
|
+
const description = String(pkg?.description || '');
|
|
271
|
+
const projectType = detectProjectType(pkg, structure);
|
|
272
|
+
const techStack = inferTechStack(pkg, tsconfig);
|
|
273
|
+
const dependencies = inferDependencies(pkg);
|
|
274
|
+
const context = {
|
|
275
|
+
version: '1.0',
|
|
276
|
+
project: {
|
|
277
|
+
name: projectName,
|
|
278
|
+
description: description || undefined,
|
|
279
|
+
type: projectType,
|
|
280
|
+
techStack,
|
|
281
|
+
structure,
|
|
282
|
+
keyFiles: ['package.json', ...(tsconfig ? ['tsconfig.json'] : [])],
|
|
283
|
+
dependencies,
|
|
284
|
+
},
|
|
285
|
+
};
|
|
286
|
+
// Write to output file if specified
|
|
287
|
+
if (options.output) {
|
|
288
|
+
const outputPath = path.resolve(process.cwd(), options.output);
|
|
289
|
+
try {
|
|
290
|
+
fs.mkdirSync(path.dirname(outputPath), { recursive: true });
|
|
291
|
+
fs.writeFileSync(outputPath, JSON.stringify(context, null, 2), 'utf8');
|
|
292
|
+
return {
|
|
293
|
+
success: true,
|
|
294
|
+
context,
|
|
295
|
+
outputPath,
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
catch (error) {
|
|
299
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
300
|
+
return {
|
|
301
|
+
success: false,
|
|
302
|
+
reason: 'file-write-error',
|
|
303
|
+
message: `Failed to write context file: ${errorMessage}`,
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
// Print to stdout
|
|
308
|
+
return {
|
|
309
|
+
success: true,
|
|
310
|
+
context,
|
|
311
|
+
};
|
|
312
|
+
}
|
|
313
|
+
function main() {
|
|
314
|
+
const args = parseArgs(process.argv);
|
|
315
|
+
const result = analyzeProject({
|
|
316
|
+
cwd: args.cwd,
|
|
317
|
+
output: args.output,
|
|
318
|
+
});
|
|
319
|
+
if (!result.success) {
|
|
320
|
+
console.error(`[ANALYZE] ${result.message}`);
|
|
321
|
+
process.exit(1);
|
|
322
|
+
}
|
|
323
|
+
if (result.outputPath) {
|
|
324
|
+
console.log(`[ANALYZE] Project context saved to: ${result.outputPath}`);
|
|
325
|
+
}
|
|
326
|
+
else {
|
|
327
|
+
// Output JSON to stdout
|
|
328
|
+
process.stdout.write(JSON.stringify(result.context, null, 2));
|
|
329
|
+
}
|
|
330
|
+
process.exit(0);
|
|
331
|
+
}
|
|
332
|
+
if (require.main === module) {
|
|
333
|
+
main();
|
|
334
|
+
}
|
|
335
|
+
exports.default = {
|
|
336
|
+
analyzeProject,
|
|
337
|
+
main,
|
|
338
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* commands/archive.ts
|
|
4
|
+
* Archive current task state
|
|
5
|
+
*/
|
|
6
|
+
import { archiveTaskState } from '../lib/archive';
|
|
7
|
+
export declare function main(): void;
|
|
8
|
+
declare const _default: {
|
|
9
|
+
archiveTaskState: typeof archiveTaskState;
|
|
10
|
+
};
|
|
11
|
+
export default _default;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
/**
|
|
4
|
+
* commands/archive.ts
|
|
5
|
+
* Archive current task state
|
|
6
|
+
*/
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
exports.main = main;
|
|
9
|
+
const archive_1 = require("../lib/archive");
|
|
10
|
+
function parseArgs(argv) {
|
|
11
|
+
const args = {};
|
|
12
|
+
for (let i = 2; i < argv.length; i += 1) {
|
|
13
|
+
const token = argv[i] || '';
|
|
14
|
+
if (!token.startsWith('--')) {
|
|
15
|
+
continue;
|
|
16
|
+
}
|
|
17
|
+
const key = token.slice(2);
|
|
18
|
+
const next = argv[i + 1] || '';
|
|
19
|
+
if (!next || next.startsWith('--')) {
|
|
20
|
+
args[key] = true;
|
|
21
|
+
continue;
|
|
22
|
+
}
|
|
23
|
+
args[key] = next;
|
|
24
|
+
i += 1;
|
|
25
|
+
}
|
|
26
|
+
return args;
|
|
27
|
+
}
|
|
28
|
+
function main() {
|
|
29
|
+
const args = parseArgs(process.argv);
|
|
30
|
+
const result = (0, archive_1.archiveTaskState)({
|
|
31
|
+
cwd: args.cwd,
|
|
32
|
+
taskId: args['task-id'],
|
|
33
|
+
eventType: args.event,
|
|
34
|
+
status: args.status,
|
|
35
|
+
phase: args.phase,
|
|
36
|
+
phaseCompleted: args['phase-completed'],
|
|
37
|
+
reviewConclusion: args['review-conclusion'],
|
|
38
|
+
reason: args.reason,
|
|
39
|
+
completedAt: args['completed-at'],
|
|
40
|
+
});
|
|
41
|
+
if ('skipped' in result && result.skipped) {
|
|
42
|
+
console.log(`[ARCHIVE] skipped: ${result.reason}`);
|
|
43
|
+
process.exit(0);
|
|
44
|
+
}
|
|
45
|
+
console.log(`[ARCHIVE] updated: ${result.archivePath}`);
|
|
46
|
+
process.exit(0);
|
|
47
|
+
}
|
|
48
|
+
if (require.main === module) {
|
|
49
|
+
main();
|
|
50
|
+
}
|
|
51
|
+
exports.default = {
|
|
52
|
+
archiveTaskState: archive_1.archiveTaskState,
|
|
53
|
+
};
|