@spaceflow/core 0.8.0 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (86) hide show
  1. package/dist/index.js +5589 -970
  2. package/dist/index.js.map +1 -1
  3. package/package.json +9 -3
  4. package/src/cli-runtime/di/config.ts +157 -0
  5. package/src/cli-runtime/di/container.ts +120 -0
  6. package/src/cli-runtime/di/index.ts +3 -0
  7. package/src/cli-runtime/di/services.ts +53 -0
  8. package/src/cli-runtime/extension-loader.ts +74 -0
  9. package/src/{shared → cli-runtime}/i18n/index.ts +7 -1
  10. package/src/cli-runtime/i18n/init.ts +117 -0
  11. package/src/cli-runtime/index.ts +131 -0
  12. package/src/cli-runtime/internal-extensions.ts +33 -0
  13. package/src/commands/build/build.service.ts +323 -0
  14. package/src/commands/build/index.ts +49 -0
  15. package/src/commands/clear/clear.service.ts +159 -0
  16. package/src/commands/clear/index.ts +35 -0
  17. package/src/commands/commit/commit.config.ts +168 -0
  18. package/src/commands/commit/commit.service.ts +950 -0
  19. package/src/commands/commit/index.ts +58 -0
  20. package/src/commands/create/create.service.ts +318 -0
  21. package/src/commands/create/index.ts +42 -0
  22. package/src/commands/dev/index.ts +30 -0
  23. package/src/commands/install/index.ts +65 -0
  24. package/src/commands/install/install.service.ts +1539 -0
  25. package/src/commands/list/index.ts +33 -0
  26. package/src/commands/list/list.service.ts +127 -0
  27. package/src/commands/mcp/index.ts +37 -0
  28. package/src/commands/mcp/mcp.service.ts +246 -0
  29. package/src/commands/runx/index.ts +47 -0
  30. package/src/commands/runx/runx.service.ts +142 -0
  31. package/src/commands/runx/runx.utils.ts +83 -0
  32. package/src/commands/schema/index.ts +30 -0
  33. package/src/commands/setup/index.ts +34 -0
  34. package/src/commands/setup/setup.service.ts +234 -0
  35. package/src/commands/uninstall/index.ts +42 -0
  36. package/src/commands/uninstall/uninstall.service.ts +166 -0
  37. package/src/commands/update/index.ts +42 -0
  38. package/src/commands/update/update.service.ts +373 -0
  39. package/src/config/index.ts +1 -30
  40. package/src/config/spaceflow.config.ts +226 -278
  41. package/src/index.ts +11 -1
  42. package/src/locales/en/build.json +22 -0
  43. package/src/locales/en/clear.json +16 -0
  44. package/src/locales/en/commit.json +45 -0
  45. package/src/locales/en/create.json +27 -0
  46. package/src/locales/en/dev.json +5 -0
  47. package/src/locales/en/install.json +71 -0
  48. package/src/locales/en/list.json +8 -0
  49. package/src/locales/en/mcp.json +19 -0
  50. package/src/locales/en/runx.json +13 -0
  51. package/src/locales/en/schema.json +4 -0
  52. package/src/locales/en/setup.json +14 -0
  53. package/src/locales/en/uninstall.json +18 -0
  54. package/src/locales/en/update.json +28 -0
  55. package/src/locales/zh-cn/build.json +22 -0
  56. package/src/locales/zh-cn/clear.json +16 -0
  57. package/src/locales/zh-cn/commit.json +45 -0
  58. package/src/locales/zh-cn/create.json +27 -0
  59. package/src/locales/zh-cn/dev.json +5 -0
  60. package/src/locales/zh-cn/install.json +71 -0
  61. package/src/locales/zh-cn/list.json +8 -0
  62. package/src/locales/zh-cn/mcp.json +19 -0
  63. package/src/locales/zh-cn/runx.json +13 -0
  64. package/src/locales/zh-cn/schema.json +4 -0
  65. package/src/locales/zh-cn/setup.json +14 -0
  66. package/src/locales/zh-cn/uninstall.json +18 -0
  67. package/src/locales/zh-cn/update.json +28 -0
  68. package/src/shared/editor-config/index.ts +2 -21
  69. package/src/shared/llm-proxy/adapters/openai.adapter.ts +3 -1
  70. package/src/shared/package-manager/index.ts +5 -76
  71. package/src/shared/source-utils/index.ts +12 -130
  72. package/src/shared/spaceflow-dir/index.ts +13 -135
  73. package/src/shared/verbose/index.ts +10 -87
  74. package/dist/524.js +0 -9
  75. package/src/config/ci.config.ts +0 -29
  76. package/src/config/config-loader.ts +0 -100
  77. package/src/config/config-reader.service.ts +0 -128
  78. package/src/config/config-reader.ts +0 -75
  79. package/src/config/feishu.config.ts +0 -35
  80. package/src/config/git-provider.config.ts +0 -29
  81. package/src/config/llm.config.ts +0 -110
  82. package/src/config/load-env.ts +0 -15
  83. package/src/config/storage.config.ts +0 -33
  84. /package/src/{shared → cli-runtime}/i18n/i18n.spec.ts +0 -0
  85. /package/src/{shared → cli-runtime}/i18n/i18n.ts +0 -0
  86. /package/src/{shared → cli-runtime}/i18n/locale-detect.ts +0 -0
