@geeseeker/easyai-dev 3.0.0-alpha.1 → 3.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.
Files changed (165) hide show
  1. package/README.md +90 -88
  2. package/README_AI.md +143 -0
  3. package/bin/easyai-dev.js +127 -1
  4. package/lib/init.js +425 -0
  5. package/lib/server/index.d.ts +14 -0
  6. package/lib/server/index.d.ts.map +1 -0
  7. package/lib/server/index.js +122 -0
  8. package/lib/server/index.js.map +1 -0
  9. package/lib/server/resources/journal-resource.d.ts +8 -0
  10. package/lib/server/resources/journal-resource.d.ts.map +1 -0
  11. package/lib/server/resources/journal-resource.js +46 -0
  12. package/lib/server/resources/journal-resource.js.map +1 -0
  13. package/lib/server/resources/spec-resource.d.ts +11 -0
  14. package/lib/server/resources/spec-resource.d.ts.map +1 -0
  15. package/lib/server/resources/spec-resource.js +126 -0
  16. package/lib/server/resources/spec-resource.js.map +1 -0
  17. package/lib/server/resources/status-resource.d.ts +8 -0
  18. package/lib/server/resources/status-resource.d.ts.map +1 -0
  19. package/lib/server/resources/status-resource.js +36 -0
  20. package/lib/server/resources/status-resource.js.map +1 -0
  21. package/lib/server/resources/subtask-context-resource.d.ts +8 -0
  22. package/lib/server/resources/subtask-context-resource.d.ts.map +1 -0
  23. package/lib/server/resources/subtask-context-resource.js +93 -0
  24. package/lib/server/resources/subtask-context-resource.js.map +1 -0
  25. package/lib/server/resources/task-context-resource.d.ts +8 -0
  26. package/lib/server/resources/task-context-resource.d.ts.map +1 -0
  27. package/lib/server/resources/task-context-resource.js +76 -0
  28. package/lib/server/resources/task-context-resource.js.map +1 -0
  29. package/lib/server/tools/conflict-check.d.ts +7 -0
  30. package/lib/server/tools/conflict-check.d.ts.map +1 -0
  31. package/lib/server/tools/conflict-check.js +242 -0
  32. package/lib/server/tools/conflict-check.js.map +1 -0
  33. package/lib/server/tools/context-budget.d.ts +7 -0
  34. package/lib/server/tools/context-budget.d.ts.map +1 -0
  35. package/lib/server/tools/context-budget.js +178 -0
  36. package/lib/server/tools/context-budget.js.map +1 -0
  37. package/lib/server/tools/context-generate.d.ts +7 -0
  38. package/lib/server/tools/context-generate.d.ts.map +1 -0
  39. package/lib/server/tools/context-generate.js +208 -0
  40. package/lib/server/tools/context-generate.js.map +1 -0
  41. package/lib/server/tools/journal-append.d.ts +7 -0
  42. package/lib/server/tools/journal-append.d.ts.map +1 -0
  43. package/lib/server/tools/journal-append.js +55 -0
  44. package/lib/server/tools/journal-append.js.map +1 -0
  45. package/lib/server/tools/journal-search.d.ts +7 -0
  46. package/lib/server/tools/journal-search.d.ts.map +1 -0
  47. package/lib/server/tools/journal-search.js +63 -0
  48. package/lib/server/tools/journal-search.js.map +1 -0
  49. package/lib/server/tools/plan-validate.d.ts +7 -0
  50. package/lib/server/tools/plan-validate.d.ts.map +1 -0
  51. package/lib/server/tools/plan-validate.js +146 -0
  52. package/lib/server/tools/plan-validate.js.map +1 -0
  53. package/lib/server/tools/spec-validate.d.ts +7 -0
  54. package/lib/server/tools/spec-validate.d.ts.map +1 -0
  55. package/lib/server/tools/spec-validate.js +170 -0
  56. package/lib/server/tools/spec-validate.js.map +1 -0
  57. package/lib/server/tools/subtask-tools.d.ts +12 -0
  58. package/lib/server/tools/subtask-tools.d.ts.map +1 -0
  59. package/lib/server/tools/subtask-tools.js +383 -0
  60. package/lib/server/tools/subtask-tools.js.map +1 -0
  61. package/lib/server/tools/task-append-log.d.ts +7 -0
  62. package/lib/server/tools/task-append-log.d.ts.map +1 -0
  63. package/lib/server/tools/task-append-log.js +108 -0
  64. package/lib/server/tools/task-append-log.js.map +1 -0
  65. package/lib/server/tools/task-cancel.d.ts +7 -0
  66. package/lib/server/tools/task-cancel.d.ts.map +1 -0
  67. package/lib/server/tools/task-cancel.js +104 -0
  68. package/lib/server/tools/task-cancel.js.map +1 -0
  69. package/lib/server/tools/task-create.d.ts +7 -0
  70. package/lib/server/tools/task-create.d.ts.map +1 -0
  71. package/lib/server/tools/task-create.js +98 -0
  72. package/lib/server/tools/task-create.js.map +1 -0
  73. package/lib/server/tools/task-get.d.ts +7 -0
  74. package/lib/server/tools/task-get.d.ts.map +1 -0
  75. package/lib/server/tools/task-get.js +152 -0
  76. package/lib/server/tools/task-get.js.map +1 -0
  77. package/lib/server/tools/task-list.d.ts +7 -0
  78. package/lib/server/tools/task-list.d.ts.map +1 -0
  79. package/lib/server/tools/task-list.js +66 -0
  80. package/lib/server/tools/task-list.js.map +1 -0
  81. package/lib/server/tools/task-transition.d.ts +7 -0
  82. package/lib/server/tools/task-transition.d.ts.map +1 -0
  83. package/lib/server/tools/task-transition.js +259 -0
  84. package/lib/server/tools/task-transition.js.map +1 -0
  85. package/lib/server/tools/worktree-tools.d.ts +17 -0
  86. package/lib/server/tools/worktree-tools.d.ts.map +1 -0
  87. package/lib/server/tools/worktree-tools.js +336 -0
  88. package/lib/server/tools/worktree-tools.js.map +1 -0
  89. package/lib/server/utils/capability-gate.d.ts +50 -0
  90. package/lib/server/utils/capability-gate.d.ts.map +1 -0
  91. package/lib/server/utils/capability-gate.js +146 -0
  92. package/lib/server/utils/capability-gate.js.map +1 -0
  93. package/lib/server/utils/git-utils.d.ts +33 -0
  94. package/lib/server/utils/git-utils.d.ts.map +1 -0
  95. package/lib/server/utils/git-utils.js +84 -0
  96. package/lib/server/utils/git-utils.js.map +1 -0
  97. package/lib/server/utils/hash-utils.d.ts +78 -0
  98. package/lib/server/utils/hash-utils.d.ts.map +1 -0
  99. package/lib/server/utils/hash-utils.js +153 -0
  100. package/lib/server/utils/hash-utils.js.map +1 -0
  101. package/lib/server/utils/journal-utils.d.ts +69 -0
  102. package/lib/server/utils/journal-utils.d.ts.map +1 -0
  103. package/lib/server/utils/journal-utils.js +387 -0
  104. package/lib/server/utils/journal-utils.js.map +1 -0
  105. package/lib/server/utils/status-utils.d.ts +58 -0
  106. package/lib/server/utils/status-utils.d.ts.map +1 -0
  107. package/lib/server/utils/status-utils.js +70 -0
  108. package/lib/server/utils/status-utils.js.map +1 -0
  109. package/lib/server/utils/task-utils.d.ts +104 -0
  110. package/lib/server/utils/task-utils.d.ts.map +1 -0
  111. package/lib/server/utils/task-utils.js +396 -0
  112. package/lib/server/utils/task-utils.js.map +1 -0
  113. package/lib/server/utils/uri-utils.d.ts +9 -0
  114. package/lib/server/utils/uri-utils.d.ts.map +1 -0
  115. package/lib/server/utils/uri-utils.js +21 -0
  116. package/lib/server/utils/uri-utils.js.map +1 -0
  117. package/package.json +30 -21
  118. package/skeleton/.agents/rules/anti-hallucination.md +42 -0
  119. package/skeleton/.agents/rules/coding-standards.md +41 -0
  120. package/skeleton/.agents/rules/project-identity.md +71 -0
  121. package/skeleton/.agents/skills/common-framework-evolve/.gitkeep +0 -0
  122. package/skeleton/.agents/skills/common-framework-evolve/SKILL.md +105 -0
  123. package/skeleton/.agents/skills/common-session-close/.gitkeep +0 -0
  124. package/skeleton/.agents/skills/common-session-close/SKILL.md +83 -0
  125. package/skeleton/.agents/skills/common-spec-update/.gitkeep +0 -0
  126. package/skeleton/.agents/skills/common-spec-update/SKILL.md +87 -0
  127. package/skeleton/.agents/skills/pm-brainstorm/.gitkeep +0 -0
  128. package/skeleton/.agents/skills/pm-brainstorm/SKILL.md +114 -0
  129. package/skeleton/.agents/skills/pm-session-start/.gitkeep +0 -0
  130. package/skeleton/.agents/skills/pm-session-start/SKILL.md +73 -0
  131. package/skeleton/.agents/skills/pm-task-planning/SKILL.md +200 -0
  132. package/skeleton/.agents/skills/pm-task-review/.gitkeep +0 -0
  133. package/skeleton/.agents/skills/pm-task-review/SKILL.md +144 -0
  134. package/skeleton/.agents/skills/worker-check/.gitkeep +0 -0
  135. package/skeleton/.agents/skills/worker-check/SKILL.md +194 -0
  136. package/skeleton/.agents/skills/worker-debug/.gitkeep +0 -0
  137. package/skeleton/.agents/skills/worker-debug/SKILL.md +241 -0
  138. package/skeleton/.agents/skills/worker-implement/.gitkeep +0 -0
  139. package/skeleton/.agents/skills/worker-implement/SKILL.md +192 -0
  140. package/skeleton/.agents/workflows/pm.md +81 -0
  141. package/skeleton/.agents/workflows/worker.md +100 -0
  142. package/skeleton/.docs/README.md +25 -0
  143. package/skeleton/.docs/archive/.gitkeep +0 -0
  144. package/skeleton/.docs/design/.gitkeep +0 -0
  145. package/skeleton/.docs/guides/.gitkeep +0 -0
  146. package/skeleton/.docs/notes/.gitkeep +0 -0
  147. package/skeleton/.docs/requirements/.gitkeep +0 -0
  148. package/skeleton/.trellis/config/config.yaml +48 -0
  149. package/skeleton/.trellis/spec/backend/.gitkeep +0 -0
  150. package/skeleton/.trellis/spec/frontend/.gitkeep +0 -0
  151. package/skeleton/.trellis/spec/guides/.gitkeep +0 -0
  152. package/skeleton/.trellis/spec/guides/external-cli-guide.md +253 -0
  153. package/skeleton/.trellis/spec/guides/task-workflow.md +34 -0
  154. package/skeleton/.trellis/spec/guides/testing.md +32 -0
  155. package/skeleton/.trellis/spec/spec-schema.json +64 -0
  156. package/skeleton/.trellis/tasks/.gitkeep +0 -0
  157. package/skeleton/.trellis/workspace/.gitkeep +0 -0
  158. package/skeleton/README.md +25 -0
  159. package/LICENSE +0 -21
  160. package/src/cli/index.js +0 -40
  161. package/src/commands/init.js +0 -37
  162. package/src/commands/update.js +0 -33
  163. package/templates/README.md +0 -22
  164. package/templates/agents/README.md +0 -4
  165. package/templates/trellis/README.md +0 -4
