@oxygen-agent/cli 1.591.1 → 1.615.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/README.md +1 -1
- package/dist/command-manifest.d.ts +18 -0
- package/dist/command-manifest.js +106 -5
- package/dist/help.js +2 -1
- package/dist/index.js +273 -38
- package/dist/skills.d.ts +6 -0
- package/dist/skills.js +200 -1
- package/node_modules/@oxygen/shared/dist/capability-discovery.d.ts +36 -0
- package/node_modules/@oxygen/shared/dist/capability-discovery.js +766 -0
- package/node_modules/@oxygen/shared/dist/cli-result.js +4 -3
- package/node_modules/@oxygen/shared/dist/index.d.ts +2 -0
- package/node_modules/@oxygen/shared/dist/index.js +2 -0
- package/node_modules/@oxygen/shared/dist/pricing-sheet.d.ts +1 -1
- package/node_modules/@oxygen/shared/dist/pricing-sheet.js +1 -1
- package/node_modules/@oxygen/shared/dist/recipes.d.ts +1 -1
- package/node_modules/@oxygen/shared/dist/recipes.js +4 -2
- package/node_modules/@oxygen/shared/dist/sequence-template.js +0 -0
- package/node_modules/@oxygen/shared/dist/sequence-terminal-events.d.ts +47 -0
- package/node_modules/@oxygen/shared/dist/sequence-terminal-events.js +70 -0
- package/node_modules/@oxygen/shared/dist/version.d.ts +1 -1
- package/node_modules/@oxygen/shared/dist/version.js +1 -1
- package/node_modules/@oxygen/workflows/dist/graph/lint.js +3 -0
- package/node_modules/@oxygen/workflows/dist/graph/manifest-schema.d.ts +5 -0
- package/node_modules/@oxygen/workflows/dist/graph/manifest-schema.js +5 -0
- package/node_modules/@oxygen/workflows/dist/graph/types.d.ts +2 -0
- package/node_modules/@oxygen/workflows/dist/index.d.ts +4 -0
- package/node_modules/@oxygen/workflows/dist/index.js +5 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -17,6 +17,7 @@ export type CommandManifestEntry = {
|
|
|
17
17
|
flags: CommandManifestFlag[];
|
|
18
18
|
spends_credits: boolean;
|
|
19
19
|
mutates: boolean;
|
|
20
|
+
preview_by_default: boolean;
|
|
20
21
|
json_supported: boolean;
|
|
21
22
|
hidden: boolean;
|
|
22
23
|
};
|
|
@@ -27,4 +28,21 @@ export type CommandManifest = {
|
|
|
27
28
|
exit_codes: Record<string, string>;
|
|
28
29
|
commands: CommandManifestEntry[];
|
|
29
30
|
};
|
|
31
|
+
export type CommandSearchResult = {
|
|
32
|
+
query: string;
|
|
33
|
+
total_matches: number;
|
|
34
|
+
returned: number;
|
|
35
|
+
route: Record<string, unknown> | null;
|
|
36
|
+
commands: Array<Pick<CommandManifestEntry, "name" | "group" | "description" | "spends_credits" | "mutates" | "json_supported" | "hidden"> & {
|
|
37
|
+
score: number;
|
|
38
|
+
}>;
|
|
39
|
+
hydrate: string;
|
|
40
|
+
};
|
|
30
41
|
export declare function buildCommandManifest(program: Command, binaryName: string): CommandManifest;
|
|
42
|
+
/** Bounded natural-language projection over the complete local manifest. */
|
|
43
|
+
export declare function searchCommandManifest(manifest: CommandManifest, query: string, requestedLimit?: number): CommandSearchResult;
|
|
44
|
+
/** Exact second hop after search; avoids serializing the full command grammar. */
|
|
45
|
+
export declare function getCommandManifestEntry(manifest: CommandManifest, exactName: string): (CommandManifestEntry & {
|
|
46
|
+
invoke: string;
|
|
47
|
+
}) | null;
|
|
48
|
+
export declare function suggestCommandNames(manifest: CommandManifest, query: string, limit?: number): string[];
|
package/dist/command-manifest.js
CHANGED
|
@@ -1,4 +1,10 @@
|
|
|
1
|
-
import { CLI_EXIT_CODE_TABLE, OXYGEN_VERSION } from "@oxygen/shared";
|
|
1
|
+
import { CLI_EXIT_CODE_TABLE, OXYGEN_VERSION, inferCapabilityRoute, serializeCapabilityRoute, } from "@oxygen/shared";
|
|
2
|
+
const DEFAULT_COMMAND_SEARCH_LIMIT = 10;
|
|
3
|
+
const MAX_COMMAND_SEARCH_LIMIT = 25;
|
|
4
|
+
const COMMAND_SEARCH_STOP_WORDS = new Set([
|
|
5
|
+
"a", "an", "and", "for", "from", "get", "i", "in", "into", "is", "me",
|
|
6
|
+
"of", "on", "or", "please", "show", "the", "to", "want", "with",
|
|
7
|
+
]);
|
|
2
8
|
// Leaf verbs (or their hyphen-prefix) that name a state mutation, external
|
|
3
9
|
// write, or paid side effect. `mutates` is derived from this set (see
|
|
4
10
|
// toManifestEntry). It is hand-curated and cross-checked against the real leaf
|
|
@@ -47,14 +53,108 @@ export function buildCommandManifest(program, binaryName) {
|
|
|
47
53
|
commands,
|
|
48
54
|
};
|
|
49
55
|
}
|
|
56
|
+
/** Bounded natural-language projection over the complete local manifest. */
|
|
57
|
+
export function searchCommandManifest(manifest, query, requestedLimit = DEFAULT_COMMAND_SEARCH_LIMIT) {
|
|
58
|
+
const normalized = query.trim();
|
|
59
|
+
const tokens = tokenizeCommandQuery(normalized);
|
|
60
|
+
const route = inferCapabilityRoute(normalized);
|
|
61
|
+
const limit = Math.min(MAX_COMMAND_SEARCH_LIMIT, Math.max(1, Math.floor(requestedLimit)));
|
|
62
|
+
const matches = tokens.length === 0
|
|
63
|
+
? []
|
|
64
|
+
: manifest.commands
|
|
65
|
+
.map((command) => ({ command, score: scoreCommand(command, tokens, route) }))
|
|
66
|
+
.filter((entry) => entry.score > 0)
|
|
67
|
+
.sort((a, b) => b.score - a.score || a.command.name.localeCompare(b.command.name));
|
|
68
|
+
return {
|
|
69
|
+
query: normalized,
|
|
70
|
+
total_matches: matches.length,
|
|
71
|
+
returned: Math.min(limit, matches.length),
|
|
72
|
+
route: serializeCapabilityRoute(route),
|
|
73
|
+
commands: matches.slice(0, limit).map(({ command, score }) => ({
|
|
74
|
+
name: command.name,
|
|
75
|
+
group: command.group,
|
|
76
|
+
description: command.description,
|
|
77
|
+
spends_credits: command.spends_credits,
|
|
78
|
+
mutates: command.mutates,
|
|
79
|
+
json_supported: command.json_supported,
|
|
80
|
+
hidden: command.hidden,
|
|
81
|
+
score,
|
|
82
|
+
})),
|
|
83
|
+
hydrate: `${manifest.binary} commands get <exact-command> --json`,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
/** Exact second hop after search; avoids serializing the full command grammar. */
|
|
87
|
+
export function getCommandManifestEntry(manifest, exactName) {
|
|
88
|
+
const normalized = exactName.trim().replace(/\s+/g, " ");
|
|
89
|
+
const command = manifest.commands.find((entry) => entry.name === normalized);
|
|
90
|
+
return command ? { ...command, invoke: `${manifest.binary} ${command.name}` } : null;
|
|
91
|
+
}
|
|
92
|
+
export function suggestCommandNames(manifest, query, limit = 3) {
|
|
93
|
+
const tokens = tokenizeCommandQuery(query);
|
|
94
|
+
return manifest.commands
|
|
95
|
+
.map((command) => ({ command, score: scoreCommand(command, tokens, null) }))
|
|
96
|
+
.filter((entry) => entry.score > 0)
|
|
97
|
+
.sort((a, b) => b.score - a.score || a.command.name.localeCompare(b.command.name))
|
|
98
|
+
.slice(0, Math.max(1, limit))
|
|
99
|
+
.map((entry) => entry.command.name);
|
|
100
|
+
}
|
|
101
|
+
function tokenizeCommandQuery(query) {
|
|
102
|
+
return query
|
|
103
|
+
.toLowerCase()
|
|
104
|
+
.split(/[^a-z0-9]+/)
|
|
105
|
+
.filter((token) => token.length > 0 && !COMMAND_SEARCH_STOP_WORDS.has(token));
|
|
106
|
+
}
|
|
107
|
+
function scoreCommand(command, tokens, route) {
|
|
108
|
+
const name = command.name.toLowerCase();
|
|
109
|
+
const description = command.description.toLowerCase();
|
|
110
|
+
const searchableDetails = [
|
|
111
|
+
...command.arguments.map((argument) => `${argument.name} ${argument.description}`),
|
|
112
|
+
...command.flags.map((flag) => `${flag.flags} ${flag.description}`),
|
|
113
|
+
].join(" ").toLowerCase();
|
|
114
|
+
let score = 0;
|
|
115
|
+
let matched = 0;
|
|
116
|
+
for (const token of tokens) {
|
|
117
|
+
let tokenMatched = false;
|
|
118
|
+
if (name.includes(token)) {
|
|
119
|
+
score += 8;
|
|
120
|
+
tokenMatched = true;
|
|
121
|
+
}
|
|
122
|
+
if (description.includes(token)) {
|
|
123
|
+
score += 3;
|
|
124
|
+
tokenMatched = true;
|
|
125
|
+
}
|
|
126
|
+
if (searchableDetails.includes(token)) {
|
|
127
|
+
score += 1;
|
|
128
|
+
tokenMatched = true;
|
|
129
|
+
}
|
|
130
|
+
if (tokenMatched)
|
|
131
|
+
matched += 1;
|
|
132
|
+
}
|
|
133
|
+
const routeIndex = route?.recommendedCommands.findIndex((recommended) => name === recommended || name.startsWith(`${recommended} `)) ?? -1;
|
|
134
|
+
const routeGroups = new Set([
|
|
135
|
+
...(route?.card.endpointSections ?? []),
|
|
136
|
+
...(route?.card.gatewayCommands ?? []).map((recommended) => recommended.split(" ")[0] ?? ""),
|
|
137
|
+
...(route?.recommendedCommands ?? []).map((recommended) => recommended.split(" ")[0] ?? ""),
|
|
138
|
+
]);
|
|
139
|
+
if (route && routeIndex < 0 && !routeGroups.has(command.group))
|
|
140
|
+
return 0;
|
|
141
|
+
if (routeIndex < 0 && matched < Math.ceil(tokens.length / 2))
|
|
142
|
+
return 0;
|
|
143
|
+
if (tokens.length > 0 && matched > 0)
|
|
144
|
+
score += Math.round((matched / tokens.length) * 20);
|
|
145
|
+
if (routeIndex >= 0)
|
|
146
|
+
score += 500 - (routeIndex * 25);
|
|
147
|
+
return score;
|
|
148
|
+
}
|
|
50
149
|
function collectCommand(command, ancestors, out) {
|
|
51
150
|
const path = [...ancestors, command.name()];
|
|
52
151
|
const children = command.commands;
|
|
53
|
-
//
|
|
54
|
-
// commands
|
|
55
|
-
|
|
152
|
+
// Most group commands only hold children. A small number (notably the
|
|
153
|
+
// backwards-compatible `commands` index) also have their own action, so
|
|
154
|
+
// preserve them as invocable entries while still walking their children.
|
|
155
|
+
const hasAction = Boolean(command._actionHandler);
|
|
156
|
+
if (children.length === 0 || hasAction) {
|
|
56
157
|
out.push(toManifestEntry(command, path));
|
|
57
|
-
return;
|
|
58
158
|
}
|
|
59
159
|
for (const child of children) {
|
|
60
160
|
collectCommand(child, path, out);
|
|
@@ -92,6 +192,7 @@ function toManifestEntry(command, path) {
|
|
|
92
192
|
(path[0] === "copilot" &&
|
|
93
193
|
(leafVerb === "send" || leafVerb === "approve")),
|
|
94
194
|
mutates: MUTATING_VERBS.has(leafVerb) || MUTATING_VERBS.has(verbPrefix),
|
|
195
|
+
preview_by_default: flagStrings.some((flags) => flags.includes("--live")),
|
|
95
196
|
json_supported: flagStrings.some((flags) => flags.includes("--json")),
|
|
96
197
|
hidden: isHiddenCommand(command),
|
|
97
198
|
};
|
package/dist/help.js
CHANGED
|
@@ -136,7 +136,8 @@ export function applyOxygenHelp(program, binaryName) {
|
|
|
136
136
|
// OXYGEN_API_KEY never runs it — without this step nothing on any
|
|
137
137
|
// first-touch surface names the skill that teaches the GTM loops.
|
|
138
138
|
` 2. ${binaryName} skills install --json load the skills that teach the GTM loops (automatic after login)`,
|
|
139
|
-
` 3. ${binaryName}
|
|
139
|
+
` 3. ${binaryName} context resolve --json load workspace context before operating primitives`,
|
|
140
|
+
` 4. ${binaryName} capabilities search "<goal>" --json route the goal, then hydrate one exact command`,
|
|
140
141
|
` Recurring monitors: use a shareable Blueprint; start with ${binaryName} blueprints list --json.`,
|
|
141
142
|
"",
|
|
142
143
|
"Conventions:",
|