@optima-chat/dev-skills 0.7.43 → 0.8.1

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 (38) hide show
  1. package/.claude/skills/grant-balance/SKILL.md +4 -2
  2. package/.claude/skills/grant-subscription/SKILL.md +5 -1
  3. package/.codex/skills/grant-balance/SKILL.md +2 -1
  4. package/.codex/skills/grant-subscription/SKILL.md +6 -2
  5. package/bin/cli.js +1 -1
  6. package/bin/helpers/billing-http.ts +55 -16
  7. package/bin/helpers/confirm-prompt.ts +8 -4
  8. package/bin/helpers/db-utils.ts +34 -12
  9. package/bin/helpers/discount/create.ts +3 -3
  10. package/bin/helpers/discount/disable.ts +3 -3
  11. package/bin/helpers/discount/generate.ts +3 -3
  12. package/bin/helpers/discount/list.ts +3 -3
  13. package/bin/helpers/entitlement/grant.ts +4 -7
  14. package/bin/helpers/entitlement/list.ts +4 -7
  15. package/bin/helpers/entitlement/revoke.ts +4 -7
  16. package/bin/helpers/generate-test-token.ts +28 -10
  17. package/bin/helpers/grant-balance.ts +6 -5
  18. package/bin/helpers/grant-subscription.ts +10 -8
  19. package/bin/helpers/query-db.ts +9 -6
  20. package/bin/helpers/show-env.ts +34 -15
  21. package/bin/helpers/verify-health.ts +10 -9
  22. package/dist/bin/helpers/billing-http.js +55 -14
  23. package/dist/bin/helpers/confirm-prompt.js +7 -4
  24. package/dist/bin/helpers/db-utils.js +34 -12
  25. package/dist/bin/helpers/discount/create.js +2 -2
  26. package/dist/bin/helpers/discount/disable.js +2 -2
  27. package/dist/bin/helpers/discount/generate.js +2 -2
  28. package/dist/bin/helpers/discount/list.js +2 -2
  29. package/dist/bin/helpers/entitlement/grant.js +3 -6
  30. package/dist/bin/helpers/entitlement/list.js +3 -6
  31. package/dist/bin/helpers/entitlement/revoke.js +3 -6
  32. package/dist/bin/helpers/generate-test-token.js +29 -9
  33. package/dist/bin/helpers/grant-balance.js +6 -5
  34. package/dist/bin/helpers/grant-subscription.js +10 -8
  35. package/dist/bin/helpers/query-db.js +9 -6
  36. package/dist/bin/helpers/show-env.js +31 -12
  37. package/dist/bin/helpers/verify-health.js +8 -7
  38. package/package.json +1 -1
@@ -64,6 +64,14 @@ const ENV_CONFIG = {
64
64
  // 注意不要换成同名的 confidential client `commerce-cli-cn-prod-*`,那个需要 client_secret,走不通 CLI 的 public ROPC flow。
65
65
  clientId: 'dev-skill-cli-cn-pro-acvkmcuq',
66
66
  envName: 'cn-prod'
67
+ },
68
+ 'cn-stage': {
69
+ // 阿里云预发(独立于 AWS stage `.optima.onl`)。域名 *.stage.optima.chat。
70
+ authUrl: 'https://auth.stage.optima.chat',
71
+ apiUrl: 'https://commerce.stage.optima.chat',
72
+ // cn-stage user-auth 里注册的 public client(开 password grant,ROPC flow)。
73
+ clientId: 'dev-skill-cli-cn-sta-3dvsxzdo',
74
+ envName: 'cn-stage'
67
75
  }
68
76
  };
