codeply-cli 0.1.1 → 0.2.0

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 (350) hide show
  1. package/bin/codeply.js +125 -14
  2. package/lib/agent.mjs +152 -14
  3. package/lib/ai.js +217 -14
  4. package/lib/apply-limit.js +13 -3
  5. package/lib/config.js +53 -15
  6. package/lib/skills.js +358 -0
  7. package/lib/tools.mjs +63 -6
  8. package/lib/tui.mjs +95 -2
  9. package/package.json +3 -2
  10. package/skills/SOURCE.md +58 -0
  11. package/skills/accessibility/SKILL.md +147 -0
  12. package/skills/agent-architecture-audit/SKILL.md +257 -0
  13. package/skills/agent-eval/SKILL.md +147 -0
  14. package/skills/agent-harness-construction/SKILL.md +74 -0
  15. package/skills/agent-introspection-debugging/SKILL.md +154 -0
  16. package/skills/agent-payment-x402/SKILL.md +225 -0
  17. package/skills/agent-self-evaluation/SKILL.md +182 -0
  18. package/skills/agent-self-evaluation/references/evaluation-criteria.md +71 -0
  19. package/skills/agent-self-evaluation/references/hook-integration.md +64 -0
  20. package/skills/agent-sort/SKILL.md +216 -0
  21. package/skills/agentic-engineering/SKILL.md +64 -0
  22. package/skills/agentic-os/SKILL.md +388 -0
  23. package/skills/ai-first-engineering/SKILL.md +52 -0
  24. package/skills/ai-regression-testing/SKILL.md +386 -0
  25. package/skills/android-clean-architecture/SKILL.md +340 -0
  26. package/skills/angular-developer/SKILL.md +155 -0
  27. package/skills/angular-developer/references/angular-animations.md +160 -0
  28. package/skills/angular-developer/references/angular-aria.md +410 -0
  29. package/skills/angular-developer/references/cli.md +86 -0
  30. package/skills/angular-developer/references/component-harnesses.md +59 -0
  31. package/skills/angular-developer/references/component-styling.md +91 -0
  32. package/skills/angular-developer/references/components.md +117 -0
  33. package/skills/angular-developer/references/creating-services.md +97 -0
  34. package/skills/angular-developer/references/data-resolvers.md +69 -0
  35. package/skills/angular-developer/references/define-routes.md +67 -0
  36. package/skills/angular-developer/references/defining-providers.md +72 -0
  37. package/skills/angular-developer/references/di-fundamentals.md +120 -0
  38. package/skills/angular-developer/references/e2e-testing.md +56 -0
  39. package/skills/angular-developer/references/effects.md +83 -0
  40. package/skills/angular-developer/references/hierarchical-injectors.md +43 -0
  41. package/skills/angular-developer/references/host-elements.md +80 -0
  42. package/skills/angular-developer/references/injection-context.md +63 -0
  43. package/skills/angular-developer/references/inputs.md +101 -0
  44. package/skills/angular-developer/references/linked-signal.md +59 -0
  45. package/skills/angular-developer/references/loading-strategies.md +61 -0
  46. package/skills/angular-developer/references/mcp.md +108 -0
  47. package/skills/angular-developer/references/navigate-to-routes.md +69 -0
  48. package/skills/angular-developer/references/outputs.md +86 -0
  49. package/skills/angular-developer/references/reactive-forms.md +122 -0
  50. package/skills/angular-developer/references/rendering-strategies.md +44 -0
  51. package/skills/angular-developer/references/resource.md +77 -0
  52. package/skills/angular-developer/references/route-animations.md +56 -0
  53. package/skills/angular-developer/references/route-guards.md +52 -0
  54. package/skills/angular-developer/references/router-lifecycle.md +45 -0
  55. package/skills/angular-developer/references/router-testing.md +87 -0
  56. package/skills/angular-developer/references/show-routes-with-outlets.md +68 -0
  57. package/skills/angular-developer/references/signal-forms.md +795 -0
  58. package/skills/angular-developer/references/signals-overview.md +94 -0
  59. package/skills/angular-developer/references/tailwind-css.md +69 -0
  60. package/skills/angular-developer/references/template-driven-forms.md +114 -0
  61. package/skills/angular-developer/references/testing-fundamentals.md +65 -0
  62. package/skills/api-connector-builder/SKILL.md +121 -0
  63. package/skills/api-design/SKILL.md +524 -0
  64. package/skills/architecture-decision-records/SKILL.md +180 -0
  65. package/skills/article-writing/SKILL.md +80 -0
  66. package/skills/automation-audit-ops/SKILL.md +143 -0
  67. package/skills/autonomous-agent-harness/SKILL.md +274 -0
  68. package/skills/autonomous-loops/SKILL.md +611 -0
  69. package/skills/backend-patterns/SKILL.md +562 -0
  70. package/skills/benchmark/SKILL.md +95 -0
  71. package/skills/benchmark-methodology/SKILL.md +191 -0
  72. package/skills/benchmark-optimization-loop/SKILL.md +71 -0
  73. package/skills/blender-motion-state-inspection/SKILL.md +165 -0
  74. package/skills/blueprint/SKILL.md +106 -0
  75. package/skills/brand-discovery/SKILL.md +145 -0
  76. package/skills/brand-discovery/references/10_purpose-why.md +40 -0
  77. package/skills/brand-discovery/references/20_positioning.md +44 -0
  78. package/skills/brand-discovery/references/30_audience-niche.md +52 -0
  79. package/skills/brand-discovery/references/40_personality-archetype.md +57 -0
  80. package/skills/brand-discovery/references/50_voice-tone.md +59 -0
  81. package/skills/brand-discovery/references/60_narrative-story.md +50 -0
  82. package/skills/brand-discovery/references/70_founder-tension.md +49 -0
  83. package/skills/brand-discovery/references/90_SYNTHESIS.md +133 -0
  84. package/skills/brand-voice/SKILL.md +98 -0
  85. package/skills/brand-voice/references/voice-profile-schema.md +55 -0
  86. package/skills/browser-qa/SKILL.md +105 -0
  87. package/skills/bun-runtime/SKILL.md +85 -0
  88. package/skills/canary-watch/SKILL.md +108 -0
  89. package/skills/carrier-relationship-management/SKILL.md +212 -0
  90. package/skills/cisco-ios-patterns/SKILL.md +164 -0
  91. package/skills/ck/SKILL.md +148 -0
  92. package/skills/claude-devfleet/SKILL.md +112 -0
  93. package/skills/click-path-audit/SKILL.md +245 -0
  94. package/skills/clickhouse-io/SKILL.md +445 -0
  95. package/skills/code-tour/SKILL.md +254 -0
  96. package/skills/codebase-onboarding/SKILL.md +234 -0
  97. package/skills/codehealth-mcp/SKILL.md +167 -0
  98. package/skills/coding-standards/SKILL.md +551 -0
  99. package/skills/competitive-platform-analysis/SKILL.md +214 -0
  100. package/skills/competitive-report-structure/SKILL.md +162 -0
  101. package/skills/compose-multiplatform-patterns/SKILL.md +300 -0
  102. package/skills/config-gc/SKILL.md +120 -0
  103. package/skills/configure-ecc/SKILL.md +385 -0
  104. package/skills/connections-optimizer/SKILL.md +190 -0
  105. package/skills/content-engine/SKILL.md +132 -0
  106. package/skills/content-hash-cache-pattern/SKILL.md +162 -0
  107. package/skills/context-budget/SKILL.md +136 -0
  108. package/skills/continuous-agent-loop/SKILL.md +46 -0
  109. package/skills/continuous-learning/SKILL.md +132 -0
  110. package/skills/continuous-learning-v2/SKILL.md +361 -0
  111. package/skills/contract-first/SKILL.md +287 -0
  112. package/skills/cost-aware-llm-pipeline/SKILL.md +184 -0
  113. package/skills/cost-tracking/SKILL.md +97 -0
  114. package/skills/council/SKILL.md +204 -0
  115. package/skills/cpp-coding-standards/SKILL.md +724 -0
  116. package/skills/cpp-testing/SKILL.md +325 -0
  117. package/skills/crosspost/SKILL.md +112 -0
  118. package/skills/csharp-testing/SKILL.md +322 -0
  119. package/skills/customer-billing-ops/SKILL.md +141 -0
  120. package/skills/customs-trade-compliance/SKILL.md +263 -0
  121. package/skills/dart-flutter-patterns/SKILL.md +564 -0
  122. package/skills/dashboard-builder/SKILL.md +109 -0
  123. package/skills/data-scraper-agent/SKILL.md +765 -0
  124. package/skills/data-throughput-accelerator/SKILL.md +74 -0
  125. package/skills/database-migrations/SKILL.md +430 -0
  126. package/skills/deep-research/SKILL.md +160 -0
  127. package/skills/defi-amm-security/SKILL.md +167 -0
  128. package/skills/delivery-gate/SKILL.md +126 -0
  129. package/skills/deployment-patterns/SKILL.md +428 -0
  130. package/skills/design-system/SKILL.md +83 -0
  131. package/skills/django-celery/SKILL.md +458 -0
  132. package/skills/django-patterns/SKILL.md +735 -0
  133. package/skills/django-security/SKILL.md +644 -0
  134. package/skills/django-tdd/SKILL.md +730 -0
  135. package/skills/django-verification/SKILL.md +470 -0
  136. package/skills/dmux-workflows/SKILL.md +192 -0
  137. package/skills/docker-patterns/SKILL.md +365 -0
  138. package/skills/documentation-lookup/SKILL.md +91 -0
  139. package/skills/dotnet-patterns/SKILL.md +322 -0
  140. package/skills/dynamic-workflow-mode/SKILL.md +124 -0
  141. package/skills/e2e-testing/SKILL.md +327 -0
  142. package/skills/ecc-guide/SKILL.md +190 -0
  143. package/skills/ecc-recipes/SKILL.md +149 -0
  144. package/skills/ecc-tools-cost-audit/SKILL.md +161 -0
  145. package/skills/email-ops/SKILL.md +122 -0
  146. package/skills/energy-procurement/SKILL.md +228 -0
  147. package/skills/enterprise-agent-ops/SKILL.md +51 -0
  148. package/skills/error-handling/SKILL.md +377 -0
  149. package/skills/eval-harness/SKILL.md +271 -0
  150. package/skills/evm-token-decimals/SKILL.md +131 -0
  151. package/skills/exa-search/SKILL.md +108 -0
  152. package/skills/fal-ai-media/SKILL.md +289 -0
  153. package/skills/fastapi-patterns/SKILL.md +514 -0
  154. package/skills/finance-billing-ops/SKILL.md +128 -0
  155. package/skills/flox-environments/SKILL.md +497 -0
  156. package/skills/flutter-dart-code-review/SKILL.md +436 -0
  157. package/skills/foundation-models-on-device/SKILL.md +243 -0
  158. package/skills/frontend-a11y/SKILL.md +446 -0
  159. package/skills/frontend-design-direction/SKILL.md +93 -0
  160. package/skills/frontend-patterns/SKILL.md +657 -0
  161. package/skills/frontend-slides/SKILL.md +185 -0
  162. package/skills/fsharp-testing/SKILL.md +281 -0
  163. package/skills/gan-style-harness/SKILL.md +279 -0
  164. package/skills/gateguard/SKILL.md +133 -0
  165. package/skills/generating-python-installer/SKILL.md +820 -0
  166. package/skills/git-workflow/SKILL.md +716 -0
  167. package/skills/github-ops/SKILL.md +145 -0
  168. package/skills/golang-patterns/SKILL.md +676 -0
  169. package/skills/golang-testing/SKILL.md +721 -0
  170. package/skills/google-workspace-ops/SKILL.md +96 -0
  171. package/skills/growth-log/SKILL.md +128 -0
  172. package/skills/healthcare-cdss-patterns/SKILL.md +246 -0
  173. package/skills/healthcare-emr-patterns/SKILL.md +160 -0
  174. package/skills/healthcare-eval-harness/SKILL.md +208 -0
  175. package/skills/healthcare-phi-compliance/SKILL.md +146 -0
  176. package/skills/hermes-imports/SKILL.md +89 -0
  177. package/skills/hexagonal-architecture/SKILL.md +277 -0
  178. package/skills/hipaa-compliance/SKILL.md +79 -0
  179. package/skills/homelab-network-readiness/SKILL.md +170 -0
  180. package/skills/homelab-network-setup/SKILL.md +130 -0
  181. package/skills/homelab-pihole-dns/SKILL.md +275 -0
  182. package/skills/homelab-vlan-segmentation/SKILL.md +312 -0
  183. package/skills/homelab-wireguard-vpn/SKILL.md +306 -0
  184. package/skills/hookify-rules/SKILL.md +128 -0
  185. package/skills/inherit-legacy-style/SKILL.md +157 -0
  186. package/skills/intent-driven-development/SKILL.md +360 -0
  187. package/skills/inventory-demand-planning/SKILL.md +247 -0
  188. package/skills/investor-materials/SKILL.md +97 -0
  189. package/skills/investor-outreach/SKILL.md +92 -0
  190. package/skills/ios-icon-gen/SKILL.md +158 -0
  191. package/skills/iterative-retrieval/SKILL.md +212 -0
  192. package/skills/ito-basket-compare/SKILL.md +64 -0
  193. package/skills/ito-compute/SKILL.md +129 -0
  194. package/skills/ito-data-atlas-agent/SKILL.md +64 -0
  195. package/skills/ito-market-intelligence/SKILL.md +61 -0
  196. package/skills/ito-trade-planner/SKILL.md +68 -0
  197. package/skills/java-coding-standards/SKILL.md +384 -0
  198. package/skills/jira-integration/SKILL.md +303 -0
  199. package/skills/jpa-patterns/SKILL.md +152 -0
  200. package/skills/knowledge-ops/SKILL.md +155 -0
  201. package/skills/kotlin-coroutines-flows/SKILL.md +285 -0
  202. package/skills/kotlin-exposed-patterns/SKILL.md +720 -0
  203. package/skills/kotlin-ktor-patterns/SKILL.md +690 -0
  204. package/skills/kotlin-patterns/SKILL.md +712 -0
  205. package/skills/kotlin-testing/SKILL.md +825 -0
  206. package/skills/kubernetes-patterns/SKILL.md +756 -0
  207. package/skills/laravel-patterns/SKILL.md +416 -0
  208. package/skills/laravel-plugin-discovery/SKILL.md +230 -0
  209. package/skills/laravel-security/SKILL.md +948 -0
  210. package/skills/laravel-tdd/SKILL.md +675 -0
  211. package/skills/laravel-verification/SKILL.md +180 -0
  212. package/skills/latency-critical-systems/SKILL.md +75 -0
  213. package/skills/lead-intelligence/SKILL.md +322 -0
  214. package/skills/liquid-glass-design/SKILL.md +279 -0
  215. package/skills/llm-trading-agent-security/SKILL.md +147 -0
  216. package/skills/logistics-exception-management/SKILL.md +222 -0
  217. package/skills/loop-design-check/SKILL.md +143 -0
  218. package/skills/mailtrap-email-integration/SKILL.md +77 -0
  219. package/skills/make-interfaces-feel-better/SKILL.md +152 -0
  220. package/skills/manim-video/SKILL.md +90 -0
  221. package/skills/market-research/SKILL.md +76 -0
  222. package/skills/marketing-campaign/SKILL.md +114 -0
  223. package/skills/mcp-server-patterns/SKILL.md +70 -0
  224. package/skills/messages-ops/SKILL.md +105 -0
  225. package/skills/ml-adoption-playbook/SKILL.md +57 -0
  226. package/skills/mle-workflow/SKILL.md +348 -0
  227. package/skills/motion-advanced/SKILL.md +596 -0
  228. package/skills/motion-foundations/SKILL.md +299 -0
  229. package/skills/motion-patterns/SKILL.md +434 -0
  230. package/skills/motion-ui/SKILL.md +576 -0
  231. package/skills/mysql-patterns/SKILL.md +413 -0
  232. package/skills/nanoclaw-repl/SKILL.md +34 -0
  233. package/skills/nestjs-patterns/SKILL.md +231 -0
  234. package/skills/netmiko-ssh-automation/SKILL.md +174 -0
  235. package/skills/network-bgp-diagnostics/SKILL.md +168 -0
  236. package/skills/network-config-validation/SKILL.md +211 -0
  237. package/skills/network-interface-health/SKILL.md +153 -0
  238. package/skills/nextjs-turbopack/SKILL.md +58 -0
  239. package/skills/nodejs-keccak256/SKILL.md +103 -0
  240. package/skills/nutrient-document-processing/SKILL.md +168 -0
  241. package/skills/nuxt4-patterns/SKILL.md +101 -0
  242. package/skills/openclaw-persona-forge/SKILL.md +289 -0
  243. package/skills/openclaw-persona-forge/references/avatar-style.md +124 -0
  244. package/skills/openclaw-persona-forge/references/boundary-rules.md +53 -0
  245. package/skills/openclaw-persona-forge/references/error-handling.md +53 -0
  246. package/skills/openclaw-persona-forge/references/identity-tension.md +48 -0
  247. package/skills/openclaw-persona-forge/references/naming-system.md +39 -0
  248. package/skills/openclaw-persona-forge/references/output-template.md +166 -0
  249. package/skills/opensource-pipeline/SKILL.md +256 -0
  250. package/skills/orch-add-feature/SKILL.md +45 -0
  251. package/skills/orch-build-mvp/SKILL.md +49 -0
  252. package/skills/orch-change-feature/SKILL.md +43 -0
  253. package/skills/orch-fix-defect/SKILL.md +43 -0
  254. package/skills/orch-pipeline/SKILL.md +121 -0
  255. package/skills/orch-refine-code/SKILL.md +44 -0
  256. package/skills/parallel-execution-optimizer/SKILL.md +74 -0
  257. package/skills/perl-patterns/SKILL.md +505 -0
  258. package/skills/perl-security/SKILL.md +504 -0
  259. package/skills/perl-testing/SKILL.md +476 -0
  260. package/skills/plan-canvas/SKILL.md +153 -0
  261. package/skills/plan-orchestrate/SKILL.md +263 -0
  262. package/skills/plankton-code-quality/SKILL.md +237 -0
  263. package/skills/postgres-patterns/SKILL.md +148 -0
  264. package/skills/prediction-market-oracle-research/SKILL.md +64 -0
  265. package/skills/prediction-market-risk-review/SKILL.md +61 -0
  266. package/skills/prisma-patterns/SKILL.md +401 -0
  267. package/skills/product-capability/SKILL.md +142 -0
  268. package/skills/product-lens/SKILL.md +93 -0
  269. package/skills/production-audit/SKILL.md +207 -0
  270. package/skills/production-scheduling/SKILL.md +238 -0
  271. package/skills/project-flow-ops/SKILL.md +112 -0
  272. package/skills/prompt-optimizer/SKILL.md +398 -0
  273. package/skills/python-patterns/SKILL.md +751 -0
  274. package/skills/python-testing/SKILL.md +817 -0
  275. package/skills/pytorch-patterns/SKILL.md +397 -0
  276. package/skills/quality-nonconformance/SKILL.md +260 -0
  277. package/skills/quarkus-patterns/SKILL.md +723 -0
  278. package/skills/quarkus-security/SKILL.md +468 -0
  279. package/skills/quarkus-tdd/SKILL.md +812 -0
  280. package/skills/quarkus-verification/SKILL.md +480 -0
  281. package/skills/ralphinho-rfc-pipeline/SKILL.md +68 -0
  282. package/skills/react-native-patterns/SKILL.md +326 -0
  283. package/skills/react-patterns/SKILL.md +342 -0
  284. package/skills/react-performance/SKILL.md +575 -0
  285. package/skills/react-testing/SKILL.md +424 -0
  286. package/skills/recsys-pipeline-architect/SKILL.md +115 -0
  287. package/skills/recursive-decision-ledger/SKILL.md +81 -0
  288. package/skills/redis-patterns/SKILL.md +404 -0
  289. package/skills/regex-vs-llm-structured-text/SKILL.md +221 -0
  290. package/skills/remotion-video-creation/SKILL.md +43 -0
  291. package/skills/repo-scan/SKILL.md +79 -0
  292. package/skills/research-ops/SKILL.md +113 -0
  293. package/skills/returns-reverse-logistics/SKILL.md +240 -0
  294. package/skills/rules-distill/SKILL.md +265 -0
  295. package/skills/rust-patterns/SKILL.md +500 -0
  296. package/skills/rust-testing/SKILL.md +501 -0
  297. package/skills/safety-guard/SKILL.md +76 -0
  298. package/skills/santa-method/SKILL.md +307 -0
  299. package/skills/scientific-db-pubmed-database/SKILL.md +176 -0
  300. package/skills/scientific-db-uspto-database/SKILL.md +178 -0
  301. package/skills/scientific-pkg-gget/SKILL.md +167 -0
  302. package/skills/scientific-thinking-literature-review/SKILL.md +193 -0
  303. package/skills/scientific-thinking-scholar-evaluation/SKILL.md +161 -0
  304. package/skills/search-first/SKILL.md +183 -0
  305. package/skills/security-bounty-hunter/SKILL.md +100 -0
  306. package/skills/security-review/SKILL.md +504 -0
  307. package/skills/security-scan/SKILL.md +166 -0
  308. package/skills/seo/SKILL.md +155 -0
  309. package/skills/skill-comply/SKILL.md +59 -0
  310. package/skills/skill-scout/SKILL.md +141 -0
  311. package/skills/skill-stocktake/SKILL.md +195 -0
  312. package/skills/social-graph-ranker/SKILL.md +155 -0
  313. package/skills/social-publisher/SKILL.md +130 -0
  314. package/skills/springboot-patterns/SKILL.md +315 -0
  315. package/skills/springboot-security/SKILL.md +273 -0
  316. package/skills/springboot-tdd/SKILL.md +159 -0
  317. package/skills/springboot-verification/SKILL.md +232 -0
  318. package/skills/strategic-compact/SKILL.md +142 -0
  319. package/skills/swift-actor-persistence/SKILL.md +144 -0
  320. package/skills/swift-concurrency-6-2/SKILL.md +216 -0
  321. package/skills/swift-protocol-di-testing/SKILL.md +191 -0
  322. package/skills/swiftui-patterns/SKILL.md +259 -0
  323. package/skills/taste/SKILL.md +264 -0
  324. package/skills/taste/references/genre-taxonomy.md +87 -0
  325. package/skills/tdd-workflow/SKILL.md +583 -0
  326. package/skills/team-agent-orchestration/SKILL.md +111 -0
  327. package/skills/team-builder/SKILL.md +169 -0
  328. package/skills/terminal-ops/SKILL.md +110 -0
  329. package/skills/tinystruct-patterns/SKILL.md +279 -0
  330. package/skills/tinystruct-patterns/references/architecture.md +90 -0
  331. package/skills/tinystruct-patterns/references/data-handling.md +60 -0
  332. package/skills/tinystruct-patterns/references/database.md +99 -0
  333. package/skills/tinystruct-patterns/references/routing.md +64 -0
  334. package/skills/tinystruct-patterns/references/system-usage.md +97 -0
  335. package/skills/tinystruct-patterns/references/testing.md +72 -0
  336. package/skills/token-budget-advisor/SKILL.md +134 -0
  337. package/skills/ui-demo/SKILL.md +466 -0
  338. package/skills/ui-to-vue/SKILL.md +135 -0
  339. package/skills/uncloud/SKILL.md +344 -0
  340. package/skills/unified-memory/SKILL.md +170 -0
  341. package/skills/unified-notifications-ops/SKILL.md +188 -0
  342. package/skills/verification-loop/SKILL.md +129 -0
  343. package/skills/video-editing/SKILL.md +311 -0
  344. package/skills/videodb/SKILL.md +375 -0
  345. package/skills/visa-doc-translate/SKILL.md +117 -0
  346. package/skills/vite-patterns/SKILL.md +450 -0
  347. package/skills/vue-patterns/SKILL.md +471 -0
  348. package/skills/windows-desktop-e2e/SKILL.md +888 -0
  349. package/skills/workspace-surface-audit/SKILL.md +126 -0
  350. package/skills/x-api/SKILL.md +235 -0
