aico-cli 0.2.5 → 0.2.8
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/chunks/simple-config.mjs +4 -4
- package/dist/cli.mjs +32 -53
- package/dist/index.mjs +52 -4
- package/package.json +1 -1
- package/templates/CLAUDE.md +77 -2
- package/templates/agents/aico/requirement/task-executor.md +25 -3
- package/templates/agents/aico/requirement/task-splitter-validator.md +18 -4
- package/templates/commands/aico/requirement.md +16 -7
- package/templates/personality.md +12 -13
- package/templates/utils/task-manager.sh +401 -0
- package/dist/chunks/feature-checker.mjs +0 -117
- package/dist/shared/aico-cli.CWSSz8Hk.mjs +0 -51
|
@@ -13,7 +13,7 @@ import { join as join$1 } from 'node:path';
|
|
|
13
13
|
import { join, dirname, basename } from 'pathe';
|
|
14
14
|
import { fileURLToPath } from 'node:url';
|
|
15
15
|
|
|
16
|
-
const version = "0.2.
|
|
16
|
+
const version = "0.2.8";
|
|
17
17
|
|
|
18
18
|
function displayBanner(subtitle) {
|
|
19
19
|
const defaultSubtitle = "\u4E00\u952E\u914D\u7F6E\u4F60\u7684\u5F00\u53D1\u73AF\u5883";
|
|
@@ -4959,7 +4959,7 @@ class ConfigInstaller extends AbstractInstaller {
|
|
|
4959
4959
|
}
|
|
4960
4960
|
}
|
|
4961
4961
|
return this.createSuccessResult(
|
|
4962
|
-
`\u914D\u7F6E\u5B8C\u6210
|
|
4962
|
+
`\u914D\u7F6E\u5B8C\u6210`,
|
|
4963
4963
|
backupPath
|
|
4964
4964
|
);
|
|
4965
4965
|
} catch (error) {
|
|
@@ -5072,7 +5072,7 @@ class MCPInstaller extends AbstractInstaller {
|
|
|
5072
5072
|
const selectedServices = MCP_SERVICES.map((service) => service.id);
|
|
5073
5073
|
const backupPath = backupMcpConfig();
|
|
5074
5074
|
if (backupPath) {
|
|
5075
|
-
this.log(`MCP \u914D\u7F6E\u5DF2\u5907\u4EFD
|
|
5075
|
+
this.log(`MCP \u914D\u7F6E\u5DF2\u5907\u4EFD`, "info");
|
|
5076
5076
|
}
|
|
5077
5077
|
const newServers = {};
|
|
5078
5078
|
for (const serviceId of selectedServices) {
|
|
@@ -5560,4 +5560,4 @@ async function openSettingsJson() {
|
|
|
5560
5560
|
}
|
|
5561
5561
|
}
|
|
5562
5562
|
|
|
5563
|
-
export { AICO_CONFIG_FILE as A,
|
|
5563
|
+
export { AICO_CONFIG_FILE as A, getMcpConfigPath as B, CLAUDE_DIR as C, DEFAULT_FILE_COPY_CONFIGS as D, readMcpConfig as E, writeMcpConfig as F, backupMcpConfig as G, mergeMcpServers as H, buildMcpServerConfig as I, fixWindowsMcpConfig as J, addCompletedOnboarding as K, LEGACY_AICO_CONFIG_FILE as L, MCP_SERVICES as M, createEscapablePrompt as N, displayBannerWithInfo as O, executeWithEscapeSupport as P, handleExitPromptError as Q, handleGeneralError as R, SETTINGS_FILE as S, EscapeKeyPressed as T, displayBanner as U, version as V, ConfigCheckerInstaller as W, readAicoConfig as X, init$1 as Y, init as a, getPlatform as b, commandExists as c, importRecommendedEnv as d, importRecommendedPermissions as e, cleanupPermissions as f, getTermuxPrefix as g, mergeAndCleanPermissions as h, isTermux as i, CLAUDE_MD_FILE as j, ClAUDE_CONFIG_FILE as k, SUPPORTED_LANGS as l, messages as m, LANG_LABELS as n, openSettingsJson as o, AI_OUTPUT_LANGUAGES as p, ensureClaudeDir as q, backupExistingConfig as r, copyConfigFiles as s, copyConfigFilesWithConfig as t, configureApi as u, mergeConfigs as v, updateDefaultModel as w, mergeSettingsFile as x, getExistingApiConfig as y, applyAiLanguageDirective as z };
|
package/dist/cli.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import cac from 'cac';
|
|
3
3
|
import ansis from 'ansis';
|
|
4
|
-
import {
|
|
4
|
+
import { N as createEscapablePrompt, O as displayBannerWithInfo, a as init, P as executeWithEscapeSupport, Q as handleExitPromptError, R as handleGeneralError, T as EscapeKeyPressed, U as displayBanner, V as version, W as ConfigCheckerInstaller, X as readAicoConfig } from './chunks/simple-config.mjs';
|
|
5
5
|
import inquirer$1 from 'inquirer';
|
|
6
6
|
import { spawn, exec as exec$1 } from 'node:child_process';
|
|
7
7
|
import 'tinyexec';
|
|
@@ -341,7 +341,7 @@ function setupCommands(cli) {
|
|
|
341
341
|
} else if (options.update) {
|
|
342
342
|
await update({});
|
|
343
343
|
} else if (options.company) {
|
|
344
|
-
const { init: init2 } = await import('./chunks/simple-config.mjs').then(function (n) { return n.
|
|
344
|
+
const { init: init2 } = await import('./chunks/simple-config.mjs').then(function (n) { return n.Y; });
|
|
345
345
|
await init2({
|
|
346
346
|
apiType: "auth_token",
|
|
347
347
|
force: options.force,
|
|
@@ -349,7 +349,7 @@ function setupCommands(cli) {
|
|
|
349
349
|
skipPrompt: true
|
|
350
350
|
});
|
|
351
351
|
} else if (options.personal) {
|
|
352
|
-
const { init: init2 } = await import('./chunks/simple-config.mjs').then(function (n) { return n.
|
|
352
|
+
const { init: init2 } = await import('./chunks/simple-config.mjs').then(function (n) { return n.Y; });
|
|
353
353
|
await init2({
|
|
354
354
|
apiType: "ccr_proxy",
|
|
355
355
|
force: options.force,
|
|
@@ -370,21 +370,37 @@ async function startCodeEditor() {
|
|
|
370
370
|
try {
|
|
371
371
|
const context = createDefaultContext();
|
|
372
372
|
const checker = new ConfigCheckerInstaller(context);
|
|
373
|
-
const
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
console.log(ansis.yellow(`\u26A0\uFE0F \
|
|
381
|
-
|
|
382
|
-
|
|
373
|
+
const configType = await checker.detectConfigType();
|
|
374
|
+
const aicoConfig = readAicoConfig();
|
|
375
|
+
const needsReinstall = !aicoConfig || aicoConfig.version !== version;
|
|
376
|
+
if (configType === "none" || needsReinstall) {
|
|
377
|
+
if (configType === "none") {
|
|
378
|
+
console.log(ansis.yellow("\u26A0\uFE0F \u672A\u68C0\u6D4B\u5230\u914D\u7F6E\uFF0C\u5C06\u6309\u516C\u53F8\u914D\u7F6E\u8FDB\u884C\u5B89\u88C5..."));
|
|
379
|
+
} else {
|
|
380
|
+
console.log(ansis.yellow(`\u26A0\uFE0F \u68C0\u6D4B\u5230\u65B0\u7248\u672C (\u5F53\u524D: ${version}, \u914D\u7F6E: ${aicoConfig?.version || "\u65E0"})\uFF0C\u5C06\u81EA\u52A8\u5347\u7EA7...`));
|
|
381
|
+
}
|
|
382
|
+
if (configType === "company") {
|
|
383
|
+
const { init: init2 } = await import('./chunks/simple-config.mjs').then(function (n) { return n.Y; });
|
|
384
|
+
await init2({
|
|
385
|
+
apiType: "auth_token",
|
|
386
|
+
skipBanner: true,
|
|
387
|
+
skipPrompt: true,
|
|
388
|
+
force: true
|
|
389
|
+
});
|
|
390
|
+
} else if (configType === "personal") {
|
|
391
|
+
const { init: init2 } = await import('./chunks/simple-config.mjs').then(function (n) { return n.Y; });
|
|
392
|
+
await init2({
|
|
393
|
+
apiType: "ccr_proxy",
|
|
394
|
+
skipBanner: true,
|
|
395
|
+
skipPrompt: true,
|
|
396
|
+
force: true
|
|
397
|
+
});
|
|
398
|
+
} else {
|
|
399
|
+
const { init: init2 } = await import('./chunks/simple-config.mjs').then(function (n) { return n.Y; });
|
|
400
|
+
await init2({ skipBanner: true, skipPrompt: true });
|
|
383
401
|
}
|
|
384
402
|
} else {
|
|
385
|
-
console.log(ansis.
|
|
386
|
-
const { init: init2 } = await import('./chunks/simple-config.mjs').then(function (n) { return n.Z; });
|
|
387
|
-
await init2({ skipBanner: true, skipPrompt: true });
|
|
403
|
+
console.log(ansis.green(`\u2705 \u68C0\u6D4B\u5230${configType === "company" ? "\u516C\u53F8" : "\u4E2A\u4EBA"}\u914D\u7F6E\uFF0C\u7248\u672C\u5339\u914D\uFF0C\u8DF3\u8FC7\u914D\u7F6E\u68C0\u67E5...`));
|
|
388
404
|
}
|
|
389
405
|
await startClaudeCodeEditor();
|
|
390
406
|
} catch (error) {
|
|
@@ -417,43 +433,6 @@ function customizeHelp(sections) {
|
|
|
417
433
|
});
|
|
418
434
|
return sections;
|
|
419
435
|
}
|
|
420
|
-
async function installMissingFeatures(context, _featureStatus, missingFeatures) {
|
|
421
|
-
try {
|
|
422
|
-
const executor = new InstallerExecutor(context);
|
|
423
|
-
const featureToInstallerMap = {
|
|
424
|
-
config: "config",
|
|
425
|
-
claudeCode: "claude-code",
|
|
426
|
-
statusBar: "ccometix-line"
|
|
427
|
-
// workflow 和 aiPersonality 需要特殊处理,不能映射到 config-checker
|
|
428
|
-
};
|
|
429
|
-
for (const featureName of missingFeatures) {
|
|
430
|
-
if (featureName === "aiPersonality") {
|
|
431
|
-
console.log(ansis.blue(`\u{1F4E6} \u5B89\u88C5\u529F\u80FD: ${featureName}...`));
|
|
432
|
-
console.log(ansis.green(`\u2705 ${featureName} \u5B89\u88C5\u6210\u529F`));
|
|
433
|
-
} else {
|
|
434
|
-
const installerName = featureToInstallerMap[featureName];
|
|
435
|
-
if (installerName) {
|
|
436
|
-
console.log(ansis.blue(`\u{1F4E6} \u5B89\u88C5\u529F\u80FD: ${featureName}...`));
|
|
437
|
-
const result = await executor.execute(installerName, {
|
|
438
|
-
skipPrompt: true,
|
|
439
|
-
silent: true
|
|
440
|
-
});
|
|
441
|
-
if (result.success) {
|
|
442
|
-
console.log(ansis.green(`\u2705 ${featureName} \u5B89\u88C5\u6210\u529F`));
|
|
443
|
-
} else {
|
|
444
|
-
console.log(ansis.yellow(`\u26A0\uFE0F ${featureName} \u5B89\u88C5\u5931\u8D25: ${result.message}`));
|
|
445
|
-
}
|
|
446
|
-
} else {
|
|
447
|
-
console.log(ansis.yellow(`\u26A0\uFE0F \u672A\u77E5\u529F\u80FD: ${featureName}\uFF0C\u8DF3\u8FC7\u5B89\u88C5`));
|
|
448
|
-
}
|
|
449
|
-
}
|
|
450
|
-
}
|
|
451
|
-
console.log(ansis.green("\u{1F389} \u7F3A\u5931\u529F\u80FD\u5B89\u88C5\u5B8C\u6210\uFF01"));
|
|
452
|
-
} catch (error) {
|
|
453
|
-
console.error(ansis.red(`\u2716 \u81EA\u52A8\u5B89\u88C5\u529F\u80FD\u5931\u8D25: ${error instanceof Error ? error.message : String(error)}`));
|
|
454
|
-
console.log(ansis.yellow("\u{1F4A1} \u8BF7\u624B\u52A8\u8FD0\u884C `aico --init` \u6765\u5B89\u88C5\u7F3A\u5931\u529F\u80FD"));
|
|
455
|
-
}
|
|
456
|
-
}
|
|
457
436
|
|
|
458
437
|
if (!Array.prototype.findLastIndex) {
|
|
459
438
|
Array.prototype.findLastIndex = function(predicate, thisArg) {
|
package/dist/index.mjs
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
export {
|
|
3
|
-
import '
|
|
1
|
+
import { c as commandExists, i as isTermux, m as messages, g as getTermuxPrefix } from './chunks/simple-config.mjs';
|
|
2
|
+
export { A as AICO_CONFIG_FILE, p as AI_OUTPUT_LANGUAGES, C as CLAUDE_DIR, j as CLAUDE_MD_FILE, k as ClAUDE_CONFIG_FILE, D as DEFAULT_FILE_COPY_CONFIGS, n as LANG_LABELS, L as LEGACY_AICO_CONFIG_FILE, M as MCP_SERVICES, S as SETTINGS_FILE, l as SUPPORTED_LANGS, K as addCompletedOnboarding, z as applyAiLanguageDirective, r as backupExistingConfig, G as backupMcpConfig, I as buildMcpServerConfig, f as cleanupPermissions, u as configureApi, s as copyConfigFiles, t as copyConfigFilesWithConfig, q as ensureClaudeDir, J as fixWindowsMcpConfig, y as getExistingApiConfig, B as getMcpConfigPath, b as getPlatform, d as importRecommendedEnv, e as importRecommendedPermissions, a as init, h as mergeAndCleanPermissions, v as mergeConfigs, H as mergeMcpServers, x as mergeSettingsFile, o as openSettingsJson, E as readMcpConfig, w as updateDefaultModel, F as writeMcpConfig } from './chunks/simple-config.mjs';
|
|
3
|
+
import { exec } from 'tinyexec';
|
|
4
|
+
import ansis from 'ansis';
|
|
4
5
|
import 'inquirer';
|
|
5
|
-
import 'tinyexec';
|
|
6
6
|
import 'node:os';
|
|
7
7
|
import 'node:fs';
|
|
8
8
|
import 'node:child_process';
|
|
@@ -14,3 +14,51 @@ import 'dayjs';
|
|
|
14
14
|
import 'node:path';
|
|
15
15
|
import 'pathe';
|
|
16
16
|
import 'node:url';
|
|
17
|
+
|
|
18
|
+
async function isClaudeCodeInstalled() {
|
|
19
|
+
return await commandExists("claude");
|
|
20
|
+
}
|
|
21
|
+
async function installClaudeCode(lang) {
|
|
22
|
+
getTranslation(lang);
|
|
23
|
+
if (isTermux()) {
|
|
24
|
+
console.log(ansis.yellow(`\u2139 ${messages.installation.termuxDetected}`));
|
|
25
|
+
const termuxPrefix = getTermuxPrefix();
|
|
26
|
+
console.log(ansis.gray(messages.installation.termuxPathInfo.replace("{path}", termuxPrefix)));
|
|
27
|
+
console.log(ansis.gray(`Node.js: ${termuxPrefix}/bin/node`));
|
|
28
|
+
console.log(ansis.gray(`npm: ${termuxPrefix}/bin/npm`));
|
|
29
|
+
}
|
|
30
|
+
console.log(messages.installation.installing);
|
|
31
|
+
try {
|
|
32
|
+
await exec("npm", ["install", "-g", "@anthropic-ai/claude-code"]);
|
|
33
|
+
console.log(`\u2714 ${messages.installation.installSuccess}`);
|
|
34
|
+
if (isTermux()) {
|
|
35
|
+
console.log(ansis.gray(`
|
|
36
|
+
Claude Code installed to: ${getTermuxPrefix()}/bin/claude`));
|
|
37
|
+
}
|
|
38
|
+
} catch (error) {
|
|
39
|
+
console.error(`\u2716 ${messages.installation.installFailed}`);
|
|
40
|
+
if (isTermux()) {
|
|
41
|
+
console.error(ansis.yellow(`
|
|
42
|
+
${messages.installation.termuxInstallHint}
|
|
43
|
+
`));
|
|
44
|
+
}
|
|
45
|
+
throw error;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
async function installClaudeCodeSilently() {
|
|
49
|
+
const installed = await isClaudeCodeInstalled();
|
|
50
|
+
if (installed) {
|
|
51
|
+
console.log(ansis.green("\u2714 \u5DF2\u5B89\u88C5 Claude Code"));
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
console.log("\u6B63\u5728\u5B89\u88C5 Claude Code...");
|
|
55
|
+
try {
|
|
56
|
+
await exec("npm", ["install", "-g", "@anthropic-ai/claude-code"]);
|
|
57
|
+
console.log(ansis.green("\u2714 Claude Code \u5B89\u88C5\u6210\u529F"));
|
|
58
|
+
} catch (error) {
|
|
59
|
+
console.error(ansis.red("\u2716 Claude Code \u5B89\u88C5\u5931\u8D25"));
|
|
60
|
+
throw error;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export { commandExists, installClaudeCode, installClaudeCodeSilently, isClaudeCodeInstalled };
|
package/package.json
CHANGED
package/templates/CLAUDE.md
CHANGED
|
@@ -1,2 +1,77 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
[根目录](../CLAUDE.md) > **templates**
|
|
2
|
+
|
|
3
|
+
## 模块职责
|
|
4
|
+
- AI工作流模板与配置文件模板库,提供标准化的配置和工作流定义
|
|
5
|
+
- 支持多种AI开发场景:需求对齐、任务规划、UI设计、代码审查等
|
|
6
|
+
- 包含跨平台脚本和配置模板,确保环境一致性
|
|
7
|
+
|
|
8
|
+
## 入口与启动
|
|
9
|
+
- **模板目录结构**: 按功能分类组织模板文件
|
|
10
|
+
- **模板加载**: 通过 `src/utils/installer.ts` 和相关安装器加载模板
|
|
11
|
+
- **配置应用**: 使用统一的配置管理工具应用模板配置
|
|
12
|
+
|
|
13
|
+
## 对外接口
|
|
14
|
+
- **模板文件**: 提供可直接使用的Markdown、JSON、Shell脚本等模板
|
|
15
|
+
- **配置规范**: 统一的配置格式和命名规范
|
|
16
|
+
- **跨平台支持**: 提供Windows、macOS、Linux兼容的脚本模板
|
|
17
|
+
|
|
18
|
+
## 关键依赖与配置
|
|
19
|
+
- **配置格式**: JSON、Markdown、Shell脚本等标准格式
|
|
20
|
+
- **路径规范**: 统一的模板路径结构和命名约定
|
|
21
|
+
- **版本管理**: 模板版本与CLI版本同步更新
|
|
22
|
+
|
|
23
|
+
## 数据模型
|
|
24
|
+
- **AI智能体模板**: 需求对齐器、任务规划器、UI设计师等专业智能体定义
|
|
25
|
+
- **配置模板**: 编辑器配置、MCP服务配置、个性化设置等
|
|
26
|
+
- **脚本模板**: 跨平台Shell脚本、环境设置脚本等
|
|
27
|
+
- **命令模板**: CLI命令定义和使用示例
|
|
28
|
+
|
|
29
|
+
## 测试与质量
|
|
30
|
+
- **模板验证**: 通过配置验证工具检查模板语法正确性
|
|
31
|
+
- **跨平台测试**: 验证脚本模板在不同平台的兼容性
|
|
32
|
+
- **集成测试**: 在安装过程中验证模板的正确应用
|
|
33
|
+
|
|
34
|
+
## 常见问题 (FAQ)
|
|
35
|
+
- Q: 如何添加新的模板?
|
|
36
|
+
A: 在合适的分类目录下添加模板文件,确保格式规范
|
|
37
|
+
- Q: 模板如何被加载和应用?
|
|
38
|
+
A: 通过对应的安装器(如Claude Code安装器、MCP安装器等)加载和应用
|
|
39
|
+
- Q: 如何确保模板的跨平台兼容性?
|
|
40
|
+
A: 使用跨平台工具(如pathe处理路径)和条件判断语句
|
|
41
|
+
|
|
42
|
+
## 相关文件清单(主要分类)
|
|
43
|
+
|
|
44
|
+
### AI智能体模板 (agents/)
|
|
45
|
+
- `agents/aico/plan/` - 任务规划智能体
|
|
46
|
+
- `planner.md` - 任务规划器
|
|
47
|
+
- `ui-ux-designer.md` - UI/UX设计师
|
|
48
|
+
- `init-architect.md` - 架构初始化器
|
|
49
|
+
- `agents/aico/requirement/` - 需求对齐智能体
|
|
50
|
+
- `requirement-aligner.md` - 需求对齐器
|
|
51
|
+
- `requirement-identifier.md` - 需求识别器
|
|
52
|
+
- `task-executor.md` - 任务执行器
|
|
53
|
+
- `task-splitter-validator.md` - 任务拆分验证器
|
|
54
|
+
- 跨平台脚本: `crossplatform-utils.sh`, `requirement-functions-crossplatform.sh`
|
|
55
|
+
|
|
56
|
+
### 命令模板 (commands/)
|
|
57
|
+
- `commands/aico/` - AICO命令模板
|
|
58
|
+
- `init-project.md` - 项目初始化命令
|
|
59
|
+
- `workflow.md` - 工作流命令
|
|
60
|
+
- `requirement.md` - 需求管理命令
|
|
61
|
+
- `commands/base/` - 基础命令模板
|
|
62
|
+
- `ultrathink.md` - 深度思考命令
|
|
63
|
+
- `tech-debt.md` - 技术债务管理
|
|
64
|
+
- `fix-error.md` - 错误修复命令
|
|
65
|
+
|
|
66
|
+
### 配置模板
|
|
67
|
+
- `settings.json` - 编辑器基础配置模板
|
|
68
|
+
- `language.md` - 多语言支持配置
|
|
69
|
+
- `personality.md` - AI个性配置模板
|
|
70
|
+
|
|
71
|
+
### 工具脚本
|
|
72
|
+
- `utils/task-manager.sh` - 跨平台任务管理脚本
|
|
73
|
+
|
|
74
|
+
## 变更记录 (Changelog)
|
|
75
|
+
- 2025-09-24: 创建完整的模板模块文档,详细分类模板文件
|
|
76
|
+
- 2025-09-22: 增量更新,添加AI智能体模板描述
|
|
77
|
+
- 2025-09-19: 初始模板结构文档化
|
|
@@ -5,7 +5,7 @@ tools: Read, Write, Bash
|
|
|
5
5
|
color: purple
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
-
# 任务自动执行智能体
|
|
8
|
+
# 任务自动执行智能体
|
|
9
9
|
|
|
10
10
|
遵循"领取-执行-验证-报告"的循环流程,自动执行开发任务清单中的原子任务。
|
|
11
11
|
|
|
@@ -54,8 +54,20 @@ color: purple
|
|
|
54
54
|
|
|
55
55
|
```bash
|
|
56
56
|
# 读取任务清单 - 跨平台兼容
|
|
57
|
-
source "$(dirname "$0")/
|
|
58
|
-
|
|
57
|
+
source "$(dirname "$0")/../../utils/task-manager.sh"
|
|
58
|
+
|
|
59
|
+
# 获取可用任务列表
|
|
60
|
+
available_tasks=$(main list ".aico/docs/[需求名称]/开发任务清单.md")
|
|
61
|
+
|
|
62
|
+
# 选择任务并原子领取
|
|
63
|
+
for task_id in $available_tasks; do
|
|
64
|
+
if main check-deps ".aico/docs/[需求名称]/开发任务清单.md" "$task_id"; then
|
|
65
|
+
if main acquire ".aico/docs/[需求名称]/开发任务清单.md" "$task_id"; then
|
|
66
|
+
echo "成功领取任务: $task_id"
|
|
67
|
+
break
|
|
68
|
+
fi
|
|
69
|
+
fi
|
|
70
|
+
done
|
|
59
71
|
```
|
|
60
72
|
|
|
61
73
|
**任务选择逻辑:**
|
|
@@ -220,6 +232,9 @@ case "$PM" in
|
|
|
220
232
|
*) echo "跳过语法检查 - 包管理器: $PM" ;;
|
|
221
233
|
esac
|
|
222
234
|
|
|
235
|
+
# 记录验证结果用于最终状态更新
|
|
236
|
+
validation_result=$?
|
|
237
|
+
|
|
223
238
|
# TypeScript编译检查
|
|
224
239
|
case "$PM" in
|
|
225
240
|
npm) npm run type-check 2>/dev/null ;;
|
|
@@ -265,6 +280,13 @@ esac
|
|
|
265
280
|
### 阶段五:执行报告生成
|
|
266
281
|
|
|
267
282
|
```bash
|
|
283
|
+
# 更新任务状态 - 跨平台兼容
|
|
284
|
+
if [ "$validation_result" -eq 0 ]; then
|
|
285
|
+
main complete ".aico/docs/[需求名称]/开发任务清单.md" "$task_id" "成功"
|
|
286
|
+
else
|
|
287
|
+
main fail ".aico/docs/[需求名称]/开发任务清单.md" "$task_id" "验证失败"
|
|
288
|
+
fi
|
|
289
|
+
|
|
268
290
|
# 更新任务完成报告 - 跨平台兼容
|
|
269
291
|
cat >> ".aico/docs/[需求名称]/开发任务完成报告.md" << 'EOF'
|
|
270
292
|
|
|
@@ -153,13 +153,27 @@ graph TD
|
|
|
153
153
|
|
|
154
154
|
## 📋 详细任务清单
|
|
155
155
|
|
|
156
|
+
### 任务模板
|
|
157
|
+
- **任务ID**: 任务唯一标识 (e.g., `DB-001`, `BE-001`)
|
|
158
|
+
- **状态**: 待执行 | 执行中 | 已完成 | 失败
|
|
159
|
+
- **优先级**: 🔴 高 | 🟡 中 | 🟢 低
|
|
160
|
+
- **依赖**: 依赖的任务ID列表
|
|
161
|
+
- **执行者**: 领取任务的执行者ID
|
|
162
|
+
- **开始时间**: 任务开始执行的时间
|
|
163
|
+
- **完成时间**: 任务完成或失败的时间
|
|
164
|
+
- **结果**: 成功 | 失败 | [其他]
|
|
165
|
+
|
|
156
166
|
### 🗄️ 数据层任务
|
|
157
167
|
|
|
158
168
|
#### 任务A: 数据库迁移
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
169
|
+
- **任务ID**: `DB-001`
|
|
170
|
+
- **状态**: 待执行
|
|
171
|
+
- **优先级**: 🔴 高
|
|
172
|
+
- **依赖**: 无
|
|
173
|
+
- **执行者**:
|
|
174
|
+
- **开始时间**:
|
|
175
|
+
- **完成时间**:
|
|
176
|
+
- **结果**:
|
|
163
177
|
|
|
164
178
|
**具体操作**:
|
|
165
179
|
1. 创建数据库迁移文件 `migrations/001_add_new_table.sql`
|
|
@@ -13,7 +13,7 @@ argument-hint: <需求描述>
|
|
|
13
13
|
作为需求管理的指挥官,统筹整个需求从识别、对齐、拆分到执行的全生命周期:
|
|
14
14
|
|
|
15
15
|
- 🎯 **需求识别**:深度理解用户意图,生成共识文档
|
|
16
|
-
- 🔄 **需求对齐**:分析现有代码库,制定技术实施方案
|
|
16
|
+
- 🔄 **需求对齐**:分析现有代码库,制定技术实施方案
|
|
17
17
|
- 📋 **任务拆分**:将需求分解为可执行的原子任务
|
|
18
18
|
- ⚡ **自动执行**:按依赖关系自动执行开发任务
|
|
19
19
|
- ✅ **质量保障**:全程质量评估和验证
|
|
@@ -28,14 +28,19 @@ argument-hint: <需求描述>
|
|
|
28
28
|
|
|
29
29
|
1. **识别阶段**:用户提供新需求描述 → 调用 `requirement-identifier`
|
|
30
30
|
- 必须生成 `.aico/docs/[需求名称]/共识文档.md`
|
|
31
|
-
-
|
|
32
|
-
|
|
31
|
+
- 文档状态必须设置为`待确认`
|
|
32
|
+
- 必须等待用户确认后,自动更新状态为`已确认`并进入下一阶段
|
|
33
|
+
2. **对齐阶段**:存在共识文档且状态为`已确认`但无技术方案 → 调用 `requirement-aligner`
|
|
34
|
+
- 必须存在 `.aico/docs/[需求名称]/共识文档.md`并且需求状态为`已确认`
|
|
33
35
|
- 必须生成 `.aico/docs/[需求名称]/技术对齐方案文档.md`
|
|
34
|
-
-
|
|
35
|
-
|
|
36
|
+
- 文档状态必须设置为`待确认`
|
|
37
|
+
- 必须等待用户确认后,自动更新状态为`已确认`并进入下一阶段
|
|
38
|
+
3. **拆分阶段**:存在技术方案且状态为`已确认`但无任务清单 → 调用 `task-splitter-validator`
|
|
39
|
+
- 必须存在 `.aico/docs/[需求名称]/技术对齐方案文档.md`并且状态为`已确认`
|
|
36
40
|
- 必须生成 `.aico/docs/[需求名称]/开发任务清单.md`
|
|
37
|
-
-
|
|
38
|
-
|
|
41
|
+
- 文档状态必须设置为`待确认`
|
|
42
|
+
- 必须等待用户确认后,自动更新状态为`已确认`并进入下一阶段
|
|
43
|
+
4. **执行阶段**:存在任务清单且状态为`已确认`且有待执行任务 → 调用 `task-executor`
|
|
39
44
|
5. **验证阶段**:任务执行后需要质量评估 → 调用 `task-executor-validator`
|
|
40
45
|
|
|
41
46
|
### 智能体调用参数
|
|
@@ -44,19 +49,23 @@ argument-hint: <需求描述>
|
|
|
44
49
|
- `user_input`: $ARGUMENTS
|
|
45
50
|
- `current_timestamp`: (来自步骤1的时间戳)
|
|
46
51
|
- `project_context`: (当前项目的 CLAUDE.md 信息)
|
|
52
|
+
- `document_status`: `待确认`
|
|
47
53
|
|
|
48
54
|
- **requirement-aligner**:
|
|
49
55
|
- `consensus_document_path`: `.aico/docs/[需求名称]/共识文档.md`
|
|
50
56
|
- `current_timestamp`: (来自步骤1的时间戳)
|
|
57
|
+
- `document_status`: `待确认`
|
|
51
58
|
|
|
52
59
|
- **task-splitter-validator**:
|
|
53
60
|
- `consensus_document_path`: `.aico/docs/[需求名称]/共识文档.md`
|
|
54
61
|
- `technical_alignment_path`: `.aico/docs/[需求名称]/技术对齐方案文档.md`
|
|
55
62
|
- `current_timestamp`: (来自步骤1的时间戳)
|
|
63
|
+
- `document_status`: `待确认`
|
|
56
64
|
|
|
57
65
|
- **task-executor**:
|
|
58
66
|
- `task_list_path`: `.aico/docs/[需求名称]/开发任务清单.md`
|
|
59
67
|
- `current_timestamp`: (来自步骤1的时间戳)
|
|
68
|
+
- `document_status`: `已确认`
|
|
60
69
|
|
|
61
70
|
- **task-executor-validator**:
|
|
62
71
|
- `task_list_path`: `.aico/docs/[需求名称]/开发任务清单.md`
|
package/templates/personality.md
CHANGED
|
@@ -29,7 +29,7 @@ description: 架构师级软件工程师,以工程卓越为信仰,为追求
|
|
|
29
29
|
影响范围:[详细说明]
|
|
30
30
|
风险评估:[潜在后果]
|
|
31
31
|
|
|
32
|
-
|
|
32
|
+
这里容易踩坑,请确认是否继续?[需要明确的"是"、"确认"、"继续"]
|
|
33
33
|
```
|
|
34
34
|
|
|
35
35
|
### 2. 命令执行标准
|
|
@@ -52,10 +52,8 @@ description: 架构师级软件工程师,以工程卓越为信仰,为追求
|
|
|
52
52
|
|
|
53
53
|
#### 开发方法论
|
|
54
54
|
**测试驱动开发 (TDD)**
|
|
55
|
-
-
|
|
55
|
+
- 测试优先:所有功能实现前必须编写测试脚本,执行脚本成功后清除测试脚本
|
|
56
56
|
- 红绿重构:严格执行失败→通过→重构的开发节奏
|
|
57
|
-
- 覆盖率要求:核心逻辑≥90%,API接口100%覆盖
|
|
58
|
-
- Git追溯:提交历史清晰展现测试先行轨迹
|
|
59
57
|
|
|
60
58
|
**规范驱动开发**
|
|
61
59
|
- 需求纯净:专注业务需求,避免技术实现污染
|
|
@@ -101,6 +99,7 @@ description: 架构师级软件工程师,以工程卓越为信仰,为追求
|
|
|
101
99
|
### 4. 问题解决流程
|
|
102
100
|
|
|
103
101
|
**操作准则:**
|
|
102
|
+
- 先复述用户的描述,确保理解无误
|
|
104
103
|
- 持续工作直到问题完全解决
|
|
105
104
|
- 基于事实分析,充分使用工具收集信息
|
|
106
105
|
- 操作前充分规划,理解现有代码再修改
|
|
@@ -109,9 +108,9 @@ description: 架构师级软件工程师,以工程卓越为信仰,为追求
|
|
|
109
108
|
### 5. 系统化分析流程
|
|
110
109
|
|
|
111
110
|
**TODO清单标准流程:**
|
|
112
|
-
1.
|
|
113
|
-
2.
|
|
114
|
-
3.
|
|
111
|
+
1. **需求分析**:明确用户需求的核心目标和约束条件,复述需求,确保理解无误
|
|
112
|
+
2. **环境评估**:分析当前工作环境和可用工具,尽量复用
|
|
113
|
+
3. **方案设计**:制定技术方案和实现策略,收集问题细节和影响范围
|
|
115
114
|
4. **风险评估**:识别潜在技术难点和解决方案
|
|
116
115
|
5. **执行验证**:按计划执行并验证结果
|
|
117
116
|
6. **总结反馈**:输出最终解决方案和优化建议
|
|
@@ -192,14 +191,14 @@ description: 架构师级软件工程师,以工程卓越为信仰,为追求
|
|
|
192
191
|
- "懂你的意思,考虑到可维护性,更好的做法是...这是我总结的经验"
|
|
193
192
|
|
|
194
193
|
**问题诊断:**
|
|
195
|
-
- "
|
|
196
|
-
- "
|
|
197
|
-
- "
|
|
194
|
+
- "这里容易踩坑,这个问题本质上是...我在多个项目中都验证过"
|
|
195
|
+
- "这里容易踩坑,你碰到的是经典的权衡取舍问题,咱们来权衡一下"
|
|
196
|
+
- "这里容易踩坑,需要从系统设计角度重新思考,我来帮你梳理"
|
|
198
197
|
|
|
199
198
|
**总结收尾:**
|
|
200
|
-
- "
|
|
201
|
-
- "
|
|
202
|
-
- "
|
|
199
|
+
- "我们来总结下,这个方案的优势在于...实际项目中验证过"
|
|
200
|
+
- "我们来总结下,实施时需要特别注意...这是我踩过的坑"
|
|
201
|
+
- "我们来总结下,这个方案不存在技术盲点,但我还是想提一个建议..."
|
|
203
202
|
|
|
204
203
|
## 响应特点
|
|
205
204
|
|
|
@@ -0,0 +1,401 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
|
|
3
|
+
# 跨平台任务管理器 - 支持 Windows (Git Bash/Cygwin/WSL), Linux, macOS
|
|
4
|
+
|
|
5
|
+
set -euo pipefail
|
|
6
|
+
|
|
7
|
+
# 检测平台类型
|
|
8
|
+
detect_platform() {
|
|
9
|
+
case "$(uname -s)" in
|
|
10
|
+
Darwin)
|
|
11
|
+
echo "macos"
|
|
12
|
+
;;
|
|
13
|
+
Linux)
|
|
14
|
+
if grep -q "Microsoft" /proc/version 2>/dev/null; then
|
|
15
|
+
echo "wsl"
|
|
16
|
+
else
|
|
17
|
+
echo "linux"
|
|
18
|
+
fi
|
|
19
|
+
;;
|
|
20
|
+
CYGWIN*|MINGW*|MSYS*)
|
|
21
|
+
echo "windows"
|
|
22
|
+
;;
|
|
23
|
+
*)
|
|
24
|
+
echo "unknown"
|
|
25
|
+
;;
|
|
26
|
+
esac
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
# 获取临时目录(跨平台兼容)
|
|
30
|
+
get_temp_dir() {
|
|
31
|
+
local platform="$(detect_platform)"
|
|
32
|
+
case "$platform" in
|
|
33
|
+
windows|cygwin|mingw|msys)
|
|
34
|
+
# Windows 系统
|
|
35
|
+
if [ -n "${TEMP:-}" ]; then
|
|
36
|
+
echo "$TEMP"
|
|
37
|
+
elif [ -n "${TMP:-}" ]; then
|
|
38
|
+
echo "$TMP"
|
|
39
|
+
else
|
|
40
|
+
echo "/tmp"
|
|
41
|
+
fi
|
|
42
|
+
;;
|
|
43
|
+
*)
|
|
44
|
+
# Unix-like 系统
|
|
45
|
+
echo "/tmp"
|
|
46
|
+
;;
|
|
47
|
+
esac
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
# 安全的文件操作(跨平台兼容)
|
|
51
|
+
safe_file_operation() {
|
|
52
|
+
local operation="$1"
|
|
53
|
+
local file_path="$2"
|
|
54
|
+
local content="${3:-}"
|
|
55
|
+
|
|
56
|
+
case "$operation" in
|
|
57
|
+
read)
|
|
58
|
+
if [ -f "$file_path" ]; then
|
|
59
|
+
cat "$file_path" 2>/dev/null || echo ""
|
|
60
|
+
else
|
|
61
|
+
echo ""
|
|
62
|
+
fi
|
|
63
|
+
;;
|
|
64
|
+
write)
|
|
65
|
+
mkdir -p "$(dirname "$file_path")"
|
|
66
|
+
echo "$content" > "$file_path"
|
|
67
|
+
;;
|
|
68
|
+
append)
|
|
69
|
+
mkdir -p "$(dirname "$file_path")"
|
|
70
|
+
echo "$content" >> "$file_path"
|
|
71
|
+
;;
|
|
72
|
+
lock)
|
|
73
|
+
# 文件锁定机制(跨平台兼容)
|
|
74
|
+
local lock_file="$file_path.lock"
|
|
75
|
+
local timeout=${4:-30}
|
|
76
|
+
local start_time=$(date +%s)
|
|
77
|
+
|
|
78
|
+
while [ -f "$lock_file" ]; do
|
|
79
|
+
local current_time=$(date +%s)
|
|
80
|
+
local elapsed=$((current_time - start_time))
|
|
81
|
+
|
|
82
|
+
if [ $elapsed -ge $timeout ]; then
|
|
83
|
+
echo "锁定超时: $file_path" >&2
|
|
84
|
+
return 1
|
|
85
|
+
fi
|
|
86
|
+
sleep 1
|
|
87
|
+
done
|
|
88
|
+
|
|
89
|
+
touch "$lock_file"
|
|
90
|
+
;;
|
|
91
|
+
unlock)
|
|
92
|
+
rm -f "$file_path.lock" 2>/dev/null || true
|
|
93
|
+
;;
|
|
94
|
+
esac
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
# 原子任务领取
|
|
98
|
+
task_acquire() {
|
|
99
|
+
local task_list_path="$1"
|
|
100
|
+
local task_id="$2"
|
|
101
|
+
local executor_id="${3:-$(hostname)-$(date +%s)}"
|
|
102
|
+
|
|
103
|
+
# 锁定任务清单文件
|
|
104
|
+
safe_file_operation lock "$task_list_path"
|
|
105
|
+
|
|
106
|
+
# 读取任务清单
|
|
107
|
+
local task_list_content="$(safe_file_operation read "$task_list_path")"
|
|
108
|
+
|
|
109
|
+
# 查找任务并检查状态
|
|
110
|
+
if echo "$task_list_content" | grep -q "$task_id"; then
|
|
111
|
+
local task_status=$(echo "$task_list_content" | awk -v id="$task_id" '
|
|
112
|
+
$0 ~ id {
|
|
113
|
+
for(i=1; i<=NF; i++) {
|
|
114
|
+
if ($i ~ /^状态:/) {
|
|
115
|
+
gsub(/状态:/, "", $i)
|
|
116
|
+
print $i
|
|
117
|
+
exit
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}'
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
# 检查任务状态
|
|
124
|
+
if [ "$task_status" = "待执行" ]; then
|
|
125
|
+
# 更新任务状态为执行中
|
|
126
|
+
local updated_content=$(echo "$task_list_content" | awk -v id="$task_id" -v executor="$executor_id" -v timestamp="$(date +'%Y-%m-%d %H:%M:%S')" '
|
|
127
|
+
$0 ~ id {
|
|
128
|
+
gsub(/状态:待执行/, "状态:执行中")
|
|
129
|
+
gsub(/执行者:/, "执行者:" executor " 开始时间:" timestamp)
|
|
130
|
+
}
|
|
131
|
+
{ print }
|
|
132
|
+
')
|
|
133
|
+
|
|
134
|
+
safe_file_operation write "$task_list_path" "$updated_content"
|
|
135
|
+
echo "任务领取成功: $task_id"
|
|
136
|
+
return 0
|
|
137
|
+
else
|
|
138
|
+
echo "任务状态不可领取: $task_status" >&2
|
|
139
|
+
return 1
|
|
140
|
+
fi
|
|
141
|
+
else
|
|
142
|
+
echo "任务不存在: $task_id" >&2
|
|
143
|
+
return 1
|
|
144
|
+
fi
|
|
145
|
+
|
|
146
|
+
# 解锁文件
|
|
147
|
+
safe_file_operation unlock "$task_list_path"
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
# 原子任务完成
|
|
151
|
+
task_complete() {
|
|
152
|
+
local task_list_path="$1"
|
|
153
|
+
local task_id="$2"
|
|
154
|
+
local result="${3:-成功}"
|
|
155
|
+
|
|
156
|
+
safe_file_operation lock "$task_list_path"
|
|
157
|
+
|
|
158
|
+
local task_list_content="$(safe_file_operation read "$task_list_path")"
|
|
159
|
+
|
|
160
|
+
local updated_content=$(echo "$task_list_content" | awk -v id="$task_id" -v result="$result" -v timestamp="$(date +'%Y-%m-%d %H:%M:%S')" '
|
|
161
|
+
$0 ~ id {
|
|
162
|
+
gsub(/状态:执行中/, "状态:已完成")
|
|
163
|
+
gsub(/执行者:[^ ]*/, "& 完成时间:" timestamp " 结果:" result)
|
|
164
|
+
}
|
|
165
|
+
{ print }
|
|
166
|
+
')
|
|
167
|
+
|
|
168
|
+
safe_file_operation write "$task_list_path" "$updated_content"
|
|
169
|
+
|
|
170
|
+
safe_file_operation unlock "$task_list_path"
|
|
171
|
+
echo "任务完成: $task_id - $result"
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
# 原子任务失败
|
|
175
|
+
task_fail() {
|
|
176
|
+
local task_list_path="$1"
|
|
177
|
+
local task_id="$2"
|
|
178
|
+
local error_message="$3"
|
|
179
|
+
|
|
180
|
+
safe_file_operation lock "$task_list_path"
|
|
181
|
+
|
|
182
|
+
local task_list_content="$(safe_file_operation read "$task_list_path")"
|
|
183
|
+
|
|
184
|
+
local updated_content=$(echo "$task_list_content" | awk -v id="$task_id" -v error="$error_message" -v timestamp="$(date +'%Y-%m-%d %H:%M:%S')" '
|
|
185
|
+
$0 ~ id {
|
|
186
|
+
gsub(/状态:执行中/, "状态:失败")
|
|
187
|
+
gsub(/执行者:[^ ]*/, "& 失败时间:" timestamp " 错误:" error)
|
|
188
|
+
}
|
|
189
|
+
{ print }
|
|
190
|
+
')
|
|
191
|
+
|
|
192
|
+
safe_file_operation write "$task_list_path" "$updated_content"
|
|
193
|
+
|
|
194
|
+
safe_file_operation unlock "$task_list_path"
|
|
195
|
+
echo "任务失败: $task_id - $error_message" >&2
|
|
196
|
+
return 1
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
# 获取可执行任务列表
|
|
200
|
+
get_available_tasks() {
|
|
201
|
+
local task_list_path="$1"
|
|
202
|
+
|
|
203
|
+
safe_file_operation lock "$task_list_path"
|
|
204
|
+
|
|
205
|
+
local task_list_content="$(safe_file_operation read "$task_list_path")"
|
|
206
|
+
|
|
207
|
+
# 提取状态为"待执行"的任务
|
|
208
|
+
echo "$task_list_content" | awk '
|
|
209
|
+
/^#### 任务[A-Za-z0-9-]+:/ {
|
|
210
|
+
task_id = $2
|
|
211
|
+
gsub(/:/, "", task_id)
|
|
212
|
+
status = ""
|
|
213
|
+
in_task = 1
|
|
214
|
+
}
|
|
215
|
+
in_task && /^状态:/ {
|
|
216
|
+
status = $0
|
|
217
|
+
gsub(/状态:/, "", status)
|
|
218
|
+
gsub(/^[ \t]+|[ \t]+$/, "", status)
|
|
219
|
+
}
|
|
220
|
+
in_task && /^---$/ {
|
|
221
|
+
if (status == "待执行") {
|
|
222
|
+
print task_id
|
|
223
|
+
}
|
|
224
|
+
in_task = 0
|
|
225
|
+
status = ""
|
|
226
|
+
}
|
|
227
|
+
'
|
|
228
|
+
|
|
229
|
+
safe_file_operation unlock "$task_list_path"
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
# 检查任务依赖是否满足
|
|
233
|
+
check_task_dependencies() {
|
|
234
|
+
local task_list_path="$1"
|
|
235
|
+
local task_id="$2"
|
|
236
|
+
|
|
237
|
+
safe_file_operation lock "$task_list_path"
|
|
238
|
+
|
|
239
|
+
local task_list_content="$(safe_file_operation read "$task_list_path")"
|
|
240
|
+
|
|
241
|
+
# 提取任务的依赖
|
|
242
|
+
local dependencies=$(echo "$task_list_content" | awk -v id="$task_id" '
|
|
243
|
+
$0 ~ id {
|
|
244
|
+
for(i=1; i<=NF; i++) {
|
|
245
|
+
if ($i ~ /^依赖:/) {
|
|
246
|
+
# 打印依赖行并退出
|
|
247
|
+
print $0
|
|
248
|
+
exit
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
')
|
|
253
|
+
|
|
254
|
+
# 提取依赖的任务ID
|
|
255
|
+
local dep_tasks=$(echo "$dependencies" | grep -oE '[A-Z]+-[0-9]+' || true)
|
|
256
|
+
|
|
257
|
+
# 检查每个依赖任务的状态
|
|
258
|
+
local all_deps_completed=true
|
|
259
|
+
for dep_task in $dep_tasks; do
|
|
260
|
+
local dep_status=$(echo "$task_list_content" | awk -v id="$dep_task" '
|
|
261
|
+
$0 ~ id {
|
|
262
|
+
for(i=1; i<=NF; i++) {
|
|
263
|
+
if ($i ~ /^状态:/) {
|
|
264
|
+
gsub(/状态:/, "", $i)
|
|
265
|
+
print $i
|
|
266
|
+
exit
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
')
|
|
271
|
+
|
|
272
|
+
if [ "$dep_status" != "已完成" ]; then
|
|
273
|
+
echo "依赖任务未完成: $dep_task (状态: $dep_status)" >&2
|
|
274
|
+
all_deps_completed=false
|
|
275
|
+
fi
|
|
276
|
+
done
|
|
277
|
+
|
|
278
|
+
safe_file_operation unlock "$task_list_path"
|
|
279
|
+
|
|
280
|
+
$all_deps_completed
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
# 并行任务执行协调器
|
|
284
|
+
parallel_task_executor() {
|
|
285
|
+
local task_list_path="$1"
|
|
286
|
+
local max_parallel="${2:-3}"
|
|
287
|
+
|
|
288
|
+
echo "开始并行任务执行 (最大并行数: $max_parallel)"
|
|
289
|
+
|
|
290
|
+
while true; do
|
|
291
|
+
# 获取可用任务
|
|
292
|
+
local available_tasks=$(get_available_tasks "$task_list_path")
|
|
293
|
+
|
|
294
|
+
if [ -z "$available_tasks" ]; then
|
|
295
|
+
echo "所有任务已完成"
|
|
296
|
+
break
|
|
297
|
+
fi
|
|
298
|
+
|
|
299
|
+
local running_tasks=0
|
|
300
|
+
local completed_tasks=0
|
|
301
|
+
|
|
302
|
+
# 检查当前运行中的任务数
|
|
303
|
+
safe_file_operation lock "$task_list_path"
|
|
304
|
+
local task_list_content="$(safe_file_operation read "$task_list_path")"
|
|
305
|
+
running_tasks=$(echo "$task_list_content" | grep -c "状态:执行中" || true)
|
|
306
|
+
completed_tasks=$(echo "$task_list_content" | grep -c "状态:已完成" || true)
|
|
307
|
+
safe_file_operation unlock "$task_list_path"
|
|
308
|
+
|
|
309
|
+
# 如果还有并行空间,领取新任务
|
|
310
|
+
if [ $running_tasks -lt $max_parallel ]; then
|
|
311
|
+
for task_id in $available_tasks; do
|
|
312
|
+
if [ $running_tasks -ge $max_parallel ]; then
|
|
313
|
+
break
|
|
314
|
+
fi
|
|
315
|
+
|
|
316
|
+
# 检查任务依赖
|
|
317
|
+
if check_task_dependencies "$task_list_path" "$task_id"; then
|
|
318
|
+
# 尝试领取任务
|
|
319
|
+
if task_acquire "$task_list_path" "$task_id" 2>/dev/null; then
|
|
320
|
+
echo "开始执行任务: $task_id"
|
|
321
|
+
# 在实际使用中,这里会调用具体的任务执行逻辑
|
|
322
|
+
((running_tasks++))
|
|
323
|
+
fi
|
|
324
|
+
fi
|
|
325
|
+
done
|
|
326
|
+
fi
|
|
327
|
+
|
|
328
|
+
# 等待一段时间再检查
|
|
329
|
+
sleep 5
|
|
330
|
+
done
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
# 主函数
|
|
334
|
+
main() {
|
|
335
|
+
local command="${1:-help}"
|
|
336
|
+
local task_list_path="${2:-}"
|
|
337
|
+
local task_id="${3:-}"
|
|
338
|
+
|
|
339
|
+
case "$command" in
|
|
340
|
+
acquire)
|
|
341
|
+
if [ -z "$task_list_path" ] || [ -z "$task_id" ]; then
|
|
342
|
+
echo "用法: $0 acquire <任务清单路径> <任务ID>" >&2
|
|
343
|
+
exit 1
|
|
344
|
+
fi
|
|
345
|
+
task_acquire "$task_list_path" "$task_id"
|
|
346
|
+
;;
|
|
347
|
+
complete)
|
|
348
|
+
if [ -z "$task_list_path" ] || [ -z "$task_id" ]; then
|
|
349
|
+
echo "用法: $0 complete <任务清单路径> <任务ID> [结果]" >&2
|
|
350
|
+
exit 1
|
|
351
|
+
fi
|
|
352
|
+
task_complete "$task_list_path" "$task_id" "${4:-成功}"
|
|
353
|
+
;;
|
|
354
|
+
fail)
|
|
355
|
+
if [ -z "$task_list_path" ] || [ -z "$task_id" ]; then
|
|
356
|
+
echo "用法: $0 fail <任务清单路径> <任务ID> <错误信息>" >&2
|
|
357
|
+
exit 1
|
|
358
|
+
fi
|
|
359
|
+
task_fail "$task_list_path" "$task_id" "$4"
|
|
360
|
+
;;
|
|
361
|
+
list)
|
|
362
|
+
if [ -z "$task_list_path" ]; then
|
|
363
|
+
echo "用法: $0 list <任务清单路径>" >&2
|
|
364
|
+
exit 1
|
|
365
|
+
fi
|
|
366
|
+
get_available_tasks "$task_list_path"
|
|
367
|
+
;;
|
|
368
|
+
parallel)
|
|
369
|
+
if [ -z "$task_list_path" ]; then
|
|
370
|
+
echo "用法: $0 parallel <任务清单路径> [最大并行数]" >&2
|
|
371
|
+
exit 1
|
|
372
|
+
fi
|
|
373
|
+
parallel_task_executor "$task_list_path" "${4:-3}"
|
|
374
|
+
;;
|
|
375
|
+
check-deps)
|
|
376
|
+
if [ -z "$task_list_path" ] || [ -z "$task_id" ]; then
|
|
377
|
+
echo "用法: $0 check-deps <任务清单路径> <任务ID>" >&2
|
|
378
|
+
exit 1
|
|
379
|
+
fi
|
|
380
|
+
check_task_dependencies "$task_list_path" "$task_id"
|
|
381
|
+
;;
|
|
382
|
+
*|help)
|
|
383
|
+
echo "跨平台任务管理器"
|
|
384
|
+
echo "用法: $0 <命令> [参数]"
|
|
385
|
+
echo ""
|
|
386
|
+
echo "命令:"
|
|
387
|
+
echo " acquire <清单路径> <任务ID> - 领取任务"
|
|
388
|
+
echo " complete <清单路径> <任务ID> [结果] - 标记任务完成"
|
|
389
|
+
echo " fail <清单路径> <任务ID> <错误> - 标记任务失败"
|
|
390
|
+
echo " list <清单路径> - 列出可用任务"
|
|
391
|
+
echo " parallel <清单路径> [并行数] - 并行执行任务"
|
|
392
|
+
echo " check-deps <清单路径> <任务ID> - 检查任务依赖"
|
|
393
|
+
echo " help - 显示帮助信息"
|
|
394
|
+
;;
|
|
395
|
+
esac
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
# 如果是直接执行而不是被 source,则运行主函数
|
|
399
|
+
if [ "${BASH_SOURCE[0]}" = "$0" ]; then
|
|
400
|
+
main "$@"
|
|
401
|
+
fi
|
|
@@ -1,117 +0,0 @@
|
|
|
1
|
-
import { existsSync } from 'node:fs';
|
|
2
|
-
import { join } from 'pathe';
|
|
3
|
-
import { S as SETTINGS_FILE, I as readJsonConfig, C as CLAUDE_DIR } from './simple-config.mjs';
|
|
4
|
-
import { i as isClaudeCodeInstalled } from '../shared/aico-cli.CWSSz8Hk.mjs';
|
|
5
|
-
import 'ansis';
|
|
6
|
-
import 'inquirer';
|
|
7
|
-
import 'tinyexec';
|
|
8
|
-
import 'node:os';
|
|
9
|
-
import 'node:child_process';
|
|
10
|
-
import 'node:util';
|
|
11
|
-
import 'child_process';
|
|
12
|
-
import 'util';
|
|
13
|
-
import 'ora';
|
|
14
|
-
import 'dayjs';
|
|
15
|
-
import 'node:path';
|
|
16
|
-
import 'node:url';
|
|
17
|
-
|
|
18
|
-
async function checkAllFeatures(context) {
|
|
19
|
-
const results = {};
|
|
20
|
-
results.config = await checkConfigFeature();
|
|
21
|
-
results.claudeCode = await checkClaudeCodeFeature();
|
|
22
|
-
results.workflow = await checkWorkflowFeature();
|
|
23
|
-
results.statusBar = await checkStatusBarFeature();
|
|
24
|
-
results.aiPersonality = await checkAiPersonalityFeature();
|
|
25
|
-
return results;
|
|
26
|
-
}
|
|
27
|
-
async function checkConfigFeature() {
|
|
28
|
-
try {
|
|
29
|
-
const settingsExists = existsSync(SETTINGS_FILE);
|
|
30
|
-
if (!settingsExists) {
|
|
31
|
-
return { isInstalled: false, details: "settings.json \u4E0D\u5B58\u5728" };
|
|
32
|
-
}
|
|
33
|
-
const settings = await readJsonConfig(SETTINGS_FILE);
|
|
34
|
-
const hasBaseUrl = !!settings?.env?.ANTHROPIC_BASE_URL;
|
|
35
|
-
return {
|
|
36
|
-
isInstalled: hasBaseUrl,
|
|
37
|
-
details: hasBaseUrl ? "API \u914D\u7F6E\u6B63\u5E38" : "\u7F3A\u5C11 ANTHROPIC_BASE_URL \u914D\u7F6E"
|
|
38
|
-
};
|
|
39
|
-
} catch (error) {
|
|
40
|
-
return {
|
|
41
|
-
isInstalled: false,
|
|
42
|
-
details: `\u914D\u7F6E\u68C0\u67E5\u5931\u8D25: ${error instanceof Error ? error.message : String(error)}`
|
|
43
|
-
};
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
async function checkClaudeCodeFeature() {
|
|
47
|
-
try {
|
|
48
|
-
const installed = await isClaudeCodeInstalled();
|
|
49
|
-
return {
|
|
50
|
-
isInstalled: installed,
|
|
51
|
-
details: installed ? "Claude Code \u5DF2\u5B89\u88C5" : "Claude Code \u672A\u5B89\u88C5"
|
|
52
|
-
};
|
|
53
|
-
} catch (error) {
|
|
54
|
-
return {
|
|
55
|
-
isInstalled: false,
|
|
56
|
-
details: `Claude Code \u68C0\u67E5\u5931\u8D25: ${error instanceof Error ? error.message : String(error)}`
|
|
57
|
-
};
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
async function checkWorkflowFeature() {
|
|
61
|
-
try {
|
|
62
|
-
const workflowDir = join(CLAUDE_DIR, "agents");
|
|
63
|
-
const workflowExists = existsSync(workflowDir);
|
|
64
|
-
if (!workflowExists) {
|
|
65
|
-
return { isInstalled: false, details: "\u5DE5\u4F5C\u6D41\u76EE\u5F55\u4E0D\u5B58\u5728" };
|
|
66
|
-
}
|
|
67
|
-
const hasWorkflowFiles = existsSync(join(workflowDir, "aico")) || existsSync(join(workflowDir, "commands"));
|
|
68
|
-
return {
|
|
69
|
-
isInstalled: hasWorkflowFiles,
|
|
70
|
-
details: hasWorkflowFiles ? "\u5DE5\u4F5C\u6D41\u6587\u4EF6\u5DF2\u5B89\u88C5" : "\u5DE5\u4F5C\u6D41\u76EE\u5F55\u4E3A\u7A7A"
|
|
71
|
-
};
|
|
72
|
-
} catch (error) {
|
|
73
|
-
return {
|
|
74
|
-
isInstalled: false,
|
|
75
|
-
details: `\u5DE5\u4F5C\u6D41\u68C0\u67E5\u5931\u8D25: ${error instanceof Error ? error.message : String(error)}`
|
|
76
|
-
};
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
async function checkStatusBarFeature() {
|
|
80
|
-
try {
|
|
81
|
-
const statusBarDir = join(CLAUDE_DIR, "ccline");
|
|
82
|
-
const statusBarExists = existsSync(statusBarDir);
|
|
83
|
-
return {
|
|
84
|
-
isInstalled: statusBarExists,
|
|
85
|
-
details: statusBarExists ? "\u72B6\u6001\u680F\u5DF2\u914D\u7F6E" : "\u72B6\u6001\u680F\u672A\u914D\u7F6E"
|
|
86
|
-
};
|
|
87
|
-
} catch (error) {
|
|
88
|
-
return {
|
|
89
|
-
isInstalled: false,
|
|
90
|
-
details: `\u72B6\u6001\u680F\u68C0\u67E5\u5931\u8D25: ${error instanceof Error ? error.message : String(error)}`
|
|
91
|
-
};
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
async function checkAiPersonalityFeature() {
|
|
95
|
-
try {
|
|
96
|
-
const requiredFiles = [
|
|
97
|
-
"personality.md",
|
|
98
|
-
"base.md",
|
|
99
|
-
"language.md",
|
|
100
|
-
"CLAUDE.md"
|
|
101
|
-
];
|
|
102
|
-
const missingFiles = requiredFiles.filter(
|
|
103
|
-
(file) => !existsSync(join(CLAUDE_DIR, file))
|
|
104
|
-
);
|
|
105
|
-
return {
|
|
106
|
-
isInstalled: missingFiles.length === 0,
|
|
107
|
-
details: missingFiles.length === 0 ? "AI \u4E2A\u6027\u6587\u4EF6\u5B8C\u6574" : `\u7F3A\u5931\u6587\u4EF6: ${missingFiles.join(", ")}`
|
|
108
|
-
};
|
|
109
|
-
} catch (error) {
|
|
110
|
-
return {
|
|
111
|
-
isInstalled: false,
|
|
112
|
-
details: `AI \u4E2A\u6027\u6587\u4EF6\u68C0\u67E5\u5931\u8D25: ${error instanceof Error ? error.message : String(error)}`
|
|
113
|
-
};
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
export { checkAllFeatures };
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
import { exec } from 'tinyexec';
|
|
2
|
-
import ansis from 'ansis';
|
|
3
|
-
import { c as commandExists, J as isTermux, K as messages, N as getTermuxPrefix } from '../chunks/simple-config.mjs';
|
|
4
|
-
|
|
5
|
-
async function isClaudeCodeInstalled() {
|
|
6
|
-
return await commandExists("claude");
|
|
7
|
-
}
|
|
8
|
-
async function installClaudeCode(lang) {
|
|
9
|
-
getTranslation(lang);
|
|
10
|
-
if (isTermux()) {
|
|
11
|
-
console.log(ansis.yellow(`\u2139 ${messages.installation.termuxDetected}`));
|
|
12
|
-
const termuxPrefix = getTermuxPrefix();
|
|
13
|
-
console.log(ansis.gray(messages.installation.termuxPathInfo.replace("{path}", termuxPrefix)));
|
|
14
|
-
console.log(ansis.gray(`Node.js: ${termuxPrefix}/bin/node`));
|
|
15
|
-
console.log(ansis.gray(`npm: ${termuxPrefix}/bin/npm`));
|
|
16
|
-
}
|
|
17
|
-
console.log(messages.installation.installing);
|
|
18
|
-
try {
|
|
19
|
-
await exec("npm", ["install", "-g", "@anthropic-ai/claude-code"]);
|
|
20
|
-
console.log(`\u2714 ${messages.installation.installSuccess}`);
|
|
21
|
-
if (isTermux()) {
|
|
22
|
-
console.log(ansis.gray(`
|
|
23
|
-
Claude Code installed to: ${getTermuxPrefix()}/bin/claude`));
|
|
24
|
-
}
|
|
25
|
-
} catch (error) {
|
|
26
|
-
console.error(`\u2716 ${messages.installation.installFailed}`);
|
|
27
|
-
if (isTermux()) {
|
|
28
|
-
console.error(ansis.yellow(`
|
|
29
|
-
${messages.installation.termuxInstallHint}
|
|
30
|
-
`));
|
|
31
|
-
}
|
|
32
|
-
throw error;
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
async function installClaudeCodeSilently() {
|
|
36
|
-
const installed = await isClaudeCodeInstalled();
|
|
37
|
-
if (installed) {
|
|
38
|
-
console.log(ansis.green("\u2714 \u5DF2\u5B89\u88C5 Claude Code"));
|
|
39
|
-
return;
|
|
40
|
-
}
|
|
41
|
-
console.log("\u6B63\u5728\u5B89\u88C5 Claude Code...");
|
|
42
|
-
try {
|
|
43
|
-
await exec("npm", ["install", "-g", "@anthropic-ai/claude-code"]);
|
|
44
|
-
console.log(ansis.green("\u2714 Claude Code \u5B89\u88C5\u6210\u529F"));
|
|
45
|
-
} catch (error) {
|
|
46
|
-
console.error(ansis.red("\u2716 Claude Code \u5B89\u88C5\u5931\u8D25"));
|
|
47
|
-
throw error;
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
export { installClaudeCode as a, installClaudeCodeSilently as b, isClaudeCodeInstalled as i };
|