global-app-skill 0.1.5 → 0.1.6

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/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # global-app-skill
2
2
 
3
- 海外 Android App 调研 skill 的薄入口、资源更新器和脚本 CLI。保持 `overseas-app-research` 的下载、业务调研与 UED 图册三个分支。
3
+ 海外 Android App 调研 skill 的薄入口、资源更新器和脚本 CLI。保持 `global-app-analysis` 的下载、业务调研与 UED 图册三个分支。
4
4
 
5
5
  ## 安装
6
6
 
@@ -10,7 +10,7 @@
10
10
  npx -y --prefer-online global-app-skill@latest skill install
11
11
  ```
12
12
 
13
- 默认只安装 `~/.agents/skills/overseas-app-research/SKILL.md`。也可用 `--dir /absolute/skills/overseas-app-research` 指定当前客户端实际使用的 skill 目录。所有目录使用完全相同的薄入口,只记录 CLI 版本,不写入安装路径。安装位置记录在本机状态目录,供 CLI 后续自动发现;该记录不放进 skill。已存在的 SKILL.md 会被原子替换,目录中的其他文件保留,符号链接入口拒绝覆盖。安装不下载 references,也不修改 MCP 或客户端配置。
13
+ 默认只安装 `~/.agents/skills/global-app-analysis/SKILL.md`。也可用 `--dir /absolute/skills/global-app-analysis` 指定当前客户端实际使用的 skill 目录。所有目录使用完全相同的薄入口,只记录 CLI 版本,不写入安装路径。安装位置记录在本机状态目录,供 CLI 后续自动发现;该记录不放进 skill。已存在的 SKILL.md 会被原子替换,目录中的其他文件保留,符号链接入口拒绝覆盖。安装不下载 references,也不修改 MCP 或客户端配置。
14
14
 
15
15
  ## 执行
16
16
 
@@ -22,7 +22,7 @@ npx -y --prefer-online global-app-skill@latest context resolve --mode research
22
22
 
23
23
  `context resolve` 和 `run` 执行前由 CLI 自动定位已安装的薄入口,更新为当前 CLI 捆绑的版本。入口只调用通用命令,不传安装路径。入口使用 `npx --prefer-online ...@latest`,因此发布新版 CLI 后,下一次执行会更新本地 SKILL.md。
24
24
 
25
- CLI 检查以下位置中的 `overseas-app-research/SKILL.md`:用户目录下的 `.agents/skills`、`.codex/skills`、`.claude/skills`、`.cursor/skills`、`.gemini/skills`、`.kiro/skills`;当前目录至最近 Git 仓库根目录之间的相同项目级目录;`CODEX_HOME/skills`;以及 CLI 安装时登记的目录。不在 Git 仓库内时,项目级发现只检查当前目录,不遍历其他工作区。
25
+ CLI 检查以下位置中的 `global-app-analysis/SKILL.md`:用户目录下的 `.agents/skills`、`.codex/skills`、`.claude/skills`、`.cursor/skills`、`.gemini/skills`、`.kiro/skills`;当前目录至最近 Git 仓库根目录之间的相同项目级目录;`CODEX_HOME/skills`;以及 CLI 安装时登记的目录。不在 Git 仓库内时,项目级发现只检查当前目录,不遍历其他工作区。
26
26
 
27
27
  自定义安装目录通过 `skill install --dir ...` 登记在 `${XDG_STATE_HOME:-~/.local/state}/global-app-skill/installations/`,不需要把路径写进 SKILL.md。复制到另一台机器的标准 skill 目录后,CLI 会按该机器环境重新发现;移到标准目录以外的位置时,再运行一次安装命令登记新位置。`--skill-dir` 仅保留为手动指定本次更新目标的可选参数,薄入口无需使用。
28
28
 
@@ -42,11 +42,11 @@ npx -y --prefer-online global-app-skill@latest run preflight --mode research --r
42
42
 
43
43
  | 路径 | 职责 |
44
44
  |---|---|
45
- | `skills/overseas-app-research/SKILL.md` | 安装到客户端的薄入口 |
45
+ | `skills/global-app-analysis/SKILL.md` | 安装到客户端的薄入口 |
46
46
  | `src/` | npm CLI:安装、资源解析、校验、脚本运行 |
47
- | `resources/overseas-app-research/references/` | 主流程和各阶段操作文档 |
48
- | `resources/overseas-app-research/scripts/` | 原有 Python 脚本及回归测试 |
49
- | `resources/overseas-app-research/examples/` | 业务报告模板 |
47
+ | `resources/global-app-analysis/references/` | 主流程和各阶段操作文档 |
48
+ | `resources/global-app-analysis/scripts/` | 原有 Python 脚本及回归测试 |
49
+ | `resources/global-app-analysis/examples/` | 业务报告模板 |
50
50
  | `scripts/build-resources.mjs` | 生成确定性的资源包与清单 |
51
51
  | `.github/workflows/publish.yml` | tag 触发的构建、GitHub Release 和 npm 发布 |
52
52
  | `scripts/release.mjs` | tag 校验、打包、校验和及发布资产核对 |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "global-app-skill",
3
- "version": "0.1.5",
3
+ "version": "0.1.6",
4
4
  "description": "Thin skill bootstrap and GitHub resource runner for overseas Android app research",
5
5
  "type": "module",
6
6
  "license": "UNLICENSED",
@@ -14,7 +14,7 @@
14
14
  "publishConfig": { "registry": "https://registry.npmjs.org/", "access": "public" },
15
15
  "scripts": {
16
16
  "test": "node --test tests/*.test.mjs",
17
- "test:python": "python3 -m unittest discover -s resources/overseas-app-research/scripts -p 'test_*.py'",
17
+ "test:python": "python3 -m unittest discover -s resources/global-app-analysis/scripts -p 'test_*.py'",
18
18
  "resources:build": "node scripts/build-resources.mjs",
19
19
  "resources:check": "node scripts/build-resources.mjs --check",
20
20
  "release:prepare": "node scripts/release.mjs",
@@ -1,9 +1,9 @@
1
1
  ---
2
- name: overseas-app-research
2
+ name: global-app-analysis
3
3
  description: 调研海外 Android App 的业务流程、功能入口、字段权限,或为 UED 提取整款 App/指定范围的原始界面图册;也支持仅下载/导出 APK。
4
4
  ---
5
5
 
6
- # 海外 App 调研
6
+ # 海外 App 分析
7
7
 
8
8
  需要 Node.js >=22.12、`python3` >=3.9;设备/AVD 门槛由解析后的工作流检查。
9
9
 
package/src/cli.mjs CHANGED
@@ -3,7 +3,7 @@ import { parseArgs } from 'node:util';
3
3
  import { installSkill, syncInstalledSkills } from './skill.mjs';
4
4
  import { resolveContext } from './resources.mjs';
5
5
  import { runScript } from './run.mjs';
6
- import { validateMode, VERSION } from './protocol.mjs';
6
+ import { SKILL_NAME, validateMode, VERSION } from './protocol.mjs';
7
7
 
8
8
  const usage = `global-app-skill ${VERSION}