@@ -0,0 +1,126 @@
1
+ import * as fs from "node:fs";
2
+ import * as path from "node:path";
3
+ import { ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
4
+ import { getProjectRoot } from "../utils/task-utils.js";
5
+ /**
6
+ * 读取 spec 文件内容的共享逻辑
7
+ * @param category - spec 分类(如 backend, frontend, guides)
8
+ * @param name - spec 名称(不带 .md 后缀)
9
+ * @param uriHref - 原始请求的 URI(用于响应)
10
+ */
11
+ function readSpec(category, name, uriHref) {
12
+ const projectRoot = getProjectRoot();
13
+ // 路径遍历防护:拒绝包含 .. 或路径分隔符的参数
14
+ if (category.includes("..") || category.includes("/") ||
15
+ category.includes("\\") ||
16
+ name.includes("..") || name.includes("/") || name.includes("\\")) {
17
+ return {
18
+ contents: [
19
+ {
20
+ uri: uriHref,
21
+ text: JSON.stringify({
22
+ error: true,
23
+ message: "非法路径: category 和 name 不允许包含 '..' 或路径分隔符",
24
+ }),
25
+ },
26
+ ],
27
+ };
28
+ }
29
+ // 防止 .md.md:如果 name 已含 .md 后缀则不再追加
30
+ const fileName = name.endsWith(".md") ? name : `${name}.md`;
31
+ const specRoot = path.join(projectRoot, ".trellis", "spec") + path.sep;
32
+ const specPath = path.resolve(projectRoot, ".trellis", "spec", category, fileName);
33
+ // 二次校验:确保解析后的路径仍在 spec 目录内
34
+ if (!specPath.startsWith(specRoot)) {
35
+ return {
36
+ contents: [
37
+ {
38
+ uri: uriHref,
39
+ text: JSON.stringify({
40
+ error: true,
41
+ message: "路径越界: 请求的路径超出 .trellis/spec/ 范围",
42
+ }),
43
+ },
44
+ ],
45
+ };
46
+ }
47
+ // 检查 spec 文件是否存在
48
+ if (!fs.existsSync(specPath)) {
49
+ return {
50
+ contents: [
51
+ {
52
+ uri: uriHref,
53
+ text: JSON.stringify({
54
+ category,
55
+ name,
56
+ error: `规格文档不存在: ${specPath}`,
57
+ }),
58
+ },
59
+ ],
60
+ };
61
+ }
62
+ // 读取 spec 文件内容
63
+ const content = fs.readFileSync(specPath, "utf-8");
64
+ return {
65
+ contents: [
66
+ {
67
+ uri: uriHref,
68
+ text: JSON.stringify({ category, name, path: specPath, content }, null, 2),
69
+ },
70
+ ],
71
+ };
72
+ }
73
+ /**
74
+ * 从 Variables 中提取字符串值
75
+ * MCP SDK 的 Variables 类型为 Record<string, string | string[]>,
76
+ * 此函数安全地提取第一个字符串值。
77
+ */
78
+ function extractParam(variables, key) {
79
+ const val = variables[key];
80
+ if (Array.isArray(val))
81
+ return val[0] ?? "";
82
+ return val ?? "";
83
+ }
84
+ /**
85
+ * spec resource handler 的共享错误处理包装
86
+ */
87
+ function createSpecHandler() {
88
+ return async (uri, variables) => {
89
+ try {
90
+ const category = extractParam(variables, "category");
91
+ const name = extractParam(variables, "name");
92
+ return readSpec(category, name, uri.href);
93
+ }
94
+ catch (error) {
95
+ return {
96
+ contents: [
97
+ {
98
+ uri: uri.href,
99
+ text: JSON.stringify({
100
+ error: true,
101
+ message: error instanceof Error ? error.message : String(error),
102
+ }),
103
+ },
104
+ ],
105
+ };
106
+ }
107
+ };
108
+ }
109
+ /**
110
+ * 注册 spec 资源
111
+ * 支持两种 URI 协议:
112
+ * 1. trellis://spec/{category}/{name} — 完整路径
113
+ * 2. spec://{category}/{name} — 简写语法(自动展开)
114
+ *
115
+ * 两者读取相同的 .trellis/spec/{category}/{name}.md 文件。
116
+ */
117
+ export function register(server) {
118
+ // 完整路径:trellis://spec/{category}/{name}
119
+ server.resource("规格文档", new ResourceTemplate("trellis://spec/{category}/{name}", {
120
+ list: undefined,
121
+ }), createSpecHandler());
122
+ // 简写语法:spec://{category}/{name}
123
+ // 等价于 trellis://spec/{category}/{name}
124
+ server.resource("规格文档(简写)", new ResourceTemplate("spec://{category}/{name}", { list: undefined }), createSpecHandler());
125
+ }
126
+ //# sourceMappingURL=spec-resource.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spec-resource.js","sourceRoot":"","sources":["../../src/resources/spec-resource.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ /**
3
+ * 注册 status 资源
4
+ * URI: trellis://status
5
+ * 返回: 项目总览(Git branch/status/recent commits + 任务统计 + 最新日志摘要)
6
+ */
7
+ export declare function register(server: McpServer): void;
8
+ //# sourceMappingURL=status-resource.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"status-resource.d.ts","sourceRoot":"","sources":["../../src/resources/status-resource.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAGpE;;;;GAIG;AACH,wBAAgB,QAAQ,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAgChD"}
@@ -0,0 +1,36 @@
1
+ import { getProjectStatusData } from '../utils/status-utils.js';
2
+ /**
3
+ * 注册 status 资源
4
+ * URI: trellis://status
5
+ * 返回: 项目总览(Git branch/status/recent commits + 任务统计 + 最新日志摘要)
6
+ */
7
+ export function register(server) {
8
+ server.resource('项目状态', 'trellis://status', async () => {
9
+ try {
10
+ // 直接使用共享的状态聚合器,返回完整结果
11
+ const statusData = getProjectStatusData(5, 3);
12
+ return {
13
+ contents: [
14
+ {
15
+ uri: 'trellis://status',
16
+ text: JSON.stringify(statusData, null, 2),
17
+ },
18
+ ],
19
+ };
20
+ }
21
+ catch (error) {
22
+ return {
23
+ contents: [
24
+ {
25
+ uri: 'trellis://status',
26
+ text: JSON.stringify({
27
+ error: true,
28
+ message: error instanceof Error ? error.message : String(error),
29
+ }),
30
+ },
31
+ ],
32
+ };
33
+ }
34
+ });
35
+ }
36
+ //# sourceMappingURL=status-resource.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"status-resource.js","sourceRoot":"","sources":["../../src/resources/status-resource.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AAEhE;;;;GAIG;AACH,MAAM,UAAU,QAAQ,CAAC,MAAiB;IACxC,MAAM,CAAC,QAAQ,CACb,MAAM,EACN,kBAAkB,EAClB,KAAK,IAAI,EAAE;QACT,IAAI,CAAC;YACH,sBAAsB;YACtB,MAAM,UAAU,GAAG,oBAAoB,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;YAE9C,OAAO;gBACL,QAAQ,EAAE;oBACR;wBACE,GAAG,EAAE,kBAAkB;wBACvB,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,UAAU,EAAE,IAAI,EAAE,CAAC,CAAC;qBAC1C;iBACF;aACF,CAAC;QACJ,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO;gBACL,QAAQ,EAAE;oBACR;wBACE,GAAG,EAAE,kBAAkB;wBACvB,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;4BACnB,KAAK,EAAE,IAAI;4BACX,OAAO,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC;yBAChE,CAAC;qBACH;iBACF;aACF,CAAC;QACJ,CAAC;IACH,CAAC,CACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,8 @@
1
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ /**
3
+ * 注册 subtask-context 资源
4
+ * URI: trellis://tasks/{id}/subtasks/{sid}/context
5
+ * 返回: 子任务上下文
6
+ */
7
+ export declare function register(server: McpServer): void;
8
+ //# sourceMappingURL=subtask-context-resource.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"subtask-context-resource.d.ts","sourceRoot":"","sources":["../../src/resources/subtask-context-resource.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAKpE;;;;GAIG;AACH,wBAAgB,QAAQ,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CA2FhD"}
@@ -0,0 +1,93 @@
1
+ import * as fs from 'node:fs';
2
+ import * as path from 'node:path';
3
+ import { ResourceTemplate } from '@modelcontextprotocol/sdk/server/mcp.js';
4
+ import { getTaskDir } from '../utils/task-utils.js';
5
+ import { parseTrellisUri } from '../utils/uri-utils.js';
6
+ /**
7
+ * 注册 subtask-context 资源
8
+ * URI: trellis://tasks/{id}/subtasks/{sid}/context
9
+ * 返回: 子任务上下文
10
+ */
11
+ export function register(server) {
12
+ server.resource('子任务上下文', new ResourceTemplate('trellis://tasks/{id}/subtasks/{sid}/context', { list: undefined }), async (uri) => {
13
+ try {
14
+ // 使用共享的 URI 解析工具
15
+ const fullPath = parseTrellisUri(uri);
16
+ const match = fullPath.match(/^tasks\/([^/]+)\/subtasks\/([^/]+)\/context$/);
17
+ if (!match) {
18
+ return {
19
+ contents: [
20
+ {
21
+ uri: uri.href,
22
+ text: JSON.stringify({
23
+ error: true,
24
+ message: '无效的子任务 URI 格式,预期: trellis://tasks/{id}/subtasks/{sid}/context',
25
+ }),
26
+ },
27
+ ],
28
+ };
29
+ }
30
+ const taskId = match[1];
31
+ const subtaskId = match[2];
32
+ const taskDir = getTaskDir(taskId);
33
+ // 尝试多个可能的子任务上下文位置
34
+ const possiblePaths = [
35
+ // 子目录中的 context.jsonl
36
+ path.join(taskDir, 'subtasks', `${subtaskId}.jsonl`),
37
+ path.join(taskDir, 'subtasks', subtaskId, 'context.jsonl'),
38
+ // 子任务的 markdown 文件
39
+ path.join(taskDir, 'subtasks', `${subtaskId}.md`),
40
+ ];
41
+ let foundContent = null;
42
+ let foundPath = '';
43
+ for (const possiblePath of possiblePaths) {
44
+ if (fs.existsSync(possiblePath)) {
45
+ foundContent = fs.readFileSync(possiblePath, 'utf-8');
46
+ foundPath = possiblePath;
47
+ break;
48
+ }
49
+ }
50
+ if (!foundContent) {
51
+ return {
52
+ contents: [
53
+ {
54
+ uri: uri.href,
55
+ text: JSON.stringify({
56
+ task_id: taskId,
57
+ subtask_id: subtaskId,
58
+ context: `子任务 ${subtaskId} 的上下文不存在`,
59
+ }),
60
+ },
61
+ ],
62
+ };
63
+ }
64
+ return {
65
+ contents: [
66
+ {
67
+ uri: uri.href,
68
+ text: JSON.stringify({
69
+ task_id: taskId,
70
+ subtask_id: subtaskId,
71
+ source: foundPath,
72
+ context: foundContent,
73
+ }, null, 2),
74
+ },
75
+ ],
76
+ };
77
+ }
78
+ catch (error) {
79
+ return {
80
+ contents: [
81
+ {
82
+ uri: uri.href,
83
+ text: JSON.stringify({
84
+ error: true,
85
+ message: error instanceof Error ? error.message : String(error),
86
+ }),
87
+ },
88
+ ],
89
+ };
90
+ }
91
+ });
92
+ }
93
+ //# sourceMappingURL=subtask-context-resource.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"subtask-context-resource.js","sourceRoot":"","sources":["../../src/resources/subtask-context-resource.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAElC,OAAO,EAAE,gBAAgB,EAAE,MAAM,yCAAyC,CAAC;AAC3E,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AACpD,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAExD;;;;GAIG;AACH,MAAM,UAAU,QAAQ,CAAC,MAAiB;IACxC,MAAM,CAAC,QAAQ,CACb,QAAQ,EACR,IAAI,gBAAgB,CAAC,6CAA6C,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC,EACxF,KAAK,EAAE,GAAQ,EAAE,EAAE;QACjB,IAAI,CAAC;YACH,iBAAiB;YACjB,MAAM,QAAQ,GAAG,eAAe,CAAC,GAAG,CAAC,CAAC;YACtC,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC,8CAA8C,CAAC,CAAC;YAE7E,IAAI,CAAC,KAAK,EAAE,CAAC;gBACX,OAAO;oBACL,QAAQ,EAAE;wBACR;4BACE,GAAG,EAAE,GAAG,CAAC,IAAI;4BACb,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;gCACnB,KAAK,EAAE,IAAI;gCACX,OAAO,EAAE,+DAA+D;6BACzE,CAAC;yBACH;qBACF;iBACF,CAAC;YACJ,CAAC;YAED,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;YACxB,MAAM,SAAS,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;YAC3B,MAAM,OAAO,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC;YAEnC,kBAAkB;YAClB,MAAM,aAAa,GAAG;gBACpB,sBAAsB;gBACtB,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,UAAU,EAAE,GAAG,SAAS,QAAQ,CAAC;gBACpD,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,eAAe,CAAC;gBAC1D,mBAAmB;gBACnB,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,UAAU,EAAE,GAAG,SAAS,KAAK,CAAC;aAClD,CAAC;YAEF,IAAI,YAAY,GAAkB,IAAI,CAAC;YACvC,IAAI,SAAS,GAAG,EAAE,CAAC;YAEnB,KAAK,MAAM,YAAY,IAAI,aAAa,EAAE,CAAC;gBACzC,IAAI,EAAE,CAAC,UAAU,CAAC,YAAY,CAAC,EAAE,CAAC;oBAChC,YAAY,GAAG,EAAE,CAAC,YAAY,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC;oBACtD,SAAS,GAAG,YAAY,CAAC;oBACzB,MAAM;gBACR,CAAC;YACH,CAAC;YAED,IAAI,CAAC,YAAY,EAAE,CAAC;gBAClB,OAAO;oBACL,QAAQ,EAAE;wBACR;4BACE,GAAG,EAAE,GAAG,CAAC,IAAI;4BACb,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;gCACnB,OAAO,EAAE,MAAM;gCACf,UAAU,EAAE,SAAS;gCACrB,OAAO,EAAE,OAAO,SAAS,UAAU;6BACpC,CAAC;yBACH;qBACF;iBACF,CAAC;YACJ,CAAC;YAED,OAAO;gBACL,QAAQ,EAAE;oBACR;wBACE,GAAG,EAAE,GAAG,CAAC,IAAI;wBACb,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;4BACnB,OAAO,EAAE,MAAM;4BACf,UAAU,EAAE,SAAS;4BACrB,MAAM,EAAE,SAAS;4BACjB,OAAO,EAAE,YAAY;yBACtB,EAAE,IAAI,EAAE,CAAC,CAAC;qBACZ;iBACF;aACF,CAAC;QACJ,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO;gBACL,QAAQ,EAAE;oBACR;wBACE,GAAG,EAAE,GAAG,CAAC,IAAI;wBACb,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;4BACnB,KAAK,EAAE,IAAI;4BACX,OAAO,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC;yBAChE,CAAC;qBACH;iBACF;aACF,CAAC;QACJ,CAAC;IACH,CAAC,CACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,8 @@
1
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ /**
3
+ * 注册 task-context 资源
4
+ * URI: trellis://tasks/{id}/context
5
+ * 返回: 任务的 context.jsonl 内容
6
+ */
7
+ export declare function register(server: McpServer): void;
8
+ //# sourceMappingURL=task-context-resource.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"task-context-resource.d.ts","sourceRoot":"","sources":["../../src/resources/task-context-resource.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAKpE;;;;GAIG;AACH,wBAAgB,QAAQ,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAwEhD"}
@@ -0,0 +1,76 @@
1
+ import * as fs from 'node:fs';
2
+ import * as path from 'node:path';
3
+ import { ResourceTemplate } from '@modelcontextprotocol/sdk/server/mcp.js';
4
+ import { getTaskDir } from '../utils/task-utils.js';
5
+ import { parseTrellisUri } from '../utils/uri-utils.js';
6
+ /**
7
+ * 注册 task-context 资源
8
+ * URI: trellis://tasks/{id}/context
9
+ * 返回: 任务的 context.jsonl 内容
10
+ */
11
+ export function register(server) {
12
+ server.resource('任务上下文', new ResourceTemplate('trellis://tasks/{id}/context', { list: undefined }), async (uri) => {
13
+ try {
14
+ // 使用共享的 URI 解析工具
15
+ const fullPath = parseTrellisUri(uri);
16
+ const match = fullPath.match(/^tasks\/([^/]+)\/context$/);
17
+ if (!match) {
18
+ return {
19
+ contents: [
20
+ {
21
+ uri: uri.href,
22
+ text: JSON.stringify({
23
+ error: true,
24
+ message: '无效的任务 URI 格式,预期: trellis://tasks/{id}/context',
25
+ }),
26
+ },
27
+ ],
28
+ };
29
+ }
30
+ const taskId = match[1];
31
+ const taskDir = getTaskDir(taskId);
32
+ const contextPath = path.join(taskDir, 'context.jsonl');
33
+ // 检查 context.jsonl 是否存在
34
+ if (!fs.existsSync(contextPath)) {
35
+ return {
36
+ contents: [
37
+ {
38
+ uri: uri.href,
39
+ text: JSON.stringify({
40
+ task_id: taskId,
41
+ context: '该任务暂无上下文记录(context.jsonl 不存在)',
42
+ }),
43
+ },
44
+ ],
45
+ };
46
+ }
47
+ // 读取 context.jsonl 内容
48
+ const content = fs.readFileSync(contextPath, 'utf-8');
49
+ return {
50
+ contents: [
51
+ {
52
+ uri: uri.href,
53
+ text: JSON.stringify({
54
+ task_id: taskId,
55
+ context: content,
56
+ }, null, 2),
57
+ },
58
+ ],
59
+ };
60
+ }
61
+ catch (error) {
62
+ return {
63
+ contents: [
64
+ {
65
+ uri: uri.href,
66
+ text: JSON.stringify({
67
+ error: true,
68
+ message: error instanceof Error ? error.message : String(error),
69
+ }),
70
+ },
71
+ ],
72
+ };
73
+ }
74
+ });
75
+ }
76
+ //# sourceMappingURL=task-context-resource.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"task-context-resource.js","sourceRoot":"","sources":["../../src/resources/task-context-resource.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAElC,OAAO,EAAE,gBAAgB,EAAE,MAAM,yCAAyC,CAAC;AAC3E,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AACpD,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAExD;;;;GAIG;AACH,MAAM,UAAU,QAAQ,CAAC,MAAiB;IACxC,MAAM,CAAC,QAAQ,CACb,OAAO,EACP,IAAI,gBAAgB,CAAC,8BAA8B,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC,EACzE,KAAK,EAAE,GAAQ,EAAE,EAAE;QACjB,IAAI,CAAC;YACH,iBAAiB;YACjB,MAAM,QAAQ,GAAG,eAAe,CAAC,GAAG,CAAC,CAAC;YACtC,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC,2BAA2B,CAAC,CAAC;YAE1D,IAAI,CAAC,KAAK,EAAE,CAAC;gBACX,OAAO;oBACL,QAAQ,EAAE;wBACR;4BACE,GAAG,EAAE,GAAG,CAAC,IAAI;4BACb,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;gCACnB,KAAK,EAAE,IAAI;gCACX,OAAO,EAAE,+CAA+C;6BACzD,CAAC;yBACH;qBACF;iBACF,CAAC;YACJ,CAAC;YAED,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;YACxB,MAAM,OAAO,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC;YACnC,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,eAAe,CAAC,CAAC;YAExD,wBAAwB;YACxB,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,WAAW,CAAC,EAAE,CAAC;gBAChC,OAAO;oBACL,QAAQ,EAAE;wBACR;4BACE,GAAG,EAAE,GAAG,CAAC,IAAI;4BACb,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;gCACnB,OAAO,EAAE,MAAM;gCACf,OAAO,EAAE,+BAA+B;6BACzC,CAAC;yBACH;qBACF;iBACF,CAAC;YACJ,CAAC;YAED,sBAAsB;YACtB,MAAM,OAAO,GAAG,EAAE,CAAC,YAAY,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;YAEtD,OAAO;gBACL,QAAQ,EAAE;oBACR;wBACE,GAAG,EAAE,GAAG,CAAC,IAAI;wBACb,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;4BACnB,OAAO,EAAE,MAAM;4BACf,OAAO,EAAE,OAAO;yBACjB,EAAE,IAAI,EAAE,CAAC,CAAC;qBACZ;iBACF;aACF,CAAC;QACJ,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO;gBACL,QAAQ,EAAE;oBACR;wBACE,GAAG,EAAE,GAAG,CAAC,IAAI;wBACb,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;4BACnB,KAAK,EAAE,IAAI;4BACX,OAAO,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC;yBAChE,CAAC;qBACH;iBACF;aACF,CAAC;QACJ,CAAC;IACH,CAAC,CACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,7 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ /**
3
+ * 注册 conflict_check 工具
4
+ * 文件范围重叠检测(架构 §4.2)
5
+ */
6
+ export declare function register(server: McpServer): void;
7
+ //# sourceMappingURL=conflict-check.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"conflict-check.d.ts","sourceRoot":"","sources":["../../src/tools/conflict-check.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAiGpE;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CA6JhD"}
@@ -0,0 +1,242 @@
1
+ import * as fs from "node:fs";
2
+ import * as path from "node:path";
3
+ import { z } from "zod";
4
+ import { listTaskDirs, getTasksDir, parseTaskMd, } from "../utils/task-utils.js";
5
+ import { checkCapability, capabilityError } from "../utils/capability-gate.js";
6
+ // ============ 辅助函数 ============
7
+ /**
8
+ * 简易 glob 匹配(支持 * 和 ** 通配符)
9
+ * - `*` 匹配不含 `/` 的任意字符
10
+ * - `**` 匹配包含 `/` 的任意路径
11
+ * @param pattern - glob 模式
12
+ * @param filePath - 待匹配的文件路径
13
+ * @returns 是否匹配
14
+ */
15
+ function globMatch(pattern, filePath) {
16
+ // 规范化路径分隔符
17
+ const normalizedPattern = pattern.replace(/\\/g, "/");
18
+ const normalizedPath = filePath.replace(/\\/g, "/");
19
+ // 精确匹配
20
+ if (normalizedPattern === normalizedPath) {
21
+ return true;
22
+ }
23
+ // 将 glob 模式转换为正则表达式
24
+ // 顺序关键:占位符 → 转义 → 单字符通配 → GLOBSTAR 展开
25
+ let regexStr = normalizedPattern
26
+ // ** 先标记为占位符(必须在转义前处理)
27
+ .replace(/\*\*/g, "§GLOBSTAR§")
28
+ // 转义正则特殊字符(除了 * 和 ?)
29
+ .replace(/[.+^${}()|[\]\\]/g, "\\$&")
30
+ // * 匹配不含 / 的任意字符(必须在 GLOBSTAR 展开前)
31
+ .replace(/\*/g, "[^/]*")
32
+ // ? 匹配单个非 / 字符(必须在 GLOBSTAR 展开前)
33
+ .replace(/\?/g, "[^/]")
34
+ // §GLOBSTAR§/ → (.*/)?(零或多层目录)
35
+ .replace(/§GLOBSTAR§\//g, "(.*/)?")
36
+ // 尾部 §GLOBSTAR§ → .*(匹配任意路径)
37
+ .replace(/§GLOBSTAR§/g, ".*");
38
+ regexStr = `^${regexStr}$`;
39
+ try {
40
+ const regex = new RegExp(regexStr);
41
+ return regex.test(normalizedPath);
42
+ }
43
+ catch {
44
+ // 正则构建失败时回退为精确匹配
45
+ return normalizedPattern === normalizedPath;
46
+ }
47
+ }
48
+ /**
49
+ * 解析 file_scope 字符串为文件路径列表
50
+ * 支持逗号分隔和换行分隔
51
+ * @param fileScope - file_scope 字段的值
52
+ * @returns 文件路径/glob 模式列表
53
+ */
54
+ function parseFileScope(fileScope) {
55
+ if (!fileScope)
56
+ return [];
57
+ return fileScope
58
+ .split(/[,\n]/)
59
+ .map((s) => s.trim())
60
+ .filter((s) => s.length > 0);
61
+ }
62
+ // ============ 注册函数 ============
63
+ /**
64
+ * 注册 conflict_check 工具
65
+ * 文件范围重叠检测(架构 §4.2)
66
+ */
67
+ export function register(server) {
68
+ server.tool("conflict_check", "检测文件范围与现有活跃任务的重叠冲突。" +
69
+ "在 PM 创建新任务前调用,防止多个任务修改同一文件导致合并冲突。" +
70
+ "返回冲突报告(冲突文件 + 涉及任务 ID + 建议)。", {
71
+ files: z
72
+ .array(z.string())
73
+ .min(1)
74
+ .describe("待检查的文件路径列表(相对于项目根,支持 glob 模式," +
75
+ "如 src/auth/*.ts)"),
76
+ tasks: z
77
+ .array(z.string())
78
+ .optional()
79
+ .describe("可选的任务 ID 列表(不指定则检查所有活跃任务)"),
80
+ role: z
81
+ .string()
82
+ .optional()
83
+ .describe("调用者角色(pm / 组长)"),
84
+ }, async ({ files, tasks, role }) => {
85
+ // Capability Gate 校验(PM + 组长)
86
+ const rejectReason = checkCapability(role, "conflict_check");
87
+ if (rejectReason) {
88
+ return capabilityError(rejectReason);
89
+ }
90
+ try {
91
+ const conflicts = [];
92
+ const tasksDir = getTasksDir();
93
+ // 获取要检查的任务列表
94
+ let taskDirNames;
95
+ if (tasks && tasks.length > 0) {
96
+ // 校验任务 ID 安全性(复用项目统一的路径穿越防护)
97
+ for (const taskId of tasks) {
98
+ if (taskId.includes("..") ||
99
+ taskId.includes("/") ||
100
+ taskId.includes("\\")) {
101
+ return {
102
+ content: [
103
+ {
104
+ type: "text",
105
+ text: JSON.stringify({
106
+ error: true,
107
+ message: `非法的任务 ID: ${taskId}`,
108
+ }),
109
+ },
110
+ ],
111
+ isError: true,
112
+ };
113
+ }
114
+ }
115
+ taskDirNames = tasks;
116
+ }
117
+ else {
118
+ // 检查所有任务目录
119
+ taskDirNames = listTaskDirs();
120
+ }
121
+ // 活跃状态(排除已完成/已归档/已取消)
122
+ const ACTIVE_STATUSES = new Set([
123
+ "pending",
124
+ "in_progress",
125
+ "under_review",
126
+ "rejected",
127
+ ]);
128
+ let checkedTaskCount = 0;
129
+ for (const taskDirName of taskDirNames) {
130
+ const taskMdPath = path.join(tasksDir, taskDirName, "task.md");
131
+ // 跳过没有 task.md 的目录
132
+ if (!fs.existsSync(taskMdPath))
133
+ continue;
134
+ const content = fs.readFileSync(taskMdPath, "utf-8");
135
+ // 复用项目统一的 frontmatter 解析器(DRY)
136
+ const { metadata } = parseTaskMd(content);
137
+ // 只检查活跃状态的任务
138
+ if (!ACTIVE_STATUSES.has(metadata.status))
139
+ continue;
140
+ checkedTaskCount++;
141
+ const taskFiles = parseFileScope(metadata.file_scope || "");
142
+ if (taskFiles.length === 0)
143
+ continue;
144
+ // 检查每个输入文件是否与任务文件范围冲突
145
+ for (const inputFile of files) {
146
+ for (const taskFile of taskFiles) {
147
+ // 双向 glob 匹配:
148
+ // 1. 输入文件匹配任务的 glob 模式
149
+ // 2. 任务文件匹配输入的 glob 模式
150
+ const inputMatchesTask = globMatch(taskFile, inputFile);
151
+ const taskMatchesInput = globMatch(inputFile, taskFile);
152
+ if (inputMatchesTask || taskMatchesInput) {
153
+ conflicts.push({
154
+ file: inputFile,
155
+ conflicting_task: taskDirName,
156
+ task_title: metadata.title || taskDirName,
157
+ task_status: metadata.status,
158
+ overlap_type: inputFile === taskFile ? "exact" : "glob",
159
+ });
160
+ }
161
+ }
162
+ }
163
+ }
164
+ // 去重(同一文件 + 同一任务只报告一次)
165
+ const uniqueConflicts = deduplicateConflicts(conflicts);
166
+ // 生成建议
167
+ const advice = generateAdvice(uniqueConflicts);
168
+ const report = {
169
+ has_conflicts: uniqueConflicts.length > 0,
170
+ conflicts: uniqueConflicts,
171
+ checked_files: files.length,
172
+ checked_tasks: checkedTaskCount,
173
+ advice,
174
+ };
175
+ return {
176
+ content: [
177
+ {
178
+ type: "text",
179
+ text: JSON.stringify(report, null, 2),
180
+ },
181
+ ],
182
+ };
183
+ }
184
+ catch (error) {
185
+ return {
186
+ content: [
187
+ {
188
+ type: "text",
189
+ text: JSON.stringify({
190
+ error: true,
191
+ message: error instanceof Error ? error.message : String(error),
192
+ }),
193
+ },
194
+ ],
195
+ isError: true,
196
+ };
197
+ }
198
+ });
199
+ }
200
+ // ============ 辅助函数 ============
201
+ /**
202
+ * 去重冲突记录(同一文件 + 同一任务只保留一条)
203
+ */
204
+ function deduplicateConflicts(conflicts) {
205
+ const seen = new Set();
206
+ return conflicts.filter((c) => {
207
+ const key = `${c.file}::${c.conflicting_task}`;
208
+ if (seen.has(key))
209
+ return false;
210
+ seen.add(key);
211
+ return true;
212
+ });
213
+ }
214
+ /**
215
+ * 根据冲突情况生成建议
216
+ */
217
+ function generateAdvice(conflicts) {
218
+ if (conflicts.length === 0) {
219
+ return "✅ 未检测到文件范围冲突,可以安全创建任务。";
220
+ }
221
+ const taskCount = new Set(conflicts.map((c) => c.conflicting_task)).size;
222
+ const lines = [
223
+ `⚠️ 检测到 ${conflicts.length} 个文件冲突,` +
224
+ `涉及 ${taskCount} 个现有任务。建议:`,
225
+ ];
226
+ // 分析冲突类型给出具体建议
227
+ const inProgressConflicts = conflicts.filter((c) => c.task_status === "in_progress");
228
+ const pendingConflicts = conflicts.filter((c) => c.task_status === "pending");
229
+ if (inProgressConflicts.length > 0) {
230
+ lines.push("1. 执行中的任务有文件冲突 → " +
231
+ "建议使用 worktree_create() 物理隔离 " +
232
+ "或建立任务依赖(等前序任务完成)");
233
+ }
234
+ if (pendingConflicts.length > 0) {
235
+ lines.push(`${inProgressConflicts.length > 0 ? "2" : "1"}. ` +
236
+ "待分配的任务有文件冲突 → " +
237
+ "建议调整文件范围避开冲突 " +
238
+ "或合并为同一任务");
239
+ }
240
+ return lines.join("\n");
241
+ }
242
+ //# sourceMappingURL=conflict-check.js.map