ctxora 6.2.2 → 6.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 (835) hide show
  1. package/.claude-plugin/plugin.json +11 -0
  2. package/README.md +102 -16
  3. package/README.vi.md +93 -7
  4. package/THIRD_PARTY_NOTICES.md +12 -0
  5. package/agents/ctxora-context-engineer.md +6 -0
  6. package/agents/ctxora-maintainer.md +6 -0
  7. package/agents/ctxora-reviewer.md +6 -0
  8. package/bin/ctxora.mjs +1 -1
  9. package/commands/context.md +15 -0
  10. package/commands/guide.md +8 -0
  11. package/commands/handoff.md +8 -0
  12. package/commands/health.md +14 -0
  13. package/commands/learn.md +8 -0
  14. package/commands/plan.md +15 -0
  15. package/commands/review.md +8 -0
  16. package/commands/route.md +6 -0
  17. package/docs/COMMAND-SKILL-MAP.md +25 -0
  18. package/docs/third-party/ECC-LICENSE +21 -0
  19. package/package.json +14 -1
  20. package/pyproject.toml +6 -1
  21. package/schemas/README.md +3 -0
  22. package/schemas/mcp-v2/context_stats.request.schema.json +10 -0
  23. package/schemas/mcp-v2/context_stats.response.schema.json +5 -0
  24. package/schemas/mcp-v2/delete_conversation_handoff.request.schema.json +17 -0
  25. package/schemas/mcp-v2/delete_conversation_handoff.response.schema.json +5 -0
  26. package/schemas/mcp-v2/ecc_search.request.schema.json +16 -0
  27. package/schemas/mcp-v2/ecc_search.response.schema.json +5 -0
  28. package/schemas/mcp-v2/ecc_status.request.schema.json +6 -0
  29. package/schemas/mcp-v2/ecc_status.response.schema.json +5 -0
  30. package/schemas/mcp-v2/error.response.schema.json +40 -0
  31. package/schemas/mcp-v2/handoff_conversation.request.schema.json +29 -0
  32. package/schemas/mcp-v2/handoff_conversation.response.schema.json +5 -0
  33. package/schemas/mcp-v2/invalidate_context.request.schema.json +17 -0
  34. package/schemas/mcp-v2/invalidate_context.response.schema.json +5 -0
  35. package/schemas/mcp-v2/list_conversation_handoffs.request.schema.json +16 -0
  36. package/schemas/mcp-v2/list_conversation_handoffs.response.schema.json +8 -0
  37. package/schemas/mcp-v2/memory_delete.request.schema.json +20 -0
  38. package/schemas/mcp-v2/memory_delete.response.schema.json +5 -0
  39. package/schemas/mcp-v2/memory_list.request.schema.json +19 -0
  40. package/schemas/mcp-v2/memory_list.response.schema.json +7 -0
  41. package/schemas/mcp-v2/memory_save.request.schema.json +42 -0
  42. package/schemas/mcp-v2/memory_save.response.schema.json +5 -0
  43. package/schemas/mcp-v2/memory_search.request.schema.json +26 -0
  44. package/schemas/mcp-v2/memory_search.response.schema.json +8 -0
  45. package/schemas/mcp-v2/plan_context.request.schema.json +24 -0
  46. package/schemas/mcp-v2/plan_context.response.schema.json +5 -0
  47. package/schemas/mcp-v2/prepare_context.request.schema.json +42 -0
  48. package/schemas/mcp-v2/prepare_context.response.schema.json +72 -0
  49. package/schemas/mcp-v2/purge_expired_handoffs.request.schema.json +6 -0
  50. package/schemas/mcp-v2/purge_expired_handoffs.response.schema.json +5 -0
  51. package/schemas/mcp-v2/refresh_workspace.request.schema.json +19 -0
  52. package/schemas/mcp-v2/refresh_workspace.response.schema.json +5 -0
  53. package/schemas/mcp-v2/register_workspace.request.schema.json +23 -0
  54. package/schemas/mcp-v2/register_workspace.response.schema.json +5 -0
  55. package/schemas/mcp-v2/restore_conversation_handoff.request.schema.json +17 -0
  56. package/schemas/mcp-v2/restore_conversation_handoff.response.schema.json +5 -0
  57. package/schemas/mcp-v2/retrieve_context.request.schema.json +26 -0
  58. package/schemas/mcp-v2/retrieve_context.response.schema.json +5 -0
  59. package/schemas/mcp-v2/route_skills.request.schema.json +29 -0
  60. package/schemas/mcp-v2/route_skills.response.schema.json +5 -0
  61. package/schemas/mcp-v2/skill_feedback.request.schema.json +33 -0
  62. package/schemas/mcp-v2/skill_feedback.response.schema.json +5 -0
  63. package/schemas/mcp-v2/skill_learning_status.request.schema.json +16 -0
  64. package/schemas/mcp-v2/skill_learning_status.response.schema.json +5 -0
  65. package/skills/ctxora-context-health/SKILL.md +29 -0
  66. package/skills/ctxora-continuous-learning/SKILL.md +26 -0
  67. package/skills/ctxora-navigation/SKILL.md +20 -0
  68. package/skills/ctxora-repository-context/SKILL.md +33 -0
  69. package/skills/ctxora-setup/SKILL.md +26 -0
  70. package/skills/ctxora-workflow-profiles/SKILL.md +30 -0
  71. package/src/harness_context/api/v2/contracts.py +9 -0
  72. package/src/harness_context/application/container.py +2 -0
  73. package/src/harness_context/application/context_service.py +11 -2
  74. package/src/harness_context/application/protocols.py +5 -0
  75. package/src/harness_context/bootstrap.py +4 -1
  76. package/src/harness_context/cli/app.py +112 -0
  77. package/src/harness_context/mcp/capabilities.py +1 -0
  78. package/src/harness_context/mcp/tool_handlers/__init__.py +5 -1
  79. package/src/harness_context/mcp/tool_handlers/skills.py +29 -0
  80. package/src/harness_context/mcp/tools.py +2 -0
  81. package/src/harness_context/skills/__init__.py +4 -0
  82. package/src/harness_context/skills/catalog.py +245 -0
  83. package/src/harness_context/skills/ecc/accessibility/LICENSE.ecc +21 -0
  84. package/src/harness_context/skills/ecc/accessibility/SKILL.md +147 -0
  85. package/src/harness_context/skills/ecc/agent-architecture-audit/LICENSE.ecc +21 -0
  86. package/src/harness_context/skills/ecc/agent-architecture-audit/SKILL.md +257 -0
  87. package/src/harness_context/skills/ecc/agent-eval/LICENSE.ecc +21 -0
  88. package/src/harness_context/skills/ecc/agent-eval/SKILL.md +147 -0
  89. package/src/harness_context/skills/ecc/agent-harness-construction/LICENSE.ecc +21 -0
  90. package/src/harness_context/skills/ecc/agent-harness-construction/SKILL.md +74 -0
  91. package/src/harness_context/skills/ecc/agent-introspection-debugging/LICENSE.ecc +21 -0
  92. package/src/harness_context/skills/ecc/agent-introspection-debugging/SKILL.md +154 -0
  93. package/src/harness_context/skills/ecc/agent-payment-x402/LICENSE.ecc +21 -0
  94. package/src/harness_context/skills/ecc/agent-payment-x402/SKILL.md +225 -0
  95. package/src/harness_context/skills/ecc/agent-self-evaluation/LICENSE.ecc +21 -0
  96. package/src/harness_context/skills/ecc/agent-self-evaluation/SKILL.md +182 -0
  97. package/src/harness_context/skills/ecc/agent-self-evaluation/examples/high-score-example.md +87 -0
  98. package/src/harness_context/skills/ecc/agent-self-evaluation/examples/low-score-example.md +86 -0
  99. package/src/harness_context/skills/ecc/agent-self-evaluation/references/evaluation-criteria.md +71 -0
  100. package/src/harness_context/skills/ecc/agent-self-evaluation/references/hook-integration.md +64 -0
  101. package/src/harness_context/skills/ecc/agent-self-evaluation/scripts/evaluate.py +408 -0
  102. package/src/harness_context/skills/ecc/agent-self-evaluation/templates/evaluation-report.md +86 -0
  103. package/src/harness_context/skills/ecc/agent-sort/LICENSE.ecc +21 -0
  104. package/src/harness_context/skills/ecc/agent-sort/SKILL.md +216 -0
  105. package/src/harness_context/skills/ecc/agentic-engineering/LICENSE.ecc +21 -0
  106. package/src/harness_context/skills/ecc/agentic-engineering/SKILL.md +64 -0
  107. package/src/harness_context/skills/ecc/agentic-os/LICENSE.ecc +21 -0
  108. package/src/harness_context/skills/ecc/agentic-os/SKILL.md +388 -0
  109. package/src/harness_context/skills/ecc/ai-first-engineering/LICENSE.ecc +21 -0
  110. package/src/harness_context/skills/ecc/ai-first-engineering/SKILL.md +52 -0
  111. package/src/harness_context/skills/ecc/ai-regression-testing/LICENSE.ecc +21 -0
  112. package/src/harness_context/skills/ecc/ai-regression-testing/SKILL.md +386 -0
  113. package/src/harness_context/skills/ecc/android-clean-architecture/LICENSE.ecc +21 -0
  114. package/src/harness_context/skills/ecc/android-clean-architecture/SKILL.md +340 -0
  115. package/src/harness_context/skills/ecc/angular-developer/LICENSE.ecc +21 -0
  116. package/src/harness_context/skills/ecc/angular-developer/SKILL.md +155 -0
  117. package/src/harness_context/skills/ecc/angular-developer/references/angular-animations.md +160 -0
  118. package/src/harness_context/skills/ecc/angular-developer/references/angular-aria.md +410 -0
  119. package/src/harness_context/skills/ecc/angular-developer/references/cli.md +86 -0
  120. package/src/harness_context/skills/ecc/angular-developer/references/component-harnesses.md +59 -0
  121. package/src/harness_context/skills/ecc/angular-developer/references/component-styling.md +91 -0
  122. package/src/harness_context/skills/ecc/angular-developer/references/components.md +117 -0
  123. package/src/harness_context/skills/ecc/angular-developer/references/creating-services.md +97 -0
  124. package/src/harness_context/skills/ecc/angular-developer/references/data-resolvers.md +69 -0
  125. package/src/harness_context/skills/ecc/angular-developer/references/define-routes.md +67 -0
  126. package/src/harness_context/skills/ecc/angular-developer/references/defining-providers.md +72 -0
  127. package/src/harness_context/skills/ecc/angular-developer/references/di-fundamentals.md +120 -0
  128. package/src/harness_context/skills/ecc/angular-developer/references/e2e-testing.md +56 -0
  129. package/src/harness_context/skills/ecc/angular-developer/references/effects.md +83 -0
  130. package/src/harness_context/skills/ecc/angular-developer/references/hierarchical-injectors.md +43 -0
  131. package/src/harness_context/skills/ecc/angular-developer/references/host-elements.md +80 -0
  132. package/src/harness_context/skills/ecc/angular-developer/references/injection-context.md +63 -0
  133. package/src/harness_context/skills/ecc/angular-developer/references/inputs.md +101 -0
  134. package/src/harness_context/skills/ecc/angular-developer/references/linked-signal.md +59 -0
  135. package/src/harness_context/skills/ecc/angular-developer/references/loading-strategies.md +61 -0
  136. package/src/harness_context/skills/ecc/angular-developer/references/mcp.md +108 -0
  137. package/src/harness_context/skills/ecc/angular-developer/references/navigate-to-routes.md +69 -0
  138. package/src/harness_context/skills/ecc/angular-developer/references/outputs.md +86 -0
  139. package/src/harness_context/skills/ecc/angular-developer/references/reactive-forms.md +122 -0
  140. package/src/harness_context/skills/ecc/angular-developer/references/rendering-strategies.md +44 -0
  141. package/src/harness_context/skills/ecc/angular-developer/references/resource.md +77 -0
  142. package/src/harness_context/skills/ecc/angular-developer/references/route-animations.md +56 -0
  143. package/src/harness_context/skills/ecc/angular-developer/references/route-guards.md +52 -0
  144. package/src/harness_context/skills/ecc/angular-developer/references/router-lifecycle.md +45 -0
  145. package/src/harness_context/skills/ecc/angular-developer/references/router-testing.md +87 -0
  146. package/src/harness_context/skills/ecc/angular-developer/references/show-routes-with-outlets.md +68 -0
  147. package/src/harness_context/skills/ecc/angular-developer/references/signal-forms.md +795 -0
  148. package/src/harness_context/skills/ecc/angular-developer/references/signals-overview.md +94 -0
  149. package/src/harness_context/skills/ecc/angular-developer/references/tailwind-css.md +69 -0
  150. package/src/harness_context/skills/ecc/angular-developer/references/template-driven-forms.md +114 -0
  151. package/src/harness_context/skills/ecc/angular-developer/references/testing-fundamentals.md +65 -0
  152. package/src/harness_context/skills/ecc/api-connector-builder/LICENSE.ecc +21 -0
  153. package/src/harness_context/skills/ecc/api-connector-builder/SKILL.md +121 -0
  154. package/src/harness_context/skills/ecc/api-design/LICENSE.ecc +21 -0
  155. package/src/harness_context/skills/ecc/api-design/SKILL.md +524 -0
  156. package/src/harness_context/skills/ecc/architecture-decision-records/LICENSE.ecc +21 -0
  157. package/src/harness_context/skills/ecc/architecture-decision-records/SKILL.md +180 -0
  158. package/src/harness_context/skills/ecc/article-writing/LICENSE.ecc +21 -0
  159. package/src/harness_context/skills/ecc/article-writing/SKILL.md +80 -0
  160. package/src/harness_context/skills/ecc/automation-audit-ops/LICENSE.ecc +21 -0
  161. package/src/harness_context/skills/ecc/automation-audit-ops/SKILL.md +143 -0
  162. package/src/harness_context/skills/ecc/autonomous-agent-harness/LICENSE.ecc +21 -0
  163. package/src/harness_context/skills/ecc/autonomous-agent-harness/SKILL.md +274 -0
  164. package/src/harness_context/skills/ecc/autonomous-loops/LICENSE.ecc +21 -0
  165. package/src/harness_context/skills/ecc/autonomous-loops/SKILL.md +611 -0
  166. package/src/harness_context/skills/ecc/backend-patterns/LICENSE.ecc +21 -0
  167. package/src/harness_context/skills/ecc/backend-patterns/SKILL.md +562 -0
  168. package/src/harness_context/skills/ecc/benchmark/LICENSE.ecc +21 -0
  169. package/src/harness_context/skills/ecc/benchmark/SKILL.md +95 -0
  170. package/src/harness_context/skills/ecc/benchmark-methodology/LICENSE.ecc +21 -0
  171. package/src/harness_context/skills/ecc/benchmark-methodology/SKILL.md +191 -0
  172. package/src/harness_context/skills/ecc/benchmark-optimization-loop/LICENSE.ecc +21 -0
  173. package/src/harness_context/skills/ecc/benchmark-optimization-loop/SKILL.md +71 -0
  174. package/src/harness_context/skills/ecc/blender-motion-state-inspection/LICENSE.ecc +21 -0
  175. package/src/harness_context/skills/ecc/blender-motion-state-inspection/SKILL.md +165 -0
  176. package/src/harness_context/skills/ecc/blueprint/LICENSE.ecc +21 -0
  177. package/src/harness_context/skills/ecc/blueprint/SKILL.md +106 -0
  178. package/src/harness_context/skills/ecc/brand-discovery/LICENSE.ecc +21 -0
  179. package/src/harness_context/skills/ecc/brand-discovery/SKILL.md +145 -0
  180. package/src/harness_context/skills/ecc/brand-discovery/references/10_purpose-why.md +40 -0
  181. package/src/harness_context/skills/ecc/brand-discovery/references/20_positioning.md +44 -0
  182. package/src/harness_context/skills/ecc/brand-discovery/references/30_audience-niche.md +52 -0
  183. package/src/harness_context/skills/ecc/brand-discovery/references/40_personality-archetype.md +57 -0
  184. package/src/harness_context/skills/ecc/brand-discovery/references/50_voice-tone.md +59 -0
  185. package/src/harness_context/skills/ecc/brand-discovery/references/60_narrative-story.md +50 -0
  186. package/src/harness_context/skills/ecc/brand-discovery/references/70_founder-tension.md +49 -0
  187. package/src/harness_context/skills/ecc/brand-discovery/references/90_SYNTHESIS.md +133 -0
  188. package/src/harness_context/skills/ecc/brand-voice/LICENSE.ecc +21 -0
  189. package/src/harness_context/skills/ecc/brand-voice/SKILL.md +98 -0
  190. package/src/harness_context/skills/ecc/brand-voice/references/voice-profile-schema.md +55 -0
  191. package/src/harness_context/skills/ecc/browser-qa/LICENSE.ecc +21 -0
  192. package/src/harness_context/skills/ecc/browser-qa/SKILL.md +105 -0
  193. package/src/harness_context/skills/ecc/bun-runtime/LICENSE.ecc +21 -0
  194. package/src/harness_context/skills/ecc/bun-runtime/SKILL.md +85 -0
  195. package/src/harness_context/skills/ecc/canary-watch/LICENSE.ecc +21 -0
  196. package/src/harness_context/skills/ecc/canary-watch/SKILL.md +108 -0
  197. package/src/harness_context/skills/ecc/carrier-relationship-management/LICENSE.ecc +21 -0
  198. package/src/harness_context/skills/ecc/carrier-relationship-management/SKILL.md +212 -0
  199. package/src/harness_context/skills/ecc/cisco-ios-patterns/LICENSE.ecc +21 -0
  200. package/src/harness_context/skills/ecc/cisco-ios-patterns/SKILL.md +164 -0
  201. package/src/harness_context/skills/ecc/ck/LICENSE.ecc +21 -0
  202. package/src/harness_context/skills/ecc/ck/SKILL.md +148 -0
  203. package/src/harness_context/skills/ecc/ck/commands/forget.mjs +44 -0
  204. package/src/harness_context/skills/ecc/ck/commands/info.mjs +24 -0
  205. package/src/harness_context/skills/ecc/ck/commands/init.mjs +143 -0
  206. package/src/harness_context/skills/ecc/ck/commands/list.mjs +40 -0
  207. package/src/harness_context/skills/ecc/ck/commands/migrate.mjs +202 -0
  208. package/src/harness_context/skills/ecc/ck/commands/resume.mjs +36 -0
  209. package/src/harness_context/skills/ecc/ck/commands/save.mjs +210 -0
  210. package/src/harness_context/skills/ecc/ck/commands/shared.mjs +387 -0
  211. package/src/harness_context/skills/ecc/ck/hooks/session-start.mjs +224 -0
  212. package/src/harness_context/skills/ecc/claude-devfleet/LICENSE.ecc +21 -0
  213. package/src/harness_context/skills/ecc/claude-devfleet/SKILL.md +112 -0
  214. package/src/harness_context/skills/ecc/click-path-audit/LICENSE.ecc +21 -0
  215. package/src/harness_context/skills/ecc/click-path-audit/SKILL.md +245 -0
  216. package/src/harness_context/skills/ecc/clickhouse-io/LICENSE.ecc +21 -0
  217. package/src/harness_context/skills/ecc/clickhouse-io/SKILL.md +445 -0
  218. package/src/harness_context/skills/ecc/code-tour/LICENSE.ecc +21 -0
  219. package/src/harness_context/skills/ecc/code-tour/SKILL.md +254 -0
  220. package/src/harness_context/skills/ecc/codebase-onboarding/LICENSE.ecc +21 -0
  221. package/src/harness_context/skills/ecc/codebase-onboarding/SKILL.md +234 -0
  222. package/src/harness_context/skills/ecc/codehealth-mcp/LICENSE.ecc +21 -0
  223. package/src/harness_context/skills/ecc/codehealth-mcp/SKILL.md +167 -0
  224. package/src/harness_context/skills/ecc/coding-standards/LICENSE.ecc +21 -0
  225. package/src/harness_context/skills/ecc/coding-standards/SKILL.md +551 -0
  226. package/src/harness_context/skills/ecc/competitive-platform-analysis/LICENSE.ecc +21 -0
  227. package/src/harness_context/skills/ecc/competitive-platform-analysis/SKILL.md +214 -0
  228. package/src/harness_context/skills/ecc/competitive-report-structure/LICENSE.ecc +21 -0
  229. package/src/harness_context/skills/ecc/competitive-report-structure/SKILL.md +162 -0
  230. package/src/harness_context/skills/ecc/compose-multiplatform-patterns/LICENSE.ecc +21 -0
  231. package/src/harness_context/skills/ecc/compose-multiplatform-patterns/SKILL.md +300 -0
  232. package/src/harness_context/skills/ecc/config-gc/LICENSE.ecc +21 -0
  233. package/src/harness_context/skills/ecc/config-gc/SKILL.md +120 -0
  234. package/src/harness_context/skills/ecc/configure-ecc/LICENSE.ecc +21 -0
  235. package/src/harness_context/skills/ecc/configure-ecc/SKILL.md +206 -0
  236. package/src/harness_context/skills/ecc/connections-optimizer/LICENSE.ecc +21 -0
  237. package/src/harness_context/skills/ecc/connections-optimizer/SKILL.md +190 -0
  238. package/src/harness_context/skills/ecc/content-engine/LICENSE.ecc +21 -0
  239. package/src/harness_context/skills/ecc/content-engine/SKILL.md +132 -0
  240. package/src/harness_context/skills/ecc/content-hash-cache-pattern/LICENSE.ecc +21 -0
  241. package/src/harness_context/skills/ecc/content-hash-cache-pattern/SKILL.md +162 -0
  242. package/src/harness_context/skills/ecc/context-budget/LICENSE.ecc +21 -0
  243. package/src/harness_context/skills/ecc/context-budget/SKILL.md +136 -0
  244. package/src/harness_context/skills/ecc/continuous-agent-loop/LICENSE.ecc +21 -0
  245. package/src/harness_context/skills/ecc/continuous-agent-loop/SKILL.md +46 -0
  246. package/src/harness_context/skills/ecc/continuous-learning/LICENSE.ecc +21 -0
  247. package/src/harness_context/skills/ecc/continuous-learning/SKILL.md +132 -0
  248. package/src/harness_context/skills/ecc/continuous-learning/config.json +18 -0
  249. package/src/harness_context/skills/ecc/continuous-learning/evaluate-session.sh +69 -0
  250. package/src/harness_context/skills/ecc/continuous-learning-v2/LICENSE.ecc +21 -0
  251. package/src/harness_context/skills/ecc/continuous-learning-v2/SKILL.md +377 -0
  252. package/src/harness_context/skills/ecc/continuous-learning-v2/agents/observer-loop.sh +372 -0
  253. package/src/harness_context/skills/ecc/continuous-learning-v2/agents/observer.md +189 -0
  254. package/src/harness_context/skills/ecc/continuous-learning-v2/agents/session-guardian.sh +150 -0
  255. package/src/harness_context/skills/ecc/continuous-learning-v2/agents/start-observer.sh +252 -0
  256. package/src/harness_context/skills/ecc/continuous-learning-v2/config.json +8 -0
  257. package/src/harness_context/skills/ecc/continuous-learning-v2/hooks/observe.sh +675 -0
  258. package/src/harness_context/skills/ecc/continuous-learning-v2/scripts/detect-project.sh +334 -0
  259. package/src/harness_context/skills/ecc/continuous-learning-v2/scripts/instinct-cli.py +2290 -0
  260. package/src/harness_context/skills/ecc/continuous-learning-v2/scripts/lib/homunculus-dir.sh +31 -0
  261. package/src/harness_context/skills/ecc/continuous-learning-v2/scripts/migrate-homunculus.sh +68 -0
  262. package/src/harness_context/skills/ecc/continuous-learning-v2/scripts/test_parse_instinct.py +1420 -0
  263. package/src/harness_context/skills/ecc/contract-first/LICENSE.ecc +21 -0
  264. package/src/harness_context/skills/ecc/contract-first/SKILL.md +287 -0
  265. package/src/harness_context/skills/ecc/cost-aware-llm-pipeline/LICENSE.ecc +21 -0
  266. package/src/harness_context/skills/ecc/cost-aware-llm-pipeline/SKILL.md +188 -0
  267. package/src/harness_context/skills/ecc/cost-tracking/LICENSE.ecc +21 -0
  268. package/src/harness_context/skills/ecc/cost-tracking/SKILL.md +97 -0
  269. package/src/harness_context/skills/ecc/council/LICENSE.ecc +21 -0
  270. package/src/harness_context/skills/ecc/council/SKILL.md +204 -0
  271. package/src/harness_context/skills/ecc/council-multi-model/LICENSE.ecc +21 -0
  272. package/src/harness_context/skills/ecc/council-multi-model/SKILL.md +167 -0
  273. package/src/harness_context/skills/ecc/council-multi-model/scripts/review-with-codex.js +305 -0
  274. package/src/harness_context/skills/ecc/cpp-coding-standards/LICENSE.ecc +21 -0
  275. package/src/harness_context/skills/ecc/cpp-coding-standards/SKILL.md +724 -0
  276. package/src/harness_context/skills/ecc/cpp-testing/LICENSE.ecc +21 -0
  277. package/src/harness_context/skills/ecc/cpp-testing/SKILL.md +325 -0
  278. package/src/harness_context/skills/ecc/crosspost/LICENSE.ecc +21 -0
  279. package/src/harness_context/skills/ecc/crosspost/SKILL.md +123 -0
  280. package/src/harness_context/skills/ecc/csharp-testing/LICENSE.ecc +21 -0
  281. package/src/harness_context/skills/ecc/csharp-testing/SKILL.md +322 -0
  282. package/src/harness_context/skills/ecc/customer-billing-ops/LICENSE.ecc +21 -0
  283. package/src/harness_context/skills/ecc/customer-billing-ops/SKILL.md +141 -0
  284. package/src/harness_context/skills/ecc/customs-trade-compliance/LICENSE.ecc +21 -0
  285. package/src/harness_context/skills/ecc/customs-trade-compliance/SKILL.md +263 -0
  286. package/src/harness_context/skills/ecc/dart-flutter-patterns/LICENSE.ecc +21 -0
  287. package/src/harness_context/skills/ecc/dart-flutter-patterns/SKILL.md +564 -0
  288. package/src/harness_context/skills/ecc/dashboard-builder/LICENSE.ecc +21 -0
  289. package/src/harness_context/skills/ecc/dashboard-builder/SKILL.md +109 -0
  290. package/src/harness_context/skills/ecc/data-scraper-agent/LICENSE.ecc +21 -0
  291. package/src/harness_context/skills/ecc/data-scraper-agent/SKILL.md +776 -0
  292. package/src/harness_context/skills/ecc/data-throughput-accelerator/LICENSE.ecc +21 -0
  293. package/src/harness_context/skills/ecc/data-throughput-accelerator/SKILL.md +74 -0
  294. package/src/harness_context/skills/ecc/database-migrations/LICENSE.ecc +21 -0
  295. package/src/harness_context/skills/ecc/database-migrations/SKILL.md +430 -0
  296. package/src/harness_context/skills/ecc/deep-research/LICENSE.ecc +21 -0
  297. package/src/harness_context/skills/ecc/deep-research/SKILL.md +170 -0
  298. package/src/harness_context/skills/ecc/defi-amm-security/LICENSE.ecc +21 -0
  299. package/src/harness_context/skills/ecc/defi-amm-security/SKILL.md +167 -0
  300. package/src/harness_context/skills/ecc/delivery-gate/LICENSE.ecc +21 -0
  301. package/src/harness_context/skills/ecc/delivery-gate/SKILL.md +126 -0
  302. package/src/harness_context/skills/ecc/delivery-gate/hooks/quality-gate.py +220 -0
  303. package/src/harness_context/skills/ecc/deployment-patterns/LICENSE.ecc +21 -0
  304. package/src/harness_context/skills/ecc/deployment-patterns/SKILL.md +428 -0
  305. package/src/harness_context/skills/ecc/design-system/LICENSE.ecc +21 -0
  306. package/src/harness_context/skills/ecc/design-system/SKILL.md +83 -0
  307. package/src/harness_context/skills/ecc/dev-team/LICENSE.ecc +21 -0
  308. package/src/harness_context/skills/ecc/dev-team/SKILL.md +203 -0
  309. package/src/harness_context/skills/ecc/django-celery/LICENSE.ecc +21 -0
  310. package/src/harness_context/skills/ecc/django-celery/SKILL.md +458 -0
  311. package/src/harness_context/skills/ecc/django-patterns/LICENSE.ecc +21 -0
  312. package/src/harness_context/skills/ecc/django-patterns/SKILL.md +735 -0
  313. package/src/harness_context/skills/ecc/django-security/LICENSE.ecc +21 -0
  314. package/src/harness_context/skills/ecc/django-security/SKILL.md +644 -0
  315. package/src/harness_context/skills/ecc/django-tdd/LICENSE.ecc +21 -0
  316. package/src/harness_context/skills/ecc/django-tdd/SKILL.md +730 -0
  317. package/src/harness_context/skills/ecc/django-verification/LICENSE.ecc +21 -0
  318. package/src/harness_context/skills/ecc/django-verification/SKILL.md +470 -0
  319. package/src/harness_context/skills/ecc/dmux-workflows/LICENSE.ecc +21 -0
  320. package/src/harness_context/skills/ecc/dmux-workflows/SKILL.md +192 -0
  321. package/src/harness_context/skills/ecc/docker-patterns/LICENSE.ecc +21 -0
  322. package/src/harness_context/skills/ecc/docker-patterns/SKILL.md +520 -0
  323. package/src/harness_context/skills/ecc/documentation-lookup/LICENSE.ecc +21 -0
  324. package/src/harness_context/skills/ecc/documentation-lookup/SKILL.md +91 -0
  325. package/src/harness_context/skills/ecc/dotnet-patterns/LICENSE.ecc +21 -0
  326. package/src/harness_context/skills/ecc/dotnet-patterns/SKILL.md +322 -0
  327. package/src/harness_context/skills/ecc/dynamic-workflow-mode/LICENSE.ecc +21 -0
  328. package/src/harness_context/skills/ecc/dynamic-workflow-mode/SKILL.md +124 -0
  329. package/src/harness_context/skills/ecc/e2e-testing/LICENSE.ecc +21 -0
  330. package/src/harness_context/skills/ecc/e2e-testing/SKILL.md +327 -0
  331. package/src/harness_context/skills/ecc/ecc-guide/LICENSE.ecc +21 -0
  332. package/src/harness_context/skills/ecc/ecc-guide/SKILL.md +190 -0
  333. package/src/harness_context/skills/ecc/ecc-recipes/LICENSE.ecc +21 -0
  334. package/src/harness_context/skills/ecc/ecc-recipes/SKILL.md +150 -0
  335. package/src/harness_context/skills/ecc/ecc-tools-cost-audit/LICENSE.ecc +21 -0
  336. package/src/harness_context/skills/ecc/ecc-tools-cost-audit/SKILL.md +161 -0
  337. package/src/harness_context/skills/ecc/email-ops/LICENSE.ecc +21 -0
  338. package/src/harness_context/skills/ecc/email-ops/SKILL.md +133 -0
  339. package/src/harness_context/skills/ecc/energy-procurement/LICENSE.ecc +21 -0
  340. package/src/harness_context/skills/ecc/energy-procurement/SKILL.md +228 -0
  341. package/src/harness_context/skills/ecc/enterprise-agent-ops/LICENSE.ecc +21 -0
  342. package/src/harness_context/skills/ecc/enterprise-agent-ops/SKILL.md +51 -0
  343. package/src/harness_context/skills/ecc/error-handling/LICENSE.ecc +21 -0
  344. package/src/harness_context/skills/ecc/error-handling/SKILL.md +377 -0
  345. package/src/harness_context/skills/ecc/eval-harness/LICENSE.ecc +21 -0
  346. package/src/harness_context/skills/ecc/eval-harness/SKILL.md +271 -0
  347. package/src/harness_context/skills/ecc/evm-token-decimals/LICENSE.ecc +21 -0
  348. package/src/harness_context/skills/ecc/evm-token-decimals/SKILL.md +131 -0
  349. package/src/harness_context/skills/ecc/exa-search/LICENSE.ecc +21 -0
  350. package/src/harness_context/skills/ecc/exa-search/SKILL.md +117 -0
  351. package/src/harness_context/skills/ecc/fal-ai-media/LICENSE.ecc +21 -0
  352. package/src/harness_context/skills/ecc/fal-ai-media/SKILL.md +289 -0
  353. package/src/harness_context/skills/ecc/fastapi-patterns/LICENSE.ecc +21 -0
  354. package/src/harness_context/skills/ecc/fastapi-patterns/SKILL.md +514 -0
  355. package/src/harness_context/skills/ecc/finance-billing-ops/LICENSE.ecc +21 -0
  356. package/src/harness_context/skills/ecc/finance-billing-ops/SKILL.md +128 -0
  357. package/src/harness_context/skills/ecc/flox-environments/LICENSE.ecc +21 -0
  358. package/src/harness_context/skills/ecc/flox-environments/SKILL.md +497 -0
  359. package/src/harness_context/skills/ecc/flutter-dart-code-review/LICENSE.ecc +21 -0
  360. package/src/harness_context/skills/ecc/flutter-dart-code-review/SKILL.md +436 -0
  361. package/src/harness_context/skills/ecc/foundation-models-on-device/LICENSE.ecc +21 -0
  362. package/src/harness_context/skills/ecc/foundation-models-on-device/SKILL.md +243 -0
  363. package/src/harness_context/skills/ecc/frontend-a11y/LICENSE.ecc +21 -0
  364. package/src/harness_context/skills/ecc/frontend-a11y/SKILL.md +446 -0
  365. package/src/harness_context/skills/ecc/frontend-design-direction/LICENSE.ecc +21 -0
  366. package/src/harness_context/skills/ecc/frontend-design-direction/SKILL.md +93 -0
  367. package/src/harness_context/skills/ecc/frontend-patterns/LICENSE.ecc +21 -0
  368. package/src/harness_context/skills/ecc/frontend-patterns/SKILL.md +657 -0
  369. package/src/harness_context/skills/ecc/frontend-slides/LICENSE.ecc +21 -0
  370. package/src/harness_context/skills/ecc/frontend-slides/SKILL.md +185 -0
  371. package/src/harness_context/skills/ecc/frontend-slides/STYLE_PRESETS.md +330 -0
  372. package/src/harness_context/skills/ecc/frontend-slides/animation-patterns.md +122 -0
  373. package/src/harness_context/skills/ecc/frontend-slides/html-template.md +419 -0
  374. package/src/harness_context/skills/ecc/frontend-slides/scripts/export-pdf.sh +418 -0
  375. package/src/harness_context/skills/ecc/frontend-slides/scripts/extract-pptx.py +96 -0
  376. package/src/harness_context/skills/ecc/frontend-slides/viewport-base.css +153 -0
  377. package/src/harness_context/skills/ecc/fsharp-testing/LICENSE.ecc +21 -0
  378. package/src/harness_context/skills/ecc/fsharp-testing/SKILL.md +281 -0
  379. package/src/harness_context/skills/ecc/gan-style-harness/LICENSE.ecc +21 -0
  380. package/src/harness_context/skills/ecc/gan-style-harness/SKILL.md +279 -0
  381. package/src/harness_context/skills/ecc/gateguard/LICENSE.ecc +21 -0
  382. package/src/harness_context/skills/ecc/gateguard/SKILL.md +182 -0
  383. package/src/harness_context/skills/ecc/generating-python-installer/LICENSE.ecc +21 -0
  384. package/src/harness_context/skills/ecc/generating-python-installer/SKILL.md +820 -0
  385. package/src/harness_context/skills/ecc/git-workflow/LICENSE.ecc +21 -0
  386. package/src/harness_context/skills/ecc/git-workflow/SKILL.md +716 -0
  387. package/src/harness_context/skills/ecc/github-ops/LICENSE.ecc +21 -0
  388. package/src/harness_context/skills/ecc/github-ops/SKILL.md +162 -0
  389. package/src/harness_context/skills/ecc/github-ops/references/ecc-release-checklist.md +211 -0
  390. package/src/harness_context/skills/ecc/golang-patterns/LICENSE.ecc +21 -0
  391. package/src/harness_context/skills/ecc/golang-patterns/SKILL.md +676 -0
  392. package/src/harness_context/skills/ecc/golang-testing/LICENSE.ecc +21 -0
  393. package/src/harness_context/skills/ecc/golang-testing/SKILL.md +721 -0
  394. package/src/harness_context/skills/ecc/google-workspace-ops/LICENSE.ecc +21 -0
  395. package/src/harness_context/skills/ecc/google-workspace-ops/SKILL.md +96 -0
  396. package/src/harness_context/skills/ecc/growth-log/LICENSE.ecc +21 -0
  397. package/src/harness_context/skills/ecc/growth-log/SKILL.md +128 -0
  398. package/src/harness_context/skills/ecc/healthcare-cdss-patterns/LICENSE.ecc +21 -0
  399. package/src/harness_context/skills/ecc/healthcare-cdss-patterns/SKILL.md +246 -0
  400. package/src/harness_context/skills/ecc/healthcare-emr-patterns/LICENSE.ecc +21 -0
  401. package/src/harness_context/skills/ecc/healthcare-emr-patterns/SKILL.md +160 -0
  402. package/src/harness_context/skills/ecc/healthcare-eval-harness/LICENSE.ecc +21 -0
  403. package/src/harness_context/skills/ecc/healthcare-eval-harness/SKILL.md +208 -0
  404. package/src/harness_context/skills/ecc/healthcare-phi-compliance/LICENSE.ecc +21 -0
  405. package/src/harness_context/skills/ecc/healthcare-phi-compliance/SKILL.md +146 -0
  406. package/src/harness_context/skills/ecc/hermes-imports/LICENSE.ecc +21 -0
  407. package/src/harness_context/skills/ecc/hermes-imports/SKILL.md +89 -0
  408. package/src/harness_context/skills/ecc/hexagonal-architecture/LICENSE.ecc +21 -0
  409. package/src/harness_context/skills/ecc/hexagonal-architecture/SKILL.md +277 -0
  410. package/src/harness_context/skills/ecc/hipaa-compliance/LICENSE.ecc +21 -0
  411. package/src/harness_context/skills/ecc/hipaa-compliance/SKILL.md +79 -0
  412. package/src/harness_context/skills/ecc/homelab-network-readiness/LICENSE.ecc +21 -0
  413. package/src/harness_context/skills/ecc/homelab-network-readiness/SKILL.md +170 -0
  414. package/src/harness_context/skills/ecc/homelab-network-setup/LICENSE.ecc +21 -0
  415. package/src/harness_context/skills/ecc/homelab-network-setup/SKILL.md +130 -0
  416. package/src/harness_context/skills/ecc/homelab-pihole-dns/LICENSE.ecc +21 -0
  417. package/src/harness_context/skills/ecc/homelab-pihole-dns/SKILL.md +275 -0
  418. package/src/harness_context/skills/ecc/homelab-vlan-segmentation/LICENSE.ecc +21 -0
  419. package/src/harness_context/skills/ecc/homelab-vlan-segmentation/SKILL.md +312 -0
  420. package/src/harness_context/skills/ecc/homelab-wireguard-vpn/LICENSE.ecc +21 -0
  421. package/src/harness_context/skills/ecc/homelab-wireguard-vpn/SKILL.md +306 -0
  422. package/src/harness_context/skills/ecc/hookify-rules/LICENSE.ecc +21 -0
  423. package/src/harness_context/skills/ecc/hookify-rules/SKILL.md +128 -0
  424. package/src/harness_context/skills/ecc/inherit-legacy-style/LICENSE.ecc +21 -0
  425. package/src/harness_context/skills/ecc/inherit-legacy-style/SKILL.md +157 -0
  426. package/src/harness_context/skills/ecc/intent-driven-development/LICENSE.ecc +21 -0
  427. package/src/harness_context/skills/ecc/intent-driven-development/SKILL.md +360 -0
  428. package/src/harness_context/skills/ecc/inventory-demand-planning/LICENSE.ecc +21 -0
  429. package/src/harness_context/skills/ecc/inventory-demand-planning/SKILL.md +247 -0
  430. package/src/harness_context/skills/ecc/investor-materials/LICENSE.ecc +21 -0
  431. package/src/harness_context/skills/ecc/investor-materials/SKILL.md +97 -0
  432. package/src/harness_context/skills/ecc/investor-outreach/LICENSE.ecc +21 -0
  433. package/src/harness_context/skills/ecc/investor-outreach/SKILL.md +92 -0
  434. package/src/harness_context/skills/ecc/ios-icon-gen/LICENSE.ecc +21 -0
  435. package/src/harness_context/skills/ecc/ios-icon-gen/SKILL.md +158 -0
  436. package/src/harness_context/skills/ecc/ios-icon-gen/scripts/generate_icons.swift +258 -0
  437. package/src/harness_context/skills/ecc/ios-icon-gen/scripts/iconify_gen.sh +235 -0
  438. package/src/harness_context/skills/ecc/iterative-retrieval/LICENSE.ecc +21 -0
  439. package/src/harness_context/skills/ecc/iterative-retrieval/SKILL.md +212 -0
  440. package/src/harness_context/skills/ecc/ito-baskets/LICENSE.ecc +21 -0
  441. package/src/harness_context/skills/ecc/ito-baskets/SKILL.md +263 -0
  442. package/src/harness_context/skills/ecc/ito-baskets/agents/openai.yaml +4 -0
  443. package/src/harness_context/skills/ecc/ito-baskets/scripts/ito-baskets.js +195 -0
  444. package/src/harness_context/skills/ecc/ito-compute/LICENSE.ecc +21 -0
  445. package/src/harness_context/skills/ecc/ito-compute/SKILL.md +165 -0
  446. package/src/harness_context/skills/ecc/ito-compute/agents/openai.yaml +4 -0
  447. package/src/harness_context/skills/ecc/ito-inference/LICENSE.ecc +21 -0
  448. package/src/harness_context/skills/ecc/ito-inference/SKILL.md +119 -0
  449. package/src/harness_context/skills/ecc/ito-training/LICENSE.ecc +21 -0
  450. package/src/harness_context/skills/ecc/ito-training/SKILL.md +123 -0
  451. package/src/harness_context/skills/ecc/java-coding-standards/LICENSE.ecc +21 -0
  452. package/src/harness_context/skills/ecc/java-coding-standards/SKILL.md +384 -0
  453. package/src/harness_context/skills/ecc/jira-integration/LICENSE.ecc +21 -0
  454. package/src/harness_context/skills/ecc/jira-integration/SKILL.md +312 -0
  455. package/src/harness_context/skills/ecc/jpa-patterns/LICENSE.ecc +21 -0
  456. package/src/harness_context/skills/ecc/jpa-patterns/SKILL.md +152 -0
  457. package/src/harness_context/skills/ecc/knowledge-ops/LICENSE.ecc +21 -0
  458. package/src/harness_context/skills/ecc/knowledge-ops/SKILL.md +155 -0
  459. package/src/harness_context/skills/ecc/kotlin-coroutines-flows/LICENSE.ecc +21 -0
  460. package/src/harness_context/skills/ecc/kotlin-coroutines-flows/SKILL.md +285 -0
  461. package/src/harness_context/skills/ecc/kotlin-exposed-patterns/LICENSE.ecc +21 -0
  462. package/src/harness_context/skills/ecc/kotlin-exposed-patterns/SKILL.md +720 -0
  463. package/src/harness_context/skills/ecc/kotlin-ktor-patterns/LICENSE.ecc +21 -0
  464. package/src/harness_context/skills/ecc/kotlin-ktor-patterns/SKILL.md +690 -0
  465. package/src/harness_context/skills/ecc/kotlin-patterns/LICENSE.ecc +21 -0
  466. package/src/harness_context/skills/ecc/kotlin-patterns/SKILL.md +712 -0
  467. package/src/harness_context/skills/ecc/kotlin-testing/LICENSE.ecc +21 -0
  468. package/src/harness_context/skills/ecc/kotlin-testing/SKILL.md +825 -0
  469. package/src/harness_context/skills/ecc/kubernetes-patterns/LICENSE.ecc +21 -0
  470. package/src/harness_context/skills/ecc/kubernetes-patterns/SKILL.md +756 -0
  471. package/src/harness_context/skills/ecc/laravel-patterns/LICENSE.ecc +21 -0
  472. package/src/harness_context/skills/ecc/laravel-patterns/SKILL.md +416 -0
  473. package/src/harness_context/skills/ecc/laravel-plugin-discovery/LICENSE.ecc +21 -0
  474. package/src/harness_context/skills/ecc/laravel-plugin-discovery/SKILL.md +230 -0
  475. package/src/harness_context/skills/ecc/laravel-security/LICENSE.ecc +21 -0
  476. package/src/harness_context/skills/ecc/laravel-security/SKILL.md +948 -0
  477. package/src/harness_context/skills/ecc/laravel-tdd/LICENSE.ecc +21 -0
  478. package/src/harness_context/skills/ecc/laravel-tdd/SKILL.md +675 -0
  479. package/src/harness_context/skills/ecc/laravel-verification/LICENSE.ecc +21 -0
  480. package/src/harness_context/skills/ecc/laravel-verification/SKILL.md +180 -0
  481. package/src/harness_context/skills/ecc/latency-critical-systems/LICENSE.ecc +21 -0
  482. package/src/harness_context/skills/ecc/latency-critical-systems/SKILL.md +75 -0
  483. package/src/harness_context/skills/ecc/lead-intelligence/LICENSE.ecc +21 -0
  484. package/src/harness_context/skills/ecc/lead-intelligence/SKILL.md +333 -0
  485. package/src/harness_context/skills/ecc/lead-intelligence/agents/enrichment-agent.md +85 -0
  486. package/src/harness_context/skills/ecc/lead-intelligence/agents/mutual-mapper.md +75 -0
  487. package/src/harness_context/skills/ecc/lead-intelligence/agents/outreach-drafter.md +98 -0
  488. package/src/harness_context/skills/ecc/lead-intelligence/agents/signal-scorer.md +60 -0
  489. package/src/harness_context/skills/ecc/liquid-glass-design/LICENSE.ecc +21 -0
  490. package/src/harness_context/skills/ecc/liquid-glass-design/SKILL.md +279 -0
  491. package/src/harness_context/skills/ecc/living-docs-governance/LICENSE.ecc +21 -0
  492. package/src/harness_context/skills/ecc/living-docs-governance/SKILL.md +137 -0
  493. package/src/harness_context/skills/ecc/llm-trading-agent-security/LICENSE.ecc +21 -0
  494. package/src/harness_context/skills/ecc/llm-trading-agent-security/SKILL.md +147 -0
  495. package/src/harness_context/skills/ecc/logistics-exception-management/LICENSE.ecc +21 -0
  496. package/src/harness_context/skills/ecc/logistics-exception-management/SKILL.md +222 -0
  497. package/src/harness_context/skills/ecc/loop-design-check/LICENSE.ecc +21 -0
  498. package/src/harness_context/skills/ecc/loop-design-check/SKILL.md +143 -0
  499. package/src/harness_context/skills/ecc/mailtrap-email-integration/LICENSE.ecc +21 -0
  500. package/src/harness_context/skills/ecc/mailtrap-email-integration/SKILL.md +77 -0
  501. package/src/harness_context/skills/ecc/make-interfaces-feel-better/LICENSE.ecc +21 -0
  502. package/src/harness_context/skills/ecc/make-interfaces-feel-better/SKILL.md +152 -0
  503. package/src/harness_context/skills/ecc/manim-video/LICENSE.ecc +21 -0
  504. package/src/harness_context/skills/ecc/manim-video/SKILL.md +90 -0
  505. package/src/harness_context/skills/ecc/manim-video/assets/network_graph_scene.py +52 -0
  506. package/src/harness_context/skills/ecc/market-research/LICENSE.ecc +21 -0
  507. package/src/harness_context/skills/ecc/market-research/SKILL.md +87 -0
  508. package/src/harness_context/skills/ecc/marketing-campaign/LICENSE.ecc +21 -0
  509. package/src/harness_context/skills/ecc/marketing-campaign/SKILL.md +114 -0
  510. package/src/harness_context/skills/ecc/mcp-server-patterns/LICENSE.ecc +21 -0
  511. package/src/harness_context/skills/ecc/mcp-server-patterns/SKILL.md +70 -0
  512. package/src/harness_context/skills/ecc/messages-ops/LICENSE.ecc +21 -0
  513. package/src/harness_context/skills/ecc/messages-ops/SKILL.md +105 -0
  514. package/src/harness_context/skills/ecc/ml-adoption-playbook/LICENSE.ecc +21 -0
  515. package/src/harness_context/skills/ecc/ml-adoption-playbook/SKILL.md +57 -0
  516. package/src/harness_context/skills/ecc/mle-workflow/LICENSE.ecc +21 -0
  517. package/src/harness_context/skills/ecc/mle-workflow/SKILL.md +348 -0
  518. package/src/harness_context/skills/ecc/motion-advanced/LICENSE.ecc +21 -0
  519. package/src/harness_context/skills/ecc/motion-advanced/SKILL.md +597 -0
  520. package/src/harness_context/skills/ecc/motion-foundations/LICENSE.ecc +21 -0
  521. package/src/harness_context/skills/ecc/motion-foundations/SKILL.md +300 -0
  522. package/src/harness_context/skills/ecc/motion-patterns/LICENSE.ecc +21 -0
  523. package/src/harness_context/skills/ecc/motion-patterns/SKILL.md +435 -0
  524. package/src/harness_context/skills/ecc/motion-ui/LICENSE.ecc +21 -0
  525. package/src/harness_context/skills/ecc/motion-ui/SKILL.md +576 -0
  526. package/src/harness_context/skills/ecc/mysql-patterns/LICENSE.ecc +21 -0
  527. package/src/harness_context/skills/ecc/mysql-patterns/SKILL.md +413 -0
  528. package/src/harness_context/skills/ecc/nanoclaw-repl/LICENSE.ecc +21 -0
  529. package/src/harness_context/skills/ecc/nanoclaw-repl/SKILL.md +34 -0
  530. package/src/harness_context/skills/ecc/nasiko-control-plane/LICENSE.ecc +21 -0
  531. package/src/harness_context/skills/ecc/nasiko-control-plane/SKILL.md +49 -0
  532. package/src/harness_context/skills/ecc/nasiko-control-plane/agents/openai.yaml +4 -0
  533. package/src/harness_context/skills/ecc/nestjs-patterns/LICENSE.ecc +21 -0
  534. package/src/harness_context/skills/ecc/nestjs-patterns/SKILL.md +231 -0
  535. package/src/harness_context/skills/ecc/netmiko-ssh-automation/LICENSE.ecc +21 -0
  536. package/src/harness_context/skills/ecc/netmiko-ssh-automation/SKILL.md +174 -0
  537. package/src/harness_context/skills/ecc/network-bgp-diagnostics/LICENSE.ecc +21 -0
  538. package/src/harness_context/skills/ecc/network-bgp-diagnostics/SKILL.md +168 -0
  539. package/src/harness_context/skills/ecc/network-config-validation/LICENSE.ecc +21 -0
  540. package/src/harness_context/skills/ecc/network-config-validation/SKILL.md +211 -0
  541. package/src/harness_context/skills/ecc/network-interface-health/LICENSE.ecc +21 -0
  542. package/src/harness_context/skills/ecc/network-interface-health/SKILL.md +153 -0
  543. package/src/harness_context/skills/ecc/nextjs-turbopack/LICENSE.ecc +21 -0
  544. package/src/harness_context/skills/ecc/nextjs-turbopack/SKILL.md +58 -0
  545. package/src/harness_context/skills/ecc/nodejs-keccak256/LICENSE.ecc +21 -0
  546. package/src/harness_context/skills/ecc/nodejs-keccak256/SKILL.md +103 -0
  547. package/src/harness_context/skills/ecc/nutrient-document-processing/LICENSE.ecc +21 -0
  548. package/src/harness_context/skills/ecc/nutrient-document-processing/SKILL.md +168 -0
  549. package/src/harness_context/skills/ecc/nuxt4-patterns/LICENSE.ecc +21 -0
  550. package/src/harness_context/skills/ecc/nuxt4-patterns/SKILL.md +101 -0
  551. package/src/harness_context/skills/ecc/openclaw-persona-forge/LICENSE.ecc +21 -0
  552. package/src/harness_context/skills/ecc/openclaw-persona-forge/SKILL.md +289 -0
  553. package/src/harness_context/skills/ecc/openclaw-persona-forge/gacha.py +224 -0
  554. package/src/harness_context/skills/ecc/openclaw-persona-forge/gacha.sh +5 -0
  555. package/src/harness_context/skills/ecc/openclaw-persona-forge/references/avatar-style.md +124 -0
  556. package/src/harness_context/skills/ecc/openclaw-persona-forge/references/boundary-rules.md +53 -0
  557. package/src/harness_context/skills/ecc/openclaw-persona-forge/references/error-handling.md +53 -0
  558. package/src/harness_context/skills/ecc/openclaw-persona-forge/references/identity-tension.md +48 -0
  559. package/src/harness_context/skills/ecc/openclaw-persona-forge/references/naming-system.md +39 -0
  560. package/src/harness_context/skills/ecc/openclaw-persona-forge/references/output-template.md +166 -0
  561. package/src/harness_context/skills/ecc/opensource-pipeline/LICENSE.ecc +21 -0
  562. package/src/harness_context/skills/ecc/opensource-pipeline/SKILL.md +256 -0
  563. package/src/harness_context/skills/ecc/orch-add-feature/LICENSE.ecc +21 -0
  564. package/src/harness_context/skills/ecc/orch-add-feature/SKILL.md +45 -0
  565. package/src/harness_context/skills/ecc/orch-build-mvp/LICENSE.ecc +21 -0
  566. package/src/harness_context/skills/ecc/orch-build-mvp/SKILL.md +49 -0
  567. package/src/harness_context/skills/ecc/orch-change-feature/LICENSE.ecc +21 -0
  568. package/src/harness_context/skills/ecc/orch-change-feature/SKILL.md +43 -0
  569. package/src/harness_context/skills/ecc/orch-fix-defect/LICENSE.ecc +21 -0
  570. package/src/harness_context/skills/ecc/orch-fix-defect/SKILL.md +43 -0
  571. package/src/harness_context/skills/ecc/orch-pipeline/LICENSE.ecc +21 -0
  572. package/src/harness_context/skills/ecc/orch-pipeline/SKILL.md +121 -0
  573. package/src/harness_context/skills/ecc/orch-refine-code/LICENSE.ecc +21 -0
  574. package/src/harness_context/skills/ecc/orch-refine-code/SKILL.md +44 -0
  575. package/src/harness_context/skills/ecc/parallel-execution-optimizer/LICENSE.ecc +21 -0
  576. package/src/harness_context/skills/ecc/parallel-execution-optimizer/SKILL.md +74 -0
  577. package/src/harness_context/skills/ecc/perl-patterns/LICENSE.ecc +21 -0
  578. package/src/harness_context/skills/ecc/perl-patterns/SKILL.md +505 -0
  579. package/src/harness_context/skills/ecc/perl-security/LICENSE.ecc +21 -0
  580. package/src/harness_context/skills/ecc/perl-security/SKILL.md +504 -0
  581. package/src/harness_context/skills/ecc/perl-testing/LICENSE.ecc +21 -0
  582. package/src/harness_context/skills/ecc/perl-testing/SKILL.md +476 -0
  583. package/src/harness_context/skills/ecc/plan-canvas/LICENSE.ecc +21 -0
  584. package/src/harness_context/skills/ecc/plan-canvas/SKILL.md +196 -0
  585. package/src/harness_context/skills/ecc/plan-orchestrate/LICENSE.ecc +21 -0
  586. package/src/harness_context/skills/ecc/plan-orchestrate/SKILL.md +263 -0
  587. package/src/harness_context/skills/ecc/plankton-code-quality/LICENSE.ecc +21 -0
  588. package/src/harness_context/skills/ecc/plankton-code-quality/SKILL.md +237 -0
  589. package/src/harness_context/skills/ecc/postgres-patterns/LICENSE.ecc +21 -0
  590. package/src/harness_context/skills/ecc/postgres-patterns/SKILL.md +148 -0
  591. package/src/harness_context/skills/ecc/prediction-market-oracle-research/LICENSE.ecc +21 -0
  592. package/src/harness_context/skills/ecc/prediction-market-oracle-research/SKILL.md +64 -0
  593. package/src/harness_context/skills/ecc/prediction-market-risk-review/LICENSE.ecc +21 -0
  594. package/src/harness_context/skills/ecc/prediction-market-risk-review/SKILL.md +61 -0
  595. package/src/harness_context/skills/ecc/prisma-patterns/LICENSE.ecc +21 -0
  596. package/src/harness_context/skills/ecc/prisma-patterns/SKILL.md +401 -0
  597. package/src/harness_context/skills/ecc/product-capability/LICENSE.ecc +21 -0
  598. package/src/harness_context/skills/ecc/product-capability/SKILL.md +142 -0
  599. package/src/harness_context/skills/ecc/product-lens/LICENSE.ecc +21 -0
  600. package/src/harness_context/skills/ecc/product-lens/SKILL.md +93 -0
  601. package/src/harness_context/skills/ecc/production-audit/LICENSE.ecc +21 -0
  602. package/src/harness_context/skills/ecc/production-audit/SKILL.md +207 -0
  603. package/src/harness_context/skills/ecc/production-scheduling/LICENSE.ecc +21 -0
  604. package/src/harness_context/skills/ecc/production-scheduling/SKILL.md +238 -0
  605. package/src/harness_context/skills/ecc/project-flow-ops/LICENSE.ecc +21 -0
  606. package/src/harness_context/skills/ecc/project-flow-ops/SKILL.md +112 -0
  607. package/src/harness_context/skills/ecc/prompt-optimizer/LICENSE.ecc +21 -0
  608. package/src/harness_context/skills/ecc/prompt-optimizer/SKILL.md +398 -0
  609. package/src/harness_context/skills/ecc/python-patterns/LICENSE.ecc +21 -0
  610. package/src/harness_context/skills/ecc/python-patterns/SKILL.md +751 -0
  611. package/src/harness_context/skills/ecc/python-testing/LICENSE.ecc +21 -0
  612. package/src/harness_context/skills/ecc/python-testing/SKILL.md +817 -0
  613. package/src/harness_context/skills/ecc/pytorch-patterns/LICENSE.ecc +21 -0
  614. package/src/harness_context/skills/ecc/pytorch-patterns/SKILL.md +397 -0
  615. package/src/harness_context/skills/ecc/quality-nonconformance/LICENSE.ecc +21 -0
  616. package/src/harness_context/skills/ecc/quality-nonconformance/SKILL.md +260 -0
  617. package/src/harness_context/skills/ecc/quarkus-patterns/LICENSE.ecc +21 -0
  618. package/src/harness_context/skills/ecc/quarkus-patterns/SKILL.md +723 -0
  619. package/src/harness_context/skills/ecc/quarkus-security/LICENSE.ecc +21 -0
  620. package/src/harness_context/skills/ecc/quarkus-security/SKILL.md +468 -0
  621. package/src/harness_context/skills/ecc/quarkus-tdd/LICENSE.ecc +21 -0
  622. package/src/harness_context/skills/ecc/quarkus-tdd/SKILL.md +812 -0
  623. package/src/harness_context/skills/ecc/quarkus-verification/LICENSE.ecc +21 -0
  624. package/src/harness_context/skills/ecc/quarkus-verification/SKILL.md +481 -0
  625. package/src/harness_context/skills/ecc/ralphinho-rfc-pipeline/LICENSE.ecc +21 -0
  626. package/src/harness_context/skills/ecc/ralphinho-rfc-pipeline/SKILL.md +68 -0
  627. package/src/harness_context/skills/ecc/react-native-patterns/LICENSE.ecc +21 -0
  628. package/src/harness_context/skills/ecc/react-native-patterns/SKILL.md +326 -0
  629. package/src/harness_context/skills/ecc/react-patterns/LICENSE.ecc +21 -0
  630. package/src/harness_context/skills/ecc/react-patterns/SKILL.md +342 -0
  631. package/src/harness_context/skills/ecc/react-performance/LICENSE.ecc +21 -0
  632. package/src/harness_context/skills/ecc/react-performance/SKILL.md +575 -0
  633. package/src/harness_context/skills/ecc/react-testing/LICENSE.ecc +21 -0
  634. package/src/harness_context/skills/ecc/react-testing/SKILL.md +424 -0
  635. package/src/harness_context/skills/ecc/recsys-pipeline-architect/LICENSE.ecc +21 -0
  636. package/src/harness_context/skills/ecc/recsys-pipeline-architect/SKILL.md +115 -0
  637. package/src/harness_context/skills/ecc/recursive-decision-ledger/LICENSE.ecc +21 -0
  638. package/src/harness_context/skills/ecc/recursive-decision-ledger/SKILL.md +81 -0
  639. package/src/harness_context/skills/ecc/redis-patterns/LICENSE.ecc +21 -0
  640. package/src/harness_context/skills/ecc/redis-patterns/SKILL.md +404 -0
  641. package/src/harness_context/skills/ecc/regex-vs-llm-structured-text/LICENSE.ecc +21 -0
  642. package/src/harness_context/skills/ecc/regex-vs-llm-structured-text/SKILL.md +221 -0
  643. package/src/harness_context/skills/ecc/remotion-video-creation/LICENSE.ecc +21 -0
  644. package/src/harness_context/skills/ecc/remotion-video-creation/SKILL.md +43 -0
  645. package/src/harness_context/skills/ecc/remotion-video-creation/rules/3d.md +86 -0
  646. package/src/harness_context/skills/ecc/remotion-video-creation/rules/animations.md +29 -0
  647. package/src/harness_context/skills/ecc/remotion-video-creation/rules/assets/charts-bar-chart.tsx +173 -0
  648. package/src/harness_context/skills/ecc/remotion-video-creation/rules/assets/text-animations-typewriter.tsx +100 -0
  649. package/src/harness_context/skills/ecc/remotion-video-creation/rules/assets/text-animations-word-highlight.tsx +108 -0
  650. package/src/harness_context/skills/ecc/remotion-video-creation/rules/assets.md +78 -0
  651. package/src/harness_context/skills/ecc/remotion-video-creation/rules/audio.md +172 -0
  652. package/src/harness_context/skills/ecc/remotion-video-creation/rules/calculate-metadata.md +104 -0
  653. package/src/harness_context/skills/ecc/remotion-video-creation/rules/can-decode.md +75 -0
  654. package/src/harness_context/skills/ecc/remotion-video-creation/rules/charts.md +58 -0
  655. package/src/harness_context/skills/ecc/remotion-video-creation/rules/compositions.md +146 -0
  656. package/src/harness_context/skills/ecc/remotion-video-creation/rules/display-captions.md +126 -0
  657. package/src/harness_context/skills/ecc/remotion-video-creation/rules/extract-frames.md +229 -0
  658. package/src/harness_context/skills/ecc/remotion-video-creation/rules/fonts.md +152 -0
  659. package/src/harness_context/skills/ecc/remotion-video-creation/rules/get-audio-duration.md +58 -0
  660. package/src/harness_context/skills/ecc/remotion-video-creation/rules/get-video-dimensions.md +68 -0
  661. package/src/harness_context/skills/ecc/remotion-video-creation/rules/get-video-duration.md +58 -0
  662. package/src/harness_context/skills/ecc/remotion-video-creation/rules/gifs.md +138 -0
  663. package/src/harness_context/skills/ecc/remotion-video-creation/rules/images.md +130 -0
  664. package/src/harness_context/skills/ecc/remotion-video-creation/rules/import-srt-captions.md +67 -0
  665. package/src/harness_context/skills/ecc/remotion-video-creation/rules/lottie.md +67 -0
  666. package/src/harness_context/skills/ecc/remotion-video-creation/rules/measuring-dom-nodes.md +34 -0
  667. package/src/harness_context/skills/ecc/remotion-video-creation/rules/measuring-text.md +143 -0
  668. package/src/harness_context/skills/ecc/remotion-video-creation/rules/sequencing.md +106 -0
  669. package/src/harness_context/skills/ecc/remotion-video-creation/rules/tailwind.md +11 -0
  670. package/src/harness_context/skills/ecc/remotion-video-creation/rules/text-animations.md +20 -0
  671. package/src/harness_context/skills/ecc/remotion-video-creation/rules/timing.md +179 -0
  672. package/src/harness_context/skills/ecc/remotion-video-creation/rules/transcribe-captions.md +19 -0
  673. package/src/harness_context/skills/ecc/remotion-video-creation/rules/transitions.md +122 -0
  674. package/src/harness_context/skills/ecc/remotion-video-creation/rules/trimming.md +52 -0
  675. package/src/harness_context/skills/ecc/remotion-video-creation/rules/videos.md +171 -0
  676. package/src/harness_context/skills/ecc/repo-scan/LICENSE.ecc +21 -0
  677. package/src/harness_context/skills/ecc/repo-scan/SKILL.md +170 -0
  678. package/src/harness_context/skills/ecc/research-ops/LICENSE.ecc +21 -0
  679. package/src/harness_context/skills/ecc/research-ops/SKILL.md +113 -0
  680. package/src/harness_context/skills/ecc/returns-reverse-logistics/LICENSE.ecc +21 -0
  681. package/src/harness_context/skills/ecc/returns-reverse-logistics/SKILL.md +240 -0
  682. package/src/harness_context/skills/ecc/rules-distill/LICENSE.ecc +21 -0
  683. package/src/harness_context/skills/ecc/rules-distill/SKILL.md +265 -0
  684. package/src/harness_context/skills/ecc/rules-distill/scripts/scan-rules.sh +58 -0
  685. package/src/harness_context/skills/ecc/rules-distill/scripts/scan-skills.sh +129 -0
  686. package/src/harness_context/skills/ecc/rust-patterns/LICENSE.ecc +21 -0
  687. package/src/harness_context/skills/ecc/rust-patterns/SKILL.md +500 -0
  688. package/src/harness_context/skills/ecc/rust-testing/LICENSE.ecc +21 -0
  689. package/src/harness_context/skills/ecc/rust-testing/SKILL.md +501 -0
  690. package/src/harness_context/skills/ecc/safety-guard/LICENSE.ecc +21 -0
  691. package/src/harness_context/skills/ecc/safety-guard/SKILL.md +76 -0
  692. package/src/harness_context/skills/ecc/santa-method/LICENSE.ecc +21 -0
  693. package/src/harness_context/skills/ecc/santa-method/SKILL.md +307 -0
  694. package/src/harness_context/skills/ecc/scientific-db-pubmed-database/LICENSE.ecc +21 -0
  695. package/src/harness_context/skills/ecc/scientific-db-pubmed-database/SKILL.md +176 -0
  696. package/src/harness_context/skills/ecc/scientific-db-uspto-database/LICENSE.ecc +21 -0
  697. package/src/harness_context/skills/ecc/scientific-db-uspto-database/SKILL.md +178 -0
  698. package/src/harness_context/skills/ecc/scientific-pkg-gget/LICENSE.ecc +21 -0
  699. package/src/harness_context/skills/ecc/scientific-pkg-gget/SKILL.md +167 -0
  700. package/src/harness_context/skills/ecc/scientific-thinking-literature-review/LICENSE.ecc +21 -0
  701. package/src/harness_context/skills/ecc/scientific-thinking-literature-review/SKILL.md +193 -0
  702. package/src/harness_context/skills/ecc/scientific-thinking-scholar-evaluation/LICENSE.ecc +21 -0
  703. package/src/harness_context/skills/ecc/scientific-thinking-scholar-evaluation/SKILL.md +161 -0
  704. package/src/harness_context/skills/ecc/search-first/LICENSE.ecc +21 -0
  705. package/src/harness_context/skills/ecc/search-first/SKILL.md +183 -0
  706. package/src/harness_context/skills/ecc/security-bounty-hunter/LICENSE.ecc +21 -0
  707. package/src/harness_context/skills/ecc/security-bounty-hunter/SKILL.md +100 -0
  708. package/src/harness_context/skills/ecc/security-review/LICENSE.ecc +21 -0
  709. package/src/harness_context/skills/ecc/security-review/SKILL.md +504 -0
  710. package/src/harness_context/skills/ecc/security-review/cloud-infrastructure-security.md +361 -0
  711. package/src/harness_context/skills/ecc/security-scan/LICENSE.ecc +21 -0
  712. package/src/harness_context/skills/ecc/security-scan/SKILL.md +166 -0
  713. package/src/harness_context/skills/ecc/seo/LICENSE.ecc +21 -0
  714. package/src/harness_context/skills/ecc/seo/SKILL.md +155 -0
  715. package/src/harness_context/skills/ecc/skill-comply/LICENSE.ecc +21 -0
  716. package/src/harness_context/skills/ecc/skill-comply/SKILL.md +59 -0
  717. package/src/harness_context/skills/ecc/skill-comply/fixtures/compliant_trace.jsonl +5 -0
  718. package/src/harness_context/skills/ecc/skill-comply/fixtures/noncompliant_trace.jsonl +3 -0
  719. package/src/harness_context/skills/ecc/skill-comply/fixtures/tdd_spec.yaml +44 -0
  720. package/src/harness_context/skills/ecc/skill-comply/prompts/classifier.md +24 -0
  721. package/src/harness_context/skills/ecc/skill-comply/prompts/scenario_generator.md +62 -0
  722. package/src/harness_context/skills/ecc/skill-comply/prompts/spec_generator.md +42 -0
  723. package/src/harness_context/skills/ecc/skill-comply/pyproject.toml +16 -0
  724. package/src/harness_context/skills/ecc/skill-comply/scripts/__init__.py +0 -0
  725. package/src/harness_context/skills/ecc/skill-comply/scripts/classifier.py +85 -0
  726. package/src/harness_context/skills/ecc/skill-comply/scripts/grader.py +124 -0
  727. package/src/harness_context/skills/ecc/skill-comply/scripts/parser.py +107 -0
  728. package/src/harness_context/skills/ecc/skill-comply/scripts/report.py +170 -0
  729. package/src/harness_context/skills/ecc/skill-comply/scripts/run.py +127 -0
  730. package/src/harness_context/skills/ecc/skill-comply/scripts/runner.py +245 -0
  731. package/src/harness_context/skills/ecc/skill-comply/scripts/scenario_generator.py +70 -0
  732. package/src/harness_context/skills/ecc/skill-comply/scripts/spec_generator.py +72 -0
  733. package/src/harness_context/skills/ecc/skill-comply/scripts/utils.py +13 -0
  734. package/src/harness_context/skills/ecc/skill-comply/tests/test_grader.py +197 -0
  735. package/src/harness_context/skills/ecc/skill-comply/tests/test_parser.py +90 -0
  736. package/src/harness_context/skills/ecc/skill-comply/tests/test_runner.py +316 -0
  737. package/src/harness_context/skills/ecc/skill-scout/LICENSE.ecc +21 -0
  738. package/src/harness_context/skills/ecc/skill-scout/SKILL.md +141 -0
  739. package/src/harness_context/skills/ecc/skill-stocktake/LICENSE.ecc +21 -0
  740. package/src/harness_context/skills/ecc/skill-stocktake/SKILL.md +195 -0
  741. package/src/harness_context/skills/ecc/skill-stocktake/scripts/quick-diff.sh +118 -0
  742. package/src/harness_context/skills/ecc/skill-stocktake/scripts/save-results.sh +56 -0
  743. package/src/harness_context/skills/ecc/skill-stocktake/scripts/scan.sh +211 -0
  744. package/src/harness_context/skills/ecc/social-graph-ranker/LICENSE.ecc +21 -0
  745. package/src/harness_context/skills/ecc/social-graph-ranker/SKILL.md +155 -0
  746. package/src/harness_context/skills/ecc/social-publisher/LICENSE.ecc +21 -0
  747. package/src/harness_context/skills/ecc/social-publisher/SKILL.md +139 -0
  748. package/src/harness_context/skills/ecc/springboot-patterns/LICENSE.ecc +21 -0
  749. package/src/harness_context/skills/ecc/springboot-patterns/SKILL.md +315 -0
  750. package/src/harness_context/skills/ecc/springboot-security/LICENSE.ecc +21 -0
  751. package/src/harness_context/skills/ecc/springboot-security/SKILL.md +273 -0
  752. package/src/harness_context/skills/ecc/springboot-tdd/LICENSE.ecc +21 -0
  753. package/src/harness_context/skills/ecc/springboot-tdd/SKILL.md +159 -0
  754. package/src/harness_context/skills/ecc/springboot-verification/LICENSE.ecc +21 -0
  755. package/src/harness_context/skills/ecc/springboot-verification/SKILL.md +232 -0
  756. package/src/harness_context/skills/ecc/strategic-compact/LICENSE.ecc +21 -0
  757. package/src/harness_context/skills/ecc/strategic-compact/SKILL.md +156 -0
  758. package/src/harness_context/skills/ecc/swift-actor-persistence/LICENSE.ecc +21 -0
  759. package/src/harness_context/skills/ecc/swift-actor-persistence/SKILL.md +144 -0
  760. package/src/harness_context/skills/ecc/swift-concurrency-6-2/LICENSE.ecc +21 -0
  761. package/src/harness_context/skills/ecc/swift-concurrency-6-2/SKILL.md +216 -0
  762. package/src/harness_context/skills/ecc/swift-protocol-di-testing/LICENSE.ecc +21 -0
  763. package/src/harness_context/skills/ecc/swift-protocol-di-testing/SKILL.md +191 -0
  764. package/src/harness_context/skills/ecc/swiftui-patterns/LICENSE.ecc +21 -0
  765. package/src/harness_context/skills/ecc/swiftui-patterns/SKILL.md +259 -0
  766. package/src/harness_context/skills/ecc/taste/LICENSE.ecc +21 -0
  767. package/src/harness_context/skills/ecc/taste/SKILL.md +264 -0
  768. package/src/harness_context/skills/ecc/taste/references/genre-taxonomy.md +87 -0
  769. package/src/harness_context/skills/ecc/tasteforge-video/LICENSE.ecc +21 -0
  770. package/src/harness_context/skills/ecc/tasteforge-video/SKILL.md +192 -0
  771. package/src/harness_context/skills/ecc/tdd-workflow/LICENSE.ecc +21 -0
  772. package/src/harness_context/skills/ecc/tdd-workflow/SKILL.md +583 -0
  773. package/src/harness_context/skills/ecc/team-agent-orchestration/LICENSE.ecc +21 -0
  774. package/src/harness_context/skills/ecc/team-agent-orchestration/SKILL.md +111 -0
  775. package/src/harness_context/skills/ecc/team-builder/LICENSE.ecc +21 -0
  776. package/src/harness_context/skills/ecc/team-builder/SKILL.md +169 -0
  777. package/src/harness_context/skills/ecc/terminal-opener/LICENSE.ecc +21 -0
  778. package/src/harness_context/skills/ecc/terminal-opener/SKILL.md +55 -0
  779. package/src/harness_context/skills/ecc/terminal-opener/agents/openai.yaml +4 -0
  780. package/src/harness_context/skills/ecc/terminal-opener/scripts/open-terminal.js +396 -0
  781. package/src/harness_context/skills/ecc/terminal-ops/LICENSE.ecc +21 -0
  782. package/src/harness_context/skills/ecc/terminal-ops/SKILL.md +110 -0
  783. package/src/harness_context/skills/ecc/tinystruct-patterns/LICENSE.ecc +21 -0
  784. package/src/harness_context/skills/ecc/tinystruct-patterns/SKILL.md +279 -0
  785. package/src/harness_context/skills/ecc/tinystruct-patterns/references/architecture.md +90 -0
  786. package/src/harness_context/skills/ecc/tinystruct-patterns/references/data-handling.md +60 -0
  787. package/src/harness_context/skills/ecc/tinystruct-patterns/references/database.md +99 -0
  788. package/src/harness_context/skills/ecc/tinystruct-patterns/references/routing.md +64 -0
  789. package/src/harness_context/skills/ecc/tinystruct-patterns/references/system-usage.md +97 -0
  790. package/src/harness_context/skills/ecc/tinystruct-patterns/references/testing.md +72 -0
  791. package/src/harness_context/skills/ecc/token-budget-advisor/LICENSE.ecc +21 -0
  792. package/src/harness_context/skills/ecc/token-budget-advisor/SKILL.md +134 -0
  793. package/src/harness_context/skills/ecc/ui-demo/LICENSE.ecc +21 -0
  794. package/src/harness_context/skills/ecc/ui-demo/SKILL.md +466 -0
  795. package/src/harness_context/skills/ecc/ui-to-vue/LICENSE.ecc +21 -0
  796. package/src/harness_context/skills/ecc/ui-to-vue/SKILL.md +135 -0
  797. package/src/harness_context/skills/ecc/uncloud/LICENSE.ecc +21 -0
  798. package/src/harness_context/skills/ecc/uncloud/SKILL.md +344 -0
  799. package/src/harness_context/skills/ecc/unified-memory/LICENSE.ecc +21 -0
  800. package/src/harness_context/skills/ecc/unified-memory/SKILL.md +170 -0
  801. package/src/harness_context/skills/ecc/unified-notifications-ops/LICENSE.ecc +21 -0
  802. package/src/harness_context/skills/ecc/unified-notifications-ops/SKILL.md +188 -0
  803. package/src/harness_context/skills/ecc/verification-loop/LICENSE.ecc +21 -0
  804. package/src/harness_context/skills/ecc/verification-loop/SKILL.md +129 -0
  805. package/src/harness_context/skills/ecc/video-editing/LICENSE.ecc +21 -0
  806. package/src/harness_context/skills/ecc/video-editing/SKILL.md +311 -0
  807. package/src/harness_context/skills/ecc/videodb/LICENSE.ecc +21 -0
  808. package/src/harness_context/skills/ecc/videodb/SKILL.md +375 -0
  809. package/src/harness_context/skills/ecc/videodb/reference/api-reference.md +550 -0
  810. package/src/harness_context/skills/ecc/videodb/reference/capture-reference.md +407 -0
  811. package/src/harness_context/skills/ecc/videodb/reference/capture.md +101 -0
  812. package/src/harness_context/skills/ecc/videodb/reference/editor.md +443 -0
  813. package/src/harness_context/skills/ecc/videodb/reference/generative.md +331 -0
  814. package/src/harness_context/skills/ecc/videodb/reference/rtstream-reference.md +564 -0
  815. package/src/harness_context/skills/ecc/videodb/reference/rtstream.md +65 -0
  816. package/src/harness_context/skills/ecc/videodb/reference/search.md +230 -0
  817. package/src/harness_context/skills/ecc/videodb/reference/streaming.md +406 -0
  818. package/src/harness_context/skills/ecc/videodb/reference/use-cases.md +118 -0
  819. package/src/harness_context/skills/ecc/videodb/scripts/ws_listener.py +282 -0
  820. package/src/harness_context/skills/ecc/visa-doc-translate/LICENSE.ecc +21 -0
  821. package/src/harness_context/skills/ecc/visa-doc-translate/README.md +86 -0
  822. package/src/harness_context/skills/ecc/visa-doc-translate/SKILL.md +117 -0
  823. package/src/harness_context/skills/ecc/vite-patterns/LICENSE.ecc +21 -0
  824. package/src/harness_context/skills/ecc/vite-patterns/SKILL.md +450 -0
  825. package/src/harness_context/skills/ecc/vue-patterns/LICENSE.ecc +21 -0
  826. package/src/harness_context/skills/ecc/vue-patterns/SKILL.md +471 -0
  827. package/src/harness_context/skills/ecc/windows-desktop-e2e/LICENSE.ecc +21 -0
  828. package/src/harness_context/skills/ecc/windows-desktop-e2e/SKILL.md +888 -0
  829. package/src/harness_context/skills/ecc/workspace-surface-audit/LICENSE.ecc +21 -0
  830. package/src/harness_context/skills/ecc/workspace-surface-audit/SKILL.md +126 -0
  831. package/src/harness_context/skills/ecc/x-api/LICENSE.ecc +21 -0
  832. package/src/harness_context/skills/ecc/x-api/SKILL.md +244 -0
  833. package/src/harness_context/skills/ecc_catalog.json +2233 -0
  834. package/src/harness_context/skills/router.py +394 -0
  835. package/src/harness_context/workspace/roots.py +1 -1
