global-app-skill 0.1.6 → 0.1.7
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/package.json +1 -1
- package/skills/global-app-analysis/SKILL.md +5 -0
- package/src/protocol.mjs +5 -2
- package/src/resources.mjs +4 -4
- package/src/run.mjs +2 -1
package/package.json
CHANGED
|
@@ -16,3 +16,8 @@ npx -y --prefer-online global-app-skill@latest context resolve --mode MODE
|
|
|
16
16
|
CLI 自动发现已安装的 skill 并同步入口,检查 `skillUpdate` 并报告失败;新入口在客户端重新加载后采用。依次读取全部 `mustRead`,以 `resourceRoot` 解析资源并按流程执行。新任务重新 resolve,同一任务沿用已读快照。资源更新或校验失败时停止依赖工作,不静默使用旧资源。
|
|
17
17
|
|
|
18
18
|
脚本通过 `npx -y --prefer-online global-app-skill@latest run SCRIPT --mode MODE --resource-root RESOURCE_ROOT -- 参数` 执行;参数见解析后的 CLI 文档。业务判断与页面操作由 agent 按文档编排,CLI 成功不代表调研完成;关键进度仍须输出到聊天正文。
|
|
19
|
+
|
|
20
|
+
## 必检入口
|
|
21
|
+
|
|
22
|
+
解析资源后,先读取当前 `resourceRoot` 下的 `references/rules.md`。读取失败时暂停依赖该规则的工作。
|
|
23
|
+
到达规则定义的检查点时执行对应校验并记录结果。通过后才可正式或阶段性交付,包括复用、重发已有报告或链接。校验未通过时按规则回退或说明阻塞与恢复条件。
|
package/src/protocol.mjs
CHANGED
|
@@ -7,6 +7,7 @@ export const RELEASE_BASE = 'https://github.com/shiguang-lab/global-app-skill/re
|
|
|
7
7
|
export const FEED = `${RELEASE_BASE}/latest/download/latest.json`;
|
|
8
8
|
export const RELEASE_TAG = /^(?:v|resources-v)(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)$/;
|
|
9
9
|
export const MAX_BYTES = 16 * 1024 * 1024;
|
|
10
|
+
export const RULES_PATH = 'references/rules.md';
|
|
10
11
|
export const MODES = {
|
|
11
12
|
download: 'references/download-workflow.md',
|
|
12
13
|
research: 'references/research-workflow.md',
|
|
@@ -21,7 +22,7 @@ export function validateMode(mode) {
|
|
|
21
22
|
return mode;
|
|
22
23
|
}
|
|
23
24
|
|
|
24
|
-
export function validateManifest(manifest) {
|
|
25
|
+
export function validateManifest(manifest, { requireRules = true } = {}) {
|
|
25
26
|
const hash = /^[a-f0-9]{64}$/;
|
|
26
27
|
if (manifest?.schemaVersion !== 1 || !RELEASE_TAG.test(manifest.releaseTag) || !hash.test(manifest.resourceHash) ||
|
|
27
28
|
!/^\d+\.\d+\.\d+$/.test(manifest.minCliVersion) ||
|
|
@@ -44,7 +45,9 @@ export function validateManifest(manifest) {
|
|
|
44
45
|
if (identity(manifest.files) !== manifest.resourceHash) throw new Error('Resource manifest hash mismatch');
|
|
45
46
|
const required = ['references/guide.md', 'references/cli.md', 'references/orchestration.md',
|
|
46
47
|
...Object.values(MODES), ...SCRIPTS.map(name => `scripts/${name}.py`), 'scripts/apk_utils.py', 'scripts/progress.py'];
|
|
47
|
-
if (required.
|
|
48
|
+
if (requireRules) required.unshift(RULES_PATH, 'references/delivery-record.md', 'scripts/check_delivery.py');
|
|
49
|
+
const missing = required.filter(name => !seen.has(name));
|
|
50
|
+
if (missing.length) throw new Error(`Resource manifest is missing workflow files: ${missing.join(', ')}`);
|
|
48
51
|
const installed = VERSION.split('.').map(Number);
|
|
49
52
|
const minimum = manifest.minCliVersion.split('.').map(Number);
|
|
50
53
|
for (let i = 0; i < 3; i++) {
|
package/src/resources.mjs
CHANGED
|
@@ -3,7 +3,7 @@ import { homedir } from 'node:os';
|
|
|
3
3
|
import path from 'node:path';
|
|
4
4
|
import { setTimeout as delay } from 'node:timers/promises';
|
|
5
5
|
import { gunzipSync } from 'node:zlib';
|
|
6
|
-
import { bundleUrl, FEED, MAX_BYTES, MODES, sha256, validateManifest, validateMode } from './protocol.mjs';
|
|
6
|
+
import { bundleUrl, FEED, MAX_BYTES, MODES, RULES_PATH, sha256, validateManifest, validateMode } from './protocol.mjs';
|
|
7
7
|
|
|
8
8
|
async function download(url, limit) {
|
|
9
9
|
const response = await fetch(url, { cache: 'no-store', signal: AbortSignal.timeout(30000) });
|
|
@@ -20,12 +20,12 @@ async function download(url, limit) {
|
|
|
20
20
|
return Buffer.concat(chunks);
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
export async function readSnapshot(resourceRoot) {
|
|
23
|
+
export async function readSnapshot(resourceRoot, options) {
|
|
24
24
|
resourceRoot = path.resolve(resourceRoot);
|
|
25
25
|
if (!(await lstat(resourceRoot)).isDirectory()) throw new Error('Resource root must be a real directory');
|
|
26
26
|
const manifestPath = path.join(resourceRoot, 'manifest.json');
|
|
27
27
|
if (!(await lstat(manifestPath)).isFile()) throw new Error('Resource manifest must be a regular file');
|
|
28
|
-
const manifest = validateManifest(JSON.parse(await readFile(manifestPath, 'utf8')));
|
|
28
|
+
const manifest = validateManifest(JSON.parse(await readFile(manifestPath, 'utf8')), options);
|
|
29
29
|
if (path.basename(resourceRoot) !== manifest.resourceHash) throw new Error('Resource root does not match its snapshot hash');
|
|
30
30
|
for (const file of manifest.files) {
|
|
31
31
|
let directory = resourceRoot;
|
|
@@ -107,7 +107,7 @@ export async function resolveContext({ mode, feed = FEED, cacheDir, progress = (
|
|
|
107
107
|
}
|
|
108
108
|
await readSnapshot(resourceRoot);
|
|
109
109
|
progress(updated ? 'Resource snapshot ready' : 'Verified cached snapshot is current');
|
|
110
|
-
const mustRead = ['references/cli.md', 'references/guide.md', 'references/orchestration.md', MODES[mode]]
|
|
110
|
+
const mustRead = [RULES_PATH, 'references/cli.md', 'references/guide.md', 'references/orchestration.md', MODES[mode]]
|
|
111
111
|
.map(name => path.join(resourceRoot, name));
|
|
112
112
|
return { ok: true, source: url.origin === 'https://github.com' ? 'github' : 'custom', feed: url.href,
|
|
113
113
|
mode, releaseTag: manifest.releaseTag, resourceHash: manifest.resourceHash, resourceRoot,
|
package/src/run.mjs
CHANGED
|
@@ -7,7 +7,8 @@ import { readSnapshot, resolveContext } from './resources.mjs';
|
|
|
7
7
|
export async function runScript(script, args, options = {}) {
|
|
8
8
|
if (!SCRIPTS.includes(script)) throw new Error(`Unknown script; choose ${SCRIPTS.join(', ')}`);
|
|
9
9
|
const resourceRoot = options.resourceRoot ? path.resolve(options.resourceRoot) : (await resolveContext(options)).resourceRoot;
|
|
10
|
-
|
|
10
|
+
// A task that already resolved an earlier snapshot may finish on it.
|
|
11
|
+
await readSnapshot(resourceRoot, { requireRules: !options.resourceRoot });
|
|
11
12
|
await promisify(execFile)('python3', ['-c', 'import sys; sys.exit(0 if sys.version_info >= (3, 9) else 1)'])
|
|
12
13
|
.catch(() => { throw new Error('Python >=3.9 must be available as python3'); });
|
|
13
14
|
return new Promise((resolve, reject) => {
|