@nick848/sf-cli 1.0.22 → 1.0.23
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/cli/index.js +341 -108
- package/dist/cli/index.js.map +1 -1
- package/dist/index.js +338 -105
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +337 -104
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -7,7 +7,7 @@ var enquirer = require('enquirer');
|
|
|
7
7
|
var child_process = require('child_process');
|
|
8
8
|
var fs7 = require('fs');
|
|
9
9
|
var path5 = require('path');
|
|
10
|
-
var
|
|
10
|
+
var fs4 = require('fs/promises');
|
|
11
11
|
var commander = require('commander');
|
|
12
12
|
var readline = require('readline');
|
|
13
13
|
require('uuid');
|
|
@@ -39,7 +39,7 @@ function _interopNamespace(e) {
|
|
|
39
39
|
var chalk9__default = /*#__PURE__*/_interopDefault(chalk9);
|
|
40
40
|
var fs7__namespace = /*#__PURE__*/_interopNamespace(fs7);
|
|
41
41
|
var path5__namespace = /*#__PURE__*/_interopNamespace(path5);
|
|
42
|
-
var
|
|
42
|
+
var fs4__namespace = /*#__PURE__*/_interopNamespace(fs4);
|
|
43
43
|
var readline__namespace = /*#__PURE__*/_interopNamespace(readline);
|
|
44
44
|
var crypto__namespace = /*#__PURE__*/_interopNamespace(crypto);
|
|
45
45
|
var os__namespace = /*#__PURE__*/_interopNamespace(os);
|
|
@@ -2032,24 +2032,13 @@ async function executeDevelopment(ctx, session) {
|
|
|
2032
2032
|
loader.start();
|
|
2033
2033
|
try {
|
|
2034
2034
|
const taskPrompt = buildTaskPrompt(session, item, i);
|
|
2035
|
+
const structureInfo = session.context?.projectStructure;
|
|
2036
|
+
const structureGuide = buildStructureGuide(structureInfo);
|
|
2037
|
+
const systemPrompt = buildCodeGenerationSystemPrompt(session, structureGuide);
|
|
2035
2038
|
const messages = [
|
|
2036
2039
|
{
|
|
2037
2040
|
role: "system",
|
|
2038
|
-
content:
|
|
2039
|
-
|
|
2040
|
-
\u26A0\uFE0F \u91CD\u8981\u89C4\u5219\uFF1A
|
|
2041
|
-
1. \u53EA\u751F\u6210\u5F53\u524D\u4EFB\u52A1\u76F8\u5173\u7684\u4EE3\u7801\uFF0C\u4E0D\u8981\u751F\u6210\u5176\u4ED6\u4EFB\u52A1\u7684\u4EE3\u7801
|
|
2042
|
-
2. \u6280\u672F\u5B9E\u73B0\u5FC5\u987B\u4E25\u683C\u9075\u5FAA\u9879\u76EE\u73B0\u6709\u7684\u5F00\u53D1\u89C4\u8303
|
|
2043
|
-
3. \u751F\u6210\u5B8C\u6574\u3001\u53EF\u8FD0\u884C\u7684\u4EE3\u7801
|
|
2044
|
-
4. \u8FD4\u56DE\u683C\u5F0F\uFF1A\u6BCF\u4E2A\u6587\u4EF6\u7528 \`\`\`filename \u4EE3\u7801 \`\`\` \u5305\u88F9
|
|
2045
|
-
|
|
2046
|
-
\u9879\u76EE\u4FE1\u606F\uFF1A
|
|
2047
|
-
- \u540D\u79F0: ${session.context?.name}
|
|
2048
|
-
- \u6846\u67B6: ${session.context?.framework || "\u672A\u6307\u5B9A"}
|
|
2049
|
-
- \u6280\u672F\u6808: ${session.context?.techStack.join(", ") || "\u672A\u6307\u5B9A"}
|
|
2050
|
-
|
|
2051
|
-
${session.context?.devStandards ? `\u3010\u5F00\u53D1\u89C4\u8303\u3011
|
|
2052
|
-
${session.context.devStandards.slice(0, 2e3)}` : ""}`
|
|
2041
|
+
content: systemPrompt
|
|
2053
2042
|
},
|
|
2054
2043
|
{
|
|
2055
2044
|
role: "user",
|
|
@@ -2069,17 +2058,17 @@ ${session.context.devStandards.slice(0, 2e3)}` : ""}`
|
|
|
2069
2058
|
for (const block of codeBlocks) {
|
|
2070
2059
|
const filePath = path5__namespace.join(workingDir, block.filename);
|
|
2071
2060
|
const dir = path5__namespace.dirname(filePath);
|
|
2072
|
-
await
|
|
2073
|
-
await
|
|
2061
|
+
await fs4__namespace.mkdir(dir, { recursive: true });
|
|
2062
|
+
await fs4__namespace.writeFile(filePath, block.code, "utf-8");
|
|
2074
2063
|
files.push(block.filename);
|
|
2075
2064
|
}
|
|
2076
2065
|
loader.stop(chalk9__default.default.green(`${prefix} \u2713 ${item.title.slice(0, 25)} (${codeBlocks.length} \u4E2A\u6587\u4EF6)`));
|
|
2077
2066
|
} else {
|
|
2078
2067
|
const implDir = path5__namespace.join(workingDir, "src");
|
|
2079
|
-
await
|
|
2068
|
+
await fs4__namespace.mkdir(implDir, { recursive: true });
|
|
2080
2069
|
const fileName = `${item.title.replace(/[^a-zA-Z0-9\u4e00-\u9fa5]/g, "_")}.ts`;
|
|
2081
2070
|
const filePath = path5__namespace.join(implDir, fileName);
|
|
2082
|
-
await
|
|
2071
|
+
await fs4__namespace.writeFile(filePath, `// TODO: ${item.title}
|
|
2083
2072
|
// ${item.description}
|
|
2084
2073
|
`, "utf-8");
|
|
2085
2074
|
files.push(`src/${fileName}`);
|
|
@@ -2102,6 +2091,91 @@ ${session.context.devStandards.slice(0, 2e3)}` : ""}`
|
|
|
2102
2091
|
};
|
|
2103
2092
|
}
|
|
2104
2093
|
}
|
|
2094
|
+
function buildStructureGuide(structure) {
|
|
2095
|
+
if (!structure) {
|
|
2096
|
+
return `## \u9879\u76EE\u7ED3\u6784
|
|
2097
|
+
- \u7EC4\u4EF6\u653E\u5728 src/components/
|
|
2098
|
+
- \u9875\u9762\u653E\u5728 src/pages/
|
|
2099
|
+
- \u5DE5\u5177\u51FD\u6570\u653E\u5728 src/utils/`;
|
|
2100
|
+
}
|
|
2101
|
+
const lines = ["## \u9879\u76EE\u7ED3\u6784\uFF08\u5FC5\u987B\u9075\u5FAA\uFF09"];
|
|
2102
|
+
if (structure.hasSrc) {
|
|
2103
|
+
lines.push(`- \u6E90\u7801\u76EE\u5F55: ${structure.srcDir}/`);
|
|
2104
|
+
}
|
|
2105
|
+
if (structure.hasPages) {
|
|
2106
|
+
lines.push(`- \u9875\u9762\u76EE\u5F55: ${structure.pagesDir}/ \uFF08\u9875\u9762\u7EC4\u4EF6\u653E\u8FD9\u91CC\uFF09`);
|
|
2107
|
+
} else {
|
|
2108
|
+
lines.push(`- \u9875\u9762\u76EE\u5F55: src/pages/ \uFF08\u9875\u9762\u7EC4\u4EF6\u653E\u8FD9\u91CC\uFF09`);
|
|
2109
|
+
}
|
|
2110
|
+
if (structure.hasComponents) {
|
|
2111
|
+
lines.push(`- \u7EC4\u4EF6\u76EE\u5F55: ${structure.componentsDir}/ \uFF08\u901A\u7528\u7EC4\u4EF6\u653E\u8FD9\u91CC\uFF09`);
|
|
2112
|
+
} else {
|
|
2113
|
+
lines.push(`- \u7EC4\u4EF6\u76EE\u5F55: src/components/ \uFF08\u901A\u7528\u7EC4\u4EF6\u653E\u8FD9\u91CC\uFF09`);
|
|
2114
|
+
}
|
|
2115
|
+
if (structure.hasApi) {
|
|
2116
|
+
lines.push(`- API \u76EE\u5F55: src/api/ \u6216 src/services/`);
|
|
2117
|
+
}
|
|
2118
|
+
if (structure.hasHooks) {
|
|
2119
|
+
lines.push(`- Hooks \u76EE\u5F55: src/hooks/`);
|
|
2120
|
+
}
|
|
2121
|
+
if (structure.hasStore) {
|
|
2122
|
+
lines.push(`- \u72B6\u6001\u7BA1\u7406\u76EE\u5F55: src/store/`);
|
|
2123
|
+
}
|
|
2124
|
+
if (structure.hasUtils) {
|
|
2125
|
+
lines.push(`- \u5DE5\u5177\u51FD\u6570\u76EE\u5F55: src/utils/`);
|
|
2126
|
+
}
|
|
2127
|
+
lines.push("");
|
|
2128
|
+
lines.push("\u26A0\uFE0F \u6587\u4EF6\u5FC5\u987B\u653E\u5728\u4E0A\u8FF0\u5BF9\u5E94\u76EE\u5F55\u4E2D\uFF0C\u4E0D\u8981\u968F\u610F\u521B\u5EFA\u65B0\u76EE\u5F55");
|
|
2129
|
+
return lines.join("\n");
|
|
2130
|
+
}
|
|
2131
|
+
function buildCodeGenerationSystemPrompt(session, structureGuide) {
|
|
2132
|
+
const framework = session.context?.framework || "React";
|
|
2133
|
+
const techStack = session.context?.techStack?.join(", ") || "TypeScript";
|
|
2134
|
+
const devStandards = session.context?.devStandards || "";
|
|
2135
|
+
const standardsText = devStandards.slice(0, 6e3);
|
|
2136
|
+
return `\u4F60\u662F\u4E00\u4E2A\u4E13\u4E1A\u7684\u524D\u7AEF\u5F00\u53D1\u5DE5\u7A0B\u5E08\u3002\u8BF7\u6839\u636E\u4EFB\u52A1\u63CF\u8FF0\u751F\u6210\u4EE3\u7801\u5B9E\u73B0\u3002
|
|
2137
|
+
|
|
2138
|
+
## \u26A0\uFE0F \u6838\u5FC3\u89C4\u5219\uFF08\u5FC5\u987B\u9075\u5B88\uFF09
|
|
2139
|
+
|
|
2140
|
+
1. **\u8BED\u8A00**: \u5FC5\u987B\u4F7F\u7528 TypeScript (.tsx/.ts \u6587\u4EF6)
|
|
2141
|
+
2. **\u6846\u67B6**: ${framework}
|
|
2142
|
+
3. **\u53EA\u751F\u6210\u5F53\u524D\u4EFB\u52A1\u76F8\u5173\u7684\u4EE3\u7801**\uFF0C\u4E0D\u8981\u751F\u6210\u5176\u4ED6\u4EFB\u52A1\u7684\u4EE3\u7801
|
|
2143
|
+
4. **\u6587\u4EF6\u8DEF\u5F84\u5FC5\u987B\u6B63\u786E**\uFF1A\u4E25\u683C\u6309\u7167\u9879\u76EE\u7ED3\u6784\u653E\u7F6E\u6587\u4EF6
|
|
2144
|
+
5. **\u5FC5\u987B\u751F\u6210\u5B8C\u6574\u3001\u53EF\u8FD0\u884C\u7684\u4EE3\u7801**\uFF0C\u4E0D\u8981\u5199 TODO \u6216\u5360\u4F4D\u7B26
|
|
2145
|
+
6. **\u5982\u679C\u4EFB\u52A1\u6D89\u53CA\u9875\u9762**\uFF0C\u5FC5\u987B\u751F\u6210\u9875\u9762\u7EC4\u4EF6
|
|
2146
|
+
7. **\u5982\u679C\u4EFB\u52A1\u6D89\u53CA UI**\uFF0C\u5FC5\u987B\u751F\u6210\u5BF9\u5E94\u7684\u7EC4\u4EF6\u548C\u6837\u5F0F
|
|
2147
|
+
|
|
2148
|
+
${structureGuide}
|
|
2149
|
+
|
|
2150
|
+
## \u9879\u76EE\u4FE1\u606F
|
|
2151
|
+
|
|
2152
|
+
- \u540D\u79F0: ${session.context?.name || "\u672A\u547D\u540D\u9879\u76EE"}
|
|
2153
|
+
- \u6846\u67B6: ${framework}
|
|
2154
|
+
- \u6280\u672F\u6808: ${techStack}
|
|
2155
|
+
|
|
2156
|
+
## \u5F00\u53D1\u89C4\u8303\uFF08\u5FC5\u987B\u9075\u5FAA\uFF09
|
|
2157
|
+
|
|
2158
|
+
${standardsText || `### \u9ED8\u8BA4\u89C4\u8303
|
|
2159
|
+
- \u4F7F\u7528\u51FD\u6570\u5F0F\u7EC4\u4EF6
|
|
2160
|
+
- \u4F7F\u7528 TypeScript \u7C7B\u578B\u5B9A\u4E49
|
|
2161
|
+
- \u4F7F\u7528 async/await \u5904\u7406\u5F02\u6B65
|
|
2162
|
+
- \u53D8\u91CF\u4F7F\u7528 camelCase\uFF0C\u7EC4\u4EF6\u4F7F\u7528 PascalCase
|
|
2163
|
+
- \u5BFC\u51FA\u4F7F\u7528 export default \u6216 export const`}
|
|
2164
|
+
|
|
2165
|
+
## \u8F93\u51FA\u683C\u5F0F
|
|
2166
|
+
|
|
2167
|
+
\u6BCF\u4E2A\u6587\u4EF6\u7528 \`\`\`filename \u5305\u88F9\uFF0C\u4F8B\u5982\uFF1A
|
|
2168
|
+
|
|
2169
|
+
\`\`\`src/pages/HomePage.tsx
|
|
2170
|
+
// \u4EE3\u7801\u5185\u5BB9
|
|
2171
|
+
\`\`\`
|
|
2172
|
+
|
|
2173
|
+
\`\`\`src/components/Button/Button.tsx
|
|
2174
|
+
// \u4EE3\u7801\u5185\u5BB9
|
|
2175
|
+
\`\`\`
|
|
2176
|
+
|
|
2177
|
+
\u73B0\u5728\u8BF7\u6839\u636E\u4EFB\u52A1\u8981\u6C42\u751F\u6210\u4EE3\u7801\u3002`;
|
|
2178
|
+
}
|
|
2105
2179
|
function buildTaskPrompt(session, item, index) {
|
|
2106
2180
|
const lines = [];
|
|
2107
2181
|
lines.push(`## \u5F53\u524D\u4EFB\u52A1 (#${index + 1})`);
|
|
@@ -2156,7 +2230,7 @@ async function executeReview(ctx, session) {
|
|
|
2156
2230
|
const codeContents = [];
|
|
2157
2231
|
for (const file of session.implFiles) {
|
|
2158
2232
|
try {
|
|
2159
|
-
const content = await
|
|
2233
|
+
const content = await fs4__namespace.readFile(path5__namespace.join(workingDir, file), "utf-8");
|
|
2160
2234
|
codeContents.push(`// ${file}
|
|
2161
2235
|
${content}`);
|
|
2162
2236
|
} catch {
|
|
@@ -2165,7 +2239,7 @@ ${content}`);
|
|
|
2165
2239
|
const testContents = [];
|
|
2166
2240
|
for (const file of session.testFiles) {
|
|
2167
2241
|
try {
|
|
2168
|
-
const content = await
|
|
2242
|
+
const content = await fs4__namespace.readFile(path5__namespace.join(workingDir, file), "utf-8");
|
|
2169
2243
|
testContents.push(`// ${file}
|
|
2170
2244
|
${content}`);
|
|
2171
2245
|
} catch {
|
|
@@ -2259,9 +2333,9 @@ async function readProjectContext(workingDir) {
|
|
|
2259
2333
|
};
|
|
2260
2334
|
const agentsPath = path5__namespace.join(workingDir, "AGENTS.md");
|
|
2261
2335
|
try {
|
|
2262
|
-
const stats = await
|
|
2336
|
+
const stats = await fs4__namespace.stat(agentsPath);
|
|
2263
2337
|
if (stats.size <= MAX_FILE_SIZE2) {
|
|
2264
|
-
context.agentsMd = await
|
|
2338
|
+
context.agentsMd = await fs4__namespace.readFile(agentsPath, "utf-8");
|
|
2265
2339
|
const nameMatch = context.agentsMd.match(/\|\s*项目名称\s*\|\s*([^\s|]+)/);
|
|
2266
2340
|
if (nameMatch) context.name = nameMatch[1];
|
|
2267
2341
|
const typeMatch = context.agentsMd.match(/\|\s*项目类型\s*\|\s*([^\s|]+)/);
|
|
@@ -2275,9 +2349,9 @@ async function readProjectContext(workingDir) {
|
|
|
2275
2349
|
}
|
|
2276
2350
|
const configPath = path5__namespace.join(workingDir, "openspec", "config.yaml");
|
|
2277
2351
|
try {
|
|
2278
|
-
const stats = await
|
|
2352
|
+
const stats = await fs4__namespace.stat(configPath);
|
|
2279
2353
|
if (stats.size <= MAX_FILE_SIZE2) {
|
|
2280
|
-
context.configYaml = await
|
|
2354
|
+
context.configYaml = await fs4__namespace.readFile(configPath, "utf-8");
|
|
2281
2355
|
const nameMatch = context.configYaml.match(/name:\s*(.+)/);
|
|
2282
2356
|
if (nameMatch) context.name = nameMatch[1].trim();
|
|
2283
2357
|
const typeMatch = context.configYaml.match(/type:\s*(.+)/);
|
|
@@ -2298,14 +2372,173 @@ async function readProjectContext(workingDir) {
|
|
|
2298
2372
|
}
|
|
2299
2373
|
const devStandardsPath = path5__namespace.join(workingDir, ".sf-cli", "norms", "devstanded.md");
|
|
2300
2374
|
try {
|
|
2301
|
-
const stats = await
|
|
2375
|
+
const stats = await fs4__namespace.stat(devStandardsPath);
|
|
2302
2376
|
if (stats.size <= MAX_FILE_SIZE2) {
|
|
2303
|
-
context.devStandards = await
|
|
2377
|
+
context.devStandards = await fs4__namespace.readFile(devStandardsPath, "utf-8");
|
|
2304
2378
|
}
|
|
2305
2379
|
} catch {
|
|
2306
2380
|
}
|
|
2381
|
+
if (!context.framework) {
|
|
2382
|
+
const detectedFramework = await detectFramework2(workingDir);
|
|
2383
|
+
if (detectedFramework) {
|
|
2384
|
+
context.framework = detectedFramework;
|
|
2385
|
+
context.techStack = [detectedFramework, ...context.techStack.filter((t) => t !== detectedFramework)];
|
|
2386
|
+
}
|
|
2387
|
+
}
|
|
2388
|
+
if (!context.devStandards) {
|
|
2389
|
+
context.devStandards = await analyzeProjectForStandards(workingDir, context);
|
|
2390
|
+
}
|
|
2391
|
+
context.projectStructure = await analyzeProjectStructure(workingDir);
|
|
2307
2392
|
return context;
|
|
2308
2393
|
}
|
|
2394
|
+
async function detectFramework2(workingDir) {
|
|
2395
|
+
try {
|
|
2396
|
+
const pkgPath = path5__namespace.join(workingDir, "package.json");
|
|
2397
|
+
const pkgContent = await fs4__namespace.readFile(pkgPath, "utf-8");
|
|
2398
|
+
const pkg = JSON.parse(pkgContent);
|
|
2399
|
+
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
2400
|
+
if (deps["react"] || deps["next"]) return "React";
|
|
2401
|
+
if (deps["vue"] || deps["nuxt"]) return "Vue";
|
|
2402
|
+
if (deps["angular"] || deps["@angular/core"]) return "Angular";
|
|
2403
|
+
if (deps["svelte"]) return "Svelte";
|
|
2404
|
+
if (deps["solid-js"]) return "Solid";
|
|
2405
|
+
if (deps["vite"] || deps["webpack"]) return "JavaScript";
|
|
2406
|
+
if (deps["typescript"]) return "TypeScript";
|
|
2407
|
+
return null;
|
|
2408
|
+
} catch {
|
|
2409
|
+
return null;
|
|
2410
|
+
}
|
|
2411
|
+
}
|
|
2412
|
+
async function analyzeProjectForStandards(workingDir, context) {
|
|
2413
|
+
const standards = [];
|
|
2414
|
+
standards.push(`# \u9879\u76EE\u5F00\u53D1\u89C4\u8303\uFF08\u81EA\u52A8\u751F\u6210\uFF09
|
|
2415
|
+
> \u9879\u76EE\u540D\u79F0: ${context.name}
|
|
2416
|
+
> \u6846\u67B6: ${context.framework || "\u672A\u68C0\u6D4B\u5230"}
|
|
2417
|
+
> \u6280\u672F\u6808: ${context.techStack.join(", ") || "\u672A\u68C0\u6D4B\u5230"}
|
|
2418
|
+
`);
|
|
2419
|
+
try {
|
|
2420
|
+
const tsConfigPath = path5__namespace.join(workingDir, "tsconfig.json");
|
|
2421
|
+
await fs4__namespace.access(tsConfigPath);
|
|
2422
|
+
standards.push("\n## \u8BED\u8A00\n- \u4F7F\u7528 TypeScript");
|
|
2423
|
+
} catch {
|
|
2424
|
+
standards.push("\n## \u8BED\u8A00\n- \u4F7F\u7528 JavaScript");
|
|
2425
|
+
}
|
|
2426
|
+
try {
|
|
2427
|
+
const srcDir = path5__namespace.join(workingDir, "src");
|
|
2428
|
+
const files = await listFilesDeep(srcDir);
|
|
2429
|
+
const hasCss = files.some((f) => f.endsWith(".css"));
|
|
2430
|
+
const hasScss = files.some((f) => f.endsWith(".scss") || f.endsWith(".sass"));
|
|
2431
|
+
const hasLess = files.some((f) => f.endsWith(".less"));
|
|
2432
|
+
const hasStyled = files.some((f) => f.includes(".styled.") || f.includes("styled-components"));
|
|
2433
|
+
if (hasScss) standards.push("\n## \u6837\u5F0F\n- \u4F7F\u7528 SCSS/Sass");
|
|
2434
|
+
else if (hasLess) standards.push("\n## \u6837\u5F0F\n- \u4F7F\u7528 Less");
|
|
2435
|
+
else if (hasStyled) standards.push("\n## \u6837\u5F0F\n- \u4F7F\u7528 styled-components");
|
|
2436
|
+
else if (hasCss) standards.push("\n## \u6837\u5F0F\n- \u4F7F\u7528 CSS");
|
|
2437
|
+
} catch {
|
|
2438
|
+
}
|
|
2439
|
+
try {
|
|
2440
|
+
const srcDir = path5__namespace.join(workingDir, "src");
|
|
2441
|
+
const entries = await fs4__namespace.readdir(srcDir, { withFileTypes: true });
|
|
2442
|
+
const dirs = entries.filter((e) => e.isDirectory()).map((e) => e.name);
|
|
2443
|
+
if (dirs.includes("components")) standards.push("\n## \u76EE\u5F55\u7ED3\u6784\n- \u7EC4\u4EF6\u653E\u5728 src/components/");
|
|
2444
|
+
if (dirs.includes("pages") || dirs.includes("views")) standards.push("- \u9875\u9762\u653E\u5728 src/pages/ \u6216 src/views/");
|
|
2445
|
+
if (dirs.includes("hooks")) standards.push("- Hooks \u653E\u5728 src/hooks/");
|
|
2446
|
+
if (dirs.includes("utils")) standards.push("- \u5DE5\u5177\u51FD\u6570\u653E\u5728 src/utils/");
|
|
2447
|
+
if (dirs.includes("api") || dirs.includes("services")) standards.push("- API \u8C03\u7528\u653E\u5728 src/api/ \u6216 src/services/");
|
|
2448
|
+
if (dirs.includes("store") || dirs.includes("stores")) standards.push("- \u72B6\u6001\u7BA1\u7406\u653E\u5728 src/store/");
|
|
2449
|
+
} catch {
|
|
2450
|
+
}
|
|
2451
|
+
standards.push("\n## \u4EE3\u7801\u89C4\u8303");
|
|
2452
|
+
standards.push("- \u4F7F\u7528 ES6+ \u8BED\u6CD5");
|
|
2453
|
+
standards.push("- \u7EC4\u4EF6\u4F7F\u7528\u51FD\u6570\u5F0F\u5199\u6CD5");
|
|
2454
|
+
standards.push("- \u4F7F\u7528 async/await \u5904\u7406\u5F02\u6B65");
|
|
2455
|
+
standards.push("- \u53D8\u91CF\u4F7F\u7528 camelCase\uFF0C\u7EC4\u4EF6\u4F7F\u7528 PascalCase");
|
|
2456
|
+
standards.push("- \u5E38\u91CF\u4F7F\u7528 UPPER_SNAKE_CASE");
|
|
2457
|
+
return standards.join("\n");
|
|
2458
|
+
}
|
|
2459
|
+
async function listFilesDeep(dir, maxDepth = 3) {
|
|
2460
|
+
const files = [];
|
|
2461
|
+
async function scan(currentDir, depth) {
|
|
2462
|
+
if (depth > maxDepth) return;
|
|
2463
|
+
try {
|
|
2464
|
+
const entries = await fs4__namespace.readdir(currentDir, { withFileTypes: true });
|
|
2465
|
+
for (const entry of entries) {
|
|
2466
|
+
if (entry.name.startsWith(".") || entry.name === "node_modules") continue;
|
|
2467
|
+
const fullPath = path5__namespace.join(currentDir, entry.name);
|
|
2468
|
+
if (entry.isDirectory()) {
|
|
2469
|
+
await scan(fullPath, depth + 1);
|
|
2470
|
+
} else {
|
|
2471
|
+
files.push(fullPath);
|
|
2472
|
+
}
|
|
2473
|
+
}
|
|
2474
|
+
} catch {
|
|
2475
|
+
}
|
|
2476
|
+
}
|
|
2477
|
+
await scan(dir, 0);
|
|
2478
|
+
return files;
|
|
2479
|
+
}
|
|
2480
|
+
async function analyzeProjectStructure(workingDir) {
|
|
2481
|
+
const structure = {
|
|
2482
|
+
hasSrc: false,
|
|
2483
|
+
hasPages: false,
|
|
2484
|
+
hasComponents: false,
|
|
2485
|
+
hasApi: false,
|
|
2486
|
+
hasHooks: false,
|
|
2487
|
+
hasStore: false,
|
|
2488
|
+
hasUtils: false,
|
|
2489
|
+
srcDir: "src",
|
|
2490
|
+
pagesDir: "",
|
|
2491
|
+
componentsDir: ""
|
|
2492
|
+
};
|
|
2493
|
+
try {
|
|
2494
|
+
const srcDir = path5__namespace.join(workingDir, "src");
|
|
2495
|
+
try {
|
|
2496
|
+
const srcStat = await fs4__namespace.stat(srcDir);
|
|
2497
|
+
if (srcStat.isDirectory()) {
|
|
2498
|
+
structure.hasSrc = true;
|
|
2499
|
+
structure.srcDir = "src";
|
|
2500
|
+
const srcEntries = await fs4__namespace.readdir(srcDir, { withFileTypes: true });
|
|
2501
|
+
const srcDirs = srcEntries.filter((e) => e.isDirectory()).map((e) => e.name);
|
|
2502
|
+
if (srcDirs.includes("pages")) {
|
|
2503
|
+
structure.hasPages = true;
|
|
2504
|
+
structure.pagesDir = "src/pages";
|
|
2505
|
+
} else if (srcDirs.includes("views")) {
|
|
2506
|
+
structure.hasPages = true;
|
|
2507
|
+
structure.pagesDir = "src/views";
|
|
2508
|
+
}
|
|
2509
|
+
if (srcDirs.includes("components")) {
|
|
2510
|
+
structure.hasComponents = true;
|
|
2511
|
+
structure.componentsDir = "src/components";
|
|
2512
|
+
}
|
|
2513
|
+
if (srcDirs.includes("api") || srcDirs.includes("services")) {
|
|
2514
|
+
structure.hasApi = true;
|
|
2515
|
+
}
|
|
2516
|
+
if (srcDirs.includes("hooks")) {
|
|
2517
|
+
structure.hasHooks = true;
|
|
2518
|
+
}
|
|
2519
|
+
if (srcDirs.includes("store") || srcDirs.includes("stores")) {
|
|
2520
|
+
structure.hasStore = true;
|
|
2521
|
+
}
|
|
2522
|
+
if (srcDirs.includes("utils")) {
|
|
2523
|
+
structure.hasUtils = true;
|
|
2524
|
+
}
|
|
2525
|
+
}
|
|
2526
|
+
} catch {
|
|
2527
|
+
const rootEntries = await fs4__namespace.readdir(workingDir, { withFileTypes: true });
|
|
2528
|
+
const rootDirs = rootEntries.filter((e) => e.isDirectory()).map((e) => e.name);
|
|
2529
|
+
if (rootDirs.includes("pages")) {
|
|
2530
|
+
structure.hasPages = true;
|
|
2531
|
+
structure.pagesDir = "pages";
|
|
2532
|
+
}
|
|
2533
|
+
if (rootDirs.includes("components")) {
|
|
2534
|
+
structure.hasComponents = true;
|
|
2535
|
+
structure.componentsDir = "components";
|
|
2536
|
+
}
|
|
2537
|
+
}
|
|
2538
|
+
} catch {
|
|
2539
|
+
}
|
|
2540
|
+
return structure;
|
|
2541
|
+
}
|
|
2309
2542
|
function analyzeComplexity(requirement, context) {
|
|
2310
2543
|
let score = 3;
|
|
2311
2544
|
if (requirement.length > 100) score += 1;
|
|
@@ -2794,10 +3027,10 @@ ${feedbacks.map((f, i) => `${i + 1}. ${f}`).join("\n")}
|
|
|
2794
3027
|
}
|
|
2795
3028
|
async function saveSpecFile(workingDir, session) {
|
|
2796
3029
|
const specDir = path5__namespace.join(workingDir, "openspec", "changes");
|
|
2797
|
-
await
|
|
3030
|
+
await fs4__namespace.mkdir(specDir, { recursive: true });
|
|
2798
3031
|
const specPath = path5__namespace.join(specDir, `${session.id}-spec.md`);
|
|
2799
3032
|
const content = formatSpecFile(session);
|
|
2800
|
-
await
|
|
3033
|
+
await fs4__namespace.writeFile(specPath, content, "utf-8");
|
|
2801
3034
|
return specPath;
|
|
2802
3035
|
}
|
|
2803
3036
|
function formatSpecFile(session) {
|
|
@@ -2882,7 +3115,7 @@ function formatSpecFile(session) {
|
|
|
2882
3115
|
}
|
|
2883
3116
|
async function generateTests(workingDir, session, ctx) {
|
|
2884
3117
|
const testDir = path5__namespace.join(workingDir, "tests");
|
|
2885
|
-
await
|
|
3118
|
+
await fs4__namespace.mkdir(testDir, { recursive: true });
|
|
2886
3119
|
const testFiles = [];
|
|
2887
3120
|
if (ctx?.modelService) {
|
|
2888
3121
|
for (const scenario of session.bddScenarios) {
|
|
@@ -2892,12 +3125,12 @@ async function generateTests(workingDir, session, ctx) {
|
|
|
2892
3125
|
loader.start();
|
|
2893
3126
|
try {
|
|
2894
3127
|
const content = await generateTestFileWithAI(scenario, session, ctx);
|
|
2895
|
-
await
|
|
3128
|
+
await fs4__namespace.writeFile(testPath, content, "utf-8");
|
|
2896
3129
|
testFiles.push(`tests/${testName}.test.ts`);
|
|
2897
3130
|
loader.stop(chalk9__default.default.green(` \u2713 \u6D4B\u8BD5\u6587\u4EF6\u5DF2\u751F\u6210`));
|
|
2898
3131
|
} catch {
|
|
2899
3132
|
const content = generateTestFile(scenario, session);
|
|
2900
|
-
await
|
|
3133
|
+
await fs4__namespace.writeFile(testPath, content, "utf-8");
|
|
2901
3134
|
testFiles.push(`tests/${testName}.test.ts`);
|
|
2902
3135
|
loader.stop(chalk9__default.default.yellow(" \u26A0 \u4F7F\u7528\u57FA\u7840\u6D4B\u8BD5\u6A21\u677F"));
|
|
2903
3136
|
}
|
|
@@ -2907,7 +3140,7 @@ async function generateTests(workingDir, session, ctx) {
|
|
|
2907
3140
|
const testName = scenario.feature.replace(/[^a-zA-Z0-9\u4e00-\u9fa5]/g, "_");
|
|
2908
3141
|
const testPath = path5__namespace.join(testDir, `${testName}.test.ts`);
|
|
2909
3142
|
const content = generateTestFile(scenario, session);
|
|
2910
|
-
await
|
|
3143
|
+
await fs4__namespace.writeFile(testPath, content, "utf-8");
|
|
2911
3144
|
testFiles.push(`tests/${testName}.test.ts`);
|
|
2912
3145
|
}
|
|
2913
3146
|
}
|
|
@@ -3001,7 +3234,7 @@ function generateTestFile(scenario, session) {
|
|
|
3001
3234
|
async function archiveWorkflow(workingDir) {
|
|
3002
3235
|
if (!activeSession) return;
|
|
3003
3236
|
const archiveDir = path5__namespace.join(workingDir, "openspec", "spec");
|
|
3004
|
-
await
|
|
3237
|
+
await fs4__namespace.mkdir(archiveDir, { recursive: true });
|
|
3005
3238
|
const archivePath = path5__namespace.join(archiveDir, `${activeSession.id}.md`);
|
|
3006
3239
|
const content = `# \u5F52\u6863: ${activeSession.requirement.slice(0, 50)}
|
|
3007
3240
|
|
|
@@ -3028,7 +3261,7 @@ ${activeSession.refinedRequirement}
|
|
|
3028
3261
|
|
|
3029
3262
|
${activeSession.testFiles.map((f) => `- ${f}`).join("\n") || "\u65E0"}
|
|
3030
3263
|
`;
|
|
3031
|
-
await
|
|
3264
|
+
await fs4__namespace.writeFile(archivePath, content, "utf-8");
|
|
3032
3265
|
}
|
|
3033
3266
|
function generateSessionId() {
|
|
3034
3267
|
const timestamp = Date.now().toString(36);
|
|
@@ -3450,11 +3683,11 @@ var NormsManager = class {
|
|
|
3450
3683
|
const files = await this.collectProjectFiles(projectPath);
|
|
3451
3684
|
for (const filePath of files.slice(0, MAX_FILES_TO_SCAN)) {
|
|
3452
3685
|
try {
|
|
3453
|
-
const stats = await
|
|
3686
|
+
const stats = await fs4__namespace.stat(filePath);
|
|
3454
3687
|
if (stats.size > MAX_FILE_SIZE) {
|
|
3455
3688
|
continue;
|
|
3456
3689
|
}
|
|
3457
|
-
const content = await
|
|
3690
|
+
const content = await fs4__namespace.readFile(filePath, "utf-8");
|
|
3458
3691
|
const patterns = await this.learnFromFile(filePath, content);
|
|
3459
3692
|
result.patternsFound += patterns.length;
|
|
3460
3693
|
result.filesScanned++;
|
|
@@ -4080,7 +4313,7 @@ ${response}`;
|
|
|
4080
4313
|
const files = [];
|
|
4081
4314
|
async function scan(dir) {
|
|
4082
4315
|
try {
|
|
4083
|
-
const entries = await
|
|
4316
|
+
const entries = await fs4__namespace.readdir(dir, { withFileTypes: true });
|
|
4084
4317
|
for (const entry of entries) {
|
|
4085
4318
|
if (entry.isDirectory()) {
|
|
4086
4319
|
if (!IGNORED_DIRS.includes(entry.name)) {
|
|
@@ -4123,8 +4356,8 @@ ${response}`;
|
|
|
4123
4356
|
* 确保规范目录存在
|
|
4124
4357
|
*/
|
|
4125
4358
|
async ensureNormsDir() {
|
|
4126
|
-
await
|
|
4127
|
-
await
|
|
4359
|
+
await fs4__namespace.mkdir(this.config.normsDir, { recursive: true });
|
|
4360
|
+
await fs4__namespace.mkdir(path5__namespace.join(this.config.normsDir, "weekly"), { recursive: true });
|
|
4128
4361
|
}
|
|
4129
4362
|
/**
|
|
4130
4363
|
* 加载已有规范
|
|
@@ -4132,7 +4365,7 @@ ${response}`;
|
|
|
4132
4365
|
async loadStandards() {
|
|
4133
4366
|
const standardsPath = path5__namespace.join(this.config.normsDir, "patterns.json");
|
|
4134
4367
|
try {
|
|
4135
|
-
const content = await
|
|
4368
|
+
const content = await fs4__namespace.readFile(standardsPath, "utf-8");
|
|
4136
4369
|
const data = JSON.parse(content);
|
|
4137
4370
|
for (const standard of data) {
|
|
4138
4371
|
this.standards.set(standard.id, {
|
|
@@ -4150,7 +4383,7 @@ ${response}`;
|
|
|
4150
4383
|
async loadWeights() {
|
|
4151
4384
|
const weightsPath = path5__namespace.join(this.config.normsDir, "weights.json");
|
|
4152
4385
|
try {
|
|
4153
|
-
const content = await
|
|
4386
|
+
const content = await fs4__namespace.readFile(weightsPath, "utf-8");
|
|
4154
4387
|
const data = JSON.parse(content);
|
|
4155
4388
|
for (const weight of data) {
|
|
4156
4389
|
this.weights.set(weight.standardId, {
|
|
@@ -4166,7 +4399,7 @@ ${response}`;
|
|
|
4166
4399
|
*/
|
|
4167
4400
|
async saveStandards() {
|
|
4168
4401
|
const standardsPath = path5__namespace.join(this.config.normsDir, "patterns.json");
|
|
4169
|
-
await
|
|
4402
|
+
await fs4__namespace.writeFile(
|
|
4170
4403
|
standardsPath,
|
|
4171
4404
|
JSON.stringify(Array.from(this.standards.values()), null, 2),
|
|
4172
4405
|
"utf-8"
|
|
@@ -4177,7 +4410,7 @@ ${response}`;
|
|
|
4177
4410
|
*/
|
|
4178
4411
|
async saveWeights() {
|
|
4179
4412
|
const weightsPath = path5__namespace.join(this.config.normsDir, "weights.json");
|
|
4180
|
-
await
|
|
4413
|
+
await fs4__namespace.writeFile(
|
|
4181
4414
|
weightsPath,
|
|
4182
4415
|
JSON.stringify(Array.from(this.weights.values()), null, 2),
|
|
4183
4416
|
"utf-8"
|
|
@@ -4205,7 +4438,7 @@ ${response}`;
|
|
|
4205
4438
|
*/
|
|
4206
4439
|
async saveWeeklyReport(weekId, report) {
|
|
4207
4440
|
const reportPath = path5__namespace.join(this.config.normsDir, "weekly", `${weekId}.json`);
|
|
4208
|
-
await
|
|
4441
|
+
await fs4__namespace.writeFile(reportPath, JSON.stringify(report, null, 2), "utf-8");
|
|
4209
4442
|
}
|
|
4210
4443
|
/**
|
|
4211
4444
|
* 更新 devstanded.md
|
|
@@ -4214,7 +4447,7 @@ ${response}`;
|
|
|
4214
4447
|
const standards = this.getEffectiveStandards();
|
|
4215
4448
|
const content = this.generateDevStandedMd(standards);
|
|
4216
4449
|
const mdPath = path5__namespace.join(this.config.normsDir, "devstanded.md");
|
|
4217
|
-
await
|
|
4450
|
+
await fs4__namespace.writeFile(mdPath, content, "utf-8");
|
|
4218
4451
|
}
|
|
4219
4452
|
/**
|
|
4220
4453
|
* 生成 devstanded.md 内容
|
|
@@ -4353,7 +4586,7 @@ async function handleInit(args, ctx) {
|
|
|
4353
4586
|
async function initProject(options = {}, workingDir) {
|
|
4354
4587
|
const cwd = workingDir || process.cwd();
|
|
4355
4588
|
try {
|
|
4356
|
-
const stats = await
|
|
4589
|
+
const stats = await fs4__namespace.stat(cwd);
|
|
4357
4590
|
if (!stats.isDirectory()) {
|
|
4358
4591
|
return {
|
|
4359
4592
|
output: chalk9__default.default.red(`\u9519\u8BEF: ${cwd} \u4E0D\u662F\u6709\u6548\u76EE\u5F55`)
|
|
@@ -4385,7 +4618,7 @@ async function initProject(options = {}, workingDir) {
|
|
|
4385
4618
|
await normsManager.initialize();
|
|
4386
4619
|
const scanResult = await normsManager.scanProject(cwd);
|
|
4387
4620
|
const agentsContent = generateAgentsMd(projectInfo, scanResult);
|
|
4388
|
-
await
|
|
4621
|
+
await fs4__namespace.writeFile(agentsMdPath, agentsContent, "utf-8");
|
|
4389
4622
|
await generateOpenSpecConfig(openspecDir, projectInfo);
|
|
4390
4623
|
return {
|
|
4391
4624
|
output: chalk9__default.default.green("\u2713 \u9879\u76EE\u521D\u59CB\u5316\u5B8C\u6210\n") + chalk9__default.default.gray(` \u9879\u76EE\u7C7B\u578B: ${projectInfo.type}
|
|
@@ -4426,7 +4659,7 @@ async function createSfCliDirectory(basePath) {
|
|
|
4426
4659
|
"logs"
|
|
4427
4660
|
];
|
|
4428
4661
|
for (const dir of dirs) {
|
|
4429
|
-
await
|
|
4662
|
+
await fs4__namespace.mkdir(path5__namespace.join(basePath, dir), { recursive: true });
|
|
4430
4663
|
}
|
|
4431
4664
|
const config = {
|
|
4432
4665
|
version: "1.0.0",
|
|
@@ -4434,22 +4667,22 @@ async function createSfCliDirectory(basePath) {
|
|
|
4434
4667
|
yolo: false,
|
|
4435
4668
|
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
4436
4669
|
};
|
|
4437
|
-
await
|
|
4670
|
+
await fs4__namespace.writeFile(
|
|
4438
4671
|
path5__namespace.join(basePath, "config.json"),
|
|
4439
4672
|
JSON.stringify(config, null, 2),
|
|
4440
4673
|
"utf-8"
|
|
4441
4674
|
);
|
|
4442
|
-
await
|
|
4675
|
+
await fs4__namespace.writeFile(
|
|
4443
4676
|
path5__namespace.join(basePath, "agents", "registry.json"),
|
|
4444
4677
|
JSON.stringify({ version: "1.0.0", agents: {} }, null, 2),
|
|
4445
4678
|
"utf-8"
|
|
4446
4679
|
);
|
|
4447
|
-
await
|
|
4680
|
+
await fs4__namespace.writeFile(
|
|
4448
4681
|
path5__namespace.join(basePath, "skills", "registry.json"),
|
|
4449
4682
|
JSON.stringify({ version: "1.0.0", skills: {} }, null, 2),
|
|
4450
4683
|
"utf-8"
|
|
4451
4684
|
);
|
|
4452
|
-
await
|
|
4685
|
+
await fs4__namespace.writeFile(
|
|
4453
4686
|
path5__namespace.join(basePath, "health", "health.md"),
|
|
4454
4687
|
generateHealthTemplate(),
|
|
4455
4688
|
"utf-8"
|
|
@@ -4459,8 +4692,8 @@ async function createOpenSpecDirectory(basePath) {
|
|
|
4459
4692
|
const changesDir = path5__namespace.join(basePath, "changes");
|
|
4460
4693
|
const archiveDir = path5__namespace.join(changesDir, "archive");
|
|
4461
4694
|
const specDir = path5__namespace.join(basePath, "spec");
|
|
4462
|
-
await
|
|
4463
|
-
await
|
|
4695
|
+
await fs4__namespace.mkdir(archiveDir, { recursive: true });
|
|
4696
|
+
await fs4__namespace.mkdir(specDir, { recursive: true });
|
|
4464
4697
|
}
|
|
4465
4698
|
async function analyzeProject(cwd) {
|
|
4466
4699
|
const result = {
|
|
@@ -4485,7 +4718,7 @@ async function analyzeProject(cwd) {
|
|
|
4485
4718
|
};
|
|
4486
4719
|
const pkgPath = path5__namespace.join(cwd, "package.json");
|
|
4487
4720
|
try {
|
|
4488
|
-
const pkgContent = await
|
|
4721
|
+
const pkgContent = await fs4__namespace.readFile(pkgPath, "utf-8");
|
|
4489
4722
|
const pkg = JSON.parse(pkgContent);
|
|
4490
4723
|
result.name = pkg.name || result.name;
|
|
4491
4724
|
result.dependencies = pkg.dependencies || {};
|
|
@@ -4568,7 +4801,7 @@ async function analyzeDirectoryStructure(cwd) {
|
|
|
4568
4801
|
others: []
|
|
4569
4802
|
};
|
|
4570
4803
|
try {
|
|
4571
|
-
const entries = await
|
|
4804
|
+
const entries = await fs4__namespace.readdir(cwd, { withFileTypes: true });
|
|
4572
4805
|
for (const entry of entries) {
|
|
4573
4806
|
if (!entry.isDirectory()) continue;
|
|
4574
4807
|
const name = entry.name;
|
|
@@ -4613,7 +4846,7 @@ async function findEntryPoints(cwd) {
|
|
|
4613
4846
|
}
|
|
4614
4847
|
async function saveProjectAnalysis(sfCliDir, analysis) {
|
|
4615
4848
|
const analysisPath = path5__namespace.join(sfCliDir, "cache", "analysis", "project-analysis.json");
|
|
4616
|
-
await
|
|
4849
|
+
await fs4__namespace.writeFile(
|
|
4617
4850
|
analysisPath,
|
|
4618
4851
|
JSON.stringify(analysis, null, 2),
|
|
4619
4852
|
"utf-8"
|
|
@@ -4654,7 +4887,7 @@ architecture:
|
|
|
4654
4887
|
# \u5F00\u53D1\u89C4\u8303 (\u4ECE\u4EE3\u7801\u4E2D\u5B66\u4E60)
|
|
4655
4888
|
standards: []
|
|
4656
4889
|
`;
|
|
4657
|
-
await
|
|
4890
|
+
await fs4__namespace.writeFile(configPath, content, "utf-8");
|
|
4658
4891
|
}
|
|
4659
4892
|
function generateAgentsMd(info, scanResult) {
|
|
4660
4893
|
const techStackList = info.techStack.length > 0 ? info.techStack.map((t) => `| ${t} | \u2713 |`).join("\n") : "| \u5F85\u8BC6\u522B | - |";
|
|
@@ -4776,7 +5009,7 @@ function generateHealthTemplate() {
|
|
|
4776
5009
|
}
|
|
4777
5010
|
async function fileExists(filePath) {
|
|
4778
5011
|
try {
|
|
4779
|
-
await
|
|
5012
|
+
await fs4__namespace.access(filePath);
|
|
4780
5013
|
return true;
|
|
4781
5014
|
} catch {
|
|
4782
5015
|
return false;
|
|
@@ -5243,19 +5476,19 @@ var WorkflowEngine = class {
|
|
|
5243
5476
|
async loadProjectContext() {
|
|
5244
5477
|
const agentsMdPath = path5__namespace.join(this.projectPath, "AGENTS.md");
|
|
5245
5478
|
try {
|
|
5246
|
-
this.projectContext = await
|
|
5479
|
+
this.projectContext = await fs4__namespace.readFile(agentsMdPath, "utf-8");
|
|
5247
5480
|
} catch {
|
|
5248
5481
|
this.projectContext = "";
|
|
5249
5482
|
}
|
|
5250
5483
|
const configPath = path5__namespace.join(this.openspecPath, "config.yaml");
|
|
5251
5484
|
try {
|
|
5252
|
-
this.projectConfig = await
|
|
5485
|
+
this.projectConfig = await fs4__namespace.readFile(configPath, "utf-8");
|
|
5253
5486
|
} catch {
|
|
5254
5487
|
this.projectConfig = "";
|
|
5255
5488
|
}
|
|
5256
5489
|
const devstandedPath = path5__namespace.join(this.projectPath, ".sf-cli", "norms", "devstanded.md");
|
|
5257
5490
|
try {
|
|
5258
|
-
this.devStandards = await
|
|
5491
|
+
this.devStandards = await fs4__namespace.readFile(devstandedPath, "utf-8");
|
|
5259
5492
|
} catch {
|
|
5260
5493
|
this.devStandards = "";
|
|
5261
5494
|
}
|
|
@@ -5284,7 +5517,7 @@ var WorkflowEngine = class {
|
|
|
5284
5517
|
const specPath = this.getSpecFilePath();
|
|
5285
5518
|
if (!specPath) return false;
|
|
5286
5519
|
try {
|
|
5287
|
-
await
|
|
5520
|
+
await fs4__namespace.access(specPath);
|
|
5288
5521
|
return true;
|
|
5289
5522
|
} catch {
|
|
5290
5523
|
return false;
|
|
@@ -5518,11 +5751,11 @@ var WorkflowEngine = class {
|
|
|
5518
5751
|
const workflows = [];
|
|
5519
5752
|
const changesDir = path5__namespace.join(this.openspecPath, "changes");
|
|
5520
5753
|
try {
|
|
5521
|
-
const files = await
|
|
5754
|
+
const files = await fs4__namespace.readdir(changesDir);
|
|
5522
5755
|
for (const file of files) {
|
|
5523
5756
|
if (!file.endsWith(".md") || file.includes("-spec.md")) continue;
|
|
5524
5757
|
const filePath = path5__namespace.join(changesDir, file);
|
|
5525
|
-
const content = await
|
|
5758
|
+
const content = await fs4__namespace.readFile(filePath, "utf-8");
|
|
5526
5759
|
const state = this.parseChangeRecord(content);
|
|
5527
5760
|
if (state && state.status === "running") {
|
|
5528
5761
|
workflows.push(state);
|
|
@@ -5573,7 +5806,7 @@ var WorkflowEngine = class {
|
|
|
5573
5806
|
}
|
|
5574
5807
|
const statePath = path5__namespace.join(this.openspecPath, ".workflow-states", `${changeId}.json`);
|
|
5575
5808
|
try {
|
|
5576
|
-
const content = await
|
|
5809
|
+
const content = await fs4__namespace.readFile(statePath, "utf-8");
|
|
5577
5810
|
this.state = JSON.parse(content, (key, value) => {
|
|
5578
5811
|
if (key.endsWith("At") && typeof value === "string") {
|
|
5579
5812
|
return new Date(value);
|
|
@@ -5586,7 +5819,7 @@ var WorkflowEngine = class {
|
|
|
5586
5819
|
const changesDir = path5__namespace.join(this.openspecPath, "changes");
|
|
5587
5820
|
const changeFile = path5__namespace.join(changesDir, `${changeId}.md`);
|
|
5588
5821
|
try {
|
|
5589
|
-
const content = await
|
|
5822
|
+
const content = await fs4__namespace.readFile(changeFile, "utf-8");
|
|
5590
5823
|
const parsed = this.parseChangeRecord(content);
|
|
5591
5824
|
if (parsed && parsed.status === "running") {
|
|
5592
5825
|
this.state = parsed;
|
|
@@ -5651,8 +5884,8 @@ var WorkflowEngine = class {
|
|
|
5651
5884
|
const archiveDir = path5__namespace.join(changesDir, "archive");
|
|
5652
5885
|
const changeFile = path5__namespace.join(changesDir, `${changeId}.md`);
|
|
5653
5886
|
const archiveFile = path5__namespace.join(archiveDir, `${changeId}.md`);
|
|
5654
|
-
await
|
|
5655
|
-
await
|
|
5887
|
+
await fs4__namespace.mkdir(archiveDir, { recursive: true });
|
|
5888
|
+
await fs4__namespace.rename(changeFile, archiveFile).catch(() => {
|
|
5656
5889
|
});
|
|
5657
5890
|
this.state = null;
|
|
5658
5891
|
this.snapshots.clear();
|
|
@@ -5678,15 +5911,15 @@ var WorkflowEngine = class {
|
|
|
5678
5911
|
const archiveDir = path5__namespace.join(changesDir, "archive");
|
|
5679
5912
|
const specDir = path5__namespace.join(this.openspecPath, "spec");
|
|
5680
5913
|
const statesDir = path5__namespace.join(this.openspecPath, ".workflow-states");
|
|
5681
|
-
await
|
|
5682
|
-
await
|
|
5683
|
-
await
|
|
5914
|
+
await fs4__namespace.mkdir(archiveDir, { recursive: true });
|
|
5915
|
+
await fs4__namespace.mkdir(specDir, { recursive: true });
|
|
5916
|
+
await fs4__namespace.mkdir(statesDir, { recursive: true });
|
|
5684
5917
|
}
|
|
5685
5918
|
async restoreState() {
|
|
5686
5919
|
const activePath = path5__namespace.join(this.openspecPath, ".workflow-active.json");
|
|
5687
5920
|
let activeId = null;
|
|
5688
5921
|
try {
|
|
5689
|
-
const activeContent = await
|
|
5922
|
+
const activeContent = await fs4__namespace.readFile(activePath, "utf-8");
|
|
5690
5923
|
const activeData = JSON.parse(activeContent);
|
|
5691
5924
|
activeId = activeData.activeId;
|
|
5692
5925
|
} catch {
|
|
@@ -5694,7 +5927,7 @@ var WorkflowEngine = class {
|
|
|
5694
5927
|
if (activeId) {
|
|
5695
5928
|
const statePath = path5__namespace.join(this.openspecPath, ".workflow-states", `${activeId}.json`);
|
|
5696
5929
|
try {
|
|
5697
|
-
const content = await
|
|
5930
|
+
const content = await fs4__namespace.readFile(statePath, "utf-8");
|
|
5698
5931
|
this.state = JSON.parse(content, (key, value) => {
|
|
5699
5932
|
if (key.endsWith("At") && typeof value === "string") {
|
|
5700
5933
|
return new Date(value);
|
|
@@ -5707,7 +5940,7 @@ var WorkflowEngine = class {
|
|
|
5707
5940
|
}
|
|
5708
5941
|
const oldStatePath = path5__namespace.join(this.openspecPath, ".workflow-state.json");
|
|
5709
5942
|
try {
|
|
5710
|
-
const content = await
|
|
5943
|
+
const content = await fs4__namespace.readFile(oldStatePath, "utf-8");
|
|
5711
5944
|
this.state = JSON.parse(content, (key, value) => {
|
|
5712
5945
|
if (key.endsWith("At") && typeof value === "string") {
|
|
5713
5946
|
return new Date(value);
|
|
@@ -5716,7 +5949,7 @@ var WorkflowEngine = class {
|
|
|
5716
5949
|
});
|
|
5717
5950
|
if (this.state) {
|
|
5718
5951
|
await this.saveState();
|
|
5719
|
-
await
|
|
5952
|
+
await fs4__namespace.unlink(oldStatePath).catch(() => {
|
|
5720
5953
|
});
|
|
5721
5954
|
}
|
|
5722
5955
|
} catch (e) {
|
|
@@ -5730,16 +5963,16 @@ var WorkflowEngine = class {
|
|
|
5730
5963
|
async saveState() {
|
|
5731
5964
|
if (!this.state) return;
|
|
5732
5965
|
const statesDir = path5__namespace.join(this.openspecPath, ".workflow-states");
|
|
5733
|
-
await
|
|
5966
|
+
await fs4__namespace.mkdir(statesDir, { recursive: true });
|
|
5734
5967
|
const statePath = path5__namespace.join(statesDir, `${this.state.id}.json`);
|
|
5735
|
-
await
|
|
5968
|
+
await fs4__namespace.writeFile(statePath, JSON.stringify(this.state, null, 2));
|
|
5736
5969
|
const activePath = path5__namespace.join(this.openspecPath, ".workflow-active.json");
|
|
5737
|
-
await
|
|
5970
|
+
await fs4__namespace.writeFile(activePath, JSON.stringify({ activeId: this.state.id }, null, 2));
|
|
5738
5971
|
}
|
|
5739
5972
|
async restoreSnapshots() {
|
|
5740
5973
|
const snapshotsPath = path5__namespace.join(this.openspecPath, ".workflow-snapshots.json");
|
|
5741
5974
|
try {
|
|
5742
|
-
const content = await
|
|
5975
|
+
const content = await fs4__namespace.readFile(snapshotsPath, "utf-8");
|
|
5743
5976
|
const data = JSON.parse(content, (key, value) => {
|
|
5744
5977
|
if (key === "timestamp" && typeof value === "string") {
|
|
5745
5978
|
return new Date(value);
|
|
@@ -5756,7 +5989,7 @@ var WorkflowEngine = class {
|
|
|
5756
5989
|
async saveSnapshots() {
|
|
5757
5990
|
const snapshotsPath = path5__namespace.join(this.openspecPath, ".workflow-snapshots.json");
|
|
5758
5991
|
const data = Array.from(this.snapshots.values());
|
|
5759
|
-
await
|
|
5992
|
+
await fs4__namespace.writeFile(snapshotsPath, JSON.stringify(data, null, 2));
|
|
5760
5993
|
}
|
|
5761
5994
|
async createSnapshot() {
|
|
5762
5995
|
if (!this.state) return;
|
|
@@ -5777,7 +6010,7 @@ var WorkflowEngine = class {
|
|
|
5777
6010
|
async createChangeRecord() {
|
|
5778
6011
|
if (!this.state) return;
|
|
5779
6012
|
const changePath = path5__namespace.join(this.openspecPath, "changes", `${this.state.id}.md`);
|
|
5780
|
-
await
|
|
6013
|
+
await fs4__namespace.writeFile(changePath, this.formatChangeRecord());
|
|
5781
6014
|
}
|
|
5782
6015
|
async updateChangeRecord(status) {
|
|
5783
6016
|
if (!this.state) return;
|
|
@@ -5785,7 +6018,7 @@ var WorkflowEngine = class {
|
|
|
5785
6018
|
this.state.status = status;
|
|
5786
6019
|
}
|
|
5787
6020
|
const changePath = path5__namespace.join(this.openspecPath, "changes", `${this.state.id}.md`);
|
|
5788
|
-
await
|
|
6021
|
+
await fs4__namespace.writeFile(changePath, this.formatChangeRecord());
|
|
5789
6022
|
}
|
|
5790
6023
|
formatChangeRecord() {
|
|
5791
6024
|
if (!this.state) return "";
|
|
@@ -5849,7 +6082,7 @@ ${this.state.steps.map((s) => `- [${s.status === "completed" ? "x" : " "}] ${s.s
|
|
|
5849
6082
|
|
|
5850
6083
|
${this.state.artifacts.map((a) => `- ${a}`).join("\n") || "\u6682\u65E0"}
|
|
5851
6084
|
`;
|
|
5852
|
-
await
|
|
6085
|
+
await fs4__namespace.writeFile(specPath, content);
|
|
5853
6086
|
}
|
|
5854
6087
|
};
|
|
5855
6088
|
var ConfirmationRequiredError = class extends Error {
|
|
@@ -6346,15 +6579,15 @@ async function loadProjectContext(workingDirectory) {
|
|
|
6346
6579
|
devStandards: ""
|
|
6347
6580
|
};
|
|
6348
6581
|
try {
|
|
6349
|
-
context.agentsMd = await
|
|
6582
|
+
context.agentsMd = await fs4__namespace.readFile(path5__namespace.join(workingDirectory, "AGENTS.md"), "utf-8");
|
|
6350
6583
|
} catch {
|
|
6351
6584
|
}
|
|
6352
6585
|
try {
|
|
6353
|
-
context.configYaml = await
|
|
6586
|
+
context.configYaml = await fs4__namespace.readFile(path5__namespace.join(workingDirectory, "openspec", "config.yaml"), "utf-8");
|
|
6354
6587
|
} catch {
|
|
6355
6588
|
}
|
|
6356
6589
|
try {
|
|
6357
|
-
context.devStandards = await
|
|
6590
|
+
context.devStandards = await fs4__namespace.readFile(path5__namespace.join(workingDirectory, ".sf-cli", "norms", "devstanded.md"), "utf-8");
|
|
6358
6591
|
} catch {
|
|
6359
6592
|
}
|
|
6360
6593
|
return context;
|
|
@@ -7276,9 +7509,9 @@ async function handleFileReference(filePath, ctx) {
|
|
|
7276
7509
|
const cwd = ctx.options.workingDirectory;
|
|
7277
7510
|
const absolutePath = path5__namespace.isAbsolute(filePath) ? filePath : path5__namespace.join(cwd, filePath);
|
|
7278
7511
|
try {
|
|
7279
|
-
const stats = await
|
|
7512
|
+
const stats = await fs4__namespace.stat(absolutePath);
|
|
7280
7513
|
if (stats.isDirectory()) {
|
|
7281
|
-
const files = await
|
|
7514
|
+
const files = await fs4__namespace.readdir(absolutePath);
|
|
7282
7515
|
return {
|
|
7283
7516
|
output: chalk9__default.default.cyan(`\u{1F4C1} ${filePath}/`) + chalk9__default.default.gray(`
|
|
7284
7517
|
${files.slice(0, 20).join("\n")}`) + (files.length > 20 ? chalk9__default.default.gray(`
|
|
@@ -7286,7 +7519,7 @@ ${files.slice(0, 20).join("\n")}`) + (files.length > 20 ? chalk9__default.defaul
|
|
|
7286
7519
|
contextUsed: 0
|
|
7287
7520
|
};
|
|
7288
7521
|
}
|
|
7289
|
-
const content = await
|
|
7522
|
+
const content = await fs4__namespace.readFile(absolutePath, "utf-8");
|
|
7290
7523
|
const lines = content.split("\n");
|
|
7291
7524
|
ctx.contextManager.addMessage({
|
|
7292
7525
|
role: "user",
|
|
@@ -7872,14 +8105,14 @@ ${summary}`,
|
|
|
7872
8105
|
async persist() {
|
|
7873
8106
|
if (!this.persistPath) return;
|
|
7874
8107
|
const dir = path5__namespace.dirname(this.persistPath);
|
|
7875
|
-
await
|
|
8108
|
+
await fs4__namespace.mkdir(dir, { recursive: true });
|
|
7876
8109
|
const data = {
|
|
7877
8110
|
messages: this.state.messages,
|
|
7878
8111
|
totalTokens: this.state.totalTokens,
|
|
7879
8112
|
limit: this.state.limit,
|
|
7880
8113
|
savedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
7881
8114
|
};
|
|
7882
|
-
await
|
|
8115
|
+
await fs4__namespace.writeFile(this.persistPath, JSON.stringify(data, null, 2), "utf-8");
|
|
7883
8116
|
}
|
|
7884
8117
|
/**
|
|
7885
8118
|
* 加载持久化的上下文
|
|
@@ -7887,7 +8120,7 @@ ${summary}`,
|
|
|
7887
8120
|
async loadPersistedContext() {
|
|
7888
8121
|
if (!this.persistPath) return;
|
|
7889
8122
|
try {
|
|
7890
|
-
const content = await
|
|
8123
|
+
const content = await fs4__namespace.readFile(this.persistPath, "utf-8");
|
|
7891
8124
|
const data = JSON.parse(content);
|
|
7892
8125
|
this.state.messages = data.messages || [];
|
|
7893
8126
|
this.state.totalTokens = data.totalTokens || 0;
|
|
@@ -7976,7 +8209,7 @@ var ConfigManager = class {
|
|
|
7976
8209
|
this.projectPath = projectPath;
|
|
7977
8210
|
this.configPath = path5__namespace.join(projectPath, ".sf-cli", "config.json");
|
|
7978
8211
|
try {
|
|
7979
|
-
const content = await
|
|
8212
|
+
const content = await fs4__namespace.readFile(this.configPath, "utf-8");
|
|
7980
8213
|
const loaded = JSON.parse(content);
|
|
7981
8214
|
if (loaded.apiKey && loaded.apiKeyEncrypted) {
|
|
7982
8215
|
loaded.apiKey = this.decrypt(loaded.apiKey);
|
|
@@ -7995,8 +8228,8 @@ var ConfigManager = class {
|
|
|
7995
8228
|
configToSave.apiKey = encrypted;
|
|
7996
8229
|
configToSave.apiKeyEncrypted = true;
|
|
7997
8230
|
}
|
|
7998
|
-
await
|
|
7999
|
-
await
|
|
8231
|
+
await fs4__namespace.mkdir(path5__namespace.dirname(this.configPath), { recursive: true });
|
|
8232
|
+
await fs4__namespace.writeFile(
|
|
8000
8233
|
this.configPath,
|
|
8001
8234
|
JSON.stringify(configToSave, null, 2),
|
|
8002
8235
|
"utf-8"
|
|
@@ -8248,8 +8481,8 @@ var ModelService = class {
|
|
|
8248
8481
|
const dailyPath = path5__namespace.join(this.statsPath, "daily.json");
|
|
8249
8482
|
const totalPath = path5__namespace.join(this.statsPath, "total.json");
|
|
8250
8483
|
const [daily, total] = await Promise.all([
|
|
8251
|
-
|
|
8252
|
-
|
|
8484
|
+
fs4__namespace.readFile(dailyPath, "utf-8").catch(() => "{}"),
|
|
8485
|
+
fs4__namespace.readFile(totalPath, "utf-8").catch(() => '{"totalInput":0,"totalOutput":0}')
|
|
8253
8486
|
]);
|
|
8254
8487
|
const dailyData = JSON.parse(daily);
|
|
8255
8488
|
const totalData = JSON.parse(total);
|
|
@@ -8262,12 +8495,12 @@ var ModelService = class {
|
|
|
8262
8495
|
async saveStats() {
|
|
8263
8496
|
if (!this.statsPath) return;
|
|
8264
8497
|
try {
|
|
8265
|
-
await
|
|
8498
|
+
await fs4__namespace.mkdir(this.statsPath, { recursive: true });
|
|
8266
8499
|
const dailyPath = path5__namespace.join(this.statsPath, "daily.json");
|
|
8267
8500
|
const totalPath = path5__namespace.join(this.statsPath, "total.json");
|
|
8268
8501
|
await Promise.all([
|
|
8269
|
-
|
|
8270
|
-
|
|
8502
|
+
fs4__namespace.writeFile(dailyPath, JSON.stringify(this.stats.byDate, null, 2)),
|
|
8503
|
+
fs4__namespace.writeFile(totalPath, JSON.stringify({
|
|
8271
8504
|
totalInput: this.stats.totalInput,
|
|
8272
8505
|
totalOutput: this.stats.totalOutput
|
|
8273
8506
|
}))
|
|
@@ -8480,7 +8713,7 @@ var Completer = class {
|
|
|
8480
8713
|
prefix = filePath.slice(lastSlash + 1);
|
|
8481
8714
|
}
|
|
8482
8715
|
try {
|
|
8483
|
-
const entries = await
|
|
8716
|
+
const entries = await fs4__namespace.readdir(dirPath, { withFileTypes: true });
|
|
8484
8717
|
const matches = [];
|
|
8485
8718
|
for (const entry of entries) {
|
|
8486
8719
|
if (entry.name.startsWith(prefix)) {
|
|
@@ -8562,7 +8795,7 @@ async function startInteractiveMode(options) {
|
|
|
8562
8795
|
const historyFile = path5__namespace.join(options.workingDirectory, ".sf-cli", "history.json");
|
|
8563
8796
|
let history = [];
|
|
8564
8797
|
try {
|
|
8565
|
-
const historyData = await
|
|
8798
|
+
const historyData = await fs4__namespace.readFile(historyFile, "utf-8");
|
|
8566
8799
|
history = JSON.parse(historyData);
|
|
8567
8800
|
} catch {
|
|
8568
8801
|
}
|
|
@@ -8649,8 +8882,8 @@ async function startInteractiveMode(options) {
|
|
|
8649
8882
|
}
|
|
8650
8883
|
const saveHistory = async () => {
|
|
8651
8884
|
try {
|
|
8652
|
-
await
|
|
8653
|
-
await
|
|
8885
|
+
await fs4__namespace.mkdir(path5__namespace.dirname(historyFile), { recursive: true });
|
|
8886
|
+
await fs4__namespace.writeFile(historyFile, JSON.stringify(history.slice(-500)));
|
|
8654
8887
|
} catch {
|
|
8655
8888
|
}
|
|
8656
8889
|
};
|