@minhpnq1807/contextos 0.5.40 → 0.5.41
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/CHANGELOG.md +8 -0
- package/bin/ctx.js +133 -9
- package/package.json +1 -1
- package/plugins/ctx/.codex-plugin/plugin.json +1 -1
- package/plugins/ctx/lib/multi-select.js +113 -0
- package/plugins/ctx/lib/skill-library.js +80 -6
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.5.41
|
|
4
|
+
|
|
5
|
+
- **Interactive community skill installer (`ctx skills`):** Replaced the info-only display with a fully functional multi-select installer. Users can now toggle multiple community library sources with `Space`, confirm with `Enter`, and the CLI automatically runs the appropriate install command for each selected source (`npx`, `git clone`, etc.).
|
|
6
|
+
- **Compact selection UI with URL hints:** The skill source picker now shows a clean box-styled header and each option displays its GitHub URL as a dimmed sub-line hint, matching the `◇ / │` visual language used throughout contextOS.
|
|
7
|
+
- **Prefixed install output (`runPrefixed`):** All child-process output during installation (including interactive npx prompts like "Ok to proceed?") is piped through a line-by-line prefixer that prepends `│ ` to every line. This keeps the visual box style consistent and prevents raw command output from breaking the layout. `stdin` remains inherited so users can still answer interactive prompts.
|
|
8
|
+
- **`multiSelect` hint support:** The multi-select component now accepts an optional `hint` property on each option, rendered as a dimmed indented line below the label. Used for URLs but available for any contextual sub-text.
|
|
9
|
+
- **Library install metadata:** `skill-library.js` now exports `getInstallCommands(libraryId)` returning structured install info (command, verify step, type) for each library source, keeping install logic out of the main CLI.
|
|
10
|
+
|
|
3
11
|
## 0.5.40
|
|
4
12
|
|
|
5
13
|
- **Update notifier:** `ctx` now checks npm for newer versions in the background (once per day, 3s timeout). If a newer release exists, a boxed notice is printed at the very end of any command: `Update available: 0.5.39 → 0.5.40`. Check result is cached in `$CONTEXTOS_DATA/.update-check.json` to avoid repeated network calls.
|
package/bin/ctx.js
CHANGED
|
@@ -5,7 +5,7 @@ import path from "node:path";
|
|
|
5
5
|
import readline from "node:readline/promises";
|
|
6
6
|
import { stdin as input, stdout as output } from "node:process";
|
|
7
7
|
import { fileURLToPath } from "node:url";
|
|
8
|
-
import { execFileSync } from "node:child_process";
|
|
8
|
+
import { execFileSync, execSync, spawn } from "node:child_process";
|
|
9
9
|
|
|
10
10
|
import { readAgentsChain } from "../plugins/ctx/lib/reader.js";
|
|
11
11
|
import { filterActionableRules, parseRules, scoreRules } from "../plugins/ctx/lib/analyzer.js";
|
|
@@ -35,7 +35,37 @@ import { parseAgentList, parseSetupArgs, setupSummaryLines } from "../plugins/ct
|
|
|
35
35
|
import { multiSelect } from "../plugins/ctx/lib/multi-select.js";
|
|
36
36
|
import { syncWorkflows, warmWorkflowEmbeddings } from "../plugins/ctx/lib/workflow-discoverer.js";
|
|
37
37
|
import { checkForUpdate } from "../plugins/ctx/lib/update-notifier.js";
|
|
38
|
-
import { fetchSkillsForAgents, printSkillRecommendations, getAllLibraries } from "../plugins/ctx/lib/skill-library.js";
|
|
38
|
+
import { fetchSkillsForAgents, printSkillRecommendations, getAllLibraries, getInstallCommands } from "../plugins/ctx/lib/skill-library.js";
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Run a shell command with all output lines prefixed by │
|
|
42
|
+
* Keeps the visual box style consistent during child-process output.
|
|
43
|
+
* stdin is inherited so interactive prompts (e.g. npx "Ok to proceed?") still work.
|
|
44
|
+
*/
|
|
45
|
+
function runPrefixed(cmd) {
|
|
46
|
+
const DIM = "\x1B[2m";
|
|
47
|
+
const RST = "\x1B[0m";
|
|
48
|
+
const pfx = `${DIM}│${RST} `;
|
|
49
|
+
return new Promise((resolve, reject) => {
|
|
50
|
+
const child = spawn("sh", ["-c", cmd], { stdio: ["inherit", "pipe", "pipe"] });
|
|
51
|
+
function pipe(stream, target) {
|
|
52
|
+
let needPrefix = true;
|
|
53
|
+
stream.on("data", (buf) => {
|
|
54
|
+
const str = buf.toString();
|
|
55
|
+
let out = "";
|
|
56
|
+
for (const ch of str) {
|
|
57
|
+
if (needPrefix) { out += pfx; needPrefix = false; }
|
|
58
|
+
out += ch;
|
|
59
|
+
if (ch === "\n") needPrefix = true;
|
|
60
|
+
}
|
|
61
|
+
target.write(out);
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
pipe(child.stdout, process.stdout);
|
|
65
|
+
pipe(child.stderr, process.stderr);
|
|
66
|
+
child.on("close", (code) => code === 0 ? resolve() : reject(new Error(`exit code ${code}`)));
|
|
67
|
+
});
|
|
68
|
+
}
|
|
39
69
|
|
|
40
70
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
41
71
|
const rootDir = path.resolve(__dirname, "..");
|
|
@@ -701,7 +731,7 @@ try {
|
|
|
701
731
|
if (!task.trim()) throw new Error('Usage: ctx benchmark -- "task"');
|
|
702
732
|
console.log(formatBenchmark(benchmarkWorkspace({ cwd: process.cwd(), task })));
|
|
703
733
|
} else if (command === "skills") {
|
|
704
|
-
//
|
|
734
|
+
// Interactive community skill library selector + installer
|
|
705
735
|
const agentsFlag = args.indexOf("--agents");
|
|
706
736
|
const forceRefresh = args.includes("--refresh");
|
|
707
737
|
let agents;
|
|
@@ -710,21 +740,115 @@ try {
|
|
|
710
740
|
} else {
|
|
711
741
|
agents = ["codex", "claude", "agy", "copilot"];
|
|
712
742
|
}
|
|
743
|
+
|
|
744
|
+
const DIM = "\x1B[2m";
|
|
745
|
+
const RESET = "\x1B[0m";
|
|
746
|
+
const CYAN = "\x1B[36m";
|
|
747
|
+
const GREEN = "\x1B[32m";
|
|
748
|
+
const YELLOW = "\x1B[33m";
|
|
749
|
+
const BOLD = "\x1B[1m";
|
|
750
|
+
|
|
713
751
|
console.log("Fetching community skill libraries...\n");
|
|
714
752
|
const libraryResults = await fetchSkillsForAgents(agents, {
|
|
715
753
|
dataDir: contextOSDataDir()
|
|
716
754
|
});
|
|
717
|
-
|
|
718
|
-
console.log("");
|
|
755
|
+
|
|
719
756
|
const totalSkills = libraryResults.reduce((sum, r) => sum + r.count, 0);
|
|
720
757
|
if (totalSkills === 0) {
|
|
721
758
|
console.log("No skills found. Check your network connection or try --refresh.");
|
|
722
|
-
|
|
723
|
-
|
|
759
|
+
process.exit(1);
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
// Compact header
|
|
763
|
+
console.log(`${CYAN}◇${RESET} ${BOLD}Community skill libraries available:${RESET}`);
|
|
764
|
+
console.log(`${DIM}│${RESET} Browse and install curated skills from the community.`);
|
|
765
|
+
console.log(`${DIM}│${RESET}`);
|
|
766
|
+
|
|
767
|
+
// Multi-select which sources to install from
|
|
768
|
+
const allLibs = getAllLibraries();
|
|
769
|
+
const availableLibs = allLibs.filter((lib) => {
|
|
770
|
+
const result = libraryResults.find((r) => r.library.id === lib.id);
|
|
771
|
+
return result && result.count > 0;
|
|
772
|
+
});
|
|
773
|
+
|
|
774
|
+
if (availableLibs.length === 0) {
|
|
775
|
+
console.log("No installable libraries available.");
|
|
776
|
+
process.exit(0);
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
const selectedSources = await multiSelect({
|
|
780
|
+
message: "Select skill sources to install:",
|
|
781
|
+
options: availableLibs.map((lib) => {
|
|
782
|
+
const result = libraryResults.find((r) => r.library.id === lib.id);
|
|
783
|
+
return {
|
|
784
|
+
label: `${lib.name} (${result?.count || 0} skills)`,
|
|
785
|
+
value: lib.id,
|
|
786
|
+
hint: lib.url,
|
|
787
|
+
selected: false
|
|
788
|
+
};
|
|
789
|
+
})
|
|
790
|
+
});
|
|
791
|
+
|
|
792
|
+
if (!selectedSources || selectedSources.length === 0) {
|
|
793
|
+
console.log(`\n${DIM}No sources selected.${RESET}`);
|
|
794
|
+
process.exit(0);
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
// Install each selected source using its provided commands
|
|
798
|
+
let successCount = 0;
|
|
799
|
+
for (const libId of selectedSources) {
|
|
800
|
+
const lib = allLibs.find((l) => l.id === libId);
|
|
801
|
+
if (!lib) continue;
|
|
802
|
+
|
|
803
|
+
const installInfo = getInstallCommands(libId);
|
|
804
|
+
if (!installInfo) {
|
|
805
|
+
console.log(`${YELLOW}⚠${RESET} No install info for ${lib.name}. Visit: ${lib.url}`);
|
|
806
|
+
continue;
|
|
807
|
+
}
|
|
808
|
+
|
|
724
809
|
console.log("");
|
|
725
|
-
console.log(
|
|
726
|
-
|
|
810
|
+
console.log(`${CYAN}◇${RESET} ${BOLD}Installing from ${lib.name}${RESET}`);
|
|
811
|
+
|
|
812
|
+
if (installInfo.type === "manual") {
|
|
813
|
+
console.log(`${DIM}│${RESET} ${installInfo.instructions}`);
|
|
814
|
+
console.log(`${DIM}│${RESET} ${DIM}URL: ${lib.url}${RESET}`);
|
|
815
|
+
continue;
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
const installCmd = installInfo.fullInstall;
|
|
819
|
+
if (installCmd) {
|
|
820
|
+
console.log(`${DIM}│${RESET} ${GREEN}$ ${installCmd}${RESET}`);
|
|
821
|
+
console.log(`${DIM}│${RESET}`);
|
|
822
|
+
|
|
823
|
+
try {
|
|
824
|
+
await runPrefixed(installCmd);
|
|
825
|
+
successCount++;
|
|
826
|
+
|
|
827
|
+
// Run verify command if available
|
|
828
|
+
if (installInfo.verify) {
|
|
829
|
+
try {
|
|
830
|
+
await runPrefixed(installInfo.verify);
|
|
831
|
+
} catch { /* verify is best-effort */ }
|
|
832
|
+
}
|
|
833
|
+
console.log(`${DIM}│${RESET}`);
|
|
834
|
+
console.log(`${GREEN}✔${RESET} ${lib.name} installed successfully.`);
|
|
835
|
+
} catch (err) {
|
|
836
|
+
console.error(`${YELLOW}⚠${RESET} Install failed for ${lib.name}. Try manually:`);
|
|
837
|
+
console.error(` ${installCmd}`);
|
|
838
|
+
console.error(` ${DIM}${err.message}${RESET}`);
|
|
839
|
+
}
|
|
840
|
+
}
|
|
727
841
|
}
|
|
842
|
+
|
|
843
|
+
// Final summary
|
|
844
|
+
console.log("");
|
|
845
|
+
if (successCount > 0) {
|
|
846
|
+
console.log(`${GREEN}✔${RESET} ${BOLD}${successCount} source${successCount > 1 ? "s" : ""} installed.${RESET}`);
|
|
847
|
+
console.log(`${DIM}│${RESET} Restart your agent to pick up new skills.`);
|
|
848
|
+
} else {
|
|
849
|
+
console.log(`${DIM}No installations were completed.${RESET}`);
|
|
850
|
+
}
|
|
851
|
+
console.log("");
|
|
728
852
|
} else if (command === "sync") {
|
|
729
853
|
if (args.includes("--workflows")) {
|
|
730
854
|
await syncWorkflows({
|
package/package.json
CHANGED
|
@@ -62,6 +62,9 @@ export function multiSelect({ message, options }) {
|
|
|
62
62
|
const label = isCursor ? `${BOLD}${CYAN}${options[i].label}${RESET}` : options[i].label;
|
|
63
63
|
const pointer = isCursor ? `${CYAN}❯${RESET}` : " ";
|
|
64
64
|
lines.push(` ${pointer} ${checkbox} ${label}`);
|
|
65
|
+
if (options[i].hint) {
|
|
66
|
+
lines.push(` ${DIM}${options[i].hint}${RESET}`);
|
|
67
|
+
}
|
|
65
68
|
}
|
|
66
69
|
return lines;
|
|
67
70
|
}
|
|
@@ -140,3 +143,113 @@ export function multiSelect({ message, options }) {
|
|
|
140
143
|
draw();
|
|
141
144
|
});
|
|
142
145
|
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Interactive single-select prompt (radio-button style).
|
|
149
|
+
*
|
|
150
|
+
* @param {{ message: string, options: Array<{label: string, value: string, disabled?: boolean, isHeader?: boolean}> }} config
|
|
151
|
+
* @returns {Promise<string|null>} selected value or null
|
|
152
|
+
*/
|
|
153
|
+
export function singleSelect({ message, options }) {
|
|
154
|
+
return new Promise((resolve) => {
|
|
155
|
+
if (!process.stdin.isTTY) {
|
|
156
|
+
// Non-interactive: return first non-disabled
|
|
157
|
+
const first = options.find((o) => !o.disabled);
|
|
158
|
+
resolve(first ? first.value : null);
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Filter to only selectable items, but keep the full list for rendering
|
|
163
|
+
const selectableIndices = options
|
|
164
|
+
.map((o, i) => (o.disabled ? -1 : i))
|
|
165
|
+
.filter((i) => i >= 0);
|
|
166
|
+
if (selectableIndices.length === 0) { resolve(null); return; }
|
|
167
|
+
|
|
168
|
+
let cursorIdx = 0; // index into selectableIndices
|
|
169
|
+
let cursor = selectableIndices[0]; // actual index in options
|
|
170
|
+
|
|
171
|
+
function render() {
|
|
172
|
+
const lines = [];
|
|
173
|
+
lines.push(`${CYAN}◇${RESET} ${message}`);
|
|
174
|
+
lines.push(`${DIM} Use ↑/↓ to navigate, Enter to select${RESET}`);
|
|
175
|
+
for (let i = 0; i < options.length; i++) {
|
|
176
|
+
const opt = options[i];
|
|
177
|
+
if (opt.disabled || opt.isHeader) {
|
|
178
|
+
lines.push(` ${DIM}${opt.label}${RESET}`);
|
|
179
|
+
continue;
|
|
180
|
+
}
|
|
181
|
+
const isCursor = i === cursor;
|
|
182
|
+
const radio = isCursor ? `${GREEN}◉${RESET}` : `${DIM}○${RESET}`;
|
|
183
|
+
const label = isCursor ? `${BOLD}${CYAN}${opt.label}${RESET}` : opt.label;
|
|
184
|
+
const pointer = isCursor ? `${CYAN}❯${RESET}` : " ";
|
|
185
|
+
lines.push(` ${pointer} ${radio} ${label}`);
|
|
186
|
+
}
|
|
187
|
+
return lines;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
let prevLineCount = 0;
|
|
191
|
+
|
|
192
|
+
function draw() {
|
|
193
|
+
const lines = render();
|
|
194
|
+
if (prevLineCount > 0) {
|
|
195
|
+
process.stdout.write(`\x1B[${prevLineCount}A`);
|
|
196
|
+
for (let i = 0; i < prevLineCount; i++) {
|
|
197
|
+
process.stdout.write("\x1B[2K");
|
|
198
|
+
if (i < prevLineCount - 1) process.stdout.write("\x1B[1B");
|
|
199
|
+
}
|
|
200
|
+
process.stdout.write(`\x1B[${prevLineCount - 1}A`);
|
|
201
|
+
}
|
|
202
|
+
process.stdout.write(lines.join("\n") + "\n");
|
|
203
|
+
prevLineCount = lines.length;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
process.stdout.write(HIDE_CURSOR);
|
|
207
|
+
|
|
208
|
+
const wasRaw = process.stdin.isRaw;
|
|
209
|
+
process.stdin.setRawMode(true);
|
|
210
|
+
process.stdin.resume();
|
|
211
|
+
process.stdin.setEncoding("utf8");
|
|
212
|
+
|
|
213
|
+
function cleanup() {
|
|
214
|
+
process.stdin.setRawMode(wasRaw || false);
|
|
215
|
+
process.stdin.pause();
|
|
216
|
+
process.stdin.removeListener("data", onData);
|
|
217
|
+
process.stdout.write(SHOW_CURSOR);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
function onData(data) {
|
|
221
|
+
if (matchKey(data, KEYS.CTRL_C)) {
|
|
222
|
+
cleanup();
|
|
223
|
+
process.exit(0);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
if (matchKey(data, KEYS.UP) || matchKey(data, KEYS.K)) {
|
|
227
|
+
cursorIdx = (cursorIdx - 1 + selectableIndices.length) % selectableIndices.length;
|
|
228
|
+
cursor = selectableIndices[cursorIdx];
|
|
229
|
+
draw();
|
|
230
|
+
} else if (matchKey(data, KEYS.DOWN) || matchKey(data, KEYS.J)) {
|
|
231
|
+
cursorIdx = (cursorIdx + 1) % selectableIndices.length;
|
|
232
|
+
cursor = selectableIndices[cursorIdx];
|
|
233
|
+
draw();
|
|
234
|
+
} else if (matchKey(data, KEYS.ENTER)) {
|
|
235
|
+
cleanup();
|
|
236
|
+
const selected = options[cursor];
|
|
237
|
+
// Clear and show result
|
|
238
|
+
if (prevLineCount > 0) {
|
|
239
|
+
process.stdout.write(`\x1B[${prevLineCount}A`);
|
|
240
|
+
for (let i = 0; i < prevLineCount; i++) {
|
|
241
|
+
process.stdout.write("\x1B[2K");
|
|
242
|
+
if (i < prevLineCount - 1) process.stdout.write("\x1B[1B");
|
|
243
|
+
}
|
|
244
|
+
process.stdout.write(`\x1B[${prevLineCount - 1}A`);
|
|
245
|
+
}
|
|
246
|
+
process.stdout.write(`${CYAN}◇${RESET} ${message}\n`);
|
|
247
|
+
process.stdout.write(`${DIM}│${RESET} ${GREEN}${selected.label}${RESET}\n`);
|
|
248
|
+
resolve(selected.value);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
process.stdin.on("data", onData);
|
|
253
|
+
draw();
|
|
254
|
+
});
|
|
255
|
+
}
|
|
@@ -19,25 +19,64 @@ const SKILL_LIBRARIES = [
|
|
|
19
19
|
url: "https://github.com/sickn33/antigravity-awesome-skills",
|
|
20
20
|
rawReadmeUrl: "https://raw.githubusercontent.com/sickn33/antigravity-awesome-skills/main/README.md",
|
|
21
21
|
agents: ["agy", "claude", "codex", "copilot"], // universal library
|
|
22
|
-
description: "1,400+ agentic skills for all agents"
|
|
22
|
+
description: "1,400+ agentic skills for all agents",
|
|
23
|
+
install: {
|
|
24
|
+
type: "npx",
|
|
25
|
+
fullInstall: "npx antigravity-awesome-skills",
|
|
26
|
+
agentFlags: {
|
|
27
|
+
codex: "npx antigravity-awesome-skills --codex",
|
|
28
|
+
claude: "npx antigravity-awesome-skills --claude",
|
|
29
|
+
agy: "npx antigravity-awesome-skills --antigravity",
|
|
30
|
+
copilot: "npx antigravity-awesome-skills --cursor",
|
|
31
|
+
gemini: "npx antigravity-awesome-skills --gemini"
|
|
32
|
+
},
|
|
33
|
+
verify: 'test -d ~/.agents/skills && echo "Skills installed in ~/.agents/skills"'
|
|
34
|
+
}
|
|
23
35
|
},
|
|
24
36
|
{
|
|
25
37
|
id: "awesome-claude",
|
|
26
38
|
name: "Awesome Claude Skills",
|
|
27
39
|
repo: "ComposioHQ/awesome-claude-skills",
|
|
28
40
|
url: "https://github.com/ComposioHQ/awesome-claude-skills",
|
|
29
|
-
rawReadmeUrl: "https://raw.githubusercontent.com/ComposioHQ/awesome-claude-skills/
|
|
41
|
+
rawReadmeUrl: "https://raw.githubusercontent.com/ComposioHQ/awesome-claude-skills/master/README.md",
|
|
30
42
|
agents: ["claude"],
|
|
31
|
-
description: "Curated Claude Code skills & workflows"
|
|
43
|
+
description: "Curated Claude Code skills & workflows",
|
|
44
|
+
install: {
|
|
45
|
+
type: "git-clone",
|
|
46
|
+
clone: "git clone https://github.com/ComposioHQ/awesome-claude-skills.git",
|
|
47
|
+
skillDir: "~/.claude/skills",
|
|
48
|
+
copyCommand: (skillName) =>
|
|
49
|
+
`cp -r awesome-claude-skills/${skillName} ~/.claude/skills/${skillName}`,
|
|
50
|
+
fullInstall: [
|
|
51
|
+
"git clone https://github.com/ComposioHQ/awesome-claude-skills.git",
|
|
52
|
+
"mkdir -p ~/.claude/skills",
|
|
53
|
+
'for d in awesome-claude-skills/*/; do [ -f "$d/SKILL.md" ] && cp -r "$d" ~/.claude/skills/; done'
|
|
54
|
+
].join(" && "),
|
|
55
|
+
verify: 'ls ~/.claude/skills/'
|
|
56
|
+
}
|
|
32
57
|
},
|
|
33
58
|
{
|
|
34
59
|
id: "awesome-codex",
|
|
35
60
|
name: "Awesome Codex Skills",
|
|
36
61
|
repo: "ComposioHQ/awesome-codex-skills",
|
|
37
62
|
url: "https://github.com/ComposioHQ/awesome-codex-skills",
|
|
38
|
-
rawReadmeUrl: "https://raw.githubusercontent.com/ComposioHQ/awesome-codex-skills/
|
|
63
|
+
rawReadmeUrl: "https://raw.githubusercontent.com/ComposioHQ/awesome-codex-skills/master/README.md",
|
|
39
64
|
agents: ["codex"],
|
|
40
|
-
description: "Practical Codex CLI skills & automations"
|
|
65
|
+
description: "Practical Codex CLI skills & automations",
|
|
66
|
+
install: {
|
|
67
|
+
type: "git-clone-python",
|
|
68
|
+
clone: "git clone https://github.com/ComposioHQ/awesome-codex-skills.git",
|
|
69
|
+
skillDir: "~/.codex/skills",
|
|
70
|
+
installScript: (skillName) =>
|
|
71
|
+
`python awesome-codex-skills/skill-installer/scripts/install-skill-from-github.py --repo ComposioHQ/awesome-codex-skills --path ${skillName}`,
|
|
72
|
+
fullInstall: [
|
|
73
|
+
"git clone https://github.com/ComposioHQ/awesome-codex-skills.git",
|
|
74
|
+
"cd awesome-codex-skills",
|
|
75
|
+
"mkdir -p ~/.codex/skills",
|
|
76
|
+
'for d in */; do [ -f "$d/SKILL.md" ] && [ "$d" != "skill-installer/" ] && cp -r "$d" ~/.codex/skills/; done'
|
|
77
|
+
].join(" && "),
|
|
78
|
+
verify: 'ls ~/.codex/skills/'
|
|
79
|
+
}
|
|
41
80
|
},
|
|
42
81
|
{
|
|
43
82
|
id: "awesome-copilot",
|
|
@@ -46,7 +85,13 @@ const SKILL_LIBRARIES = [
|
|
|
46
85
|
url: "https://github.com/github/awesome-copilot",
|
|
47
86
|
rawReadmeUrl: "https://raw.githubusercontent.com/github/awesome-copilot/main/README.md",
|
|
48
87
|
agents: ["copilot"],
|
|
49
|
-
description: "Community GitHub Copilot instructions & agents"
|
|
88
|
+
description: "Community GitHub Copilot instructions & agents",
|
|
89
|
+
install: {
|
|
90
|
+
type: "manual",
|
|
91
|
+
fullInstall: null,
|
|
92
|
+
instructions: "Browse the repository and copy relevant .instructions.md files to your .github/ directory.",
|
|
93
|
+
url: "https://github.com/github/awesome-copilot"
|
|
94
|
+
}
|
|
50
95
|
}
|
|
51
96
|
];
|
|
52
97
|
|
|
@@ -288,3 +333,32 @@ export function printSkillRecommendations(libraryResults, { logger = console.log
|
|
|
288
333
|
export function getAllLibraries() {
|
|
289
334
|
return SKILL_LIBRARIES;
|
|
290
335
|
}
|
|
336
|
+
|
|
337
|
+
/**
|
|
338
|
+
* Get install commands for a specific library.
|
|
339
|
+
* @param {string} libraryId
|
|
340
|
+
* @param {string} [agent] - optional agent to target
|
|
341
|
+
* @returns {{ fullInstall: string|null, agentInstall: string|null, verify: string|null, instructions: string|null, type: string }}
|
|
342
|
+
*/
|
|
343
|
+
export function getInstallCommands(libraryId, agent) {
|
|
344
|
+
const lib = SKILL_LIBRARIES.find((l) => l.id === libraryId);
|
|
345
|
+
if (!lib || !lib.install) return null;
|
|
346
|
+
|
|
347
|
+
const inst = lib.install;
|
|
348
|
+
const result = {
|
|
349
|
+
type: inst.type,
|
|
350
|
+
fullInstall: typeof inst.fullInstall === "string" ? inst.fullInstall : null,
|
|
351
|
+
agentInstall: null,
|
|
352
|
+
verify: inst.verify || null,
|
|
353
|
+
instructions: inst.instructions || null,
|
|
354
|
+
url: inst.url || lib.url
|
|
355
|
+
};
|
|
356
|
+
|
|
357
|
+
// Agent-specific install (only for npx-based libs)
|
|
358
|
+
if (agent && inst.agentFlags) {
|
|
359
|
+
const normalized = agent.toLowerCase().replace("antigravity", "agy");
|
|
360
|
+
result.agentInstall = inst.agentFlags[normalized] || null;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
return result;
|
|
364
|
+
}
|