package/lib/skills.js ADDED
@@ -0,0 +1,358 @@
1
+ /**
2
+ * Codeply skills.
3
+ *
4
+ * A skill is a directory with a SKILL.md: YAML frontmatter (`name`,
5
+ * `description`) followed by a markdown body of instructions. This is the same
6
+ * convention Claude Code and affaan-m/ECC use — see skills/SOURCE.md for where
7
+ * the bundled set came from and why the format was copied rather than invented.
8
+ *
9
+ * Two-level progressive disclosure — this matters at 281 skills:
10
+ *
11
+ * 1. Per-skill: buildSystemPrompt() only ever injects a skill's NAME and a
12
+ * capped one-line description. The full body — which can run to several
13
+ * KB with reference files — loads on demand via <codeply:use_skill>. The
14
+ * alternative, concatenating every skill's full body into the system
15
+ * prompt always, would resend ~3.4MB per request for the bundled set
16
+ * alone.
17
+ *
18
+ * 2. Across the library: even name+description for all 281 is ~44KB
19
+ * (~11,000 tokens) — resent on every step of every turn, that alone would
20
+ * make the skill library cost more than it saves. So the system prompt by
21
+ * default only lists a curated subset (DAILY_SKILLS below, ~2KB) covering
22
+ * general coding work; the complete 281 are still fully installed and
23
+ * usable, just one cheap `list_skills` action away instead of force-fed
24
+ * into every request. DAILY_SKILLS is not a cut Codeply invented — it's
25
+ * ECC's own `.agents/skills/` curation of its 281-skill `skills/`
26
+ * library (see skills/SOURCE.md), reused rather than re-decided.
27
+ *
28
+ * Two sources, later wins on a name collision:
29
+ * built-in <repo>/skills/<name>/SKILL.md — vendored, ships with Codeply
30
+ * user ~/.codeply/skills/<name>/SKILL.md — `codeply skill install`
31
+ */
32
+ const fs = require('fs');
33
+ const path = require('path');
34
+ const os = require('os');
35
+
36
+ const BUILTIN_DIR = path.join(__dirname, '..', 'skills');
37
+ const USER_DIR = path.join(os.homedir(), '.codeply', 'skills');
38
+
39
+ const MAX_BODY_CHARS = 8000; // fed to the model on demand; keep one skill from eating the whole budget
40
+
41
+ // ECC's own curated subset of its full library — see skills/SOURCE.md. Kept as
42
+ // a name list rather than a directory so a user-installed skill with one of
43
+ // these names (or a future re-vendor that drops/renames one) degrades
44
+ // gracefully instead of erroring.
45
+ const DAILY_SKILLS = new Set([
46
+ 'agent-introspection-debugging', 'agent-sort', 'api-design', 'article-writing',
47
+ 'backend-patterns', 'benchmark-methodology', 'brand-discovery', 'brand-voice',
48
+ 'bun-runtime', 'coding-standards', 'competitive-platform-analysis',
49
+ 'competitive-report-structure', 'content-engine', 'crosspost', 'deep-research',
50
+ 'dmux-workflows', 'documentation-lookup', 'e2e-testing', 'eval-harness',
51
+ 'exa-search', 'fal-ai-media', 'frontend-patterns', 'frontend-slides',
52
+ 'investor-materials', 'investor-outreach', 'market-research', 'mcp-server-patterns',
53
+ 'mle-workflow', 'nextjs-turbopack', 'plan-canvas', 'product-capability',
54
+ 'security-review', 'strategic-compact', 'tdd-workflow', 'unified-memory',
55
+ 'verification-loop', 'video-editing', 'x-api',
56
+ ]);
57
+
58
+ function listSkillDirs(root, source) {
59
+ let entries;
60
+ try { entries = fs.readdirSync(root, { withFileTypes: true }); }
61
+ catch { return []; }
62
+
63
+ return entries
64
+ .filter((e) => e.isDirectory())
65
+ .map((e) => ({ name: e.name, dir: path.join(root, e.name), source }))
66
+ .filter((s) => fs.existsSync(path.join(s.dir, 'SKILL.md')));
67
+ }
68
+
69
+ /**
70
+ * Minimal YAML frontmatter reader for exactly the two keys skills use.
71
+ * Handles plain `key: value` and folded/literal block scalars (`key: >-` /
72
+ * `key: |-` etc.) since real skills in the wild use both — a naive
73
+ * single-line regex silently truncates ~10% of the bundled set's descriptions.
74
+ */
75
+ function parseFrontmatter(text) {
76
+ const m = text.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n?/);
77
+ if (!m) return { name: null, description: null, body: text };
78
+
79
+ const fmLines = m[1].split(/\r?\n/);
80
+ const values = {};
81
+ for (let i = 0; i < fmLines.length; i++) {
82
+ const line = fmLines[i];
83
+ const kv = line.match(/^([a-zA-Z_-]+):\s*(.*)$/);
84
+ if (!kv) continue;
85
+ const [, key, rest] = kv;
86
+
87
+ const block = rest.match(/^([|>])(-|\+)?\s*$/);
88
+ if (block) {
89
+ const folded = block[1] === '>';
90
+ const collected = [];
91
+ let j = i + 1;
92
+ let indent = null;
93
+ while (j < fmLines.length) {
94
+ const l = fmLines[j];
95
+ if (l.trim() === '') { collected.push(''); j++; continue; }
96
+ const lineIndent = l.match(/^\s*/)[0].length;
97
+ if (indent === null) indent = lineIndent;
98
+ if (lineIndent < indent) break;
99
+ collected.push(l.slice(indent));
100
+ j++;
101
+ }
102
+ i = j - 1;
103
+ values[key] = folded
104
+ ? collected.join(' ').replace(/\s+/g, ' ').trim()
105
+ : collected.join('\n').replace(/\n+$/, '');
106
+ continue;
107
+ }
108
+
109
+ // Strip a matching pair of quotes, nothing fancier — these are simple labels.
110
+ values[key] = rest.replace(/^["'](.*)["']$/, '$1').trim();
111
+ }
112
+
113
+ return {
114
+ name: values.name || null,
115
+ description: values.description || null,
116
+ body: text.slice(m[0].length),
117
+ };
118
+ }
119
+
120
+ /** Every skill available right now, built-in and user, deduped by name. */
121
+ function listSkills() {
122
+ const found = new Map();
123
+ for (const s of listSkillDirs(BUILTIN_DIR, 'built-in')) found.set(s.name, s);
124
+ // User skills are installed deliberately, so a same-named one wins.
125
+ for (const s of listSkillDirs(USER_DIR, 'user')) found.set(s.name, s);
126
+
127
+ const skills = [];
128
+ for (const s of found.values()) {
129
+ let raw;
130
+ try { raw = fs.readFileSync(path.join(s.dir, 'SKILL.md'), 'utf8'); }
131
+ catch { continue; }
132
+ const { name, description } = parseFrontmatter(raw);
133
+ const finalName = name || s.name;
134
+ skills.push({
135
+ name: finalName,
136
+ description: description || '(no description)',
137
+ source: s.source,
138
+ dir: s.dir,
139
+ // A user-installed skill overriding a daily one keeps its daily status —
140
+ // the name is still what buildSystemPrompt filters on, not the source.
141
+ daily: DAILY_SKILLS.has(finalName),
142
+ });
143
+ }
144
+ return skills.sort((a, b) => a.name.localeCompare(b.name));
145
+ }
146
+
147
+ const INDEX_DESC_MAX = 140; // per-skill cap in the always-injected index; use_skill has the rest
148
+
149
+ /**
150
+ * Compact index of skill name + a capped description, one per line.
151
+ *
152
+ * `daily` (default) is what goes in the system prompt on every request — see
153
+ * the module doc for why the full 281 is too large to inject unconditionally.
154
+ * `all` is for the list_skills action and `/skill list`, where the cost is
155
+ * paid once, on demand, instead of on every step.
156
+ */
157
+ function formatSkillIndex(skills = listSkills(), { all = false } = {}) {
158
+ const shown = all ? skills : skills.filter((s) => s.daily);
159
+ if (shown.length === 0) return null;
160
+ return shown
161
+ .map((s) => {
162
+ const d = s.description.length > INDEX_DESC_MAX
163
+ ? s.description.slice(0, INDEX_DESC_MAX - 1).trim() + '…'
164
+ : s.description;
165
+ return `- ${s.name}: ${d}`;
166
+ })
167
+ .join('\n');
168
+ }
169
+
170
+ /** The full body of one skill, truncated to a sane size, or null if unknown. */
171
+ function loadSkillBody(name) {
172
+ const skill = listSkills().find((s) => s.name === name);
173
+ if (!skill) return null;
174
+
175
+ let raw;
176
+ try { raw = fs.readFileSync(path.join(skill.dir, 'SKILL.md'), 'utf8'); }
177
+ catch { return null; }
178
+ const { body } = parseFrontmatter(raw);
179
+ const trimmed = body.trim();
180
+
181
+ if (trimmed.length <= MAX_BODY_CHARS) return trimmed;
182
+ return trimmed.slice(0, MAX_BODY_CHARS) +
183
+ `\n\n[… truncated. The rest is at ${path.join(skill.dir, 'SKILL.md')} if you need it via read_file.]`;
184
+ }
185
+
186
+ /**
187
+ * Copy a local directory containing a SKILL.md into the user skill store.
188
+ * Named separately from the GitHub path below because it never touches the
189
+ * network — used by both `codeply skill install <local-dir>` and as the last
190
+ * step after a GitHub download lands in a temp directory.
191
+ */
192
+ function installFromLocalDir(sourceDir, name) {
193
+ const skillMd = path.join(sourceDir, 'SKILL.md');
194
+ if (!fs.existsSync(skillMd)) {
195
+ return { ok: false, error: `No SKILL.md found in ${sourceDir}` };
196
+ }
197
+ const raw = fs.readFileSync(skillMd, 'utf8');
198
+ const { name: fmName } = parseFrontmatter(raw);
199
+ const finalName = name || fmName || path.basename(sourceDir);
200
+
201
+ const dest = path.join(USER_DIR, finalName);
202
+ fs.mkdirSync(dest, { recursive: true });
203
+ copyRecursive(sourceDir, dest);
204
+ return { ok: true, name: finalName, dest };
205
+ }
206
+
207
+ function copyRecursive(src, dest) {
208
+ for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
209
+ const s = path.join(src, entry.name);
210
+ const d = path.join(dest, entry.name);
211
+ if (entry.isDirectory()) {
212
+ fs.mkdirSync(d, { recursive: true });
213
+ copyRecursive(s, d);
214
+ } else if (entry.isFile()) {
215
+ fs.copyFileSync(s, d);
216
+ }
217
+ }
218
+ }
219
+
220
+ // ─── Installing from GitHub ─────────────────────────────────────────────────
221
+
222
+ /**
223
+ * Parse anything a user might paste for a GitHub skill source:
224
+ * owner/repo
225
+ * https://github.com/owner/repo
226
+ * https://github.com/owner/repo/tree/<branch>/<subpath...>
227
+ * https://github.com/owner/repo/blob/<branch>/<subpath...>/SKILL.md
228
+ */
229
+ function parseGitHubSource(source) {
230
+ const s = source.trim().replace(/\/+$/, '');
231
+
232
+ let m = s.match(/^(?:https?:\/\/)?github\.com\/([^/]+)\/([^/]+)(?:\/(tree|blob)\/([^/]+)(?:\/(.*))?)?$/i);
233
+ if (m) {
234
+ const [, owner, repo, , branch, subpath] = m;
235
+ return { owner, repo: repo.replace(/\.git$/, ''), branch: branch || null, subpath: subpath || '' };
236
+ }
237
+
238
+ m = s.match(/^([\w.-]+)\/([\w.-]+)$/);
239
+ if (m) return { owner: m[1], repo: m[2], branch: null, subpath: '' };
240
+
241
+ return null;
242
+ }
243
+
244
+ async function githubJson(url) {
245
+ const res = await fetch(url, { headers: { Accept: 'application/vnd.github+json' } });
246
+ const body = await res.json().catch(() => ({}));
247
+ if (!res.ok) throw new Error(body.message || `GitHub API returned HTTP ${res.status}`);
248
+ return body;
249
+ }
250
+
251
+ /**
252
+ * Install one or more skills from a GitHub repo.
253
+ *
254
+ * If `subpath` points directly at a skill (a directory whose own SKILL.md
255
+ * lives there), that one skill is installed under `name` (or its own frontmatter
256
+ * name). Otherwise every SKILL.md found anywhere under `subpath` (the whole
257
+ * repo, if none was given) is installed — this is what makes a bare repo URL
258
+ * like affaan-m/ECC pull in its entire skill library in one command, and also
259
+ * what lets a single-skill link install just that one.
260
+ */
261
+ async function installFromGitHub(source, explicitName) {
262
+ const parsed = parseGitHubSource(source);
263
+ if (!parsed) return { ok: false, error: `Not a recognizable GitHub source: "${source}"` };
264
+ const { owner, repo, subpath } = parsed;
265
+
266
+ let branch = parsed.branch;
267
+ if (!branch) {
268
+ try {
269
+ const repoInfo = await githubJson(`https://api.github.com/repos/${owner}/${repo}`);
270
+ branch = repoInfo.default_branch || 'main';
271
+ } catch (e) {
272
+ return { ok: false, error: `Could not reach ${owner}/${repo}: ${e.message}` };
273
+ }
274
+ }
275
+
276
+ let tree;
277
+ try {
278
+ const treeData = await githubJson(
279
+ `https://api.github.com/repos/${owner}/${repo}/git/trees/${encodeURIComponent(branch)}?recursive=1`
280
+ );
281
+ if (treeData.truncated) {
282
+ // GitHub's own recursion cap (huge monorepos) — rare, but better to say
283
+ // so than to silently install an incomplete skill.
284
+ return { ok: false, error: `${owner}/${repo}@${branch} is too large to list in one call. Point --install at a narrower subpath.` };
285
+ }
286
+ tree = treeData.tree || [];
287
+ } catch (e) {
288
+ return { ok: false, error: `Could not read ${owner}/${repo}@${branch}: ${e.message}` };
289
+ }
290
+
291
+ const cleanSubpath = subpath.replace(/\/SKILL\.md$/i, '').replace(/\/+$/, '');
292
+ const scoped = cleanSubpath
293
+ ? tree.filter((t) => t.path === cleanSubpath || t.path.startsWith(cleanSubpath + '/'))
294
+ : tree;
295
+
296
+ const skillMdFiles = scoped.filter((t) => t.type === 'blob' && /(^|\/)SKILL\.md$/i.test(t.path));
297
+ if (skillMdFiles.length === 0) {
298
+ return { ok: false, error: `No SKILL.md found under ${owner}/${repo}${cleanSubpath ? '/' + cleanSubpath : ''}.` };
299
+ }
300
+
301
+ const skillDirs = skillMdFiles.map((f) => f.path.replace(/\/SKILL\.md$/i, ''));
302
+ const singleSkill = skillDirs.length === 1;
303
+
304
+ const installed = [];
305
+ const failed = [];
306
+
307
+ for (const dir of skillDirs) {
308
+ const filesInDir = tree.filter((t) =>
309
+ t.type === 'blob' && (t.path === `${dir}/SKILL.md` || t.path.startsWith(`${dir}/references/`))
310
+ );
311
+ const rawBase = `https://raw.githubusercontent.com/${owner}/${repo}/${branch}`;
312
+
313
+ const fetched = [];
314
+ let ok = true;
315
+ for (const f of filesInDir) {
316
+ try {
317
+ const res = await fetch(`${rawBase}/${f.path}`);
318
+ if (!res.ok) throw new Error(`HTTP ${res.status}`);
319
+ fetched.push({ relPath: f.path.slice(dir.length + 1), content: await res.text() });
320
+ } catch (e) {
321
+ ok = false;
322
+ failed.push({ dir, error: e.message });
323
+ break;
324
+ }
325
+ }
326
+ if (!ok) continue;
327
+
328
+ const skillMd = fetched.find((f) => f.relPath === 'SKILL.md');
329
+ const { name: fmName } = parseFrontmatter(skillMd.content);
330
+ const finalName = (singleSkill && explicitName) || fmName || path.basename(dir);
331
+
332
+ const dest = path.join(USER_DIR, finalName);
333
+ fs.mkdirSync(dest, { recursive: true });
334
+ for (const f of fetched) {
335
+ const target = path.join(dest, f.relPath);
336
+ fs.mkdirSync(path.dirname(target), { recursive: true });
337
+ fs.writeFileSync(target, f.content, 'utf8');
338
+ }
339
+ installed.push({ name: finalName, fileCount: fetched.length, dest });
340
+ }
341
+
342
+ return { ok: installed.length > 0, installed, failed, source: `${owner}/${repo}@${branch}` };
343
+ }
344
+
345
+ function removeSkill(name) {
346
+ const dest = path.join(USER_DIR, name);
347
+ if (!fs.existsSync(dest)) {
348
+ return { ok: false, error: `No user-installed skill named "${name}" (built-in skills can't be removed this way).` };
349
+ }
350
+ fs.rmSync(dest, { recursive: true, force: true });
351
+ return { ok: true };
352
+ }
353
+
354
+ module.exports = {
355
+ BUILTIN_DIR, USER_DIR, MAX_BODY_CHARS, DAILY_SKILLS,
356
+ parseFrontmatter, listSkills, formatSkillIndex, loadSkillBody,
357
+ installFromLocalDir, installFromGitHub, parseGitHubSource, removeSkill,
358
+ };
package/lib/tools.mjs CHANGED
@@ -19,6 +19,7 @@ const require = createRequire(import.meta.url);
19
19
  const editEngine = require('./edit-engine.js');
