@kodiak-finance/orderly-devkit 1.0.2
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 +160 -0
- package/bin/cli.js +112 -0
- package/package.json +37 -0
- package/src/commands/create/module.js +49 -0
- package/src/commands/create/plugin.js +270 -0
- package/src/commands/delete.js +224 -0
- package/src/commands/disable.js +219 -0
- package/src/commands/list.js +196 -0
- package/src/commands/login.js +147 -0
- package/src/commands/logout.js +22 -0
- package/src/commands/mcp/detect.js +128 -0
- package/src/commands/mcp/install.js +122 -0
- package/src/commands/mcp.js +9 -0
- package/src/commands/skills/install.js +211 -0
- package/src/commands/skills.js +10 -0
- package/src/commands/submit.js +457 -0
- package/src/commands/update.js +240 -0
- package/src/commands/view.js +76 -0
- package/src/commands/whoami.js +19 -0
- package/src/internal/auth.js +222 -0
- package/src/internal/constants.js +80 -0
- package/src/internal/login-server.js +114 -0
- package/src/internal/manifest.js +189 -0
- package/src/internal/orderlySdkDocsMcpDetect.js +255 -0
- package/src/internal/templateGenerator.js +294 -0
- package/src/shared.js +136 -0
- package/src/version.ts +13 -0
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
const crypto = require("crypto");
|
|
2
|
+
const { exec } = require("child_process");
|
|
3
|
+
const { heading, info, success, warn, error } = require("../shared");
|
|
4
|
+
const { saveOAuthToken, isLoggedIn } = require("../internal/auth");
|
|
5
|
+
const { startCallbackServer } = require("../internal/login-server");
|
|
6
|
+
const {
|
|
7
|
+
CLI_CALLBACK_PORT,
|
|
8
|
+
CLI_LOGIN_TIMEOUT_MS,
|
|
9
|
+
MARKETPLACE_WEB_LOGIN_URL,
|
|
10
|
+
} = require("../internal/constants");
|
|
11
|
+
|
|
12
|
+
function openBrowser(url) {
|
|
13
|
+
const platform = process.platform;
|
|
14
|
+
let command;
|
|
15
|
+
if (platform === "darwin") {
|
|
16
|
+
command = `open "${url}"`;
|
|
17
|
+
} else if (platform === "win32") {
|
|
18
|
+
command = `start "" "${url}"`;
|
|
19
|
+
} else {
|
|
20
|
+
command = `xdg-open "${url}"`;
|
|
21
|
+
}
|
|
22
|
+
return new Promise((resolve, reject) => {
|
|
23
|
+
exec(command, (err) => {
|
|
24
|
+
if (err) reject(err);
|
|
25
|
+
else resolve();
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
module.exports = {
|
|
31
|
+
command: "login",
|
|
32
|
+
describe: "Login to Orderly Marketplace via GitHub",
|
|
33
|
+
builder: (yargs) => {
|
|
34
|
+
return yargs
|
|
35
|
+
.option("port", {
|
|
36
|
+
alias: "p",
|
|
37
|
+
type: "number",
|
|
38
|
+
describe:
|
|
39
|
+
"number; local callback server port used for the OAuth redirect (must be available). Default is the CLI_CALLBACK_PORT constant",
|
|
40
|
+
default: CLI_CALLBACK_PORT,
|
|
41
|
+
})
|
|
42
|
+
.option("force", {
|
|
43
|
+
alias: "f",
|
|
44
|
+
type: "boolean",
|
|
45
|
+
describe:
|
|
46
|
+
"boolean; if true, force re-login even if you're already logged in",
|
|
47
|
+
default: false,
|
|
48
|
+
})
|
|
49
|
+
.example("orderly login", "Open the browser and complete GitHub OAuth")
|
|
50
|
+
.example(
|
|
51
|
+
"orderly login --force --port 9877",
|
|
52
|
+
"Re-authenticate and use a custom callback port",
|
|
53
|
+
);
|
|
54
|
+
},
|
|
55
|
+
handler: async (argv) => {
|
|
56
|
+
heading("Login to Orderly Marketplace");
|
|
57
|
+
info("This will open your browser to authenticate via GitHub.\n");
|
|
58
|
+
|
|
59
|
+
if (isLoggedIn() && !argv.force) {
|
|
60
|
+
warn("You are already logged in.");
|
|
61
|
+
info("Use 'orderly login --force' to re-authenticate.");
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Generate CSRF state
|
|
66
|
+
const state = crypto.randomBytes(16).toString("hex");
|
|
67
|
+
const port = argv.port;
|
|
68
|
+
const browserUrl = `${MARKETPLACE_WEB_LOGIN_URL}?port=${port}&state=${state}`;
|
|
69
|
+
|
|
70
|
+
// Start local callback server
|
|
71
|
+
const { server, waitForToken } = startCallbackServer({ port, state });
|
|
72
|
+
|
|
73
|
+
// Handle server errors (e.g. port in use)
|
|
74
|
+
const serverReady = new Promise((resolve, reject) => {
|
|
75
|
+
server.on("error", (err) => {
|
|
76
|
+
if (err.code === "EADDRINUSE") {
|
|
77
|
+
reject(
|
|
78
|
+
new Error(
|
|
79
|
+
`Port ${port} is already in use. Use --port to specify a different port.`,
|
|
80
|
+
),
|
|
81
|
+
);
|
|
82
|
+
} else {
|
|
83
|
+
reject(err);
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
server.on("listening", resolve);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
try {
|
|
90
|
+
server.listen(port);
|
|
91
|
+
await serverReady;
|
|
92
|
+
} catch (err) {
|
|
93
|
+
error(err.message);
|
|
94
|
+
server.close();
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Open browser
|
|
99
|
+
info("Opening browser...");
|
|
100
|
+
info(` ${browserUrl}\n`);
|
|
101
|
+
try {
|
|
102
|
+
await openBrowser(browserUrl);
|
|
103
|
+
} catch (e) {
|
|
104
|
+
info("Could not open browser automatically.");
|
|
105
|
+
info("Please open the URL above in your browser manually.\n");
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
info("Waiting for authentication...");
|
|
109
|
+
|
|
110
|
+
// Timeout
|
|
111
|
+
let timedOut = false;
|
|
112
|
+
const timeout = setTimeout(() => {
|
|
113
|
+
timedOut = true;
|
|
114
|
+
error("Login timed out after 3 minutes.");
|
|
115
|
+
}, CLI_LOGIN_TIMEOUT_MS);
|
|
116
|
+
|
|
117
|
+
// Handle Ctrl+C during login
|
|
118
|
+
const onSigInt = () => {
|
|
119
|
+
info("\nLogin cancelled.");
|
|
120
|
+
clearTimeout(timeout);
|
|
121
|
+
server.close();
|
|
122
|
+
process.exit(1);
|
|
123
|
+
};
|
|
124
|
+
process.on("SIGINT", onSigInt);
|
|
125
|
+
|
|
126
|
+
try {
|
|
127
|
+
const tokenData = await waitForToken;
|
|
128
|
+
clearTimeout(timeout);
|
|
129
|
+
process.removeListener("SIGINT", onSigInt);
|
|
130
|
+
|
|
131
|
+
saveOAuthToken(tokenData);
|
|
132
|
+
|
|
133
|
+
success("Login successful!");
|
|
134
|
+
const displayName = tokenData.email || tokenData.username;
|
|
135
|
+
if (displayName) {
|
|
136
|
+
info(`Logged in as: ${displayName}`);
|
|
137
|
+
}
|
|
138
|
+
info("Use 'orderly-devkit whoami' to verify your account.");
|
|
139
|
+
} catch (err) {
|
|
140
|
+
if (!timedOut) {
|
|
141
|
+
error(`Login failed: ${err.message}`);
|
|
142
|
+
}
|
|
143
|
+
} finally {
|
|
144
|
+
server.close();
|
|
145
|
+
}
|
|
146
|
+
},
|
|
147
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
const { heading, info, success, warn } = require("../shared");
|
|
2
|
+
const { isLoggedIn, logout } = require("../internal/auth");
|
|
3
|
+
|
|
4
|
+
module.exports = {
|
|
5
|
+
command: "logout",
|
|
6
|
+
describe: "Logout from Orderly Marketplace",
|
|
7
|
+
handler: async () => {
|
|
8
|
+
heading("Logout from Orderly Marketplace");
|
|
9
|
+
|
|
10
|
+
if (!isLoggedIn()) {
|
|
11
|
+
warn("You are not logged in.");
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
info("This command will clear your stored authentication.\n");
|
|
16
|
+
|
|
17
|
+
logout();
|
|
18
|
+
|
|
19
|
+
success("Logout successful!");
|
|
20
|
+
console.log("Your session has been cleared.");
|
|
21
|
+
},
|
|
22
|
+
};
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
const chalk = require("chalk");
|
|
2
|
+
const { heading, info, dim } = require("../../shared");
|
|
3
|
+
const {
|
|
4
|
+
ALL_CLIENTS,
|
|
5
|
+
getOrderlySdkDocsMcpReport,
|
|
6
|
+
} = require("../../internal/orderlySdkDocsMcpDetect");
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* @param {string} clientArg
|
|
10
|
+
* @returns {string[]}
|
|
11
|
+
*/
|
|
12
|
+
function parseClients(clientArg) {
|
|
13
|
+
const raw = String(clientArg || "all").trim();
|
|
14
|
+
if (raw === "all") {
|
|
15
|
+
return [...ALL_CLIENTS];
|
|
16
|
+
}
|
|
17
|
+
const parts = raw
|
|
18
|
+
.split(",")
|
|
19
|
+
.map((x) => x.trim())
|
|
20
|
+
.filter(Boolean);
|
|
21
|
+
for (const p of parts) {
|
|
22
|
+
if (!ALL_CLIENTS.includes(p)) {
|
|
23
|
+
throw new Error(
|
|
24
|
+
`Invalid --client value: ${p}. Use: ${ALL_CLIENTS.join(", ")}, or all (comma-separated).`,
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return parts;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* @param {{ cwd: string; defaultServerName: string; clients: Record<string, { user: { configured: boolean }; project: { configured: boolean } }> }} report
|
|
33
|
+
*/
|
|
34
|
+
function printHumanReport(report) {
|
|
35
|
+
heading("Orderly SDK Docs MCP — detect");
|
|
36
|
+
info(`Working directory: ${report.cwd}`);
|
|
37
|
+
info(`Default server name: ${report.defaultServerName}\n`);
|
|
38
|
+
|
|
39
|
+
for (const client of Object.keys(report.clients)) {
|
|
40
|
+
const row = report.clients[client];
|
|
41
|
+
console.log(chalk.bold.cyan(client));
|
|
42
|
+
for (const scope of /** @type {const} */ (["user", "project"])) {
|
|
43
|
+
const cell = row[scope];
|
|
44
|
+
const scopeLabel = scope === "user" ? "user" : "proj";
|
|
45
|
+
const pathLine = chalk.dim(cell.configPath);
|
|
46
|
+
if (cell.error) {
|
|
47
|
+
console.log(
|
|
48
|
+
` [${scopeLabel}] ${chalk.red("invalid JSON")} — ${cell.error}`,
|
|
49
|
+
);
|
|
50
|
+
console.log(` ${pathLine}`);
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
if (!cell.exists) {
|
|
54
|
+
console.log(` [${scopeLabel}] ${chalk.yellow("no file")}`);
|
|
55
|
+
console.log(` ${pathLine}`);
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
if (cell.configured) {
|
|
59
|
+
const key = cell.serverKey ? ` (${cell.serverKey})` : "";
|
|
60
|
+
console.log(` [${scopeLabel}] ${chalk.green("configured")}${key}`);
|
|
61
|
+
} else {
|
|
62
|
+
console.log(` [${scopeLabel}] ${chalk.yellow("not detected")}`);
|
|
63
|
+
}
|
|
64
|
+
console.log(` ${pathLine}`);
|
|
65
|
+
}
|
|
66
|
+
console.log();
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const anyConfigured = Object.values(report.clients).some(
|
|
70
|
+
(row) => row.user.configured || row.project.configured,
|
|
71
|
+
);
|
|
72
|
+
if (!anyConfigured) {
|
|
73
|
+
dim("Orderly SDK Docs MCP was not found in any checked config.");
|
|
74
|
+
info("Install MCP for your agent(s):");
|
|
75
|
+
info(" orderly-devkit mcp install");
|
|
76
|
+
info(" orderly-devkit mcp install --client cursor --scope project");
|
|
77
|
+
info("Install plugin workflow skills for your coding agent:");
|
|
78
|
+
info(" orderly-devkit skills install");
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
module.exports = {
|
|
83
|
+
command: "detect",
|
|
84
|
+
describe:
|
|
85
|
+
"Detect whether Orderly SDK Docs MCP is present in agent config files (user + project scope)",
|
|
86
|
+
builder: (yargs) => {
|
|
87
|
+
return yargs
|
|
88
|
+
.option("client", {
|
|
89
|
+
type: "string",
|
|
90
|
+
describe:
|
|
91
|
+
"claude|codex|cursor|opencode|all, or comma-separated list (default: all)",
|
|
92
|
+
default: "all",
|
|
93
|
+
})
|
|
94
|
+
.option("json", {
|
|
95
|
+
type: "boolean",
|
|
96
|
+
default: false,
|
|
97
|
+
describe: "Print machine-readable JSON to stdout",
|
|
98
|
+
})
|
|
99
|
+
.example(
|
|
100
|
+
"orderly-devkit mcp detect",
|
|
101
|
+
"Scan all supported agents (user + project configs for cwd)",
|
|
102
|
+
)
|
|
103
|
+
.example(
|
|
104
|
+
"orderly-devkit mcp detect --client cursor",
|
|
105
|
+
"Only inspect Cursor mcp.json paths",
|
|
106
|
+
)
|
|
107
|
+
.example("orderly-devkit mcp detect --json", "JSON report for scripting");
|
|
108
|
+
},
|
|
109
|
+
handler: async (argv) => {
|
|
110
|
+
let clients;
|
|
111
|
+
try {
|
|
112
|
+
clients = parseClients(argv.client);
|
|
113
|
+
} catch (e) {
|
|
114
|
+
console.error(chalk.red(e instanceof Error ? e.message : String(e)));
|
|
115
|
+
process.exitCode = 1;
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const report = getOrderlySdkDocsMcpReport(process.cwd(), { clients });
|
|
120
|
+
|
|
121
|
+
if (argv.json) {
|
|
122
|
+
console.log(JSON.stringify(report, null, 2));
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
printHumanReport(report);
|
|
127
|
+
},
|
|
128
|
+
};
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
const { spawnSync } = require("child_process");
|
|
2
|
+
const chalk = require("chalk");
|
|
3
|
+
|
|
4
|
+
/** stderr-only; set ORDERLY_MCP_INSTALL_DEBUG=1 to trace spawn failures / hangs */
|
|
5
|
+
function debugMcpInstall(...parts) {
|
|
6
|
+
if (
|
|
7
|
+
process.env.ORDERLY_MCP_INSTALL_DEBUG !== "1" &&
|
|
8
|
+
process.env.ORDERLY_MCP_INSTALL_DEBUG !== "true"
|
|
9
|
+
) {
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
console.error(chalk.dim("[orderly-devkit mcp install:debug]"), ...parts);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Forward MCP install to sdk-docs so install ownership stays centralized.
|
|
17
|
+
* Uses an explicit bin invocation to avoid npx argument parsing ambiguity
|
|
18
|
+
* across npm versions and caches.
|
|
19
|
+
*/
|
|
20
|
+
function forwardToSdkDocs(argv) {
|
|
21
|
+
console.log(
|
|
22
|
+
chalk.cyan("orderly-devkit:") +
|
|
23
|
+
" Installing Orderly SDK Docs MCP config via npx…",
|
|
24
|
+
);
|
|
25
|
+
console.log(
|
|
26
|
+
chalk.dim("First run may download @orderly.network/sdk-docs; please wait."),
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
/** Pinned package spec for npx -y <spec> … (must match sdk-docs install merge). */
|
|
30
|
+
const rawSdkVer = argv.sdkDocsVersion ?? argv["sdk-docs-version"];
|
|
31
|
+
const ver =
|
|
32
|
+
rawSdkVer && String(rawSdkVer).trim() ? String(rawSdkVer).trim() : "";
|
|
33
|
+
const pkgSpec = ver
|
|
34
|
+
? `@orderly.network/sdk-docs@${ver}`
|
|
35
|
+
: "@orderly.network/sdk-docs";
|
|
36
|
+
if (ver) {
|
|
37
|
+
console.log(chalk.dim(`Using package: ${pkgSpec}`));
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const command = "npx";
|
|
41
|
+
const args = ["-y", pkgSpec, "orderly-sdk-docs-mcp", "install"];
|
|
42
|
+
|
|
43
|
+
if (argv.client) args.push("--client", String(argv.client));
|
|
44
|
+
if (argv.scope) args.push("--scope", String(argv.scope));
|
|
45
|
+
if (argv.name) args.push("--name", String(argv.name));
|
|
46
|
+
if (ver) args.push("--sdk-docs-version", ver);
|
|
47
|
+
if (argv["dry-run"]) args.push("--dry-run");
|
|
48
|
+
if (argv.force) args.push("--force");
|
|
49
|
+
|
|
50
|
+
debugMcpInstall("cwd", process.cwd());
|
|
51
|
+
debugMcpInstall("spawn", command, args);
|
|
52
|
+
|
|
53
|
+
const result = spawnSync(command, args, {
|
|
54
|
+
stdio: "inherit",
|
|
55
|
+
shell: process.platform === "win32",
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
debugMcpInstall(
|
|
59
|
+
"spawnSync done",
|
|
60
|
+
"status",
|
|
61
|
+
result.status,
|
|
62
|
+
"signal",
|
|
63
|
+
result.signal,
|
|
64
|
+
"error",
|
|
65
|
+
result.error,
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
if (result.error) {
|
|
69
|
+
throw result.error;
|
|
70
|
+
}
|
|
71
|
+
return result.status ?? 1;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
module.exports = {
|
|
75
|
+
command: "install",
|
|
76
|
+
describe:
|
|
77
|
+
"Install Orderly SDK Docs MCP config for Claude/Codex/Cursor/OpenCode",
|
|
78
|
+
builder: (yargs) => {
|
|
79
|
+
return yargs
|
|
80
|
+
.option("client", {
|
|
81
|
+
type: "string",
|
|
82
|
+
describe:
|
|
83
|
+
"Target client(s): claude|codex|cursor|opencode|all (comma-separated supported)",
|
|
84
|
+
default: "all",
|
|
85
|
+
})
|
|
86
|
+
.option("scope", {
|
|
87
|
+
type: "string",
|
|
88
|
+
choices: ["user", "project"],
|
|
89
|
+
default: "user",
|
|
90
|
+
})
|
|
91
|
+
.option("name", {
|
|
92
|
+
type: "string",
|
|
93
|
+
describe: "MCP server name in config mcpServers map",
|
|
94
|
+
default: "orderly-sdk-docs",
|
|
95
|
+
})
|
|
96
|
+
.option("dry-run", {
|
|
97
|
+
type: "boolean",
|
|
98
|
+
default: false,
|
|
99
|
+
})
|
|
100
|
+
.option("force", {
|
|
101
|
+
type: "boolean",
|
|
102
|
+
default: false,
|
|
103
|
+
})
|
|
104
|
+
.option("sdk-docs-version", {
|
|
105
|
+
type: "string",
|
|
106
|
+
describe:
|
|
107
|
+
"Pin @orderly.network/sdk-docs (semver or dist-tag, e.g. 0.1.0 or beta); applies to this run and merged MCP config",
|
|
108
|
+
})
|
|
109
|
+
.example(
|
|
110
|
+
"orderly-devkit mcp install --client cursor --scope project",
|
|
111
|
+
"Install MCP entry for Cursor in project scope",
|
|
112
|
+
)
|
|
113
|
+
.example(
|
|
114
|
+
"orderly-devkit mcp install --sdk-docs-version 0.1.0 --scope user",
|
|
115
|
+
"Pin a specific sdk-docs release for npx and editor MCP configs",
|
|
116
|
+
);
|
|
117
|
+
},
|
|
118
|
+
handler: async (argv) => {
|
|
119
|
+
const status = forwardToSdkDocs(argv);
|
|
120
|
+
process.exitCode = status;
|
|
121
|
+
},
|
|
122
|
+
};
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
const { spawnSync } = require("child_process");
|
|
2
|
+
const chalk = require("chalk");
|
|
3
|
+
const {
|
|
4
|
+
ORDERLY_SKILLS_REPO,
|
|
5
|
+
ORDERLY_PLUGIN_SKILL_NAMES,
|
|
6
|
+
} = require("../../internal/constants");
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Extra argv after `--` is forwarded verbatim to `skills add` (upstream flags).
|
|
10
|
+
* @returns {string[]}
|
|
11
|
+
*/
|
|
12
|
+
function getPassthroughArgs() {
|
|
13
|
+
const idx = process.argv.indexOf("--");
|
|
14
|
+
if (idx === -1) {
|
|
15
|
+
return [];
|
|
16
|
+
}
|
|
17
|
+
return process.argv.slice(idx + 1);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* @param {unknown} value
|
|
22
|
+
* @returns {string[]}
|
|
23
|
+
*/
|
|
24
|
+
function asStringArray(value) {
|
|
25
|
+
if (value == null) {
|
|
26
|
+
return [];
|
|
27
|
+
}
|
|
28
|
+
if (Array.isArray(value)) {
|
|
29
|
+
return value.map(String).filter(Boolean);
|
|
30
|
+
}
|
|
31
|
+
return [String(value)];
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Build argv for: npx -y skills add …
|
|
36
|
+
* @param {Record<string, unknown>} argv
|
|
37
|
+
* @returns {string[]}
|
|
38
|
+
*/
|
|
39
|
+
function buildSkillsAddArgs(argv) {
|
|
40
|
+
const source =
|
|
41
|
+
typeof argv.source === "string" && argv.source.trim() !== ""
|
|
42
|
+
? argv.source.trim()
|
|
43
|
+
: ORDERLY_SKILLS_REPO;
|
|
44
|
+
|
|
45
|
+
const args = [source];
|
|
46
|
+
const passthrough = getPassthroughArgs();
|
|
47
|
+
|
|
48
|
+
if (argv.list) {
|
|
49
|
+
args.push("--list");
|
|
50
|
+
if (argv.yes === true) {
|
|
51
|
+
args.push("-y");
|
|
52
|
+
}
|
|
53
|
+
appendSharedTail(args, argv);
|
|
54
|
+
args.push(...passthrough);
|
|
55
|
+
return args;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (argv.all === true) {
|
|
59
|
+
args.push("--all");
|
|
60
|
+
appendSharedTail(args, argv);
|
|
61
|
+
args.push(...passthrough);
|
|
62
|
+
return args;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const userSkills = asStringArray(argv.skill);
|
|
66
|
+
if (userSkills.length > 0) {
|
|
67
|
+
for (const name of userSkills) {
|
|
68
|
+
args.push("--skill", name);
|
|
69
|
+
}
|
|
70
|
+
} else {
|
|
71
|
+
for (const name of ORDERLY_PLUGIN_SKILL_NAMES) {
|
|
72
|
+
args.push("--skill", name);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
args.push("-y");
|
|
76
|
+
|
|
77
|
+
appendSharedTail(args, argv);
|
|
78
|
+
args.push(...passthrough);
|
|
79
|
+
return args;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* @param {string[]} args
|
|
84
|
+
* @param {Record<string, unknown>} argv
|
|
85
|
+
*/
|
|
86
|
+
function appendSharedTail(args, argv) {
|
|
87
|
+
if (argv.global === true) {
|
|
88
|
+
args.push("-g");
|
|
89
|
+
}
|
|
90
|
+
const agents = asStringArray(argv.agent);
|
|
91
|
+
for (const a of agents) {
|
|
92
|
+
args.push("-a", a);
|
|
93
|
+
}
|
|
94
|
+
if (argv.copy === true) {
|
|
95
|
+
args.push("--copy");
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* @param {string[]} skillsAddArgs
|
|
101
|
+
* @returns {string[]}
|
|
102
|
+
*/
|
|
103
|
+
function buildNpxArgv(skillsAddArgs) {
|
|
104
|
+
return ["-y", "skills", "add", ...skillsAddArgs];
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* @param {string[]} argv
|
|
109
|
+
*/
|
|
110
|
+
function printDryRun(argv) {
|
|
111
|
+
const line = [
|
|
112
|
+
"npx",
|
|
113
|
+
...argv.map((a) => (/\s/.test(a) ? JSON.stringify(a) : a)),
|
|
114
|
+
].join(" ");
|
|
115
|
+
console.log(chalk.cyan("Dry run — would execute:"));
|
|
116
|
+
console.log(line);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
module.exports = {
|
|
120
|
+
command: "install [source]",
|
|
121
|
+
describe:
|
|
122
|
+
"Install Orderly plugin workflow agent skills (defaults: official repo + four skills + -y)",
|
|
123
|
+
builder: (yargs) => {
|
|
124
|
+
return yargs
|
|
125
|
+
.positional("source", {
|
|
126
|
+
type: "string",
|
|
127
|
+
describe:
|
|
128
|
+
"GitHub owner/repo, URL, or local path (default: OrderlyNetwork/orderly-skills)",
|
|
129
|
+
})
|
|
130
|
+
.option("list", {
|
|
131
|
+
type: "boolean",
|
|
132
|
+
default: false,
|
|
133
|
+
describe:
|
|
134
|
+
"List skills in the source repo without installing (no default --skill/-y)",
|
|
135
|
+
})
|
|
136
|
+
.option("skill", {
|
|
137
|
+
alias: "s",
|
|
138
|
+
type: "array",
|
|
139
|
+
describe:
|
|
140
|
+
"Install only these skill names (replaces default four when set; not used with --list)",
|
|
141
|
+
})
|
|
142
|
+
.option("all", {
|
|
143
|
+
type: "boolean",
|
|
144
|
+
default: false,
|
|
145
|
+
describe:
|
|
146
|
+
"Forward --all to skills CLI (install all skills/agents; skips default four + auto -y)",
|
|
147
|
+
})
|
|
148
|
+
.option("global", {
|
|
149
|
+
alias: "g",
|
|
150
|
+
type: "boolean",
|
|
151
|
+
default: false,
|
|
152
|
+
describe: "Install globally (forward -g to skills CLI)",
|
|
153
|
+
})
|
|
154
|
+
.option("agent", {
|
|
155
|
+
alias: "a",
|
|
156
|
+
type: "array",
|
|
157
|
+
describe: "Target agent(s), e.g. -a cursor -a claude-code",
|
|
158
|
+
})
|
|
159
|
+
.option("copy", {
|
|
160
|
+
type: "boolean",
|
|
161
|
+
default: false,
|
|
162
|
+
describe: "Copy files instead of symlinks (--copy)",
|
|
163
|
+
})
|
|
164
|
+
.option("yes", {
|
|
165
|
+
alias: "y",
|
|
166
|
+
type: "boolean",
|
|
167
|
+
describe:
|
|
168
|
+
"With --list only: also pass -y to skills CLI (default install already uses -y)",
|
|
169
|
+
})
|
|
170
|
+
.option("dry-run", {
|
|
171
|
+
type: "boolean",
|
|
172
|
+
default: false,
|
|
173
|
+
describe: "Print the npx command without running it",
|
|
174
|
+
})
|
|
175
|
+
.example(
|
|
176
|
+
"orderly-devkit skills install",
|
|
177
|
+
"Install all four Orderly plugin skills from the official repo (-y, non-interactive)",
|
|
178
|
+
)
|
|
179
|
+
.example(
|
|
180
|
+
"orderly-devkit skills install --list",
|
|
181
|
+
"List available skills in the official repo",
|
|
182
|
+
)
|
|
183
|
+
.example(
|
|
184
|
+
"orderly-devkit skills install other/repo --skill my-skill -y",
|
|
185
|
+
"Advanced: different repo and explicit skills (default four skipped)",
|
|
186
|
+
)
|
|
187
|
+
.example(
|
|
188
|
+
"orderly-devkit skills install -- --some-new-skills-flag",
|
|
189
|
+
"Forward extra flags after -- to the skills CLI",
|
|
190
|
+
);
|
|
191
|
+
},
|
|
192
|
+
handler: async (argv) => {
|
|
193
|
+
const skillsAddArgs = buildSkillsAddArgs(argv);
|
|
194
|
+
const npxArgs = buildNpxArgv(skillsAddArgs);
|
|
195
|
+
|
|
196
|
+
if (argv["dry-run"]) {
|
|
197
|
+
printDryRun(npxArgs);
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
const result = spawnSync("npx", npxArgs, {
|
|
202
|
+
stdio: "inherit",
|
|
203
|
+
shell: process.platform === "win32",
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
if (result.error) {
|
|
207
|
+
throw result.error;
|
|
208
|
+
}
|
|
209
|
+
process.exitCode = result.status ?? 1;
|
|
210
|
+
},
|
|
211
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
module.exports = {
|
|
2
|
+
command: "skills <subcommand>",
|
|
3
|
+
describe:
|
|
4
|
+
"Install Orderly agent skills for plugin workflows (create, write, add, submit)",
|
|
5
|
+
builder: (yargs) => {
|
|
6
|
+
return yargs
|
|
7
|
+
.commandDir("skills")
|
|
8
|
+
.demandCommand(1, "Please provide a skills subcommand.");
|
|
9
|
+
},
|
|
10
|
+
};
|