codeply-cli 0.1.1 → 0.3.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 (384) hide show
  1. package/THIRD_PARTY_NOTICES.md +63 -0
  2. package/bin/codeply.js +562 -259
  3. package/lib/agent.mjs +1721 -116
  4. package/lib/ai.js +1107 -184
  5. package/lib/apply-limit.js +33 -41
  6. package/lib/apply-patch.js +217 -0
  7. package/lib/arity-table.json +138 -0
  8. package/lib/arity.js +70 -0
  9. package/lib/auth.js +118 -112
  10. package/lib/commands.js +103 -0
  11. package/lib/config.js +392 -98
  12. package/lib/design-library/artifact-template.html +500 -0
  13. package/lib/design-library/assemble-artifact.mjs +21 -0
  14. package/lib/design-library/build.mjs +226 -0
  15. package/lib/design-library/cleanup.mjs +45 -0
  16. package/lib/design-library/index.json +22380 -0
  17. package/lib/design-library/make-table-data.mjs +39 -0
  18. package/lib/design-library/query.mjs +104 -0
  19. package/lib/diagnostics.js +192 -0
  20. package/lib/edit-engine.js +171 -14
  21. package/lib/github-agent.mjs +290 -0
  22. package/lib/history.js +75 -0
  23. package/lib/mcp.js +258 -0
  24. package/lib/native-tools.mjs +189 -0
  25. package/lib/oauth-connectors.js +478 -0
  26. package/lib/permissions.js +109 -0
  27. package/lib/plugins.js +353 -0
  28. package/lib/render-md.mjs +617 -617
  29. package/lib/server.mjs +457 -0
  30. package/lib/session-db.js +220 -0
  31. package/lib/share.js +168 -0
  32. package/lib/skills.js +416 -0
  33. package/lib/snapshot.js +146 -0
  34. package/lib/subagents.js +164 -0
  35. package/lib/terminal-hints.js +154 -0
  36. package/lib/textarea.mjs +258 -258
  37. package/lib/tools.mjs +1709 -73
  38. package/lib/tui.js +7 -7
  39. package/lib/tui.mjs +1086 -899
  40. package/lib/typecheck-worker.js +147 -0
  41. package/lib/web-tools.js +158 -0
  42. package/package.json +5 -3
  43. package/skills/SOURCE.md +58 -0
  44. package/skills/accessibility/SKILL.md +147 -0
  45. package/skills/agent-architecture-audit/SKILL.md +257 -0
  46. package/skills/agent-eval/SKILL.md +147 -0
  47. package/skills/agent-harness-construction/SKILL.md +74 -0
  48. package/skills/agent-introspection-debugging/SKILL.md +154 -0
  49. package/skills/agent-payment-x402/SKILL.md +225 -0
  50. package/skills/agent-self-evaluation/SKILL.md +182 -0
  51. package/skills/agent-self-evaluation/references/evaluation-criteria.md +71 -0
  52. package/skills/agent-self-evaluation/references/hook-integration.md +64 -0
  53. package/skills/agent-sort/SKILL.md +216 -0
  54. package/skills/agentic-engineering/SKILL.md +64 -0
  55. package/skills/agentic-os/SKILL.md +388 -0
  56. package/skills/ai-first-engineering/SKILL.md +52 -0
  57. package/skills/ai-regression-testing/SKILL.md +386 -0
  58. package/skills/android-clean-architecture/SKILL.md +340 -0
  59. package/skills/angular-developer/SKILL.md +155 -0
  60. package/skills/angular-developer/references/angular-animations.md +160 -0
  61. package/skills/angular-developer/references/angular-aria.md +410 -0
  62. package/skills/angular-developer/references/cli.md +86 -0
  63. package/skills/angular-developer/references/component-harnesses.md +59 -0
  64. package/skills/angular-developer/references/component-styling.md +91 -0
  65. package/skills/angular-developer/references/components.md +117 -0
  66. package/skills/angular-developer/references/creating-services.md +97 -0
  67. package/skills/angular-developer/references/data-resolvers.md +69 -0
  68. package/skills/angular-developer/references/define-routes.md +67 -0
  69. package/skills/angular-developer/references/defining-providers.md +72 -0
  70. package/skills/angular-developer/references/di-fundamentals.md +120 -0
  71. package/skills/angular-developer/references/e2e-testing.md +56 -0
  72. package/skills/angular-developer/references/effects.md +83 -0
  73. package/skills/angular-developer/references/hierarchical-injectors.md +43 -0
  74. package/skills/angular-developer/references/host-elements.md +80 -0
  75. package/skills/angular-developer/references/injection-context.md +63 -0
  76. package/skills/angular-developer/references/inputs.md +101 -0
  77. package/skills/angular-developer/references/linked-signal.md +59 -0
  78. package/skills/angular-developer/references/loading-strategies.md +61 -0
  79. package/skills/angular-developer/references/mcp.md +108 -0
  80. package/skills/angular-developer/references/navigate-to-routes.md +69 -0
  81. package/skills/angular-developer/references/outputs.md +86 -0
  82. package/skills/angular-developer/references/reactive-forms.md +122 -0
  83. package/skills/angular-developer/references/rendering-strategies.md +44 -0
  84. package/skills/angular-developer/references/resource.md +77 -0
  85. package/skills/angular-developer/references/route-animations.md +56 -0
  86. package/skills/angular-developer/references/route-guards.md +52 -0
  87. package/skills/angular-developer/references/router-lifecycle.md +45 -0
  88. package/skills/angular-developer/references/router-testing.md +87 -0
  89. package/skills/angular-developer/references/show-routes-with-outlets.md +68 -0
  90. package/skills/angular-developer/references/signal-forms.md +795 -0
  91. package/skills/angular-developer/references/signals-overview.md +94 -0
  92. package/skills/angular-developer/references/tailwind-css.md +69 -0
  93. package/skills/angular-developer/references/template-driven-forms.md +114 -0
  94. package/skills/angular-developer/references/testing-fundamentals.md +65 -0
  95. package/skills/api-connector-builder/SKILL.md +121 -0
  96. package/skills/api-design/SKILL.md +524 -0
  97. package/skills/architecture-decision-records/SKILL.md +180 -0
  98. package/skills/article-writing/SKILL.md +80 -0
  99. package/skills/automation-audit-ops/SKILL.md +143 -0
  100. package/skills/autonomous-agent-harness/SKILL.md +274 -0
  101. package/skills/autonomous-loops/SKILL.md +611 -0
  102. package/skills/backend-patterns/SKILL.md +562 -0
  103. package/skills/benchmark/SKILL.md +95 -0
  104. package/skills/benchmark-methodology/SKILL.md +191 -0
  105. package/skills/benchmark-optimization-loop/SKILL.md +71 -0
  106. package/skills/blender-motion-state-inspection/SKILL.md +165 -0
  107. package/skills/blueprint/SKILL.md +106 -0
  108. package/skills/brand-discovery/SKILL.md +145 -0
  109. package/skills/brand-discovery/references/10_purpose-why.md +40 -0
  110. package/skills/brand-discovery/references/20_positioning.md +44 -0
  111. package/skills/brand-discovery/references/30_audience-niche.md +52 -0
  112. package/skills/brand-discovery/references/40_personality-archetype.md +57 -0
  113. package/skills/brand-discovery/references/50_voice-tone.md +59 -0
  114. package/skills/brand-discovery/references/60_narrative-story.md +50 -0
  115. package/skills/brand-discovery/references/70_founder-tension.md +49 -0
  116. package/skills/brand-discovery/references/90_SYNTHESIS.md +133 -0
  117. package/skills/brand-voice/SKILL.md +98 -0
  118. package/skills/brand-voice/references/voice-profile-schema.md +55 -0
  119. package/skills/browser-qa/SKILL.md +105 -0
  120. package/skills/bun-runtime/SKILL.md +85 -0
  121. package/skills/canary-watch/SKILL.md +108 -0
  122. package/skills/carrier-relationship-management/SKILL.md +212 -0
  123. package/skills/cisco-ios-patterns/SKILL.md +164 -0
  124. package/skills/ck/SKILL.md +148 -0
  125. package/skills/claude-devfleet/SKILL.md +112 -0
  126. package/skills/click-path-audit/SKILL.md +245 -0
  127. package/skills/clickhouse-io/SKILL.md +445 -0
  128. package/skills/code-tour/SKILL.md +254 -0
  129. package/skills/codebase-onboarding/SKILL.md +234 -0
  130. package/skills/codehealth-mcp/SKILL.md +167 -0
  131. package/skills/coding-standards/SKILL.md +551 -0
  132. package/skills/competitive-platform-analysis/SKILL.md +214 -0
  133. package/skills/competitive-report-structure/SKILL.md +162 -0
  134. package/skills/compose-multiplatform-patterns/SKILL.md +300 -0
  135. package/skills/config-gc/SKILL.md +120 -0
  136. package/skills/configure-ecc/SKILL.md +385 -0
  137. package/skills/connections-optimizer/SKILL.md +190 -0
  138. package/skills/content-engine/SKILL.md +132 -0
  139. package/skills/content-hash-cache-pattern/SKILL.md +162 -0
  140. package/skills/context-budget/SKILL.md +136 -0
  141. package/skills/continuous-agent-loop/SKILL.md +46 -0
  142. package/skills/continuous-learning/SKILL.md +132 -0
  143. package/skills/continuous-learning-v2/SKILL.md +361 -0
  144. package/skills/contract-first/SKILL.md +287 -0
  145. package/skills/cost-aware-llm-pipeline/SKILL.md +184 -0
  146. package/skills/cost-tracking/SKILL.md +97 -0
  147. package/skills/council/SKILL.md +204 -0
  148. package/skills/cpp-coding-standards/SKILL.md +724 -0
  149. package/skills/cpp-testing/SKILL.md +325 -0
  150. package/skills/crosspost/SKILL.md +112 -0
  151. package/skills/csharp-testing/SKILL.md +322 -0
  152. package/skills/customer-billing-ops/SKILL.md +141 -0
  153. package/skills/customs-trade-compliance/SKILL.md +263 -0
  154. package/skills/dart-flutter-patterns/SKILL.md +564 -0
  155. package/skills/dashboard-builder/SKILL.md +109 -0
  156. package/skills/data-scraper-agent/SKILL.md +765 -0
  157. package/skills/data-throughput-accelerator/SKILL.md +74 -0
  158. package/skills/database-migrations/SKILL.md +430 -0
  159. package/skills/deep-research/SKILL.md +160 -0
  160. package/skills/defi-amm-security/SKILL.md +167 -0
  161. package/skills/delivery-gate/SKILL.md +126 -0
  162. package/skills/deployment-patterns/SKILL.md +428 -0
  163. package/skills/design-system/SKILL.md +83 -0
  164. package/skills/django-celery/SKILL.md +458 -0
  165. package/skills/django-patterns/SKILL.md +735 -0
  166. package/skills/django-security/SKILL.md +644 -0
  167. package/skills/django-tdd/SKILL.md +730 -0
  168. package/skills/django-verification/SKILL.md +470 -0
  169. package/skills/dmux-workflows/SKILL.md +192 -0
  170. package/skills/docker-patterns/SKILL.md +365 -0
  171. package/skills/documentation-lookup/SKILL.md +91 -0
  172. package/skills/dotnet-patterns/SKILL.md +322 -0
  173. package/skills/dynamic-workflow-mode/SKILL.md +124 -0
  174. package/skills/e2e-testing/SKILL.md +327 -0
  175. package/skills/ecc-guide/SKILL.md +190 -0
  176. package/skills/ecc-recipes/SKILL.md +149 -0
  177. package/skills/ecc-tools-cost-audit/SKILL.md +161 -0
  178. package/skills/email-ops/SKILL.md +122 -0
  179. package/skills/energy-procurement/SKILL.md +228 -0
  180. package/skills/enterprise-agent-ops/SKILL.md +51 -0
  181. package/skills/error-handling/SKILL.md +377 -0
  182. package/skills/eval-harness/SKILL.md +271 -0
  183. package/skills/evm-token-decimals/SKILL.md +131 -0
  184. package/skills/exa-search/SKILL.md +108 -0
  185. package/skills/fal-ai-media/SKILL.md +289 -0
  186. package/skills/fastapi-patterns/SKILL.md +514 -0
  187. package/skills/finance-billing-ops/SKILL.md +128 -0
  188. package/skills/flox-environments/SKILL.md +497 -0
  189. package/skills/flutter-dart-code-review/SKILL.md +436 -0
  190. package/skills/foundation-models-on-device/SKILL.md +243 -0
  191. package/skills/frontend-a11y/SKILL.md +446 -0
  192. package/skills/frontend-design-direction/SKILL.md +93 -0
  193. package/skills/frontend-patterns/SKILL.md +657 -0
  194. package/skills/frontend-slides/SKILL.md +185 -0
  195. package/skills/fsharp-testing/SKILL.md +281 -0
  196. package/skills/gan-style-harness/SKILL.md +279 -0
  197. package/skills/gateguard/SKILL.md +133 -0
  198. package/skills/generating-python-installer/SKILL.md +820 -0
  199. package/skills/git-workflow/SKILL.md +716 -0
  200. package/skills/github-ops/SKILL.md +145 -0
  201. package/skills/golang-patterns/SKILL.md +676 -0
  202. package/skills/golang-testing/SKILL.md +721 -0
  203. package/skills/google-workspace-ops/SKILL.md +96 -0
  204. package/skills/growth-log/SKILL.md +128 -0
  205. package/skills/healthcare-cdss-patterns/SKILL.md +246 -0
  206. package/skills/healthcare-emr-patterns/SKILL.md +160 -0
  207. package/skills/healthcare-eval-harness/SKILL.md +208 -0
  208. package/skills/healthcare-phi-compliance/SKILL.md +146 -0
  209. package/skills/hermes-imports/SKILL.md +89 -0
  210. package/skills/hexagonal-architecture/SKILL.md +277 -0
  211. package/skills/hipaa-compliance/SKILL.md +79 -0
  212. package/skills/homelab-network-readiness/SKILL.md +170 -0
  213. package/skills/homelab-network-setup/SKILL.md +130 -0
  214. package/skills/homelab-pihole-dns/SKILL.md +275 -0
  215. package/skills/homelab-vlan-segmentation/SKILL.md +312 -0
  216. package/skills/homelab-wireguard-vpn/SKILL.md +306 -0
  217. package/skills/hookify-rules/SKILL.md +128 -0
  218. package/skills/inherit-legacy-style/SKILL.md +157 -0
  219. package/skills/intent-driven-development/SKILL.md +360 -0
  220. package/skills/inventory-demand-planning/SKILL.md +247 -0
  221. package/skills/investor-materials/SKILL.md +97 -0
  222. package/skills/investor-outreach/SKILL.md +92 -0
  223. package/skills/ios-icon-gen/SKILL.md +158 -0
  224. package/skills/iterative-retrieval/SKILL.md +212 -0
  225. package/skills/ito-basket-compare/SKILL.md +64 -0
  226. package/skills/ito-compute/SKILL.md +129 -0
  227. package/skills/ito-data-atlas-agent/SKILL.md +64 -0
  228. package/skills/ito-market-intelligence/SKILL.md +61 -0
  229. package/skills/ito-trade-planner/SKILL.md +68 -0
  230. package/skills/java-coding-standards/SKILL.md +384 -0
  231. package/skills/jira-integration/SKILL.md +303 -0
  232. package/skills/jpa-patterns/SKILL.md +152 -0
  233. package/skills/knowledge-ops/SKILL.md +155 -0
  234. package/skills/kotlin-coroutines-flows/SKILL.md +285 -0
  235. package/skills/kotlin-exposed-patterns/SKILL.md +720 -0
  236. package/skills/kotlin-ktor-patterns/SKILL.md +690 -0
  237. package/skills/kotlin-patterns/SKILL.md +712 -0
  238. package/skills/kotlin-testing/SKILL.md +825 -0
  239. package/skills/kubernetes-patterns/SKILL.md +756 -0
  240. package/skills/laravel-patterns/SKILL.md +416 -0
  241. package/skills/laravel-plugin-discovery/SKILL.md +230 -0
  242. package/skills/laravel-security/SKILL.md +948 -0
  243. package/skills/laravel-tdd/SKILL.md +675 -0
  244. package/skills/laravel-verification/SKILL.md +180 -0
  245. package/skills/latency-critical-systems/SKILL.md +75 -0
  246. package/skills/lead-intelligence/SKILL.md +322 -0
  247. package/skills/liquid-glass-design/SKILL.md +279 -0
  248. package/skills/llm-trading-agent-security/SKILL.md +147 -0
  249. package/skills/logistics-exception-management/SKILL.md +222 -0
  250. package/skills/loop-design-check/SKILL.md +143 -0
  251. package/skills/mailtrap-email-integration/SKILL.md +77 -0
  252. package/skills/make-interfaces-feel-better/SKILL.md +152 -0
  253. package/skills/manim-video/SKILL.md +90 -0
  254. package/skills/market-research/SKILL.md +76 -0
  255. package/skills/marketing-campaign/SKILL.md +114 -0
  256. package/skills/mcp-server-patterns/SKILL.md +70 -0
  257. package/skills/messages-ops/SKILL.md +105 -0
  258. package/skills/ml-adoption-playbook/SKILL.md +57 -0
  259. package/skills/mle-workflow/SKILL.md +348 -0
  260. package/skills/motion-advanced/SKILL.md +596 -0
  261. package/skills/motion-foundations/SKILL.md +299 -0
  262. package/skills/motion-patterns/SKILL.md +434 -0
  263. package/skills/motion-ui/SKILL.md +576 -0
  264. package/skills/mysql-patterns/SKILL.md +413 -0
  265. package/skills/nanoclaw-repl/SKILL.md +34 -0
  266. package/skills/nestjs-patterns/SKILL.md +231 -0
  267. package/skills/netmiko-ssh-automation/SKILL.md +174 -0
  268. package/skills/network-bgp-diagnostics/SKILL.md +168 -0
  269. package/skills/network-config-validation/SKILL.md +211 -0
  270. package/skills/network-interface-health/SKILL.md +153 -0
  271. package/skills/nextjs-turbopack/SKILL.md +58 -0
  272. package/skills/nodejs-keccak256/SKILL.md +103 -0
  273. package/skills/nutrient-document-processing/SKILL.md +168 -0
  274. package/skills/nuxt4-patterns/SKILL.md +101 -0
  275. package/skills/openclaw-persona-forge/SKILL.md +289 -0
  276. package/skills/openclaw-persona-forge/references/avatar-style.md +124 -0
  277. package/skills/openclaw-persona-forge/references/boundary-rules.md +53 -0
  278. package/skills/openclaw-persona-forge/references/error-handling.md +53 -0
  279. package/skills/openclaw-persona-forge/references/identity-tension.md +48 -0
  280. package/skills/openclaw-persona-forge/references/naming-system.md +39 -0
  281. package/skills/openclaw-persona-forge/references/output-template.md +166 -0
  282. package/skills/opensource-pipeline/SKILL.md +256 -0
  283. package/skills/orch-add-feature/SKILL.md +45 -0
  284. package/skills/orch-build-mvp/SKILL.md +49 -0
  285. package/skills/orch-change-feature/SKILL.md +43 -0
  286. package/skills/orch-fix-defect/SKILL.md +43 -0
  287. package/skills/orch-pipeline/SKILL.md +121 -0
  288. package/skills/orch-refine-code/SKILL.md +44 -0
  289. package/skills/parallel-execution-optimizer/SKILL.md +74 -0
  290. package/skills/perl-patterns/SKILL.md +505 -0
  291. package/skills/perl-security/SKILL.md +504 -0
  292. package/skills/perl-testing/SKILL.md +476 -0
  293. package/skills/plan-canvas/SKILL.md +153 -0
  294. package/skills/plan-orchestrate/SKILL.md +263 -0
  295. package/skills/plankton-code-quality/SKILL.md +237 -0
  296. package/skills/postgres-patterns/SKILL.md +148 -0
  297. package/skills/prediction-market-oracle-research/SKILL.md +64 -0
  298. package/skills/prediction-market-risk-review/SKILL.md +61 -0
  299. package/skills/premium-web-design/SKILL.md +154 -0
  300. package/skills/prisma-patterns/SKILL.md +401 -0
  301. package/skills/product-capability/SKILL.md +142 -0
  302. package/skills/product-lens/SKILL.md +93 -0
  303. package/skills/production-audit/SKILL.md +207 -0
  304. package/skills/production-scheduling/SKILL.md +238 -0
  305. package/skills/project-flow-ops/SKILL.md +112 -0
  306. package/skills/prompt-optimizer/SKILL.md +398 -0
  307. package/skills/python-patterns/SKILL.md +751 -0
  308. package/skills/python-testing/SKILL.md +817 -0
  309. package/skills/pytorch-patterns/SKILL.md +397 -0
  310. package/skills/quality-nonconformance/SKILL.md +260 -0
  311. package/skills/quarkus-patterns/SKILL.md +723 -0
  312. package/skills/quarkus-security/SKILL.md +468 -0
  313. package/skills/quarkus-tdd/SKILL.md +812 -0
  314. package/skills/quarkus-verification/SKILL.md +480 -0
  315. package/skills/ralphinho-rfc-pipeline/SKILL.md +68 -0
  316. package/skills/react-native-patterns/SKILL.md +326 -0
  317. package/skills/react-patterns/SKILL.md +342 -0
  318. package/skills/react-performance/SKILL.md +575 -0
  319. package/skills/react-testing/SKILL.md +424 -0
  320. package/skills/recsys-pipeline-architect/SKILL.md +115 -0
  321. package/skills/recursive-decision-ledger/SKILL.md +81 -0
  322. package/skills/redis-patterns/SKILL.md +404 -0
  323. package/skills/regex-vs-llm-structured-text/SKILL.md +221 -0
  324. package/skills/remotion-video-creation/SKILL.md +43 -0
  325. package/skills/repo-scan/SKILL.md +79 -0
  326. package/skills/research-ops/SKILL.md +113 -0
  327. package/skills/returns-reverse-logistics/SKILL.md +240 -0
  328. package/skills/rules-distill/SKILL.md +265 -0
  329. package/skills/rust-patterns/SKILL.md +500 -0
  330. package/skills/rust-testing/SKILL.md +501 -0
  331. package/skills/safety-guard/SKILL.md +76 -0
  332. package/skills/santa-method/SKILL.md +307 -0
  333. package/skills/scientific-db-pubmed-database/SKILL.md +176 -0
  334. package/skills/scientific-db-uspto-database/SKILL.md +178 -0
  335. package/skills/scientific-pkg-gget/SKILL.md +167 -0
  336. package/skills/scientific-thinking-literature-review/SKILL.md +193 -0
  337. package/skills/scientific-thinking-scholar-evaluation/SKILL.md +161 -0
  338. package/skills/search-first/SKILL.md +183 -0
  339. package/skills/security-bounty-hunter/SKILL.md +100 -0
  340. package/skills/security-review/SKILL.md +504 -0
  341. package/skills/security-scan/SKILL.md +166 -0
  342. package/skills/seo/SKILL.md +155 -0
  343. package/skills/skill-comply/SKILL.md +59 -0
  344. package/skills/skill-scout/SKILL.md +141 -0
  345. package/skills/skill-stocktake/SKILL.md +195 -0
  346. package/skills/social-graph-ranker/SKILL.md +155 -0
  347. package/skills/social-publisher/SKILL.md +130 -0
  348. package/skills/springboot-patterns/SKILL.md +315 -0
  349. package/skills/springboot-security/SKILL.md +273 -0
  350. package/skills/springboot-tdd/SKILL.md +159 -0
  351. package/skills/springboot-verification/SKILL.md +232 -0
  352. package/skills/strategic-compact/SKILL.md +142 -0
  353. package/skills/swift-actor-persistence/SKILL.md +144 -0
  354. package/skills/swift-concurrency-6-2/SKILL.md +216 -0
  355. package/skills/swift-protocol-di-testing/SKILL.md +191 -0
  356. package/skills/swiftui-patterns/SKILL.md +259 -0
  357. package/skills/taste/SKILL.md +264 -0
  358. package/skills/taste/references/genre-taxonomy.md +87 -0
  359. package/skills/tdd-workflow/SKILL.md +583 -0
  360. package/skills/team-agent-orchestration/SKILL.md +111 -0
  361. package/skills/team-builder/SKILL.md +169 -0
  362. package/skills/terminal-ops/SKILL.md +110 -0
  363. package/skills/tinystruct-patterns/SKILL.md +279 -0
  364. package/skills/tinystruct-patterns/references/architecture.md +90 -0
  365. package/skills/tinystruct-patterns/references/data-handling.md +60 -0
  366. package/skills/tinystruct-patterns/references/database.md +99 -0
  367. package/skills/tinystruct-patterns/references/routing.md +64 -0
  368. package/skills/tinystruct-patterns/references/system-usage.md +97 -0
  369. package/skills/tinystruct-patterns/references/testing.md +72 -0
  370. package/skills/token-budget-advisor/SKILL.md +134 -0
  371. package/skills/ui-demo/SKILL.md +466 -0
  372. package/skills/ui-to-vue/SKILL.md +135 -0
  373. package/skills/uncloud/SKILL.md +344 -0
  374. package/skills/unified-memory/SKILL.md +170 -0
  375. package/skills/unified-notifications-ops/SKILL.md +188 -0
  376. package/skills/verification-loop/SKILL.md +129 -0
  377. package/skills/video-editing/SKILL.md +311 -0
  378. package/skills/videodb/SKILL.md +375 -0
  379. package/skills/visa-doc-translate/SKILL.md +117 -0
  380. package/skills/vite-patterns/SKILL.md +450 -0
  381. package/skills/vue-patterns/SKILL.md +471 -0
  382. package/skills/windows-desktop-e2e/SKILL.md +888 -0
  383. package/skills/workspace-surface-audit/SKILL.md +126 -0
  384. package/skills/x-api/SKILL.md +235 -0