9
9
  skill install [--dir ABSOLUTE_SKILL_DIRECTORY]
@@ -11,7 +11,7 @@ const usage = `global-app-skill ${VERSION}
11
11
  run SCRIPT [--mode download|research|ued] [--skill-dir PATH] [--resource-root PATH] [--cache-dir PATH] [--feed URL] -- SCRIPT_ARGS
12
12
  --version
13
13
 
14
- skill install writes only SKILL.md; default: ~/.agents/skills/overseas-app-research.
14
+ skill install writes only SKILL.md; default: ~/.agents/skills/${SKILL_NAME}.
15
15
  context resolve and run synchronize existing managed skill entries from this CLI bundle.
16
16
  Skill locations are discovered from client user/project directories and CLI installation records.
17
17
  run uses a verified pinned snapshot when --resource-root is supplied; otherwise it resolves current resources.
@@ -2,10 +2,9 @@ import { lstat, mkdir, readFile, readdir, rename, rm, writeFile } from 'node:fs/
2
2
  import { randomUUID } from 'node:crypto';
3
3
  import { homedir } from 'node:os';
4
4
  import path from 'node:path';
5
- import { sha256 } from './protocol.mjs';
5
+ import { sha256, SKILL_NAME } from './protocol.mjs';
6
6
 
7
7
  export const SKILL_ROOTS = ['.agents/skills', '.codex/skills', '.claude/skills', '.cursor/skills', '.gemini/skills', '.kiro/skills'];
8
- const skillName = 'overseas-app-research';
9
8
 
10
9
  function stateRoot({ home = homedir(), env = process.env, stateDirectory } = {}) {
11
10
  return path.resolve(stateDirectory || path.join(env.XDG_STATE_HOME || path.join(home, '.local/state'), 'global-app-skill'));
@@ -55,8 +54,8 @@ async function projectDirectories(cwd) {
55
54
  export async function discoverSkillDirectories(options = {}) {
56
55
  const { home = homedir(), env = process.env, cwd = process.cwd() } = options;
57
56
  const bases = [path.resolve(home), ...await projectDirectories(cwd)];
58
- const candidates = bases.flatMap(base => SKILL_ROOTS.map(root => path.join(base, root, skillName)));
59
- if (env.CODEX_HOME) candidates.push(path.resolve(env.CODEX_HOME, 'skills', skillName));
57
+ const candidates = bases.flatMap(base => SKILL_ROOTS.map(root => path.join(base, root, SKILL_NAME)));
58
+ if (env.CODEX_HOME) candidates.push(path.resolve(env.CODEX_HOME, 'skills', SKILL_NAME));
60
59
  candidates.push(...await registeredDirectories(options));
61
60
  const unique = [...new Set(candidates)];
62
61
  const present = await Promise.all(unique.map(directory => exists(path.join(directory, 'SKILL.md'))));
package/src/protocol.mjs CHANGED
@@ -2,6 +2,7 @@ import { createHash } from 'node:crypto';
2
2
  import { readFileSync } from 'node:fs';
3
3
 
4
4
  export const VERSION = JSON.parse(readFileSync(new URL('../package.json', import.meta.url))).version;
5
+ export const SKILL_NAME = 'global-app-analysis';
5
6
  export const RELEASE_BASE = 'https://github.com/shiguang-lab/global-app-skill/releases';
6
7
  export const FEED = `${RELEASE_BASE}/latest/download/latest.json`;
7
8
  export const RELEASE_TAG = /^(?:v|resources-v)(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)$/;
package/src/skill.mjs CHANGED
@@ -3,7 +3,7 @@ import { randomUUID } from 'node:crypto';
3
3
  import { homedir } from 'node:os';
4
4
  import path from 'node:path';
5
5
  import { setTimeout as delay } from 'node:timers/promises';
6
- import { VERSION } from './protocol.mjs';
6
+ import { SKILL_NAME, VERSION } from './protocol.mjs';
7
7
  import { discoverSkillDirectories, rememberInstallation } from './installations.mjs';
8
8
 
9
9
  async function stat(filename) {
@@ -35,7 +35,8 @@ async function writeSkill(target, existingOnly) {
35
35
  let previous;
36
36
  try { previous = await readFile(skillPath, 'utf8'); }
37
37
  catch (error) { if (error.code !== 'ENOENT') throw error; }
38
- if (existingOnly && !previous?.includes('global-app-skill@latest context resolve')) {
38
+ const installedName = previous?.match(/^name: ([a-z0-9-]+)$/m)?.[1];
39
+ if (existingOnly && (installedName !== SKILL_NAME || !previous?.includes('global-app-skill@latest context resolve'))) {
39
40
  return { skillPath, status: 'skipped', reason: 'not-managed' };
40
41
  }
41
42
  const installedVersion = previous?.match(/^<!-- global-app-skill-version: (\d+\.\d+\.\d+) -->$/m)?.[1];
@@ -46,7 +47,7 @@ async function writeSkill(target, existingOnly) {
46
47
  if (installed[i] > bundled[i]) return { skillPath, status: 'skipped', reason: 'newer-installed', installedVersion };
47
48
  }
48
49
  }
49
- const bundled = await readFile(new URL('../skills/overseas-app-research/SKILL.md', import.meta.url), 'utf8');
50
+ const bundled = await readFile(new URL(`../skills/${SKILL_NAME}/SKILL.md`, import.meta.url), 'utf8');
50
51
  const content = bundled.trimEnd() + `\n\n<!-- global-app-skill-version: ${VERSION} -->\n`;
51
52
  if (previous === content) return { skillPath, status: 'unchanged' };
52
53
  const temporary = path.join(target, `.SKILL-${randomUUID()}.tmp`);
@@ -59,7 +60,7 @@ async function writeSkill(target, existingOnly) {
59
60
  }
60
61
 
61
62
  export async function installSkill(directory, options = {}) {
62
- const target = path.resolve(directory || path.join(options.home || homedir(), '.agents/skills/overseas-app-research'));
63
+ const target = path.resolve(directory || path.join(options.home || homedir(), '.agents/skills', SKILL_NAME));
63
64
  const result = await writeSkill(target, false);
64
65
  await rememberInstallation(target, options);
65
66
  return { ok: true, ...result };