skilld 1.5.5 → 1.6.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.
Files changed (101) hide show
  1. package/README.md +3 -3
  2. package/dist/_chunks/agent.mjs +0 -77
  3. package/dist/_chunks/agent.mjs.map +1 -1
  4. package/dist/_chunks/assemble.mjs +0 -17
  5. package/dist/_chunks/assemble.mjs.map +1 -1
  6. package/dist/_chunks/author.mjs +0 -18
  7. package/dist/_chunks/author.mjs.map +1 -1
  8. package/dist/_chunks/cache.mjs +0 -72
  9. package/dist/_chunks/cache.mjs.map +1 -1
  10. package/dist/_chunks/cache2.mjs +84 -17
  11. package/dist/_chunks/cache2.mjs.map +1 -1
  12. package/dist/_chunks/cli-helpers.mjs +0 -47
  13. package/dist/_chunks/cli-helpers.mjs.map +1 -1
  14. package/dist/_chunks/cli-helpers2.mjs +0 -11
  15. package/dist/_chunks/config.mjs +0 -27
  16. package/dist/_chunks/config.mjs.map +1 -1
  17. package/dist/_chunks/core.mjs +9 -0
  18. package/dist/_chunks/detect.mjs +29 -226
  19. package/dist/_chunks/detect.mjs.map +1 -1
  20. package/dist/_chunks/embedding-cache.mjs +0 -5
  21. package/dist/_chunks/embedding-cache2.mjs +1 -2
  22. package/dist/_chunks/formatting.mjs +0 -6
  23. package/dist/_chunks/formatting.mjs.map +1 -1
  24. package/dist/_chunks/index.d.mts +0 -10
  25. package/dist/_chunks/index.d.mts.map +1 -1
  26. package/dist/_chunks/index2.d.mts +3 -6
  27. package/dist/_chunks/index2.d.mts.map +1 -1
  28. package/dist/_chunks/index3.d.mts +2 -31
  29. package/dist/_chunks/index3.d.mts.map +1 -1
  30. package/dist/_chunks/install.mjs +0 -15
  31. package/dist/_chunks/install.mjs.map +1 -1
  32. package/dist/_chunks/libs/@sinclair/typebox.mjs +0 -444
  33. package/dist/_chunks/libs/@sinclair/typebox.mjs.map +1 -1
  34. package/dist/_chunks/list.mjs +0 -16
  35. package/dist/_chunks/list.mjs.map +1 -1
  36. package/dist/_chunks/lockfile.mjs +1 -10
  37. package/dist/_chunks/lockfile.mjs.map +1 -1
  38. package/dist/_chunks/markdown.mjs +0 -9
  39. package/dist/_chunks/markdown.mjs.map +1 -1
  40. package/dist/_chunks/package-json.mjs +0 -25
  41. package/dist/_chunks/package-json.mjs.map +1 -1
  42. package/dist/_chunks/pool2.mjs +0 -2
  43. package/dist/_chunks/pool2.mjs.map +1 -1
  44. package/dist/_chunks/prepare.mjs +8 -7
  45. package/dist/_chunks/prepare.mjs.map +1 -1
  46. package/dist/_chunks/prepare2.mjs +0 -18
  47. package/dist/_chunks/prepare2.mjs.map +1 -1
  48. package/dist/_chunks/prompts.mjs +1 -102
  49. package/dist/_chunks/prompts.mjs.map +1 -1
  50. package/dist/_chunks/retriv.mjs +23 -24
  51. package/dist/_chunks/retriv.mjs.map +1 -1
  52. package/dist/_chunks/rolldown-runtime.mjs +0 -2
  53. package/dist/_chunks/sanitize.mjs +0 -78
  54. package/dist/_chunks/sanitize.mjs.map +1 -1
  55. package/dist/_chunks/search-interactive.mjs +0 -17
  56. package/dist/_chunks/search-interactive.mjs.map +1 -1
  57. package/dist/_chunks/search.mjs +0 -19
  58. package/dist/_chunks/search2.mjs +3 -12
  59. package/dist/_chunks/search2.mjs.map +1 -1
  60. package/dist/_chunks/setup.mjs +0 -13
  61. package/dist/_chunks/setup.mjs.map +1 -1
  62. package/dist/_chunks/shared.mjs +0 -10
  63. package/dist/_chunks/shared.mjs.map +1 -1
  64. package/dist/_chunks/skills.mjs +2 -2
  65. package/dist/_chunks/skills.mjs.map +1 -1
  66. package/dist/_chunks/sources.mjs +3 -453
  67. package/dist/_chunks/sources.mjs.map +1 -1
  68. package/dist/_chunks/sync-shared.mjs +0 -16
  69. package/dist/_chunks/sync-shared2.mjs +0 -42
  70. package/dist/_chunks/sync-shared2.mjs.map +1 -1
  71. package/dist/_chunks/sync.mjs +1 -21
  72. package/dist/_chunks/sync.mjs.map +1 -1
  73. package/dist/_chunks/sync2.mjs +0 -20
  74. package/dist/_chunks/types.d.mts +0 -2
  75. package/dist/_chunks/types.d.mts.map +1 -1
  76. package/dist/_chunks/uninstall.mjs +0 -25
  77. package/dist/_chunks/uninstall.mjs.map +1 -1
  78. package/dist/_chunks/validate.mjs +0 -7
  79. package/dist/_chunks/validate.mjs.map +1 -1
  80. package/dist/_chunks/wizard.mjs +0 -2
  81. package/dist/_chunks/wizard.mjs.map +1 -1
  82. package/dist/_chunks/yaml.mjs +0 -21
  83. package/dist/_chunks/yaml.mjs.map +1 -1
  84. package/dist/agent/index.d.mts +0 -24
  85. package/dist/agent/index.d.mts.map +1 -1
  86. package/dist/agent/index.mjs +0 -8
  87. package/dist/cache/index.mjs +0 -2
  88. package/dist/cli-entry.mjs +0 -6
  89. package/dist/cli-entry.mjs.map +1 -1
  90. package/dist/cli.mjs +0 -13
  91. package/dist/cli.mjs.map +1 -1
  92. package/dist/index.mjs +0 -6
  93. package/dist/prepare.mjs +0 -12
  94. package/dist/prepare.mjs.map +1 -1
  95. package/dist/retriv/index.mjs +0 -2
  96. package/dist/retriv/worker.d.mts +0 -3
  97. package/dist/retriv/worker.d.mts.map +1 -1
  98. package/dist/retriv/worker.mjs +0 -2
  99. package/dist/retriv/worker.mjs.map +1 -1
  100. package/dist/sources/index.mjs +0 -4
  101. package/package.json +17 -17
@@ -1,17 +1,10 @@
1
- import { t as CACHE_DIR } from "./config.mjs";
2
- import "./package-json.mjs";
3
- import "./prepare.mjs";
4
- import "./sanitize.mjs";
1
+ import { n as REFERENCES_DIR, r as REPOS_DIR, t as CACHE_DIR } from "./config.mjs";
5
2
  import "./cache.mjs";
6
3
  import { n as clearEmbeddingCache } from "./embedding-cache2.mjs";
7
4
  import { join } from "pathe";
8
5
  import { existsSync, readFileSync, readdirSync, rmSync, statSync } from "node:fs";
9
6
  import * as p from "@clack/prompts";
10
7
  import { defineCommand } from "citty";
11
- //#region src/commands/cache.ts
12
- /**
13
- * Cache management commands
14
- */
15
8
  const LLM_CACHE_DIR = join(CACHE_DIR, "llm-cache");
16
9
  const LLM_CACHE_MAX_AGE = 10080 * 60 * 1e3;
17
10
  function safeRemove(path) {
@@ -61,23 +54,97 @@ async function cacheCleanCommand() {
61
54
  p.log.success(`Removed ${parts.join(" + ")} (${freedKB}KB freed)`);
62
55
  } else p.log.info("Cache is clean — no expired entries");
63
56
  }