@@ -0,0 +1,220 @@
1
+ /**
2
+ * SQLite-backed session store. Replaces rewriting one big JSON file on every
3
+ * save: each session and each message is a row, and a save only writes the rows
4
+ * whose content changed. Messages are searchable across every session.
5
+ *
6
+ * Driver order (first that loads wins), all with the same sync API:
7
+ * 1. node:sqlite (Node 22.5+, built in, nothing to install)
8
+ * 2. better-sqlite3 (optional dependency; Electron 29 needs this one)
9
+ * When neither loads, openSessionDb() returns null and the caller keeps its
10
+ * JSON file, so a machine without SQLite never loses anything.
11
+ *
12
+ * Shape it stores (same as the old JSON store):
13
+ * { sessions: [{ id, title, cwd, messages: [...], alwaysAllowed, createdAt, updatedAt, ...extra }],
14
+ * <any other top-level key>: value }
15
+ */
16
+ const fs = require('fs');
17
+ const path = require('path');
18
+
19
+ const SCHEMA_VERSION = 1;
20
+
21
+ function loadDriver(modulePaths) {
22
+ try {
23
+ const { DatabaseSync } = require('node:sqlite');
24
+ if (DatabaseSync) return { kind: 'node:sqlite', open: (f) => new DatabaseSync(f) };
25
+ } catch {}
26
+ try {
27
+ let mod;
28
+ try { mod = require('better-sqlite3'); }
29
+ catch (e) {
30
+ if (!modulePaths || !modulePaths.length) throw e;
31
+ mod = require(require.resolve('better-sqlite3', { paths: modulePaths }));
32
+ }
33
+ return { kind: 'better-sqlite3', open: (f) => new mod(f) };
34
+ } catch {}
35
+ return null;
36
+ }
37
+
38
+ const CORE_SESSION_KEYS = new Set(['id', 'title', 'cwd', 'messages', 'alwaysAllowed', 'createdAt', 'updatedAt']);
39
+
40
+ /**
41
+ * @param {string} file database path (created if missing)
42
+ * @param {{ modulePaths?: string[], driver?: 'node:sqlite'|'better-sqlite3' }} [opts]
43
+ * @returns {null | { kind:string, file:string, isEmpty():boolean, load():object, save(store:object):void, search(q:string, limit?:number):object[], close():void }}
44
+ */
45
+ function openSessionDb(file, opts = {}) {
46
+ const driver = loadDriver(opts.modulePaths);
47
+ if (!driver || (opts.driver && driver.kind !== opts.driver)) return null;
48
+ let db;
49
+ try {
50
+ fs.mkdirSync(path.dirname(file), { recursive: true });
51
+ db = driver.open(file);
52
+ db.exec('PRAGMA journal_mode = WAL');
53
+ db.exec('PRAGMA synchronous = NORMAL');
54
+ db.exec('PRAGMA foreign_keys = ON');
55
+ db.exec(`
56
+ CREATE TABLE IF NOT EXISTS meta (key TEXT PRIMARY KEY, value TEXT NOT NULL);
57
+ CREATE TABLE IF NOT EXISTS kv (key TEXT PRIMARY KEY, value TEXT NOT NULL);
58
+ CREATE TABLE IF NOT EXISTS sessions (
59
+ id TEXT PRIMARY KEY,
60
+ title TEXT,
61
+ cwd TEXT,
62
+ always_allowed TEXT,
63
+ extra TEXT,
64
+ created_at INTEGER,
65
+ updated_at INTEGER,
66
+ position INTEGER NOT NULL DEFAULT 0
67
+ );
68
+ CREATE TABLE IF NOT EXISTS messages (
69
+ session_id TEXT NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,
70
+ idx INTEGER NOT NULL,
71
+ kind TEXT,
72
+ body TEXT,
73
+ data TEXT NOT NULL,
74
+ PRIMARY KEY (session_id, idx)
75
+ );
76
+ CREATE INDEX IF NOT EXISTS sessions_updated ON sessions(updated_at DESC);
77
+ `);
78
+ db.prepare('INSERT OR IGNORE INTO meta (key, value) VALUES (?, ?)').run('schema', String(SCHEMA_VERSION));
79
+ } catch {
80
+ try { db && db.close(); } catch {}
81
+ return null;
82
+ }
83
+
84
+ const q = {
85
+ countSessions: db.prepare('SELECT COUNT(*) AS n FROM sessions'),
86
+ countKv: db.prepare('SELECT COUNT(*) AS n FROM kv'),
87
+ allSessions: db.prepare('SELECT * FROM sessions ORDER BY position ASC'),
88
+ sessionMessages: db.prepare('SELECT data FROM messages WHERE session_id = ? ORDER BY idx ASC'),
89
+ allKv: db.prepare('SELECT key, value FROM kv'),
90
+ upsertSession: db.prepare(`INSERT INTO sessions (id, title, cwd, always_allowed, extra, created_at, updated_at, position)
91
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?)
92
+ ON CONFLICT(id) DO UPDATE SET title=excluded.title, cwd=excluded.cwd, always_allowed=excluded.always_allowed,
93
+ extra=excluded.extra, created_at=excluded.created_at, updated_at=excluded.updated_at, position=excluded.position`),
94
+ deleteSession: db.prepare('DELETE FROM sessions WHERE id = ?'),
95
+ listSessionIds: db.prepare('SELECT id FROM sessions'),
96
+ upsertMessage: db.prepare(`INSERT INTO messages (session_id, idx, kind, body, data) VALUES (?, ?, ?, ?, ?)
97
+ ON CONFLICT(session_id, idx) DO UPDATE SET kind=excluded.kind, body=excluded.body, data=excluded.data`),
98
+ trimMessages: db.prepare('DELETE FROM messages WHERE session_id = ? AND idx >= ?'),
99
+ upsertKv: db.prepare('INSERT INTO kv (key, value) VALUES (?, ?) ON CONFLICT(key) DO UPDATE SET value=excluded.value'),
100
+ deleteKv: db.prepare('DELETE FROM kv WHERE key = ?'),
101
+ search: db.prepare(`SELECT m.session_id AS sessionId, m.idx AS idx, m.kind AS kind, m.body AS body, s.title AS title, s.cwd AS cwd, s.updated_at AS updatedAt
102
+ FROM messages m JOIN sessions s ON s.id = m.session_id
103
+ WHERE m.body LIKE ? ESCAPE '\\' ORDER BY s.updated_at DESC, m.idx ASC LIMIT ?`),
104
+ };
105
+
106
+ // What the database already holds, so a save only writes what changed.
107
+ const seenSession = new Map(); // id -> serialized session header
108
+ const seenMessages = new Map(); // id -> string[] (serialized messages)
109
+ const seenKv = new Map(); // key -> serialized value
110
+
111
+ const tx = (fn) => {
112
+ db.exec('BEGIN');
113
+ try { fn(); db.exec('COMMIT'); } catch (e) { try { db.exec('ROLLBACK'); } catch {} throw e; }
114
+ };
115
+
116
+ const bodyOf = (m) => {
117
+ if (!m || typeof m !== 'object') return '';
118
+ const t = m.text != null ? m.text : (m.label != null ? m.label : '');
119
+ return typeof t === 'string' ? t.slice(0, 20000) : '';
120
+ };
121
+
122
+ return {
123
+ kind: driver.kind,
124
+ file,
125
+
126
+ isEmpty() {
127
+ return q.countSessions.get().n === 0 && q.countKv.get().n === 0;
128
+ },
129
+
130
+ load() {
131
+ const store = {};
132
+ for (const row of q.allKv.all()) {
133
+ try { store[row.key] = JSON.parse(row.value); seenKv.set(row.key, row.value); } catch {}
134
+ }
135
+ store.sessions = q.allSessions.all().map((r, position) => {
136
+ const messages = q.sessionMessages.all(r.id).map((m) => { try { return JSON.parse(m.data); } catch { return null; } }).filter(Boolean);
137
+ let extra = {};
138
+ try { extra = r.extra ? JSON.parse(r.extra) : {}; } catch {}
139
+ let alwaysAllowed = [];
140
+ try { alwaysAllowed = r.always_allowed ? JSON.parse(r.always_allowed) : []; } catch {}
141
+ const s = { ...extra, id: r.id, title: r.title, cwd: r.cwd, messages, alwaysAllowed, createdAt: r.created_at, updatedAt: r.updated_at };
142
+ seenSession.set(r.id, headerOf(s, position));
143
+ seenMessages.set(r.id, messages.map((m) => JSON.stringify(m)));
144
+ return s;
145
+ });
146
+ return store;
147
+ },
148
+
149
+ save(store) {
150
+ const sessions = Array.isArray(store.sessions) ? store.sessions : [];
151
+ tx(() => {
152
+ const keep = new Set();
153
+ sessions.forEach((s, position) => {
154
+ if (!s || !s.id) return;
155
+ keep.add(s.id);
156
+ const header = headerOf(s, position);
157
+ if (seenSession.get(s.id) !== header) {
158
+ const extra = {};
159
+ for (const k of Object.keys(s)) if (!CORE_SESSION_KEYS.has(k)) extra[k] = s[k];
160
+ q.upsertSession.run(
161
+ s.id, s.title == null ? null : String(s.title), s.cwd == null ? null : String(s.cwd),
162
+ JSON.stringify(s.alwaysAllowed || []), JSON.stringify(extra),
163
+ Number(s.createdAt) || Date.now(), Number(s.updatedAt) || Date.now(), position,
164
+ );
165
+ seenSession.set(s.id, header);
166
+ }
167
+ const msgs = Array.isArray(s.messages) ? s.messages : [];
168
+ const prev = seenMessages.get(s.id) || [];
169
+ const next = new Array(msgs.length);
170
+ for (let i = 0; i < msgs.length; i++) {
171
+ const ser = JSON.stringify(msgs[i]);
172
+ next[i] = ser;
173
+ if (prev[i] !== ser) q.upsertMessage.run(s.id, i, msgs[i] && msgs[i].kind ? String(msgs[i].kind) : null, bodyOf(msgs[i]), ser);
174
+ }
175
+ if (prev.length > msgs.length) q.trimMessages.run(s.id, msgs.length);
176
+ seenMessages.set(s.id, next);
177
+ });
178
+ for (const { id } of q.listSessionIds.all()) {
179
+ if (!keep.has(id)) { q.deleteSession.run(id); seenSession.delete(id); seenMessages.delete(id); }
180
+ }
181
+ const keys = new Set();
182
+ for (const k of Object.keys(store)) {
183
+ if (k === 'sessions') continue;
184
+ keys.add(k);
185
+ if (store[k] === undefined) continue;
186
+ const ser = JSON.stringify(store[k]);
187
+ if (seenKv.get(k) !== ser) { q.upsertKv.run(k, ser); seenKv.set(k, ser); }
188
+ }
189
+ for (const k of [...seenKv.keys()]) if (!keys.has(k)) { q.deleteKv.run(k); seenKv.delete(k); }
190
+ });
191
+ },
192
+
193
+ search(query, limit = 50) {
194
+ const term = String(query || '').trim();
195
+ if (!term) return [];
196
+ const like = `%${term.replace(/[\\%_]/g, (c) => `\\${c}`)}%`;
197
+ return q.search.all(like, Math.min(Math.max(limit | 0, 1), 200)).map((r) => ({
198
+ ...r, snippet: snippetAround(r.body, term),
199
+ }));
200
+ },
201
+
202
+ close() { try { db.exec('PRAGMA wal_checkpoint(TRUNCATE)'); } catch {} try { db.close(); } catch {} },
203
+ };
204
+ }
205
+
206
+ function headerOf(s, position) {
207
+ const extra = {};
208
+ for (const k of Object.keys(s)) if (!CORE_SESSION_KEYS.has(k)) extra[k] = s[k];
209
+ return JSON.stringify([s.title, s.cwd, s.alwaysAllowed || [], extra, Number(s.createdAt) || 0, Number(s.updatedAt) || 0, position]);
210
+ }
211
+
212
+ function snippetAround(text, term) {
213
+ const t = String(text || '');
214
+ const at = t.toLowerCase().indexOf(term.toLowerCase());
215
+ if (at < 0) return t.slice(0, 120);
216
+ const from = Math.max(0, at - 50);
217
+ return `${from > 0 ? '...' : ''}${t.slice(from, at + term.length + 70).replace(/\s+/g, ' ')}${at + term.length + 70 < t.length ? '...' : ''}`;
218
+ }
219
+
220
+ module.exports = { openSessionDb };
package/lib/share.js ADDED
@@ -0,0 +1,168 @@
1
+ /**
2
+ * Turn a chat into something you can hand to someone else: a Markdown file, a
3
+ * self-contained HTML page, or a secret GitHub gist. Everything is cleaned
4
+ * first: keys and tokens are masked and your home folder and project path are
5
+ * replaced, so a shared chat does not leak the machine it came from.
6
+ */
7
+ const os = require('os');
8
+ const path = require('path');
9
+
10
+ const SECRET_PATTERNS = [
11
+ /-----BEGIN [A-Z ]*PRIVATE KEY-----[\s\S]*?-----END [A-Z ]*PRIVATE KEY-----/g,
12
+ /\b(?:sk|pk|rk)-[A-Za-z0-9_-]{16,}/g,
13
+ /\bsk-ant-[A-Za-z0-9_-]{16,}/g,
14
+ /\bgh[pousr]_[A-Za-z0-9]{30,}/g,
15
+ /\bgithub_pat_[A-Za-z0-9_]{30,}/g,
16
+ /\bxox[abprs]-[A-Za-z0-9-]{10,}/g,
17
+ /\bAKIA[0-9A-Z]{16}\b/g,
18
+ /\bAIza[0-9A-Za-z_-]{30,}/g,
19
+ /\beyJ[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}/g,
20
+ /(\bBearer\s+)[A-Za-z0-9._~+/=-]{20,}/gi,
21
+ /((?:api[_-]?key|secret|token|password|passwd)["']?\s*[:=]\s*["']?)[^\s"',;]{8,}/gi,
22
+ ];
23
+
24
+ function escapeRe(s) { return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); }
25
+
26
+ /** Cleans one string for sharing. `cwd` is the project folder, if known. */
27
+ function scrub(text, cwd) {
28
+ let s = String(text == null ? '' : text);
29
+ for (const re of SECRET_PATTERNS) s = s.replace(re, (m, pre) => (typeof pre === 'string' ? `${pre}[hidden]` : '[hidden]'));
30
+ const paths = [];
31
+ if (cwd) paths.push([cwd, '<project>']);
32
+ const home = os.homedir();
33
+ if (home && home.length > 3) paths.push([home, '~']);
34
+ for (const [from, to] of paths) {
35
+ for (const v of new Set([from, from.replace(/\\/g, '/')])) s = s.replace(new RegExp(escapeRe(v), 'gi'), to);
36
+ }
37
+ return s;
38
+ }
39
+
40
+ /** The parts of a stored chat that belong in a shared copy. */
41
+ function collect(session, opts = {}) {
42
+ const cwd = session.cwd || '';
43
+ const clean = (t) => scrub(t, cwd);
44
+ const items = [];
45
+ for (const m of session.messages || []) {
46
+ if (m.kind === 'user') items.push({ type: 'user', text: clean(m.text), images: (m.images || []).length });
47
+ else if (m.kind === 'assistant' && m.text && !m.interim) items.push({ type: 'assistant', text: clean(m.text) });
48
+ else if (m.kind === 'assistant' && m.text && opts.includeNarration) items.push({ type: 'narration', text: clean(m.text) });
49
+ else if (m.kind === 'reasoning' && m.text && opts.includeThinking) items.push({ type: 'thinking', text: clean(m.text) });
50
+ else if (m.kind === 'tool') items.push({ type: 'tool', name: m.name, label: clean(m.label || ''), ok: m.ok !== false });
51
+ else if (m.kind === 'question') items.push({ type: 'question', question: clean(m.question), answer: m.answer ? clean(m.answer) : '' });
52
+ else if (m.kind === 'notice' && m.level === 'error') items.push({ type: 'error', text: clean(m.text) });
53
+ }
54
+ return { title: clean(session.title || 'Chat'), project: cwd ? path.basename(cwd) : '', when: session.updatedAt || session.createdAt || null, items };
55
+ }
56
+
57
+ function toMarkdown(session, opts) {
58
+ const c = collect(session, opts);
59
+ const out = [`# ${c.title}`, ''];
60
+ const meta = [c.project && `Project: ${c.project}`, c.when && new Date(c.when).toISOString().slice(0, 10), 'Made with Codeply Craft'].filter(Boolean);
61
+ out.push(`_${meta.join(' - ')}_`, '');
62
+ let tools = [];
63
+ const flush = () => { if (tools.length) { out.push(...tools, ''); tools = []; } };
64
+ for (const it of c.items) {
65
+ if (it.type === 'tool') { tools.push(`- \`${it.name}\` ${it.label}${it.ok ? '' : ' (failed)'}`.trimEnd()); continue; }
66
+ flush();
67
+ if (it.type === 'user') out.push('## You', '', it.text + (it.images ? `\n\n_(${it.images} image${it.images === 1 ? '' : 's'} not included)_` : ''), '');
68
+ else if (it.type === 'assistant') out.push('## Codeply', '', it.text, '');
69
+ else if (it.type === 'narration') out.push(`> ${it.text.replace(/\n/g, '\n> ')}`, '');
70
+ else if (it.type === 'thinking') out.push('<details><summary>Thinking</summary>', '', it.text, '', '</details>', '');
71
+ else if (it.type === 'question') out.push(`> Asked: ${it.question}${it.answer ? ` - answered: ${it.answer}` : ''}`, '');
72
+ else if (it.type === 'error') out.push(`> Error: ${it.text}`, '');
73
+ }
74
+ flush();
75
+ return out.join('\n').replace(/\n{3,}/g, '\n\n');
76
+ }
77
+
78
+ const esc = (s) => String(s).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;').replace(/'/g, '&#39;');
79
+
80
+ /** A small, safe Markdown subset: everything is escaped first, then fences, code, bold, links, lists. */
81
+ function inline(escaped) {
82
+ return escaped
83
+ .replace(/`([^`\n]+)`/g, '<code>$1</code>')
84
+ .replace(/\*\*([^*\n]+)\*\*/g, '<strong>$1</strong>')
85
+ .replace(/\[([^\]\n]+)\]\((https?:\/\/[^\s)]+)\)/g, '<a href="$2" rel="noopener noreferrer nofollow">$1</a>');
86
+ }
87
+
88
+ function mdBlock(text) {
89
+ const parts = String(text).split(/```/);
90
+ let html = '';
91
+ parts.forEach((part, i) => {
92
+ if (i % 2 === 1) {
93
+ const nl = part.indexOf('\n');
94
+ const lang = nl === -1 ? '' : part.slice(0, nl).trim();
95
+ const code = nl === -1 ? part : part.slice(nl + 1);
96
+ html += `<pre${lang ? ` data-lang="${esc(lang.slice(0, 20))}"` : ''}><code>${esc(code.replace(/\n$/, ''))}</code></pre>`;
97
+ return;
98
+ }
99
+ let list = null;
100
+ const closeList = () => { if (list) { html += `</${list}>`; list = null; } };
101
+ for (const line of part.split('\n')) {
102
+ const e = esc(line);
103
+ let m;
104
+ if ((m = /^\s*[-*]\s+(.*)$/.exec(e))) { if (list !== 'ul') { closeList(); html += '<ul>'; list = 'ul'; } html += `<li>${inline(m[1])}</li>`; }
105
+ else if ((m = /^\s*\d+[.)]\s+(.*)$/.exec(e))) { if (list !== 'ol') { closeList(); html += '<ol>'; list = 'ol'; } html += `<li>${inline(m[1])}</li>`; }
106
+ else if ((m = /^#{1,6}\s+(.*)$/.exec(e))) { closeList(); html += `<p><strong>${inline(m[1])}</strong></p>`; }
107
+ else if (e.trim()) { closeList(); html += `<p>${inline(e)}</p>`; }
108
+ else closeList();
109
+ }
110
+ closeList();
111
+ });
112
+ return html;
113
+ }
114
+
115
+ const CSS = `:root{color-scheme:light dark;--bg:#fff;--fg:#1d1d1f;--mut:#6e6e73;--card:#f5f5f7;--line:#e5e5ea;--acc:#5b5bd6}
116
+ @media(prefers-color-scheme:dark){:root{--bg:#141416;--fg:#ececf0;--mut:#9a9aa3;--card:#1f1f23;--line:#2c2c31;--acc:#8b8bf0}}
117
+ *{box-sizing:border-box}body{margin:0;background:var(--bg);color:var(--fg);font:15px/1.6 system-ui,-apple-system,Segoe UI,sans-serif}
118
+ main{max-width:760px;margin:0 auto;padding:32px 20px 64px}h1{font-size:22px;margin:0 0 4px}.meta{color:var(--mut);font-size:13px;margin-bottom:28px}
119
+ .turn{margin:18px 0}.who{font-size:12px;font-weight:600;letter-spacing:.04em;text-transform:uppercase;color:var(--mut);margin-bottom:4px}
120
+ .user .body{background:var(--card);border:1px solid var(--line);border-radius:12px;padding:10px 14px}.assistant .who{color:var(--acc)}
121
+ .body p{margin:.5em 0}.body ul,.body ol{margin:.5em 0;padding-left:1.4em}code{font:13px/1.5 ui-monospace,SFMono-Regular,Consolas,monospace;background:var(--card);border-radius:4px;padding:1px 5px}
122
+ pre{background:var(--card);border:1px solid var(--line);border-radius:10px;padding:12px 14px;overflow:auto;margin:.7em 0}pre code{background:none;padding:0}
123
+ .tools{margin:6px 0;padding:0;list-style:none;color:var(--mut);font-size:13px}.tools li{padding:1px 0}.tools .bad{color:#d1453b}.note{color:var(--mut);font-size:14px;border-left:3px solid var(--line);padding-left:10px;margin:10px 0}
124
+ .err{color:#d1453b}footer{margin-top:40px;color:var(--mut);font-size:12px}a{color:var(--acc)}details{color:var(--mut);font-size:13px}`;
125
+
126
+ function toHtml(session, opts) {
127
+ const c = collect(session, opts);
128
+ const meta = [c.project && esc(c.project), c.when && new Date(c.when).toISOString().slice(0, 10)].filter(Boolean).join(' &middot; ');
129
+ let body = '';
130
+ let tools = [];
131
+ const flush = () => { if (tools.length) { body += `<ul class="tools">${tools.join('')}</ul>`; tools = []; } };
132
+ for (const it of c.items) {
133
+ if (it.type === 'tool') { tools.push(`<li${it.ok ? '' : ' class="bad"'}><code>${esc(it.name)}</code> ${esc(it.label)}${it.ok ? '' : ' (failed)'}</li>`); continue; }
134
+ flush();
135
+ if (it.type === 'user') body += `<div class="turn user"><div class="who">You</div><div class="body">${mdBlock(it.text)}${it.images ? `<p class="note">${it.images} image${it.images === 1 ? '' : 's'} not included</p>` : ''}</div></div>`;
136
+ else if (it.type === 'assistant') body += `<div class="turn assistant"><div class="who">Codeply</div><div class="body">${mdBlock(it.text)}</div></div>`;
137
+ else if (it.type === 'narration') body += `<div class="note">${mdBlock(it.text)}</div>`;
138
+ else if (it.type === 'thinking') body += `<details><summary>Thinking</summary>${mdBlock(it.text)}</details>`;
139
+ else if (it.type === 'question') body += `<div class="note">Asked: ${esc(it.question)}${it.answer ? ` &middot; answered: ${esc(it.answer)}` : ''}</div>`;
140
+ else if (it.type === 'error') body += `<div class="note err">${esc(it.text)}</div>`;
141
+ }
142
+ flush();
143
+ return `<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><meta name="robots" content="noindex"><title>${esc(c.title)}</title><style>${CSS}</style></head><body><main><h1>${esc(c.title)}</h1><div class="meta">${meta}</div>${body}<footer>Made with Codeply Craft</footer></main></body></html>`;
144
+ }
145
+
146
+ /** Uploads the chat as a secret gist. Returns {ok, url} or {ok:false, error}. */
147
+ async function createGist(session, token, { fetchImpl = fetch, apiUrl = 'https://api.github.com', isPublic = false, ...opts } = {}) {
148
+ if (!token) return { ok: false, error: 'No GitHub token.' };
149
+ const c = collect(session, opts);
150
+ const base = (c.title.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '').slice(0, 40)) || 'chat';
151
+ let res;
152
+ try {
153
+ res = await fetchImpl(`${apiUrl.replace(/\/$/, '')}/gists`, {
154
+ method: 'POST',
155
+ headers: { Authorization: `Bearer ${token}`, Accept: 'application/vnd.github+json', 'Content-Type': 'application/json', 'User-Agent': 'codeply-craft' },
156
+ body: JSON.stringify({ description: `${c.title} (Codeply Craft)`.slice(0, 200), public: !!isPublic, files: { [`${base}.md`]: { content: toMarkdown(session, opts) } } }),
157
+ });
158
+ } catch (e) { return { ok: false, error: `Could not reach GitHub: ${e.message}` }; }
159
+ let json = null;
160
+ try { json = JSON.parse(await res.text()); } catch {}
161
+ if (!res.ok) {
162
+ const scopeHint = res.status === 404 || res.status === 403 ? ' Reconnect GitHub in Connect Apps so Craft can create gists.' : '';
163
+ return { ok: false, error: `GitHub said ${res.status}${json && json.message ? `: ${json.message}` : ''}.${scopeHint}` };
164
+ }
165
+ return { ok: true, url: json.html_url, public: !!isPublic };
166
+ }
167
+
168
+ module.exports = { scrub, collect, toMarkdown, toHtml, createGist };