kenmark-skills 1.1.4 → 1.1.6

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kenmark-skills",
3
- "version": "1.1.4",
3
+ "version": "1.1.6",
4
4
  "description": "Skills by Kenmark ITan Solutions — Cursor/Codex agent skills from our development workflows. Created by Tanooj Mehra & Adwait Date.",
5
5
  "license": "MIT",
6
6
  "private": false,
@@ -15,6 +15,7 @@
15
15
  "scripts/skills-adopt.js",
16
16
  "scripts/cli.js",
17
17
  "scripts/interactive.js",
18
+ "scripts/recommended-catalog.js",
18
19
  "scripts/skills-init.js",
19
20
  "scripts/skills-inventory.js",
20
21
  "scripts/skills-install-recommended.js",
@@ -140,6 +140,70 @@ async function confirmPlan(lines, dryRun = false) {
140
140
  return false;
141
141
  }
142
142
 
143
+ /**
144
+ * @param {Array<{id: string, name: string, defaultSelected?: boolean}>} packs
145
+ * @returns {Promise<string[]>} selected pack ids
146
+ */
147
+ async function promptSelectPacks(packs) {
148
+ const rl = createRl();
149
+ console.log("\nSelect packs to install:\n");
150
+ packs.forEach((p, i) => {
151
+ const mark = p.defaultSelected ? " [default]" : "";
152
+ console.log(` ${i + 1}) ${p.id}${mark} — ${p.name}`);
153
+ });
154
+ console.log("\nEnter: number(s) 1,2 · ids impeccable,ecc · all · defaults · Enter for defaults\n");
155
+ const answer = await ask(rl, "Choice> ");
156
+ rl.close();
157
+ if (!answer) {
158
+ return packs.filter((p) => p.defaultSelected).map((p) => p.id);
159
+ }
160
+ const lower = answer.toLowerCase();
161
+ if (lower === "all") return packs.map((p) => p.id);
162
+ if (lower === "defaults" || lower === "default" || lower === "d") {
163
+ return packs.filter((p) => p.defaultSelected).map((p) => p.id);
164
+ }
165
+ const nums = lower.split(/[\s,]+/).filter(Boolean);
166
+ const byNum = [];
167
+ for (const part of nums) {
168
+ const n = parseInt(part, 10);
169
+ if (!Number.isNaN(n) && n >= 1 && n <= packs.length) {
170
+ byNum.push(packs[n - 1].id);
171
+ }
172
+ }
173
+ if (byNum.length > 0) return [...new Set(byNum)];
174
+ return answer.split(",").map((s) => s.trim()).filter(Boolean);
175
+ }
176
+
177
+ /**
178
+ * @param {{install?: {profiles?: Array<{id: string, description: string}>, defaultProfile?: string}}} pack
179
+ * @param {string|null} preset
180
+ * @returns {Promise<string>}
181
+ */
182
+ async function promptEccProfile(pack, preset) {
183
+ if (preset) return preset;
184
+ const profiles = pack.install?.profiles;
185
+ if (!profiles?.length) {
186
+ return pack.install?.defaultProfile || "core";
187
+ }
188
+ const rl = createRl();
189
+ console.log("\nECC install profile:");
190
+ profiles.forEach((p, i) => {
191
+ console.log(` ${i + 1}) ${p.id} — ${p.description}`);
192
+ });
193
+ const defaultId = pack.install.defaultProfile || "core";
194
+ const answer = await ask(rl, `Profile [1-${profiles.length} or id] (default ${defaultId}): `);
195
+ rl.close();
196
+ if (!answer) return defaultId;
197
+ const num = parseInt(answer, 10);
198
+ if (!Number.isNaN(num) && num >= 1 && num <= profiles.length) {
199
+ return profiles[num - 1].id;
200
+ }
201
+ const byId = profiles.find((p) => p.id === answer);
202
+ if (byId) return byId.id;
203
+ console.error(`Unknown profile "${answer}", using ${defaultId}.`);
204
+ return defaultId;
205
+ }
206
+
143
207
  function banner(title, subtitle) {
144
208
  console.log(`\n${"═".repeat(60)}`);
145
209
  console.log(` ${title}`);
@@ -156,6 +220,8 @@ module.exports = {
156
220
  promptScope,
157
221
  promptAction,
158
222
  promptIde,
223
+ promptSelectPacks,
224
+ promptEccProfile,
159
225
  confirmPlan,
160
226
  banner
161
227
  };
@@ -0,0 +1,20 @@
1
+ #!/usr/bin/env node
2
+
3
+ const fs = require("fs");
4
+ const path = require("path");
5
+
6
+ const catalogPath = path.join(
7
+ path.resolve(__dirname, ".."),
8
+ "skills",
9
+ "user-skills",
10
+ "recommended-catalog.json"
11
+ );
12
+
13
+ function loadCatalog() {
14
+ if (!fs.existsSync(catalogPath)) {
15
+ return { packs: [] };
16
+ }
17
+ return JSON.parse(fs.readFileSync(catalogPath, "utf8"));
18
+ }
19
+
20
+ module.exports = { catalogPath, loadCatalog };
@@ -7,9 +7,12 @@ const {
7
7
  promptScope,
8
8
  promptIde,
9
9
  promptYesNo,
10
+ promptSelectPacks,
11
+ promptEccProfile,
10
12
  confirmPlan,
11
13
  banner
12
14
  } = require("./interactive");
15
+ const { loadCatalog } = require("./recommended-catalog");
13
16
 
14
17
  const repoRoot = path.resolve(__dirname, "..");
15
18
  const setupScript = path.join(__dirname, "setup-skills.js");
@@ -135,6 +138,8 @@ async function run() {
135
138
  let ideArg = args.ide;
136
139
  let installKenmark = !args.recommendedOnly;
137
140
  let installRecommended = !args.skipRecommended;
141
+ let selectedPacks = [];
142
+ let eccProfile = null;
138
143
 
139
144
  if (interactive) {
140
145
  if (!args.recommendedOnly && !args.skipRecommended) {
@@ -148,6 +153,25 @@ async function run() {
148
153
  installRecommended = await promptYesNo("Install only curated recommended packs?", true);
149
154
  }
150
155
  }
156
+ if (installRecommended) {
157
+ const catalog = loadCatalog();
158
+ const packs = catalog.packs || [];
159
+ if (packs.length === 0) {
160
+ console.log("No curated packs available; skipping recommended step.");
161
+ installRecommended = false;
162
+ } else {
163
+ selectedPacks = await promptSelectPacks(packs);
164
+ if (selectedPacks.length === 0) {
165
+ console.log("No packs chosen; skipping recommended step.");
166
+ installRecommended = false;
167
+ } else {
168
+ const eccPack = packs.find((p) => p.id === "ecc");
169
+ if (eccPack && selectedPacks.includes("ecc")) {
170
+ eccProfile = await promptEccProfile(eccPack, null);
171
+ }
172
+ }
173
+ }
174
+ }
151
175
  if (installKenmark || installRecommended) {
152
176
  if (!scope) scope = await promptScope("global");
153
177
  }
@@ -170,7 +194,9 @@ async function run() {
170
194
  plan.push(`Kenmark skills → ${scope} (${ideLabel})`);
171
195
  }
172
196
  if (installRecommended) {
173
- plan.push(`Recommended packs (defaults) ${scope} via npx`);
197
+ const label = selectedPacks.length > 0 ? selectedPacks.join(", ") : "defaults";
198
+ plan.push(`Recommended packs → ${scope}: ${label}`);
199
+ if (eccProfile) plan.push(` ECC profile: ${eccProfile}`);
174
200
  }
175
201
  plan.push("Tip: run init-brain in your agent chat to bootstrap brain/ in a repo");
176
202
 
@@ -212,8 +238,15 @@ async function run() {
212
238
  }
213
239
 
214
240
  if (installRecommended) {
215
- const recArgs = [scope === "project" ? "--project" : "--global", "-y"];
241
+ const recArgs = [scope === "project" ? "--project" : "--global"];
242
+ if (selectedPacks.length > 0) {
243
+ recArgs.push("--ids", selectedPacks.join(","));
244
+ } else {
245
+ recArgs.push("--all");
246
+ }
247
+ if (eccProfile) recArgs.push("--ecc-profile", eccProfile);
216
248
  if (args.dryRun) recArgs.push("--dry-run");
249
+ recArgs.push("-y");
217
250
  const result = runNode(recommendedScript, recArgs, args.dryRun, "Recommended packs");
218
251
  if (!args.dryRun && result.status !== 0) process.exit(result.status || 1);
219
252
  }
@@ -1,23 +1,15 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- const fs = require("fs");
4
- const path = require("path");
5
3
  const { spawnSync } = require("child_process");
6
4
  const {
7
5
  wantsInteractive,
8
6
  promptScope,
7
+ promptSelectPacks,
8
+ promptEccProfile,
9
9
  confirmPlan,
10
10
  banner
11
11
  } = require("./interactive");
12
- const readline = require("readline");
13
-
14
- const repoRoot = path.resolve(__dirname, "..");
15
- const catalogPath = path.join(
16
- repoRoot,
17
- "skills",
18
- "user-skills",
19
- "recommended-catalog.json"
20
- );
12
+ const { loadCatalog } = require("./recommended-catalog");
21
13
 
22
14
  function printUsage() {
23
15
  console.log("Usage: node scripts/skills-install-recommended.js [options]");
@@ -87,14 +79,6 @@ function parseArgs(argv) {
87
79
  return args;
88
80
  }
89
81
 
90
- function loadCatalog() {
91
- if (!fs.existsSync(catalogPath)) {
92
- console.error(`Catalog not found: ${catalogPath}`);
93
- process.exit(1);
94
- }
95
- return JSON.parse(fs.readFileSync(catalogPath, "utf8"));
96
- }
97
-
98
82
  function resolveInstallCommand(pack, scope, eccProfile) {
99
83
  const block = pack.install?.[scope];
100
84
  if (!block?.command) {
@@ -169,72 +153,6 @@ function verifyPack(pack, scope) {
169
153
  return result.status === 0;
170
154
  }
171
155
 
172
- function createRl() {
173
- return readline.createInterface({
174
- input: process.stdin,
175
- output: process.stdout
176
- });
177
- }
178
-
179
- function ask(rl, question) {
180
- return new Promise((resolve) => rl.question(question, (ans) => resolve(ans.trim())));
181
- }
182
-
183
- async function promptEccProfile(pack, preset) {
184
- if (preset) return preset;
185
- const profiles = pack.install?.profiles;
186
- if (!profiles?.length) {
187
- return pack.install?.defaultProfile || "core";
188
- }
189
- const rl = createRl();
190
- console.log("\nECC install profile:");
191
- profiles.forEach((p, i) => {
192
- console.log(` ${i + 1}) ${p.id} — ${p.description}`);
193
- });
194
- const defaultId = pack.install.defaultProfile || "core";
195
- const answer = await ask(rl, `Profile [1-${profiles.length} or id] (default ${defaultId}): `);
196
- rl.close();
197
- if (!answer) return defaultId;
198
- const num = parseInt(answer, 10);
199
- if (!Number.isNaN(num) && num >= 1 && num <= profiles.length) {
200
- return profiles[num - 1].id;
201
- }
202
- const byId = profiles.find((p) => p.id === answer);
203
- if (byId) return byId.id;
204
- console.error(`Unknown profile "${answer}", using ${defaultId}.`);
205
- return defaultId;
206
- }
207
-
208
- async function promptSelectPacks(packs) {
209
- const rl = createRl();
210
- console.log("\nSelect packs to install:\n");
211
- packs.forEach((p, i) => {
212
- const mark = p.defaultSelected ? " [default]" : "";
213
- console.log(` ${i + 1}) ${p.id}${mark} — ${p.name}`);
214
- });
215
- console.log("\nEnter: number(s) 1,2 · ids impeccable,ecc · all · defaults · Enter for defaults\n");
216
- const answer = await ask(rl, "Choice> ");
217
- rl.close();
218
- if (!answer) {
219
- return packs.filter((p) => p.defaultSelected).map((p) => p.id);
220
- }
221
- const lower = answer.toLowerCase();
222
- if (lower === "all") return packs.map((p) => p.id);
223
- if (lower === "defaults" || lower === "default" || lower === "d") {
224
- return packs.filter((p) => p.defaultSelected).map((p) => p.id);
225
- }
226
- const nums = lower.split(/[\s,]+/).filter(Boolean);
227
- const byNum = [];
228
- for (const part of nums) {
229
- const n = parseInt(part, 10);
230
- if (!Number.isNaN(n) && n >= 1 && n <= packs.length) {
231
- byNum.push(packs[n - 1].id);
232
- }
233
- }
234
- if (byNum.length > 0) return [...new Set(byNum)];
235
- return answer.split(",").map((s) => s.trim()).filter(Boolean);
236
- }
237
-
238
156
  async function run() {
239
157
  const args = parseArgs(process.argv.slice(2));
240
158
  if (args.help) {