@minhpnq1807/contextos 0.5.41 → 0.5.42
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 +5 -0
- package/bin/ctx.js +134 -107
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.5.42
|
|
4
|
+
|
|
5
|
+
- **Setup fallback when no skills found:** During `ctx setup`, if `syncSkills` completes but `detectExistingSkills` finds zero skills on the machine, the CLI now automatically offers the community skill library installer. After successful install, `syncSkills` re-runs to distribute the newly installed skills to all selected agents.
|
|
6
|
+
- **Reusable `runCommunitySkillInstaller()`:** Extracted the fetch → select → install flow into a shared function used by both `ctx skills` and the `ctx setup` fallback. Returns the number of successfully installed sources for downstream branching.
|
|
7
|
+
|
|
3
8
|
## 0.5.41
|
|
4
9
|
|
|
5
10
|
- **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.).
|
package/bin/ctx.js
CHANGED
|
@@ -28,7 +28,7 @@ import { installCopilotHooks } from "../plugins/ctx/lib/copilot-hooks.js";
|
|
|
28
28
|
import { installCopilotMcp } from "../plugins/ctx/lib/copilot-mcp.js";
|
|
29
29
|
import { syncRules } from "../plugins/ctx/lib/ruler-sync.js";
|
|
30
30
|
import { writeInnerGitignore, ensureRootGitignore } from "../plugins/ctx/lib/gitignore.js";
|
|
31
|
-
import { syncSkills } from "../plugins/ctx/lib/skillshare-sync.js";
|
|
31
|
+
import { syncSkills, detectExistingSkills } from "../plugins/ctx/lib/skillshare-sync.js";
|
|
32
32
|
import { scanSkills, warmSkillEmbeddings } from "../plugins/ctx/lib/skill-discoverer.js";
|
|
33
33
|
import { parsePassthroughArgs, runPassthrough } from "../plugins/ctx/lib/passthrough.js";
|
|
34
34
|
import { parseAgentList, parseSetupArgs, setupSummaryLines } from "../plugins/ctx/lib/setup-wizard.js";
|
|
@@ -67,6 +67,115 @@ function runPrefixed(cmd) {
|
|
|
67
67
|
});
|
|
68
68
|
}
|
|
69
69
|
|
|
70
|
+
/**
|
|
71
|
+
* Interactive community skill library installer.
|
|
72
|
+
* Fetches library metadata, shows a multiSelect, and runs install commands.
|
|
73
|
+
* @param {string[]} agents - Agent names to filter libraries for.
|
|
74
|
+
* @returns {Promise<number>} Number of successfully installed sources.
|
|
75
|
+
*/
|
|
76
|
+
async function runCommunitySkillInstaller(agents = []) {
|
|
77
|
+
const RESET = "\x1B[0m";
|
|
78
|
+
const DIM = "\x1B[2m";
|
|
79
|
+
const CYAN = "\x1B[36m";
|
|
80
|
+
const GREEN = "\x1B[32m";
|
|
81
|
+
const YELLOW = "\x1B[33m";
|
|
82
|
+
const BOLD = "\x1B[1m";
|
|
83
|
+
|
|
84
|
+
console.log("Fetching community skill libraries...\n");
|
|
85
|
+
const libraryResults = await fetchSkillsForAgents(agents, { dataDir: contextOSDataDir() });
|
|
86
|
+
|
|
87
|
+
const totalSkills = libraryResults.reduce((sum, r) => sum + r.count, 0);
|
|
88
|
+
if (totalSkills === 0) {
|
|
89
|
+
console.log("No skills found. Check your network connection or try --refresh.");
|
|
90
|
+
return 0;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Compact header
|
|
94
|
+
console.log(`${CYAN}◇${RESET} ${BOLD}Community skill libraries available:${RESET}`);
|
|
95
|
+
console.log(`${DIM}│${RESET} Browse and install curated skills from the community.`);
|
|
96
|
+
console.log(`${DIM}│${RESET}`);
|
|
97
|
+
|
|
98
|
+
const allLibs = getAllLibraries();
|
|
99
|
+
const availableLibs = allLibs.filter((lib) => {
|
|
100
|
+
const result = libraryResults.find((r) => r.library.id === lib.id);
|
|
101
|
+
return result && result.count > 0;
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
if (availableLibs.length === 0) {
|
|
105
|
+
console.log("No installable libraries available.");
|
|
106
|
+
return 0;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const selectedSources = await multiSelect({
|
|
110
|
+
message: "Select skill sources to install:",
|
|
111
|
+
options: availableLibs.map((lib) => {
|
|
112
|
+
const result = libraryResults.find((r) => r.library.id === lib.id);
|
|
113
|
+
return {
|
|
114
|
+
label: `${lib.name} (${result?.count || 0} skills)`,
|
|
115
|
+
value: lib.id,
|
|
116
|
+
hint: lib.url,
|
|
117
|
+
selected: false
|
|
118
|
+
};
|
|
119
|
+
})
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
if (!selectedSources || selectedSources.length === 0) {
|
|
123
|
+
console.log(`\n${DIM}No sources selected.${RESET}`);
|
|
124
|
+
return 0;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Install each selected source
|
|
128
|
+
let successCount = 0;
|
|
129
|
+
for (const libId of selectedSources) {
|
|
130
|
+
const lib = allLibs.find((l) => l.id === libId);
|
|
131
|
+
if (!lib) continue;
|
|
132
|
+
|
|
133
|
+
const installInfo = getInstallCommands(libId);
|
|
134
|
+
if (!installInfo) {
|
|
135
|
+
console.log(`${YELLOW}⚠${RESET} No install info for ${lib.name}. Visit: ${lib.url}`);
|
|
136
|
+
continue;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
console.log("");
|
|
140
|
+
console.log(`${CYAN}◇${RESET} ${BOLD}Installing from ${lib.name}${RESET}`);
|
|
141
|
+
|
|
142
|
+
if (installInfo.type === "manual") {
|
|
143
|
+
console.log(`${DIM}│${RESET} ${installInfo.instructions}`);
|
|
144
|
+
console.log(`${DIM}│${RESET} ${DIM}URL: ${lib.url}${RESET}`);
|
|
145
|
+
continue;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const installCmd = installInfo.fullInstall;
|
|
149
|
+
if (installCmd) {
|
|
150
|
+
console.log(`${DIM}│${RESET} ${GREEN}$ ${installCmd}${RESET}`);
|
|
151
|
+
console.log(`${DIM}│${RESET}`);
|
|
152
|
+
|
|
153
|
+
try {
|
|
154
|
+
await runPrefixed(installCmd);
|
|
155
|
+
successCount++;
|
|
156
|
+
|
|
157
|
+
if (installInfo.verify) {
|
|
158
|
+
try { await runPrefixed(installInfo.verify); } catch { /* best-effort */ }
|
|
159
|
+
}
|
|
160
|
+
console.log(`${DIM}│${RESET}`);
|
|
161
|
+
console.log(`${GREEN}✔${RESET} ${lib.name} installed successfully.`);
|
|
162
|
+
} catch (err) {
|
|
163
|
+
console.error(`${YELLOW}⚠${RESET} Install failed for ${lib.name}. Try manually:`);
|
|
164
|
+
console.error(` ${installCmd}`);
|
|
165
|
+
console.error(` ${DIM}${err.message}${RESET}`);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// Summary
|
|
171
|
+
console.log("");
|
|
172
|
+
if (successCount > 0) {
|
|
173
|
+
console.log(`${GREEN}✔${RESET} ${BOLD}${successCount} source${successCount > 1 ? "s" : ""} installed.${RESET}`);
|
|
174
|
+
console.log(`${DIM}│${RESET} Restart your agent to pick up new skills.`);
|
|
175
|
+
}
|
|
176
|
+
return successCount;
|
|
177
|
+
}
|
|
178
|
+
|
|
70
179
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
71
180
|
const rootDir = path.resolve(__dirname, "..");
|
|
72
181
|
const pluginSourceDir = path.join(rootDir, "plugins", "ctx");
|
|
@@ -626,7 +735,8 @@ async function setup({ args = [], cwd = process.cwd() } = {}) {
|
|
|
626
735
|
const skillAgents = options.agents.map((agent) => agent === "agy" ? "antigravity" : agent).join(",");
|
|
627
736
|
const syncArgs = ["--skills", "--agents", skillAgents];
|
|
628
737
|
if (options.yes) syncArgs.push("--yes");
|
|
629
|
-
|
|
738
|
+
|
|
739
|
+
const doSyncSkills = async () => streamSetupOutput(() => syncSkills({
|
|
630
740
|
cwd,
|
|
631
741
|
args: syncArgs,
|
|
632
742
|
rebuildSkillEmbeddings: async ({ cwd: skillCwd, sourceDir }) => warmSkillEmbeddings({
|
|
@@ -636,6 +746,25 @@ async function setup({ args = [], cwd = process.cwd() } = {}) {
|
|
|
636
746
|
skills: scanSkills({ cwd: skillCwd, roots: [sourceDir] })
|
|
637
747
|
})
|
|
638
748
|
}));
|
|
749
|
+
|
|
750
|
+
await doSyncSkills();
|
|
751
|
+
|
|
752
|
+
// Fallback: if no skills were found, offer community library installer
|
|
753
|
+
const existing = detectExistingSkills({ cwd });
|
|
754
|
+
const totalExisting = existing.reduce((sum, e) => sum + e.count, 0);
|
|
755
|
+
if (totalExisting === 0) {
|
|
756
|
+
console.log("");
|
|
757
|
+
console.log(`${YELLOW}⚠${RESET} No skills found on this machine.`);
|
|
758
|
+
console.log(`${DIM}│${RESET} Install community skills to get started.`);
|
|
759
|
+
console.log("");
|
|
760
|
+
|
|
761
|
+
const installed = await runCommunitySkillInstaller(options.agents);
|
|
762
|
+
if (installed > 0) {
|
|
763
|
+
console.log("");
|
|
764
|
+
console.log("◇ Re-syncing skills after install...");
|
|
765
|
+
await doSyncSkills();
|
|
766
|
+
}
|
|
767
|
+
}
|
|
639
768
|
}
|
|
640
769
|
|
|
641
770
|
console.log("");
|
|
@@ -643,12 +772,6 @@ async function setup({ args = [], cwd = process.cwd() } = {}) {
|
|
|
643
772
|
console.log("│ Next: restart/open your agent from this project directory.");
|
|
644
773
|
console.log("│ Try: ctx debug -- \"Recheck authen flow\"");
|
|
645
774
|
console.log("");
|
|
646
|
-
|
|
647
|
-
// Recommend community skills based on selected agents
|
|
648
|
-
try {
|
|
649
|
-
const libraryResults = await fetchSkillsForAgents(options.agents, { dataDir: contextOSDataDir() });
|
|
650
|
-
printSkillRecommendations(libraryResults);
|
|
651
|
-
} catch { /* skill library is best-effort */ }
|
|
652
775
|
}
|
|
653
776
|
|
|
654
777
|
const args = process.argv.slice(2);
|
|
@@ -748,105 +871,9 @@ try {
|
|
|
748
871
|
const YELLOW = "\x1B[33m";
|
|
749
872
|
const BOLD = "\x1B[1m";
|
|
750
873
|
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
});
|
|
755
|
-
|
|
756
|
-
const totalSkills = libraryResults.reduce((sum, r) => sum + r.count, 0);
|
|
757
|
-
if (totalSkills === 0) {
|
|
758
|
-
console.log("No skills found. Check your network connection or try --refresh.");
|
|
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
|
-
|
|
809
|
-
console.log("");
|
|
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
|
-
}
|
|
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}`);
|
|
874
|
+
const installed = await runCommunitySkillInstaller(agents);
|
|
875
|
+
if (installed === 0) {
|
|
876
|
+
console.log(`\n${DIM}No installations were completed.${RESET}`);
|
|
850
877
|
}
|
|
851
878
|
console.log("");
|
|
852
879
|
} else if (command === "sync") {
|
package/package.json
CHANGED