@aigne/doc-smith 0.9.8-alpha.1 → 0.9.8-alpha.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/agentic-agents/common/base-info.md +5 -2
- package/agentic-agents/common/planner.md +69 -16
- package/agentic-agents/common/worker.md +42 -0
- package/agentic-agents/create/index.yaml +52 -13
- package/agentic-agents/create/set-custom-prompt.mjs +2 -18
- package/agentic-agents/detail/index.yaml +15 -2
- package/agentic-agents/predict-resources/index.yaml +44 -0
- package/agentic-agents/predict-resources/instructions.md +61 -0
- package/agentic-agents/structure/index.yaml +29 -6
- package/agentic-agents/structure/objective.md +1 -1
- package/agentic-agents/utils/init-workspace-cache.mjs +171 -0
- package/agentic-agents/utils/load-base-sources.mjs +1 -77
- package/agentic-agents/workspace-cache-sharing-design.md +671 -0
- package/package.json +1 -1
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import { readdir } from "node:fs/promises";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
|
|
4
|
+
const MAX_DEPTH = 2;
|
|
5
|
+
const MAX_CHARS = 10000;
|
|
6
|
+
const EXCLUDE_DIRS = new Set([
|
|
7
|
+
"node_modules",
|
|
8
|
+
".git",
|
|
9
|
+
".github",
|
|
10
|
+
"dist",
|
|
11
|
+
"build",
|
|
12
|
+
"coverage",
|
|
13
|
+
".next",
|
|
14
|
+
".nuxt",
|
|
15
|
+
"out",
|
|
16
|
+
"__pycache__",
|
|
17
|
+
"venv",
|
|
18
|
+
".venv",
|
|
19
|
+
".turbo",
|
|
20
|
+
".cache",
|
|
21
|
+
]);
|
|
22
|
+
const INCLUDE_HIDDEN = new Set([".github"]);
|
|
23
|
+
|
|
24
|
+
export default async function initWorkspaceCache(_input, options) {
|
|
25
|
+
try {
|
|
26
|
+
const startTime = Date.now();
|
|
27
|
+
const directoryTree = await buildDirectoryTree(options);
|
|
28
|
+
const elapsed = Date.now() - startTime;
|
|
29
|
+
|
|
30
|
+
// 存储到 userContext
|
|
31
|
+
options.context.userContext.workspaceCache = {
|
|
32
|
+
static: {
|
|
33
|
+
tree: directoryTree,
|
|
34
|
+
metadata: {
|
|
35
|
+
cachedAt: new Date().toISOString(),
|
|
36
|
+
size: directoryTree.length,
|
|
37
|
+
maxDepth: MAX_DEPTH,
|
|
38
|
+
buildTimeMs: elapsed,
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
console.log(
|
|
44
|
+
`Workspace directory tree cached: ${directoryTree.length} chars, max depth ${MAX_DEPTH}, took ${elapsed}ms`,
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
return { message: "Workspace cache initialized" };
|
|
48
|
+
} catch (error) {
|
|
49
|
+
console.warn("Failed to initialize workspace cache:", error.message);
|
|
50
|
+
// 失败时设置空缓存,不阻塞执行
|
|
51
|
+
options.context.userContext.workspaceCache = {
|
|
52
|
+
static: {
|
|
53
|
+
tree: "",
|
|
54
|
+
metadata: {
|
|
55
|
+
error: error.message,
|
|
56
|
+
cachedAt: new Date().toISOString(),
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
return { message: "Workspace cache initialization failed, will use on-demand loading" };
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
async function buildDirectoryTree(_options) {
|
|
65
|
+
const entries = [];
|
|
66
|
+
const cwd = process.cwd();
|
|
67
|
+
|
|
68
|
+
// 递归扫描目录
|
|
69
|
+
// 从当前工作目录开始扫描
|
|
70
|
+
async function scanDir(dirPath, relativePath, depth) {
|
|
71
|
+
if (depth > MAX_DEPTH) return;
|
|
72
|
+
|
|
73
|
+
try {
|
|
74
|
+
const items = await readdir(dirPath, { withFileTypes: true });
|
|
75
|
+
|
|
76
|
+
for (const item of items) {
|
|
77
|
+
const itemRelativePath = relativePath ? `${relativePath}/${item.name}` : item.name;
|
|
78
|
+
const itemFullPath = join(dirPath, item.name);
|
|
79
|
+
|
|
80
|
+
// 过滤规则
|
|
81
|
+
if (item.isDirectory()) {
|
|
82
|
+
// 排除大目录和隐藏目录(除了白名单)
|
|
83
|
+
if (EXCLUDE_DIRS.has(item.name)) continue;
|
|
84
|
+
if (item.name.startsWith(".") && !INCLUDE_HIDDEN.has(item.name)) continue;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
entries.push({
|
|
88
|
+
path: `/${itemRelativePath}`,
|
|
89
|
+
name: item.name,
|
|
90
|
+
isDirectory: item.isDirectory(),
|
|
91
|
+
depth,
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
// 递归扫描子目录
|
|
95
|
+
if (item.isDirectory()) {
|
|
96
|
+
await scanDir(itemFullPath, itemRelativePath, depth + 1);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
} catch (error) {
|
|
100
|
+
// 忽略无法访问的目录
|
|
101
|
+
console.warn(`Failed to scan directory ${dirPath}:`, error.message);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
await scanDir(cwd, "", 0);
|
|
106
|
+
|
|
107
|
+
// 构建树形视图
|
|
108
|
+
let treeView = buildTreeView(entries);
|
|
109
|
+
|
|
110
|
+
// 如果超过最大字符数,截断并添加说明
|
|
111
|
+
if (treeView.length > MAX_CHARS) {
|
|
112
|
+
const lines = treeView.split("\n");
|
|
113
|
+
const truncatedLines = [];
|
|
114
|
+
let currentLength = 0;
|
|
115
|
+
|
|
116
|
+
for (const line of lines) {
|
|
117
|
+
if (currentLength + line.length + 1 > MAX_CHARS) break;
|
|
118
|
+
truncatedLines.push(line);
|
|
119
|
+
currentLength += line.length + 1;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
treeView = `${truncatedLines.join("\n")}\n... (截断,总共 ${entries.length} 项,最大深度 ${MAX_DEPTH})`;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
return treeView;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function buildTreeView(entries) {
|
|
129
|
+
// 构建树形结构
|
|
130
|
+
const tree = {};
|
|
131
|
+
|
|
132
|
+
for (const entry of entries) {
|
|
133
|
+
const parts = entry.path.split("/").filter(Boolean);
|
|
134
|
+
let current = tree;
|
|
135
|
+
|
|
136
|
+
for (let i = 0; i < parts.length; i++) {
|
|
137
|
+
const part = parts[i];
|
|
138
|
+
if (!current[part]) {
|
|
139
|
+
current[part] = { _meta: null, children: {} };
|
|
140
|
+
}
|
|
141
|
+
if (i === parts.length - 1) {
|
|
142
|
+
current[part]._meta = entry;
|
|
143
|
+
}
|
|
144
|
+
current = current[part].children;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// 渲染树形视图
|
|
149
|
+
function renderTree(node, prefix = "", _parentPrefix = "") {
|
|
150
|
+
let result = "";
|
|
151
|
+
const keys = Object.keys(node);
|
|
152
|
+
|
|
153
|
+
keys.forEach((key, index) => {
|
|
154
|
+
const isLast = index === keys.length - 1;
|
|
155
|
+
const entry = node[key]._meta;
|
|
156
|
+
const suffix = entry?.isDirectory ? "/" : "";
|
|
157
|
+
const connector = isLast ? "└── " : "├── ";
|
|
158
|
+
const childPrefix = isLast ? " " : "│ ";
|
|
159
|
+
|
|
160
|
+
result += `${prefix}${connector}${key}${suffix}\n`;
|
|
161
|
+
|
|
162
|
+
if (Object.keys(node[key].children).length > 0) {
|
|
163
|
+
result += renderTree(node[key].children, prefix + childPrefix, prefix);
|
|
164
|
+
}
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
return result;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
return renderTree(tree);
|
|
171
|
+
}
|
|
@@ -1,72 +1,6 @@
|
|
|
1
|
-
import { readFile
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
|
|
4
|
-
async function buildDirectoryTree(dirPath) {
|
|
5
|
-
const entries = [];
|
|
6
|
-
|
|
7
|
-
async function scanDir(currentPath, relativePath = "") {
|
|
8
|
-
try {
|
|
9
|
-
const items = await readdir(currentPath);
|
|
10
|
-
|
|
11
|
-
for (const item of items) {
|
|
12
|
-
const fullPath = join(currentPath, item);
|
|
13
|
-
const itemRelativePath = relativePath ? `${relativePath}/${item}` : `/${item}`;
|
|
14
|
-
const stats = await stat(fullPath);
|
|
15
|
-
|
|
16
|
-
entries.push({
|
|
17
|
-
path: itemRelativePath,
|
|
18
|
-
isDirectory: stats.isDirectory(),
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
if (stats.isDirectory()) {
|
|
22
|
-
await scanDir(fullPath, itemRelativePath);
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
} catch {
|
|
26
|
-
// 忽略读取错误
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
await scanDir(dirPath);
|
|
31
|
-
return entries;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
function buildTreeView(entries) {
|
|
35
|
-
const tree = {};
|
|
36
|
-
const entryMap = new Map();
|
|
37
|
-
|
|
38
|
-
for (const entry of entries) {
|
|
39
|
-
entryMap.set(entry.path, entry);
|
|
40
|
-
const parts = entry.path.split("/").filter(Boolean);
|
|
41
|
-
let current = tree;
|
|
42
|
-
|
|
43
|
-
for (const part of parts) {
|
|
44
|
-
if (!current[part]) {
|
|
45
|
-
current[part] = {};
|
|
46
|
-
}
|
|
47
|
-
current = current[part];
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
function renderTree(node, prefix = "", currentPath = "") {
|
|
52
|
-
let result = "";
|
|
53
|
-
const keys = Object.keys(node);
|
|
54
|
-
keys.forEach((key, index) => {
|
|
55
|
-
const isLast = index === keys.length - 1;
|
|
56
|
-
const fullPath = currentPath ? `${currentPath}/${key}` : `/${key}`;
|
|
57
|
-
const entry = entryMap.get(fullPath);
|
|
58
|
-
|
|
59
|
-
const suffix = entry?.isDirectory ? "/" : "";
|
|
60
|
-
result += `${prefix}${isLast ? "└── " : "├── "}${key}${suffix}`;
|
|
61
|
-
result += "\n";
|
|
62
|
-
result += renderTree(node[key], `${prefix}${isLast ? " " : "│ "}`, fullPath);
|
|
63
|
-
});
|
|
64
|
-
return result;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
return renderTree(tree);
|
|
68
|
-
}
|
|
69
|
-
|
|
70
4
|
export default async function loadBaseSources() {
|
|
71
5
|
const cwd = process.cwd();
|
|
72
6
|
const docSmithPath = join(cwd, ".aigne/doc-smith");
|
|
@@ -80,17 +14,7 @@ export default async function loadBaseSources() {
|
|
|
80
14
|
// 文件不存在时忽略错误
|
|
81
15
|
}
|
|
82
16
|
|
|
83
|
-
// 读取 .aigne/doc-smith 目录结构
|
|
84
|
-
let directoryTree = "";
|
|
85
|
-
try {
|
|
86
|
-
const entries = await buildDirectoryTree(docSmithPath);
|
|
87
|
-
directoryTree = buildTreeView(entries);
|
|
88
|
-
} catch {
|
|
89
|
-
// 目录不存在时忽略错误
|
|
90
|
-
}
|
|
91
|
-
|
|
92
17
|
return {
|
|
93
18
|
structureContent,
|
|
94
|
-
directoryTree,
|
|
95
19
|
};
|
|
96
20
|
}
|