@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.
Files changed (3) hide show
  1. package/CHANGELOG.md +5 -0
  2. package/bin/ctx.js +134 -107
  3. 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
- await streamSetupOutput(() => syncSkills({
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
- console.log("Fetching community skill libraries...\n");
752
- const libraryResults = await fetchSkillsForAgents(agents, {
753
- dataDir: contextOSDataDir()
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@minhpnq1807/contextos",
3
- "version": "0.5.41",
3
+ "version": "0.5.42",
4
4
  "description": "Task-aware AGENTS.md context injection and compliance reporting for Codex, Claude Code, and Antigravity.",
5
5
  "type": "module",
6
6
  "bin": {