57
+ function dirEntries(dir) {
58
+ if (!existsSync(dir)) return [];
59
+ return readdirSync(dir, {
60
+ withFileTypes: true,
61
+ recursive: true
62
+ });
63
+ }
64
+ function sumFileBytes(entries) {
65
+ return entries.filter((e) => e.isFile()).reduce((sum, e) => {
66
+ try {
67
+ return sum + statSync(join(e.parentPath, e.name)).size;
68
+ } catch {
69
+ return sum;
70
+ }
71
+ }, 0);
72
+ }
73
+ function fmtBytes(n) {
74
+ const units = [
75
+ "B",
76
+ "KB",
77
+ "MB",
78
+ "GB"
79
+ ];
80
+ let i = 0;
81
+ while (n >= 1024 && i < units.length - 1) {
82
+ n /= 1024;
83
+ i++;
84
+ }
85
+ return i === 0 ? `${n}${units[i]}` : `${n.toFixed(1)}${units[i]}`;
86
+ }
87
+ function cacheStatsCommand() {
88
+ const dim = (s) => `\x1B[90m${s}\x1B[0m`;
89
+ const refs = dirEntries(REFERENCES_DIR);
90
+ const repos = dirEntries(REPOS_DIR);
91
+ const llm = dirEntries(LLM_CACHE_DIR);
92
+ const embPath = join(CACHE_DIR, "embeddings.db");
93
+ const embSize = existsSync(embPath) ? statSync(embPath).size : 0;
94
+ const packages = refs.filter((e) => e.isDirectory() && (e.parentPath === REFERENCES_DIR ? !e.name.startsWith("@") : e.parentPath.startsWith(REFERENCES_DIR))).length;
95
+ const llmFiles = llm.filter((e) => e.isFile());
96
+ const sizes = {
97
+ refs: sumFileBytes(refs),
98
+ repos: sumFileBytes(repos),
99
+ llm: sumFileBytes(llmFiles),
100
+ emb: embSize
101
+ };
102
+ const total = sizes.refs + sizes.repos + sizes.llm + sizes.emb;
103
+ const lines = [
104
+ `References ${fmtBytes(sizes.refs)} ${dim(`${packages} packages`)}`,
105
+ ...sizes.repos > 0 ? [`Repos ${fmtBytes(sizes.repos)}`] : [],
106
+ `LLM cache ${fmtBytes(sizes.llm)} ${dim(`${llmFiles.length} entries`)}`,
107
+ ...sizes.emb > 0 ? [`Embeddings ${fmtBytes(sizes.emb)}`] : [],
108
+ "",
109
+ `Total ${fmtBytes(total)} ${dim(CACHE_DIR)}`
110
+ ];
111
+ p.log.message(lines.join("\n"));
112
+ }
64
113
  const cacheCommandDef = defineCommand({
65
114
  meta: {
66
115
  name: "cache",
67
116
  description: "Cache management",
68
117
  hidden: true
69
118
  },
70
- args: { clean: {
71
- type: "boolean",
72
- description: "Remove expired enhancement cache entries",
73
- default: true
74
- } },
75
- async run() {
76
- p.intro(`\x1B[1m\x1B[35mskilld\x1B[0m cache clean`);
77
- await cacheCleanCommand();
119
+ args: {
120
+ clean: {
121
+ type: "boolean",
122
+ alias: "c",
123
+ description: "Remove expired enhancement cache entries",
124
+ default: false
125
+ },
126
+ stats: {
127
+ type: "boolean",
128
+ alias: "s",
129
+ description: "Show cache disk usage",
130
+ default: false
131
+ }
132
+ },
133
+ async run({ args }) {
134
+ if (args.stats) {
135
+ p.intro(`\x1B[1m\x1B[35mskilld\x1B[0m cache stats`);
136
+ cacheStatsCommand();
137
+ return;
138
+ }
139
+ if (args.clean) {
140
+ p.intro(`\x1B[1m\x1B[35mskilld\x1B[0m cache clean`);
141
+ await cacheCleanCommand();
142
+ return;
143
+ }
144
+ p.intro(`\x1B[1m\x1B[35mskilld\x1B[0m cache`);
145
+ p.log.message("Usage:\n skilld cache --clean Remove expired cache entries\n skilld cache --stats Show cache disk usage");
78
146
  }
79
147
  });
80
- //#endregion
81
148
  export { cacheCommandDef };
82
149
 
83
150
  //# sourceMappingURL=cache2.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"cache2.mjs","names":[],"sources":["../../src/commands/cache.ts"],"sourcesContent":["/**\n * Cache management commands\n */\n\nimport { existsSync, readdirSync, readFileSync, rmSync, statSync } from 'node:fs'\nimport * as p from '@clack/prompts'\nimport { defineCommand } from 'citty'\nimport { join } from 'pathe'\nimport { CACHE_DIR } from '../cache/index.ts'\nimport { clearEmbeddingCache } from '../retriv/embedding-cache.ts'\n\nconst LLM_CACHE_DIR = join(CACHE_DIR, 'llm-cache')\nconst LLM_CACHE_MAX_AGE = 7 * 24 * 60 * 60 * 1000\n\nfunction safeRemove(path: string): number {\n try {\n const size = statSync(path).size\n rmSync(path)\n return size\n }\n catch {\n try {\n rmSync(path)\n }\n catch {}\n return 0\n }\n}\n\nexport async function cacheCleanCommand(): Promise<void> {\n let expiredLlm = 0\n let freedBytes = 0\n\n // Clean expired LLM cache entries\n if (existsSync(LLM_CACHE_DIR)) {\n const now = Date.now()\n for (const entry of readdirSync(LLM_CACHE_DIR)) {\n const path = join(LLM_CACHE_DIR, entry)\n try {\n const { timestamp } = JSON.parse(readFileSync(path, 'utf-8'))\n if (now - timestamp > LLM_CACHE_MAX_AGE) {\n freedBytes += safeRemove(path)\n expiredLlm++\n }\n }\n catch {\n // Corrupt cache entry — remove it\n freedBytes += safeRemove(path)\n expiredLlm++\n }\n }\n }\n\n // Clear embedding cache\n const embeddingDbPath = join(CACHE_DIR, 'embeddings.db')\n let embeddingCleared = false\n if (existsSync(embeddingDbPath)) {\n const size = statSync(embeddingDbPath).size\n clearEmbeddingCache()\n freedBytes += size\n embeddingCleared = true\n }\n\n const freedKB = Math.round(freedBytes / 1024)\n if (expiredLlm > 0 || embeddingCleared) {\n const parts: string[] = []\n if (expiredLlm > 0)\n parts.push(`${expiredLlm} expired enhancement cache entries`)\n if (embeddingCleared)\n parts.push('embedding cache')\n p.log.success(`Removed ${parts.join(' + ')} (${freedKB}KB freed)`)\n }\n else {\n p.log.info('Cache is clean — no expired entries')\n }\n}\n\nexport const cacheCommandDef = defineCommand({\n meta: { name: 'cache', description: 'Cache management', hidden: true },\n args: {\n clean: {\n type: 'boolean',\n description: 'Remove expired enhancement cache entries',\n default: true,\n },\n },\n async run() {\n p.intro(`\\x1B[1m\\x1B[35mskilld\\x1B[0m cache clean`)\n await cacheCleanCommand()\n },\n})\n"],"mappings":";;;;;;;;;;;;;;AAWA,MAAM,gBAAgB,KAAK,WAAW,YAAY;AAClD,MAAM,oBAAoB,QAAc,KAAK;AAE7C,SAAS,WAAW,MAAsB;AACxC,KAAI;EACF,MAAM,OAAO,SAAS,KAAK,CAAC;AAC5B,SAAO,KAAK;AACZ,SAAO;SAEH;AACJ,MAAI;AACF,UAAO,KAAK;UAER;AACN,SAAO;;;AAIX,eAAsB,oBAAmC;CACvD,IAAI,aAAa;CACjB,IAAI,aAAa;AAGjB,KAAI,WAAW,cAAc,EAAE;EAC7B,MAAM,MAAM,KAAK,KAAK;AACtB,OAAK,MAAM,SAAS,YAAY,cAAc,EAAE;GAC9C,MAAM,OAAO,KAAK,eAAe,MAAM;AACvC,OAAI;IACF,MAAM,EAAE,cAAc,KAAK,MAAM,aAAa,MAAM,QAAQ,CAAC;AAC7D,QAAI,MAAM,YAAY,mBAAmB;AACvC,mBAAc,WAAW,KAAK;AAC9B;;WAGE;AAEJ,kBAAc,WAAW,KAAK;AAC9B;;;;CAMN,MAAM,kBAAkB,KAAK,WAAW,gBAAgB;CACxD,IAAI,mBAAmB;AACvB,KAAI,WAAW,gBAAgB,EAAE;EAC/B,MAAM,OAAO,SAAS,gBAAgB,CAAC;AACvC,uBAAqB;AACrB,gBAAc;AACd,qBAAmB;;CAGrB,MAAM,UAAU,KAAK,MAAM,aAAa,KAAK;AAC7C,KAAI,aAAa,KAAK,kBAAkB;EACtC,MAAM,QAAkB,EAAE;AAC1B,MAAI,aAAa,EACf,OAAM,KAAK,GAAG,WAAW,oCAAoC;AAC/D,MAAI,iBACF,OAAM,KAAK,kBAAkB;AAC/B,IAAE,IAAI,QAAQ,WAAW,MAAM,KAAK,MAAM,CAAC,IAAI,QAAQ,WAAW;OAGlE,GAAE,IAAI,KAAK,sCAAsC;;AAIrD,MAAa,kBAAkB,cAAc;CAC3C,MAAM;EAAE,MAAM;EAAS,aAAa;EAAoB,QAAQ;EAAM;CACtE,MAAM,EACJ,OAAO;EACL,MAAM;EACN,aAAa;EACb,SAAS;EACV,EACF;CACD,MAAM,MAAM;AACV,IAAE,MAAM,2CAA2C;AACnD,QAAM,mBAAmB;;CAE5B,CAAC"}
1
+ {"version":3,"file":"cache2.mjs","names":[],"sources":["../../src/commands/cache.ts"],"sourcesContent":["/**\n * Cache management commands\n */\n\nimport type { Dirent } from 'node:fs'\nimport { existsSync, readdirSync, readFileSync, rmSync, statSync } from 'node:fs'\nimport * as p from '@clack/prompts'\nimport { defineCommand } from 'citty'\nimport { join } from 'pathe'\nimport { CACHE_DIR, REFERENCES_DIR, REPOS_DIR } from '../cache/index.ts'\nimport { clearEmbeddingCache } from '../retriv/embedding-cache.ts'\n\nconst LLM_CACHE_DIR = join(CACHE_DIR, 'llm-cache')\nconst LLM_CACHE_MAX_AGE = 7 * 24 * 60 * 60 * 1000\n\nfunction safeRemove(path: string): number {\n try {\n const size = statSync(path).size\n rmSync(path)\n return size\n }\n catch {\n try {\n rmSync(path)\n }\n catch {}\n return 0\n }\n}\n\nexport async function cacheCleanCommand(): Promise<void> {\n let expiredLlm = 0\n let freedBytes = 0\n\n // Clean expired LLM cache entries\n if (existsSync(LLM_CACHE_DIR)) {\n const now = Date.now()\n for (const entry of readdirSync(LLM_CACHE_DIR)) {\n const path = join(LLM_CACHE_DIR, entry)\n try {\n const { timestamp } = JSON.parse(readFileSync(path, 'utf-8'))\n if (now - timestamp > LLM_CACHE_MAX_AGE) {\n freedBytes += safeRemove(path)\n expiredLlm++\n }\n }\n catch {\n // Corrupt cache entry — remove it\n freedBytes += safeRemove(path)\n expiredLlm++\n }\n }\n }\n\n // Clear embedding cache\n const embeddingDbPath = join(CACHE_DIR, 'embeddings.db')\n let embeddingCleared = false\n if (existsSync(embeddingDbPath)) {\n const size = statSync(embeddingDbPath).size\n clearEmbeddingCache()\n freedBytes += size\n embeddingCleared = true\n }\n\n const freedKB = Math.round(freedBytes / 1024)\n if (expiredLlm > 0 || embeddingCleared) {\n const parts: string[] = []\n if (expiredLlm > 0)\n parts.push(`${expiredLlm} expired enhancement cache entries`)\n if (embeddingCleared)\n parts.push('embedding cache')\n p.log.success(`Removed ${parts.join(' + ')} (${freedKB}KB freed)`)\n }\n else {\n p.log.info('Cache is clean — no expired entries')\n }\n}\n\nfunction dirEntries(dir: string): Dirent[] {\n if (!existsSync(dir))\n return []\n return readdirSync(dir, { withFileTypes: true, recursive: true })\n}\n\nfunction sumFileBytes(entries: Dirent[]): number {\n return entries\n .filter(e => e.isFile())\n .reduce((sum, e) => {\n try {\n return sum + statSync(join(e.parentPath, e.name)).size\n }\n catch { return sum }\n }, 0)\n}\n\nfunction fmtBytes(n: number): string {\n const units = ['B', 'KB', 'MB', 'GB'] as const\n let i = 0\n while (n >= 1024 && i < units.length - 1) {\n n /= 1024\n i++\n }\n return i === 0 ? `${n}${units[i]}` : `${n.toFixed(1)}${units[i]}`\n}\n\nexport function cacheStatsCommand(): void {\n const dim = (s: string) => `\\x1B[90m${s}\\x1B[0m`\n\n const refs = dirEntries(REFERENCES_DIR)\n const repos = dirEntries(REPOS_DIR)\n const llm = dirEntries(LLM_CACHE_DIR)\n const embPath = join(CACHE_DIR, 'embeddings.db')\n const embSize = existsSync(embPath) ? statSync(embPath).size : 0\n\n // Count packages: top-level non-scoped dirs + dirs inside @scope/ dirs\n const packages = refs.filter(e =>\n e.isDirectory()\n && (e.parentPath === REFERENCES_DIR\n ? !e.name.startsWith('@')\n : e.parentPath.startsWith(REFERENCES_DIR)),\n ).length\n\n const llmFiles = llm.filter(e => e.isFile())\n const sizes = { refs: sumFileBytes(refs), repos: sumFileBytes(repos), llm: sumFileBytes(llmFiles), emb: embSize }\n const total = sizes.refs + sizes.repos + sizes.llm + sizes.emb\n\n const lines = [\n `References ${fmtBytes(sizes.refs)} ${dim(`${packages} packages`)}`,\n ...(sizes.repos > 0 ? [`Repos ${fmtBytes(sizes.repos)}`] : []),\n `LLM cache ${fmtBytes(sizes.llm)} ${dim(`${llmFiles.length} entries`)}`,\n ...(sizes.emb > 0 ? [`Embeddings ${fmtBytes(sizes.emb)}`] : []),\n '',\n `Total ${fmtBytes(total)} ${dim(CACHE_DIR)}`,\n ]\n p.log.message(lines.join('\\n'))\n}\n\nexport const cacheCommandDef = defineCommand({\n meta: { name: 'cache', description: 'Cache management', hidden: true },\n args: {\n clean: {\n type: 'boolean',\n alias: 'c',\n description: 'Remove expired enhancement cache entries',\n default: false,\n },\n stats: {\n type: 'boolean',\n alias: 's',\n description: 'Show cache disk usage',\n default: false,\n },\n },\n async run({ args }) {\n if (args.stats) {\n p.intro(`\\x1B[1m\\x1B[35mskilld\\x1B[0m cache stats`)\n cacheStatsCommand()\n return\n }\n if (args.clean) {\n p.intro(`\\x1B[1m\\x1B[35mskilld\\x1B[0m cache clean`)\n await cacheCleanCommand()\n return\n }\n // No flag: show usage\n p.intro(`\\x1B[1m\\x1B[35mskilld\\x1B[0m cache`)\n p.log.message('Usage:\\n skilld cache --clean Remove expired cache entries\\n skilld cache --stats Show cache disk usage')\n },\n})\n"],"mappings":"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"}
@@ -10,7 +10,6 @@ import { fileURLToPath } from "node:url";
10
10
  import * as p from "@clack/prompts";
11
11
  import { dirname as dirname$1, resolve as resolve$1 } from "node:path";
12
12
  import { detectCurrentAgent } from "unagent/env";
13
- //#region src/core/config.ts
14
13
  const defaultFeatures = {
15
14
  search: true,
16
15
  issues: true,
@@ -23,7 +22,6 @@ let configCache;
23
22
  function hasConfig() {
24
23
  return existsSync(CONFIG_PATH);
25
24
  }
26
- /** Whether the first-run wizard has been completed (not just agent selection) */
27
25
  function hasCompletedWizard() {
28
26
  if (!existsSync(CONFIG_PATH)) return false;
29
27
  const config = readConfig();
@@ -127,8 +125,6 @@ function unregisterProject(projectPath) {
127
125
  function getRegisteredProjects() {
128
126
  return readConfig().projects || [];
129
127
  }
130
- //#endregion
131
- //#region src/version.ts
132
128
  function findPackageJson() {
133
129
  let dir = dirname$1(fileURLToPath(import.meta.url));
134
130
  for (let i = 0; i < 5; i++) {
@@ -142,8 +138,6 @@ function findPackageJson() {
142
138
  return "{\"version\":\"0.0.0\"}";
143
139
  }
144
140
  const version = JSON.parse(findPackageJson()).version;
145
- //#endregion
146
- //#region src/cli-helpers.ts
147
141
  const sharedArgs = {
148
142
  global: {
149
143
  type: "boolean",
@@ -181,25 +175,13 @@ const sharedArgs = {
181
175
  default: false
182
176
  }
183
177
  };
184
- /** Thrown when a clack prompt is cancelled inside a menuLoop handler */
185
178
  var MenuCancel = class extends Error {
186
179
  name = "MenuCancel";
187
180
  };
188
- /** Assert a clack prompt result is not cancelled. Throws MenuCancel if cancelled. */
189
181
  function guard(value) {
190
182
  if (p.isCancel(value)) throw new MenuCancel();
191
183
  return value;
192
184
  }
193
- /**
194
- * Run a select menu in a loop with automatic back-navigation.
195
- *
196
- * - Cancel (Escape) at the menu itself → exits (returns)
197
- * - Cancel inside a handler (via guard()) → caught, loops back to menu
198
- * - Handler returns truthy → exits (returns)
199
- * - Handler returns void/false → loops back to menu
200
- *
201
- * Options are rebuilt each iteration so hints stay fresh after changes.
202
- */
203
185
  async function menuLoop(opts) {
204
186
  while (true) {
205
187
  const options = await opts.options();
@@ -222,25 +204,21 @@ async function menuLoop(opts) {
222
204
  }
223
205
  }
224
206
  }
225
- /** Check if we're running inside an AI coding agent */
226
207
  function isRunningInsideAgent() {
227
208
  return !!detectCurrentAgent();
228
209
  }
229
- /** Check if the current environment supports interactive prompts */
230
210
  function isInteractive() {
231
211
  if (isRunningInsideAgent()) return false;
232
212
  if (process.env.CI) return false;
233
213
  if (!process.stdout.isTTY) return false;
234
214
  return true;
235
215
  }
236
- /** Exit with error if interactive terminal is required but unavailable */
237
216
  function requireInteractive(command) {
238
217
  if (!isInteractive()) {
239
218
  console.error(`Error: \`skilld ${command}\` requires an interactive terminal`);
240
219
  process.exit(1);
241
220
  }
242
221
  }
243
- /** Resolve agent from flags/cwd/config. cwd is source of truth over config. */
244
222
  function resolveAgent(agentFlag) {
245
223
  if (process.env.SKILLD_NO_AGENT) return null;
246
224
  return agentFlag ?? detectTargetAgent() ?? readConfig().agent ?? null;
@@ -251,7 +229,6 @@ function warnNoAgent() {
251
229
  _warnedNoAgent = true;
252
230
  p.log.warn("No target agent detected — falling back to prompt-only mode.\n Use --agent <name> to specify, or run `skilld config` to set a default.");
253
231
  }
254
- /** Prompt user to pick an agent when auto-detection fails */
255
232
  async function promptForAgent() {
256
233
  const noAgent = !!process.env.SKILLD_NO_AGENT;
257
234
  const installed = noAgent ? [] : detectInstalledAgents();
@@ -311,7 +288,6 @@ async function promptForAgent() {
311
288
  p.log.success(`Target agent set to ${targets[choice].displayName}`);
312
289
  return choice;
313
290
  }
314
- /** Get installed LLM generators with working CLIs (verified via --version) */
315
291
  function getInstalledGenerators() {
316
292
  return detectInstalledAgents().filter((id) => targets[id].cli).map((id) => {
317
293
  const ver = getAgentVersion(id);
@@ -358,7 +334,6 @@ function formatStatus(synced, outdated) {
358
334
  }
359
335
  const OAUTH_NOTE = "\x1B[33m⚠\x1B[0m OAuth providers are disabled.\n\nConsumer subscription OAuth impersonates official CLI clients and\nviolates provider Terms of Service, risking account bans.\n\nUse API keys or native CLI tools instead:\n \x1B[36mANTHROPIC_API_KEY\x1B[0m / \x1B[36mclaude\x1B[0m CLI\n \x1B[36mOPENAI_API_KEY\x1B[0m / \x1B[36mcodex\x1B[0m CLI\n \x1B[36mGEMINI_API_KEY\x1B[0m / \x1B[36mgemini\x1B[0m CLI";
360
336
  const NO_MODELS_MESSAGE = "No enhancement models detected.\n \x1B[90mSkills work fine without this, you get raw docs, issues, and types.\n Enhancement compresses them into a concise cheat sheet with gotchas.\x1B[0m\n\n To connect a model (optional):\n 1. Set an env var: ANTHROPIC_API_KEY, GEMINI_API_KEY, or OPENAI_API_KEY\n 2. Install a CLI tool: \x1B[36mclaude\x1B[0m, \x1B[36mgemini\x1B[0m, or \x1B[36mcodex\x1B[0m (restart wizard after)";
361
- /** Group models by vendor for provider→model selection. Uses vendorGroup to merge CLI and API entries under one heading. */
362
337
  function groupModelsByProvider(models) {
363
338
  const byVendor = /* @__PURE__ */ new Map();
364
339
  for (const m of models) {
@@ -371,10 +346,6 @@ function groupModelsByProvider(models) {
371
346
  }
372
347
  return byVendor;
373
348
  }
374
- /**
375
- * Smart provider→model picker. Skips the provider step when there's only 1 provider.
376
- * Returns the selected model value, or a sentinel string from before/after options.
377
- */
378
349
  async function pickModel(models, opts = {}) {
379
350
  const byProvider = groupModelsByProvider(models);
380
351
  const before = opts.before ?? [];
@@ -417,20 +388,12 @@ async function pickModel(models, opts = {}) {
417
388
  });
418
389
  return p.isCancel(modelChoice) ? null : modelChoice;
419
390
  }
420
- /**
421
- * Check if the prepare hook is already installed in package.json.
422
- */
423
391
  function hasPrepareHook(cwd = process.cwd()) {
424
392
  const pkg = readPackageJsonSafe(join(cwd, "package.json"));
425
393
  if (!pkg) return true;
426
394
  const existing = pkg.parsed.scripts?.prepare;
427
395
  return typeof existing === "string" && existing.includes("skilld");
428
396
  }
429
- /**
430
- * Prompt to add `skilld prepare` to package.json "prepare" script.
431
- * In non-interactive environments, falls back to an info log.
432
- * Returns true if the hook was added or already present.
433
- */
434
397
  async function suggestPrepareHook(cwd = process.cwd()) {
435
398
  const pkgJsonPath = join(cwd, "package.json");
436
399
  const pkg = readPackageJsonSafe(pkgJsonPath);
@@ -457,9 +420,6 @@ async function suggestPrepareHook(cwd = process.cwd()) {
457
420
  p.log.success("Added \x1B[36mskilld prepare\x1B[0m to package.json");
458
421
  return true;
459
422
  }
460
- /**
461
- * Build the full prepare script value, safely appending to any existing command.
462
- */
463
423
  function buildPrepareScript(existing, cwd = process.cwd()) {
464
424
  const cmd = `${isNpxExecution() && !isSkilldDep(cwd) ? "npx skilld" : "skilld"} prepare || true`;
465
425
  if (!existing || !existing.trim()) return cmd;
@@ -467,17 +427,11 @@ function buildPrepareScript(existing, cwd = process.cwd()) {
467
427
  if (!cleaned) return cmd;
468
428
  return `${cleaned} && (${cmd})`;
469
429
  }
470
- /**
471
- * Detect if the current process was launched via npx, pnpm dlx, or similar one-shot runners.
472
- */
473
430
  function isNpxExecution() {
474
431
  if (process.env.npm_command === "exec") return true;
475
432
  const execPath = process.env._ || "";
476
433
  return /npx|\.store|dlx/.test(execPath);
477
434
  }
478
- /**
479
- * Check if skilld is listed as a dependency (dev or regular) in the project's package.json.
480
- */
481
435
  function isSkilldDep(cwd) {
482
436
  const pkg = readPackageJsonSafe(join(cwd, "package.json"));
483
437
  if (!pkg) return false;
@@ -492,7 +446,6 @@ function getRepoHint(name, cwd) {
492
446
  if (!url) return void 0;
493
447
  return url.replace(/^git\+/, "").replace(/\.git$/, "").replace(/^git:\/\//, "https://").replace(/^ssh:\/\/git@github\.com/, "https://github.com").replace(/^https?:\/\/(www\.)?github\.com\//, "");
494
448
  }
495
- //#endregion
496
449
  export { unregisterProject as A, version as C, hasConfig as D, hasCompletedWizard as E, readConfig as O, suggestPrepareHook as S, getRegisteredProjects as T, promptForAgent as _, formatStatus as a, resolveAgent as b, getRepoHint as c, hasPrepareHook as d, introLine as f, pickModel as g, menuLoop as h, buildPrepareScript as i, updateConfig as j, registerProject as k, groupModelsByProvider as l, isRunningInsideAgent as m, NO_MODELS_MESSAGE as n, getInstalledGenerators as o, isInteractive as p, OAUTH_NOTE as r, getLastSynced as s, MenuCancel as t, guard as u, relativeTime as v, defaultFeatures as w, sharedArgs as x, requireInteractive as y };
497
450
 
498
451
  //# sourceMappingURL=cli-helpers.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"cli-helpers.mjs","names":["dirname","resolve","agents"],"sources":["../../src/core/config.ts","../../src/version.ts","../../src/cli-helpers.ts"],"sourcesContent":["import type { OptimizeModel } from '../agent/index.ts'\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs'\nimport { homedir } from 'node:os'\nimport { join } from 'pathe'\nimport { yamlEscape, yamlParseKV, yamlUnescape } from './yaml.ts'\n\nexport interface FeaturesConfig {\n search: boolean\n issues: boolean\n discussions: boolean\n releases: boolean\n}\n\nexport const defaultFeatures: FeaturesConfig = {\n search: true,\n issues: true,\n discussions: true,\n releases: true,\n}\n\nexport interface SkilldConfig {\n model?: OptimizeModel\n agent?: string\n features?: FeaturesConfig\n projects?: string[]\n skipLlm?: boolean\n}\n\nconst CONFIG_DIR = join(homedir(), '.skilld')\nconst CONFIG_PATH = join(CONFIG_DIR, 'config.yaml')\n\nlet configCache: SkilldConfig | undefined\n\nexport function hasConfig(): boolean {\n return existsSync(CONFIG_PATH)\n}\n\n/** Whether the first-run wizard has been completed (not just agent selection) */\nexport function hasCompletedWizard(): boolean {\n if (!existsSync(CONFIG_PATH))\n return false\n const config = readConfig()\n return config.features !== undefined || config.model !== undefined || config.skipLlm !== undefined\n}\n\nexport function readConfig(): SkilldConfig {\n if (configCache) {\n return {\n ...configCache,\n features: configCache.features ? { ...configCache.features } : undefined,\n projects: configCache.projects ? [...configCache.projects] : undefined,\n }\n }\n if (!existsSync(CONFIG_PATH))\n return {}\n\n const content = readFileSync(CONFIG_PATH, 'utf-8')\n const config: SkilldConfig = {}\n let inBlock: 'projects' | 'features' | null = null\n const projects: string[] = []\n const features: Partial<FeaturesConfig> = {}\n\n for (const line of content.split('\\n')) {\n if (line.startsWith('projects:')) {\n inBlock = 'projects'\n continue\n }\n if (line.startsWith('features:')) {\n inBlock = 'features'\n continue\n }\n if (inBlock === 'projects') {\n if (line.startsWith(' - ')) {\n projects.push(yamlUnescape(line.slice(4)))\n continue\n }\n inBlock = null\n }\n if (inBlock === 'features') {\n const m = line.match(/^ {2}(\\w+):\\s*(.+)/)\n if (m) {\n const key = m[1] as keyof FeaturesConfig\n if (key in defaultFeatures)\n features[key] = m[2] === 'true'\n continue\n }\n inBlock = null\n }\n const kv = yamlParseKV(line)\n if (!kv)\n continue\n const [key, value] = kv\n if (key === 'model' && value)\n config.model = value as OptimizeModel\n if (key === 'agent' && value)\n config.agent = value\n if (key === 'skipLlm')\n config.skipLlm = value === 'true'\n }\n\n if (projects.length > 0)\n config.projects = projects\n if (Object.keys(features).length > 0)\n config.features = { ...defaultFeatures, ...features }\n configCache = config\n return config\n}\n\nexport function writeConfig(config: SkilldConfig): void {\n mkdirSync(CONFIG_DIR, { recursive: true, mode: 0o700 })\n\n let yaml = ''\n if (config.model)\n yaml += `model: ${config.model}\\n`\n if (config.agent)\n yaml += `agent: ${config.agent}\\n`\n if (config.skipLlm)\n yaml += `skipLlm: true\\n`\n if (config.features) {\n yaml += 'features:\\n'\n for (const [k, v] of Object.entries(config.features)) {\n yaml += ` ${k}: ${v}\\n`\n }\n }\n if (config.projects?.length) {\n yaml += 'projects:\\n'\n for (const p of config.projects) {\n yaml += ` - ${yamlEscape(p)}\\n`\n }\n }\n\n writeFileSync(CONFIG_PATH, yaml, { mode: 0o600 })\n configCache = undefined\n}\n\nexport function updateConfig(updates: Partial<SkilldConfig>): void {\n const config = readConfig()\n writeConfig({ ...config, ...updates })\n}\n\nexport function registerProject(projectPath: string): void {\n const config = readConfig()\n const projects = new Set(config.projects || [])\n projects.add(projectPath)\n writeConfig({ ...config, projects: [...projects] })\n}\n\nexport function unregisterProject(projectPath: string): void {\n const config = readConfig()\n const projects = (config.projects || []).filter(p => p !== projectPath)\n writeConfig({ ...config, projects })\n}\n\nexport function getRegisteredProjects(): string[] {\n return readConfig().projects || []\n}\n","import { readFileSync } from 'node:fs'\nimport { dirname, resolve } from 'node:path'\nimport { fileURLToPath } from 'node:url'\n\n// Walk up from current file to find package.json (works in both src/ and dist/_chunks/)\nfunction findPackageJson(): string {\n let dir = dirname(fileURLToPath(import.meta.url))\n for (let i = 0; i < 5; i++) {\n const candidate = resolve(dir, 'package.json')\n try {\n const content = readFileSync(candidate, 'utf8')\n if (content)\n return content\n }\n catch {}\n dir = resolve(dir, '..')\n }\n return '{\"version\":\"0.0.0\"}'\n}\n\nexport const version: string = JSON.parse(findPackageJson()).version\n","/**\n * Shared CLI helpers used by subcommand definitions and the main CLI entry.\n * Extracted to avoid circular deps between cli.ts and commands/*.ts.\n */\n\nimport type { AgentType, OptimizeModel } from './agent/index.ts'\nimport type { ProjectState } from './core/skills.ts'\nimport * as p from '@clack/prompts'\nimport { parseTree } from 'jsonc-parser'\nimport { join } from 'pathe'\nimport { detectCurrentAgent } from 'unagent/env'\nimport { agents, detectInstalledAgents, detectProjectAgents, detectTargetAgent, getAgentVersion, getModelName } from './agent/index.ts'\nimport { readConfig, updateConfig } from './core/config.ts'\nimport { editJsonProperty, patchPackageJson, readPackageJsonSafe } from './core/package-json.ts'\nimport { version } from './version.ts'\n\nexport type { AgentType, OptimizeModel }\n\nexport interface IntroOptions {\n state: ProjectState\n /** Installed CLIs that can serve as enhancement models */\n generators?: Array<{ name: string, version: string }>\n /** Configured enhancement model ID */\n modelId?: string\n /** Resolved target agent ID */\n agentId?: string\n}\n\nexport const sharedArgs = {\n global: {\n type: 'boolean' as const,\n alias: 'g',\n description: 'Install globally to ~/<agent>/skills',\n default: false,\n },\n agent: {\n type: 'enum' as const,\n options: Object.keys(agents),\n alias: 'a',\n description: 'Target agent — where skills are installed',\n },\n model: {\n type: 'string' as const,\n alias: 'm',\n description: 'Enhancement model for SKILL.md generation',\n valueHint: 'id',\n },\n yes: {\n type: 'boolean' as const,\n alias: 'y',\n description: 'Skip prompts, use defaults',\n default: false,\n },\n force: {\n type: 'boolean' as const,\n alias: 'f',\n description: 'Ignore all caches, re-fetch docs and regenerate',\n default: false,\n },\n debug: {\n type: 'boolean' as const,\n description: 'Save raw enhancement output to logs/ for each section',\n default: false,\n },\n}\n\n// ── Menu loop utility ─────────────────────────────────────────────────\n\n/** Thrown when a clack prompt is cancelled inside a menuLoop handler */\nexport class MenuCancel extends Error { override name = 'MenuCancel' }\n\n/** Assert a clack prompt result is not cancelled. Throws MenuCancel if cancelled. */\nexport function guard<T>(value: T | symbol): T {\n if (p.isCancel(value))\n throw new MenuCancel()\n return value as T\n}\n\nexport interface MenuOption {\n label: string\n value: string\n hint?: string\n}\n\n/**\n * Run a select menu in a loop with automatic back-navigation.\n *\n * - Cancel (Escape) at the menu itself → exits (returns)\n * - Cancel inside a handler (via guard()) → caught, loops back to menu\n * - Handler returns truthy → exits (returns)\n * - Handler returns void/false → loops back to menu\n *\n * Options are rebuilt each iteration so hints stay fresh after changes.\n */\nexport async function menuLoop(opts: {\n message: string\n options: () => MenuOption[] | Promise<MenuOption[]>\n onSelect: (value: string) => Promise<boolean | void>\n initialValue?: string | (() => string | undefined)\n /** Use fuzzy-searchable autocomplete instead of static select */\n searchable?: boolean\n}): Promise<void> {\n while (true) {\n const options = await opts.options()\n const initial = typeof opts.initialValue === 'function' ? opts.initialValue() : opts.initialValue\n const choice = opts.searchable\n ? await p.autocomplete({ message: opts.message, options, ...(initial != null ? { initialValue: initial } : {}) })\n : await p.select({ message: opts.message, options, ...(initial != null ? { initialValue: initial } : {}) })\n if (p.isCancel(choice))\n return\n try {\n if (await opts.onSelect(choice as string))\n return\n }\n catch (err) {\n if (err instanceof MenuCancel)\n continue\n throw err\n }\n }\n}\n\n/** Check if we're running inside an AI coding agent */\nexport function isRunningInsideAgent(): boolean {\n return !!detectCurrentAgent()\n}\n\n/** Check if the current environment supports interactive prompts */\nexport function isInteractive(): boolean {\n if (isRunningInsideAgent())\n return false\n if (process.env.CI)\n return false\n if (!process.stdout.isTTY)\n return false\n return true\n}\n\n/** Exit with error if interactive terminal is required but unavailable */\nexport function requireInteractive(command: string): void {\n if (!isInteractive()) {\n console.error(`Error: \\`skilld ${command}\\` requires an interactive terminal`)\n process.exit(1)\n }\n}\n\n/** Resolve agent from flags/cwd/config. cwd is source of truth over config. */\nexport function resolveAgent(agentFlag?: string): AgentType | 'none' | null {\n if (process.env.SKILLD_NO_AGENT)\n return null\n return (agentFlag as AgentType | undefined)\n ?? detectTargetAgent()\n ?? (readConfig().agent as AgentType | undefined)\n ?? null\n}\n\nlet _warnedNoAgent = false\nfunction warnNoAgent(): void {\n if (_warnedNoAgent)\n return\n _warnedNoAgent = true\n p.log.warn('No target agent detected — falling back to prompt-only mode.\\n Use --agent <name> to specify, or run `skilld config` to set a default.')\n}\n\n/** Prompt user to pick an agent when auto-detection fails */\nexport async function promptForAgent(): Promise<AgentType | 'none' | null> {\n const noAgent = !!process.env.SKILLD_NO_AGENT\n const installed = noAgent ? [] : detectInstalledAgents()\n const projectMatches = noAgent ? [] : detectProjectAgents()\n\n // Non-interactive: auto-select sole installed agent or fall back to prompt-only\n if (!isInteractive()) {\n if (installed.length === 1) {\n updateConfig({ agent: installed[0] })\n return installed[0]!\n }\n warnNoAgent()\n return 'none'\n }\n\n // Brief context before asking about agents\n p.log.info(\n `Skilld generates reference cards from package docs so your AI agent\\n`\n + ` always has accurate APIs for your exact dependency versions.`,\n )\n\n // Build options: prefer project-matched agents, then installed, then all\n const candidateIds = projectMatches.length > 0\n ? projectMatches\n : installed.length > 0\n ? installed\n : Object.keys(agents) as AgentType[]\n\n // Agents that also read .claude/skills/\n const sharedAgents = new Set(\n Object.entries(agents)\n .filter(([, a]) => a.additionalSkillsDirs.some(d => d.includes('.claude/skills')))\n .map(([id]) => id),\n )\n\n // Group: agents that share skills vs agents with their own directory\n const sharedIds = candidateIds.filter(id => id === 'claude-code' || sharedAgents.has(id))\n const isolatedIds = candidateIds.filter(id => id !== 'claude-code' && !sharedAgents.has(id))\n\n const options: Array<{ label: string, value: AgentType | 'none', hint?: string }> = []\n\n // Show shared-compatible agents first\n if (sharedIds.length > 0 && isolatedIds.length > 0) {\n for (const id of sharedIds) {\n const a = agents[id]\n const hint = id === 'claude-code'\n ? `skills shared with ${sharedIds.length - 1} other agents`\n : `skills shared with Claude Code and others`\n options.push({ label: a.displayName, value: id as AgentType, hint })\n }\n }\n\n // Agents with isolated skill dirs\n const isolatedAgentIds = new Set(\n Object.entries(agents)\n .filter(([, a]) => a.additionalSkillsDirs.length === 0)\n .map(([id]) => id),\n )\n\n for (const id of (sharedIds.length > 0 && isolatedIds.length > 0 ? isolatedIds : candidateIds)) {\n if (options.some(o => o.value === id))\n continue\n const a = agents[id]\n const hint = sharedAgents.has(id) && id !== 'claude-code'\n ? 'skills shared with Claude Code and others'\n : isolatedAgentIds.has(id)\n ? 'skills only visible to this agent'\n : undefined\n options.push({ label: a.displayName, value: id as AgentType, hint })\n }\n\n options.push({ label: 'No agent', value: 'none', hint: 'export as standalone files for any AI' })\n\n if (!_warnedNoAgent) {\n _warnedNoAgent = true\n const hint = projectMatches.length > 1\n ? `Multiple agent directories found: ${projectMatches.map(t => agents[t].displayName).join(', ')}`\n : installed.length > 0\n ? `Found ${installed.map(t => agents[t].displayName).join(', ')} but couldn't determine which to use`\n : 'No agents auto-detected'\n const crossNote = sharedIds.length > 1\n ? `\\n \\x1B[90mTip: Picking Claude Code shares skills with ${sharedIds.filter(id => id !== 'claude-code').map(id => agents[id].displayName).join(', ')} automatically.\\x1B[0m`\n : ''\n p.log.warn(`${hint}\\n Pick the agent you actively code with.${crossNote}`)\n }\n\n const choice = await p.select({\n message: 'Which AI coding agent do you use?',\n options,\n })\n\n if (p.isCancel(choice))\n return null\n\n if (choice === 'none')\n return 'none'\n\n // Save as default so they don't get asked again\n updateConfig({ agent: choice })\n p.log.success(`Target agent set to ${agents[choice].displayName}`)\n return choice\n}\n\n/** Get installed LLM generators with working CLIs (verified via --version) */\nexport function getInstalledGenerators(): Array<{ name: string, version: string }> {\n const installed = detectInstalledAgents()\n return installed\n .filter(id => agents[id].cli)\n .map((id) => {\n const ver = getAgentVersion(id)\n return ver ? { name: agents[id].displayName, version: ver } : null\n })\n .filter((a): a is { name: string, version: string } => a !== null)\n}\n\nexport function relativeTime(date: Date): string {\n const now = Date.now()\n const diff = now - date.getTime()\n const mins = Math.floor(diff / 60000)\n const hours = Math.floor(diff / 3600000)\n const days = Math.floor(diff / 86400000)\n if (mins < 1)\n return 'just now'\n if (mins < 60)\n return `${mins}m ago`\n if (hours < 24)\n return `${hours}h ago`\n return `${days}d ago`\n}\n\nexport function getLastSynced(state: ProjectState): string | null {\n let latest: Date | null = null\n for (const skill of state.skills) {\n if (skill.info?.syncedAt) {\n const d = new Date(skill.info.syncedAt)\n if (!latest || d > latest)\n latest = d\n }\n }\n return latest ? relativeTime(latest) : null\n}\n\nexport function introLine({ state, generators, modelId, agentId }: IntroOptions): string {\n const name = '\\x1B[1m\\x1B[35mskilld\\x1B[0m'\n const ver = `\\x1B[90mv${version}\\x1B[0m`\n const lastSynced = getLastSynced(state)\n const synced = lastSynced ? ` · \\x1B[90msynced ${lastSynced}\\x1B[0m` : ''\n\n // Status line: enhancement model → target agent\n const parts: string[] = []\n if (modelId)\n parts.push(getModelName(modelId as any))\n else if (generators?.length)\n parts.push(generators.map(g => `${g.name} v${g.version}`).join(', '))\n if (agentId && agents[agentId as AgentType])\n parts.push(agents[agentId as AgentType].displayName)\n const statusLine = parts.length > 0\n ? `\\n\\x1B[90m↳ ${parts.join(' → ')}\\x1B[0m`\n : ''\n\n return `${name} ${ver}${synced}${statusLine}`\n}\n\nexport function formatStatus(synced: number, outdated: number): string {\n const parts: string[] = []\n if (synced > 0)\n parts.push(`\\x1B[32m${synced} synced\\x1B[0m`)\n if (outdated > 0)\n parts.push(`\\x1B[33m${outdated} outdated\\x1B[0m`)\n return `Skills: ${parts.join(' · ')}`\n}\n\n// ── Shared UI constants ───────────────────────────────────────────────\n\nexport const OAUTH_NOTE\n = '\\x1B[33m⚠\\x1B[0m OAuth providers are disabled.\\n'\n + '\\n'\n + 'Consumer subscription OAuth impersonates official CLI clients and\\n'\n + 'violates provider Terms of Service, risking account bans.\\n'\n + '\\n'\n + 'Use API keys or native CLI tools instead:\\n'\n + ' \\x1B[36mANTHROPIC_API_KEY\\x1B[0m / \\x1B[36mclaude\\x1B[0m CLI\\n'\n + ' \\x1B[36mOPENAI_API_KEY\\x1B[0m / \\x1B[36mcodex\\x1B[0m CLI\\n'\n + ' \\x1B[36mGEMINI_API_KEY\\x1B[0m / \\x1B[36mgemini\\x1B[0m CLI'\n\nexport const NO_MODELS_MESSAGE = 'No enhancement models detected.\\n'\n + ' \\x1B[90mSkills work fine without this, you get raw docs, issues, and types.\\n'\n + ' Enhancement compresses them into a concise cheat sheet with gotchas.\\x1B[0m\\n'\n + '\\n'\n + ' To connect a model (optional):\\n'\n + ' 1. Set an env var: ANTHROPIC_API_KEY, GEMINI_API_KEY, or OPENAI_API_KEY\\n'\n + ' 2. Install a CLI tool: \\x1B[36mclaude\\x1B[0m, \\x1B[36mgemini\\x1B[0m, or \\x1B[36mcodex\\x1B[0m (restart wizard after)'\n\n/** Group models by vendor for provider→model selection. Uses vendorGroup to merge CLI and API entries under one heading. */\nexport function groupModelsByProvider<T extends { provider: string, providerName: string, vendorGroup?: string }>(models: T[]): Map<string, { name: string, models: T[] }> {\n const byVendor = new Map<string, { name: string, models: T[] }>()\n for (const m of models) {\n const key = m.vendorGroup ?? m.provider\n if (!byVendor.has(key))\n byVendor.set(key, { name: key, models: [] })\n byVendor.get(key)!.models.push(m)\n }\n return byVendor\n}\n\nexport interface ModelPickerOptions {\n /** Extra options prepended (e.g. Auto, Connect OAuth) */\n before?: Array<{ label: string, value: string, hint?: string }>\n /** Extra options appended (e.g. Skip) */\n after?: Array<{ label: string, value: string, hint?: string }>\n}\n\n/**\n * Smart provider→model picker. Skips the provider step when there's only 1 provider.\n * Returns the selected model value, or a sentinel string from before/after options.\n */\nexport async function pickModel<T extends { provider: string, providerName: string, name: string, id: string, hint: string, recommended?: boolean }>(\n models: T[],\n opts: ModelPickerOptions = {},\n): Promise<string | null> {\n const byProvider = groupModelsByProvider(models)\n const before = opts.before ?? []\n const after = opts.after ?? []\n\n // Single provider → skip provider step, show models directly\n if (byProvider.size === 1 && before.length === 0) {\n const [, group] = [...byProvider.entries()][0]!\n const choice = await p.select({\n message: `${group.name}`,\n options: [\n ...group.models.map(m => ({\n label: m.recommended ? `${m.name} (recommended - fast and cheap)` : m.name,\n value: m.id,\n hint: m.hint,\n })),\n ...after,\n ],\n })\n return p.isCancel(choice) ? null : choice as string\n }\n\n // Multiple providers or has before options - two-step\n const providerChoice = await p.select({\n message: 'Select provider',\n options: [\n ...before,\n ...Array.from(byProvider.entries(), ([key, { name, models: ms }]) => ({\n label: name,\n value: key,\n hint: `${ms.length} models`,\n })),\n ...after,\n ],\n })\n\n if (p.isCancel(providerChoice))\n return null\n\n // Check if it's a sentinel from before/after\n const providerStr = providerChoice as string\n if (before.some(o => o.value === providerStr) || after.some(o => o.value === providerStr))\n return providerStr\n\n // Drill into provider's models\n const group = byProvider.get(providerStr)!\n const modelChoice = await p.select({\n message: `Select model (${group.name})`,\n options: group.models.map(m => ({\n label: m.recommended ? `${m.name} (recommended - fast and cheap)` : m.name,\n value: m.id,\n hint: m.hint,\n })),\n })\n\n return p.isCancel(modelChoice) ? null : modelChoice as string\n}\n\n/**\n * Check if the prepare hook is already installed in package.json.\n */\nexport function hasPrepareHook(cwd: string = process.cwd()): boolean {\n const pkg = readPackageJsonSafe(join(cwd, 'package.json'))\n if (!pkg)\n return true // no package.json means nothing to suggest\n const existing = (pkg.parsed.scripts as Record<string, unknown> | undefined)?.prepare\n return typeof existing === 'string' && existing.includes('skilld')\n}\n\n/**\n * Prompt to add `skilld prepare` to package.json \"prepare\" script.\n * In non-interactive environments, falls back to an info log.\n * Returns true if the hook was added or already present.\n */\nexport async function suggestPrepareHook(cwd: string = process.cwd()): Promise<boolean> {\n const pkgJsonPath = join(cwd, 'package.json')\n const pkg = readPackageJsonSafe(pkgJsonPath)\n if (!pkg)\n return false\n\n const rawExisting = (pkg.parsed.scripts as Record<string, unknown> | undefined)?.prepare\n const existing: string | undefined = typeof rawExisting === 'string' ? rawExisting : undefined\n\n if (existing?.includes('skilld'))\n return true\n\n const prepareCmd = buildPrepareScript(existing, cwd)\n\n if (!isInteractive()) {\n p.log.info(\n `\\x1B[90mAdd to package.json scripts:\\n`\n + ` \\x1B[36m\"prepare\": \"${prepareCmd}\"\\x1B[0m\\n`\n + ` \\x1B[90mRestores references and shipped skills on install.\\x1B[0m`,\n )\n return false\n }\n\n const confirmed = await p.confirm({\n message: `Add \\x1B[36m\"prepare\": \"${prepareCmd}\"\\x1B[0m to package.json?`,\n initialValue: true,\n })\n if (p.isCancel(confirmed) || !confirmed)\n return false\n\n patchPackageJson(pkgJsonPath, (content) => {\n const tree = parseTree(content)\n const hasScripts = tree?.children?.some(c =>\n c.type === 'property' && c.children?.[0]?.value === 'scripts',\n )\n\n let patched = content\n if (!hasScripts)\n patched = editJsonProperty(patched, ['scripts'], {})\n\n return editJsonProperty(patched, ['scripts', 'prepare'], prepareCmd)\n })\n p.log.success('Added \\x1B[36mskilld prepare\\x1B[0m to package.json')\n return true\n}\n\n/**\n * Build the full prepare script value, safely appending to any existing command.\n */\nexport function buildPrepareScript(existing: string | undefined, cwd: string = process.cwd()): string {\n const bin = isNpxExecution() && !isSkilldDep(cwd) ? 'npx skilld' : 'skilld'\n const cmd = `${bin} prepare || true`\n if (!existing || !existing.trim())\n return cmd\n\n const trimmed = existing.trim()\n\n // Strip trailing && or ; that would leave a dangling operator\n const cleaned = trimmed.replace(/[&|;]+\\s*$/, '').trim()\n if (!cleaned)\n return cmd\n\n return `${cleaned} && (${cmd})`\n}\n\n/**\n * Detect if the current process was launched via npx, pnpm dlx, or similar one-shot runners.\n */\nfunction isNpxExecution(): boolean {\n // npm/pnpm set npm_command=exec when running via npx/dlx\n if (process.env.npm_command === 'exec')\n return true\n // Fallback: check if the resolved binary path contains npx or dlx cache dirs\n const execPath = process.env._ || ''\n return /npx|\\.store|dlx/.test(execPath)\n}\n\n/**\n * Check if skilld is listed as a dependency (dev or regular) in the project's package.json.\n */\nfunction isSkilldDep(cwd: string): boolean {\n const pkg = readPackageJsonSafe(join(cwd, 'package.json'))\n if (!pkg)\n return false\n const deps = pkg.parsed as Record<string, any>\n return !!(deps.dependencies?.skilld || deps.devDependencies?.skilld)\n}\n\nexport function getRepoHint(name: string, cwd: string): string | undefined {\n const result = readPackageJsonSafe(join(cwd, 'node_modules', name, 'package.json'))\n if (!result)\n return undefined\n const pkg = result.parsed as Record<string, any>\n const url = typeof pkg.repository === 'string'\n ? pkg.repository\n : pkg.repository?.url\n if (!url)\n return undefined\n return url\n .replace(/^git\\+/, '')\n .replace(/\\.git$/, '')\n .replace(/^git:\\/\\//, 'https://')\n .replace(/^ssh:\\/\\/git@github\\.com/, 'https://github.com')\n .replace(/^https?:\\/\\/(www\\.)?github\\.com\\//, '')\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"cli-helpers.mjs","names":["dirname","resolve","agents"],"sources":["../../src/core/config.ts","../../src/version.ts","../../src/cli-helpers.ts"],"sourcesContent":["import type { OptimizeModel } from '../agent/index.ts'\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs'\nimport { homedir } from 'node:os'\nimport { join } from 'pathe'\nimport { yamlEscape, yamlParseKV, yamlUnescape } from './yaml.ts'\n\nexport interface FeaturesConfig {\n search: boolean\n issues: boolean\n discussions: boolean\n releases: boolean\n}\n\nexport const defaultFeatures: FeaturesConfig = {\n search: true,\n issues: true,\n discussions: true,\n releases: true,\n}\n\nexport interface SkilldConfig {\n model?: OptimizeModel\n agent?: string\n features?: FeaturesConfig\n projects?: string[]\n skipLlm?: boolean\n}\n\nconst CONFIG_DIR = join(homedir(), '.skilld')\nconst CONFIG_PATH = join(CONFIG_DIR, 'config.yaml')\n\nlet configCache: SkilldConfig | undefined\n\nexport function hasConfig(): boolean {\n return existsSync(CONFIG_PATH)\n}\n\n/** Whether the first-run wizard has been completed (not just agent selection) */\nexport function hasCompletedWizard(): boolean {\n if (!existsSync(CONFIG_PATH))\n return false\n const config = readConfig()\n return config.features !== undefined || config.model !== undefined || config.skipLlm !== undefined\n}\n\nexport function readConfig(): SkilldConfig {\n if (configCache) {\n return {\n ...configCache,\n features: configCache.features ? { ...configCache.features } : undefined,\n projects: configCache.projects ? [...configCache.projects] : undefined,\n }\n }\n if (!existsSync(CONFIG_PATH))\n return {}\n\n const content = readFileSync(CONFIG_PATH, 'utf-8')\n const config: SkilldConfig = {}\n let inBlock: 'projects' | 'features' | null = null\n const projects: string[] = []\n const features: Partial<FeaturesConfig> = {}\n\n for (const line of content.split('\\n')) {\n if (line.startsWith('projects:')) {\n inBlock = 'projects'\n continue\n }\n if (line.startsWith('features:')) {\n inBlock = 'features'\n continue\n }\n if (inBlock === 'projects') {\n if (line.startsWith(' - ')) {\n projects.push(yamlUnescape(line.slice(4)))\n continue\n }\n inBlock = null\n }\n if (inBlock === 'features') {\n const m = line.match(/^ {2}(\\w+):\\s*(.+)/)\n if (m) {\n const key = m[1] as keyof FeaturesConfig\n if (key in defaultFeatures)\n features[key] = m[2] === 'true'\n continue\n }\n inBlock = null\n }\n const kv = yamlParseKV(line)\n if (!kv)\n continue\n const [key, value] = kv\n if (key === 'model' && value)\n config.model = value as OptimizeModel\n if (key === 'agent' && value)\n config.agent = value\n if (key === 'skipLlm')\n config.skipLlm = value === 'true'\n }\n\n if (projects.length > 0)\n config.projects = projects\n if (Object.keys(features).length > 0)\n config.features = { ...defaultFeatures, ...features }\n configCache = config\n return config\n}\n\nexport function writeConfig(config: SkilldConfig): void {\n mkdirSync(CONFIG_DIR, { recursive: true, mode: 0o700 })\n\n let yaml = ''\n if (config.model)\n yaml += `model: ${config.model}\\n`\n if (config.agent)\n yaml += `agent: ${config.agent}\\n`\n if (config.skipLlm)\n yaml += `skipLlm: true\\n`\n if (config.features) {\n yaml += 'features:\\n'\n for (const [k, v] of Object.entries(config.features)) {\n yaml += ` ${k}: ${v}\\n`\n }\n }\n if (config.projects?.length) {\n yaml += 'projects:\\n'\n for (const p of config.projects) {\n yaml += ` - ${yamlEscape(p)}\\n`\n }\n }\n\n writeFileSync(CONFIG_PATH, yaml, { mode: 0o600 })\n configCache = undefined\n}\n\nexport function updateConfig(updates: Partial<SkilldConfig>): void {\n const config = readConfig()\n writeConfig({ ...config, ...updates })\n}\n\nexport function registerProject(projectPath: string): void {\n const config = readConfig()\n const projects = new Set(config.projects || [])\n projects.add(projectPath)\n writeConfig({ ...config, projects: [...projects] })\n}\n\nexport function unregisterProject(projectPath: string): void {\n const config = readConfig()\n const projects = (config.projects || []).filter(p => p !== projectPath)\n writeConfig({ ...config, projects })\n}\n\nexport function getRegisteredProjects(): string[] {\n return readConfig().projects || []\n}\n","import { readFileSync } from 'node:fs'\nimport { dirname, resolve } from 'node:path'\nimport { fileURLToPath } from 'node:url'\n\n// Walk up from current file to find package.json (works in both src/ and dist/_chunks/)\nfunction findPackageJson(): string {\n let dir = dirname(fileURLToPath(import.meta.url))\n for (let i = 0; i < 5; i++) {\n const candidate = resolve(dir, 'package.json')\n try {\n const content = readFileSync(candidate, 'utf8')\n if (content)\n return content\n }\n catch {}\n dir = resolve(dir, '..')\n }\n return '{\"version\":\"0.0.0\"}'\n}\n\nexport const version: string = JSON.parse(findPackageJson()).version\n","/**\n * Shared CLI helpers used by subcommand definitions and the main CLI entry.\n * Extracted to avoid circular deps between cli.ts and commands/*.ts.\n */\n\nimport type { AgentType, OptimizeModel } from './agent/index.ts'\nimport type { ProjectState } from './core/skills.ts'\nimport * as p from '@clack/prompts'\nimport { parseTree } from 'jsonc-parser'\nimport { join } from 'pathe'\nimport { detectCurrentAgent } from 'unagent/env'\nimport { agents, detectInstalledAgents, detectProjectAgents, detectTargetAgent, getAgentVersion, getModelName } from './agent/index.ts'\nimport { readConfig, updateConfig } from './core/config.ts'\nimport { editJsonProperty, patchPackageJson, readPackageJsonSafe } from './core/package-json.ts'\nimport { version } from './version.ts'\n\nexport type { AgentType, OptimizeModel }\n\nexport interface IntroOptions {\n state: ProjectState\n /** Installed CLIs that can serve as enhancement models */\n generators?: Array<{ name: string, version: string }>\n /** Configured enhancement model ID */\n modelId?: string\n /** Resolved target agent ID */\n agentId?: string\n}\n\nexport const sharedArgs = {\n global: {\n type: 'boolean' as const,\n alias: 'g',\n description: 'Install globally to ~/<agent>/skills',\n default: false,\n },\n agent: {\n type: 'enum' as const,\n options: Object.keys(agents),\n alias: 'a',\n description: 'Target agent — where skills are installed',\n },\n model: {\n type: 'string' as const,\n alias: 'm',\n description: 'Enhancement model for SKILL.md generation',\n valueHint: 'id',\n },\n yes: {\n type: 'boolean' as const,\n alias: 'y',\n description: 'Skip prompts, use defaults',\n default: false,\n },\n force: {\n type: 'boolean' as const,\n alias: 'f',\n description: 'Ignore all caches, re-fetch docs and regenerate',\n default: false,\n },\n debug: {\n type: 'boolean' as const,\n description: 'Save raw enhancement output to logs/ for each section',\n default: false,\n },\n}\n\n// ── Menu loop utility ─────────────────────────────────────────────────\n\n/** Thrown when a clack prompt is cancelled inside a menuLoop handler */\nexport class MenuCancel extends Error { override name = 'MenuCancel' }\n\n/** Assert a clack prompt result is not cancelled. Throws MenuCancel if cancelled. */\nexport function guard<T>(value: T | symbol): T {\n if (p.isCancel(value))\n throw new MenuCancel()\n return value as T\n}\n\nexport interface MenuOption {\n label: string\n value: string\n hint?: string\n}\n\n/**\n * Run a select menu in a loop with automatic back-navigation.\n *\n * - Cancel (Escape) at the menu itself → exits (returns)\n * - Cancel inside a handler (via guard()) → caught, loops back to menu\n * - Handler returns truthy → exits (returns)\n * - Handler returns void/false → loops back to menu\n *\n * Options are rebuilt each iteration so hints stay fresh after changes.\n */\nexport async function menuLoop(opts: {\n message: string\n options: () => MenuOption[] | Promise<MenuOption[]>\n onSelect: (value: string) => Promise<boolean | void>\n initialValue?: string | (() => string | undefined)\n /** Use fuzzy-searchable autocomplete instead of static select */\n searchable?: boolean\n}): Promise<void> {\n while (true) {\n const options = await opts.options()\n const initial = typeof opts.initialValue === 'function' ? opts.initialValue() : opts.initialValue\n const choice = opts.searchable\n ? await p.autocomplete({ message: opts.message, options, ...(initial != null ? { initialValue: initial } : {}) })\n : await p.select({ message: opts.message, options, ...(initial != null ? { initialValue: initial } : {}) })\n if (p.isCancel(choice))\n return\n try {\n if (await opts.onSelect(choice as string))\n return\n }\n catch (err) {\n if (err instanceof MenuCancel)\n continue\n throw err\n }\n }\n}\n\n/** Check if we're running inside an AI coding agent */\nexport function isRunningInsideAgent(): boolean {\n return !!detectCurrentAgent()\n}\n\n/** Check if the current environment supports interactive prompts */\nexport function isInteractive(): boolean {\n if (isRunningInsideAgent())\n return false\n if (process.env.CI)\n return false\n if (!process.stdout.isTTY)\n return false\n return true\n}\n\n/** Exit with error if interactive terminal is required but unavailable */\nexport function requireInteractive(command: string): void {\n if (!isInteractive()) {\n console.error(`Error: \\`skilld ${command}\\` requires an interactive terminal`)\n process.exit(1)\n }\n}\n\n/** Resolve agent from flags/cwd/config. cwd is source of truth over config. */\nexport function resolveAgent(agentFlag?: string): AgentType | 'none' | null {\n if (process.env.SKILLD_NO_AGENT)\n return null\n return (agentFlag as AgentType | undefined)\n ?? detectTargetAgent()\n ?? (readConfig().agent as AgentType | undefined)\n ?? null\n}\n\nlet _warnedNoAgent = false\nfunction warnNoAgent(): void {\n if (_warnedNoAgent)\n return\n _warnedNoAgent = true\n p.log.warn('No target agent detected — falling back to prompt-only mode.\\n Use --agent <name> to specify, or run `skilld config` to set a default.')\n}\n\n/** Prompt user to pick an agent when auto-detection fails */\nexport async function promptForAgent(): Promise<AgentType | 'none' | null> {\n const noAgent = !!process.env.SKILLD_NO_AGENT\n const installed = noAgent ? [] : detectInstalledAgents()\n const projectMatches = noAgent ? [] : detectProjectAgents()\n\n // Non-interactive: auto-select sole installed agent or fall back to prompt-only\n if (!isInteractive()) {\n if (installed.length === 1) {\n updateConfig({ agent: installed[0] })\n return installed[0]!\n }\n warnNoAgent()\n return 'none'\n }\n\n // Brief context before asking about agents\n p.log.info(\n `Skilld generates reference cards from package docs so your AI agent\\n`\n + ` always has accurate APIs for your exact dependency versions.`,\n )\n\n // Build options: prefer project-matched agents, then installed, then all\n const candidateIds = projectMatches.length > 0\n ? projectMatches\n : installed.length > 0\n ? installed\n : Object.keys(agents) as AgentType[]\n\n // Agents that also read .claude/skills/\n const sharedAgents = new Set(\n Object.entries(agents)\n .filter(([, a]) => a.additionalSkillsDirs.some(d => d.includes('.claude/skills')))\n .map(([id]) => id),\n )\n\n // Group: agents that share skills vs agents with their own directory\n const sharedIds = candidateIds.filter(id => id === 'claude-code' || sharedAgents.has(id))\n const isolatedIds = candidateIds.filter(id => id !== 'claude-code' && !sharedAgents.has(id))\n\n const options: Array<{ label: string, value: AgentType | 'none', hint?: string }> = []\n\n // Show shared-compatible agents first\n if (sharedIds.length > 0 && isolatedIds.length > 0) {\n for (const id of sharedIds) {\n const a = agents[id]\n const hint = id === 'claude-code'\n ? `skills shared with ${sharedIds.length - 1} other agents`\n : `skills shared with Claude Code and others`\n options.push({ label: a.displayName, value: id as AgentType, hint })\n }\n }\n\n // Agents with isolated skill dirs\n const isolatedAgentIds = new Set(\n Object.entries(agents)\n .filter(([, a]) => a.additionalSkillsDirs.length === 0)\n .map(([id]) => id),\n )\n\n for (const id of (sharedIds.length > 0 && isolatedIds.length > 0 ? isolatedIds : candidateIds)) {\n if (options.some(o => o.value === id))\n continue\n const a = agents[id]\n const hint = sharedAgents.has(id) && id !== 'claude-code'\n ? 'skills shared with Claude Code and others'\n : isolatedAgentIds.has(id)\n ? 'skills only visible to this agent'\n : undefined\n options.push({ label: a.displayName, value: id as AgentType, hint })\n }\n\n options.push({ label: 'No agent', value: 'none', hint: 'export as standalone files for any AI' })\n\n if (!_warnedNoAgent) {\n _warnedNoAgent = true\n const hint = projectMatches.length > 1\n ? `Multiple agent directories found: ${projectMatches.map(t => agents[t].displayName).join(', ')}`\n : installed.length > 0\n ? `Found ${installed.map(t => agents[t].displayName).join(', ')} but couldn't determine which to use`\n : 'No agents auto-detected'\n const crossNote = sharedIds.length > 1\n ? `\\n \\x1B[90mTip: Picking Claude Code shares skills with ${sharedIds.filter(id => id !== 'claude-code').map(id => agents[id].displayName).join(', ')} automatically.\\x1B[0m`\n : ''\n p.log.warn(`${hint}\\n Pick the agent you actively code with.${crossNote}`)\n }\n\n const choice = await p.select({\n message: 'Which AI coding agent do you use?',\n options,\n })\n\n if (p.isCancel(choice))\n return null\n\n if (choice === 'none')\n return 'none'\n\n // Save as default so they don't get asked again\n updateConfig({ agent: choice })\n p.log.success(`Target agent set to ${agents[choice].displayName}`)\n return choice\n}\n\n/** Get installed LLM generators with working CLIs (verified via --version) */\nexport function getInstalledGenerators(): Array<{ name: string, version: string }> {\n const installed = detectInstalledAgents()\n return installed\n .filter(id => agents[id].cli)\n .map((id) => {\n const ver = getAgentVersion(id)\n return ver ? { name: agents[id].displayName, version: ver } : null\n })\n .filter((a): a is { name: string, version: string } => a !== null)\n}\n\nexport function relativeTime(date: Date): string {\n const now = Date.now()\n const diff = now - date.getTime()\n const mins = Math.floor(diff / 60000)\n const hours = Math.floor(diff / 3600000)\n const days = Math.floor(diff / 86400000)\n if (mins < 1)\n return 'just now'\n if (mins < 60)\n return `${mins}m ago`\n if (hours < 24)\n return `${hours}h ago`\n return `${days}d ago`\n}\n\nexport function getLastSynced(state: ProjectState): string | null {\n let latest: Date | null = null\n for (const skill of state.skills) {\n if (skill.info?.syncedAt) {\n const d = new Date(skill.info.syncedAt)\n if (!latest || d > latest)\n latest = d\n }\n }\n return latest ? relativeTime(latest) : null\n}\n\nexport function introLine({ state, generators, modelId, agentId }: IntroOptions): string {\n const name = '\\x1B[1m\\x1B[35mskilld\\x1B[0m'\n const ver = `\\x1B[90mv${version}\\x1B[0m`\n const lastSynced = getLastSynced(state)\n const synced = lastSynced ? ` · \\x1B[90msynced ${lastSynced}\\x1B[0m` : ''\n\n // Status line: enhancement model → target agent\n const parts: string[] = []\n if (modelId)\n parts.push(getModelName(modelId as any))\n else if (generators?.length)\n parts.push(generators.map(g => `${g.name} v${g.version}`).join(', '))\n if (agentId && agents[agentId as AgentType])\n parts.push(agents[agentId as AgentType].displayName)\n const statusLine = parts.length > 0\n ? `\\n\\x1B[90m↳ ${parts.join(' → ')}\\x1B[0m`\n : ''\n\n return `${name} ${ver}${synced}${statusLine}`\n}\n\nexport function formatStatus(synced: number, outdated: number): string {\n const parts: string[] = []\n if (synced > 0)\n parts.push(`\\x1B[32m${synced} synced\\x1B[0m`)\n if (outdated > 0)\n parts.push(`\\x1B[33m${outdated} outdated\\x1B[0m`)\n return `Skills: ${parts.join(' · ')}`\n}\n\n// ── Shared UI constants ───────────────────────────────────────────────\n\nexport const OAUTH_NOTE\n = '\\x1B[33m⚠\\x1B[0m OAuth providers are disabled.\\n'\n + '\\n'\n + 'Consumer subscription OAuth impersonates official CLI clients and\\n'\n + 'violates provider Terms of Service, risking account bans.\\n'\n + '\\n'\n + 'Use API keys or native CLI tools instead:\\n'\n + ' \\x1B[36mANTHROPIC_API_KEY\\x1B[0m / \\x1B[36mclaude\\x1B[0m CLI\\n'\n + ' \\x1B[36mOPENAI_API_KEY\\x1B[0m / \\x1B[36mcodex\\x1B[0m CLI\\n'\n + ' \\x1B[36mGEMINI_API_KEY\\x1B[0m / \\x1B[36mgemini\\x1B[0m CLI'\n\nexport const NO_MODELS_MESSAGE = 'No enhancement models detected.\\n'\n + ' \\x1B[90mSkills work fine without this, you get raw docs, issues, and types.\\n'\n + ' Enhancement compresses them into a concise cheat sheet with gotchas.\\x1B[0m\\n'\n + '\\n'\n + ' To connect a model (optional):\\n'\n + ' 1. Set an env var: ANTHROPIC_API_KEY, GEMINI_API_KEY, or OPENAI_API_KEY\\n'\n + ' 2. Install a CLI tool: \\x1B[36mclaude\\x1B[0m, \\x1B[36mgemini\\x1B[0m, or \\x1B[36mcodex\\x1B[0m (restart wizard after)'\n\n/** Group models by vendor for provider→model selection. Uses vendorGroup to merge CLI and API entries under one heading. */\nexport function groupModelsByProvider<T extends { provider: string, providerName: string, vendorGroup?: string }>(models: T[]): Map<string, { name: string, models: T[] }> {\n const byVendor = new Map<string, { name: string, models: T[] }>()\n for (const m of models) {\n const key = m.vendorGroup ?? m.provider\n if (!byVendor.has(key))\n byVendor.set(key, { name: key, models: [] })\n byVendor.get(key)!.models.push(m)\n }\n return byVendor\n}\n\nexport interface ModelPickerOptions {\n /** Extra options prepended (e.g. Auto, Connect OAuth) */\n before?: Array<{ label: string, value: string, hint?: string }>\n /** Extra options appended (e.g. Skip) */\n after?: Array<{ label: string, value: string, hint?: string }>\n}\n\n/**\n * Smart provider→model picker. Skips the provider step when there's only 1 provider.\n * Returns the selected model value, or a sentinel string from before/after options.\n */\nexport async function pickModel<T extends { provider: string, providerName: string, name: string, id: string, hint: string, recommended?: boolean }>(\n models: T[],\n opts: ModelPickerOptions = {},\n): Promise<string | null> {\n const byProvider = groupModelsByProvider(models)\n const before = opts.before ?? []\n const after = opts.after ?? []\n\n // Single provider → skip provider step, show models directly\n if (byProvider.size === 1 && before.length === 0) {\n const [, group] = [...byProvider.entries()][0]!\n const choice = await p.select({\n message: `${group.name}`,\n options: [\n ...group.models.map(m => ({\n label: m.recommended ? `${m.name} (recommended - fast and cheap)` : m.name,\n value: m.id,\n hint: m.hint,\n })),\n ...after,\n ],\n })\n return p.isCancel(choice) ? null : choice as string\n }\n\n // Multiple providers or has before options - two-step\n const providerChoice = await p.select({\n message: 'Select provider',\n options: [\n ...before,\n ...Array.from(byProvider.entries(), ([key, { name, models: ms }]) => ({\n label: name,\n value: key,\n hint: `${ms.length} models`,\n })),\n ...after,\n ],\n })\n\n if (p.isCancel(providerChoice))\n return null\n\n // Check if it's a sentinel from before/after\n const providerStr = providerChoice as string\n if (before.some(o => o.value === providerStr) || after.some(o => o.value === providerStr))\n return providerStr\n\n // Drill into provider's models\n const group = byProvider.get(providerStr)!\n const modelChoice = await p.select({\n message: `Select model (${group.name})`,\n options: group.models.map(m => ({\n label: m.recommended ? `${m.name} (recommended - fast and cheap)` : m.name,\n value: m.id,\n hint: m.hint,\n })),\n })\n\n return p.isCancel(modelChoice) ? null : modelChoice as string\n}\n\n/**\n * Check if the prepare hook is already installed in package.json.\n */\nexport function hasPrepareHook(cwd: string = process.cwd()): boolean {\n const pkg = readPackageJsonSafe(join(cwd, 'package.json'))\n if (!pkg)\n return true // no package.json means nothing to suggest\n const existing = (pkg.parsed.scripts as Record<string, unknown> | undefined)?.prepare\n return typeof existing === 'string' && existing.includes('skilld')\n}\n\n/**\n * Prompt to add `skilld prepare` to package.json \"prepare\" script.\n * In non-interactive environments, falls back to an info log.\n * Returns true if the hook was added or already present.\n */\nexport async function suggestPrepareHook(cwd: string = process.cwd()): Promise<boolean> {\n const pkgJsonPath = join(cwd, 'package.json')\n const pkg = readPackageJsonSafe(pkgJsonPath)\n if (!pkg)\n return false\n\n const rawExisting = (pkg.parsed.scripts as Record<string, unknown> | undefined)?.prepare\n const existing: string | undefined = typeof rawExisting === 'string' ? rawExisting : undefined\n\n if (existing?.includes('skilld'))\n return true\n\n const prepareCmd = buildPrepareScript(existing, cwd)\n\n if (!isInteractive()) {\n p.log.info(\n `\\x1B[90mAdd to package.json scripts:\\n`\n + ` \\x1B[36m\"prepare\": \"${prepareCmd}\"\\x1B[0m\\n`\n + ` \\x1B[90mRestores references and shipped skills on install.\\x1B[0m`,\n )\n return false\n }\n\n const confirmed = await p.confirm({\n message: `Add \\x1B[36m\"prepare\": \"${prepareCmd}\"\\x1B[0m to package.json?`,\n initialValue: true,\n })\n if (p.isCancel(confirmed) || !confirmed)\n return false\n\n patchPackageJson(pkgJsonPath, (content) => {\n const tree = parseTree(content)\n const hasScripts = tree?.children?.some(c =>\n c.type === 'property' && c.children?.[0]?.value === 'scripts',\n )\n\n let patched = content\n if (!hasScripts)\n patched = editJsonProperty(patched, ['scripts'], {})\n\n return editJsonProperty(patched, ['scripts', 'prepare'], prepareCmd)\n })\n p.log.success('Added \\x1B[36mskilld prepare\\x1B[0m to package.json')\n return true\n}\n\n/**\n * Build the full prepare script value, safely appending to any existing command.\n */\nexport function buildPrepareScript(existing: string | undefined, cwd: string = process.cwd()): string {\n const bin = isNpxExecution() && !isSkilldDep(cwd) ? 'npx skilld' : 'skilld'\n const cmd = `${bin} prepare || true`\n if (!existing || !existing.trim())\n return cmd\n\n const trimmed = existing.trim()\n\n // Strip trailing && or ; that would leave a dangling operator\n const cleaned = trimmed.replace(/[&|;]+\\s*$/, '').trim()\n if (!cleaned)\n return cmd\n\n return `${cleaned} && (${cmd})`\n}\n\n/**\n * Detect if the current process was launched via npx, pnpm dlx, or similar one-shot runners.\n */\nfunction isNpxExecution(): boolean {\n // npm/pnpm set npm_command=exec when running via npx/dlx\n if (process.env.npm_command === 'exec')\n return true\n // Fallback: check if the resolved binary path contains npx or dlx cache dirs\n const execPath = process.env._ || ''\n return /npx|\\.store|dlx/.test(execPath)\n}\n\n/**\n * Check if skilld is listed as a dependency (dev or regular) in the project's package.json.\n */\nfunction isSkilldDep(cwd: string): boolean {\n const pkg = readPackageJsonSafe(join(cwd, 'package.json'))\n if (!pkg)\n return false\n const deps = pkg.parsed as Record<string, any>\n return !!(deps.dependencies?.skilld || deps.devDependencies?.skilld)\n}\n\nexport function getRepoHint(name: string, cwd: string): string | undefined {\n const result = readPackageJsonSafe(join(cwd, 'node_modules', name, 'package.json'))\n if (!result)\n return undefined\n const pkg = result.parsed as Record<string, any>\n const url = typeof pkg.repository === 'string'\n ? pkg.repository\n : pkg.repository?.url\n if (!url)\n return undefined\n return url\n .replace(/^git\\+/, '')\n .replace(/\\.git$/, '')\n .replace(/^git:\\/\\//, 'https://')\n .replace(/^ssh:\\/\\/git@github\\.com/, 'https://github.com')\n .replace(/^https?:\\/\\/(www\\.)?github\\.com\\//, '')\n}\n"],"mappings":"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"}
@@ -1,13 +1,2 @@
1
- import "./config.mjs";
2
- import "./package-json.mjs";
3
- import "./prepare.mjs";
4
- import "./sanitize.mjs";
5
- import "./cache.mjs";
6
- import "./yaml.mjs";
7
- import "./shared.mjs";
8
- import "./detect.mjs";
9
- import "./prompts.mjs";
10
- import "./agent.mjs";
11
- import "./libs/@sinclair/typebox.mjs";
12
1
  import { S as suggestPrepareHook } from "./cli-helpers.mjs";
13
2
  export { suggestPrepareHook };
@@ -1,29 +1,13 @@
1
1
  import { homedir } from "node:os";
2
2
  import { join, resolve } from "pathe";
3
- //#region src/cache/version.ts
4
- /**
5
- * Version utilities
6
- */
7
- /** Validate npm package name (scoped or unscoped) */
8
3
  const VALID_PKG_NAME = /^(?:@[a-z0-9][-a-z0-9._]*\/)?[a-z0-9][-a-z0-9._]*$/;
9
- /** Validate version string (semver-ish, no path separators) */
10
4
  const VALID_VERSION = /^[a-z0-9][-\w.+]*$/i;
11
- /**
12
- * Get exact version key for cache keying
13
- */
14
5
  function getVersionKey(version) {
15
6
  return version;
16
7
  }
17
- /**
18
- * Get cache key for a package: name@version
19
- */
20
8
  function getCacheKey(name, version) {
21
9
  return `${name}@${getVersionKey(version)}`;
22
10
  }
23
- /**
24
- * Get path to cached package references.
25
- * Validates name/version to prevent path traversal.
26
- */
27
11
  function getCacheDir(name, version) {
28
12
  if (!VALID_PKG_NAME.test(name)) throw new Error(`Invalid package name: ${name}`);
29
13
  if (!VALID_VERSION.test(version)) throw new Error(`Invalid version: ${version}`);
@@ -31,27 +15,16 @@ function getCacheDir(name, version) {
31
15
  if (!dir.startsWith(REFERENCES_DIR)) throw new Error(`Path traversal detected: ${dir}`);
32
16
  return dir;
33
17
  }
34
- //#endregion
35
- //#region src/cache/config.ts
36
- /**
37
- * Cache configuration
38
- */
39
- /** Global cache directory */
40
18
  const CACHE_DIR = join(homedir(), ".skilld");
41
- /** References subdirectory */
42
19
  const REFERENCES_DIR = join(CACHE_DIR, "references");
43
- /** Repo-level cache (issues, discussions, releases shared across monorepo packages) */
44
20
  const REPOS_DIR = join(CACHE_DIR, "repos");
45
- /** Get repo cache dir for owner/repo with path traversal validation */
46
21
  function getRepoCacheDir(owner, repo) {
47
22
  if (owner.includes("..") || repo.includes("..") || owner.includes("/") || repo.includes("/")) throw new Error(`Invalid repo path: ${owner}/${repo}`);
48
23
  return join(REPOS_DIR, owner, repo);
49
24
  }
50
- /** Get search DB path for a specific package@version */
51
25
  function getPackageDbPath(name, version) {
52
26
  return join(REFERENCES_DIR, getCacheKey(name, version), "search.db");
53
27
  }
54
- //#endregion
55
28
  export { getRepoCacheDir as a, getVersionKey as c, getPackageDbPath as i, REFERENCES_DIR as n, getCacheDir as o, REPOS_DIR as r, getCacheKey as s, CACHE_DIR as t };
56
29
 
57
30
  //# sourceMappingURL=config.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"config.mjs","names":[],"sources":["../../src/cache/version.ts","../../src/cache/config.ts"],"sourcesContent":["/**\n * Version utilities\n */\n\nimport { resolve } from 'pathe'\nimport { REFERENCES_DIR } from './config.ts'\n\n/** Validate npm package name (scoped or unscoped) */\nconst VALID_PKG_NAME = /^(?:@[a-z0-9][-a-z0-9._]*\\/)?[a-z0-9][-a-z0-9._]*$/\n\n/** Validate version string (semver-ish, no path separators) */\nconst VALID_VERSION = /^[a-z0-9][-\\w.+]*$/i\n\n/**\n * Get exact version key for cache keying\n */\nexport function getVersionKey(version: string): string {\n return version\n}\n\n/**\n * Get cache key for a package: name@version\n */\nexport function getCacheKey(name: string, version: string): string {\n return `${name}@${getVersionKey(version)}`\n}\n\n/**\n * Get path to cached package references.\n * Validates name/version to prevent path traversal.\n */\nexport function getCacheDir(name: string, version: string): string {\n if (!VALID_PKG_NAME.test(name))\n throw new Error(`Invalid package name: ${name}`)\n if (!VALID_VERSION.test(version))\n throw new Error(`Invalid version: ${version}`)\n\n const dir = resolve(REFERENCES_DIR, getCacheKey(name, version))\n if (!dir.startsWith(REFERENCES_DIR))\n throw new Error(`Path traversal detected: ${dir}`)\n return dir\n}\n","/**\n * Cache configuration\n */\n\nimport { homedir } from 'node:os'\nimport { join } from 'pathe'\nimport { getCacheKey } from './version.ts'\n\n/** Global cache directory */\nexport const CACHE_DIR = join(homedir(), '.skilld')\n\n/** References subdirectory */\nexport const REFERENCES_DIR = join(CACHE_DIR, 'references')\n\n/** Repo-level cache (issues, discussions, releases shared across monorepo packages) */\nexport const REPOS_DIR = join(CACHE_DIR, 'repos')\n\n/** Get repo cache dir for owner/repo with path traversal validation */\nexport function getRepoCacheDir(owner: string, repo: string): string {\n if (owner.includes('..') || repo.includes('..') || owner.includes('/') || repo.includes('/'))\n throw new Error(`Invalid repo path: ${owner}/${repo}`)\n return join(REPOS_DIR, owner, repo)\n}\n\n/** Get search DB path for a specific package@version */\nexport function getPackageDbPath(name: string, version: string): string {\n return join(REFERENCES_DIR, getCacheKey(name, version), 'search.db')\n}\n"],"mappings":";;;;;;;AAQA,MAAM,iBAAiB;;AAGvB,MAAM,gBAAgB;;;;AAKtB,SAAgB,cAAc,SAAyB;AACrD,QAAO;;;;;AAMT,SAAgB,YAAY,MAAc,SAAyB;AACjE,QAAO,GAAG,KAAK,GAAG,cAAc,QAAQ;;;;;;AAO1C,SAAgB,YAAY,MAAc,SAAyB;AACjE,KAAI,CAAC,eAAe,KAAK,KAAK,CAC5B,OAAM,IAAI,MAAM,yBAAyB,OAAO;AAClD,KAAI,CAAC,cAAc,KAAK,QAAQ,CAC9B,OAAM,IAAI,MAAM,oBAAoB,UAAU;CAEhD,MAAM,MAAM,QAAQ,gBAAgB,YAAY,MAAM,QAAQ,CAAC;AAC/D,KAAI,CAAC,IAAI,WAAW,eAAe,CACjC,OAAM,IAAI,MAAM,4BAA4B,MAAM;AACpD,QAAO;;;;;;;;AC/BT,MAAa,YAAY,KAAK,SAAS,EAAE,UAAU;;AAGnD,MAAa,iBAAiB,KAAK,WAAW,aAAa;;AAG3D,MAAa,YAAY,KAAK,WAAW,QAAQ;;AAGjD,SAAgB,gBAAgB,OAAe,MAAsB;AACnE,KAAI,MAAM,SAAS,KAAK,IAAI,KAAK,SAAS,KAAK,IAAI,MAAM,SAAS,IAAI,IAAI,KAAK,SAAS,IAAI,CAC1F,OAAM,IAAI,MAAM,sBAAsB,MAAM,GAAG,OAAO;AACxD,QAAO,KAAK,WAAW,OAAO,KAAK;;;AAIrC,SAAgB,iBAAiB,MAAc,SAAyB;AACtE,QAAO,KAAK,gBAAgB,YAAY,MAAM,QAAQ,EAAE,YAAY"}
1
+ {"version":3,"file":"config.mjs","names":[],"sources":["../../src/cache/version.ts","../../src/cache/config.ts"],"sourcesContent":["/**\n * Version utilities\n */\n\nimport { resolve } from 'pathe'\nimport { REFERENCES_DIR } from './config.ts'\n\n/** Validate npm package name (scoped or unscoped) */\nconst VALID_PKG_NAME = /^(?:@[a-z0-9][-a-z0-9._]*\\/)?[a-z0-9][-a-z0-9._]*$/\n\n/** Validate version string (semver-ish, no path separators) */\nconst VALID_VERSION = /^[a-z0-9][-\\w.+]*$/i\n\n/**\n * Get exact version key for cache keying\n */\nexport function getVersionKey(version: string): string {\n return version\n}\n\n/**\n * Get cache key for a package: name@version\n */\nexport function getCacheKey(name: string, version: string): string {\n return `${name}@${getVersionKey(version)}`\n}\n\n/**\n * Get path to cached package references.\n * Validates name/version to prevent path traversal.\n */\nexport function getCacheDir(name: string, version: string): string {\n if (!VALID_PKG_NAME.test(name))\n throw new Error(`Invalid package name: ${name}`)\n if (!VALID_VERSION.test(version))\n throw new Error(`Invalid version: ${version}`)\n\n const dir = resolve(REFERENCES_DIR, getCacheKey(name, version))\n if (!dir.startsWith(REFERENCES_DIR))\n throw new Error(`Path traversal detected: ${dir}`)\n return dir\n}\n","/**\n * Cache configuration\n */\n\nimport { homedir } from 'node:os'\nimport { join } from 'pathe'\nimport { getCacheKey } from './version.ts'\n\n/** Global cache directory */\nexport const CACHE_DIR = join(homedir(), '.skilld')\n\n/** References subdirectory */\nexport const REFERENCES_DIR = join(CACHE_DIR, 'references')\n\n/** Repo-level cache (issues, discussions, releases shared across monorepo packages) */\nexport const REPOS_DIR = join(CACHE_DIR, 'repos')\n\n/** Get repo cache dir for owner/repo with path traversal validation */\nexport function getRepoCacheDir(owner: string, repo: string): string {\n if (owner.includes('..') || repo.includes('..') || owner.includes('/') || repo.includes('/'))\n throw new Error(`Invalid repo path: ${owner}/${repo}`)\n return join(REPOS_DIR, owner, repo)\n}\n\n/** Get search DB path for a specific package@version */\nexport function getPackageDbPath(name: string, version: string): string {\n return join(REFERENCES_DIR, getCacheKey(name, version), 'search.db')\n}\n"],"mappings":";;;AAQA,MAAM,gBAAA;AAGN,SAAM,cAAgB,SAAA;;;AAKtB,SAAgB,YAAA,MAAc,SAAyB;AACrD,QAAO,GAAA,KAAA,GAAA,cAAA,QAAA;;;;AAMT,KAAA,CAAA,cAAgB,KAAY,QAAc,CAAA,OAAyB,IAAA,MAAA,oBAAA,UAAA;CACjE,MAAA,MAAU,QAAQ,gBAAc,YAAQ,MAAA,QAAA,CAAA;;;;AAQxC,MAAK,YAAA,KAAe,SAClB,EAAA,UAAU;MAIZ,iBAAoB,KAAA,WAAgB,aAAY;AAGhD,MAAA,YAAO,KAAA,WAAA,QAAA;;;;;AC/BT,SAAa,iBAAiB,MAAS,SAAE;;;AAMzC,SAAa,mBAAiB,GAAA,iBAAmB,GAAA,oBAAA,GAAA,kBAAA,GAAA,eAAA,GAAA,aAAA,GAAA,eAAA,GAAA,aAAA"}
@@ -1 +1,10 @@
1
+ import "./package-json.mjs";
2
+ import "./sanitize.mjs";
3
+ import "./yaml.mjs";
4
+ import "./markdown.mjs";
5
+ import "./shared.mjs";
6
+ import "./cli-helpers.mjs";
7
+ import "./lockfile.mjs";
8
+ import "./skills.mjs";
9
+ import "./formatting.mjs";
1
10
  export {};