@@ -1,316 +1,256 @@
1
- import { readFileSync, existsSync, writeFileSync } from "fs";
2
- import { join } from "path";
3
- import { homedir } from "os";
4
- import stringify from "json-stringify-pretty-compact";
5
1
  import { z } from "zod";
6
2
  import type { LLMMode } from "../shared/llm-proxy";
7
3
  import { registerPluginSchema } from "./schema-generator.service";
4
+ import { detectProvider } from "../shared/git-provider/detect-provider";
5
+
6
+ // 从 @spaceflow/shared 重导出配置工具函数
7
+ export {
8
+ DEFAULT_SUPPORT_EDITOR,
9
+ CONFIG_FILE_NAME,
10
+ RC_FILE_NAME,
11
+ deepMerge,
12
+ getConfigPath,
13
+ getConfigPaths,
14
+ getEnvFilePaths,
15
+ readConfigSync,
16
+ writeConfigSync,
17
+ getSupportedEditors,
18
+ getDependencies,
19
+ findConfigFileWithField,
20
+ updateDependency,
21
+ removeDependency,
22
+ } from "@spaceflow/shared";
23
+
24
+ import { DEFAULT_SUPPORT_EDITOR, readConfigSync } from "@spaceflow/shared";
25
+
26
+ // ============ 子模块配置 Schema ============
27
+
28
+ /** 从环境自动检测的默认值 */
29
+ const detected = detectProvider();
30
+
31
+ /** Git Provider 配置 Schema */
32
+ const GitProviderConfigSchema = z.object({
33
+ provider: z
34
+ .enum(["gitea", "github", "gitlab"])
35
+ .default(detected.provider)
36
+ .describe("Git Provider 类型 (github | gitea | gitlab),未指定时自动检测"),
37
+ serverUrl: z.string().default(detected.serverUrl).describe("Git Provider 服务器 URL"),
38
+ token: z.string().default(detected.token).describe("Git Provider API Token"),
39
+ });
40
+
41
+ /** CI 配置 Schema */
42
+ const CiConfigSchema = z.object({
43
+ repository: z
44
+ .string()
45
+ .default(process.env.GITHUB_REPOSITORY || "")
46
+ .describe("仓库名称 (owner/repo 格式)"),
47
+ refName: z
48
+ .string()
49
+ .default(process.env.GITHUB_REF_NAME || "")
50
+ .describe("当前分支名称"),
51
+ actor: z
52
+ .string()
53
+ .default(process.env.GITHUB_ACTOR || "")
54
+ .describe("当前操作者"),
55
+ });
56
+
57
+ /** Claude Code 适配器配置 Schema */
58
+ const ClaudeCodeConfigSchema = z.object({
59
+ baseUrl: z
60
+ .string()
61
+ .default(process.env.CLAUDE_CODE_BASE_URL || "")
62
+ .describe("API 基础 URL"),
63
+ authToken: z
64
+ .string()
65
+ .default(process.env.CLAUDE_CODE_AUTH_TOKEN || "")
66
+ .describe("认证令牌"),
67
+ model: z
68
+ .string()
69
+ .default(process.env.CLAUDE_CODE_MODEL || "claude-sonnet-4-5")
70
+ .describe("模型名称"),
71
+ hasCompletedOnboarding: z.boolean().optional().describe("是否已完成 Claude Code 引导流程"),
72
+ });
73
+
74
+ /** OpenAI 适配器配置 Schema */
75
+ const OpenAIConfigSchema = z.object({
76
+ baseUrl: z
77
+ .string()
78
+ .default(process.env.OPENAI_BASE_URL || "")
79
+ .describe("API 基础 URL"),
80
+ apiKey: z
81
+ .string()
82
+ .default(process.env.OPENAI_API_KEY || "")
83
+ .describe("API Key"),
84
+ model: z
85
+ .string()
86
+ .default(process.env.OPENAI_MODEL || "gpt-4o")
87
+ .describe("模型名称"),
88
+ });
89
+
90
+ /** OpenCode 适配器配置 Schema */
91
+ const OpenCodeConfigSchema = z.object({
92
+ serverUrl: z
93
+ .string()
94
+ .default(process.env.OPENCODE_SERVER_URL || "http://localhost:4096")
95
+ .describe("服务器 URL"),
96
+ baseUrl: z
97
+ .string()
98
+ .default(process.env.OPENCODE_BASE_URL || "")
99
+ .describe("API 基础 URL"),
100
+ apiKey: z
101
+ .string()
102
+ .default(process.env.OPENCODE_API_KEY || "")
103
+ .describe("API Key"),
104
+ providerID: z
105
+ .string()
106
+ .default(process.env.OPENCODE_PROVIDER_ID || "openai")
107
+ .describe("Provider ID"),
108
+ model: z
109
+ .string()
110
+ .default(process.env.OPENCODE_MODEL || "")
111
+ .describe("模型名称"),
112
+ });
113
+
114
+ /** Gemini 适配器配置 Schema */
115
+ const GeminiConfigSchema = z.object({
116
+ baseUrl: z
117
+ .string()
118
+ .default(process.env.GEMINI_BASE_URL || "")
119
+ .describe("API 基础 URL"),
120
+ apiKey: z
121
+ .string()
122
+ .default(process.env.GEMINI_API_KEY || "")
123
+ .describe("API Key"),
124
+ model: z
125
+ .string()
126
+ .default(process.env.GEMINI_MODEL || "")
127
+ .describe("模型名称"),
128
+ });
129
+
130
+ /** LLM 配置 Schema */
131
+ const LlmConfigSchema = z.object({
132
+ claudeCode: z
133
+ .preprocess((v) => v ?? {}, ClaudeCodeConfigSchema)
134
+ .describe("Claude Code 适配器配置"),
135
+ openai: z.preprocess((v) => v ?? {}, OpenAIConfigSchema).describe("OpenAI 适配器配置"),
136
+ openCode: z.preprocess((v) => v ?? {}, OpenCodeConfigSchema).describe("OpenCode 适配器配置"),
137
+ gemini: z.preprocess((v) => v ?? {}, GeminiConfigSchema).describe("Gemini 适配器配置"),
138
+ });
8
139
 