@@ -0,0 +1,1420 @@
1
+ """Tests for continuous-learning-v2 instinct-cli.py
2
+
3
+ Covers:
4
+ - parse_instinct_file() — content preservation, edge cases
5
+ - _validate_file_path() — path traversal blocking
6
+ - detect_project() — project detection with mocked git/env
7
+ - load_all_instincts() — loading from project + global dirs, dedup
8
+ - _load_instincts_from_dir() — directory scanning
9
+ - cmd_projects() — listing projects from registry
10
+ - cmd_status() — status display
11
+ - _promote_specific() — single instinct promotion
12
+ - _promote_auto() — auto-promotion across projects
13
+ """
14
+
15
+ import importlib.util
16
+ import io
17
+ import json
18
+ import os
19
+ import sys
20
+ from pathlib import Path
21
+ from types import SimpleNamespace
22
+
23
+ import pytest
24
+
25
+ # Load instinct-cli.py (hyphenated filename requires importlib)
26
+ _spec = importlib.util.spec_from_file_location(
27
+ "instinct_cli",
28
+ os.path.join(os.path.dirname(__file__), "instinct-cli.py"),
29
+ )
30
+ _mod = importlib.util.module_from_spec(_spec)
31
+ _spec.loader.exec_module(_mod)
32
+
33
+ parse_instinct_file = _mod.parse_instinct_file
34
+ _validate_file_path = _mod._validate_file_path
35
+ detect_project = _mod.detect_project
36
+ load_all_instincts = _mod.load_all_instincts
37
+ load_project_only_instincts = _mod.load_project_only_instincts
38
+ _load_instincts_from_dir = _mod._load_instincts_from_dir
39
+ cmd_status = _mod.cmd_status
40
+ cmd_projects = _mod.cmd_projects
41
+ _promote_specific = _mod._promote_specific
42
+ _promote_auto = _mod._promote_auto
43
+ _find_cross_project_instincts = _mod._find_cross_project_instincts
44
+ load_registry = _mod.load_registry
45
+ _validate_instinct_id = _mod._validate_instinct_id
46
+ _validate_import_url = _mod._validate_import_url
47
+ _update_registry = _mod._update_registry
48
+ _write_registry = _mod._write_registry
49
+ _remove_project_storage = _mod._remove_project_storage
50
+ _confidence_bar = _mod._confidence_bar
51
+
52
+
53
+ # ─────────────────────────────────────────────
54
+ # Fixtures
55
+ # ─────────────────────────────────────────────
56
+
57
+ SAMPLE_INSTINCT_YAML = """\
58
+ ---
59
+ id: test-instinct
60
+ trigger: "when writing tests"
61
+ confidence: 0.8
62
+ domain: testing
63
+ scope: project
64
+ ---
65
+
66
+ ## Action
67
+ Always write tests first.
68
+
69
+ ## Evidence
70
+ TDD leads to better design.
71
+ """
72
+
73
+ SAMPLE_GLOBAL_INSTINCT_YAML = """\
74
+ ---
75
+ id: global-instinct
76
+ trigger: "always"
77
+ confidence: 0.9
78
+ domain: security
79
+ scope: global
80
+ ---
81
+
82
+ ## Action
83
+ Validate all user input.
84
+ """
85
+
86
+
87
+ @pytest.fixture
88
+ def project_tree(tmp_path):
89
+ """Create a realistic project directory tree for testing."""
90
+ homunculus = tmp_path / ".claude" / "homunculus"
91
+ projects_dir = homunculus / "projects"
92
+ global_personal = homunculus / "instincts" / "personal"
93
+ global_inherited = homunculus / "instincts" / "inherited"
94
+ global_evolved = homunculus / "evolved"
95
+
96
+ for d in [
97
+ global_personal, global_inherited,
98
+ global_evolved / "skills", global_evolved / "commands", global_evolved / "agents",
99
+ projects_dir,
100
+ ]:
101
+ d.mkdir(parents=True, exist_ok=True)
102
+
103
+ return {
104
+ "root": tmp_path,
105
+ "homunculus": homunculus,
106
+ "projects_dir": projects_dir,
107
+ "global_personal": global_personal,
108
+ "global_inherited": global_inherited,
109
+ "global_evolved": global_evolved,
110
+ "registry_file": homunculus / "projects.json",
111
+ }
112
+
113
+
114
+ @pytest.fixture
115
+ def patch_globals(project_tree, monkeypatch):
116
+ """Patch module-level globals to use tmp_path-based directories."""
117
+ monkeypatch.setattr(_mod, "HOMUNCULUS_DIR", project_tree["homunculus"])
118
+ monkeypatch.setattr(_mod, "PROJECTS_DIR", project_tree["projects_dir"])
119
+ monkeypatch.setattr(_mod, "REGISTRY_FILE", project_tree["registry_file"])
120
+ monkeypatch.setattr(_mod, "GLOBAL_PERSONAL_DIR", project_tree["global_personal"])
121
+ monkeypatch.setattr(_mod, "GLOBAL_INHERITED_DIR", project_tree["global_inherited"])
122
+ monkeypatch.setattr(_mod, "GLOBAL_EVOLVED_DIR", project_tree["global_evolved"])
123
+ monkeypatch.setattr(_mod, "GLOBAL_OBSERVATIONS_FILE", project_tree["homunculus"] / "observations.jsonl")
124
+ return project_tree
125
+
126
+
127
+ def _make_project(tree, pid="abc123", pname="test-project"):
128
+ """Create project directory structure and return a project dict."""
129
+ project_dir = tree["projects_dir"] / pid
130
+ personal_dir = project_dir / "instincts" / "personal"
131
+ inherited_dir = project_dir / "instincts" / "inherited"
132
+ for d in [personal_dir, inherited_dir,
133
+ project_dir / "evolved" / "skills",
134
+ project_dir / "evolved" / "commands",
135
+ project_dir / "evolved" / "agents",
136
+ project_dir / "observations.archive"]:
137
+ d.mkdir(parents=True, exist_ok=True)
138
+
139
+ return {
140
+ "id": pid,
141
+ "name": pname,
142
+ "root": str(tree["root"] / "fake-repo"),
143
+ "remote": "https://github.com/test/test-project.git",
144
+ "project_dir": project_dir,
145
+ "instincts_personal": personal_dir,
146
+ "instincts_inherited": inherited_dir,
147
+ "evolved_dir": project_dir / "evolved",
148
+ "observations_file": project_dir / "observations.jsonl",
149
+ }
150
+
151
+
152
+ # ─────────────────────────────────────────────
153
+ # parse_instinct_file tests
154
+ # ─────────────────────────────────────────────
155
+
156
+ MULTI_SECTION = """\
157
+ ---
158
+ id: instinct-a
159
+ trigger: "when coding"
160
+ confidence: 0.9
161
+ domain: general
162
+ ---
163
+
164
+ ## Action
165
+ Do thing A.
166
+
167
+ ## Examples
168
+ - Example A1
169
+
170
+ ---
171
+ id: instinct-b
172
+ trigger: "when testing"
173
+ confidence: 0.7
174
+ domain: testing
175
+ ---
176
+
177
+ ## Action
178
+ Do thing B.
179
+ """
180
+
181
+
182
+ def test_multiple_instincts_preserve_content():
183
+ result = parse_instinct_file(MULTI_SECTION)
184
+ assert len(result) == 2
185
+ assert "Do thing A." in result[0]["content"]
186
+ assert "Example A1" in result[0]["content"]
187
+ assert "Do thing B." in result[1]["content"]
188
+
189
+
190
+ def test_single_instinct_preserves_content():
191
+ content = """\
192
+ ---
193
+ id: solo
194
+ trigger: "when reviewing"
195
+ confidence: 0.8
196
+ domain: review
197
+ ---
198
+
199
+ ## Action
200
+ Check for security issues.
201
+
202
+ ## Evidence
203
+ Prevents vulnerabilities.
204
+ """
205
+ result = parse_instinct_file(content)
206
+ assert len(result) == 1
207
+ assert "Check for security issues." in result[0]["content"]
208
+ assert "Prevents vulnerabilities." in result[0]["content"]
209
+
210
+
211
+ def test_empty_content_no_error():
212
+ content = """\
213
+ ---
214
+ id: empty
215
+ trigger: "placeholder"
216
+ confidence: 0.5
217
+ domain: general
218
+ ---
219
+ """
220
+ result = parse_instinct_file(content)
221
+ assert len(result) == 1
222
+ assert result[0]["content"] == ""
223
+
224
+
225
+ def test_parse_no_id_skipped():
226
+ """Instincts without an 'id' field should be silently dropped."""
227
+ content = """\
228
+ ---
229
+ trigger: "when doing nothing"
230
+ confidence: 0.5
231
+ ---
232
+
233
+ No id here.
234
+ """
235
+ result = parse_instinct_file(content)
236
+ assert len(result) == 0
237
+
238
+
239
+ def test_parse_confidence_is_float():
240
+ content = """\
241
+ ---
242
+ id: float-check
243
+ trigger: "when parsing"
244
+ confidence: 0.42
245
+ domain: general
246
+ ---
247
+
248
+ Body.
249
+ """
250
+ result = parse_instinct_file(content)
251
+ assert isinstance(result[0]["confidence"], float)
252
+ assert result[0]["confidence"] == pytest.approx(0.42)
253
+
254
+
255
+ def test_parse_trigger_strips_quotes():
256
+ content = """\
257
+ ---
258
+ id: quote-check
259
+ trigger: "when quoting"
260
+ confidence: 0.5
261
+ domain: general
262
+ ---
263
+
264
+ Body.
265
+ """
266
+ result = parse_instinct_file(content)
267
+ assert result[0]["trigger"] == "when quoting"
268
+
269
+
270
+ def test_parse_empty_string():
271
+ result = parse_instinct_file("")
272
+ assert result == []
273
+
274
+
275
+ def test_parse_garbage_input():
276
+ result = parse_instinct_file("this is not yaml at all\nno frontmatter here")
277
+ assert result == []
278
+
279
+
280
+ # ─────────────────────────────────────────────
281
+ # _validate_file_path tests
282
+ # ─────────────────────────────────────────────
283
+
284
+ def test_validate_normal_path(tmp_path):
285
+ test_file = tmp_path / "test.yaml"
286
+ test_file.write_text("hello")
287
+ result = _validate_file_path(str(test_file), must_exist=True)
288
+ assert result == test_file.resolve()
289
+
290
+
291
+ def test_validate_rejects_etc():
292
+ with pytest.raises(ValueError, match="system directory"):
293
+ _validate_file_path("/etc/passwd")
294
+
295
+
296
+ def test_validate_rejects_var_log():
297
+ with pytest.raises(ValueError, match="system directory"):
298
+ _validate_file_path("/var/log/syslog")
299
+
300
+
301
+ def test_validate_rejects_usr():
302
+ with pytest.raises(ValueError, match="system directory"):
303
+ _validate_file_path("/usr/local/bin/foo")
304
+
305
+
306
+ def test_validate_rejects_proc():
307
+ with pytest.raises(ValueError, match="system directory"):
308
+ _validate_file_path("/proc/self/status")
309
+
310
+
311
+ def test_validate_must_exist_fails(tmp_path):
312
+ with pytest.raises(ValueError, match="does not exist"):
313
+ _validate_file_path(str(tmp_path / "nonexistent.yaml"), must_exist=True)
314
+
315
+
316
+ def test_validate_home_expansion(tmp_path):
317
+ """Tilde expansion should work."""
318
+ result = _validate_file_path("~/test.yaml")
319
+ assert str(result).startswith(str(Path.home()))
320
+
321
+
322
+ def test_validate_relative_path(tmp_path, monkeypatch):
323
+ """Relative paths should be resolved."""
324
+ monkeypatch.chdir(tmp_path)
325
+ test_file = tmp_path / "rel.yaml"
326
+ test_file.write_text("content")
327
+ result = _validate_file_path("rel.yaml", must_exist=True)
328
+ assert result == test_file.resolve()
329
+
330
+
331
+ def test_validate_import_url_rejects_http():
332
+ """Remote imports should not downgrade to plaintext HTTP."""
333
+ with pytest.raises(ValueError, match="require https"):
334
+ _validate_import_url("http://example.com/instincts.yaml")
335
+
336
+
337
+ def test_validate_import_url_rejects_private_hosts(monkeypatch):
338
+ """Remote imports should not resolve to private or loopback addresses."""
339
+ monkeypatch.setattr(
340
+ _mod.socket,
341
+ "getaddrinfo",
342
+ lambda *args, **kwargs: [(None, None, None, None, ("127.0.0.1", 443))],
343
+ )
344
+ with pytest.raises(ValueError, match="non-public address"):
345
+ _validate_import_url("https://example.com/instincts.yaml")
346
+
347
+
348
+ def test_validate_import_url_allows_public_https(monkeypatch):
349
+ monkeypatch.setattr(
350
+ _mod.socket,
351
+ "getaddrinfo",
352
+ lambda *args, **kwargs: [(None, None, None, None, ("93.184.216.34", 443))],
353
+ )
354
+ assert _validate_import_url("https://example.com/instincts.yaml") == "https://example.com/instincts.yaml"
355
+
356
+
357
+ # ─────────────────────────────────────────────
358
+ # detect_project tests
359
+ # ─────────────────────────────────────────────
360
+
361
+ def test_detect_project_global_fallback(patch_globals, monkeypatch):
362
+ """When no git and no env var, should return global project."""
363
+ monkeypatch.delenv("CLAUDE_PROJECT_DIR", raising=False)
364
+
365
+ # Mock subprocess.run to simulate git not available
366
+ def mock_run(*args, **kwargs):
367
+ raise FileNotFoundError("git not found")
368
+
369
+ monkeypatch.setattr("subprocess.run", mock_run)
370
+
371
+ project = detect_project()
372
+ assert project["id"] == "global"
373
+ assert project["name"] == "global"
374
+
375
+
376
+ def test_detect_project_from_env(patch_globals, monkeypatch, tmp_path):
377
+ """CLAUDE_PROJECT_DIR env var should be used as project root."""
378
+ fake_repo = tmp_path / "my-repo"
379
+ fake_repo.mkdir()
380
+ monkeypatch.setenv("CLAUDE_PROJECT_DIR", str(fake_repo))
381
+
382
+ # Mock git remote to return a URL
383
+ def mock_run(cmd, **kwargs):
384
+ if "rev-parse" in cmd:
385
+ return SimpleNamespace(returncode=0, stdout=str(fake_repo) + "\n", stderr="")
386
+ if "get-url" in cmd:
387
+ return SimpleNamespace(returncode=0, stdout="https://github.com/test/my-repo.git\n", stderr="")
388
+ return SimpleNamespace(returncode=1, stdout="", stderr="")
389
+
390
+ monkeypatch.setattr("subprocess.run", mock_run)
391
+
392
+ project = detect_project()
393
+ assert project["id"] != "global"
394
+ assert project["name"] == "my-repo"
395
+
396
+
397
+ def test_detect_project_git_timeout(patch_globals, monkeypatch):
398
+ """Git timeout should fall through to global."""
399
+ monkeypatch.delenv("CLAUDE_PROJECT_DIR", raising=False)
400
+ import subprocess as sp
401
+
402
+ def mock_run(cmd, **kwargs):
403
+ raise sp.TimeoutExpired(cmd, 5)
404
+
405
+ monkeypatch.setattr("subprocess.run", mock_run)
406
+
407
+ project = detect_project()
408
+ assert project["id"] == "global"
409
+
410
+
411
+ def test_detect_project_creates_directories(patch_globals, monkeypatch, tmp_path):
412
+ """detect_project should create the project dir structure."""
413
+ fake_repo = tmp_path / "structured-repo"
414
+ fake_repo.mkdir()
415
+ monkeypatch.setenv("CLAUDE_PROJECT_DIR", str(fake_repo))
416
+
417
+ def mock_run(cmd, **kwargs):
418
+ if "rev-parse" in cmd:
419
+ return SimpleNamespace(returncode=0, stdout=str(fake_repo) + "\n", stderr="")
420
+ if "get-url" in cmd:
421
+ return SimpleNamespace(returncode=1, stdout="", stderr="no remote")
422
+ return SimpleNamespace(returncode=1, stdout="", stderr="")
423
+
424
+ monkeypatch.setattr("subprocess.run", mock_run)
425
+
426
+ project = detect_project()
427
+ assert project["instincts_personal"].exists()
428
+ assert project["instincts_inherited"].exists()
429
+ assert (project["evolved_dir"] / "skills").exists()
430
+
431
+
432
+ # ─────────────────────────────────────────────
433
+ # _load_instincts_from_dir tests
434
+ # ─────────────────────────────────────────────
435
+
436
+ def test_load_from_empty_dir(tmp_path):
437
+ result = _load_instincts_from_dir(tmp_path, "personal", "project")
438
+ assert result == []
439
+
440
+
441
+ def test_load_from_nonexistent_dir(tmp_path):
442
+ result = _load_instincts_from_dir(tmp_path / "does-not-exist", "personal", "project")
443
+ assert result == []
444
+
445
+
446
+ def test_load_annotates_metadata(tmp_path):
447
+ """Loaded instincts should have _source_file, _source_type, _scope_label."""
448
+ yaml_file = tmp_path / "test.yaml"
449
+ yaml_file.write_text(SAMPLE_INSTINCT_YAML)
450
+
451
+ result = _load_instincts_from_dir(tmp_path, "personal", "project")
452
+ assert len(result) == 1
453
+ assert result[0]["_source_file"] == str(yaml_file)
454
+ assert result[0]["_source_type"] == "personal"
455
+ assert result[0]["_scope_label"] == "project"
456
+
457
+
458
+ def test_load_defaults_scope_from_label(tmp_path):
459
+ """If an instinct has no 'scope' in frontmatter, it should default to scope_label."""
460
+ no_scope_yaml = """\
461
+ ---
462
+ id: no-scope
463
+ trigger: "test"
464
+ confidence: 0.5
465
+ domain: general
466
+ ---
467
+
468
+ Body.
469
+ """
470
+ (tmp_path / "no-scope.yaml").write_text(no_scope_yaml)
471
+ result = _load_instincts_from_dir(tmp_path, "inherited", "global")
472
+ assert result[0]["scope"] == "global"
473
+
474
+
475
+ def test_load_preserves_explicit_scope(tmp_path):
476
+ """If frontmatter has explicit scope, it should be preserved."""
477
+ yaml_file = tmp_path / "test.yaml"
478
+ yaml_file.write_text(SAMPLE_INSTINCT_YAML)
479
+
480
+ result = _load_instincts_from_dir(tmp_path, "personal", "global")
481
+ # Frontmatter says scope: project, scope_label is global
482
+ # The explicit scope should be preserved (not overwritten)
483
+ assert result[0]["scope"] == "project"
484
+
485
+
486
+ def test_load_handles_corrupt_file(tmp_path, capsys):
487
+ """Corrupt YAML files should be warned about but not crash."""
488
+ # A file that will cause parse_instinct_file to return empty
489
+ (tmp_path / "good.yaml").write_text(SAMPLE_INSTINCT_YAML)
490
+ (tmp_path / "bad.yaml").write_text("not yaml\nno frontmatter")
491
+
492
+ result = _load_instincts_from_dir(tmp_path, "personal", "project")
493
+ # bad.yaml has no valid instincts (no id), so only good.yaml contributes
494
+ assert len(result) == 1
495
+ assert result[0]["id"] == "test-instinct"
496
+
497
+
498
+ def test_load_supports_yml_extension(tmp_path):
499
+ yml_file = tmp_path / "test.yml"
500
+ yml_file.write_text(SAMPLE_INSTINCT_YAML)
501
+
502
+ result = _load_instincts_from_dir(tmp_path, "personal", "project")
503
+ ids = {i["id"] for i in result}
504
+ assert "test-instinct" in ids
505
+
506
+
507
+ def test_load_supports_md_extension(tmp_path):
508
+ md_file = tmp_path / "legacy-instinct.md"
509
+ md_file.write_text(SAMPLE_INSTINCT_YAML)
510
+
511
+ result = _load_instincts_from_dir(tmp_path, "personal", "project")
512
+ ids = {i["id"] for i in result}
513
+ assert "test-instinct" in ids
514
+
515
+
516
+ def test_load_instincts_from_dir_uses_utf8_encoding(tmp_path, monkeypatch):
517
+ yaml_file = tmp_path / "test.yaml"
518
+ yaml_file.write_text("placeholder")
519
+ calls = []
520
+
521
+ def fake_read_text(self, *args, **kwargs):
522
+ calls.append(kwargs.get("encoding"))
523
+ return SAMPLE_INSTINCT_YAML
524
+
525
+ monkeypatch.setattr(Path, "read_text", fake_read_text)
526
+ result = _load_instincts_from_dir(tmp_path, "personal", "project")
527
+ assert result[0]["id"] == "test-instinct"
528
+ assert calls == ["utf-8"]
529
+
530
+
531
+ # ─────────────────────────────────────────────
532
+ # load_all_instincts tests
533
+ # ─────────────────────────────────────────────
534
+
535
+ def test_load_all_project_and_global(patch_globals):
536
+ """Should load from both project and global directories."""
537
+ tree = patch_globals
538
+ project = _make_project(tree)
539
+
540
+ # Write a project instinct
541
+ (project["instincts_personal"] / "proj.yaml").write_text(SAMPLE_INSTINCT_YAML)
542
+ # Write a global instinct
543
+ (tree["global_personal"] / "glob.yaml").write_text(SAMPLE_GLOBAL_INSTINCT_YAML)
544
+
545
+ result = load_all_instincts(project)
546
+ ids = {i["id"] for i in result}
547
+ assert "test-instinct" in ids
548
+ assert "global-instinct" in ids
549
+
550
+
551
+ def test_load_all_project_overrides_global(patch_globals):
552
+ """When project and global have same ID, project wins."""
553
+ tree = patch_globals
554
+ project = _make_project(tree)
555
+
556
+ # Same ID but different confidence
557
+ proj_yaml = SAMPLE_INSTINCT_YAML.replace("id: test-instinct", "id: shared-id")
558
+ proj_yaml = proj_yaml.replace("confidence: 0.8", "confidence: 0.9")
559
+ glob_yaml = SAMPLE_GLOBAL_INSTINCT_YAML.replace("id: global-instinct", "id: shared-id")
560
+ glob_yaml = glob_yaml.replace("confidence: 0.9", "confidence: 0.3")
561
+
562
+ (project["instincts_personal"] / "shared.yaml").write_text(proj_yaml)
563
+ (tree["global_personal"] / "shared.yaml").write_text(glob_yaml)
564
+
565
+ result = load_all_instincts(project)
566
+ shared = [i for i in result if i["id"] == "shared-id"]
567
+ assert len(shared) == 1
568
+ assert shared[0]["_scope_label"] == "project"
569
+ assert shared[0]["confidence"] == 0.9
570
+
571
+
572
+ def test_load_all_global_only(patch_globals):
573
+ """Global project should only load global instincts."""
574
+ tree = patch_globals
575
+ (tree["global_personal"] / "glob.yaml").write_text(SAMPLE_GLOBAL_INSTINCT_YAML)
576
+
577
+ global_project = {
578
+ "id": "global",
579
+ "name": "global",
580
+ "root": "",
581
+ "project_dir": tree["homunculus"],
582
+ "instincts_personal": tree["global_personal"],
583
+ "instincts_inherited": tree["global_inherited"],
584
+ "evolved_dir": tree["global_evolved"],
585
+ "observations_file": tree["homunculus"] / "observations.jsonl",
586
+ }
587
+
588
+ result = load_all_instincts(global_project)
589
+ assert len(result) == 1
590
+ assert result[0]["id"] == "global-instinct"
591
+
592
+
593
+ def test_load_project_only_excludes_global(patch_globals):
594
+ """load_project_only_instincts should NOT include global instincts."""
595
+ tree = patch_globals
596
+ project = _make_project(tree)
597
+
598
+ (project["instincts_personal"] / "proj.yaml").write_text(SAMPLE_INSTINCT_YAML)
599
+ (tree["global_personal"] / "glob.yaml").write_text(SAMPLE_GLOBAL_INSTINCT_YAML)
600
+
601
+ result = load_project_only_instincts(project)
602
+ ids = {i["id"] for i in result}
603
+ assert "test-instinct" in ids
604
+ assert "global-instinct" not in ids
605
+
606
+
607
+ def test_load_project_only_global_fallback_loads_global(patch_globals):
608
+ """Global fallback should return global instincts for project-only queries."""
609
+ tree = patch_globals
610
+ (tree["global_personal"] / "glob.yaml").write_text(SAMPLE_GLOBAL_INSTINCT_YAML)
611
+
612
+ global_project = {
613
+ "id": "global",
614
+ "name": "global",
615
+ "root": "",
616
+ "project_dir": tree["homunculus"],
617
+ "instincts_personal": tree["global_personal"],
618
+ "instincts_inherited": tree["global_inherited"],
619
+ "evolved_dir": tree["global_evolved"],
620
+ "observations_file": tree["homunculus"] / "observations.jsonl",
621
+ }
622
+
623
+ result = load_project_only_instincts(global_project)
624
+ assert len(result) == 1
625
+ assert result[0]["id"] == "global-instinct"
626
+
627
+
628
+ def test_load_all_empty(patch_globals):
629
+ """No instincts at all should return empty list."""
630
+ tree = patch_globals
631
+ project = _make_project(tree)
632
+
633
+ result = load_all_instincts(project)
634
+ assert result == []
635
+
636
+
637
+ # ─────────────────────────────────────────────
638
+ # cmd_status tests
639
+ # ─────────────────────────────────────────────
640
+
641
+ def test_cmd_status_no_instincts(patch_globals, monkeypatch, capsys):
642
+ """Status with no instincts should print fallback message."""
643
+ tree = patch_globals
644
+ project = _make_project(tree)
645
+ monkeypatch.setattr(_mod, "detect_project", lambda: project)
646
+
647
+ args = SimpleNamespace()
648
+ ret = cmd_status(args)
649
+ assert ret == 0
650
+ out = capsys.readouterr().out
651
+ assert "No instincts found." in out
652
+
653
+
654
+ def test_cmd_status_with_instincts(patch_globals, monkeypatch, capsys):
655
+ """Status should show project and global instinct counts."""
656
+ tree = patch_globals
657
+ project = _make_project(tree)
658
+ monkeypatch.setattr(_mod, "detect_project", lambda: project)
659
+
660
+ (project["instincts_personal"] / "proj.yaml").write_text(SAMPLE_INSTINCT_YAML)
661
+ (tree["global_personal"] / "glob.yaml").write_text(SAMPLE_GLOBAL_INSTINCT_YAML)
662
+
663
+ args = SimpleNamespace()
664
+ ret = cmd_status(args)
665
+ assert ret == 0
666
+ out = capsys.readouterr().out
667
+ assert "INSTINCT STATUS" in out
668
+ assert "Project instincts: 1" in out
669
+ assert "Global instincts: 1" in out
670
+ assert "PROJECT-SCOPED" in out
671
+ assert "GLOBAL" in out
672
+
673
+
674
+ def test_confidence_bar_uses_unicode_when_supported():
675
+ """Confidence bars should retain block glyphs on UTF-8 streams."""
676
+ stream = SimpleNamespace(encoding="utf-8")
677
+ assert _confidence_bar(0.8, stream=stream) == "\u2588" * 8 + "\u2591" * 2
678
+
679
+
680
+ def test_confidence_bar_uses_ascii_when_stream_rejects_block_glyphs():
681
+ """Windows cp1252 streams cannot encode block glyphs."""
682
+ stream = SimpleNamespace(encoding="cp1252")
683
+ assert _confidence_bar(0.8, stream=stream) == "########.."
684
+
685
+
686
+ def test_print_instincts_by_domain_is_cp1252_safe(monkeypatch):
687
+ """Status rendering should not crash on Windows cp1252 stdout."""
688
+ raw = io.BytesIO()
689
+ stream = io.TextIOWrapper(raw, encoding="cp1252")
690
+ monkeypatch.setattr(_mod.sys, "stdout", stream)
691
+
692
+ _mod._print_instincts_by_domain([{
693
+ "id": "windows-safe",
694
+ "trigger": "when stdout uses cp1252",
695
+ "confidence": 0.8,
696
+ "domain": "platform",
697
+ "scope": "project",
698
+ }])
699
+
700
+ stream.flush()
701
+ out = raw.getvalue().decode("cp1252")
702
+ assert "########.." in out
703
+ assert "\u2588" not in out
704
+ assert "\u2591" not in out
705
+
706
+
707
+ def test_cmd_status_returns_int(patch_globals, monkeypatch):
708
+ """cmd_status should always return an int."""
709
+ tree = patch_globals
710
+ project = _make_project(tree)
711
+ monkeypatch.setattr(_mod, "detect_project", lambda: project)
712
+
713
+ args = SimpleNamespace()
714
+ ret = cmd_status(args)
715
+ assert isinstance(ret, int)
716
+
717
+
718
+ # ─────────────────────────────────────────────
719
+ # cmd_projects tests
720
+ # ─────────────────────────────────────────────
721
+
722
+ def test_cmd_projects_empty_registry(patch_globals, capsys):
723
+ """No projects should print helpful message."""
724
+ args = SimpleNamespace()
725
+ ret = cmd_projects(args)
726
+ assert ret == 0
727
+ out = capsys.readouterr().out
728
+ assert "No projects registered yet." in out
729
+
730
+
731
+ def test_cmd_projects_with_registry(patch_globals, capsys):
732
+ """Should list projects from registry."""
733
+ tree = patch_globals
734
+
735
+ # Create a project dir with instincts
736
+ pid = "test123abc"
737
+ project = _make_project(tree, pid=pid, pname="my-app")
738
+ (project["instincts_personal"] / "inst.yaml").write_text(SAMPLE_INSTINCT_YAML)
739
+
740
+ # Write registry
741
+ registry = {
742
+ pid: {
743
+ "name": "my-app",
744
+ "root": "/home/user/my-app",
745
+ "remote": "https://github.com/user/my-app.git",
746
+ "last_seen": "2025-01-15T12:00:00Z",
747
+ }
748
+ }
749
+ tree["registry_file"].write_text(json.dumps(registry))
750
+
751
+ args = SimpleNamespace()
752
+ ret = cmd_projects(args)
753
+ assert ret == 0
754
+ out = capsys.readouterr().out
755
+ assert "my-app" in out
756
+ assert pid in out
757
+ assert "1 personal" in out
758
+
759
+
760
+ # ─────────────────────────────────────────────
761
+ # _promote_specific tests
762
+ # ─────────────────────────────────────────────
763
+
764
+ def test_promote_specific_not_found(patch_globals, capsys):
765
+ """Promoting nonexistent instinct should fail."""
766
+ tree = patch_globals
767
+ project = _make_project(tree)
768
+
769
+ ret = _promote_specific(project, "nonexistent", force=True)
770
+ assert ret == 1
771
+ out = capsys.readouterr().out
772
+ assert "not found" in out
773
+
774
+
775
+ def test_promote_specific_rejects_invalid_id(patch_globals, capsys):
776
+ """Path-like instinct IDs should be rejected before file writes."""
777
+ tree = patch_globals
778
+ project = _make_project(tree)
779
+
780
+ ret = _promote_specific(project, "../escape", force=True)
781
+ assert ret == 1
782
+ err = capsys.readouterr().err
783
+ assert "Invalid instinct ID" in err
784
+
785
+
786
+ def test_promote_specific_already_global(patch_globals, capsys):
787
+ """Promoting an instinct that already exists globally should fail."""
788
+ tree = patch_globals
789
+ project = _make_project(tree)
790
+
791
+ # Write same-id instinct in both project and global
792
+ (project["instincts_personal"] / "shared.yaml").write_text(SAMPLE_INSTINCT_YAML)
793
+ global_yaml = SAMPLE_INSTINCT_YAML # same id: test-instinct
794
+ (tree["global_personal"] / "shared.yaml").write_text(global_yaml)
795
+
796
+ ret = _promote_specific(project, "test-instinct", force=True)
797
+ assert ret == 1
798
+ out = capsys.readouterr().out
799
+ assert "already exists in global" in out
800
+
801
+
802
+ def test_promote_specific_success(patch_globals, capsys):
803
+ """Promote a project instinct to global with --force."""
804
+ tree = patch_globals
805
+ project = _make_project(tree)
806
+
807
+ (project["instincts_personal"] / "inst.yaml").write_text(SAMPLE_INSTINCT_YAML)
808
+
809
+ ret = _promote_specific(project, "test-instinct", force=True)
810
+ assert ret == 0
811
+ out = capsys.readouterr().out
812
+ assert "Promoted" in out
813
+
814
+ # Verify file was created in global dir
815
+ promoted_file = tree["global_personal"] / "test-instinct.yaml"
816
+ assert promoted_file.exists()
817
+ content = promoted_file.read_text()
818
+ assert "scope: global" in content
819
+ assert "promoted_from: abc123" in content
820
+
821
+
822
+ # ─────────────────────────────────────────────
823
+ # _promote_auto tests
824
+ # ─────────────────────────────────────────────
825
+
826
+ def test_promote_auto_no_candidates(patch_globals, capsys):
827
+ """Auto-promote with no cross-project instincts should say so."""
828
+ tree = patch_globals
829
+ project = _make_project(tree)
830
+
831
+ # Empty registry
832
+ tree["registry_file"].write_text("{}")
833
+
834
+ ret = _promote_auto(project, force=True, dry_run=False)
835
+ assert ret == 0
836
+ out = capsys.readouterr().out
837
+ assert "No instincts qualify" in out
838
+
839
+
840
+ def test_promote_auto_dry_run(patch_globals, capsys):
841
+ """Dry run should list candidates but not write files."""
842
+ tree = patch_globals
843
+
844
+ # Create two projects with the same high-confidence instinct
845
+ p1 = _make_project(tree, pid="proj1", pname="project-one")
846
+ p2 = _make_project(tree, pid="proj2", pname="project-two")
847
+
848
+ high_conf_yaml = """\
849
+ ---
850
+ id: cross-project-instinct
851
+ trigger: "when reviewing"
852
+ confidence: 0.95
853
+ domain: security
854
+ scope: project
855
+ ---
856
+
857
+ ## Action
858
+ Always review for injection.
859
+ """
860
+ (p1["instincts_personal"] / "cross.yaml").write_text(high_conf_yaml)
861
+ (p2["instincts_personal"] / "cross.yaml").write_text(high_conf_yaml)
862
+
863
+ # Write registry
864
+ registry = {
865
+ "proj1": {"name": "project-one", "root": "/a", "remote": "", "last_seen": "2025-01-01T00:00:00Z"},
866
+ "proj2": {"name": "project-two", "root": "/b", "remote": "", "last_seen": "2025-01-01T00:00:00Z"},
867
+ }
868
+ tree["registry_file"].write_text(json.dumps(registry))
869
+
870
+ project = p1
871
+ ret = _promote_auto(project, force=True, dry_run=True)
872
+ assert ret == 0
873
+ out = capsys.readouterr().out
874
+ assert "DRY RUN" in out
875
+ assert "cross-project-instinct" in out
876
+
877
+ # Verify no file was created
878
+ assert not (tree["global_personal"] / "cross-project-instinct.yaml").exists()
879
+
880
+
881
+ def test_promote_auto_writes_file(patch_globals, capsys):
882
+ """Auto-promote with force should write global instinct file."""
883
+ tree = patch_globals
884
+
885
+ p1 = _make_project(tree, pid="proj1", pname="project-one")
886
+ p2 = _make_project(tree, pid="proj2", pname="project-two")
887
+
888
+ high_conf_yaml = """\
889
+ ---
890
+ id: universal-pattern
891
+ trigger: "when coding"
892
+ confidence: 0.85
893
+ domain: general
894
+ scope: project
895
+ ---
896
+
897
+ ## Action
898
+ Use descriptive variable names.
899
+ """
900
+ (p1["instincts_personal"] / "uni.yaml").write_text(high_conf_yaml)
901
+ (p2["instincts_personal"] / "uni.yaml").write_text(high_conf_yaml)
902
+
903
+ registry = {
904
+ "proj1": {"name": "project-one", "root": "/a", "remote": "", "last_seen": "2025-01-01T00:00:00Z"},
905
+ "proj2": {"name": "project-two", "root": "/b", "remote": "", "last_seen": "2025-01-01T00:00:00Z"},
906
+ }
907
+ tree["registry_file"].write_text(json.dumps(registry))
908
+
909
+ ret = _promote_auto(p1, force=True, dry_run=False)
910
+ assert ret == 0
911
+
912
+ promoted = tree["global_personal"] / "universal-pattern.yaml"
913
+ assert promoted.exists()
914
+ content = promoted.read_text()
915
+ assert "scope: global" in content
916
+ assert "auto-promoted" in content
917
+
918
+
919
+ def test_promote_auto_skips_invalid_id(patch_globals, capsys):
920
+ tree = patch_globals
921
+
922
+ p1 = _make_project(tree, pid="proj1", pname="project-one")
923
+ p2 = _make_project(tree, pid="proj2", pname="project-two")
924
+
925
+ bad_id_yaml = """\
926
+ ---
927
+ id: ../escape
928
+ trigger: "when coding"
929
+ confidence: 0.9
930
+ domain: general
931
+ scope: project
932
+ ---
933
+
934
+ ## Action
935
+ Invalid id should be skipped.
936
+ """
937
+ (p1["instincts_personal"] / "bad.yaml").write_text(bad_id_yaml)
938
+ (p2["instincts_personal"] / "bad.yaml").write_text(bad_id_yaml)
939
+
940
+ registry = {
941
+ "proj1": {"name": "project-one", "root": "/a", "remote": "", "last_seen": "2025-01-01T00:00:00Z"},
942
+ "proj2": {"name": "project-two", "root": "/b", "remote": "", "last_seen": "2025-01-01T00:00:00Z"},
943
+ }
944
+ tree["registry_file"].write_text(json.dumps(registry))
945
+
946
+ ret = _promote_auto(p1, force=True, dry_run=False)
947
+ assert ret == 0
948
+ err = capsys.readouterr().err
949
+ assert "Skipping invalid instinct ID" in err
950
+ assert not (tree["global_personal"] / "../escape.yaml").exists()
951
+
952
+
953
+ # ─────────────────────────────────────────────
954
+ # _find_cross_project_instincts tests
955
+ # ─────────────────────────────────────────────
956
+
957
+ def test_find_cross_project_empty_registry(patch_globals):
958
+ tree = patch_globals
959
+ tree["registry_file"].write_text("{}")
960
+ result = _find_cross_project_instincts()
961
+ assert result == {}
962
+
963
+
964
+ def test_find_cross_project_single_project(patch_globals):
965
+ """Single project should return nothing (need 2+)."""
966
+ tree = patch_globals
967
+ p1 = _make_project(tree, pid="proj1", pname="project-one")
968
+ (p1["instincts_personal"] / "inst.yaml").write_text(SAMPLE_INSTINCT_YAML)
969
+
970
+ registry = {"proj1": {"name": "project-one", "root": "/a", "remote": "", "last_seen": "2025-01-01T00:00:00Z"}}
971
+ tree["registry_file"].write_text(json.dumps(registry))
972
+
973
+ result = _find_cross_project_instincts()
974
+ assert result == {}
975
+
976
+
977
+ def test_find_cross_project_shared_instinct(patch_globals):
978
+ """Same instinct ID in 2 projects should be found."""
979
+ tree = patch_globals
980
+ p1 = _make_project(tree, pid="proj1", pname="project-one")
981
+ p2 = _make_project(tree, pid="proj2", pname="project-two")
982
+
983
+ (p1["instincts_personal"] / "shared.yaml").write_text(SAMPLE_INSTINCT_YAML)
984
+ (p2["instincts_personal"] / "shared.yaml").write_text(SAMPLE_INSTINCT_YAML)
985
+
986
+ registry = {
987
+ "proj1": {"name": "project-one", "root": "/a", "remote": "", "last_seen": "2025-01-01T00:00:00Z"},
988
+ "proj2": {"name": "project-two", "root": "/b", "remote": "", "last_seen": "2025-01-01T00:00:00Z"},
989
+ }
990
+ tree["registry_file"].write_text(json.dumps(registry))
991
+
992
+ result = _find_cross_project_instincts()
993
+ assert "test-instinct" in result
994
+ assert len(result["test-instinct"]) == 2
995
+
996
+
997
+ # ─────────────────────────────────────────────
998
+ # load_registry tests
999
+ # ─────────────────────────────────────────────
1000
+
1001
+ def test_load_registry_missing_file(patch_globals):
1002
+ result = load_registry()
1003
+ assert result == {}
1004
+
1005
+
1006
+ def test_load_registry_corrupt_json(patch_globals):
1007
+ tree = patch_globals
1008
+ tree["registry_file"].write_text("not json at all {{{")
1009
+ result = load_registry()
1010
+ assert result == {}
1011
+
1012
+
1013
+ def test_load_registry_valid(patch_globals):
1014
+ tree = patch_globals
1015
+ data = {"abc": {"name": "test", "root": "/test"}}
1016
+ tree["registry_file"].write_text(json.dumps(data))
1017
+ result = load_registry()
1018
+ assert result == data
1019
+
1020
+
1021
+ def test_load_registry_uses_utf8_encoding(monkeypatch):
1022
+ calls = []
1023
+
1024
+ def fake_open(path, mode="r", *args, **kwargs):
1025
+ calls.append(kwargs.get("encoding"))
1026
+ return io.StringIO("{}")
1027
+
1028
+ monkeypatch.setattr(_mod, "open", fake_open, raising=False)
1029
+ assert load_registry() == {}
1030
+ assert calls == ["utf-8"]
1031
+
1032
+
1033
+ def test_validate_instinct_id():
1034
+ assert _validate_instinct_id("good-id_1.0")
1035
+ assert not _validate_instinct_id("../bad")
1036
+ assert not _validate_instinct_id("bad/name")
1037
+ assert not _validate_instinct_id(".hidden")
1038
+
1039
+
1040
+ def test_update_registry_atomic_replaces_file(patch_globals):
1041
+ tree = patch_globals
1042
+ _update_registry("abc123", "demo", "/repo", "https://example.com/repo.git")
1043
+ data = json.loads(tree["registry_file"].read_text())
1044
+ assert "abc123" in data
1045
+ leftovers = list(tree["registry_file"].parent.glob(".projects.json.tmp.*"))
1046
+ assert leftovers == []
1047
+
1048
+
1049
+ def test_update_registry_matches_shell_schema(patch_globals):
1050
+ # Issue #2299: the Python writer must emit the same field set as the shell
1051
+ # counterpart in detect-project.sh (id, name, root, remote, created_at,
1052
+ # last_seen) so a projects.json entry has a consistent shape regardless of
1053
+ # which path wrote it.
1054
+ tree = patch_globals
1055
+ _update_registry("abc123", "demo", "/repo", "https://example.com/repo.git")
1056
+ entry = json.loads(tree["registry_file"].read_text())["abc123"]
1057
+ assert set(entry) == {"id", "name", "root", "remote", "created_at", "last_seen"}
1058
+ assert entry["id"] == "abc123"
1059
+ assert entry["name"] == "demo"
1060
+ assert entry["root"] == "/repo"
1061
+ assert entry["remote"] == "https://example.com/repo.git"
1062
+ # On the initial write both timestamps come from the same `now`, so the
1063
+ # first-write contract is created_at == last_seen.
1064
+ assert entry["created_at"]
1065
+ assert entry["created_at"] == entry["last_seen"]
1066
+
1067
+
1068
+ def test_update_registry_preserves_created_at(patch_globals):
1069
+ # created_at is stamped on first write and preserved on subsequent updates,
1070
+ # while last_seen advances — matching entry.get("created_at", now) in the
1071
+ # shell counterpart.
1072
+ tree = patch_globals
1073
+ _update_registry("abc123", "demo", "/repo", "https://example.com/repo.git")
1074
+ first = json.loads(tree["registry_file"].read_text())["abc123"]
1075
+
1076
+ _update_registry("abc123", "demo-renamed", "/repo", "https://example.com/repo.git")
1077
+ second = json.loads(tree["registry_file"].read_text())["abc123"]
1078
+
1079
+ assert second["created_at"] == first["created_at"]
1080
+ assert second["name"] == "demo-renamed"
1081
+ assert second["last_seen"] >= first["last_seen"]
1082
+
1083
+
1084
+ def test_update_registry_heals_malformed_entry(patch_globals):
1085
+ # Issue #2299 follow-up: a non-dict value for the project id (e.g. a
1086
+ # corrupt registry) must not crash _update_registry. The entry is healed by
1087
+ # the rewrite, preserving the old unconditional-overwrite behavior.
1088
+ tree = patch_globals
1089
+ tree["registry_file"].write_text(json.dumps({"abc123": None}), encoding="utf-8")
1090
+ _update_registry("abc123", "demo", "/repo", "https://example.com/repo.git")
1091
+ entry = json.loads(tree["registry_file"].read_text())["abc123"]
1092
+ assert isinstance(entry, dict)
1093
+ assert entry["id"] == "abc123"
1094
+ assert entry["created_at"]
1095
+ assert entry["created_at"] == entry["last_seen"]
1096
+
1097
+
1098
+ def test_update_registry_heals_non_dict_registry(patch_globals):
1099
+ # Issue #2299 follow-up: a top-level registry that is valid JSON but not a
1100
+ # mapping (e.g. a list or string from a corrupt projects.json) must not
1101
+ # crash _update_registry before the per-entry guard runs. The whole file is
1102
+ # healed by the rewrite, preserving the old unconditional-overwrite behavior.
1103
+ tree = patch_globals
1104
+ tree["registry_file"].write_text(json.dumps(["oops"]), encoding="utf-8")
1105
+ _update_registry("abc123", "demo", "/repo", "https://example.com/repo.git")
1106
+ registry = json.loads(tree["registry_file"].read_text())
1107
+ assert isinstance(registry, dict)
1108
+ entry = registry["abc123"]
1109
+ assert entry["id"] == "abc123"
1110
+ assert entry["created_at"] == entry["last_seen"]
1111
+
1112
+
1113
+ def test_write_registry_atomic_no_tmp_leftovers(patch_globals):
1114
+ # Issue #2294: _write_registry now holds the registry lock like
1115
+ # _update_registry. It must still write atomically with no stray tmp files.
1116
+ tree = patch_globals
1117
+ _write_registry({"keep": {"name": "demo", "root": "/repo", "remote": ""}})
1118
+ data = json.loads(tree["registry_file"].read_text())
1119
+ assert data == {"keep": {"name": "demo", "root": "/repo", "remote": ""}}
1120
+ leftovers = list(tree["registry_file"].parent.glob(".projects.json.tmp.*"))
1121
+ assert leftovers == []
1122
+
1123
+
1124
+ def test_remove_project_storage_deletes_contained_dir(patch_globals):
1125
+ tree = patch_globals
1126
+ target = tree["projects_dir"] / "proj-1"
1127
+ (target / "instincts").mkdir(parents=True)
1128
+ (target / "instincts" / "x.md").write_text("hi", encoding="utf-8")
1129
+ _remove_project_storage("proj-1")
1130
+ assert not target.exists()
1131
+
1132
+
1133
+ def test_remove_project_storage_missing_dir_is_noop(patch_globals):
1134
+ # No raise when the contained dir simply does not exist.
1135
+ _remove_project_storage("never-created")
1136
+
1137
+
1138
+ def test_remove_project_storage_blocks_traversal(patch_globals):
1139
+ # Issue #2297: defense-in-depth — a traversal id must be refused even when a
1140
+ # caller skips _validate_project_id, so this can never delete outside
1141
+ # PROJECTS_DIR.
1142
+ with pytest.raises(ValueError):
1143
+ _remove_project_storage("../../etc")
1144
+
1145
+
1146
+ def test_remove_project_storage_blocks_root_itself(patch_globals):
1147
+ with pytest.raises(ValueError):
1148
+ _remove_project_storage(".")
1149
+
1150
+
1151
+ # ─────────────────────────────────────────────
1152
+ # Issue #2302 coverage:
1153
+ # _normalize_remote_url, _promote_specific dry-run,
1154
+ # projects delete/gc/merge, cmd_prune
1155
+ # ─────────────────────────────────────────────
1156
+
1157
+ _normalize_remote_url = _mod._normalize_remote_url
1158
+ _cmd_projects_delete = _mod._cmd_projects_delete
1159
+ _cmd_projects_gc = _mod._cmd_projects_gc
1160
+ _cmd_projects_merge = _mod._cmd_projects_merge
1161
+ cmd_prune = _mod.cmd_prune
1162
+
1163
+
1164
+ # ── _normalize_remote_url ────────────────────
1165
+
1166
+ def test_normalize_remote_url_empty_returns_empty():
1167
+ assert _normalize_remote_url("") == ""
1168
+ assert _normalize_remote_url(None) == ""
1169
+
1170
+
1171
+ def test_normalize_remote_url_scp_form():
1172
+ # scp-style host:path -> host/path, credentials/.git stripped, lowercased
1173
+ assert _normalize_remote_url("git@github.com:Test/Repo.git") == "github.com/test/repo"
1174
+
1175
+
1176
+ def test_normalize_remote_url_https_strips_credentials_and_scheme():
1177
+ assert (
1178
+ _normalize_remote_url("https://user:token@github.com/test/repo.git")
1179
+ == "github.com/test/repo"
1180
+ )
1181
+
1182
+
1183
+ def test_normalize_remote_url_network_is_lowercased():
1184
+ assert _normalize_remote_url("https://GitHub.com/Owner/Project") == "github.com/owner/project"
1185
+
1186
+
1187
+ def test_normalize_remote_url_trailing_slash_and_dotgit_stripped():
1188
+ assert _normalize_remote_url("https://github.com/a/b.git/") == "github.com/a/b"
1189
+
1190
+
1191
+ def test_normalize_remote_url_file_scheme_preserves_case():
1192
+ # Local file paths are not network URLs: scheme is stripped but case is preserved.
1193
+ assert _normalize_remote_url("file:///srv/Repos/My-Repo/") == "/srv/Repos/My-Repo"
1194
+
1195
+
1196
+ def test_normalize_remote_url_idempotent():
1197
+ once = _normalize_remote_url("https://user@github.com/Test/Repo.git")
1198
+ assert _normalize_remote_url(once) == once
1199
+
1200
+
1201
+ # ── _promote_specific dry-run ────────────────
1202
+
1203
+ def test_promote_specific_dry_run_writes_nothing(patch_globals, capsys):
1204
+ """dry_run returns 0, prints [DRY RUN], and writes no global file."""
1205
+ tree = patch_globals
1206
+ project = _make_project(tree)
1207
+ (project["instincts_personal"] / "inst.yaml").write_text(SAMPLE_INSTINCT_YAML)
1208
+
1209
+ ret = _promote_specific(project, "test-instinct", force=True, dry_run=True)
1210
+ assert ret == 0
1211
+ out = capsys.readouterr().out
1212
+ assert "[DRY RUN]" in out
1213
+ assert not (tree["global_personal"] / "test-instinct.yaml").exists()
1214
+ assert list(tree["global_personal"].iterdir()) == []
1215
+
1216
+
1217
+ # ── projects delete ──────────────────────────
1218
+
1219
+ def test_projects_delete_rejects_invalid_id(patch_globals, capsys):
1220
+ args = SimpleNamespace(project_id="../escape", dry_run=False, force=True)
1221
+ assert _cmd_projects_delete(args) == 1
1222
+ assert "Invalid project ID" in capsys.readouterr().err
1223
+
1224
+
1225
+ def test_projects_delete_not_found(patch_globals, capsys):
1226
+ args = SimpleNamespace(project_id="ghost123", dry_run=False, force=True)
1227
+ assert _cmd_projects_delete(args) == 1
1228
+ assert "not found" in capsys.readouterr().err
1229
+
1230
+
1231
+ def test_projects_delete_dry_run_keeps_registry_and_storage(patch_globals, capsys):
1232
+ tree = patch_globals
1233
+ _make_project(tree, pid="proj1", pname="p1")
1234
+ tree["registry_file"].write_text(json.dumps({"proj1": {"name": "p1"}}))
1235
+
1236
+ args = SimpleNamespace(project_id="proj1", dry_run=True, force=False)
1237
+ assert _cmd_projects_delete(args) == 0
1238
+ assert "[DRY RUN]" in capsys.readouterr().out
1239
+ assert (tree["projects_dir"] / "proj1").exists()
1240
+ assert "proj1" in json.loads(tree["registry_file"].read_text())
1241
+
1242
+
1243
+ def test_projects_delete_force_removes_registry_and_storage(patch_globals, capsys):
1244
+ tree = patch_globals
1245
+ _make_project(tree, pid="proj1", pname="p1")
1246
+ tree["registry_file"].write_text(json.dumps({"proj1": {"name": "p1"}}))
1247
+
1248
+ args = SimpleNamespace(project_id="proj1", dry_run=False, force=True)
1249
+ assert _cmd_projects_delete(args) == 0
1250
+ assert "Deleted project" in capsys.readouterr().out
1251
+ assert not (tree["projects_dir"] / "proj1").exists()
1252
+ assert "proj1" not in json.loads(tree["registry_file"].read_text())
1253
+
1254
+
1255
+ # ── projects gc ──────────────────────────────
1256
+
1257
+ def test_projects_gc_no_candidates(patch_globals, capsys):
1258
+ tree = patch_globals
1259
+ tree["registry_file"].write_text("{}")
1260
+ args = SimpleNamespace(dry_run=False, force=True)
1261
+ assert _cmd_projects_gc(args) == 0
1262
+ assert "No zero-value project entries" in capsys.readouterr().out
1263
+
1264
+
1265
+ def test_projects_gc_dry_run_keeps_entry(patch_globals, capsys):
1266
+ tree = patch_globals
1267
+ _make_project(tree, pid="empty1", pname="e1") # zero instincts/observations
1268
+ tree["registry_file"].write_text(json.dumps({"empty1": {"name": "e1"}}))
1269
+
1270
+ args = SimpleNamespace(dry_run=True, force=False)
1271
+ assert _cmd_projects_gc(args) == 0
1272
+ assert "[DRY RUN]" in capsys.readouterr().out
1273
+ assert "empty1" in json.loads(tree["registry_file"].read_text())
1274
+ # dry-run must not touch storage on disk
1275
+ assert (tree["projects_dir"] / "empty1").exists()
1276
+
1277
+
1278
+ def test_projects_gc_force_removes_only_zero_value(patch_globals, capsys):
1279
+ tree = patch_globals
1280
+ _make_project(tree, pid="empty1", pname="e1")
1281
+ full = _make_project(tree, pid="full1", pname="f1")
1282
+ (full["instincts_personal"] / "inst.yaml").write_text(SAMPLE_INSTINCT_YAML)
1283
+ tree["registry_file"].write_text(
1284
+ json.dumps({"empty1": {"name": "e1"}, "full1": {"name": "f1"}})
1285
+ )
1286
+
1287
+ args = SimpleNamespace(dry_run=False, force=True)
1288
+ assert _cmd_projects_gc(args) == 0
1289
+ reg = json.loads(tree["registry_file"].read_text())
1290
+ assert "empty1" not in reg
1291
+ assert "full1" in reg
1292
+ assert not (tree["projects_dir"] / "empty1").exists()
1293
+ assert (tree["projects_dir"] / "full1").exists()
1294
+
1295
+
1296
+ # ── projects merge ───────────────────────────
1297
+
1298
+ def test_projects_merge_rejects_same_id(patch_globals, capsys):
1299
+ args = SimpleNamespace(from_id="dup", into_id="dup", dry_run=False, force=True)
1300
+ assert _cmd_projects_merge(args) == 1
1301
+ assert "into itself" in capsys.readouterr().err
1302
+
1303
+
1304
+ def test_projects_merge_missing_source(patch_globals, capsys):
1305
+ tree = patch_globals
1306
+ tree["registry_file"].write_text(json.dumps({"dest": {"name": "d"}}))
1307
+ args = SimpleNamespace(from_id="src", into_id="dest", dry_run=False, force=True)
1308
+ assert _cmd_projects_merge(args) == 1
1309
+ assert "Source project" in capsys.readouterr().err
1310
+
1311
+
1312
+ def test_projects_merge_missing_destination(patch_globals, capsys):
1313
+ tree = patch_globals
1314
+ # Source present, destination absent — exercises the symmetric error branch.
1315
+ tree["registry_file"].write_text(json.dumps({"src": {"name": "s"}}))
1316
+ args = SimpleNamespace(from_id="src", into_id="dest", dry_run=False, force=True)
1317
+ assert _cmd_projects_merge(args) == 1
1318
+ assert "Destination project" in capsys.readouterr().err
1319
+
1320
+
1321
+ def test_projects_merge_dry_run_no_changes(patch_globals, capsys):
1322
+ tree = patch_globals
1323
+ src = _make_project(tree, pid="src", pname="s")
1324
+ _make_project(tree, pid="dest", pname="d")
1325
+ (src["instincts_personal"] / "i.yaml").write_text(SAMPLE_INSTINCT_YAML)
1326
+ tree["registry_file"].write_text(json.dumps({"src": {"name": "s"}, "dest": {"name": "d"}}))
1327
+
1328
+ args = SimpleNamespace(from_id="src", into_id="dest", dry_run=True, force=False)
1329
+ assert _cmd_projects_merge(args) == 0
1330
+ assert "[DRY RUN]" in capsys.readouterr().out
1331
+ reg = json.loads(tree["registry_file"].read_text())
1332
+ assert "src" in reg and "dest" in reg
1333
+ assert (tree["projects_dir"] / "src").exists()
1334
+ # dry-run must not copy any instinct into the destination storage
1335
+ assert not list((tree["projects_dir"] / "dest" / "instincts" / "personal").glob("*.yaml"))
1336
+
1337
+
1338
+ def test_projects_merge_force_moves_and_removes_source(patch_globals, capsys):
1339
+ tree = patch_globals
1340
+ src = _make_project(tree, pid="src", pname="s")
1341
+ _make_project(tree, pid="dest", pname="d")
1342
+ (src["instincts_personal"] / "i.yaml").write_text(SAMPLE_INSTINCT_YAML)
1343
+ tree["registry_file"].write_text(json.dumps({"src": {"name": "s"}, "dest": {"name": "d"}}))
1344
+
1345
+ args = SimpleNamespace(from_id="src", into_id="dest", dry_run=False, force=True)
1346
+ assert _cmd_projects_merge(args) == 0
1347
+ reg = json.loads(tree["registry_file"].read_text())
1348
+ assert "src" not in reg
1349
+ assert "dest" in reg
1350
+ assert not (tree["projects_dir"] / "src").exists()
1351
+ moved = list((tree["projects_dir"] / "dest" / "instincts" / "personal").glob("*.yaml"))
1352
+ assert len(moved) >= 1
1353
+
1354
+
1355
+ # ── cmd_prune ────────────────────────────────
1356
+
1357
+ def _pending_item(path, age_days):
1358
+ return {
1359
+ "path": path,
1360
+ "created": None,
1361
+ "age_days": age_days,
1362
+ "name": path.stem,
1363
+ "parent_dir": str(path.parent),
1364
+ }
1365
+
1366
+
1367
+ def test_cmd_prune_dry_run_keeps_files(monkeypatch, tmp_path, capsys):
1368
+ f_old = tmp_path / "old.yaml"
1369
+ f_old.write_text("x", encoding="utf-8")
1370
+ f_new = tmp_path / "new.yaml"
1371
+ f_new.write_text("y", encoding="utf-8")
1372
+ items = [_pending_item(f_old, 40), _pending_item(f_new, 5)]
1373
+ monkeypatch.setattr(_mod, "_collect_pending_instincts", lambda: items)
1374
+
1375
+ args = SimpleNamespace(max_age=30, dry_run=True, quiet=False)
1376
+ assert cmd_prune(args) == 0
1377
+ assert "[DRY RUN]" in capsys.readouterr().out
1378
+ assert f_old.exists()
1379
+ assert f_new.exists()
1380
+
1381
+
1382
+ def test_cmd_prune_deletes_only_expired(monkeypatch, tmp_path, capsys):
1383
+ f_old = tmp_path / "old.yaml"
1384
+ f_old.write_text("x", encoding="utf-8")
1385
+ f_new = tmp_path / "new.yaml"
1386
+ f_new.write_text("y", encoding="utf-8")
1387
+ items = [_pending_item(f_old, 40), _pending_item(f_new, 5)]
1388
+ monkeypatch.setattr(_mod, "_collect_pending_instincts", lambda: items)
1389
+
1390
+ args = SimpleNamespace(max_age=30, dry_run=False, quiet=False)
1391
+ assert cmd_prune(args) == 0
1392
+ assert not f_old.exists()
1393
+ assert f_new.exists()
1394
+ assert "Pruned 1" in capsys.readouterr().out
1395
+
1396
+
1397
+ def test_cmd_prune_quiet_suppresses_output(monkeypatch, tmp_path, capsys):
1398
+ f_old = tmp_path / "old.yaml"
1399
+ f_old.write_text("x", encoding="utf-8")
1400
+ items = [_pending_item(f_old, 99)]
1401
+ monkeypatch.setattr(_mod, "_collect_pending_instincts", lambda: items)
1402
+
1403
+ args = SimpleNamespace(max_age=30, dry_run=False, quiet=True)
1404
+ assert cmd_prune(args) == 0
1405
+ assert not f_old.exists()
1406
+ captured = capsys.readouterr()
1407
+ assert captured.out == ""
1408
+ assert captured.err == ""
1409
+
1410
+
1411
+ def test_cmd_prune_empty_pending_nothing_to_do(monkeypatch, capsys):
1412
+ # Nothing pending at all: the non-dry-run, non-quiet branch must report
1413
+ # "nothing to do" (not "[DRY RUN]"), return 0, and not crash.
1414
+ monkeypatch.setattr(_mod, "_collect_pending_instincts", lambda: [])
1415
+
1416
+ args = SimpleNamespace(max_age=30, dry_run=False, quiet=False)
1417
+ assert cmd_prune(args) == 0
1418
+ out = capsys.readouterr().out
1419
+ assert "No pending instincts older than 30 days." in out
1420
+ assert "[DRY RUN]" not in out