20
20
  const applyLimit = require('./apply-limit.js');
21
21
  const config = require('./config.js');
22
+ const skills = require('./skills.js');
22
23
 
23
24
  // A write is a real "apply" against the shared 100/day cap only when it's
24
25
  // actually spending the shared codeply proxy — Ollama (local or the user's
@@ -328,7 +329,63 @@ async function run(args, ctx) {
328
329
  });
329
330
  }
330
331
 
331
- export const TOOLS = { list_dir, read_file, write_file, edit_file, search, run };
332
+ /**
333
+ * Load one skill's full instructions on demand. Read-only, no approval — the
334
+ * whole point of the skill index living in the system prompt (see
335
+ * lib/skills.js) is that only a name and a one-line description cost anything
336
+ * until the agent actually decides a skill is relevant, at which point this is
337
+ * the one call that pulls in the rest.
338
+ */
339
+ async function use_skill(args, ctx) {
340
+ const name = (args.name || '').trim();
341
+ if (!name) return { ok: false, output: 'use_skill needs a <name>.' };
342
+
343
+ const body = skills.loadSkillBody(name);
344
+ if (body == null) {
345
+ // Capped rather than dumping the full library (281 names) into a reply
346
+ // that only exists because of one wrong guess.
347
+ const all = skills.listSkills().map((s) => s.name);
348
+ const shown = all.slice(0, 40).join(', ') + (all.length > 40 ? `, … (${all.length - 40} more — see list_skills)` : '');
349
+ return {
350
+ ok: false,
351
+ output: `No skill named "${name}". ${all.length ? `Some available: ${shown}` : '(none installed)'}`,
352
+ };
353
+ }
354
+ return { ok: true, output: body, meta: { label: name } };
355
+ }
356
+
357
+ /**
358
+ * The full skill catalog — the counterpart to the DAILY-only index that
359
+ * always rides in the system prompt (see lib/skills.js). One call, paid once,
360
+ * only when the task actually needs something outside that curated set.
361
+ *
362
+ * At 281 skills, name+description together run past MAX_TOOL_OUTPUT — an
363
+ * optional query narrows to matching names/descriptions before formatting, so
364
+ * the normal case (looking for "something about X") returns a handful of
365
+ * relevant lines instead of a wall of text truncated at an arbitrary,
366
+ * alphabetically-biased cutoff.
367
+ */
368
+ async function list_skills(args) {
369
+ const all = skills.listSkills();
370
+ const query = (args.query || '').trim().toLowerCase();
371
+ const matched = query
372
+ ? all.filter((s) => s.name.toLowerCase().includes(query) || s.description.toLowerCase().includes(query))
373
+ : all;
374
+
375
+ if (query && matched.length === 0) {
376
+ return { ok: true, output: `No skills matched "${query}" out of ${all.length} total.`, meta: { count: 0 } };
377
+ }
378
+
379
+ const index = skills.formatSkillIndex(matched, { all: true });
380
+ const header = query ? `${matched.length} of ${all.length} skills match "${query}":\n` : `${all.length} skills:\n`;
381
+ return {
382
+ ok: true,
383
+ output: truncate(header + (index || '(none)')),
384
+ meta: { label: query || 'all skills', count: matched.length },
385
+ };
386
+ }
387
+
388
+ export const TOOLS = { list_dir, read_file, write_file, edit_file, search, run, use_skill, list_skills };
332
389
 
