@lark-apaas/miaoda-cli 0.1.9 → 0.1.11

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.
@@ -121,7 +121,7 @@ function registerAppInit(parent) {
121
121
  .command('init')
122
122
  .description('初始化应用代码:抓 template 渲染、同步 upgrade/templates、装 .agent/steering/ skills、写 .spark/meta.json。.spark/meta.json 已存在则直接退出')
123
123
  .option('--template <stack>', `技术栈短名(${index_1.SUPPORTED_STACKS.join(' / ')})`)
124
- .option('--conf <json>', 'init 配置 JSON。支持 {"version": "<template 版本>"} / {"steeringVersion": "<steering 版本>"},均默认 latest')
124
+ .option('--conf <json>', 'init 配置 JSON。支持 {"version": "<template 版本>"},默认 latest')
125
125
  .option('--skip-install', '跳过依赖安装', false)
126
126
  .addOption((0, shared_1.appIdOption)())
127
127
  .addHelpText('after', `
@@ -161,7 +161,6 @@ JSON 输出
161
161
  $ miaoda app init --template vite-react
162
162
  $ miaoda app init --template nestjs-react-fullstack --app-id app_xxx
163
163
  $ miaoda app init --template vite-react --conf '{"version": "0.1.0"}'
164
- $ miaoda app init --template vite-react --conf '{"steeringVersion": "0.1.0"}'
165
164
  $ miaoda app init --template vite-react --skip-install
166
165
  $ MIAODA_DEP_CACHE_DIR=/tmp/dep-cache miaoda app init --template vite-react
167
166
  `);
@@ -193,11 +192,7 @@ function parseInitConf(raw) {
193
192
  if (version !== undefined && typeof version !== 'string') {
194
193
  throw new error_1.AppError('ARGS_INVALID', '--conf.version 必须是字符串');
195
194
  }
196
- const steeringVersion = parsed.steeringVersion;
197
- if (steeringVersion !== undefined && typeof steeringVersion !== 'string') {
198
- throw new error_1.AppError('ARGS_INVALID', '--conf.steeringVersion 必须是字符串');
199
- }
200
- return { version, steeringVersion };
195
+ return { version };
201
196
  }
202
197
  function isPlainObject(v) {
203
198
  return typeof v === 'object' && v !== null && !Array.isArray(v);
@@ -22,8 +22,8 @@ function registerSkillsSync(parent) {
22
22
  const cmd = parent
23
23
  .command('sync')
24
24
  .description('同步 coding-steering(latest 或指定版本)到 user app')
25
- .argument('[version]', 'coding-steering 包版本或 dist-tag,缺省 latest')
26
25
  .option('--dir <path>', '项目目录,默认当前目录', '.')
26
+ .option('--version <ver>', 'coding-steering 包版本或 dist-tag,缺省 latest')
27
27
  .option('--local', '走本地 dev / agent 输出形态:拷到 .agents/skills 平铺 + 创建 .claude/skills 软链。' +
28
28
  '不传时退回老形态:拷到 .agent/skills/steering/<stack>/skills/(跟沙箱端 update-skills.sh 对齐)', false)
29
29
  .addHelpText('after', `
@@ -46,15 +46,15 @@ JSON 输出
46
46
  "claudeSkillsLink": "created|updated|noop|conflict" // 仅 --local 时}}
47
47
 
48
48
  示例
49
- $ miaoda skills sync
50
- $ miaoda skills sync --local # 本地 dev / agent 用
51
- $ miaoda skills sync 0.2.0
49
+ $ miaoda skills sync # 拉 @latest
50
+ $ miaoda skills sync --local # 本地 dev / agent 用
51
+ $ miaoda skills sync --version 0.2.0 # 显式指定版本
52
52
  $ miaoda skills sync --dir ./my-app
53
53
  `);
54
- cmd.action((0, shared_1.withHelp)(cmd, async (version, rawOpts) => {
54
+ cmd.action((0, shared_1.withHelp)(cmd, async (rawOpts) => {
55
55
  await (0, index_1.handleSkillsSync)({
56
56
  dir: rawOpts.dir,
57
- version,
57
+ version: rawOpts.version,
58
58
  local: rawOpts.local,
59
59
  });
60
60
  }));
@@ -64,14 +64,20 @@ async function handleAppInit(opts) {
64
64
  const upgradedPackages = (0, sync_1.upgradePlatformDeps)(targetDir, 'init');
65
65
  // skills 同步软失败:拉不到 coding-steering 包不该阻断 writeSparkMeta /
66
66
  // activateGitHooks(之前会让 .spark/meta.json 没写,下次 init 半渲染状态又得重跑全套)。
67
+ // 按 stack 分流 outputLayout:
68
+ // - nestjs-react-fullstack:本地主战场,用 flat 布局
69
+ // (.agents/skills + .claude/skills 软链),不再创建 .agent/ 老目录
70
+ // - 其他 stack:仍走默认 nested (.agent/skills/steering/<stack>/skills/),
71
+ // 跟沙箱端 update-skills.sh 老链路对齐,避免破坏
72
+ const outputLayout = stack === 'nestjs-react-fullstack' ? 'flat' : 'nested';
67
73
  let steeringResult;
68
74
  let steeringError;
69
75
  try {
70
76
  steeringResult = (0, coding_steering_1.syncCodingSteering)({
71
77
  stack,
72
78
  targetDir,
73
- version: opts.conf?.steeringVersion,
74
79
  logPrefix: 'init',
80
+ outputLayout,
75
81
  });
76
82
  }
77
83
  catch (err) {
@@ -91,7 +97,6 @@ async function handleAppInit(opts) {
91
97
  version: tplResult.version,
92
98
  archType: tplResult.archType,
93
99
  app_id: opts.appId,
94
- steeringVersion: steeringResult.version,
95
100
  });
96
101
  const syncedSummary = syncRunResult.stackFound
97
102
  ? syncRunResult.rulesApplied.length > 0
@@ -7,6 +7,7 @@ exports.handleAppUpgrade = void 0;
7
7
  exports.handleAppSync = handleAppSync;
8
8
  exports.runStackSync = runStackSync;
9
9
  exports.summarizeSyncResults = summarizeSyncResults;
10
+ exports.listManagedDeps = listManagedDeps;
10
11
  exports.upgradePlatformDeps = upgradePlatformDeps;
11
12
  const node_child_process_1 = require("node:child_process");
12
13
  const node_fs_1 = __importDefault(require("node:fs"));
@@ -82,15 +83,17 @@ async function handleAppSync(opts) {
82
83
  const upgradedPackages = upgradePlatformDeps(targetDir, 'sync');
83
84
  // 3. activate git hooks(template 自带 .githooks/pre-commit,core.hooksPath 一次性设置)
84
85
  const hookActivation = (0, githooks_1.activateGitHooks)(targetDir);
85
- // 4. npm install —— 跟 init 一样软失败,install 挂了不该阻断 emit / sync 总结输出。
86
- // 把本次实际有 from→to 变化的管控包**显式**作为位置参数传给 npm install,强制 npm 重新
87
- // resolve lockfile entry。原因:user app 老 lockfile 可能锁着 alpha 版本,纯 `npm install`
88
- // lockfile 锁定值跟 package.json spec ("latest" range) 兼容会保留 alpha,导致 sync 后
89
- // package.json 写了 latest 但实际还是装 alpha。显式 `npm install foo@latest bar@latest`
90
- // 会重新解析这些包,lockfile 跟着更新。没变化的包不动,避免 noisy lockfile diff。
86
+ // 4. npm install —— 跟 init 一样软失败,install 挂了不该阻断 emit / sync 总结输出。
87
+ // user app 装着的**所有**管控包显式作为位置参数传给 npm install,强制 npm 重新
88
+ // resolve lockfile entry。原因:
89
+ // - 即便 package.json spec 已经是 "latest"(没有 from→to 变化),user app lockfile
90
+ // 可能锁着具体老版本 (如 fullstack-vite-preset@1.0.9),纯 `npm install` 会按 lockfile
91
+ // 拉老版,绕过 spec "latest" 的解析意图
92
+ // - 显式 `npm install foo@latest bar@latest` 让 npm 重新解析,lockfile 跟着更新到当前 latest
93
+ // - 没装的管控包不动 (sync 的职责是对齐,不是塞依赖)
91
94
  // --ignore-scripts 绕开 action-plugin postinstall 在缺平台 env 时的 ENOENT。
92
- // --registry 跟 init 钉同一份 npmmirror,避免 user 全局 ~/.npmrc 把 sync 拉到 ~~一个~~
93
- // 另一个源(详见 services/app/init/install.ts 上的注释)。MIAODA_NPM_REGISTRY env 兜底。
95
+ // --registry 跟 init 钉同一份 npmmirror,避免 user 全局 ~/.npmrc 把 sync 拉到另一个源
96
+ // (详见 services/app/init/install.ts 上的注释)。MIAODA_NPM_REGISTRY env 兜底。
94
97
  const installArgs = [
95
98
  'install',
96
99
  '--no-audit',
@@ -99,8 +102,8 @@ async function handleAppSync(opts) {
99
102
  '--registry',
100
103
  (0, install_1.resolveNpmInstallRegistry)(),
101
104
  ];
102
- for (const bump of upgradedPackages) {
103
- installArgs.push(`${bump.name}@${bump.to}`);
105
+ for (const name of listManagedDeps(targetDir)) {
106
+ installArgs.push(`${name}@${MANAGED_PLATFORM_PACKAGES[name]}`);
104
107
  }
105
108
  (0, logger_1.log)('sync', `Running npm ${installArgs.join(' ')}...`);
106
109
  let installError;
@@ -200,6 +203,29 @@ function readPackageJson(pkgJsonPath) {
200
203
  return null;
201
204
  return JSON.parse(node_fs_1.default.readFileSync(pkgJsonPath, 'utf-8'));
202
205
  }
206
+ /**
207
+ * 列出 user app 装着的 @lark-apaas/* 管控包 (在 MANAGED_PLATFORM_PACKAGES 白名单内的)。
208
+ * sync handler 用来给 `npm install` 显式列管控包名 + @latest,强制 npm 重新 resolve
209
+ * lockfile entry —— 即便 package.json spec 已经是 "latest" 没 from→to 变化,
210
+ * 老 lockfile 锁的具体老版本(如 fullstack-vite-preset@1.0.9)也会被刷掉。
211
+ */
212
+ function listManagedDeps(targetDir) {
213
+ const pkg = readPackageJson(node_path_1.default.join(targetDir, 'package.json'));
214
+ if (!pkg)
215
+ return [];
216
+ const names = new Set();
217
+ for (const section of ['dependencies', 'devDependencies']) {
218
+ const deps = pkg[section];
219
+ if (!deps || typeof deps !== 'object')
220
+ continue;
221
+ for (const name of Object.keys(deps)) {
222
+ if (Object.prototype.hasOwnProperty.call(MANAGED_PLATFORM_PACKAGES, name)) {
223
+ names.add(name);
224
+ }
225
+ }
226
+ }
227
+ return [...names];
228
+ }
203
229
  /**
204
230
  * 把 user app package.json 里已装的 @lark-apaas/* 平台包改写成 MANAGED_PLATFORM_PACKAGES
205
231
  * 表里的目标 spec(缺省 `"latest"`)。由 `miaoda app sync` 和 `miaoda app init`(template
@@ -10,10 +10,13 @@ const spark_meta_1 = require("../../../utils/spark-meta");
10
10
  const error_1 = require("../../../utils/error");
11
11
  const output_1 = require("../../../utils/output");
12
12
  /**
13
- * miaoda skills sync [version] [--dir <path>]
13
+ * miaoda skills sync [--dir <path>] [--version <ver>]
14
14
  *
15
- * 从 .spark/meta.json 读 stack(+ 默认 steeringVersion),把 coding-steering 包内对应 stack
16
- * 的 skills + tech.md 同步到 <dir>/.agent/steering/。需要先跑过 `miaoda app init`。
15
+ * 从 .spark/meta.json 读 stack,把 coding-steering 包内对应 stack 的 skills + tech.md
16
+ * 同步到 <dir>/.agent/steering/。需要先跑过 `miaoda app init`。
17
+ *
18
+ * 版本来源:CLI `--version` flag 或默认 `latest`。**不读 meta.json**——steering 版本
19
+ * 不在 meta 里钉,每次 sync 都跟随 `--version` 显式传或退回 @latest。
17
20
  */
18
21
  async function handleSkillsSync(opts) {
19
22
  await Promise.resolve();
@@ -22,23 +25,17 @@ async function handleSkillsSync(opts) {
22
25
  if (meta.stack === undefined || meta.stack === '') {
23
26
  throw new error_1.AppError('SKILLS_META_INCOMPLETE', '.spark/meta.json missing stack — run `miaoda app init` first');
24
27
  }
25
- const version = opts.version ?? meta.steeringVersion;
26
- const versionSource = opts.version
27
- ? 'cli-arg'
28
- : meta.steeringVersion
29
- ? 'meta.json'
30
- : 'default-latest';
31
28
  const result = (0, coding_steering_1.syncCodingSteering)({
32
29
  stack: meta.stack,
33
30
  targetDir,
34
- version,
31
+ version: opts.version,
35
32
  outputLayout: opts.local === true ? 'flat' : 'nested',
36
33
  });
37
34
  (0, output_1.emit)({
38
35
  data: {
39
36
  stack: meta.stack,
40
37
  version: result.version,
41
- versionSource,
38
+ versionSource: opts.version !== undefined ? 'cli-arg' : 'default-latest',
42
39
  syncedSkills: result.syncedSkills,
43
40
  techSynced: result.techSynced,
44
41
  ...(result.claudeSkillsLink !== undefined
@@ -8,6 +8,21 @@ const node_fs_1 = __importDefault(require("node:fs"));
8
8
  const node_path_1 = __importDefault(require("node:path"));
9
9
  const node_child_process_1 = require("node:child_process");
10
10
  const logger_1 = require("../utils/logger");
11
+ const GIT_LEAKED_ENV_KEYS = new Set([
12
+ 'GIT_DIR',
13
+ 'GIT_WORK_TREE',
14
+ 'GIT_INDEX_FILE',
15
+ 'GIT_COMMON_DIR',
16
+ 'GIT_PREFIX',
17
+ 'GIT_OBJECT_DIRECTORY',
18
+ 'GIT_ALTERNATE_OBJECT_DIRECTORIES',
19
+ 'GIT_CONFIG',
20
+ 'GIT_CONFIG_PARAMETERS',
21
+ 'GIT_CONFIG_COUNT',
22
+ ]);
23
+ function envWithoutGitLeaks() {
24
+ return Object.fromEntries(Object.entries(process.env).filter(([key]) => !GIT_LEAKED_ENV_KEYS.has(key)));
25
+ }
11
26
  /**
12
27
  * 把 `.githooks/` 注册为本仓库的 git hooks 目录,让 pre-commit 等 hook 立即生效。
13
28
  *
@@ -33,8 +48,10 @@ function activateGitHooks(targetDir) {
33
48
  if ((currentMode & 0o111) !== 0o111) {
34
49
  node_fs_1.default.chmodSync(hookFile, 0o755);
35
50
  }
51
+ const env = envWithoutGitLeaks();
36
52
  const probe = (0, node_child_process_1.spawnSync)('git', ['config', '--get', 'core.hooksPath'], {
37
53
  cwd: targetDir,
54
+ env,
38
55
  stdio: ['ignore', 'pipe', 'ignore'],
39
56
  });
40
57
  const currentHooksPath = probe.stdout.toString().trim();
@@ -43,6 +60,7 @@ function activateGitHooks(targetDir) {
43
60
  }
44
61
  const res = (0, node_child_process_1.spawnSync)('git', ['config', 'core.hooksPath', '.githooks'], {
45
62
  cwd: targetDir,
63
+ env,
46
64
  stdio: ['ignore', 'ignore', 'pipe'],
47
65
  });
48
66
  if (res.status !== 0) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lark-apaas/miaoda-cli",
3
- "version": "0.1.9",
3
+ "version": "0.1.11",
4
4
  "description": "Miaoda 平台命令行工具,面向 Agent 调用",
5
5
  "type": "commonjs",
6
6
  "bin": {