9
- /** 默认编辑器 */
10
- export const DEFAULT_SUPPORT_EDITOR = "claudeCode";
140
+ /** 飞书配置 Schema */
141
+ const FeishuConfigSchema = z.object({
142
+ appId: z
143
+ .string()
144
+ .default(process.env.FEISHU_APP_ID || "")
145
+ .describe("飞书应用 ID"),
146
+ appSecret: z
147
+ .string()
148
+ .default(process.env.FEISHU_APP_SECRET || "")
149
+ .describe("飞书应用密钥"),
150
+ appType: z
151
+ .enum(["self_build", "store"])
152
+ .default((process.env.FEISHU_APP_TYPE as "self_build" | "store") || "self_build")
153
+ .describe("应用类型"),
154
+ domain: z
155
+ .enum(["feishu", "lark"])
156
+ .default((process.env.FEISHU_DOMAIN as "feishu" | "lark") || "feishu")
157
+ .describe("域名"),
158
+ });
11
159
 
12
- /** 配置文件名 */
13
- export const CONFIG_FILE_NAME = "spaceflow.json";
160
+ /** Storage 配置 Schema */
161
+ const StorageConfigSchema = z.object({
162
+ adapter: z
163
+ .enum(["memory", "file"])
164
+ .default((process.env.STORAGE_ADAPTER as "memory" | "file") || "memory")
165
+ .describe("适配器类型"),
166
+ filePath: z.string().optional().describe("文件存储路径"),
167
+ defaultTtl: z
168
+ .number()
169
+ .default(process.env.STORAGE_DEFAULT_TTL ? parseInt(process.env.STORAGE_DEFAULT_TTL, 10) : 0)
170
+ .describe("默认过期时间(毫秒)"),
171
+ maxKeys: z
172
+ .number()
173
+ .default(process.env.STORAGE_MAX_KEYS ? parseInt(process.env.STORAGE_MAX_KEYS, 10) : 0)
174
+ .describe("最大 key 数量"),
175
+ });
14
176
 
15
- /** RC 配置文件名(位于 .spaceflow 同级目录) */
16
- export const RC_FILE_NAME = ".spaceflowrc";
177
+ // ============ 统一配置 Schema ============
17
178
 
