@aopslabs/aops 0.2.0
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/LICENSE +19 -0
- package/NOTICE +7 -0
- package/README.md +203 -0
- package/assets/agent-assets/core/SKILL.md +49 -0
- package/assets/agent-assets/core/references/agentspace/SKILL.md +78 -0
- package/assets/agent-assets/core/references/aops-cli-core/SKILL.md +108 -0
- package/assets/agent-assets/core/references/chatv3/SKILL.md +48 -0
- package/assets/agent-assets/core/references/collaborative-work/SKILL.md +85 -0
- package/assets/agent-assets/core/references/discuss/SKILL.md +66 -0
- package/assets/agent-assets/core/references/docman/SKILL.md +69 -0
- package/assets/agent-assets/core/references/hosted-chat/SKILL.md +64 -0
- package/assets/agent-assets/core/references/mission/SKILL.md +54 -0
- package/assets/agent-assets/core/references/projectman/SKILL.md +86 -0
- package/assets/agent-assets/core/references/sys/SKILL.md +36 -0
- package/assets/agent-assets/core/references/view/SKILL.md +58 -0
- package/assets/agent-assets/core/references/working-disciplines/SKILL.md +69 -0
- package/assets/agent-assets/core/user-guides/agent-assets.md +81 -0
- package/assets/agent-assets/core/user-guides/agentspace.md +1098 -0
- package/assets/agent-assets/core/user-guides/aops-cli.md +1107 -0
- package/assets/agent-assets/core/user-guides/aops-system.md +63 -0
- package/assets/agent-assets/core/user-guides/chatv3.md +113 -0
- package/assets/agent-assets/core/user-guides/docman.md +162 -0
- package/assets/agent-assets/core/user-guides/projectman.md +212 -0
- package/assets/agent-assets/core/user-guides/sys.md +94 -0
- package/assets/agent-assets/core/user-guides/working-disciplines.md +588 -0
- package/assets/agent-assets/gateway/aops/SKILL.md +14 -0
- package/assets/r6-remote-session-baseline.json +19 -0
- package/assets/skills/aops-install/SKILL.md +231 -0
- package/assets/skills/aops-install/agents/openai.yaml +4 -0
- package/dist/commands/activity.js +352 -0
- package/dist/commands/agent-profile.js +653 -0
- package/dist/commands/agent.js +710 -0
- package/dist/commands/agents-md.js +125 -0
- package/dist/commands/api.js +84 -0
- package/dist/commands/archive.js +873 -0
- package/dist/commands/artifact.js +535 -0
- package/dist/commands/assets.js +1414 -0
- package/dist/commands/auth/device-login.js +1 -0
- package/dist/commands/auth/legacy-export.js +177 -0
- package/dist/commands/auth/login.js +426 -0
- package/dist/commands/chat.js +1363 -0
- package/dist/commands/chatv3.js +1684 -0
- package/dist/commands/checkpoint.js +549 -0
- package/dist/commands/commercial-artifact.js +475 -0
- package/dist/commands/commercial-license.js +594 -0
- package/dist/commands/community-auth.js +62 -0
- package/dist/commands/community-cockpit.js +185 -0
- package/dist/commands/community-console.js +109 -0
- package/dist/commands/community-doctor.js +335 -0
- package/dist/commands/community-server.js +2062 -0
- package/dist/commands/community-setup.js +284 -0
- package/dist/commands/discuss.js +1448 -0
- package/dist/commands/doc.js +5219 -0
- package/dist/commands/experience.js +922 -0
- package/dist/commands/host.js +1127 -0
- package/dist/commands/init.js +118 -0
- package/dist/commands/memory.js +3129 -0
- package/dist/commands/mission.js +562 -0
- package/dist/commands/official-catalog.js +134 -0
- package/dist/commands/plan.js +84 -0
- package/dist/commands/playbook.js +415 -0
- package/dist/commands/pm/context.js +6 -0
- package/dist/commands/pm/index.js +701 -0
- package/dist/commands/pm/projectman.js +6266 -0
- package/dist/commands/project.js +1080 -0
- package/dist/commands/prompt.js +1024 -0
- package/dist/commands/repo-sync.js +1907 -0
- package/dist/commands/resource.js +527 -0
- package/dist/commands/skill.js +1211 -0
- package/dist/commands/start-disciplines.js +510 -0
- package/dist/commands/start.js +1343 -0
- package/dist/commands/target.js +309 -0
- package/dist/commands/version.js +42 -0
- package/dist/commands/view.js +1852 -0
- package/dist/launchers/cockpit.js +28 -0
- package/dist/launchers/options.js +74 -0
- package/dist/launchers/server.js +41 -0
- package/dist/lib/agent-assets/envelope.js +83 -0
- package/dist/lib/agent-assets/gateway.js +15 -0
- package/dist/lib/agent-assets/guards.js +23 -0
- package/dist/lib/agent-assets/hosted-discovery.js +148 -0
- package/dist/lib/agent-assets/hosted-package-input.js +154 -0
- package/dist/lib/agent-assets/legacy-pointer-migration.js +677 -0
- package/dist/lib/agent-assets/native-fs.js +589 -0
- package/dist/lib/agent-assets/package-manifest.js +324 -0
- package/dist/lib/agent-assets/portable-path.js +67 -0
- package/dist/lib/agent-assets/release-input.js +177 -0
- package/dist/lib/agent-assets/roots.js +45 -0
- package/dist/lib/agent-assets/runtime-binding-reader.js +545 -0
- package/dist/lib/agent-assets/runtime-targets.js +50 -0
- package/dist/lib/agent-assets/store-reader.js +1212 -0
- package/dist/lib/agent-assets/store-types.js +1 -0
- package/dist/lib/agent-assets/store-writer.js +1484 -0
- package/dist/lib/agent-assets/types.js +1 -0
- package/dist/lib/agent-assets/unicode-case-folding-v15-1.generated.js +1548 -0
- package/dist/lib/commercial-final-readiness.js +504 -0
- package/dist/lib/commercial-lifecycle-smoke-isolation.js +236 -0
- package/dist/lib/commercial-managed-postgres-docker-adapter.js +274 -0
- package/dist/lib/commercial-setup-readiness-adapter.js +179 -0
- package/dist/lib/community-client-contract.js +164 -0
- package/dist/lib/community-cockpit-lifecycle.js +601 -0
- package/dist/lib/community-command-abort.js +59 -0
- package/dist/lib/community-diagnostic.js +237 -0
- package/dist/lib/community-docker-adapter.js +498 -0
- package/dist/lib/community-home.js +31 -0
- package/dist/lib/community-instance-contract.js +148 -0
- package/dist/lib/community-instance-layout.js +97 -0
- package/dist/lib/community-lifecycle.js +1631 -0
- package/dist/lib/community-migration-snapshot.js +770 -0
- package/dist/lib/community-native-application-recovery.js +425 -0
- package/dist/lib/community-native-child.js +240 -0
- package/dist/lib/community-native-database-recovery.js +456 -0
- package/dist/lib/community-native-lifecycle.js +2458 -0
- package/dist/lib/community-native-migration.js +339 -0
- package/dist/lib/community-native-postgres.js +596 -0
- package/dist/lib/community-operation-journal.js +773 -0
- package/dist/lib/community-operation-lock.js +596 -0
- package/dist/lib/community-release-resolver.js +291 -0
- package/dist/lib/community-release-verifier.js +475 -0
- package/dist/lib/community-repo-discovery.js +252 -0
- package/dist/lib/community-setup-server-env.js +203 -0
- package/dist/lib/external-url.js +110 -0
- package/dist/lib/official-catalog-gateway.js +108 -0
- package/dist/lib/official-catalog.js +364 -0
- package/dist/lib/server-bootstrap.js +240 -0
- package/dist/lib/setup-agent-assets-bridge.js +176 -0
- package/dist/lib/setup-agent-assets-release.js +52 -0
- package/dist/lib/setup-external-postgres.js +108 -0
- package/dist/lib/setup-init-orchestrator.js +990 -0
- package/dist/lib/setup-install-guide.js +52 -0
- package/dist/lib/setup-installer-release.js +47 -0
- package/dist/lib/setup-local-postgres.js +329 -0
- package/dist/lib/setup-official-catalog-bridge.js +148 -0
- package/dist/lib/setup-readiness.js +888 -0
- package/dist/lib/tui-launcher.js +59 -0
- package/dist/main.js +150 -0
- package/dist/seeds/agents-md.js +54 -0
- package/dist/utils/agent-gateway.js +275 -0
- package/dist/utils/agents-md.js +281 -0
- package/dist/utils/api.js +255 -0
- package/dist/utils/chatv3-session-store.js +185 -0
- package/dist/utils/command.js +67 -0
- package/dist/utils/community-host-runtime.js +376 -0
- package/dist/utils/config.js +1015 -0
- package/dist/utils/discussion-workspace.js +552 -0
- package/dist/utils/experience-workspace.js +326 -0
- package/dist/utils/guide-paths.js +67 -0
- package/dist/utils/hosted-sugar.js +56 -0
- package/dist/utils/hosted-workspace.js +229 -0
- package/dist/utils/memory-workspace.js +537 -0
- package/dist/utils/operator-runtime.js +199 -0
- package/dist/utils/password-crypto.js +39 -0
- package/dist/utils/playbook-workspace.js +479 -0
- package/dist/utils/project-context.js +255 -0
- package/dist/utils/prompts.js +70 -0
- package/dist/utils/refresh-race.js +28 -0
- package/dist/utils/repo-config.js +109 -0
- package/dist/utils/repo-first-projectman.js +194 -0
- package/dist/utils/repo-first-storage.js +13 -0
- package/dist/utils/repo-first-view.js +305 -0
- package/dist/utils/session-guidance.js +278 -0
- package/dist/utils/session-state.js +137 -0
- package/dist/utils/table.js +39 -0
- package/dist/utils/transient-fs.js +49 -0
- package/dist/utils/workspace-root.js +1 -0
- package/launchers/aops-cockpit.ps1 +15 -0
- package/launchers/aops-cockpit.sh +16 -0
- package/launchers/aops-server.ps1 +15 -0
- package/launchers/aops-server.sh +16 -0
- package/native/tui/win32-x64/aops-tui.exe +0 -0
- package/package.json +81 -0
|
@@ -0,0 +1,1211 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
2
|
+
import { Command } from 'commander';
|
|
3
|
+
import { logError, logSuccess } from '@aopslabs/xf-cli-ui';
|
|
4
|
+
import { applyCommonOptions, compactPayload, normalizeNonEmpty } from '../utils/command.js';
|
|
5
|
+
import { invokeHostedToolWithApiState, parseJsonInput, requireApiState, unwrapHostedToolResult, } from '../utils/agent-gateway.js';
|
|
6
|
+
import { resolveOwnerScopeIdFromBinding, resolveOwnerScopeIdFromProjectRecord, resolveProjectBindingContext, } from '../utils/project-context.js';
|
|
7
|
+
import { buildHostedSugarEnvelope, buildOperatorCookbook, ensureDestructiveWrite, ensureGuardedWrite, missingScopeIdMessage, resolveNextVersionNumber, } from '../utils/hosted-sugar.js';
|
|
8
|
+
import { GUIDE_PATHS } from '../utils/guide-paths.js';
|
|
9
|
+
function isRecord(value) {
|
|
10
|
+
return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
|
|
11
|
+
}
|
|
12
|
+
function unwrapResultData(result) {
|
|
13
|
+
if (isRecord(result) && Object.prototype.hasOwnProperty.call(result, 'data')) {
|
|
14
|
+
return result.data;
|
|
15
|
+
}
|
|
16
|
+
return result;
|
|
17
|
+
}
|
|
18
|
+
function unwrapListItems(result) {
|
|
19
|
+
const data = unwrapResultData(result);
|
|
20
|
+
if (Array.isArray(data)) {
|
|
21
|
+
return data.filter((entry) => isRecord(entry));
|
|
22
|
+
}
|
|
23
|
+
if (isRecord(data) && Array.isArray(data.items)) {
|
|
24
|
+
return data.items.filter((entry) => isRecord(entry));
|
|
25
|
+
}
|
|
26
|
+
if (Array.isArray(result)) {
|
|
27
|
+
return result.filter((entry) => isRecord(entry));
|
|
28
|
+
}
|
|
29
|
+
return [];
|
|
30
|
+
}
|
|
31
|
+
function collectRepeatedOption(value, previous = []) {
|
|
32
|
+
const normalized = normalizeNonEmpty(value);
|
|
33
|
+
return normalized ? [...previous, normalized] : previous;
|
|
34
|
+
}
|
|
35
|
+
function toInteger(value, label) {
|
|
36
|
+
if (typeof value === 'number' && Number.isInteger(value))
|
|
37
|
+
return value;
|
|
38
|
+
const normalized = normalizeNonEmpty(value);
|
|
39
|
+
if (!normalized)
|
|
40
|
+
throw new Error(`${label} must be an integer.`);
|
|
41
|
+
const parsed = Number.parseInt(normalized, 10);
|
|
42
|
+
if (!Number.isInteger(parsed))
|
|
43
|
+
throw new Error(`${label} must be an integer.`);
|
|
44
|
+
return parsed;
|
|
45
|
+
}
|
|
46
|
+
export function resolveSkillDiscoveryInput(params) {
|
|
47
|
+
const query = normalizeNonEmpty(params.query);
|
|
48
|
+
if (!query)
|
|
49
|
+
throw new Error('Provide --query <text> or -q <text>.');
|
|
50
|
+
if (query.length > 256 || Buffer.byteLength(query, 'utf8') > 256) {
|
|
51
|
+
throw new Error('--query must be at most 256 characters and 256 UTF-8 bytes.');
|
|
52
|
+
}
|
|
53
|
+
const limit = params.limit === undefined ? undefined : toInteger(params.limit, '--limit');
|
|
54
|
+
if (limit !== undefined && (limit < 1 || limit > 5)) {
|
|
55
|
+
throw new Error('--limit must be between 1 and 5.');
|
|
56
|
+
}
|
|
57
|
+
const scopeResolution = normalizeNonEmpty(params.scopeResolution);
|
|
58
|
+
if (scopeResolution && scopeResolution !== 'explicit' && scopeResolution !== 'cascade') {
|
|
59
|
+
throw new Error('--scope-resolution must be explicit or cascade.');
|
|
60
|
+
}
|
|
61
|
+
return compactPayload({
|
|
62
|
+
query,
|
|
63
|
+
scopeId: normalizeNonEmpty(params.scopeId),
|
|
64
|
+
scopeResolution,
|
|
65
|
+
limit,
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
export function formatSkillSearchResult(result) {
|
|
69
|
+
if (!isRecord(result))
|
|
70
|
+
return 'No published hosted skill matched the query.';
|
|
71
|
+
const candidates = Array.isArray(result.candidates)
|
|
72
|
+
? result.candidates.filter((candidate) => isRecord(candidate))
|
|
73
|
+
: [];
|
|
74
|
+
if (candidates.length === 0) {
|
|
75
|
+
const query = normalizeNonEmpty(result.query);
|
|
76
|
+
return query
|
|
77
|
+
? `No published hosted skill matched "${query}".`
|
|
78
|
+
: 'No published hosted skill matched the query.';
|
|
79
|
+
}
|
|
80
|
+
return candidates.map((candidate, index) => {
|
|
81
|
+
const name = normalizeNonEmpty(candidate.name) ?? '(unnamed skill)';
|
|
82
|
+
const shortDescription = normalizeNonEmpty(candidate.shortDescription);
|
|
83
|
+
const exactRef = normalizeNonEmpty(candidate.exactRef) ?? normalizeNonEmpty(candidate.versionId) ?? '(no ref)';
|
|
84
|
+
const rationale = normalizeNonEmpty(candidate.rationale);
|
|
85
|
+
return [
|
|
86
|
+
`${index + 1}. ${name}${shortDescription ? ` — ${shortDescription}` : ''}`,
|
|
87
|
+
` ref: ${exactRef}`,
|
|
88
|
+
...(rationale ? [` match: ${rationale}`] : []),
|
|
89
|
+
].join('\n');
|
|
90
|
+
}).join('\n');
|
|
91
|
+
}
|
|
92
|
+
function toStringArray(values) {
|
|
93
|
+
return Array.isArray(values)
|
|
94
|
+
? values.map((entry) => normalizeNonEmpty(entry)).filter((entry) => Boolean(entry))
|
|
95
|
+
: [];
|
|
96
|
+
}
|
|
97
|
+
function slugifyProjectSelector(value) {
|
|
98
|
+
return value
|
|
99
|
+
.toLowerCase()
|
|
100
|
+
.replace(/[^a-z0-9]+/g, '-')
|
|
101
|
+
.replace(/^-+|-+$/g, '')
|
|
102
|
+
.slice(0, 80);
|
|
103
|
+
}
|
|
104
|
+
function matchesHostedProjectSlugSelector(row, selector) {
|
|
105
|
+
const normalizedSelector = slugifyProjectSelector(selector);
|
|
106
|
+
if (!normalizedSelector)
|
|
107
|
+
return false;
|
|
108
|
+
const aliases = [
|
|
109
|
+
normalizeNonEmpty(row.slug),
|
|
110
|
+
normalizeNonEmpty(row.projectSlug),
|
|
111
|
+
normalizeNonEmpty(row.name),
|
|
112
|
+
];
|
|
113
|
+
return aliases.some((alias) => alias ? slugifyProjectSelector(alias) === normalizedSelector : false);
|
|
114
|
+
}
|
|
115
|
+
function stripRepoProjectSelectors(options) {
|
|
116
|
+
const { projectId: _projectId, projectName: _projectName, projectSlug: _projectSlug, ...rest } = options;
|
|
117
|
+
return rest;
|
|
118
|
+
}
|
|
119
|
+
function stripHostedProjectSlug(options) {
|
|
120
|
+
const cleaned = { ...options };
|
|
121
|
+
for (const key of ['hostedProjectSlug', 'projectSlug', 'preview', 'apply', 'confirm', 'idempotencyKey', 'input']) {
|
|
122
|
+
delete cleaned[key];
|
|
123
|
+
}
|
|
124
|
+
return cleaned;
|
|
125
|
+
}
|
|
126
|
+
function parseJsonSeed(input, label = '--input') {
|
|
127
|
+
const normalized = normalizeNonEmpty(input);
|
|
128
|
+
if (!normalized)
|
|
129
|
+
return {};
|
|
130
|
+
const parsed = parseJsonInput(normalized, label);
|
|
131
|
+
if (!isRecord(parsed))
|
|
132
|
+
throw new Error(`${label} must be a JSON object or @file.json object.`);
|
|
133
|
+
return parsed;
|
|
134
|
+
}
|
|
135
|
+
function parseUnknownJsonInput(input, label) {
|
|
136
|
+
const normalized = normalizeNonEmpty(input);
|
|
137
|
+
if (!normalized)
|
|
138
|
+
return undefined;
|
|
139
|
+
return parseJsonInput(normalized, label);
|
|
140
|
+
}
|
|
141
|
+
function readTextInput(input) {
|
|
142
|
+
if (typeof input !== 'string')
|
|
143
|
+
return undefined;
|
|
144
|
+
const trimmed = input.trim();
|
|
145
|
+
if (!trimmed)
|
|
146
|
+
return undefined;
|
|
147
|
+
if (trimmed.startsWith('@')) {
|
|
148
|
+
const filePath = trimmed.slice(1).trim();
|
|
149
|
+
if (!filePath)
|
|
150
|
+
throw new Error('Expected a file path after @.');
|
|
151
|
+
return readFileSync(filePath, 'utf8');
|
|
152
|
+
}
|
|
153
|
+
return input;
|
|
154
|
+
}
|
|
155
|
+
function resolveStringField(explicit, seed, key) {
|
|
156
|
+
return normalizeNonEmpty(explicit) ?? normalizeNonEmpty(seed[key]);
|
|
157
|
+
}
|
|
158
|
+
function resolveStringArrayField(explicit, seed, key) {
|
|
159
|
+
const explicitValues = toStringArray(explicit);
|
|
160
|
+
if (explicitValues.length > 0)
|
|
161
|
+
return explicitValues;
|
|
162
|
+
const seededValues = toStringArray(seed[key]);
|
|
163
|
+
return seededValues.length > 0 ? seededValues : undefined;
|
|
164
|
+
}
|
|
165
|
+
function resolveIntegerField(explicit, seed, key, label) {
|
|
166
|
+
if (explicit !== undefined && explicit !== null && explicit !== '')
|
|
167
|
+
return toInteger(explicit, label);
|
|
168
|
+
if (seed[key] !== undefined && seed[key] !== null && seed[key] !== '')
|
|
169
|
+
return toInteger(seed[key], label);
|
|
170
|
+
return undefined;
|
|
171
|
+
}
|
|
172
|
+
function resolveTextField(explicit, seed, key) {
|
|
173
|
+
const explicitValue = readTextInput(explicit);
|
|
174
|
+
if (explicitValue !== undefined)
|
|
175
|
+
return explicitValue;
|
|
176
|
+
return typeof seed[key] === 'string' ? seed[key] : undefined;
|
|
177
|
+
}
|
|
178
|
+
function resolveUnknownField(explicit, seed, key, label) {
|
|
179
|
+
const explicitValue = parseUnknownJsonInput(explicit, label);
|
|
180
|
+
if (explicitValue !== undefined)
|
|
181
|
+
return explicitValue;
|
|
182
|
+
return seed[key];
|
|
183
|
+
}
|
|
184
|
+
function extractId(value) {
|
|
185
|
+
return normalizeNonEmpty(value);
|
|
186
|
+
}
|
|
187
|
+
function collectSkillArtifacts(result) {
|
|
188
|
+
const root = unwrapResultData(result) ?? (isRecord(result) ? result : {});
|
|
189
|
+
const artifacts = {};
|
|
190
|
+
const push = (key, value) => {
|
|
191
|
+
const normalized = extractId(value);
|
|
192
|
+
if (normalized)
|
|
193
|
+
artifacts[key] = normalized;
|
|
194
|
+
};
|
|
195
|
+
push('skillId', root.skillId);
|
|
196
|
+
if (!artifacts.skillId && !normalizeNonEmpty(root.skillId) && !normalizeNonEmpty(root.content)) {
|
|
197
|
+
push('skillId', root.id);
|
|
198
|
+
}
|
|
199
|
+
push('currentVersionId', root.currentVersionId);
|
|
200
|
+
push('skillVersionId', root.skillVersionId);
|
|
201
|
+
if (!artifacts.skillVersionId && (normalizeNonEmpty(root.skillId) || normalizeNonEmpty(root.content))) {
|
|
202
|
+
push('skillVersionId', root.id);
|
|
203
|
+
}
|
|
204
|
+
return Object.keys(artifacts).length > 0 ? artifacts : undefined;
|
|
205
|
+
}
|
|
206
|
+
function summarizeContent(value) {
|
|
207
|
+
const content = normalizeNonEmpty(value);
|
|
208
|
+
if (!content)
|
|
209
|
+
return undefined;
|
|
210
|
+
return compactPayload({
|
|
211
|
+
omitted: true,
|
|
212
|
+
bytes: Buffer.byteLength(content, 'utf8'),
|
|
213
|
+
lines: content.split(/\r\n|\r|\n/).length,
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
function summarizeSkillVersionRecord(value) {
|
|
217
|
+
if (!isRecord(value))
|
|
218
|
+
return value;
|
|
219
|
+
const { content, ...rest } = value;
|
|
220
|
+
return compactPayload({
|
|
221
|
+
...rest,
|
|
222
|
+
contentSummary: summarizeContent(content),
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
function summarizeSkillCurrentResult(result) {
|
|
226
|
+
if (!isRecord(result))
|
|
227
|
+
return result;
|
|
228
|
+
return compactPayload({
|
|
229
|
+
...result,
|
|
230
|
+
currentVersion: summarizeSkillVersionRecord(result.currentVersion),
|
|
231
|
+
summary: {
|
|
232
|
+
mode: 'skill-current-summary',
|
|
233
|
+
omitted: ['currentVersion.content'],
|
|
234
|
+
fullRecordHint: 'Use `aops-cli skill current --id <skill-id> --json` for full content.',
|
|
235
|
+
},
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
function summarizeSkillInspectResult(result) {
|
|
239
|
+
if (!isRecord(result))
|
|
240
|
+
return result;
|
|
241
|
+
const versions = Array.isArray(result.versions)
|
|
242
|
+
? result.versions.map(summarizeSkillVersionRecord)
|
|
243
|
+
: result.versions;
|
|
244
|
+
return compactPayload({
|
|
245
|
+
...result,
|
|
246
|
+
versions,
|
|
247
|
+
currentVersion: summarizeSkillVersionRecord(result.currentVersion),
|
|
248
|
+
summary: {
|
|
249
|
+
...(isRecord(result.summary) ? result.summary : {}),
|
|
250
|
+
mode: 'skill-inspect-summary',
|
|
251
|
+
omitted: ['versions[].content', 'currentVersion.content'],
|
|
252
|
+
fullRecordHint: 'Use `aops-cli skill inspect --id <skill-id> --json` for full content.',
|
|
253
|
+
},
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
function summarizeSkillVersionListResult(result) {
|
|
257
|
+
if (Array.isArray(result))
|
|
258
|
+
return result.map(summarizeSkillVersionRecord);
|
|
259
|
+
if (!isRecord(result))
|
|
260
|
+
return result;
|
|
261
|
+
const data = Array.isArray(result.data)
|
|
262
|
+
? result.data.map(summarizeSkillVersionRecord)
|
|
263
|
+
: result.data;
|
|
264
|
+
const items = Array.isArray(result.items)
|
|
265
|
+
? result.items.map(summarizeSkillVersionRecord)
|
|
266
|
+
: result.items;
|
|
267
|
+
return compactPayload({
|
|
268
|
+
...result,
|
|
269
|
+
data,
|
|
270
|
+
items,
|
|
271
|
+
summary: {
|
|
272
|
+
...(isRecord(result.summary) ? result.summary : {}),
|
|
273
|
+
mode: 'skill-version-list-summary',
|
|
274
|
+
omitted: ['data[].content', 'items[].content'],
|
|
275
|
+
fullRecordHint: 'Use `aops-cli skill version list --skill-id <skill-id> --json` for full content.',
|
|
276
|
+
},
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
function buildGatewayOptions(options, resolvedContext) {
|
|
280
|
+
const baseOptions = stripHostedProjectSlug(options);
|
|
281
|
+
if (normalizeNonEmpty(options.hostedProjectSlug)) {
|
|
282
|
+
return {
|
|
283
|
+
...baseOptions,
|
|
284
|
+
projectId: normalizeNonEmpty(resolvedContext.projectId),
|
|
285
|
+
projectName: undefined,
|
|
286
|
+
scopeId: normalizeNonEmpty(resolvedContext.scopeId),
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
return {
|
|
290
|
+
...baseOptions,
|
|
291
|
+
projectId: normalizeNonEmpty(options.projectId) ?? normalizeNonEmpty(resolvedContext.projectId),
|
|
292
|
+
projectName: normalizeNonEmpty(options.projectName) ?? normalizeNonEmpty(resolvedContext.projectName),
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
async function hydrateProjectContext(apiState, options, resolvedContext) {
|
|
296
|
+
const projectId = normalizeNonEmpty(resolvedContext.projectId);
|
|
297
|
+
if (!projectId)
|
|
298
|
+
return resolvedContext;
|
|
299
|
+
if (normalizeNonEmpty(options.scopeId)) {
|
|
300
|
+
return resolvedContext;
|
|
301
|
+
}
|
|
302
|
+
const payload = await invokeHostedToolWithApiState(apiState, {
|
|
303
|
+
...buildGatewayOptions(options, resolvedContext),
|
|
304
|
+
toolId: 'agentspace.project.get-by-id',
|
|
305
|
+
input: { id: projectId },
|
|
306
|
+
});
|
|
307
|
+
const result = unwrapHostedToolResult(payload);
|
|
308
|
+
const project = unwrapResultData(result);
|
|
309
|
+
if (!isRecord(project))
|
|
310
|
+
return resolvedContext;
|
|
311
|
+
const scopeId = resolveOwnerScopeIdFromProjectRecord(project, resolvedContext.scopeId);
|
|
312
|
+
const projectName = normalizeNonEmpty(project.name) ?? resolvedContext.projectName;
|
|
313
|
+
return {
|
|
314
|
+
...resolvedContext,
|
|
315
|
+
scopeId,
|
|
316
|
+
projectName,
|
|
317
|
+
};
|
|
318
|
+
}
|
|
319
|
+
async function listHostedProjectsForSlug(apiState, options, resolvedContext, input) {
|
|
320
|
+
const payload = await invokeHostedToolWithApiState(apiState, {
|
|
321
|
+
...buildGatewayOptions(stripRepoProjectSelectors(options), resolvedContext),
|
|
322
|
+
tenantId: options.tenantId,
|
|
323
|
+
locale: options.locale,
|
|
324
|
+
fallbackLocale: options.fallbackLocale,
|
|
325
|
+
timeoutMs: options.timeoutMs,
|
|
326
|
+
apiBaseUrl: options.apiBaseUrl,
|
|
327
|
+
accessToken: options.accessToken,
|
|
328
|
+
refreshToken: options.refreshToken,
|
|
329
|
+
toolId: 'agentspace.project.list-projects',
|
|
330
|
+
input,
|
|
331
|
+
});
|
|
332
|
+
return unwrapListItems(unwrapHostedToolResult(payload));
|
|
333
|
+
}
|
|
334
|
+
async function resolveHostedProjectBySlug(apiState, options, resolvedContext, selector) {
|
|
335
|
+
const slug = normalizeNonEmpty(selector);
|
|
336
|
+
if (!slug)
|
|
337
|
+
return resolvedContext;
|
|
338
|
+
const filteredRows = await listHostedProjectsForSlug(apiState, options, resolvedContext, {
|
|
339
|
+
filter: { slug },
|
|
340
|
+
options: { limit: 20 },
|
|
341
|
+
});
|
|
342
|
+
const fallbackRows = filteredRows.length > 0
|
|
343
|
+
? []
|
|
344
|
+
: await listHostedProjectsForSlug(apiState, options, resolvedContext, {
|
|
345
|
+
filter: {},
|
|
346
|
+
options: { limit: 500 },
|
|
347
|
+
});
|
|
348
|
+
const rows = filteredRows.length > 0 ? filteredRows : fallbackRows;
|
|
349
|
+
const matches = rows.filter((row) => matchesHostedProjectSlugSelector(row, slug));
|
|
350
|
+
if (matches.length === 0) {
|
|
351
|
+
throw new Error(`Hosted project slug "${slug}" was not found.`);
|
|
352
|
+
}
|
|
353
|
+
if (matches.length > 1) {
|
|
354
|
+
const ids = matches
|
|
355
|
+
.map((project) => `${normalizeNonEmpty(project.slug) ?? normalizeNonEmpty(project.name) ?? '(unnamed)'} (${normalizeNonEmpty(project.id) ?? normalizeNonEmpty(project.projectId) ?? normalizeNonEmpty(project.scopeId) ?? 'no-id'})`)
|
|
356
|
+
.join(', ');
|
|
357
|
+
throw new Error(`Hosted project slug "${slug}" is ambiguous: ${ids}.`);
|
|
358
|
+
}
|
|
359
|
+
const project = matches[0];
|
|
360
|
+
const projectId = normalizeNonEmpty(project.id) ?? normalizeNonEmpty(project.projectId) ?? normalizeNonEmpty(project.scopeId);
|
|
361
|
+
const scopeId = normalizeNonEmpty(project.scopeId) ?? projectId;
|
|
362
|
+
if (!projectId && !scopeId) {
|
|
363
|
+
throw new Error(`Hosted project slug "${slug}" resolved without a project id.`);
|
|
364
|
+
}
|
|
365
|
+
return {
|
|
366
|
+
...resolvedContext,
|
|
367
|
+
projectId: projectId ?? scopeId,
|
|
368
|
+
scopeId: scopeId ?? projectId,
|
|
369
|
+
projectName: normalizeNonEmpty(project.name),
|
|
370
|
+
projectSlug: normalizeNonEmpty(project.slug) ?? normalizeNonEmpty(project.projectSlug) ?? slug,
|
|
371
|
+
};
|
|
372
|
+
}
|
|
373
|
+
async function resolveSkillContext(options, apiState) {
|
|
374
|
+
const hostedProjectSlug = normalizeNonEmpty(options.hostedProjectSlug);
|
|
375
|
+
const bindingOptions = hostedProjectSlug ? stripRepoProjectSelectors(options) : options;
|
|
376
|
+
const resolved = await resolveProjectBindingContext(bindingOptions, {
|
|
377
|
+
requireProject: false,
|
|
378
|
+
});
|
|
379
|
+
const scopeId = normalizeNonEmpty(bindingOptions.scopeId) ?? resolveOwnerScopeIdFromBinding(resolved);
|
|
380
|
+
const baseContext = {
|
|
381
|
+
...resolved,
|
|
382
|
+
scopeId,
|
|
383
|
+
};
|
|
384
|
+
if (hostedProjectSlug) {
|
|
385
|
+
return resolveHostedProjectBySlug(apiState, options, baseContext, hostedProjectSlug);
|
|
386
|
+
}
|
|
387
|
+
return hydrateProjectContext(apiState, options, baseContext);
|
|
388
|
+
}
|
|
389
|
+
function buildResolvedContextRecord(context) {
|
|
390
|
+
return compactPayload({
|
|
391
|
+
repoRoot: context.repoRoot,
|
|
392
|
+
configPath: context.configPath,
|
|
393
|
+
configFound: context.configFound,
|
|
394
|
+
scopeId: context.scopeId,
|
|
395
|
+
projectId: context.projectId,
|
|
396
|
+
projectName: context.projectName,
|
|
397
|
+
projectSlug: context.projectSlug,
|
|
398
|
+
});
|
|
399
|
+
}
|
|
400
|
+
const buildEnvelope = buildHostedSugarEnvelope;
|
|
401
|
+
function ensureSkillId(id, label = '--id') {
|
|
402
|
+
const resolved = normalizeNonEmpty(id);
|
|
403
|
+
if (!resolved)
|
|
404
|
+
throw new Error(`Provide ${label}.`);
|
|
405
|
+
return resolved;
|
|
406
|
+
}
|
|
407
|
+
function requireScopeId(context, seed) {
|
|
408
|
+
const scopeId = normalizeNonEmpty(context.scopeId) ?? normalizeNonEmpty(seed.scopeId);
|
|
409
|
+
if (!scopeId) {
|
|
410
|
+
throw new Error(missingScopeIdMessage('Skill create'));
|
|
411
|
+
}
|
|
412
|
+
return scopeId;
|
|
413
|
+
}
|
|
414
|
+
function requireProjectId(options, context, seed) {
|
|
415
|
+
const projectId = normalizeNonEmpty(options.projectId) ??
|
|
416
|
+
normalizeNonEmpty(context.projectId) ??
|
|
417
|
+
normalizeNonEmpty(seed.projectId);
|
|
418
|
+
if (!projectId) {
|
|
419
|
+
throw new Error('Skill version commands require repo-bound project context or --project-id.');
|
|
420
|
+
}
|
|
421
|
+
return projectId;
|
|
422
|
+
}
|
|
423
|
+
function isSkillVersionConflictMessage(message) {
|
|
424
|
+
const normalized = message.toLowerCase();
|
|
425
|
+
return (normalized.includes('api 409') ||
|
|
426
|
+
normalized.includes('agentspace_operation_failed.conflict') ||
|
|
427
|
+
(normalized.includes('skill-version') && normalized.includes('unique')));
|
|
428
|
+
}
|
|
429
|
+
function formatSkillVersionCreateError(message, hint) {
|
|
430
|
+
if (hint.skillId && hint.version !== undefined && isSkillVersionConflictMessage(message)) {
|
|
431
|
+
return `skill version ${hint.version} already exists for skill ${hint.skillId}; pass --version ${hint.version + 1} or omit to auto-resolve`;
|
|
432
|
+
}
|
|
433
|
+
return message;
|
|
434
|
+
}
|
|
435
|
+
async function invokeSkillTool(apiState, options, resolvedContext, params) {
|
|
436
|
+
ensureGuardedWrite(options, 'This command mutates hosted skill state.');
|
|
437
|
+
const payload = await invokeHostedToolWithApiState(apiState, {
|
|
438
|
+
...buildGatewayOptions(options, resolvedContext),
|
|
439
|
+
tenantId: options.tenantId,
|
|
440
|
+
locale: options.locale,
|
|
441
|
+
fallbackLocale: options.fallbackLocale,
|
|
442
|
+
timeoutMs: options.timeoutMs,
|
|
443
|
+
apiBaseUrl: options.apiBaseUrl,
|
|
444
|
+
accessToken: options.accessToken,
|
|
445
|
+
refreshToken: options.refreshToken,
|
|
446
|
+
toolId: params.toolId,
|
|
447
|
+
input: params.input,
|
|
448
|
+
preview: options.preview,
|
|
449
|
+
apply: options.apply,
|
|
450
|
+
confirm: options.confirm,
|
|
451
|
+
idempotencyKey: options.idempotencyKey,
|
|
452
|
+
});
|
|
453
|
+
const result = unwrapHostedToolResult(payload);
|
|
454
|
+
if (options.json) {
|
|
455
|
+
console.log(JSON.stringify(buildEnvelope({
|
|
456
|
+
command: params.command,
|
|
457
|
+
toolId: params.toolId,
|
|
458
|
+
resolvedContext: buildResolvedContextRecord(resolvedContext),
|
|
459
|
+
input: params.input,
|
|
460
|
+
artifacts: collectSkillArtifacts(result),
|
|
461
|
+
result,
|
|
462
|
+
}), null, 2));
|
|
463
|
+
return;
|
|
464
|
+
}
|
|
465
|
+
logSuccess(params.successText);
|
|
466
|
+
console.log(JSON.stringify(result, null, 2));
|
|
467
|
+
}
|
|
468
|
+
export async function runSkillList(options = {}) {
|
|
469
|
+
try {
|
|
470
|
+
const apiState = await requireApiState(options);
|
|
471
|
+
if (!apiState)
|
|
472
|
+
return;
|
|
473
|
+
const resolvedContext = await resolveSkillContext(options, apiState);
|
|
474
|
+
const input = {
|
|
475
|
+
filter: compactPayload({
|
|
476
|
+
scopeId: normalizeNonEmpty(resolvedContext.scopeId),
|
|
477
|
+
scopeResolution: options.scopeResolution,
|
|
478
|
+
name: normalizeNonEmpty(options.name),
|
|
479
|
+
tags: options.tag && options.tag.length > 0 ? options.tag : undefined,
|
|
480
|
+
}),
|
|
481
|
+
options: compactPayload({
|
|
482
|
+
limit: options.limit !== undefined ? toInteger(options.limit, '--limit') : undefined,
|
|
483
|
+
}),
|
|
484
|
+
};
|
|
485
|
+
const payload = await invokeHostedToolWithApiState(apiState, {
|
|
486
|
+
...buildGatewayOptions(options, resolvedContext),
|
|
487
|
+
tenantId: options.tenantId,
|
|
488
|
+
locale: options.locale,
|
|
489
|
+
fallbackLocale: options.fallbackLocale,
|
|
490
|
+
timeoutMs: options.timeoutMs,
|
|
491
|
+
apiBaseUrl: options.apiBaseUrl,
|
|
492
|
+
accessToken: options.accessToken,
|
|
493
|
+
refreshToken: options.refreshToken,
|
|
494
|
+
toolId: 'agentspace.skill.list-skills',
|
|
495
|
+
input,
|
|
496
|
+
});
|
|
497
|
+
const result = unwrapHostedToolResult(payload);
|
|
498
|
+
if (options.json) {
|
|
499
|
+
console.log(JSON.stringify(buildEnvelope({
|
|
500
|
+
command: 'skill.list',
|
|
501
|
+
toolId: 'agentspace.skill.list-skills',
|
|
502
|
+
resolvedContext: buildResolvedContextRecord(resolvedContext),
|
|
503
|
+
input,
|
|
504
|
+
result,
|
|
505
|
+
}), null, 2));
|
|
506
|
+
return;
|
|
507
|
+
}
|
|
508
|
+
logSuccess('Skill list loaded.');
|
|
509
|
+
console.log(JSON.stringify(result, null, 2));
|
|
510
|
+
}
|
|
511
|
+
catch (error) {
|
|
512
|
+
logError(error instanceof Error ? error.message : String(error));
|
|
513
|
+
process.exitCode = 1;
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
async function runSkillDiscovery(mode, options = {}) {
|
|
517
|
+
try {
|
|
518
|
+
const apiState = await requireApiState(options);
|
|
519
|
+
if (!apiState)
|
|
520
|
+
return;
|
|
521
|
+
const resolvedContext = await resolveSkillContext(options, apiState);
|
|
522
|
+
const input = resolveSkillDiscoveryInput({
|
|
523
|
+
query: options.query ?? options.q,
|
|
524
|
+
limit: options.limit,
|
|
525
|
+
scopeId: resolvedContext.scopeId,
|
|
526
|
+
scopeResolution: options.scopeResolution,
|
|
527
|
+
});
|
|
528
|
+
const toolId = `agentspace.skill.${mode}`;
|
|
529
|
+
const payload = await invokeHostedToolWithApiState(apiState, {
|
|
530
|
+
...buildGatewayOptions(options, resolvedContext),
|
|
531
|
+
tenantId: options.tenantId,
|
|
532
|
+
locale: options.locale,
|
|
533
|
+
fallbackLocale: options.fallbackLocale,
|
|
534
|
+
timeoutMs: options.timeoutMs,
|
|
535
|
+
apiBaseUrl: options.apiBaseUrl,
|
|
536
|
+
accessToken: options.accessToken,
|
|
537
|
+
refreshToken: options.refreshToken,
|
|
538
|
+
toolId,
|
|
539
|
+
input,
|
|
540
|
+
});
|
|
541
|
+
const hostedResult = unwrapHostedToolResult(payload);
|
|
542
|
+
const result = unwrapResultData(hostedResult) ?? hostedResult;
|
|
543
|
+
if (options.json) {
|
|
544
|
+
console.log(JSON.stringify(buildEnvelope({
|
|
545
|
+
command: `skill.${mode}`,
|
|
546
|
+
toolId,
|
|
547
|
+
resolvedContext: buildResolvedContextRecord(resolvedContext),
|
|
548
|
+
input,
|
|
549
|
+
result,
|
|
550
|
+
}), null, 2));
|
|
551
|
+
return;
|
|
552
|
+
}
|
|
553
|
+
if (mode === 'ask' && isRecord(result) && normalizeNonEmpty(result.answer)) {
|
|
554
|
+
logSuccess('Hosted skill recommendation ready.');
|
|
555
|
+
console.log(normalizeNonEmpty(result.answer));
|
|
556
|
+
return;
|
|
557
|
+
}
|
|
558
|
+
const count = isRecord(result) && Number.isInteger(result.count) ? Number(result.count) : 0;
|
|
559
|
+
if (count > 0)
|
|
560
|
+
logSuccess(`${count} hosted skill${count === 1 ? '' : 's'} matched.`);
|
|
561
|
+
console.log(formatSkillSearchResult(result));
|
|
562
|
+
}
|
|
563
|
+
catch (error) {
|
|
564
|
+
logError(error instanceof Error ? error.message : String(error));
|
|
565
|
+
process.exitCode = 1;
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
export async function runSkillSearch(options = {}) {
|
|
569
|
+
return runSkillDiscovery('search', options);
|
|
570
|
+
}
|
|
571
|
+
export async function runSkillAsk(options = {}) {
|
|
572
|
+
return runSkillDiscovery('ask', options);
|
|
573
|
+
}
|
|
574
|
+
export async function runSkillGet(options = {}) {
|
|
575
|
+
try {
|
|
576
|
+
const apiState = await requireApiState(options);
|
|
577
|
+
if (!apiState)
|
|
578
|
+
return;
|
|
579
|
+
const resolvedContext = await resolveSkillContext(options, apiState);
|
|
580
|
+
const id = ensureSkillId(options.id);
|
|
581
|
+
const input = { id };
|
|
582
|
+
const payload = await invokeHostedToolWithApiState(apiState, {
|
|
583
|
+
...buildGatewayOptions(options, resolvedContext),
|
|
584
|
+
tenantId: options.tenantId,
|
|
585
|
+
locale: options.locale,
|
|
586
|
+
fallbackLocale: options.fallbackLocale,
|
|
587
|
+
timeoutMs: options.timeoutMs,
|
|
588
|
+
apiBaseUrl: options.apiBaseUrl,
|
|
589
|
+
accessToken: options.accessToken,
|
|
590
|
+
refreshToken: options.refreshToken,
|
|
591
|
+
toolId: 'agentspace.skill.get-skill',
|
|
592
|
+
input,
|
|
593
|
+
});
|
|
594
|
+
const result = unwrapHostedToolResult(payload);
|
|
595
|
+
if (options.json) {
|
|
596
|
+
console.log(JSON.stringify(buildEnvelope({
|
|
597
|
+
command: 'skill.get',
|
|
598
|
+
toolId: 'agentspace.skill.get-skill',
|
|
599
|
+
resolvedContext: buildResolvedContextRecord(resolvedContext),
|
|
600
|
+
input,
|
|
601
|
+
artifacts: collectSkillArtifacts(result),
|
|
602
|
+
result,
|
|
603
|
+
}), null, 2));
|
|
604
|
+
return;
|
|
605
|
+
}
|
|
606
|
+
logSuccess('Skill loaded.');
|
|
607
|
+
console.log(JSON.stringify(result, null, 2));
|
|
608
|
+
}
|
|
609
|
+
catch (error) {
|
|
610
|
+
logError(error instanceof Error ? error.message : String(error));
|
|
611
|
+
process.exitCode = 1;
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
export async function runSkillCreate(options = {}) {
|
|
615
|
+
try {
|
|
616
|
+
const apiState = await requireApiState(options);
|
|
617
|
+
if (!apiState)
|
|
618
|
+
return;
|
|
619
|
+
const resolvedContext = await resolveSkillContext(options, apiState);
|
|
620
|
+
const seed = parseJsonSeed(options.input);
|
|
621
|
+
const name = resolveStringField(options.name, seed, 'name');
|
|
622
|
+
if (!name)
|
|
623
|
+
throw new Error('Skill create requires --name or input.name.');
|
|
624
|
+
const input = {
|
|
625
|
+
data: compactPayload({
|
|
626
|
+
scopeId: requireScopeId(resolvedContext, seed),
|
|
627
|
+
name,
|
|
628
|
+
description: resolveStringField(options.description, seed, 'description'),
|
|
629
|
+
shortDescription: resolveStringField(options.shortDescription, seed, 'shortDescription'),
|
|
630
|
+
tags: resolveStringArrayField(options.tag, seed, 'tags'),
|
|
631
|
+
}),
|
|
632
|
+
};
|
|
633
|
+
await invokeSkillTool(apiState, options, resolvedContext, {
|
|
634
|
+
command: 'skill.create',
|
|
635
|
+
toolId: 'agentspace.skill.create',
|
|
636
|
+
input,
|
|
637
|
+
successText: 'Skill created.',
|
|
638
|
+
});
|
|
639
|
+
}
|
|
640
|
+
catch (error) {
|
|
641
|
+
logError(error instanceof Error ? error.message : String(error));
|
|
642
|
+
process.exitCode = 1;
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
export async function runSkillUpdate(options = {}) {
|
|
646
|
+
try {
|
|
647
|
+
const apiState = await requireApiState(options);
|
|
648
|
+
if (!apiState)
|
|
649
|
+
return;
|
|
650
|
+
const resolvedContext = await resolveSkillContext(options, apiState);
|
|
651
|
+
const seed = parseJsonSeed(options.input);
|
|
652
|
+
const id = ensureSkillId(resolveStringField(options.id, seed, 'id'));
|
|
653
|
+
const patch = compactPayload({
|
|
654
|
+
name: resolveStringField(options.name, seed, 'name'),
|
|
655
|
+
description: resolveStringField(options.description, seed, 'description'),
|
|
656
|
+
shortDescription: resolveStringField(options.shortDescription, seed, 'shortDescription'),
|
|
657
|
+
tags: resolveStringArrayField(options.tag, seed, 'tags'),
|
|
658
|
+
currentVersionId: resolveStringField(options.currentVersionId, seed, 'currentVersionId'),
|
|
659
|
+
});
|
|
660
|
+
if (Object.keys(patch).length === 0) {
|
|
661
|
+
throw new Error('Provide at least one skill field to update.');
|
|
662
|
+
}
|
|
663
|
+
const input = { id, patch };
|
|
664
|
+
await invokeSkillTool(apiState, options, resolvedContext, {
|
|
665
|
+
command: 'skill.update',
|
|
666
|
+
toolId: 'agentspace.skill.update-skill',
|
|
667
|
+
input,
|
|
668
|
+
successText: 'Skill updated.',
|
|
669
|
+
});
|
|
670
|
+
}
|
|
671
|
+
catch (error) {
|
|
672
|
+
logError(error instanceof Error ? error.message : String(error));
|
|
673
|
+
process.exitCode = 1;
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
export async function runSkillDelete(options = {}) {
|
|
677
|
+
try {
|
|
678
|
+
ensureDestructiveWrite(options, 'This command deletes hosted skills.');
|
|
679
|
+
const apiState = await requireApiState(options);
|
|
680
|
+
if (!apiState)
|
|
681
|
+
return;
|
|
682
|
+
const resolvedContext = await resolveSkillContext(options, apiState);
|
|
683
|
+
const id = ensureSkillId(options.id);
|
|
684
|
+
const input = { id };
|
|
685
|
+
await invokeSkillTool(apiState, options, resolvedContext, {
|
|
686
|
+
command: 'skill.delete',
|
|
687
|
+
toolId: 'agentspace.skill.remove-skill',
|
|
688
|
+
input,
|
|
689
|
+
successText: 'Skill deleted.',
|
|
690
|
+
});
|
|
691
|
+
}
|
|
692
|
+
catch (error) {
|
|
693
|
+
logError(error instanceof Error ? error.message : String(error));
|
|
694
|
+
process.exitCode = 1;
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
async function loadSkill(apiState, options, resolvedContext, id) {
|
|
698
|
+
const payload = await invokeHostedToolWithApiState(apiState, {
|
|
699
|
+
...buildGatewayOptions(options, resolvedContext),
|
|
700
|
+
tenantId: options.tenantId,
|
|
701
|
+
locale: options.locale,
|
|
702
|
+
fallbackLocale: options.fallbackLocale,
|
|
703
|
+
timeoutMs: options.timeoutMs,
|
|
704
|
+
apiBaseUrl: options.apiBaseUrl,
|
|
705
|
+
accessToken: options.accessToken,
|
|
706
|
+
refreshToken: options.refreshToken,
|
|
707
|
+
toolId: 'agentspace.skill.get-skill',
|
|
708
|
+
input: { id },
|
|
709
|
+
});
|
|
710
|
+
const result = unwrapHostedToolResult(payload);
|
|
711
|
+
const skill = unwrapResultData(result);
|
|
712
|
+
return isRecord(skill) ? skill : null;
|
|
713
|
+
}
|
|
714
|
+
async function loadSkillVersionById(apiState, options, resolvedContext, id) {
|
|
715
|
+
const payload = await invokeHostedToolWithApiState(apiState, {
|
|
716
|
+
...buildGatewayOptions(options, resolvedContext),
|
|
717
|
+
tenantId: options.tenantId,
|
|
718
|
+
locale: options.locale,
|
|
719
|
+
fallbackLocale: options.fallbackLocale,
|
|
720
|
+
timeoutMs: options.timeoutMs,
|
|
721
|
+
apiBaseUrl: options.apiBaseUrl,
|
|
722
|
+
accessToken: options.accessToken,
|
|
723
|
+
refreshToken: options.refreshToken,
|
|
724
|
+
toolId: 'agentspace.skill-version.get-skill-version',
|
|
725
|
+
input: { id },
|
|
726
|
+
});
|
|
727
|
+
const result = unwrapHostedToolResult(payload);
|
|
728
|
+
const version = unwrapResultData(result);
|
|
729
|
+
return isRecord(version) ? version : null;
|
|
730
|
+
}
|
|
731
|
+
async function loadSkillVersions(apiState, options, resolvedContext, skillId, extraFilter = {}) {
|
|
732
|
+
const filter = compactPayload({
|
|
733
|
+
skillId,
|
|
734
|
+
projectId: normalizeNonEmpty(options.projectId) ?? normalizeNonEmpty(resolvedContext.projectId),
|
|
735
|
+
...extraFilter,
|
|
736
|
+
});
|
|
737
|
+
const payload = await invokeHostedToolWithApiState(apiState, {
|
|
738
|
+
...buildGatewayOptions(options, resolvedContext),
|
|
739
|
+
tenantId: options.tenantId,
|
|
740
|
+
locale: options.locale,
|
|
741
|
+
fallbackLocale: options.fallbackLocale,
|
|
742
|
+
timeoutMs: options.timeoutMs,
|
|
743
|
+
apiBaseUrl: options.apiBaseUrl,
|
|
744
|
+
accessToken: options.accessToken,
|
|
745
|
+
refreshToken: options.refreshToken,
|
|
746
|
+
toolId: 'agentspace.skill-version.list-skill-versions',
|
|
747
|
+
input: { filter },
|
|
748
|
+
});
|
|
749
|
+
return unwrapListItems(unwrapHostedToolResult(payload));
|
|
750
|
+
}
|
|
751
|
+
export async function runSkillInspect(options = {}) {
|
|
752
|
+
try {
|
|
753
|
+
const apiState = await requireApiState(options);
|
|
754
|
+
if (!apiState)
|
|
755
|
+
return;
|
|
756
|
+
const resolvedContext = await resolveSkillContext(options, apiState);
|
|
757
|
+
const id = ensureSkillId(options.id);
|
|
758
|
+
const skill = await loadSkill(apiState, options, resolvedContext, id);
|
|
759
|
+
if (!skill)
|
|
760
|
+
throw new Error(`Skill not found: ${id}`);
|
|
761
|
+
const versions = await loadSkillVersions(apiState, options, resolvedContext, id);
|
|
762
|
+
const currentVersionId = normalizeNonEmpty(skill.currentVersionId);
|
|
763
|
+
const currentVersion = versions.find((entry) => normalizeNonEmpty(entry.id) === currentVersionId) ??
|
|
764
|
+
(currentVersionId ? await loadSkillVersionById(apiState, options, resolvedContext, currentVersionId) : null);
|
|
765
|
+
const result = compactPayload({
|
|
766
|
+
skill,
|
|
767
|
+
versions,
|
|
768
|
+
currentVersion,
|
|
769
|
+
summary: compactPayload({
|
|
770
|
+
skillId: normalizeNonEmpty(skill.id),
|
|
771
|
+
currentVersionId,
|
|
772
|
+
versionCount: versions.length,
|
|
773
|
+
}),
|
|
774
|
+
});
|
|
775
|
+
const outputResult = options.summary ? summarizeSkillInspectResult(result) : result;
|
|
776
|
+
if (options.json) {
|
|
777
|
+
console.log(JSON.stringify(buildEnvelope({
|
|
778
|
+
command: 'skill.inspect',
|
|
779
|
+
surface: 'skill inspection pack',
|
|
780
|
+
resolvedContext: buildResolvedContextRecord(resolvedContext),
|
|
781
|
+
input: { id },
|
|
782
|
+
result: outputResult,
|
|
783
|
+
}), null, 2));
|
|
784
|
+
return;
|
|
785
|
+
}
|
|
786
|
+
logSuccess('Skill inspection pack ready.');
|
|
787
|
+
console.log(JSON.stringify(outputResult, null, 2));
|
|
788
|
+
}
|
|
789
|
+
catch (error) {
|
|
790
|
+
logError(error instanceof Error ? error.message : String(error));
|
|
791
|
+
process.exitCode = 1;
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
export async function runSkillCurrent(options = {}) {
|
|
795
|
+
try {
|
|
796
|
+
const apiState = await requireApiState(options);
|
|
797
|
+
if (!apiState)
|
|
798
|
+
return;
|
|
799
|
+
const resolvedContext = await resolveSkillContext(options, apiState);
|
|
800
|
+
const id = ensureSkillId(options.id);
|
|
801
|
+
const skill = await loadSkill(apiState, options, resolvedContext, id);
|
|
802
|
+
if (!skill)
|
|
803
|
+
throw new Error(`Skill not found: ${id}`);
|
|
804
|
+
const currentVersionId = normalizeNonEmpty(skill.currentVersionId);
|
|
805
|
+
if (!currentVersionId) {
|
|
806
|
+
throw new Error(`Skill ${id} does not have a currentVersionId. Publish or select a skill version first.`);
|
|
807
|
+
}
|
|
808
|
+
const currentVersion = await loadSkillVersionById(apiState, options, resolvedContext, currentVersionId);
|
|
809
|
+
if (!currentVersion) {
|
|
810
|
+
throw new Error(`Current skill version ${currentVersionId} could not be loaded.`);
|
|
811
|
+
}
|
|
812
|
+
const result = { skill, currentVersion };
|
|
813
|
+
const outputResult = options.summary ? summarizeSkillCurrentResult(result) : result;
|
|
814
|
+
if (options.json) {
|
|
815
|
+
console.log(JSON.stringify(buildEnvelope({
|
|
816
|
+
command: 'skill.current',
|
|
817
|
+
surface: 'skill current resolution',
|
|
818
|
+
resolvedContext: buildResolvedContextRecord(resolvedContext),
|
|
819
|
+
input: { id },
|
|
820
|
+
artifacts: compactPayload({ skillId: normalizeNonEmpty(skill.id), skillVersionId: currentVersionId }),
|
|
821
|
+
result: outputResult,
|
|
822
|
+
}), null, 2));
|
|
823
|
+
return;
|
|
824
|
+
}
|
|
825
|
+
logSuccess('Current skill version loaded.');
|
|
826
|
+
console.log(JSON.stringify(outputResult, null, 2));
|
|
827
|
+
}
|
|
828
|
+
catch (error) {
|
|
829
|
+
logError(error instanceof Error ? error.message : String(error));
|
|
830
|
+
process.exitCode = 1;
|
|
831
|
+
}
|
|
832
|
+
}
|
|
833
|
+
export async function runSkillVersionList(options = {}) {
|
|
834
|
+
try {
|
|
835
|
+
const apiState = await requireApiState(options);
|
|
836
|
+
if (!apiState)
|
|
837
|
+
return;
|
|
838
|
+
const resolvedContext = await resolveSkillContext(options, apiState);
|
|
839
|
+
const input = {
|
|
840
|
+
filter: compactPayload({
|
|
841
|
+
skillId: normalizeNonEmpty(options.skillId),
|
|
842
|
+
projectId: normalizeNonEmpty(options.projectId) ?? normalizeNonEmpty(resolvedContext.projectId),
|
|
843
|
+
status: normalizeNonEmpty(options.status),
|
|
844
|
+
refType: normalizeNonEmpty(options.refType),
|
|
845
|
+
refId: normalizeNonEmpty(options.refId),
|
|
846
|
+
}),
|
|
847
|
+
options: compactPayload({
|
|
848
|
+
limit: options.limit !== undefined ? toInteger(options.limit, '--limit') : undefined,
|
|
849
|
+
}),
|
|
850
|
+
};
|
|
851
|
+
const payload = await invokeHostedToolWithApiState(apiState, {
|
|
852
|
+
...buildGatewayOptions(options, resolvedContext),
|
|
853
|
+
tenantId: options.tenantId,
|
|
854
|
+
locale: options.locale,
|
|
855
|
+
fallbackLocale: options.fallbackLocale,
|
|
856
|
+
timeoutMs: options.timeoutMs,
|
|
857
|
+
apiBaseUrl: options.apiBaseUrl,
|
|
858
|
+
accessToken: options.accessToken,
|
|
859
|
+
refreshToken: options.refreshToken,
|
|
860
|
+
toolId: 'agentspace.skill-version.list-skill-versions',
|
|
861
|
+
input,
|
|
862
|
+
});
|
|
863
|
+
const result = unwrapHostedToolResult(payload);
|
|
864
|
+
const outputResult = options.summary ? summarizeSkillVersionListResult(result) : result;
|
|
865
|
+
if (options.json) {
|
|
866
|
+
console.log(JSON.stringify(buildEnvelope({
|
|
867
|
+
command: 'skill.version.list',
|
|
868
|
+
toolId: 'agentspace.skill-version.list-skill-versions',
|
|
869
|
+
resolvedContext: buildResolvedContextRecord(resolvedContext),
|
|
870
|
+
input,
|
|
871
|
+
result: outputResult,
|
|
872
|
+
}), null, 2));
|
|
873
|
+
return;
|
|
874
|
+
}
|
|
875
|
+
logSuccess('Skill version list loaded.');
|
|
876
|
+
console.log(JSON.stringify(outputResult, null, 2));
|
|
877
|
+
}
|
|
878
|
+
catch (error) {
|
|
879
|
+
logError(error instanceof Error ? error.message : String(error));
|
|
880
|
+
process.exitCode = 1;
|
|
881
|
+
}
|
|
882
|
+
}
|
|
883
|
+
export async function runSkillVersionGet(options = {}) {
|
|
884
|
+
try {
|
|
885
|
+
const apiState = await requireApiState(options);
|
|
886
|
+
if (!apiState)
|
|
887
|
+
return;
|
|
888
|
+
const resolvedContext = await resolveSkillContext(options, apiState);
|
|
889
|
+
const id = ensureSkillId(options.id);
|
|
890
|
+
const input = { id };
|
|
891
|
+
const payload = await invokeHostedToolWithApiState(apiState, {
|
|
892
|
+
...buildGatewayOptions(options, resolvedContext),
|
|
893
|
+
tenantId: options.tenantId,
|
|
894
|
+
locale: options.locale,
|
|
895
|
+
fallbackLocale: options.fallbackLocale,
|
|
896
|
+
timeoutMs: options.timeoutMs,
|
|
897
|
+
apiBaseUrl: options.apiBaseUrl,
|
|
898
|
+
accessToken: options.accessToken,
|
|
899
|
+
refreshToken: options.refreshToken,
|
|
900
|
+
toolId: 'agentspace.skill-version.get-skill-version',
|
|
901
|
+
input,
|
|
902
|
+
});
|
|
903
|
+
const result = unwrapHostedToolResult(payload);
|
|
904
|
+
if (options.json) {
|
|
905
|
+
console.log(JSON.stringify(buildEnvelope({
|
|
906
|
+
command: 'skill.version.get',
|
|
907
|
+
toolId: 'agentspace.skill-version.get-skill-version',
|
|
908
|
+
resolvedContext: buildResolvedContextRecord(resolvedContext),
|
|
909
|
+
input,
|
|
910
|
+
artifacts: collectSkillArtifacts(result),
|
|
911
|
+
result,
|
|
912
|
+
}), null, 2));
|
|
913
|
+
return;
|
|
914
|
+
}
|
|
915
|
+
logSuccess('Skill version loaded.');
|
|
916
|
+
console.log(JSON.stringify(result, null, 2));
|
|
917
|
+
}
|
|
918
|
+
catch (error) {
|
|
919
|
+
logError(error instanceof Error ? error.message : String(error));
|
|
920
|
+
process.exitCode = 1;
|
|
921
|
+
}
|
|
922
|
+
}
|
|
923
|
+
export async function runSkillVersionCreate(options = {}) {
|
|
924
|
+
const conflictHint = {};
|
|
925
|
+
try {
|
|
926
|
+
const apiState = await requireApiState(options);
|
|
927
|
+
if (!apiState)
|
|
928
|
+
return;
|
|
929
|
+
const resolvedContext = await resolveSkillContext(options, apiState);
|
|
930
|
+
const seed = parseJsonSeed(options.input);
|
|
931
|
+
const skillId = resolveStringField(options.skillId, seed, 'skillId');
|
|
932
|
+
if (!skillId)
|
|
933
|
+
throw new Error('Skill version create requires --skill-id or input.skillId.');
|
|
934
|
+
const content = resolveTextField(options.content, seed, 'content');
|
|
935
|
+
if (!content)
|
|
936
|
+
throw new Error('Skill version create requires --content or input.content.');
|
|
937
|
+
const projectId = requireProjectId(options, resolvedContext, seed);
|
|
938
|
+
const version = resolveIntegerField(options.version, seed, 'version', '--version') ??
|
|
939
|
+
resolveNextVersionNumber(await loadSkillVersions(apiState, { ...options, projectId }, { ...resolvedContext, projectId: normalizeNonEmpty(resolvedContext.projectId) ?? projectId }, skillId));
|
|
940
|
+
conflictHint.skillId = skillId;
|
|
941
|
+
conflictHint.version = version;
|
|
942
|
+
const input = {
|
|
943
|
+
data: compactPayload({
|
|
944
|
+
projectId,
|
|
945
|
+
skillId,
|
|
946
|
+
version,
|
|
947
|
+
status: resolveStringField(options.status, seed, 'status') ?? 'draft',
|
|
948
|
+
content,
|
|
949
|
+
entryFile: resolveStringField(options.entryFile, seed, 'entryFile'),
|
|
950
|
+
skillStandard: resolveStringField(options.skillStandard, seed, 'skillStandard'),
|
|
951
|
+
meta: resolveUnknownField(options.meta, seed, 'meta', '--meta'),
|
|
952
|
+
refType: resolveStringField(options.refType, seed, 'refType'),
|
|
953
|
+
refId: resolveStringField(options.refId, seed, 'refId'),
|
|
954
|
+
}),
|
|
955
|
+
};
|
|
956
|
+
await invokeSkillTool(apiState, options, resolvedContext, {
|
|
957
|
+
command: 'skill.version.create',
|
|
958
|
+
toolId: 'agentspace.skill-version.create',
|
|
959
|
+
input,
|
|
960
|
+
successText: 'Skill version created.',
|
|
961
|
+
});
|
|
962
|
+
}
|
|
963
|
+
catch (error) {
|
|
964
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
965
|
+
logError(formatSkillVersionCreateError(message, conflictHint));
|
|
966
|
+
process.exitCode = 1;
|
|
967
|
+
}
|
|
968
|
+
}
|
|
969
|
+
export async function runSkillVersionUpdate(options = {}) {
|
|
970
|
+
try {
|
|
971
|
+
const apiState = await requireApiState(options);
|
|
972
|
+
if (!apiState)
|
|
973
|
+
return;
|
|
974
|
+
const resolvedContext = await resolveSkillContext(options, apiState);
|
|
975
|
+
const seed = parseJsonSeed(options.input);
|
|
976
|
+
const id = ensureSkillId(resolveStringField(options.id, seed, 'id'));
|
|
977
|
+
const patch = compactPayload({
|
|
978
|
+
projectId: normalizeNonEmpty(options.projectId) ??
|
|
979
|
+
normalizeNonEmpty(seed.projectId),
|
|
980
|
+
version: resolveIntegerField(options.version, seed, 'version', '--version'),
|
|
981
|
+
status: resolveStringField(options.status, seed, 'status'),
|
|
982
|
+
content: resolveTextField(options.content, seed, 'content'),
|
|
983
|
+
entryFile: resolveStringField(options.entryFile, seed, 'entryFile'),
|
|
984
|
+
skillStandard: resolveStringField(options.skillStandard, seed, 'skillStandard'),
|
|
985
|
+
meta: resolveUnknownField(options.meta, seed, 'meta', '--meta'),
|
|
986
|
+
refType: resolveStringField(options.refType, seed, 'refType'),
|
|
987
|
+
refId: resolveStringField(options.refId, seed, 'refId'),
|
|
988
|
+
});
|
|
989
|
+
if (Object.keys(patch).length === 0) {
|
|
990
|
+
throw new Error('Provide at least one skill version field to update.');
|
|
991
|
+
}
|
|
992
|
+
const input = { id, patch };
|
|
993
|
+
await invokeSkillTool(apiState, options, resolvedContext, {
|
|
994
|
+
command: 'skill.version.update',
|
|
995
|
+
toolId: 'agentspace.skill-version.update-skill-version',
|
|
996
|
+
input,
|
|
997
|
+
successText: 'Skill version updated.',
|
|
998
|
+
});
|
|
999
|
+
}
|
|
1000
|
+
catch (error) {
|
|
1001
|
+
logError(error instanceof Error ? error.message : String(error));
|
|
1002
|
+
process.exitCode = 1;
|
|
1003
|
+
}
|
|
1004
|
+
}
|
|
1005
|
+
export async function runSkillVersionPublish(options = {}) {
|
|
1006
|
+
try {
|
|
1007
|
+
const apiState = await requireApiState(options);
|
|
1008
|
+
if (!apiState)
|
|
1009
|
+
return;
|
|
1010
|
+
const resolvedContext = await resolveSkillContext(options, apiState);
|
|
1011
|
+
const id = ensureSkillId(options.id);
|
|
1012
|
+
const input = { id };
|
|
1013
|
+
await invokeSkillTool(apiState, options, resolvedContext, {
|
|
1014
|
+
command: 'skill.version.publish',
|
|
1015
|
+
toolId: 'agentspace.skill-version.publish-skill-version',
|
|
1016
|
+
input,
|
|
1017
|
+
successText: 'Skill version published.',
|
|
1018
|
+
});
|
|
1019
|
+
}
|
|
1020
|
+
catch (error) {
|
|
1021
|
+
logError(error instanceof Error ? error.message : String(error));
|
|
1022
|
+
process.exitCode = 1;
|
|
1023
|
+
}
|
|
1024
|
+
}
|
|
1025
|
+
export async function runSkillVersionDelete(options = {}) {
|
|
1026
|
+
try {
|
|
1027
|
+
ensureDestructiveWrite(options, 'This command deletes hosted skill versions.');
|
|
1028
|
+
const apiState = await requireApiState(options);
|
|
1029
|
+
if (!apiState)
|
|
1030
|
+
return;
|
|
1031
|
+
const resolvedContext = await resolveSkillContext(options, apiState);
|
|
1032
|
+
const id = ensureSkillId(options.id);
|
|
1033
|
+
const input = { id };
|
|
1034
|
+
await invokeSkillTool(apiState, options, resolvedContext, {
|
|
1035
|
+
command: 'skill.version.delete',
|
|
1036
|
+
toolId: 'agentspace.skill-version.remove-skill-version',
|
|
1037
|
+
input,
|
|
1038
|
+
successText: 'Skill version deleted.',
|
|
1039
|
+
});
|
|
1040
|
+
}
|
|
1041
|
+
catch (error) {
|
|
1042
|
+
logError(error instanceof Error ? error.message : String(error));
|
|
1043
|
+
process.exitCode = 1;
|
|
1044
|
+
}
|
|
1045
|
+
}
|
|
1046
|
+
function applySkillContextOptions(cmd, params = {}) {
|
|
1047
|
+
applyCommonOptions(cmd);
|
|
1048
|
+
cmd.option('--project-id <id>', 'Project id used to resolve repo-bound skill ownership');
|
|
1049
|
+
cmd.option('--project-name <name>', 'Project name used to resolve repo-bound skill ownership');
|
|
1050
|
+
cmd.option('--hosted-project-slug <slug>', 'Resolve a hosted project by slug through the gateway, bypassing repo config lookup');
|
|
1051
|
+
cmd.option('--scope-id <id>', 'Explicit scope id override for skill shell ownership');
|
|
1052
|
+
if (params.withScopeResolution) {
|
|
1053
|
+
cmd.option('--scope-resolution <mode>', 'Scope resolution policy: explicit or cascade');
|
|
1054
|
+
}
|
|
1055
|
+
return cmd;
|
|
1056
|
+
}
|
|
1057
|
+
function applyWriteGuards(cmd, params = {}) {
|
|
1058
|
+
cmd.option('--preview', 'Return a validated preflight summary without executing the tool');
|
|
1059
|
+
cmd.option('--apply', 'Execute the hosted write operation');
|
|
1060
|
+
if (params.destructive) {
|
|
1061
|
+
cmd.option('--confirm', 'Required with --apply for destructive deletes');
|
|
1062
|
+
}
|
|
1063
|
+
cmd.option('--idempotency-key <key>', 'Optional idempotency key for hosted writes');
|
|
1064
|
+
return cmd;
|
|
1065
|
+
}
|
|
1066
|
+
function applyJsonSeedOption(cmd) {
|
|
1067
|
+
cmd.option('--input <jsonOrFile>', 'Optional JSON object seed or @file.json');
|
|
1068
|
+
return cmd;
|
|
1069
|
+
}
|
|
1070
|
+
export function makeSkillCommand() {
|
|
1071
|
+
const cmd = new Command('skill').description('Agentspace skill and skill-version sugar commands over the hosted AOPS gateway');
|
|
1072
|
+
applySkillContextOptions(cmd.command('search')
|
|
1073
|
+
.description('Find current published hosted skills by bounded deterministic metadata ranking')
|
|
1074
|
+
.option('--q <text>', 'Search phrase (maximum 256 UTF-8 bytes)')
|
|
1075
|
+
.option('--query <text>', 'Long-form alias for --q')
|
|
1076
|
+
.option('--limit <n>', 'Maximum candidates, 1 to 5'), { withScopeResolution: true }).action(async (options) => {
|
|
1077
|
+
await runSkillSearch(options);
|
|
1078
|
+
});
|
|
1079
|
+
applySkillContextOptions(cmd.command('ask')
|
|
1080
|
+
.description('Recommend hosted skills using one deterministic metadata search (no LLM or body loading)')
|
|
1081
|
+
.option('--q <text>', 'Operator or agent need (maximum 256 UTF-8 bytes)')
|
|
1082
|
+
.option('--query <text>', 'Long-form alias for --q')
|
|
1083
|
+
.option('--limit <n>', 'Maximum candidates, 1 to 5'), { withScopeResolution: true }).action(async (options) => {
|
|
1084
|
+
await runSkillAsk(options);
|
|
1085
|
+
});
|
|
1086
|
+
applySkillContextOptions(cmd.command('list')
|
|
1087
|
+
.description('List reusable skill shells')
|
|
1088
|
+
.option('--name <text>', 'Skill name filter')
|
|
1089
|
+
.option('--tag <tag>', 'Skill tag filter', collectRepeatedOption, [])
|
|
1090
|
+
.option('--limit <n>', 'Optional item limit'), { withScopeResolution: true }).action(async (options) => {
|
|
1091
|
+
await runSkillList(options);
|
|
1092
|
+
});
|
|
1093
|
+
applySkillContextOptions(cmd.command('get')
|
|
1094
|
+
.description('Get a skill shell by id')
|
|
1095
|
+
.requiredOption('--id <id>', 'Skill id')).action(async (options) => {
|
|
1096
|
+
await runSkillGet(options);
|
|
1097
|
+
});
|
|
1098
|
+
applyWriteGuards(applyJsonSeedOption(applySkillContextOptions(cmd.command('create')
|
|
1099
|
+
.description('Create a scope-owned skill shell')
|
|
1100
|
+
.requiredOption('--name <text>', 'Skill name')
|
|
1101
|
+
.option('--description <text>', 'Skill description')
|
|
1102
|
+
.option('--short-description <text>', 'Short skill description')
|
|
1103
|
+
.option('--tag <tag>', 'Skill tag', collectRepeatedOption, [])))).action(async (options) => {
|
|
1104
|
+
await runSkillCreate(options);
|
|
1105
|
+
});
|
|
1106
|
+
applyWriteGuards(applyJsonSeedOption(applySkillContextOptions(cmd.command('update')
|
|
1107
|
+
.description('Update a skill shell')
|
|
1108
|
+
.requiredOption('--id <id>', 'Skill id')
|
|
1109
|
+
.option('--name <text>', 'Skill name')
|
|
1110
|
+
.option('--description <text>', 'Skill description')
|
|
1111
|
+
.option('--short-description <text>', 'Short skill description')
|
|
1112
|
+
.option('--tag <tag>', 'Skill tag', collectRepeatedOption, [])
|
|
1113
|
+
.option('--current-version-id <id>', 'Explicitly set currentVersionId on the skill shell')))).action(async (options) => {
|
|
1114
|
+
await runSkillUpdate(options);
|
|
1115
|
+
});
|
|
1116
|
+
applyWriteGuards(applySkillContextOptions(cmd.command('delete')
|
|
1117
|
+
.description('Delete a skill shell')
|
|
1118
|
+
.requiredOption('--id <id>', 'Skill id')), { destructive: true }).action(async (options) => {
|
|
1119
|
+
await runSkillDelete(options);
|
|
1120
|
+
});
|
|
1121
|
+
applySkillContextOptions(cmd.command('inspect')
|
|
1122
|
+
.description('Load the skill shell, versions, and current version together')
|
|
1123
|
+
.requiredOption('--id <id>', 'Skill id')
|
|
1124
|
+
.option('--summary', 'Omit skill-version content and print content size summaries')).action(async (options) => {
|
|
1125
|
+
await runSkillInspect(options);
|
|
1126
|
+
});
|
|
1127
|
+
applySkillContextOptions(cmd.command('current')
|
|
1128
|
+
.description('Resolve the current skill version for a skill shell')
|
|
1129
|
+
.requiredOption('--id <id>', 'Skill id')
|
|
1130
|
+
.option('--summary', 'Omit current version content and print content size summary')).action(async (options) => {
|
|
1131
|
+
await runSkillCurrent(options);
|
|
1132
|
+
});
|
|
1133
|
+
const version = cmd.command('version').description('Skill-version authoring and lifecycle commands');
|
|
1134
|
+
applySkillContextOptions(version.command('list')
|
|
1135
|
+
.description('List skill versions')
|
|
1136
|
+
.option('--skill-id <id>', 'Skill id filter')
|
|
1137
|
+
.option('--status <status>', 'Skill version status filter')
|
|
1138
|
+
.option('--ref-type <type>', 'Skill version refType filter')
|
|
1139
|
+
.option('--ref-id <id>', 'Skill version refId filter')
|
|
1140
|
+
.option('--limit <n>', 'Optional item limit')
|
|
1141
|
+
.option('--summary', 'Omit skill-version content and print content size summaries')).action(async (options) => {
|
|
1142
|
+
await runSkillVersionList(options);
|
|
1143
|
+
});
|
|
1144
|
+
applySkillContextOptions(version.command('get')
|
|
1145
|
+
.description('Get a skill version by id')
|
|
1146
|
+
.requiredOption('--id <id>', 'Skill version id')).action(async (options) => {
|
|
1147
|
+
await runSkillVersionGet(options);
|
|
1148
|
+
});
|
|
1149
|
+
applyWriteGuards(applyJsonSeedOption(applySkillContextOptions(version.command('create')
|
|
1150
|
+
.description('Create a new skill version')
|
|
1151
|
+
.requiredOption('--skill-id <id>', 'Skill id')
|
|
1152
|
+
.option('--version <n>', 'Optional explicit version number')
|
|
1153
|
+
.option('--status <status>', 'Skill version status')
|
|
1154
|
+
.option('--content <textOrFile>', 'Skill content or @file.md')
|
|
1155
|
+
.option('--entry-file <path>', 'Optional entry file path')
|
|
1156
|
+
.option('--skill-standard <id>', 'Optional skill standard id')
|
|
1157
|
+
.option('--meta <jsonOrFile>', 'JSON meta object/array/value or @file.json')
|
|
1158
|
+
.option('--ref-type <type>', 'Optional refType linkage')
|
|
1159
|
+
.option('--ref-id <id>', 'Optional refId linkage')))).action(async (options) => {
|
|
1160
|
+
await runSkillVersionCreate(options);
|
|
1161
|
+
});
|
|
1162
|
+
applyWriteGuards(applyJsonSeedOption(applySkillContextOptions(version.command('update')
|
|
1163
|
+
.description('Update an existing skill version')
|
|
1164
|
+
.requiredOption('--id <id>', 'Skill version id')
|
|
1165
|
+
.option('--version <n>', 'Explicit version number')
|
|
1166
|
+
.option('--status <status>', 'Skill version status')
|
|
1167
|
+
.option('--content <textOrFile>', 'Skill content or @file.md')
|
|
1168
|
+
.option('--entry-file <path>', 'Optional entry file path')
|
|
1169
|
+
.option('--skill-standard <id>', 'Optional skill standard id')
|
|
1170
|
+
.option('--meta <jsonOrFile>', 'JSON meta object/array/value or @file.json')
|
|
1171
|
+
.option('--ref-type <type>', 'Optional refType linkage')
|
|
1172
|
+
.option('--ref-id <id>', 'Optional refId linkage')))).action(async (options) => {
|
|
1173
|
+
await runSkillVersionUpdate(options);
|
|
1174
|
+
});
|
|
1175
|
+
applyWriteGuards(applySkillContextOptions(version.command('publish')
|
|
1176
|
+
.description('Publish a skill version and sync the skill current version')
|
|
1177
|
+
.requiredOption('--id <id>', 'Skill version id'))).action(async (options) => {
|
|
1178
|
+
await runSkillVersionPublish(options);
|
|
1179
|
+
});
|
|
1180
|
+
applyWriteGuards(applySkillContextOptions(version.command('delete')
|
|
1181
|
+
.description('Delete a skill version')
|
|
1182
|
+
.requiredOption('--id <id>', 'Skill version id')), { destructive: true }).action(async (options) => {
|
|
1183
|
+
await runSkillVersionDelete(options);
|
|
1184
|
+
});
|
|
1185
|
+
cmd.addHelpText('after', buildOperatorCookbook({
|
|
1186
|
+
examples: [
|
|
1187
|
+
'aops skill search --q "kanban sprint planning" --limit 3',
|
|
1188
|
+
'aops skill ask --q "Which skill should an agent use to plan a sprint?" --limit 3',
|
|
1189
|
+
'aops-cli skill create --name "Projectman Delivery" --apply --json',
|
|
1190
|
+
'aops-cli skill version list --skill-id <skill-id> --summary --json',
|
|
1191
|
+
'aops-cli skill version create --skill-id <skill-id> --content @./SKILL.md --apply --json',
|
|
1192
|
+
'aops-cli skill version publish --id <skill-version-id> --hosted-project-slug aops --apply --json',
|
|
1193
|
+
'aops-cli sync pull --apply --hosted-project-slug aops --json',
|
|
1194
|
+
'aops-cli skill inspect --id <skill-id> --json',
|
|
1195
|
+
'aops-cli skill current --id <skill-id> --summary --json',
|
|
1196
|
+
],
|
|
1197
|
+
guide: GUIDE_PATHS.agentspace,
|
|
1198
|
+
notes: [
|
|
1199
|
+
'Canonical reusable skill truth lives in aops-server/DB; create/update/publish through these skill commands.',
|
|
1200
|
+
'.aops/hosted/skills/** is a read-only repo mirror refreshed by sync pull/bootstrap, not an authoring source.',
|
|
1201
|
+
'Skill version list/inspect/current are curated read surfaces and do not require --apply.',
|
|
1202
|
+
'skill search/ask inspect only current published package metadata; they do not load skill bodies or maintain a local search index.',
|
|
1203
|
+
'skill ask is a deterministic projection of one skill search result, not an LLM-generated semantic answer.',
|
|
1204
|
+
'Use version list/inspect/current --summary for token-efficient checks; omit --summary only when full skill content is needed.',
|
|
1205
|
+
'Use --hosted-project-slug <slug> from a non-bound repo when the hosted project is not in local repo config.',
|
|
1206
|
+
'When --version is omitted, the CLI resolves the next version number from existing skill versions.',
|
|
1207
|
+
'After publishing a hosted skill, run sync pull for the hosted project and verify the mirror currentVersionId/currentVersionStatus before review.',
|
|
1208
|
+
],
|
|
1209
|
+
}));
|
|
1210
|
+
return cmd;
|
|
1211
|
+
}
|