333
390
  export const TOOL_NEEDS_APPROVAL = new Set(['write_file', 'edit_file', 'run']);
334
391
 
@@ -340,6 +397,8 @@ export const TOOL_DISPLAY = {
340
397
  edit_file: { verb: 'edit', icon: '✎' },
341
398
  search: { verb: 'search', icon: '▸' },
342
399
  run: { verb: 'run', icon: '$' },
400
+ use_skill: { verb: 'skill', icon: '★' },
401
+ list_skills:{ verb: 'skills', icon: '★' },
343
402
  };
344
403
 
345
404
  export async function executeTool(name, args, ctx) {
@@ -355,17 +414,15 @@ export async function executeTool(name, args, ctx) {
355
414
  if (capped) {
356
415
  const limit = await applyLimit.checkApplyLimit();
357
416
  if (!limit.allowed) {
358
- return {
359
- ok: false,
360
- output: limit.error || `Daily apply limit reached (${limit.count}/${limit.limit}). This is shared with the Codeply desktop app and resets at midnight UTC.`,
361
- };
417
+ const base = limit.error || `Daily apply limit reached (${limit.count}/${limit.limit}). This is shared with the Codeply desktop app and resets at midnight UTC.`;
418
+ return { ok: false, output: `${base} ${config.byokHint()}` };
362
419
  }
363
420
  }
364
421
 
365
422
  try {
366
423
  const result = await fn(args, ctx);
367
424
  if (capped && result.ok && result.meta?.wrote) {
368
- await applyLimit.recordApplyEvent(args.path);
425
+ await applyLimit.recordApplyEvent(args.path, result.meta.added, result.meta.removed);
369
426
  }
370
427
  return result;
371
428
  } catch (e) {
package/lib/tui.mjs CHANGED
@@ -26,10 +26,12 @@ import Spinner from 'ink-spinner';
26
26
  import { createRequire } from 'module';
27
27
  import path from 'path';
28
28
  import os from 'os';
29
+ import fs from 'fs';
29
30
 
30
31
  const require = createRequire(import.meta.url);
31
32
  const auth = require('../lib/auth.js');
32
33
  const appConfig = require('../lib/config.js');
34
+ const skills = require('../lib/skills.js');
33
35
  const VERSION = require('../package.json').version;
34
36
  import {
35
37
  renderMarkdown, Panel, hexToAnsi, hexToAnsiBg, fillRow, SURFACE,
@@ -122,6 +124,8 @@ const COMMANDS = [
122
124
  { name: '/help', description: 'Show available commands' },
123
125
  { name: '/clear', description: 'Clear conversation and context' },
124
126
  { name: '/mode', description: 'Cycle Build → Plan → Ask' },
127
+ { name: '/provider', description: 'Show or switch model provider (BYOK: openrouter, groq, anthropic, openai)' },
128
+ { name: '/skill', description: 'List, install, or remove agent skills' },
125
129
  { name: '/keys', description: 'Show what your terminal sends for a key' },
126
130
  { name: '/quit', description: 'Exit Codeply' },
127
131
  ];
@@ -579,7 +583,7 @@ const App = () => {
579
583
  });
580
584
 
581
585
  // ── slash commands ──
582
- const runCommand = async (cmd) => {
586
+ const runCommand = async (cmd, args = '') => {
583
587
  if (cmd === '/login') {
584
588
  push({ role: 'system', content: 'Opening sign-in…' });
585
589
  try {
@@ -616,6 +620,94 @@ const App = () => {
616
620
  const next = (modeIndex + 1) % MODES.length;
617
621
  setModeIndex(next);
618
622
  push({ role: 'system', content: `Mode → ${MODES[next]} — ${MODE_HINT[MODES[next]]}` });
623
+ } else if (cmd === '/provider') {
624
+ const parts = args.trim().split(/\s+/).filter(Boolean);
625
+ const name = parts[0];
626
+
627
+ if (!name) {
628
+ const cfg = appConfig.getConfig();
629
+ let msg = `Provider: ${appConfig.describeProvider(cfg)}\n` +
630
+ `Providers: ${appConfig.PROVIDERS.join(', ')}\n` +
631
+ 'Set with /provider <name> [key] — e.g. /provider openrouter sk-or-v1-...';
632
+ if (cfg.provider === 'codeply') msg += `\n\n${appConfig.byokHint()}`;
633
+ push({ role: 'system', content: msg });
634
+ return;
635
+ }
636
+
637
+ if (!appConfig.PROVIDERS.includes(name)) {
638
+ push({ role: 'error', content: `Unknown provider "${name}". Use one of: ${appConfig.PROVIDERS.join(', ')}.` });
639
+ return;
640
+ }
641
+
642
+ const key = parts[1];
643
+ const patch = { provider: name };
644
+ if (key && (appConfig.BYOK_PROVIDERS.includes(name) || name === 'ollama')) {
645
+ patch[name] = { apiKey: key };
646
+ }
647
+
648
+ const saved = appConfig.saveConfig(patch);
649
+ if (!saved.ok) {
650
+ push({ role: 'error', content: `Could not save config: ${saved.error}` });
651
+ return;
652
+ }
653
+ push({ role: 'system', content: `Provider → ${appConfig.describeProvider(appConfig.getConfig())}` });
654
+ } else if (cmd === '/keys') {
655
+ push({ role: 'system', content: 'Press any key (or combination)…' });
656
+ setKeyProbe(true);
657
+ } else if (cmd === '/skill') {
658
+ const parts = args.trim().split(/\s+/).filter(Boolean);
659
+ const sub = parts[0];
660
+
661
+ if (!sub || sub === 'list') {
662
+ const list = skills.listSkills();
663
+ if (list.length === 0) {
664
+ push({ role: 'system', content: 'No skills available.' });
665
+ } else {
666
+ const lines = list.map((s) => `${s.name.padEnd(28)} [${s.source}] ${s.description}`);
667
+ push({ role: 'system', content: `Skills (${list.length})\n` + lines.join('\n') +
668
+ '\n\n/skill install <path-or-github-url> add one\n/skill remove <name> remove a user-installed one' });
669
+ }
670
+ return;
671
+ }
672
+
673
+ if (sub === 'install') {
674
+ const source = parts.slice(1).join(' ');
675
+ if (!source) { push({ role: 'error', content: 'Usage: /skill install <local-path-or-github-url>' }); return; }
676
+ push({ role: 'system', content: `Installing from ${source}…` });
677
+ try {
678
+ const isLocal = fs.existsSync(source);
679
+ const result = isLocal
680
+ ? skills.installFromLocalDir(path.resolve(cwd, source))
681
+ : await skills.installFromGitHub(source);
682
+ if (isLocal) {
683
+ if (!result.ok) push({ role: 'error', content: result.error });
684
+ else push({ role: 'system', content: `Installed "${result.name}" → ${result.dest}` });
685
+ } else if (!result.ok) {
686
+ push({ role: 'error', content: result.error || `No skills installed from ${source}.` });
687
+ } else {
688
+ const names = result.installed.map((i) => i.name).join(', ');
689
+ push({ role: 'system', content: `Installed ${result.installed.length} skill(s) from ${result.source}: ${names}` });
690
+ if (result.failed.length) {
691
+ push({ role: 'error', content: `${result.failed.length} failed: ${result.failed.map((f) => f.dir).join(', ')}` });
692
+ }
693
+ }
694
+ } catch (e) {
695
+ push({ role: 'error', content: `Install failed: ${e.message}` });
696
+ }
697
+ return;
698
+ }
699
+
700
+ if (sub === 'remove') {
701
+ const name = parts[1];
702
+ if (!name) { push({ role: 'error', content: 'Usage: /skill remove <name>' }); return; }
703
+ const result = skills.removeSkill(name);
704
+ push(result.ok
705
+ ? { role: 'system', content: `Removed "${name}".` }
706
+ : { role: 'error', content: result.error });
707
+ return;
708
+ }
709
+
710
+ push({ role: 'error', content: `Unknown /skill subcommand "${sub}". Use list, install, or remove.` });
619
711
  } else if (cmd === '/quit' || cmd === '/exit') {
620
712
  exit();
621
713
  } else {
@@ -639,7 +731,8 @@ const App = () => {
639
731
  setInput('');
640
732
 
641
733
  if (submitValue.startsWith('/')) {
642
- await runCommand(submitValue.split(/\s+/)[0].toLowerCase());
734
+ const [first, ...rest] = submitValue.split(/\s+/);
735
+ await runCommand(first.toLowerCase(), rest.join(' '));
643
736
  return;
644
737
  }
645
738
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codeply-cli",
3
- "version": "0.1.1",
3
+ "version": "0.2.0",
4
4
  "description": "Codeply from the terminal — an interactive AI coding agent, or a single apply <instruction> --file <path> command.",
5
5
  "main": "bin/codeply.js",
6
6
  "bin": {
@@ -8,7 +8,8 @@
8
8
  },
9
9
  "files": [
10
10
  "bin",
11
- "lib"
11
+ "lib",
12
+ "skills"
12
13
  ],
13
14
  "engines": {
14
15
  "node": ">=18"
@@ -0,0 +1,58 @@
1
+ # Where these skills came from
2
+
3
+ The skill library in this directory — all 281 of them — is vendored from
4
+ [affaan-m/ECC](https://github.com/affaan-m/ECC) ("Every Coding Copilot"), an
5
+ open-source collection of agent skills for Claude Code, Codex, and other
6
+ harnesses. It's their full `skills/` library, not a subset — every `SKILL.md`
7
+ and its `references/*.md` companions, unmodified.
8
+
9
+ Codeply loads its skill format directly from that project's convention: a
10
+ `SKILL.md` per skill with YAML frontmatter (`name`, `description`) and a
11
+ markdown body. See [lib/skills.js](../lib/skills.js) for the loader.
12
+
13
+ Only `SKILL.md` and `references/*.md` were vendored — each skill's
14
+ `agents/openai.yaml` (a tool schema for a different harness's native
15
+ function-calling) was left out, since Codeply calls skills through its own
16
+ text-based action protocol and has no use for it.
17
+
18
+ Not every one of the 281 is loaded into the agent's context by default —
19
+ `lib/skills.js` shows a curated "daily" subset (ECC's own `.agents/skills/`
20
+ curation of its full library) to keep the always-injected index small; the
21
+ rest are still fully installed and reachable with `list_skills <query>` or
22
+ `codeply skill show <name>`.
23
+
24
+ ## License
25
+
26
+ ECC is MIT-licensed. Its license and copyright notice, reproduced here as MIT
27
+ requires when redistributing:
28
+
29
+ ```
30
+ MIT License
31
+
32
+ Copyright (c) 2026 Affaan Mustafa
33
+
34
+ Permission is hereby granted, free of charge, to any person obtaining a copy
35
+ of this software and associated documentation files (the "Software"), to deal
36
+ in the Software without restriction, including without limitation the rights
37
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
38
+ copies of the Software, and to permit persons to whom the Software is
39
+ furnished to do so, subject to the following conditions:
40
+
41
+ The above copyright notice and this permission notice shall be included in all
42
+ copies or substantial portions of the Software.
43
+
44
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
45
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
46
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
47
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
48
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
49
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
50
+ SOFTWARE.
51
+ ```
52
+
53
+ ## Adding your own
54
+
55
+ Anything you drop in `~/.codeply/skills/<name>/SKILL.md` (same frontmatter
56
+ format) is picked up the same way, and takes priority over a bundled skill of
57
+ the same name. `codeply skill install <path-or-github-url>` automates that —
58
+ see `codeply skill --help`.