release-skill 0.9.26 → 0.9.28
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/.claude-plugin/plugin.json +1 -1
- package/.codebuddy-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +2 -2
- package/.cursor-plugin/plugin.json +1 -1
- package/.kimi-plugin/plugin.json +1 -1
- package/.qoder-plugin/plugin.json +1 -1
- package/CHANGELOG.md +56 -0
- package/INSTALL.md +2 -2
- package/INSTALL.zh-CN.md +2 -2
- package/README.md +16 -10
- package/README.zh-CN.md +16 -10
- package/adapters/claude/.claude-plugin/plugin.json +1 -1
- package/adapters/claude/bin/consumer-contract-vectors.json +7 -7
- package/adapters/claude/bin/foundation-resource-binding.json +1 -1
- package/adapters/claude/bin/release-skill.bundle.mjs +2631 -1678
- package/adapters/claude/skills/release-assess/SKILL.md +64 -1
- package/adapters/claude/skills/release-finish/SKILL.md +2 -0
- package/adapters/claude/skills/release-help/SKILL.md +2 -2
- package/adapters/claude/skills/release-setup/SKILL.md +5 -2
- package/adapters/codex/.codex-plugin/plugin.json +2 -2
- package/adapters/codex/bin/consumer-contract-vectors.json +7 -7
- package/adapters/codex/bin/foundation-resource-binding.json +1 -1
- package/adapters/codex/bin/release-skill.bundle.mjs +2631 -1678
- package/adapters/codex/skills/release-assess/SKILL.md +64 -1
- package/adapters/codex/skills/release-finish/SKILL.md +2 -0
- package/adapters/codex/skills/release-help/SKILL.md +2 -2
- package/adapters/codex/skills/release-setup/SKILL.md +5 -2
- package/adapters/cursor/.cursor-plugin/plugin.json +1 -1
- package/adapters/cursor/bin/consumer-contract-vectors.json +7 -7
- package/adapters/cursor/bin/foundation-resource-binding.json +1 -1
- package/adapters/cursor/bin/release-skill.bundle.mjs +2631 -1678
- package/adapters/cursor/skills/release-assess/SKILL.md +64 -1
- package/adapters/cursor/skills/release-finish/SKILL.md +2 -0
- package/adapters/cursor/skills/release-help/SKILL.md +2 -2
- package/adapters/cursor/skills/release-setup/SKILL.md +5 -2
- package/adapters/kimi/.kimi-plugin/plugin.json +1 -1
- package/adapters/kimi/bin/consumer-contract-vectors.json +7 -7
- package/adapters/kimi/bin/foundation-resource-binding.json +1 -1
- package/adapters/kimi/bin/release-skill.bundle.mjs +2631 -1678
- package/adapters/kimi/skills/release-assess/SKILL.md +64 -1
- package/adapters/kimi/skills/release-finish/SKILL.md +2 -0
- package/adapters/kimi/skills/release-help/SKILL.md +2 -2
- package/adapters/kimi/skills/release-setup/SKILL.md +5 -2
- package/adapters/qoder/.qoder-plugin/plugin.json +1 -1
- package/adapters/qoder/bin/consumer-contract-vectors.json +7 -7
- package/adapters/qoder/bin/foundation-resource-binding.json +1 -1
- package/adapters/qoder/bin/release-skill.bundle.mjs +2631 -1678
- package/adapters/qoder/skills/release-assess/SKILL.md +64 -1
- package/adapters/qoder/skills/release-finish/SKILL.md +2 -0
- package/adapters/qoder/skills/release-help/SKILL.md +2 -2
- package/adapters/qoder/skills/release-setup/SKILL.md +5 -2
- package/adapters/workbuddy/.codebuddy-plugin/plugin.json +1 -1
- package/adapters/workbuddy/bin/consumer-contract-vectors.json +7 -7
- package/adapters/workbuddy/bin/foundation-resource-binding.json +1 -1
- package/adapters/workbuddy/bin/release-skill.bundle.mjs +2631 -1678
- package/adapters/workbuddy/skills/release-assess/SKILL.md +64 -1
- package/adapters/workbuddy/skills/release-finish/SKILL.md +2 -0
- package/adapters/workbuddy/skills/release-help/SKILL.md +2 -2
- package/adapters/workbuddy/skills/release-setup/SKILL.md +5 -2
- package/bin/consumer-contract-vectors.json +7 -7
- package/bin/foundation-resource-binding.json +1 -1
- package/bin/release-skill-cli.mjs +45 -1
- package/bin/release-skill.bundle.mjs +2631 -1678
- package/package.json +4 -4
- package/platform-manifest.json +4 -4
- package/skills/release-assess/SKILL.md +64 -1
- package/skills/release-finish/SKILL.md +2 -0
- package/skills/release-help/SKILL.md +2 -2
- package/skills/release-setup/SKILL.md +5 -2
- package/skills-src/release-assess/SKILL.md +64 -1
- package/skills-src/release-finish/SKILL.md +2 -0
- package/skills-src/release-help/SKILL.md +2 -2
- package/skills-src/release-setup/SKILL.md +5 -2
- package/src/commands/post-release-local.mjs +36 -92
- package/src/commands/setup.mjs +149 -6
- package/src/core/adoption-assessment.mjs +502 -4
- package/src/core/local-host-observation.mjs +225 -0
- package/src/platforms/registry.mjs +12 -4
- package/src/producers/foundation-resource-projection.mjs +3 -3
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared local host process and list-observation helpers.
|
|
3
|
+
*
|
|
4
|
+
* Extracted so post-release local updates and host-coverage diagnosis reuse
|
|
5
|
+
* one process runner and the platform registry list strategies. Callers pass
|
|
6
|
+
* host/plugin/marketplace identity; command binaries and argv never come from
|
|
7
|
+
* caller-controlled input.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { homedir } from 'node:os';
|
|
11
|
+
import { join } from 'node:path';
|
|
12
|
+
|
|
13
|
+
import { superviseProcess, withTemporaryWorkspace } from 'skill-family-harness-node';
|
|
14
|
+
|
|
15
|
+
import { getPlatform, normalizeHostId } from '../platforms/registry.mjs';
|
|
16
|
+
|
|
17
|
+
export const OBSERVABLE_HOSTS = Object.freeze(['claude', 'codex']);
|
|
18
|
+
|
|
19
|
+
const SAFE_ENV_KEYS = Object.freeze([
|
|
20
|
+
'PATH', 'HOME', 'USER', 'LANG', 'LC_ALL', 'LC_CTYPE', 'TERM', 'TMPDIR',
|
|
21
|
+
'HTTP_PROXY', 'HTTPS_PROXY', 'ALL_PROXY', 'NO_PROXY', 'http_proxy', 'https_proxy', 'all_proxy', 'no_proxy',
|
|
22
|
+
'SSL_CERT_FILE', 'SSL_CERT_DIR', 'NODE_EXTRA_CA_CERTS',
|
|
23
|
+
'CLAUDE_CONFIG_DIR', 'CODEX_HOME', 'KIMI_CODE_HOME',
|
|
24
|
+
'CODEBUDDY_CONFIG_DIR', 'WORKBUDDY_CONFIG_DIR',
|
|
25
|
+
]);
|
|
26
|
+
|
|
27
|
+
export function attachFoundationFailure(error, { envelope, stdout, stderr }) {
|
|
28
|
+
Object.defineProperties(error, {
|
|
29
|
+
foundationEnvelope: { value: envelope, enumerable: false },
|
|
30
|
+
foundationStdout: { value: stdout, enumerable: false },
|
|
31
|
+
foundationStderr: { value: stderr, enumerable: false },
|
|
32
|
+
});
|
|
33
|
+
return error;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function hostEnvironment(host, { kimiHome } = {}) {
|
|
37
|
+
const env = {};
|
|
38
|
+
for (const key of SAFE_ENV_KEYS) {
|
|
39
|
+
if (process.env[key] !== undefined) env[key] = process.env[key];
|
|
40
|
+
}
|
|
41
|
+
env.HOME ??= homedir();
|
|
42
|
+
env.PATH ??= '/usr/bin:/bin';
|
|
43
|
+
env.GIT_TERMINAL_PROMPT = '0';
|
|
44
|
+
if (host === 'codebuddy') env.CODEBUDDY_CONFIG_DIR = join(env.HOME, '.codebuddy');
|
|
45
|
+
if (host === 'workbuddy') {
|
|
46
|
+
env.CODEBUDDY_CONFIG_DIR = join(env.HOME, '.workbuddy');
|
|
47
|
+
env.WORKBUDDY_CONFIG_DIR = join(env.HOME, '.workbuddy');
|
|
48
|
+
env.CODEBUDDY_FORCE_LITE_WB_BUNDLE = '1';
|
|
49
|
+
}
|
|
50
|
+
if (host === 'kimi' && kimiHome) env.KIMI_CODE_HOME = kimiHome;
|
|
51
|
+
return env;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export async function runLocalFinishCommand(command, args, options = {}) {
|
|
55
|
+
return withTemporaryWorkspace(async (workspace) => {
|
|
56
|
+
const stdoutFile = 'stdout.log';
|
|
57
|
+
const stderrFile = 'stderr.log';
|
|
58
|
+
const envelope = await superviseProcess({
|
|
59
|
+
command,
|
|
60
|
+
args,
|
|
61
|
+
cwd: options.cwd ?? process.cwd(),
|
|
62
|
+
env: options.env ?? hostEnvironment('generic'),
|
|
63
|
+
timeoutPolicy: {
|
|
64
|
+
maxSeconds: Math.max(1, Math.ceil((options.timeout ?? 120_000) / 1000)),
|
|
65
|
+
killGraceSeconds: 5,
|
|
66
|
+
},
|
|
67
|
+
rawSink: { root: workspace.root, stdoutFile, stderrFile },
|
|
68
|
+
outputByteLimits: { stdout: 8 * 1024 * 1024, stderr: 8 * 1024 * 1024 },
|
|
69
|
+
});
|
|
70
|
+
const [stdout, stderr] = await Promise.all([
|
|
71
|
+
workspace.readFile(stdoutFile, { encoding: 'utf8' }),
|
|
72
|
+
workspace.readFile(stderrFile, { encoding: 'utf8' }),
|
|
73
|
+
]);
|
|
74
|
+
if (!envelope.ok) {
|
|
75
|
+
const error = new Error(
|
|
76
|
+
`${command} exited ${envelope.exitStatus}${stderr.trim() ? `: ${stderr.trim()}` : ''}`,
|
|
77
|
+
);
|
|
78
|
+
error.exitStatus = envelope.exitStatus;
|
|
79
|
+
error.hostCommandUnavailable = envelope.processStatus === 'FAILED_TO_START';
|
|
80
|
+
error.details = {
|
|
81
|
+
processStatus: envelope.processStatus,
|
|
82
|
+
terminationReason: envelope.terminationReason,
|
|
83
|
+
watchdogReason: envelope.watchdogReason,
|
|
84
|
+
...(envelope.evidence?.spawnError ? { spawnError: envelope.evidence.spawnError } : {}),
|
|
85
|
+
};
|
|
86
|
+
throw attachFoundationFailure(error, { envelope, stdout, stderr });
|
|
87
|
+
}
|
|
88
|
+
return { stdout, stderr };
|
|
89
|
+
}, { prefix: 'release-skill-host-command-' });
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export function parseHostCommandJson(stdout, label) {
|
|
93
|
+
try {
|
|
94
|
+
return JSON.parse(stdout);
|
|
95
|
+
} catch {
|
|
96
|
+
throw new Error(`${label} did not return JSON`);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export function interpretPluginListOutput(host, stdout, { plugin, marketplace } = {}) {
|
|
101
|
+
if (typeof plugin !== 'string' || plugin.length === 0 || typeof marketplace !== 'string' || marketplace.length === 0) {
|
|
102
|
+
return { state: 'unknown', reason: 'plugin identity is missing' };
|
|
103
|
+
}
|
|
104
|
+
let parsed;
|
|
105
|
+
try {
|
|
106
|
+
parsed = parseHostCommandJson(stdout, `${host} plugin list`);
|
|
107
|
+
} catch (error) {
|
|
108
|
+
return { state: 'unknown', reason: error.message };
|
|
109
|
+
}
|
|
110
|
+
let platform;
|
|
111
|
+
try {
|
|
112
|
+
platform = getPlatform(host);
|
|
113
|
+
} catch (error) {
|
|
114
|
+
return { state: 'unknown', reason: error.message };
|
|
115
|
+
}
|
|
116
|
+
if (typeof platform.strategy?.parseListOutput !== 'function') {
|
|
117
|
+
return { state: 'unknown', reason: 'host observation is not implemented' };
|
|
118
|
+
}
|
|
119
|
+
const selector = `${plugin}@${marketplace}`;
|
|
120
|
+
const observed = platform.strategy.parseListOutput(parsed, selector);
|
|
121
|
+
if (!observed.ok) {
|
|
122
|
+
if (String(observed.error ?? '').includes('not found')) {
|
|
123
|
+
return { state: 'not-installed', reason: observed.error };
|
|
124
|
+
}
|
|
125
|
+
return { state: 'unknown', reason: observed.error };
|
|
126
|
+
}
|
|
127
|
+
const identity = typeof platform.strategy.extractListIdentity === 'function'
|
|
128
|
+
? platform.strategy.extractListIdentity(observed.found)
|
|
129
|
+
: {};
|
|
130
|
+
if (identity.plugin && identity.plugin !== plugin) {
|
|
131
|
+
return {
|
|
132
|
+
state: 'unknown',
|
|
133
|
+
reason: 'list identity plugin does not match',
|
|
134
|
+
identity,
|
|
135
|
+
found: observed.found,
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
if (identity.marketplace && identity.marketplace !== marketplace) {
|
|
139
|
+
return {
|
|
140
|
+
state: 'unknown',
|
|
141
|
+
reason: 'list identity marketplace does not match',
|
|
142
|
+
identity,
|
|
143
|
+
found: observed.found,
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
return {
|
|
147
|
+
state: 'installed',
|
|
148
|
+
identity,
|
|
149
|
+
found: observed.found,
|
|
150
|
+
...(observed.installPath ? { installPath: observed.installPath } : {}),
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
export async function observeHostPlugin({
|
|
155
|
+
host,
|
|
156
|
+
plugin,
|
|
157
|
+
marketplace,
|
|
158
|
+
run = runLocalFinishCommand,
|
|
159
|
+
env,
|
|
160
|
+
clock = () => new Date().toISOString(),
|
|
161
|
+
} = {}) {
|
|
162
|
+
let canonical = host;
|
|
163
|
+
try {
|
|
164
|
+
canonical = await normalizeHostId(host);
|
|
165
|
+
} catch {
|
|
166
|
+
return {
|
|
167
|
+
host,
|
|
168
|
+
plugin,
|
|
169
|
+
marketplace,
|
|
170
|
+
state: 'unknown',
|
|
171
|
+
source: 'setup --observe-hosts',
|
|
172
|
+
observedAt: clock(),
|
|
173
|
+
reason: 'host id could not be normalized',
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
try {
|
|
177
|
+
canonical = getPlatform(canonical).id;
|
|
178
|
+
} catch {
|
|
179
|
+
try {
|
|
180
|
+
canonical = getPlatform(host).id;
|
|
181
|
+
} catch {
|
|
182
|
+
// Keep the Foundation identity when the local registry has no matching platform.
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
if (!OBSERVABLE_HOSTS.includes(canonical)) {
|
|
186
|
+
return {
|
|
187
|
+
host: canonical,
|
|
188
|
+
plugin,
|
|
189
|
+
marketplace,
|
|
190
|
+
state: 'unknown',
|
|
191
|
+
source: 'setup --observe-hosts',
|
|
192
|
+
observedAt: clock(),
|
|
193
|
+
reason: 'host observation is not implemented',
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
const platform = getPlatform(canonical);
|
|
197
|
+
const command = platform.cli.binary;
|
|
198
|
+
const args = platform.cli.list();
|
|
199
|
+
try {
|
|
200
|
+
const listed = await run(command, [...args], {
|
|
201
|
+
env: env ?? hostEnvironment(canonical),
|
|
202
|
+
});
|
|
203
|
+
const interpreted = interpretPluginListOutput(canonical, listed.stdout, { plugin, marketplace });
|
|
204
|
+
return {
|
|
205
|
+
host: canonical,
|
|
206
|
+
plugin,
|
|
207
|
+
marketplace,
|
|
208
|
+
state: interpreted.state,
|
|
209
|
+
...(interpreted.identity?.version ? { version: interpreted.identity.version } : {}),
|
|
210
|
+
source: 'setup --observe-hosts',
|
|
211
|
+
observedAt: clock(),
|
|
212
|
+
...(interpreted.reason ? { reason: interpreted.reason } : {}),
|
|
213
|
+
};
|
|
214
|
+
} catch (error) {
|
|
215
|
+
return {
|
|
216
|
+
host: canonical,
|
|
217
|
+
plugin,
|
|
218
|
+
marketplace,
|
|
219
|
+
state: 'unknown',
|
|
220
|
+
source: 'setup --observe-hosts',
|
|
221
|
+
observedAt: clock(),
|
|
222
|
+
reason: error?.message ?? 'host list failed',
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
}
|
|
@@ -57,10 +57,14 @@ function claudeParseListOutput(listOutput, pluginId) {
|
|
|
57
57
|
if (!Array.isArray(listOutput)) {
|
|
58
58
|
return { ok: false, error: 'Claude plugin list did not return an array' };
|
|
59
59
|
}
|
|
60
|
-
const
|
|
61
|
-
if (
|
|
60
|
+
const matches = listOutput.filter((p) => p.id === pluginId);
|
|
61
|
+
if (matches.length === 0) {
|
|
62
62
|
return { ok: false, error: `plugin "${pluginId}" not found in Claude plugin list` };
|
|
63
63
|
}
|
|
64
|
+
if (matches.length !== 1) {
|
|
65
|
+
return { ok: false, error: `plugin "${pluginId}" has duplicate Claude list entries` };
|
|
66
|
+
}
|
|
67
|
+
const [found] = matches;
|
|
64
68
|
if (!found.installPath) {
|
|
65
69
|
return { ok: false, error: `plugin "${pluginId}" found but missing installPath` };
|
|
66
70
|
}
|
|
@@ -93,10 +97,14 @@ function codexParseListOutput(listOutput, pluginId) {
|
|
|
93
97
|
if (!Array.isArray(installed)) {
|
|
94
98
|
return { ok: false, error: 'Codex plugin list did not return {installed: [...]}' };
|
|
95
99
|
}
|
|
96
|
-
const
|
|
97
|
-
if (
|
|
100
|
+
const matches = installed.filter((p) => p.pluginId === pluginId);
|
|
101
|
+
if (matches.length === 0) {
|
|
98
102
|
return { ok: false, error: `plugin "${pluginId}" not found in Codex installed list` };
|
|
99
103
|
}
|
|
104
|
+
if (matches.length !== 1) {
|
|
105
|
+
return { ok: false, error: `plugin "${pluginId}" has duplicate Codex list entries` };
|
|
106
|
+
}
|
|
107
|
+
const [found] = matches;
|
|
100
108
|
return { ok: true, found };
|
|
101
109
|
}
|
|
102
110
|
|
|
@@ -55,9 +55,9 @@ import { digestBytes, publishFileOrReplace, readFileStrict, withTemporaryWorkspa
|
|
|
55
55
|
import { canonicalJson } from '../core/digest.mjs';
|
|
56
56
|
|
|
57
57
|
export const FOUNDATION_PACKAGES = Object.freeze({
|
|
58
|
-
'skill-family-contracts': '0.
|
|
59
|
-
'skill-family-engineering-kit': '0.
|
|
60
|
-
'skill-family-harness-node': '0.
|
|
58
|
+
'skill-family-contracts': '0.25.0',
|
|
59
|
+
'skill-family-engineering-kit': '0.25.0',
|
|
60
|
+
'skill-family-harness-node': '0.25.0',
|
|
61
61
|
});
|
|
62
62
|
|
|
63
63
|
export const BINDING_RECORD_PATH = 'bin/foundation-resource-binding.json';
|