69
77
  async function httpRequest(url, options = {}) {
@@ -166,6 +174,7 @@ async function main() {
166
174
  let phone;
167
175
  let address;
168
176
  let environment = 'ci';
177
+ let skipMerchant = false;
169
178
  // 解析命令行参数
170
179
  for (let i = 0; i < args.length; i++) {
171
180
  const arg = args[i];
@@ -184,13 +193,16 @@ async function main() {
184
193
  else if (arg === '--address' && args[i + 1]) {
185
194
  address = args[++i];
186
195
  }
196
+ else if (arg === '--skip-merchant') {
197
+ skipMerchant = true;
198
+ }
187
199
  else if (arg === '--env' && args[i + 1]) {
188
200
  const envArg = args[++i];
189
- if (envArg === 'ci' || envArg === 'stage' || envArg === 'prod' || envArg === 'cn-prod') {
201
+ if (envArg === 'ci' || envArg === 'stage' || envArg === 'prod' || envArg === 'cn-prod' || envArg === 'cn-stage') {
190
202
  environment = envArg;
191
203
  }
192
204
  else {
193
- console.error(`❌ Invalid environment: ${envArg}. Must be 'ci', 'stage', 'prod', or 'cn-prod'.`);
205
+ console.error(`❌ Invalid environment: ${envArg}. Must be 'ci', 'stage', 'prod', 'cn-prod', or 'cn-stage'.`);
194
206
  process.exit(1);
195
207
  }
196
208
  }
@@ -204,20 +216,24 @@ Options:
204
216
  --business-name <name> Merchant business name (default: auto-generated)
205
217
  --phone <phone> Merchant phone number (optional)
206
218
  --address <address> Merchant address (optional)
207
- --env <environment> Environment: ci (default), stage, prod, or cn-prod
219
+ --skip-merchant 只注册用户 + token,跳过 Commerce merchant profile 设置
220
+ (token-only;用于 gateway E2E、或 commerce 未就绪的环境)
221
+ --env <environment> Environment: ci (default), stage, prod, cn-prod, or cn-stage
208
222
  --help, -h Show this help message
209
223
 
210
224
  Environments:
211
- ci CI 环境 (auth.optima.chat, api.optima.chat)
212
- stage Stage 环境 (auth.stage.optima.onl, api.stage.optima.onl)
213
- prod Prod 环境 (auth.optima.onl, api.optima.onl)
214
- cn-prod 阿里云生产环境 (auth.yzsgo.com, commerce.yzsgo.com) [#201]
225
+ ci CI 环境 (auth.optima.chat, api.optima.chat)
226
+ stage Stage 环境 (auth.stage.optima.onl, api.stage.optima.onl)
227
+ prod Prod 环境 (auth.optima.onl, api.optima.onl)
228
+ cn-prod 阿里云生产环境 (auth.yzsgo.com, commerce.yzsgo.com) [#201]
229
+ cn-stage 阿里云预发环境 (auth.stage.optima.chat, commerce.stage.optima.chat)
215
230
 
216
231
  Example:
217
232
  optima-generate-test-token
218
233
  optima-generate-test-token --env stage
219
234
  optima-generate-test-token --env prod
220
235
  optima-generate-test-token --env cn-prod
236
+ optima-generate-test-token --env cn-stage
221
237
  optima-generate-test-token --business-name "My Test Shop" --env stage
222
238
  `);
223
239
  process.exit(0);
@@ -237,8 +253,12 @@ Example:
237
253
  const user = await registerMerchant(email, password, businessName, config, phone, address);
238
254
  // 2. 获取 token
239
255
  const token = await getToken(email, password, config);
240
- // 3. 设置 merchant profile(Commerce API)
241
- const merchantProfile = await setupMerchantProfile(token, businessName, config);
256
+ // 3. 设置 merchant profile(Commerce API);--skip-merchant 时跳过(token-only
257
+ const merchantProfile = skipMerchant
258
+ ? { merchant_id: 'N/A (skipped)' }
259
+ : await setupMerchantProfile(token, businessName, config);
260
+ if (skipMerchant)
261
+ console.log('\n⏭️ Skipped merchant profile setup (--skip-merchant)');
242
262
  // 4. 保存 token 到临时文件
243
263
  const tmpDir = os.tmpdir();
244
264
  const tokenFileName = `optima-test-token-${Date.now()}.txt`;
@@ -19,13 +19,14 @@ $1 = 700 credits (P15 unified ledger; the USD wallet is retired).
19
19
  Options:
20
20
  --amount <usd> USD amount to grant (required, e.g. 5 for $5.00 = 3500 credits)
21
21
  --description <text> Description for audit trail (optional)
22
- --env <env> Environment: stage, prod, cn-prod (default: stage)
22
+ --env <env> Environment: stage, prod, cn-prod, cn-stage (default: stage)
23
23
  -h, --help Show this help
24
24
 
25
25
  Examples:
26
26
  optima-grant-balance user@example.com --amount 5 --env prod
27
27
  optima-grant-balance user@example.com --amount 10 --description "Service outage compensation"
28
- optima-grant-balance user@example.com --amount 1 --env cn-prod # ¥-priced env, still USD input ($1 = 700 credits)`);
28
+ optima-grant-balance user@example.com --amount 1 --env cn-prod # ¥-priced env, still USD input ($1 = 700 credits)
29
+ optima-grant-balance user@example.com --amount 1 --env cn-stage # 阿里云预发`);
29
30
  process.exit(0);
30
31
  }
31
32
  const email = args[0];
@@ -55,10 +56,10 @@ async function main() {
55
56
  console.log(`\n🎁 Granting $${amountUsd.toFixed(2)} (${Math.round(amountUsd * 700)} credits) to ${email} [${env.toUpperCase()}]\n`);
56
57
  if (description)
57
58
  console.log(` Reason: ${description}`);
58
- // cn-prod has no SSH tunnel into the Aliyun RDS — resolve via user-auth's
59
- // internal lookup API instead of the direct SQL path.
59
+ // cn-prod / cn-stage have no SSH tunnel into the Aliyun RDS — resolve via
60
+ // user-auth's internal lookup API instead of the direct SQL path.
60
61
  let userId;
61
- if (env === 'cn-prod') {
62
+ if (env === 'cn-prod' || env === 'cn-stage') {
62
63
  userId = await (0, billing_http_1.resolveUserIdByEmail)(env, email);
63
64
  }
64
65
  else {
@@ -55,7 +55,7 @@ function assertPhoneMatch(inputPhone, accountPhone) {
55
55
  */
56
56
  function assertAwsEmailOnly(env, kind) {
57
57
  if ((env === 'stage' || env === 'prod') && kind !== 'email') {
58
- throw new Error('stage/prod 仅支持 email 标识;手机号/userId 解析仅 cn-prod 可用');
58
+ throw new Error('stage/prod 仅支持 email 标识;手机号/userId 解析仅 cn-prod / cn-stage 可用');
59
59
  }
60
60
  }
61
61
  // cn-prod sells the CNY-priced -cn plans (P12); the bare USD plan ids also
@@ -65,6 +65,8 @@ const PLANS_BY_ENV = {
65
65
  stage: ['trial', 'starter', 'pro', 'enterprise'],
66
66
  prod: ['trial', 'starter', 'pro', 'enterprise'],
67
67
  'cn-prod': ['trial', 'starter-cn', 'pro-cn', 'enterprise-cn'],
68
+ // cn-stage 卖同一套 CNY -cn plan(billing plan 目录与 cn-prod 一致)
69
+ 'cn-stage': ['trial', 'starter-cn', 'pro-cn', 'enterprise-cn'],
68
70
  };
69
71
  function parseArgs(args) {
70
72
  if (args.length === 0 || args[0] === '--help' || args[0] === '-h') {
@@ -72,9 +74,9 @@ function parseArgs(args) {
72
74
 
73
75
  Options:
74
76
  --plan <id> Plan: trial, starter, pro, enterprise (default: pro)
75
- cn-prod plans: trial, starter-cn, pro-cn, enterprise-cn (default: pro-cn)
77
+ cn-prod/cn-stage plans: trial, starter-cn, pro-cn, enterprise-cn (default: pro-cn)
76
78
  --months <n> Duration in months (default: 1)
77
- --env <env> Environment: stage, prod, cn-prod (default: stage)
79
+ --env <env> Environment: stage, prod, cn-prod, cn-stage (default: stage)
78
80
  -h, --help Show this help`);
79
81
  process.exit(0);
80
82
  }
@@ -94,7 +96,7 @@ Options:
94
96
  }
95
97
  }
96
98
  (0, billing_http_1.validateEnvCnProd)(env);
97
- plan = plan ?? (env === 'cn-prod' ? 'pro-cn' : 'pro');
99
+ plan = plan ?? (env === 'cn-prod' || env === 'cn-stage' ? 'pro-cn' : 'pro');
98
100
  const allowed = PLANS_BY_ENV[env];
99
101
  if (!allowed.includes(plan)) {
100
102
  console.error(`Unknown plan for ${env}: ${plan}. Available: ${allowed.join(', ')}`);
@@ -113,12 +115,12 @@ async function main() {
113
115
  console.log(`\n🎁 Granting ${plan} subscription to ${identifier} (${kind}) for ${months} month(s) [${env.toUpperCase()}]\n`);
114
116
  // identity is the verified target account to print on success. AWS keeps the
115
117
  // pre-change behavior (email-only, no internal HTTP reverse-verify); only
116
- // cn-prod runs the full classify→resolve→getUserById→phone-assert防呆 path.
118
+ // cn-prod / cn-stage run the full classify→resolve→getUserById→phone-assert防呆 path.
117
119
  let userId;
118
120
  let identity;
119
- if (env === 'cn-prod') {
120
- // cn-prod has no SSH tunnel into the Aliyun RDS — resolve via user-auth,
121
- // and its dev-skills token carries internal:users:write for the lookups.
121
+ if (env === 'cn-prod' || env === 'cn-stage') {
122
+ // cn-prod / cn-stage have no SSH tunnel into the Aliyun RDS — resolve via
123
+ // user-auth, and the dev-skills token carries internal:users:write for lookups.
122
124
  if (kind === 'userId') {
123
125
  userId = identifier;
124
126
  }
@@ -170,7 +170,7 @@ async function main() {
170
170
  console.error('Usage: query-db.ts <service> <sql> [environment]');
171
171
  console.error('');
172
172
  console.error('Services: commerce-backend, user-auth, agentic-chat, bi-backend, session-gateway, gateway-core, optima-logistics, billing, ads-backend, amazon-backend, browser-backend, shopify-backend, optima-generation, optima-sentinel');
173
- console.error('Environments: ci (default), stage, prod, cn (阿里云 cn-prod)');
173
+ console.error('Environments: ci (default), stage, prod, cn-prod (阿里云生产), cn-stage (阿里云预发)');
174
174
  console.error('');
175
175
  console.error('Example: query-db.ts user-auth "SELECT COUNT(*) FROM users" prod');
176
176
  process.exit(1);
@@ -184,19 +184,22 @@ async function main() {
184
184
  // cn-prod(阿里云):独立 Infisical + 经 buildbox 跳板连内网 RDS(动态端口隧道)。
185
185
  // 两类 cred:① shared-secrets/database-users(按 prefix)② 服务自己的 DATABASE_URL(展开引用)。
186
186
  if ((0, db_utils_1.isCnEnv)(environment)) {
187
+ // cn-prod / cn-stage 共用 SERVICE_DB_MAP[*].prod 的 userKey/databaseUrlPath(prefix、
188
+ // /services/<svc> 路径两环境一致);connectCn* 按 env 切 cn Infisical 环境 + RDS 实例。
189
+ const label = environment === 'cn-stage' ? 'CN-STAGE' : 'CN-PROD';
187
190
  const prodCfg = SERVICE_DB_MAP[service].prod;
188
191
  let db;
189
192
  if (prodCfg?.userKey) {
190
193
  const prefix = prodCfg.userKey.replace(/_DB_USER$/, '');
191
- console.log(`\n🔍 Querying ${service} (CN-PROD, prefix ${prefix})...`);
192
- db = (0, db_utils_1.connectCnDB)(prefix);
194
+ console.log(`\n🔍 Querying ${service} (${label}, prefix ${prefix})...`);
195
+ db = (0, db_utils_1.connectCnDB)(prefix, environment);
193
196
  }
194
197
  else if (prodCfg?.databaseUrlPath) {
195
- console.log(`\n🔍 Querying ${service} (CN-PROD, DATABASE_URL @ ${prodCfg.databaseUrlPath})...`);
196
- db = (0, db_utils_1.connectCnDBFromUrl)(prodCfg.databaseUrlPath);
198
+ console.log(`\n🔍 Querying ${service} (${label}, DATABASE_URL @ ${prodCfg.databaseUrlPath})...`);
199
+ db = (0, db_utils_1.connectCnDBFromUrl)(prodCfg.databaseUrlPath, environment);
197
200
  }
198
201
  else {
199
- console.error(`cn-prod query 暂不支持 ${service}(既无 userKey 也无 databaseUrlPath)。见 optima-dev-skills#21。`);
202
+ console.error(`${label} query 暂不支持 ${service}(既无 userKey 也无 databaseUrlPath)。见 optima-dev-skills#21。`);
200
203
  process.exit(1);
201
204
  }
202
205
  console.log('\n' + db.query(sql));
@@ -31,11 +31,15 @@ const SUPPORTED_SERVICES = [
31
31
  'optima-sentinel-worker',
32
32
  'shopify-backend',
33
33
  ];
34
- // 环境到 Infisical environment 的映射
34
+ // 环境到 AWS Infisical environment 的映射(cn 走独立 cn Infisical,见下方分支)
35
35
  const ENV_MAP = {
36
36
  stage: 'staging',
37
37
  prod: 'prod'
38
38
  };
39
+ /** 该 environment 是否被支持(AWS stage/prod 或阿里云 cn-prod/cn-stage)。 */
40
+ function isSupportedEnv(env) {
41
+ return ENV_MAP[env] !== undefined || (0, db_utils_1.isCnEnv)(env);
42
+ }
39
43
  // NOTE: getInfisicalSecrets is kept local because it encodes secretPath (encodeURIComponent),
40
44
  // unlike db-utils' raw-path variant; getGitHubVariable/getInfisicalConfig/getInfisicalToken are shared from db-utils.
41
45
  function getInfisicalSecrets(config, token, environment, secretPath) {
@@ -53,17 +57,22 @@ Usage: optima-show-env <service> <environment> [options]
53
57
 
54
58
  Arguments:
55
59
  service Service name (${SUPPORTED_SERVICES.join(', ')})
56
- environment Environment (stage, prod)
60
+ environment Environment (stage, prod, cn-prod, cn-stage)
57
61
 
58
62
  Options:
59
63
  --filter Filter by key pattern (e.g., --filter DATABASE)
60
64
  --keys-only Show only key names, not values
61
65
  --help Show this help message
62
66
 
67
+ Note:
68
+ cn-prod / cn-stage 读阿里云 cn Infisical(需 INFISICAL_CN_EMAIL/PASSWORD 环境变量,
69
+ admin user,见 optima-dev-skills#21)。stage/prod 读 AWS Infisical(GitHub Variables)。
70
+
63
71
  Examples:
64
72
  optima-show-env commerce-backend stage
65
73
  optima-show-env user-auth prod --filter DATABASE
66
- optima-show-env bi stage --keys-only
74
+ optima-show-env gateway-core cn-stage
75
+ optima-show-env bi-backend stage --keys-only
67
76
  `);
68
77
  }
69
78
  async function main() {
@@ -101,22 +110,32 @@ async function main() {
101
110
  process.exit(1);
102
111
  }
103
112
  // 验证环境
104
- if (!ENV_MAP[environment]) {
113
+ if (!isSupportedEnv(environment)) {
105
114
  console.error(`Error: Unknown environment '${environment}'`);
106
- console.error('Available environments: stage, prod');
115
+ console.error('Available environments: stage, prod, cn-prod, cn-stage');
107
116
  process.exit(1);
108
117
  }
109
118
  // Infisical 路径为 /services/<service-name>
110
119
  const secretPath = `/services/${service}`;
111
120
  console.log(`\n🔍 Fetching environment variables for ${service} (${environment.toUpperCase()})...\n`);
112
121
  try {
113
- const infisicalConfig = (0, db_utils_1.getInfisicalConfig)();
114
- console.log('✓ Loaded Infisical config from GitHub Variables');
115
- const token = (0, db_utils_1.getInfisicalToken)(infisicalConfig);
116
- console.log('✓ Obtained Infisical access token');
117
- const infisicalEnv = ENV_MAP[environment];
118
- const secrets = getInfisicalSecrets(infisicalConfig, token, infisicalEnv, secretPath);
119
- console.log(`✓ Retrieved secrets from Infisical (path: ${secretPath})\n`);
122
+ let secrets;
123
+ if ((0, db_utils_1.isCnEnv)(environment)) {
124
+ // 阿里云 cn Infisical(独立实例,admin email/password 认证;prod / staging 双环境)
125
+ const cnToken = (0, db_utils_1.getCnInfisicalToken)();
126
+ console.log('✓ Obtained cn Infisical access token');
127
+ secrets = (0, db_utils_1.getCnSecrets)(cnToken, secretPath, false, (0, db_utils_1.cnInfisicalEnv)(environment));
128
+ console.log(`✓ Retrieved secrets from cn Infisical (env: ${(0, db_utils_1.cnInfisicalEnv)(environment)}, path: ${secretPath})\n`);
129
+ }
130
+ else {
131
+ const infisicalConfig = (0, db_utils_1.getInfisicalConfig)();
132
+ console.log('✓ Loaded Infisical config from GitHub Variables');
133
+ const token = (0, db_utils_1.getInfisicalToken)(infisicalConfig);
134
+ console.log('✓ Obtained Infisical access token');
135
+ const infisicalEnv = ENV_MAP[environment];
136
+ secrets = getInfisicalSecrets(infisicalConfig, token, infisicalEnv, secretPath);
137
+ console.log(`✓ Retrieved secrets from Infisical (path: ${secretPath})\n`);
138
+ }
120
139
  const keys = Object.keys(secrets).sort();
121
140
  if (keys.length === 0) {
122
141
  console.log('No environment variables found.');
@@ -66,16 +66,17 @@ const https = __importStar(require("node:https"));
66
66
  // 服务 × 环境 FQDN 表。某服务某环境没部署 → 该 env 键缺省,探时跳过。
67
67
  // cn-prod 真实 subdomain 抄自 optima-terraform alicloud/stacks/cn-prod-ingress-sae/main.tf。
68
68
  // #201 (2026-06-12): yzsgo.com 全量迁移完成,旧 *-cn.optima.chat 路由已下线。
69
+ // cn-stage(阿里云预发)域名 *.stage.optima.chat,抄自 cn-stage-ingress-sae services map。
69
70
  const SERVICES = {
70
- 'user-auth': { path: '/health', stage: 'auth-stage.optima.onl', prod: 'auth.optima.onl', cn: 'auth.yzsgo.com' },
71
- 'agentic-chat': { path: '/api/health', stage: 'ai-stage.optima.onl', prod: 'ai.optima.onl', cn: 'app.yzsgo.com' },
72
- 'commerce-backend': { path: '/health', stage: 'api-stage.optima.onl', prod: 'api.optima.onl', cn: 'commerce.yzsgo.com', cn_path: '/health/live' },
71
+ 'user-auth': { path: '/health', stage: 'auth-stage.optima.onl', prod: 'auth.optima.onl', cn: 'auth.yzsgo.com', 'cn-stage': 'auth.stage.optima.chat' },
72
+ 'agentic-chat': { path: '/api/health', stage: 'ai-stage.optima.onl', prod: 'ai.optima.onl', cn: 'app.yzsgo.com', 'cn-stage': 'app.stage.optima.chat' },
73
+ 'commerce-backend': { path: '/health', stage: 'api-stage.optima.onl', prod: 'api.optima.onl', cn: 'commerce.yzsgo.com', cn_path: '/health/live', 'cn-stage': 'commerce.stage.optima.chat', 'cn-stage_path': '/health/live' },
73
74
  'mcp-host': { path: '/health', stage: 'mcp-stage.optima.onl', prod: 'mcp.optima.onl' },
74
- 'gateway-core': { path: '/health', cn: 'gw.yzsgo.com' },
75
- 'optima-scout': { path: '/health', cn: 'scout.yzsgo.com' },
76
- 'optima-skills': { path: '/health', cn: 'skills.yzsgo.com' },
75
+ 'gateway-core': { path: '/health', cn: 'gw.yzsgo.com', 'cn-stage': 'gw.stage.optima.chat' },
76
+ 'optima-scout': { path: '/health', cn: 'scout.yzsgo.com', 'cn-stage': 'scout.stage.optima.chat' },
77
+ 'optima-skills': { path: '/health', cn: 'skills.yzsgo.com', 'cn-stage': 'skills.stage.optima.chat' },
77
78
  };
78
- const ENVS = ['stage', 'prod', 'cn'];
79
+ const ENVS = ['stage', 'prod', 'cn', 'cn-stage'];
79
80
  const G = '\x1b[32m', R = '\x1b[31m', Y = '\x1b[33m', B = '\x1b[34m', N = '\x1b[0m';
80
81
  const MARK = { ok: `${G}✅${N}`, fail: `${R}❌${N}`, warn: `${Y}⚠️ ${N}`, na: `${B}··${N}` };
81
82
  const verdict = (ls) => ls.some((l) => l.result === 'fail') ? 'fail' : ls.some((l) => l.result === 'warn') ? 'warn' : 'pass';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@optima-chat/dev-skills",
3
- "version": "0.7.43",
3
+ "version": "0.8.1",
4
4
  "description": "Claude Code Skills for Optima development team - cross-environment collaboration tools",
5
5
  "main": "index.js",
6
6
  "bin": {