18
- /** Spaceflow 核心配置 Schema */
19
- const SpaceflowCoreConfigSchema = z.object({
179
+ /** Spaceflow 完整配置 Schema */
180
+ const SpaceflowConfigSchema = z.object({
20
181
  /** 界面语言,如 zh-CN、en */
21
182
  lang: z.string().optional().describe("界面语言,如 zh-CN、en"),
22
183
  /** 已安装的技能包注册表 */
23
184
  dependencies: z.record(z.string(), z.string()).optional().describe("已安装的技能包注册表"),
24
- /** 支持的编辑器列表,用于安装 skills 和 commands 时关联目录 */
185
+ /** 支持的编辑器列表 */
25
186
  support: z.array(z.string()).default([DEFAULT_SUPPORT_EDITOR]).describe("支持的编辑器列表"),
187
+ /** Git Provider 配置 */
188
+ gitProvider: z
189
+ .preprocess((v) => v ?? {}, GitProviderConfigSchema)
190
+ .describe("Git Provider 服务配置"),
191
+ /** CI 配置 */
192
+ ci: z.preprocess((v) => v ?? {}, CiConfigSchema).describe("CI 环境配置"),
193
+ /** LLM 配置 */
194
+ llm: z.preprocess((v) => v ?? {}, LlmConfigSchema).describe("LLM 服务配置"),
195
+ /** 飞书配置 */
196
+ feishu: z.preprocess((v) => v ?? {}, FeishuConfigSchema).describe("飞书 SDK 配置"),
197
+ /** Storage 配置 */
198
+ storage: z.preprocess((v) => v ?? {}, StorageConfigSchema).describe("存储服务配置"),
26
199
  });
27
200
 
28
- // 注册 spaceflow 核心配置 schema
201
+ // 注册完整 schema(供 JSON Schema 生成使用)
29
202
  registerPluginSchema({
30
203
  configKey: "spaceflow",
31
- schemaFactory: () => SpaceflowCoreConfigSchema,
32
- description: "Spaceflow 核心配置",
204
+ schemaFactory: () => SpaceflowConfigSchema,
205
+ description: "Spaceflow 配置",
33
206
  });
34
207
 
35
- /**
36
- * SpaceflowConfig - 通用配置
37
- * 子命令的配置由各自模块定义和管理
38
- */
39
- export type SpaceflowConfig = z.infer<typeof SpaceflowCoreConfigSchema> & {
40
- /** 子命令配置,由各子命令模块自行定义类型 */
208
+ // ============ 类型导出 ============
209
+
210
+ /** Spaceflow 完整配置类型 */
211
+ export type SpaceflowConfig = z.infer<typeof SpaceflowConfigSchema> & {
212
+ /** 扩展插件配置,由各插件自行定义类型 */
41
213
  [key: string]: unknown;
42
214
  };
43
215
 
44
- // ============ 配置文件操作工具函数 ============
45
-
46
- // 不应该被深度合并的字段,这些字段应该直接覆盖而非合并
47
- const NO_MERGE_FIELDS = ["dependencies"];
48
-
49
- /**
50
- * 深度合并对象
51
- * 后面的对象会覆盖前面的对象,数组会被替换而非合并
52
- * NO_MERGE_FIELDS 中的字段不会被深度合并,而是直接覆盖
53
- */
54
- function deepMerge<T extends Record<string, unknown>>(...objects: Partial<T>[]): Partial<T> {
55
- const result: Record<string, unknown> = {};
56
-
57
- for (const obj of objects) {
58
- for (const key in obj) {
59
- const value = obj[key];
60
- const existing = result[key];
61
-
62
- // 对于 NO_MERGE_FIELDS 中的字段,直接覆盖而非合并
63
- if (NO_MERGE_FIELDS.includes(key)) {
64
- if (value !== undefined) {
65
- result[key] = value;
66
- }
67
- } else if (
68
- value !== null &&
69
- typeof value === "object" &&
70
- !Array.isArray(value) &&
71
- existing !== null &&
72
- typeof existing === "object" &&
73
- !Array.isArray(existing)
74
- ) {
75
- result[key] = deepMerge(
76
- existing as Record<string, unknown>,
77
- value as Record<string, unknown>,
78
- );
79
- } else if (value !== undefined) {
80
- result[key] = value;
81
- }
82
- }
83
- }
84
-
85
- return result as Partial<T>;
86
- }
87
-
88
- /**
89
- * 获取主配置文件路径(用于写入)
90
- * 配置文件统一存放在 .spaceflow/ 目录下
91
- * @param cwd 工作目录,默认为 process.cwd()
92
- */
93
- export function getConfigPath(cwd?: string): string {
94
- return join(cwd || process.cwd(), ".spaceflow", CONFIG_FILE_NAME);
95
- }
96
-
97
- /**
98
- * 获取所有配置文件路径(按优先级从低到高排列)
99
- * 优先级: ~/.spaceflow/spaceflow.json < ~/.spaceflowrc < ./.spaceflow/spaceflow.json < ./.spaceflowrc
100
- * @param cwd 工作目录,默认为 process.cwd()
101
- */
102
- export function getConfigPaths(cwd?: string): string[] {
103
- const workDir = cwd || process.cwd();
104
- return [
105
- join(homedir(), ".spaceflow", CONFIG_FILE_NAME),
106
- join(homedir(), RC_FILE_NAME),
107
- join(workDir, ".spaceflow", CONFIG_FILE_NAME),
108
- join(workDir, RC_FILE_NAME),
109
- ];
110
- }
111
-
112
- /** .env 文件名 */
113
- const ENV_FILE_NAME = ".env";
114
-
115
- /**
116
- * 获取所有 .env 文件路径(按优先级从高到低排列,供 ConfigModule.envFilePath 使用)
117
- *
118
- * NestJS ConfigModule 中 envFilePath 数组靠前的优先级更高(先读到的变量不会被后面覆盖)
119
- * 因此返回顺序为从高到低:
120
- * 1. ./.env (程序启动目录,最高优先级)
121
- * 2. ./.spaceflow/.env (项目配置目录)
122
- * 3. ~/.env (全局 home 目录)
123
- * 4. ~/.spaceflow/.env (全局配置目录,最低优先级)
124
- *
125
- * @param cwd 工作目录,默认为 process.cwd()
126
- */
127
- export function getEnvFilePaths(cwd?: string): string[] {
128
- const workDir = cwd || process.cwd();
129
- return [
130
- join(workDir, ENV_FILE_NAME),
131
- join(workDir, ".spaceflow", ENV_FILE_NAME),
132
- join(homedir(), ENV_FILE_NAME),
133
- join(homedir(), ".spaceflow", ENV_FILE_NAME),
134
- ];
135
- }
136
-
137
- /**
138
- * 读取单个配置文件(同步)
139
- * @param configPath 配置文件路径
140
- */
141
- function readSingleConfigSync(configPath: string): Partial<SpaceflowConfig> {
142
- if (!existsSync(configPath)) {
143
- return {};
144
- }
145
-
146
- try {
147
- const content = readFileSync(configPath, "utf-8");
148
- return JSON.parse(content);
149
- } catch {
150
- console.warn(`警告: 无法解析配置文件 ${configPath}`);
151
- return {};
152
- }
153
- }
154
-
155
- /**
156
- * 读取配置文件(同步)
157
- * 按优先级从低到高读取并合并配置:
158
- * 1. ~/.spaceflow/spaceflow.json (全局配置,最低优先级)
159
- * 2. ~/.spaceflowrc (全局 RC 配置)
160
- * 3. ./.spaceflow/spaceflow.json (项目配置)
161
- * 4. ./.spaceflowrc (项目根目录 RC 配置,最高优先级)
162
- * @param cwd 工作目录,默认为 process.cwd()
163
- */
164
- export function readConfigSync(cwd?: string): Partial<SpaceflowConfig> {
165
- const configPaths = getConfigPaths(cwd);
166
- const configs = configPaths.map((p) => readSingleConfigSync(p));
167
- return deepMerge(...configs);
168
- }
216
+ /** Git Provider 配置类型 */
217
+ export type GitProviderConfig = z.infer<typeof GitProviderConfigSchema>;
169
218
 
170
- /**
171
- * 写入配置文件(同步)
172
- * @param config 配置对象
173
- * @param cwd 工作目录,默认为 process.cwd()
174
- */
175
- export function writeConfigSync(config: Partial<SpaceflowConfig>, cwd?: string): void {
176
- const configPath = getConfigPath(cwd);
177
- writeFileSync(configPath, stringify(config, { indent: 2 }) + "\n");
178
- }
219
+ /** CI 配置类型 */
220
+ export type CiConfig = z.infer<typeof CiConfigSchema>;
179
221
 
180
- /**
181
- * 获取支持的编辑器列表
182
- * @param cwd 工作目录,默认为 process.cwd()
183
- */
184
- export function getSupportedEditors(cwd?: string): string[] {
185
- const config = readConfigSync(cwd);
186
- return config.support || [DEFAULT_SUPPORT_EDITOR];
187
- }
222
+ /** LLM 系统配置类型 */
223
+ export type LlmConfig = z.infer<typeof LlmConfigSchema>;
188
224
 
189
- /**
190
- * 获取 dependencies
191
- * @param cwd 工作目录,默认为 process.cwd()
192
- */
193
- export function getDependencies(cwd?: string): Record<string, string> {
194
- const config = readConfigSync(cwd);
195
- return (config.dependencies as Record<string, string>) || {};
196
- }
225
+ /** Claude Code 适配器配置类型 */
226
+ export type ClaudeCodeConfig = z.infer<typeof ClaudeCodeConfigSchema>;
197
227
 
198
- /**
199
- * 找到包含指定字段的最高优先级配置文件路径
200
- * 如果没有找到,返回项目级 .spaceflowrc 路径(默认写入位置)
201
- * @param field 要查找的字段名
202
- * @param cwd 工作目录
203
- */
204
- export function findConfigFileWithField(field: string, cwd?: string): string {
205
- const workDir = cwd || process.cwd();
206
- // 按优先级从高到低查找,找到第一个包含该字段的文件
207
- const candidates = [
208
- join(workDir, RC_FILE_NAME),
209
- join(workDir, ".spaceflow", CONFIG_FILE_NAME),
210
- join(homedir(), RC_FILE_NAME),
211
- join(homedir(), ".spaceflow", CONFIG_FILE_NAME),
212
- ];
213
-
214
- for (const filePath of candidates) {
215
- if (existsSync(filePath)) {
216
- try {
217
- const content = readFileSync(filePath, "utf-8");
218
- const config = JSON.parse(content);
219
- if (config[field] !== undefined) {
220
- return filePath;
221
- }
222
- } catch {
223
- // 解析失败,跳过
224
- }
225
- }
226
- }
228
+ /** OpenAI 适配器配置类型 */
229
+ export type OpenAIConfig = z.infer<typeof OpenAIConfigSchema>;
227
230
 
228
- // 默认写入项目级 .spaceflowrc
229
- return join(workDir, RC_FILE_NAME);
230
- }
231
+ /** OpenCode 适配器配置类型 */
232
+ export type OpenCodeConfig = z.infer<typeof OpenCodeConfigSchema>;
231
233
 
232
- /**
233
- * 更新单个 dependency
234
- * 找到 dependencies 所在的配置文件并原地更新,默认写入 .spaceflowrc
235
- * @param name 依赖名称
236
- * @param source 依赖来源
237
- * @param cwd 工作目录,默认为 process.cwd()
238
- * @returns 是否有更新(false 表示已存在相同配置)
239
- */
240
- export function updateDependency(name: string, source: string, cwd?: string): boolean {
241
- const targetFile = findConfigFileWithField("dependencies", cwd);
242
- const config = existsSync(targetFile)
243
- ? (JSON.parse(readFileSync(targetFile, "utf-8")) as Record<string, unknown>)
244
- : ({} as Record<string, unknown>);
245
-
246
- if (!config.dependencies) {
247
- config.dependencies = {};
248
- }
234
+ /** Gemini 适配器配置类型 */
235
+ export type GeminiConfig = z.infer<typeof GeminiConfigSchema>;
249
236
 
250
- const dependencies = config.dependencies as Record<string, string>;
237
+ /** 飞书配置类型 */
238
+ export type FeishuConfig = z.infer<typeof FeishuConfigSchema>;
251
239
 
252
- // 检查是否已存在相同配置
253
- if (dependencies[name] === source) {
254
- return false;
255
- }
240
+ /** Storage 配置类型 */
241
+ export type StorageConfig = z.infer<typeof StorageConfigSchema>;
256
242
 
257
- dependencies[name] = source;
258
- writeFileSync(targetFile, stringify(config, { indent: 2 }) + "\n");
259
- return true;
260
- }
243
+ // ============ 配置加载 ============
261
244
 
262
245
  /**
263
- * 删除单个 dependency
264
- * 找到 dependencies 所在的配置文件并原地更新
265
- * @param name 依赖名称
246
+ * 加载 spaceflow.json 配置
247
+ * 从多级配置文件读取并合并,使用 zod 验证和填充默认值
266
248
  * @param cwd 工作目录,默认为 process.cwd()
267
- * @returns 是否有删除(false 表示不存在)
268
- */
269
- export function removeDependency(name: string, cwd?: string): boolean {
270
- const targetFile = findConfigFileWithField("dependencies", cwd);
271
- if (!existsSync(targetFile)) {
272
- return false;
273
- }
274
-
275
- let config: Record<string, unknown>;
276
- try {
277
- config = JSON.parse(readFileSync(targetFile, "utf-8"));
278
- } catch {
279
- return false;
280
- }
281
-
282
- if (!config.dependencies) {
283
- return false;
284
- }
285
-
286
- const dependencies = config.dependencies as Record<string, string>;
287
-
288
- if (!(name in dependencies)) {
289
- return false;
290
- }
291
-
292
- delete dependencies[name];
293
- writeFileSync(targetFile, stringify(config, { indent: 2 }) + "\n");
294
- return true;
295
- }
296
-
297
- /**
298
- * 获取 spaceflow 配置(兼容旧 API)
299
- * @deprecated 请使用 loadSpaceflowConfig()
300
- */
301
- export function spaceflowConfig(): SpaceflowConfig {
302
- return loadSpaceflowConfig();
303
- }
304
-
305
- /**
306
- * 加载 spaceflow.json 配置(用于 CLI 启动时)
307
- * 使用 zod 验证配置
308
249
  */
309
- export function loadSpaceflowConfig(): SpaceflowConfig {
310
- const fileConfig = readConfigSync();
250
+ export function loadSpaceflowConfig(cwd?: string): SpaceflowConfig {
251
+ const fileConfig = readConfigSync(cwd);
311
252
 
312
- // 使用 zod 验证核心配置
313
- const result = SpaceflowCoreConfigSchema.safeParse(fileConfig);
253
+ const result = SpaceflowConfigSchema.safeParse(fileConfig);
314
254
 
315
255
  if (!result.success) {
316
256
  const errors = result.error.issues
@@ -325,4 +265,12 @@ export function loadSpaceflowConfig(): SpaceflowConfig {
325
265
  } as SpaceflowConfig;
326
266
  }
327
267
 
268
+ /**
269
+ * 获取 spaceflow 配置(兼容旧 API)
270
+ * @deprecated 请使用 loadSpaceflowConfig()
271
+ */
272
+ export function spaceflowConfig(): SpaceflowConfig {
273
+ return loadSpaceflowConfig();
274
+ }
275
+
328
276
  export type { LLMMode };
package/src/index.ts CHANGED
@@ -65,7 +65,7 @@ export {
65
65
  } from "./shared/mcp";
66
66
 
67
67
  // I18n - 国际化
68
- export * from "./shared/i18n";
68
+ export * from "./cli-runtime/i18n";
69
69
 
70
70
  // Logger - 全局日志工具
71
71
  export * from "./shared/logger";
@@ -73,5 +73,15 @@ export * from "./shared/logger";
73
73
  // ============ 配置相关 ============
74
74
  export * from "./config";
75
75
 
76
+ // ============ CLI Runtime ============
77
+ // exec 入口、DI 容器、扩展加载器、i18n 初始化
78
+ export {
79
+ exec,
80
+ ServiceContainer,
81
+ ExtensionLoader,
82
+ initCliI18n,
83
+ internalExtensions,
84
+ } from "./cli-runtime";
85
+
76
86
  // ============ Zod 重导出 ============
77
87
  export { z } from "zod";
@@ -0,0 +1,22 @@
1
+ {
2
+ "description": "Build skill packages in the skills directory",
3
+ "options.watch": "Watch for file changes and rebuild automatically",
4
+ "buildFailed": "❌ Build failed: {{error}}",
5
+ "extensionDescription": "Build plugins/skills",
6
+ "noPlugins": "📦 No buildable plugins found",
7
+ "startBuilding": "📦 Building {{count}} plugins...",
8
+ "buildComplete": "✅ Build complete: {{success}} succeeded, {{fail}} failed",
9
+ "startWatching": "👀 Watching {{count}} plugins...",
10
+ "stopWatching": "🛑 Stop watching: {{name}}",
11
+ "building": "🔨 Building: {{name}}",
12
+ "buildSuccess": " ✅ Done ({{duration}}ms)",
13
+ "buildFailedWithDuration": " ❌ Failed ({{duration}}ms)",
14
+ "buildFailedWithMessage": " ❌ Failed ({{duration}}ms): {{message}}",
15
+ "buildWarnings": " ⚠️ Done ({{duration}}ms) - {{count}} warnings",
16
+ "watching": "👀 Watching: {{name}}",
17
+ "watchError": " ❌ [{{name}}] Error: {{message}}",
18
+ "watchBuildFailed": " ❌ [{{name}}] Build failed",
19
+ "watchBuildWarnings": " ⚠️ [{{name}}] Build complete - {{count}} warnings",
20
+ "watchBuildSuccess": " ✅ [{{name}}] Build complete",
21
+ "watchInitFailed": " ❌ [{{name}}] Init failed: {{message}}"
22
+ }
@@ -0,0 +1,16 @@
1
+ {
2
+ "description": "Clear all installed skill packages",
3
+ "options.global": "Clear installed content in global directory (~/.spaceflow)",
4
+ "clearFailed": "❌ Clear failed: {{error}}",
5
+ "extensionDescription": "Clear cache and temporary files",
6
+ "clearingGlobal": "🧹 Clearing all skill packages (global)",
7
+ "clearing": "🧹 Clearing all skill packages",
8
+ "clearDone": "✅ Clear complete",
9
+ "spaceflowNotExist": " .spaceflow does not exist, skipping",
10
+ "spaceflowNoClean": " .spaceflow has nothing to clean",
11
+ "clearingSpaceflow": " Clearing .spaceflow ({{count}} items, preserving config files)",
12
+ "deleted": " Deleted: {{entry}}",
13
+ "deleteFailed": " Warning: Failed to delete {{entry}}:",
14
+ "clearingSkills": " Clearing {{editor}}/skills ({{count}} items)",
15
+ "clearingCommands": " Clearing {{editor}}/commands ({{count}} .md files)"
16
+ }
@@ -0,0 +1,45 @@
1
+ {
2
+ "description": "Auto-generate conventional commit messages from staged changes",
3
+ "options.dryRun": "Only generate commit message without committing",
4
+ "options.noVerify": "Skip pre-commit and commit-msg hooks",
5
+ "options.split": "Split into multiple commits by package/logic/domain",
6
+ "dryRunMode": "\n🔍 Dry Run mode",
7
+ "splitSuccess": "\n✅ Successfully split into {{count}} commits",
8
+ "commitSuccess": "\n✅ Commit successful",
9
+ "commitFailed": "\n❌ Commit failed: {{error}}",
10
+ "extensionDescription": "Smart commit command using LLM to generate commit messages",
11
+ "getDiffFailed": "Failed to get staged diff, make sure you are in a git repository",
12
+ "noFilesToCommit": "No files to commit",
13
+ "noChanges": "No changes found",
14
+ "generatingMessage": "📝 Generating commit message with AI...",
15
+ "strategyRules": "custom rules",
16
+ "strategyRulesFirst": "rules-first",
17
+ "strategyPackage": "package directory",
18
+ "groupingByStrategy": "🔍 Grouping files by {{strategy}} strategy...",
19
+ "detectedGroups": "📦 Detected {{count}} groups of changes",
20
+ "scopeChanges": "{{scope}} changes",
21
+ "rootChanges": "Root directory changes",
22
+ "otherChanges": "Other changes",
23
+ "allChanges": "All changes",
24
+ "analyzingSplit": "🔍 Analyzing how to split commits within package...",
25
+ "dryRunMessage": "[Dry Run] Will commit the following message:\n{{message}}",
26
+ "commitFail": "Commit failed",
27
+ "stageFilesFailed": "Failed to stage files: {{error}}",
28
+ "noWorkingChanges": "No working directory changes",
29
+ "noStagedFiles": "No staged files",
30
+ "singleCommit": "📦 No split needed, committing as a single commit",
31
+ "generatedMessage": "\n📋 Generated commit message:",
32
+ "splitIntoCommits": "\n📦 Splitting into {{count}} commits",
33
+ "groupItem": " {{index}}. {{reason}}{{pkg}} ({{count}} files)",
34
+ "parallelGenerating": "\n🚀 Generating {{count}} commit messages in parallel...",
35
+ "allMessagesGenerated": "✅ All commit messages generated",
36
+ "generateMessageFailed": "Failed to generate commit messages: {{errors}}",
37
+ "resetStagingFailed": "Failed to reset staging area",
38
+ "committingGroup": "\n📝 Committing group {{current}}/{{total}}: {{reason}}{{pkg}}",
39
+ "skippingNoChanges": "⏭️ Skipping: no actual changes",
40
+ "commitMessage": "📋 Commit message:",
41
+ "commitItemFailed": "❌ Commit {{index}} failed: {{error}}",
42
+ "commitItemFailedDetail": "Commit {{index}} failed: {{error}}\n\nCompleted commits:\n{{committed}}",
43
+ "noChangesAll": "No changes found, please modify files or use git add first",
44
+ "noStagedFilesHint": "No staged files, please use git add first"
45
+ }
@@ -0,0 +1,27 @@
1
+ {
2
+ "description": "Create a new plugin from template",
3
+ "options.directory": "Specify target directory",
4
+ "options.list": "List available templates",
5
+ "options.from": "Use a remote Git repository as template source",
6
+ "options.ref": "Specify Git branch or tag (default: main)",
7
+ "noName": "❌ Please specify a name",
8
+ "usage": "Usage: spaceflow create <template> <name>",
9
+ "createFailed": "❌ Creation failed: {{error}}",
10
+ "availableTemplates": "Available templates:",
11
+ "extensionDescription": "Create new plugin/skill from template",
12
+ "updatingRepo": "🔄 Updating template repository: {{url}}",
13
+ "updateFailed": "⚠️ Update failed, using cached version",
14
+ "cloningRepo": "📥 Cloning template repository: {{url}}",
15
+ "cloneFailed": "Failed to clone repository: {{error}}",
16
+ "repoReady": "✅ Template repository ready: {{dir}}",
17
+ "templatesDirNotFound": "Templates directory not found",
18
+ "creatingPlugin": "📦 Creating {{template}} plugin: {{name}}",
19
+ "targetDir": " Directory: {{dir}}",
20
+ "dirExists": "Directory already exists: {{dir}}",
21
+ "templateNotFound": "Template \"{{template}}\" not found. Available templates: {{available}}",
22
+ "noTemplatesAvailable": "none",
23
+ "pluginCreated": "✅ {{template}} plugin created: {{name}}",
24
+ "nextSteps": "Next steps:",
25
+ "fileCreated": " ✓ Created {{file}}",
26
+ "fileCopied": " ✓ Copied {{file}}"
27
+ }