@jagilber-org/index-server 1.19.1

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 (360) hide show
  1. package/CHANGELOG.md +1218 -0
  2. package/CODE_OF_CONDUCT.md +49 -0
  3. package/CONTRIBUTING.md +75 -0
  4. package/LICENSE +21 -0
  5. package/README.md +523 -0
  6. package/SECURITY.md +50 -0
  7. package/dist/config/configUtils.d.ts +11 -0
  8. package/dist/config/configUtils.js +87 -0
  9. package/dist/config/dashboardConfig.d.ts +45 -0
  10. package/dist/config/dashboardConfig.js +63 -0
  11. package/dist/config/defaultValues.d.ts +61 -0
  12. package/dist/config/defaultValues.js +70 -0
  13. package/dist/config/dirConstants.d.ts +17 -0
  14. package/dist/config/dirConstants.js +28 -0
  15. package/dist/config/featureConfig.d.ts +61 -0
  16. package/dist/config/featureConfig.js +121 -0
  17. package/dist/config/runtimeConfig.d.ts +145 -0
  18. package/dist/config/runtimeConfig.js +334 -0
  19. package/dist/config/serverConfig.d.ts +90 -0
  20. package/dist/config/serverConfig.js +164 -0
  21. package/dist/dashboard/analytics/AnalyticsEngine.d.ts +142 -0
  22. package/dist/dashboard/analytics/AnalyticsEngine.js +373 -0
  23. package/dist/dashboard/analytics/BusinessIntelligence.d.ts +187 -0
  24. package/dist/dashboard/analytics/BusinessIntelligence.js +594 -0
  25. package/dist/dashboard/client/admin.html +2150 -0
  26. package/dist/dashboard/client/chunks/mermaid-layout-elk.esm.min/chunk-SP2CHFBE.mjs +1 -0
  27. package/dist/dashboard/client/chunks/mermaid-layout-elk.esm.min/render-T6MDALS3.mjs +27 -0
  28. package/dist/dashboard/client/css/admin.css +1466 -0
  29. package/dist/dashboard/client/js/admin.boot.js +359 -0
  30. package/dist/dashboard/client/js/admin.config.js +196 -0
  31. package/dist/dashboard/client/js/admin.embeddings.js +425 -0
  32. package/dist/dashboard/client/js/admin.graph.js +583 -0
  33. package/dist/dashboard/client/js/admin.instances.js +120 -0
  34. package/dist/dashboard/client/js/admin.instructions.js +552 -0
  35. package/dist/dashboard/client/js/admin.logs.js +113 -0
  36. package/dist/dashboard/client/js/admin.maintenance.js +354 -0
  37. package/dist/dashboard/client/js/admin.messaging.js +635 -0
  38. package/dist/dashboard/client/js/admin.monitor.js +181 -0
  39. package/dist/dashboard/client/js/admin.overview.js +221 -0
  40. package/dist/dashboard/client/js/admin.performance.js +61 -0
  41. package/dist/dashboard/client/js/admin.sessions.js +293 -0
  42. package/dist/dashboard/client/js/admin.sqlite.js +366 -0
  43. package/dist/dashboard/client/js/admin.utils.js +49 -0
  44. package/dist/dashboard/client/js/chart.umd.js +14 -0
  45. package/dist/dashboard/client/js/elk.bundled.js +6696 -0
  46. package/dist/dashboard/client/js/marked.umd.js +74 -0
  47. package/dist/dashboard/client/js/mermaid.min.js +3022 -0
  48. package/dist/dashboard/client/mermaid-layout-elk.esm.min.mjs +1 -0
  49. package/dist/dashboard/export/DataExporter.d.ts +169 -0
  50. package/dist/dashboard/export/DataExporter.js +737 -0
  51. package/dist/dashboard/export/exporters/csvExporter.d.ts +11 -0
  52. package/dist/dashboard/export/exporters/csvExporter.js +46 -0
  53. package/dist/dashboard/export/exporters/exportTypes.d.ts +89 -0
  54. package/dist/dashboard/export/exporters/exportTypes.js +5 -0
  55. package/dist/dashboard/export/exporters/jsonExporter.d.ts +7 -0
  56. package/dist/dashboard/export/exporters/jsonExporter.js +22 -0
  57. package/dist/dashboard/export/exporters/xmlExporter.d.ts +17 -0
  58. package/dist/dashboard/export/exporters/xmlExporter.js +175 -0
  59. package/dist/dashboard/integration/APIIntegration.d.ts +41 -0
  60. package/dist/dashboard/integration/APIIntegration.js +95 -0
  61. package/dist/dashboard/security/SecurityMonitor.d.ts +167 -0
  62. package/dist/dashboard/security/SecurityMonitor.js +559 -0
  63. package/dist/dashboard/server/AdminPanel.d.ts +183 -0
  64. package/dist/dashboard/server/AdminPanel.js +792 -0
  65. package/dist/dashboard/server/AdminPanelConfig.d.ts +42 -0
  66. package/dist/dashboard/server/AdminPanelConfig.js +80 -0
  67. package/dist/dashboard/server/AdminPanelState.d.ts +47 -0
  68. package/dist/dashboard/server/AdminPanelState.js +214 -0
  69. package/dist/dashboard/server/ApiRoutes.d.ts +17 -0
  70. package/dist/dashboard/server/ApiRoutes.js +149 -0
  71. package/dist/dashboard/server/DashboardServer.d.ts +49 -0
  72. package/dist/dashboard/server/DashboardServer.js +159 -0
  73. package/dist/dashboard/server/FileMetricsStorage.d.ts +49 -0
  74. package/dist/dashboard/server/FileMetricsStorage.js +195 -0
  75. package/dist/dashboard/server/HttpTransport.d.ts +23 -0
  76. package/dist/dashboard/server/HttpTransport.js +116 -0
  77. package/dist/dashboard/server/InstanceManager.d.ts +53 -0
  78. package/dist/dashboard/server/InstanceManager.js +284 -0
  79. package/dist/dashboard/server/KnowledgeStore.d.ts +35 -0
  80. package/dist/dashboard/server/KnowledgeStore.js +105 -0
  81. package/dist/dashboard/server/LeaderElection.d.ts +81 -0
  82. package/dist/dashboard/server/LeaderElection.js +268 -0
  83. package/dist/dashboard/server/MetricsCollector.d.ts +200 -0
  84. package/dist/dashboard/server/MetricsCollector.js +803 -0
  85. package/dist/dashboard/server/SessionPersistenceManager.d.ts +88 -0
  86. package/dist/dashboard/server/SessionPersistenceManager.js +457 -0
  87. package/dist/dashboard/server/ThinClient.d.ts +64 -0
  88. package/dist/dashboard/server/ThinClient.js +237 -0
  89. package/dist/dashboard/server/WebSocketManager.d.ts +161 -0
  90. package/dist/dashboard/server/WebSocketManager.js +463 -0
  91. package/dist/dashboard/server/httpLifecycle.d.ts +17 -0
  92. package/dist/dashboard/server/httpLifecycle.js +35 -0
  93. package/dist/dashboard/server/legacyDashboardHtml.d.ts +9 -0
  94. package/dist/dashboard/server/legacyDashboardHtml.js +618 -0
  95. package/dist/dashboard/server/legacyDashboardStyles.d.ts +5 -0
  96. package/dist/dashboard/server/legacyDashboardStyles.js +490 -0
  97. package/dist/dashboard/server/metricsAggregation.d.ts +252 -0
  98. package/dist/dashboard/server/metricsAggregation.js +206 -0
  99. package/dist/dashboard/server/metricsSerializer.d.ts +25 -0
  100. package/dist/dashboard/server/metricsSerializer.js +195 -0
  101. package/dist/dashboard/server/routes/admin.routes.d.ts +16 -0
  102. package/dist/dashboard/server/routes/admin.routes.js +596 -0
  103. package/dist/dashboard/server/routes/alerts.routes.d.ts +7 -0
  104. package/dist/dashboard/server/routes/alerts.routes.js +93 -0
  105. package/dist/dashboard/server/routes/api.feedback.routes.d.ts +73 -0
  106. package/dist/dashboard/server/routes/api.feedback.routes.js +171 -0
  107. package/dist/dashboard/server/routes/api.instructions.routes.d.ts +101 -0
  108. package/dist/dashboard/server/routes/api.instructions.routes.js +213 -0
  109. package/dist/dashboard/server/routes/api.usage.routes.d.ts +57 -0
  110. package/dist/dashboard/server/routes/api.usage.routes.js +374 -0
  111. package/dist/dashboard/server/routes/embeddings.routes.d.ts +6 -0
  112. package/dist/dashboard/server/routes/embeddings.routes.js +246 -0
  113. package/dist/dashboard/server/routes/graph.routes.d.ts +6 -0
  114. package/dist/dashboard/server/routes/graph.routes.js +280 -0
  115. package/dist/dashboard/server/routes/index.d.ts +38 -0
  116. package/dist/dashboard/server/routes/index.js +194 -0
  117. package/dist/dashboard/server/routes/instances.routes.d.ts +6 -0
  118. package/dist/dashboard/server/routes/instances.routes.js +35 -0
  119. package/dist/dashboard/server/routes/instructions.routes.d.ts +8 -0
  120. package/dist/dashboard/server/routes/instructions.routes.js +336 -0
  121. package/dist/dashboard/server/routes/knowledge.routes.d.ts +6 -0
  122. package/dist/dashboard/server/routes/knowledge.routes.js +82 -0
  123. package/dist/dashboard/server/routes/logs.routes.d.ts +6 -0
  124. package/dist/dashboard/server/routes/logs.routes.js +164 -0
  125. package/dist/dashboard/server/routes/messaging.routes.d.ts +16 -0
  126. package/dist/dashboard/server/routes/messaging.routes.js +293 -0
  127. package/dist/dashboard/server/routes/metrics.routes.d.ts +10 -0
  128. package/dist/dashboard/server/routes/metrics.routes.js +346 -0
  129. package/dist/dashboard/server/routes/scripts.routes.d.ts +9 -0
  130. package/dist/dashboard/server/routes/scripts.routes.js +84 -0
  131. package/dist/dashboard/server/routes/sqlite.routes.d.ts +9 -0
  132. package/dist/dashboard/server/routes/sqlite.routes.js +569 -0
  133. package/dist/dashboard/server/routes/status.routes.d.ts +7 -0
  134. package/dist/dashboard/server/routes/status.routes.js +183 -0
  135. package/dist/dashboard/server/routes/synthetic.routes.d.ts +7 -0
  136. package/dist/dashboard/server/routes/synthetic.routes.js +195 -0
  137. package/dist/dashboard/server/routes/tools.routes.d.ts +6 -0
  138. package/dist/dashboard/server/routes/tools.routes.js +46 -0
  139. package/dist/dashboard/server/routes/usage.routes.d.ts +6 -0
  140. package/dist/dashboard/server/routes/usage.routes.js +25 -0
  141. package/dist/dashboard/server/wsInit.d.ts +16 -0
  142. package/dist/dashboard/server/wsInit.js +35 -0
  143. package/dist/externalClientLib.d.ts +1 -0
  144. package/dist/externalClientLib.js +2 -0
  145. package/dist/minimal/index.d.ts +1 -0
  146. package/dist/minimal/index.js +140 -0
  147. package/dist/models/SessionPersistence.d.ts +115 -0
  148. package/dist/models/SessionPersistence.js +66 -0
  149. package/dist/models/instruction.d.ts +45 -0
  150. package/dist/models/instruction.js +2 -0
  151. package/dist/perf/benchmark.d.ts +1 -0
  152. package/dist/perf/benchmark.js +50 -0
  153. package/dist/portableClientWrapper.d.ts +1 -0
  154. package/dist/portableClientWrapper.js +2 -0
  155. package/dist/schemas/index.d.ts +128 -0
  156. package/dist/schemas/index.js +371 -0
  157. package/dist/scripts/runPerformanceBaseline.d.ts +1 -0
  158. package/dist/scripts/runPerformanceBaseline.js +17 -0
  159. package/dist/server/handshakeManager.d.ts +25 -0
  160. package/dist/server/handshakeManager.js +472 -0
  161. package/dist/server/index-server.d.ts +56 -0
  162. package/dist/server/index-server.js +822 -0
  163. package/dist/server/registry.d.ts +44 -0
  164. package/dist/server/registry.js +236 -0
  165. package/dist/server/sdkServer.d.ts +8 -0
  166. package/dist/server/sdkServer.js +299 -0
  167. package/dist/server/shutdownGuard.d.ts +41 -0
  168. package/dist/server/shutdownGuard.js +52 -0
  169. package/dist/server/thin-client.d.ts +22 -0
  170. package/dist/server/thin-client.js +111 -0
  171. package/dist/server/transport.d.ts +41 -0
  172. package/dist/server/transport.js +312 -0
  173. package/dist/server/transportFactory.d.ts +21 -0
  174. package/dist/server/transportFactory.js +429 -0
  175. package/dist/services/atomicFs.d.ts +22 -0
  176. package/dist/services/atomicFs.js +103 -0
  177. package/dist/services/auditLog.d.ts +38 -0
  178. package/dist/services/auditLog.js +142 -0
  179. package/dist/services/autoBackup.d.ts +14 -0
  180. package/dist/services/autoBackup.js +171 -0
  181. package/dist/services/autoSplit.d.ts +32 -0
  182. package/dist/services/autoSplit.js +113 -0
  183. package/dist/services/backupZip.d.ts +25 -0
  184. package/dist/services/backupZip.js +110 -0
  185. package/dist/services/bootstrapGating.d.ts +123 -0
  186. package/dist/services/bootstrapGating.js +221 -0
  187. package/dist/services/canonical.d.ts +23 -0
  188. package/dist/services/canonical.js +65 -0
  189. package/dist/services/categoryRules.d.ts +7 -0
  190. package/dist/services/categoryRules.js +37 -0
  191. package/dist/services/classificationService.d.ts +42 -0
  192. package/dist/services/classificationService.js +168 -0
  193. package/dist/services/embeddingService.d.ts +62 -0
  194. package/dist/services/embeddingService.js +259 -0
  195. package/dist/services/errors.d.ts +22 -0
  196. package/dist/services/errors.js +31 -0
  197. package/dist/services/featureFlags.d.ts +25 -0
  198. package/dist/services/featureFlags.js +89 -0
  199. package/dist/services/features.d.ts +13 -0
  200. package/dist/services/features.js +35 -0
  201. package/dist/services/handlers/instructions.add.d.ts +1 -0
  202. package/dist/services/handlers/instructions.add.js +496 -0
  203. package/dist/services/handlers/instructions.groom.d.ts +1 -0
  204. package/dist/services/handlers/instructions.groom.js +523 -0
  205. package/dist/services/handlers/instructions.import.d.ts +1 -0
  206. package/dist/services/handlers/instructions.import.js +173 -0
  207. package/dist/services/handlers/instructions.patch.d.ts +1 -0
  208. package/dist/services/handlers/instructions.patch.js +167 -0
  209. package/dist/services/handlers/instructions.query.d.ts +163 -0
  210. package/dist/services/handlers/instructions.query.js +522 -0
  211. package/dist/services/handlers/instructions.reload.d.ts +1 -0
  212. package/dist/services/handlers/instructions.reload.js +13 -0
  213. package/dist/services/handlers/instructions.remove.d.ts +1 -0
  214. package/dist/services/handlers/instructions.remove.js +118 -0
  215. package/dist/services/handlers/instructions.shared.d.ts +31 -0
  216. package/dist/services/handlers/instructions.shared.js +124 -0
  217. package/dist/services/handlers.activation.d.ts +1 -0
  218. package/dist/services/handlers.activation.js +203 -0
  219. package/dist/services/handlers.bootstrap.d.ts +1 -0
  220. package/dist/services/handlers.bootstrap.js +38 -0
  221. package/dist/services/handlers.dashboardConfig.d.ts +34 -0
  222. package/dist/services/handlers.dashboardConfig.js +108 -0
  223. package/dist/services/handlers.diagnostics.d.ts +1 -0
  224. package/dist/services/handlers.diagnostics.js +64 -0
  225. package/dist/services/handlers.feedback.d.ts +15 -0
  226. package/dist/services/handlers.feedback.js +378 -0
  227. package/dist/services/handlers.gates.d.ts +1 -0
  228. package/dist/services/handlers.gates.js +46 -0
  229. package/dist/services/handlers.graph.d.ts +53 -0
  230. package/dist/services/handlers.graph.js +231 -0
  231. package/dist/services/handlers.help.d.ts +1 -0
  232. package/dist/services/handlers.help.js +119 -0
  233. package/dist/services/handlers.instructionSchema.d.ts +1 -0
  234. package/dist/services/handlers.instructionSchema.js +227 -0
  235. package/dist/services/handlers.instructions.d.ts +8 -0
  236. package/dist/services/handlers.instructions.js +14 -0
  237. package/dist/services/handlers.instructionsDiagnostics.d.ts +1 -0
  238. package/dist/services/handlers.instructionsDiagnostics.js +14 -0
  239. package/dist/services/handlers.integrity.d.ts +1 -0
  240. package/dist/services/handlers.integrity.js +35 -0
  241. package/dist/services/handlers.manifest.d.ts +1 -0
  242. package/dist/services/handlers.manifest.js +24 -0
  243. package/dist/services/handlers.messaging.d.ts +12 -0
  244. package/dist/services/handlers.messaging.js +203 -0
  245. package/dist/services/handlers.metrics.d.ts +1 -0
  246. package/dist/services/handlers.metrics.js +43 -0
  247. package/dist/services/handlers.promote.d.ts +1 -0
  248. package/dist/services/handlers.promote.js +306 -0
  249. package/dist/services/handlers.prompt.d.ts +1 -0
  250. package/dist/services/handlers.prompt.js +7 -0
  251. package/dist/services/handlers.search.d.ts +69 -0
  252. package/dist/services/handlers.search.js +645 -0
  253. package/dist/services/handlers.testPrimitive.d.ts +1 -0
  254. package/dist/services/handlers.testPrimitive.js +5 -0
  255. package/dist/services/handlers.trace.d.ts +1 -0
  256. package/dist/services/handlers.trace.js +31 -0
  257. package/dist/services/handlers.usage.d.ts +1 -0
  258. package/dist/services/handlers.usage.js +11 -0
  259. package/dist/services/hotScore.d.ts +137 -0
  260. package/dist/services/hotScore.js +244 -0
  261. package/dist/services/indexContext.d.ts +117 -0
  262. package/dist/services/indexContext.js +968 -0
  263. package/dist/services/indexLoader.d.ts +44 -0
  264. package/dist/services/indexLoader.js +921 -0
  265. package/dist/services/indexRepository.d.ts +32 -0
  266. package/dist/services/indexRepository.js +71 -0
  267. package/dist/services/indexingService.d.ts +1 -0
  268. package/dist/services/indexingService.js +2 -0
  269. package/dist/services/instructions.dispatcher.d.ts +1 -0
  270. package/dist/services/instructions.dispatcher.js +231 -0
  271. package/dist/services/logPrefix.d.ts +1 -0
  272. package/dist/services/logPrefix.js +30 -0
  273. package/dist/services/logger.d.ts +52 -0
  274. package/dist/services/logger.js +268 -0
  275. package/dist/services/manifestManager.d.ts +82 -0
  276. package/dist/services/manifestManager.js +200 -0
  277. package/dist/services/messaging/agentMailbox.d.ts +60 -0
  278. package/dist/services/messaging/agentMailbox.js +353 -0
  279. package/dist/services/messaging/messagingPersistence.d.ts +20 -0
  280. package/dist/services/messaging/messagingPersistence.js +111 -0
  281. package/dist/services/messaging/messagingTypes.d.ts +150 -0
  282. package/dist/services/messaging/messagingTypes.js +66 -0
  283. package/dist/services/ownershipService.d.ts +1 -0
  284. package/dist/services/ownershipService.js +38 -0
  285. package/dist/services/performanceBaseline.d.ts +19 -0
  286. package/dist/services/performanceBaseline.js +210 -0
  287. package/dist/services/preflight.d.ts +12 -0
  288. package/dist/services/preflight.js +79 -0
  289. package/dist/services/promptReviewService.d.ts +44 -0
  290. package/dist/services/promptReviewService.js +101 -0
  291. package/dist/services/responseEnvelope.d.ts +6 -0
  292. package/dist/services/responseEnvelope.js +25 -0
  293. package/dist/services/seedBootstrap.d.ts +34 -0
  294. package/dist/services/seedBootstrap.js +427 -0
  295. package/dist/services/storage/factory.d.ts +17 -0
  296. package/dist/services/storage/factory.js +35 -0
  297. package/dist/services/storage/hashUtils.d.ts +11 -0
  298. package/dist/services/storage/hashUtils.js +35 -0
  299. package/dist/services/storage/index.d.ts +12 -0
  300. package/dist/services/storage/index.js +18 -0
  301. package/dist/services/storage/jsonFileStore.d.ts +32 -0
  302. package/dist/services/storage/jsonFileStore.js +241 -0
  303. package/dist/services/storage/migrationEngine.d.ts +35 -0
  304. package/dist/services/storage/migrationEngine.js +93 -0
  305. package/dist/services/storage/sqliteMessageStore.d.ts +53 -0
  306. package/dist/services/storage/sqliteMessageStore.js +146 -0
  307. package/dist/services/storage/sqliteSchema.d.ts +12 -0
  308. package/dist/services/storage/sqliteSchema.js +122 -0
  309. package/dist/services/storage/sqliteStore.d.ts +41 -0
  310. package/dist/services/storage/sqliteStore.js +339 -0
  311. package/dist/services/storage/sqliteUsageStore.d.ts +35 -0
  312. package/dist/services/storage/sqliteUsageStore.js +94 -0
  313. package/dist/services/storage/types.d.ts +171 -0
  314. package/dist/services/storage/types.js +12 -0
  315. package/dist/services/toolHandlers.d.ts +23 -0
  316. package/dist/services/toolHandlers.js +50 -0
  317. package/dist/services/toolRegistry.d.ts +20 -0
  318. package/dist/services/toolRegistry.js +490 -0
  319. package/dist/services/toolRegistry.zod.d.ts +10 -0
  320. package/dist/services/toolRegistry.zod.js +323 -0
  321. package/dist/services/tracing.d.ts +26 -0
  322. package/dist/services/tracing.js +260 -0
  323. package/dist/services/usageBuckets.d.ts +161 -0
  324. package/dist/services/usageBuckets.js +364 -0
  325. package/dist/services/validationService.d.ts +38 -0
  326. package/dist/services/validationService.js +125 -0
  327. package/dist/utils/BufferRing.d.ts +203 -0
  328. package/dist/utils/BufferRing.js +551 -0
  329. package/dist/utils/BufferRingExamples.d.ts +55 -0
  330. package/dist/utils/BufferRingExamples.js +188 -0
  331. package/dist/utils/envUtils.d.ts +42 -0
  332. package/dist/utils/envUtils.js +80 -0
  333. package/dist/utils/memoryMonitor.d.ts +83 -0
  334. package/dist/utils/memoryMonitor.js +275 -0
  335. package/dist/versioning/schemaVersion.d.ts +6 -0
  336. package/dist/versioning/schemaVersion.js +93 -0
  337. package/package.json +134 -0
  338. package/schemas/README.md +13 -0
  339. package/schemas/feedback-entry.schema.json +27 -0
  340. package/schemas/graph-export-v2.schema.json +60 -0
  341. package/schemas/index-server.code-schema.json +38477 -0
  342. package/schemas/instruction.schema.json +262 -0
  343. package/schemas/json-schema/SessionPersistence-persisted-admin-session.schema.json +54 -0
  344. package/schemas/json-schema/SessionPersistence-persisted-session-history-entry.schema.json +51 -0
  345. package/schemas/json-schema/SessionPersistence-persisted-web-socket-connection.schema.json +54 -0
  346. package/schemas/json-schema/SessionPersistence-session-persistence-config.schema.json +110 -0
  347. package/schemas/json-schema/SessionPersistence-session-persistence-data.schema.json +229 -0
  348. package/schemas/json-schema/SessionPersistence-session-persistence-manifest.schema.json +109 -0
  349. package/schemas/json-schema/SessionPersistence-session-persistence-metadata.schema.json +55 -0
  350. package/schemas/json-schema/instruction-audience-scope.schema.json +14 -0
  351. package/schemas/json-schema/instruction-content-type.schema.json +17 -0
  352. package/schemas/json-schema/instruction-instruction-entry.schema.json +206 -0
  353. package/schemas/json-schema/instruction-requirement-level.schema.json +16 -0
  354. package/schemas/manifest.json +78 -0
  355. package/schemas/manifest.schema.json +33 -0
  356. package/schemas/usage-batch.schema.json +16 -0
  357. package/schemas/usage-buckets.schema.json +30 -0
  358. package/schemas/usage-event.schema.json +17 -0
  359. package/scripts/copy-dashboard-assets.mjs +170 -0
  360. package/scripts/setup-hooks.cjs +28 -0
@@ -0,0 +1,552 @@
1
+ /* eslint-disable */
2
+ // Extracted instruction management from admin.html
3
+ (function(){
4
+ 'use strict';
5
+
6
+ // Helper: safe global references (these live on page scope)
7
+ const globals = window;
8
+
9
+ // Defensive defaults so first render has a valid page context even if loadInstructions
10
+ // has not executed yet (prevents slice(NaN, NaN) -> empty list artifact).
11
+ if (globals.instructionPage == null || Number.isNaN(globals.instructionPage)) globals.instructionPage = 1;
12
+ if (globals.instructionPageSize == null || (globals.instructionPageSize !== 'All' && !Number.isFinite(globals.instructionPageSize))) globals.instructionPageSize = 25;
13
+
14
+ // Usage snapshot cache (loaded once per loadInstructions call)
15
+ let usageSnapshot = {};
16
+ async function fetchUsageSnapshot() {
17
+ try {
18
+ const res = await fetch('/api/usage/snapshot');
19
+ if (!res.ok) return {};
20
+ const data = await res.json();
21
+ return data.snapshot || {};
22
+ } catch { return {}; }
23
+ }
24
+
25
+ function getSignalBadge(signal) {
26
+ if (!signal) return '';
27
+ const colors = { 'outdated': '#f2495c', 'not-relevant': '#ff9830', 'helpful': '#73bf69', 'applied': '#5794f2' };
28
+ const color = colors[signal] || '#888';
29
+ return '<span class="signal-badge" style="background:' + color + '22;color:' + color + ';border:1px solid ' + color + '44;padding:1px 6px;border-radius:3px;font-size:10px;font-weight:600;">' + signal + '</span>';
30
+ }
31
+
32
+ async function loadInstructionCategories() {
33
+ try {
34
+ const res = await fetch('/api/instructions_categories');
35
+ if(!res.ok) throw new Error('http '+res.status);
36
+ const data = await res.json();
37
+ let cats = data.categories || data.data?.categories || [];
38
+ if(Array.isArray(cats) && cats.length && typeof cats[0] === 'string') {
39
+ cats = cats.map(n=>({ name:n, count: undefined }));
40
+ }
41
+ if(!Array.isArray(cats)) cats = [];
42
+ const select = document.getElementById('instruction-category-filter');
43
+ if(select){
44
+ select.innerHTML = '<option value="">All Categories</option>';
45
+ cats.forEach(cat => {
46
+ if(!cat || !cat.name) return;
47
+ const option = document.createElement('option');
48
+ option.value = cat.name;
49
+ option.textContent = cat.count != null ? `${cat.name} (${cat.count})` : cat.name;
50
+ select.appendChild(option);
51
+ });
52
+ }
53
+ return cats.map(c=>c.name);
54
+ } catch (e) {
55
+ console.warn('Failed to load instruction categories:', e);
56
+ return [];
57
+ }
58
+ }
59
+
60
+ function getFilteredInstructions(list) {
61
+ const nameFilter = (document.getElementById('instruction-filter').value || '');
62
+ const isRegex = document.getElementById('instruction-regex-toggle')?.checked || false;
63
+ const categoryFilter = (document.getElementById('instruction-category-filter')?.value || '');
64
+ const sizeFilter = (document.getElementById('instruction-size-filter')?.value || '');
65
+ const filterInput = document.getElementById('instruction-filter');
66
+ let filtered;
67
+ if (isRegex && nameFilter) {
68
+ try {
69
+ const re = new RegExp(nameFilter, 'i');
70
+ filtered = list.filter(i => re.test(i.name || ''));
71
+ if (filterInput) filterInput.style.borderColor = '';
72
+ } catch (e) {
73
+ filtered = [];
74
+ if (filterInput) filterInput.style.borderColor = '#f2495c';
75
+ }
76
+ } else {
77
+ if (filterInput) filterInput.style.borderColor = '';
78
+ filtered = list.filter(i => (i.name||'').toLowerCase().includes(nameFilter.toLowerCase()));
79
+ }
80
+ if (categoryFilter) {
81
+ filtered = filtered.filter(i => {
82
+ if (i.category === categoryFilter) return true;
83
+ if (Array.isArray(i.categories) && i.categories.includes(categoryFilter)) return true;
84
+ return false;
85
+ });
86
+ }
87
+ if (sizeFilter) filtered = filtered.filter(i => i.sizeCategory === sizeFilter);
88
+ const sortSelect = document.getElementById('instruction-sort');
89
+ const sortVal = sortSelect ? sortSelect.value : 'name-asc';
90
+ const cmp = (a,b, key, dir='asc') => {
91
+ if (a[key] === b[key]) return 0;
92
+ return (a[key] < b[key] ? -1 : 1) * (dir === 'asc' ? 1 : -1);
93
+ };
94
+ switch(sortVal) {
95
+ case 'name-desc': filtered.sort((a,b)=>cmp(a,b,'name','desc')); break;
96
+ case 'size-asc': filtered.sort((a,b)=>cmp(a,b,'size','asc')); break;
97
+ case 'size-desc': filtered.sort((a,b)=>cmp(a,b,'size','desc')); break;
98
+ case 'mtime-asc': filtered.sort((a,b)=>cmp(a,b,'mtime','asc')); break;
99
+ case 'mtime-desc': filtered.sort((a,b)=>cmp(a,b,'mtime','desc')); break;
100
+ case 'category': filtered.sort((a,b)=>cmp(a,b,'category','asc') || cmp(a,b,'name','asc')); break;
101
+ case 'usage-desc': filtered.sort((a,b)=> {
102
+ const ua = (usageSnapshot[a.name]?.usageCount ?? 0);
103
+ const ub = (usageSnapshot[b.name]?.usageCount ?? 0);
104
+ return ub - ua || cmp(a,b,'name','asc');
105
+ }); break;
106
+ case 'signal': filtered.sort((a,b)=> {
107
+ const sa = usageSnapshot[a.name]?.lastSignal || '';
108
+ const sb = usageSnapshot[b.name]?.lastSignal || '';
109
+ return sa.localeCompare(sb) || cmp(a,b,'name','asc');
110
+ }); break;
111
+ default:
112
+ filtered.sort((a,b)=>cmp(a,b,'name','asc'));
113
+ }
114
+ return filtered;
115
+ }
116
+
117
+ function highlightMatch(text, filter, isRegex) {
118
+ if (!filter || !text) return text;
119
+ try {
120
+ const re = isRegex ? new RegExp('(' + filter + ')', 'gi') : new RegExp('(' + filter.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') + ')', 'gi');
121
+ return text.replace(re, '<mark class="search-highlight">$1</mark>');
122
+ } catch { return text; }
123
+ }
124
+
125
+ function buildInstructionPaginationControls(totalFiltered) {
126
+ const container = document.getElementById('instruction-pagination');
127
+ if (!container) return;
128
+ const total = totalFiltered;
129
+ const pageSize = globals.instructionPageSize === 'All' ? total : globals.instructionPageSize;
130
+ const totalPages = pageSize === 0 ? 1 : Math.max(1, Math.ceil(total / pageSize));
131
+ if (globals.instructionPage > totalPages) globals.instructionPage = totalPages;
132
+ const disablePrev = globals.instructionPage <= 1;
133
+ const disableNext = globals.instructionPage >= totalPages;
134
+ const sizeOptions = [10,25,50,100,'All'].map(s => `<option value="${s}" ${s===globals.instructionPageSize? 'selected':''}>${s}</option>`).join('');
135
+ container.innerHTML = `
136
+ <div style="display:flex; align-items:center; gap:8px; flex-wrap:wrap;">
137
+ <label style="display:flex; align-items:center; gap:4px;">Page Size:
138
+ <select id="instruction-page-size" class="form-input" style="width:auto; padding:4px;">${sizeOptions}</select>
139
+ </label>
140
+ <div style="display:flex; align-items:center; gap:4px;">
141
+ <button class="action-btn" onclick="changeInstructionPage('first')" ${disablePrev?'disabled':''}>⏮ First</button>
142
+ <button class="action-btn" onclick="changeInstructionPage('prev')" ${disablePrev?'disabled':''}>â—€ Prev</button>
143
+ <span style="font-size:12px;">Page ${globals.instructionPage} / ${totalPages}</span>
144
+ <button class="action-btn" onclick="changeInstructionPage('next')" ${disableNext?'disabled':''}>Next â–¶</button>
145
+ <button class="action-btn" onclick="changeInstructionPage('last')" ${disableNext?'disabled':''}>Last ⏭</button>
146
+ </div>
147
+ <span style="margin-left:auto; font-size:12px; opacity:0.8;">Filtered: ${total} total</span>
148
+ </div>`;
149
+ const sizeSelect = document.getElementById('instruction-page-size');
150
+ if(sizeSelect) sizeSelect.onchange = () => {
151
+ globals.instructionPageSize = sizeSelect.value === 'All' ? 'All' : parseInt(sizeSelect.value,10);
152
+ globals.instructionPage = 1;
153
+ renderInstructionList(globals.allInstructions || []);
154
+ };
155
+ }
156
+
157
+ function changeInstructionPage(dir) {
158
+ const totalFiltered = getFilteredInstructions(globals.allInstructions || []).length;
159
+ const pageSizeVal = globals.instructionPageSize === 'All' ? totalFiltered : globals.instructionPageSize;
160
+ const totalPages = pageSizeVal === 0 ? 1 : Math.max(1, Math.ceil(totalFiltered / pageSizeVal));
161
+ if (dir === 'first') globals.instructionPage = 1;
162
+ else if (dir === 'prev' && globals.instructionPage > 1) globals.instructionPage--;
163
+ else if (dir === 'next' && globals.instructionPage < totalPages) globals.instructionPage++;
164
+ else if (dir === 'last') globals.instructionPage = totalPages;
165
+ renderInstructionList(globals.allInstructions || []);
166
+ }
167
+
168
+ function renderInstructionList(instructions) {
169
+ const filtered = getFilteredInstructions(instructions || []);
170
+ try { console.debug('[admin.instructions] renderInstructionList: filteredCount=', filtered.length, 'pageSize=', globals.instructionPageSize, 'page=', globals.instructionPage); } catch(e){}
171
+ if (filtered.length === 0) {
172
+ const el = document.getElementById('instructions-list'); if(el) el.innerHTML = '<p>No instructions found</p>';
173
+ buildInstructionPaginationControls(0);
174
+ try { console.debug('[admin.instructions] renderInstructionList: no items rendered'); } catch(e){}
175
+ try { const dbg = document.getElementById('admin-debug'); if(dbg) dbg.textContent = JSON.stringify({ stage:'renderInstructionList', filtered:0, page: globals.instructionPage }, null, 2); } catch(e){}
176
+ return;
177
+ }
178
+ const totalFiltered = filtered.length;
179
+ let pageItems = filtered;
180
+ if (globals.instructionPageSize !== 'All') {
181
+ const start = (globals.instructionPage - 1) * globals.instructionPageSize;
182
+ const end = start + globals.instructionPageSize;
183
+ pageItems = filtered.slice(start, end);
184
+ }
185
+ const nameFilter = (document.getElementById('instruction-filter')?.value || '').trim();
186
+ const isRegex = document.getElementById('instruction-regex-toggle')?.checked || false;
187
+ const rows = pageItems.map(instr => {
188
+ const rawSummary = (instr.semanticSummary || '').trim();
189
+ let short = rawSummary.slice(0, 200);
190
+ if (rawSummary.length > 200) short += '…';
191
+ const safeSummary = globals.escapeHtml ? globals.escapeHtml(short) : (short.replace(/&/g,'&amp;'));
192
+ const cat = instr.category || (Array.isArray(instr.categories) && instr.categories[0]) || '—';
193
+ const highlightedName = nameFilter ? highlightMatch(instr.name, nameFilter, isRegex) : instr.name;
194
+ const highlightedSummary = nameFilter ? highlightMatch(safeSummary, nameFilter, isRegex) : safeSummary;
195
+ const usage = usageSnapshot[instr.name] || {};
196
+ const usageCount = usage.usageCount ?? 0;
197
+ const signal = usage.lastSignal || '';
198
+ const comment = usage.lastComment || '';
199
+ const signalHtml = signal ? getSignalBadge(signal) : '<span style="opacity:.4;font-size:10px;">none</span>';
200
+ const commentTip = comment ? ' title="Last comment: ' + comment.replace(/"/g, '&quot;').slice(0, 200) + '"' : '';
201
+ return `
202
+ <div class="instruction-item" data-instruction="${instr.name}">
203
+ <div class="instruction-item-header">
204
+ <div class="instruction-name">${highlightedName}</div>
205
+ <div class="instruction-actions">
206
+ <button class="action-btn" onclick="editInstruction('${instr.name}')">✏ Edit</button>
207
+ <button class="action-btn danger" onclick="deleteInstruction('${instr.name}')">đź—‘ Delete</button>
208
+ </div>
209
+ </div>
210
+ <div class="instruction-meta">
211
+ <div class="meta-chip" title="Category"><span class="chip-label">CAT</span><span class="chip-value">${cat}</span></div>
212
+ <div class="meta-chip" title="Size"><span class="chip-label">SIZE</span><span class="chip-value">${instr.size}</span><span class="chip-sub">(${instr.sizeCategory})</span></div>
213
+ <div class="meta-chip" title="Last Modified"><span class="chip-label">MTIME</span><span class="chip-value">${new Date(instr.mtime).toLocaleString()}</span></div>
214
+ <div class="meta-chip" title="Usage Count"><span class="chip-label">USES</span><span class="chip-value">${usageCount}</span></div>
215
+ <div class="meta-chip"${commentTip}><span class="chip-label">SIGNAL</span><span class="chip-value">${signalHtml}</span></div>
216
+ </div>
217
+ <div class="instruction-summary">${highlightedSummary || '<span class="summary-empty">No summary</span>'}</div>
218
+ </div>`;
219
+ }).join('');
220
+ const listEl = document.getElementById('instructions-list'); if(listEl) listEl.innerHTML = rows;
221
+ buildInstructionPaginationControls(totalFiltered);
222
+ try { console.debug('[admin.instructions] renderInstructionList: rendered rows=', pageItems.length); } catch(e){}
223
+ try { const dbg = document.getElementById('admin-debug'); if(dbg) dbg.textContent = JSON.stringify({ stage:'renderInstructionList', filtered: totalFiltered, rendered: pageItems.length, page: globals.instructionPage }, null, 2); } catch(e){}
224
+ }
225
+
226
+ function filterInstructions(){ globals.instructionPage = 1; renderInstructionList(globals.allInstructions || []); }
227
+
228
+ function showCreateInstruction(){
229
+ globals.instructionEditing = null;
230
+ const title = document.getElementById('instruction-editor-title'); if(title) title.textContent = 'New Instruction';
231
+ const filename = document.getElementById('instruction-filename'); if(filename){ filename.value=''; filename.disabled=false; }
232
+ const content = document.getElementById('instruction-content'); if(content) content.value = '';
233
+ applyInstructionTemplate();
234
+ globals.ensureInstructionEditorAtTop && globals.ensureInstructionEditorAtTop();
235
+ const ed = document.getElementById('instruction-editor'); if(ed) ed.classList.remove('hidden');
236
+ try { ed.scrollIntoView({ behavior:'smooth', block:'start' }); } catch {}
237
+ const fn = document.getElementById('instruction-filename'); if(fn) fn.focus();
238
+ globals.instructionOriginalContent = document.getElementById('instruction-content').value;
239
+ updateInstructionEditorDiagnostics();
240
+ }
241
+
242
+ async function editInstruction(name){
243
+ const editor = document.getElementById('instruction-editor');
244
+ const filenameEl = document.getElementById('instruction-filename');
245
+ const contentEl = document.getElementById('instruction-content');
246
+ let attempts = 0; const maxAttempts = 2; let lastError;
247
+ while(attempts < maxAttempts){
248
+ try{
249
+ attempts++;
250
+ if(contentEl && attempts===1) contentEl.value = '// Loading ' + name + '...';
251
+ const res = await fetch('/api/instructions/' + encodeURIComponent(name));
252
+ if(!res.ok) throw new Error('http '+res.status);
253
+ const data = await res.json();
254
+ if(data.success === false && !data.content && !data.data?.content) throw new Error('server reported failure');
255
+ if(!data.content && data.data?.content) data.content = data.data.content;
256
+ if(!data.content) throw new Error('missing content');
257
+ globals.instructionEditing = name;
258
+ const title = document.getElementById('instruction-editor-title'); if(title) title.textContent = 'Edit Instruction: ' + name;
259
+ if(filenameEl){ filenameEl.value = name; filenameEl.disabled = true; }
260
+ const pretty = JSON.stringify(data.content, null, 2);
261
+ if(contentEl) contentEl.value = pretty;
262
+ globals.ensureInstructionEditorAtTop && globals.ensureInstructionEditorAtTop();
263
+ if(editor) editor.classList.remove('hidden');
264
+ try { editor.scrollIntoView({ behavior:'smooth', block:'start' }); } catch {}
265
+ globals.instructionOriginalContent = pretty;
266
+ updateInstructionEditorDiagnostics();
267
+ return;
268
+ } catch(e){ lastError = e; if(attempts < maxAttempts) await new Promise(r=>setTimeout(r,120)); }
269
+ }
270
+ console.warn('editInstruction failed after retries', lastError);
271
+ globals.showError && globals.showError('Failed to load instruction');
272
+ }
273
+
274
+ function cancelEditInstruction(){ const ed = document.getElementById('instruction-editor'); if(ed) ed.classList.add('hidden'); const diff = document.getElementById('instruction-diff-container'); if(diff) diff.classList.add('hidden'); const preview = document.getElementById('instruction-preview-container'); if(preview) preview.classList.add('hidden'); globals.instructionOriginalContent=''; globals.instructionPreviewVisible = false; const btn = document.getElementById('instruction-preview-btn'); if(btn) btn.textContent = 'đź“– Preview'; }
275
+
276
+ function ensureInstructionEditorAtTop(){
277
+ try{
278
+ const editor = document.getElementById('instruction-editor');
279
+ const list = document.getElementById('instructions-list');
280
+ if(!editor || !list) return;
281
+ const parent = list.parentElement;
282
+ if(parent && parent.contains(list)){
283
+ if(editor.nextElementSibling !== list) parent.insertBefore(editor, list);
284
+ }
285
+ }catch{}
286
+ }
287
+
288
+ function safeParseInstruction(raw){ try { return JSON.parse(raw); } catch { return null; } }
289
+
290
+ function updateInstructionEditorDiagnostics(){
291
+ const ta = document.getElementById('instruction-content');
292
+ const diag = document.getElementById('instruction-diagnostics');
293
+ if(!ta||!diag) return;
294
+ const raw = ta.value;
295
+ if(!raw.trim()){ diag.innerHTML = '<em>Empty.</em>'; return; }
296
+ const parsed = safeParseInstruction(raw);
297
+ if(!parsed){ diag.innerHTML = '<span style="color:#f2495c;">Invalid JSON</span>'; }
298
+ else {
299
+ const size = raw.length;
300
+ const cats = Array.isArray(parsed.categories)? parsed.categories.length : 0;
301
+ const schemaVer = parsed.schemaVersion || parsed.schema || '?';
302
+ const changed = globals.instructionOriginalContent && raw !== globals.instructionOriginalContent;
303
+ diag.innerHTML = `Size: ${size} chars • Categories: ${cats} • Schema: ${schemaVer} ${changed?'<span style="color:#ff9830;' + '">(modified)</span>':''}`;
304
+ }
305
+ if(globals.instructionDiffVisible) refreshInstructionDiff();
306
+ if(globals.instructionPreviewVisible) refreshInstructionPreview();
307
+ }
308
+
309
+ function refreshInstructionDiff(){
310
+ const diffWrap = document.getElementById('instruction-diff-container');
311
+ const diffPre = document.getElementById('instruction-diff');
312
+ const ta = document.getElementById('instruction-content');
313
+ if(!diffWrap||!diffPre||!ta) return;
314
+ if(!globals.instructionOriginalContent){ diffPre.textContent='(no baseline)'; return; }
315
+ if(ta.value === globals.instructionOriginalContent){ diffPre.textContent='(no changes)'; return; }
316
+ const before = globals.instructionOriginalContent.split(/\r?\n/);
317
+ const after = ta.value.split(/\r?\n/);
318
+ const max = Math.max(before.length, after.length);
319
+ const out = [];
320
+ for(let i=0;i<max;i++){ const a = before[i]; const b = after[i]; if(a === b){ if(a !== undefined) out.push(' ' + a); } else { if(a !== undefined) out.push('- ' + a); if(b !== undefined) out.push('+ ' + b); } }
321
+ diffPre.textContent = out.join('\n');
322
+ }
323
+
324
+ function toggleInstructionDiff(){ globals.instructionDiffVisible = !globals.instructionDiffVisible; const wrap = document.getElementById('instruction-diff-container'); if(!wrap) return; if(globals.instructionDiffVisible){ wrap.classList.remove('hidden'); refreshInstructionDiff(); } else { wrap.classList.add('hidden'); } }
325
+
326
+ function toggleInstructionPreview(){
327
+ globals.instructionPreviewVisible = !globals.instructionPreviewVisible;
328
+ const wrap = document.getElementById('instruction-preview-container');
329
+ const btn = document.getElementById('instruction-preview-btn');
330
+ if(!wrap) return;
331
+ if(globals.instructionPreviewVisible){
332
+ wrap.classList.remove('hidden');
333
+ if(btn) btn.textContent = 'đź“– Hide Preview';
334
+ refreshInstructionPreview();
335
+ } else {
336
+ wrap.classList.add('hidden');
337
+ if(btn) btn.textContent = 'đź“– Preview';
338
+ }
339
+ }
340
+
341
+ function refreshInstructionPreview(){
342
+ const previewEl = document.getElementById('instruction-preview');
343
+ const ta = document.getElementById('instruction-content');
344
+ if(!previewEl || !ta) return;
345
+ const raw = ta.value;
346
+ const parsed = safeParseInstruction(raw);
347
+ if(!parsed){ previewEl.innerHTML = '<em style="opacity:.5;">Cannot preview: invalid JSON</em>'; return; }
348
+ const body = parsed.body || '';
349
+ if(!body.trim()){ previewEl.innerHTML = '<em style="opacity:.5;">No body content to preview</em>'; return; }
350
+ try {
351
+ if(typeof marked !== 'undefined' && marked.parse){
352
+ previewEl.innerHTML = marked.parse(body, { breaks: false, gfm: true });
353
+ } else {
354
+ previewEl.textContent = body;
355
+ }
356
+ } catch(e){
357
+ previewEl.textContent = body;
358
+ }
359
+ }
360
+
361
+ async function saveInstruction(){
362
+ const nameEl = document.getElementById('instruction-filename');
363
+ const ta = document.getElementById('instruction-content');
364
+ if(!nameEl||!ta) return;
365
+ const raw = ta.value;
366
+ const parsed = safeParseInstruction(raw);
367
+ if(!parsed){ globals.showError && globals.showError('Cannot save: invalid JSON'); return; }
368
+ if(parsed && parsed.schemaVersion && /^1(\.|$)/.test(String(parsed.schemaVersion))){ parsed.schemaVersion = '2'; }
369
+ const body = { content: parsed };
370
+ let url = '/api/instructions'; let method = 'POST';
371
+ if(globals.instructionEditing){ url += '/' + encodeURIComponent(globals.instructionEditing); method = 'PUT'; }
372
+ else { body.name = nameEl.value.trim(); if(!body.name){ globals.showError && globals.showError('Provide file name'); return; } }
373
+ try{
374
+ const res = await fetch(url, { method, headers:{'Content-Type':'application/json'}, body: JSON.stringify(body)});
375
+ const data = await res.json();
376
+ if(!res.ok || !data.success){ throw new Error(data.error || data.message || 'Save failed'); }
377
+ globals.showSuccess && globals.showSuccess(globals.instructionEditing? 'Instruction updated':'Instruction created');
378
+ globals.instructionOriginalContent = JSON.stringify(parsed, null, 2);
379
+ ta.value = globals.instructionOriginalContent;
380
+ if(!globals.instructionEditing) globals.instructionEditing = body.name;
381
+ updateInstructionEditorDiagnostics();
382
+ loadInstructions();
383
+ } catch(e){ globals.showError && globals.showError(e.message || 'Save failed'); }
384
+ }
385
+
386
+ async function loadInstructions() {
387
+ const listEl = document.getElementById('instructions-list'); if(listEl) listEl.innerHTML = 'Loading...';
388
+ // create hidden debug sink so Playwright can read client diagnostics from DOM
389
+ try {
390
+ let dbg = document.getElementById('admin-debug');
391
+ if(!dbg){ dbg = document.createElement('div'); dbg.id = 'admin-debug'; dbg.style.display='none'; dbg.style.whiteSpace='pre'; document.body.appendChild(dbg); }
392
+ } catch(e){}
393
+ try{
394
+ try { console.debug('[admin.instructions] loadInstructions: start'); } catch(e){}
395
+ const [catNames, snapData] = await Promise.all([loadInstructionCategories(), fetchUsageSnapshot()]);
396
+ usageSnapshot = snapData;
397
+ const res = await fetch('/api/instructions'); if(!res.ok) throw new Error('http '+res.status);
398
+ const data = await res.json();
399
+ if (!('success' in data) && !('data' in data) && !('instructions' in data)) throw new Error('unrecognized instructions payload');
400
+ const rawList = data.instructions || data.data?.instructions || [];
401
+ globals.allInstructions = Array.isArray(rawList) ? rawList : [];
402
+ try { console.log('[admin.instructions] fetched instructions:', globals.allInstructions.length); } catch {}
403
+ try { console.debug('[admin.instructions] loadInstructions: sampleNames=', (globals.allInstructions||[]).slice(0,6).map(i=>i.name)); } catch(e){}
404
+ try { const dbg = document.getElementById('admin-debug'); if(dbg) dbg.textContent = JSON.stringify({ stage:'loadInstructions', count: (globals.allInstructions||[]).length, sample: (globals.allInstructions||[]).slice(0,6).map(i=>i.name) }, null, 2); } catch(e){}
405
+ if(!catNames.length){ try { const select = document.getElementById('instruction-category-filter'); if(select){ select.innerHTML = '<option value="">All Categories</option>'; const derived = Array.from(new Set(globals.allInstructions.flatMap(i=> [i.category, ...(Array.isArray(i.categories)? i.categories: [])]).filter(Boolean))).sort(); derived.forEach(n=>{ const opt = document.createElement('option'); opt.value = n; opt.textContent = n; select.appendChild(opt); }); } } catch(_){} }
406
+ globals.instructionPage = 1;
407
+ renderInstructionList(globals.allInstructions || []);
408
+ } catch(e){ console.warn('loadInstructions error', e); if(listEl) listEl.innerHTML = '<div class="error">Failed to load instructions</div>'; }
409
+ }
410
+
411
+ function formatInstructionJson(){ const ta = document.getElementById('instruction-content'); if(!ta) return; try{ const parsed = JSON.parse(ta.value); ta.value = JSON.stringify(parsed, null, 2); updateInstructionEditorDiagnostics(); } catch { globals.showError && globals.showError('Cannot format: invalid JSON'); } }
412
+
413
+ function applyInstructionTemplate(){ const ta = document.getElementById('instruction-content'); if(!ta) return; if(ta.value.trim() && !confirm('Replace current content with template?')) return; const now = new Date().toISOString(); const template = { id:'sample-instruction', title:'Sample Instruction', body:'Detailed instruction content here.\nAdd multi-line guidance and steps.', contentType:'instruction', priority:50, audience:'all', requirement:'optional', categories:['general'], primaryCategory:'general', schemaVersion:'4', status:'draft', owner:'you@example.com', version:'1.0.0', reviewIntervalDays:180, semanticSummary:'Brief summary of what this instruction covers.', createdAt: now, updatedAt: now }; ta.value = JSON.stringify(template, null, 2); updateInstructionEditorDiagnostics(); }
414
+
415
+ async function deleteInstruction(name) {
416
+ if (!confirm('Delete instruction ' + name + '?')) return;
417
+ try {
418
+ const res = await fetch('/api/instructions/' + encodeURIComponent(name), { method:'DELETE' });
419
+ const data = await res.json();
420
+ if (data.success) { globals.showSuccess && globals.showSuccess('Deleted'); loadInstructions(); } else { globals.showError && globals.showError(data.error || 'Delete failed'); }
421
+ } catch { globals.showError && globals.showError('Delete failed'); }
422
+ }
423
+
424
+ async function performGlobalInstructionSearch(query){
425
+ const outEl = document.getElementById('instruction-global-results');
426
+ if(!outEl) return;
427
+ const trimmed = (query||'').trim();
428
+ const isRegex = document.getElementById('instruction-global-regex-toggle')?.checked || false;
429
+ if(!trimmed || trimmed.length < 2){ outEl.innerHTML = '<em style="opacity:.6;">Enter 2+ chars for global search.</em>'; return; }
430
+ let re = null;
431
+ if (isRegex) {
432
+ try { re = new RegExp(trimmed, 'i'); } catch (e) { outEl.innerHTML = '<span style="color:#f2495c;">Invalid regex: ' + (e.message||e) + '</span>'; return; }
433
+ }
434
+ const started = performance.now();
435
+ outEl.innerHTML = '<span style="opacity:.75;">🔍 Searching…</span>';
436
+ try {
437
+ let results;
438
+ if (re) {
439
+ // Regex mode: fetch all instructions and filter client-side
440
+ const res = await fetch('/api/instructions');
441
+ const data = await res.json();
442
+ if(!res.ok) throw new Error('http ' + res.status);
443
+ const allInstrs = data.instructions || data.data?.instructions || [];
444
+ // Load full content for each matching instruction
445
+ const matched = [];
446
+ for (const instr of allInstrs) {
447
+ const nameMatch = re.test(instr.name || '');
448
+ const catMatch = Array.isArray(instr.categories) && instr.categories.some(c => re.test(c));
449
+ const summaryMatch = re.test(instr.semanticSummary || '');
450
+ if (nameMatch || catMatch || summaryMatch) {
451
+ matched.push({ name: instr.name, snippet: instr.semanticSummary || '', categories: instr.categories || [instr.category].filter(Boolean), score: nameMatch ? 1 : 0.5 });
452
+ }
453
+ }
454
+ results = { results: matched, count: matched.length };
455
+ } else {
456
+ const res = await fetch('/api/instructions_search?q=' + encodeURIComponent(trimmed));
457
+ results = await res.json();
458
+ if(!res.ok || results.success === false){ throw new Error(results.error||'Search failed'); }
459
+ }
460
+ const elapsed = Math.round(performance.now() - started);
461
+ if(!Array.isArray(results.results) || !results.results.length){ outEl.innerHTML = `<span style="opacity:.6;">No global matches (${isRegex ? 'regex' : 'q'}='${trimmed}', ${elapsed}ms).</span>`; return; }
462
+ const rows = results.results.map(r=>{
463
+ let safeName = (r.name||'').replace(/[&<>]/g, c=> ({'&':'&amp;','<':'&lt;','>':'&gt;'}[c]));
464
+ let safeSnippet = (r.snippet||'').replace(/[&<>]/g, c=> ({'&':'&amp;','<':'&lt;','>':'&gt;'}[c])).replace(/\*\*(.+?)\*\*/g,'<mark>$1</mark>');
465
+ safeName = highlightMatch(safeName, trimmed, isRegex);
466
+ safeSnippet = highlightMatch(safeSnippet, trimmed, isRegex);
467
+ const cats = Array.isArray(r.categories) && r.categories.length? r.categories.slice(0,6).join(', ') : '—';
468
+ return `<div class="instruction-global-result" style="background:#1f2228; border:1px solid #2c3038; border-radius:4px; padding:6px 8px; margin-bottom:6px;">
469
+ <div style="font-weight:600; font-size:12px;">${safeName} <span style="opacity:.55; font-weight:400;">(${cats})</span></div>
470
+ <div style="font-size:11px; white-space:normal;">${safeSnippet}</div>
471
+ </div>`; }).join('');
472
+ outEl.innerHTML = `<div style="margin-bottom:6px; font-weight:600;">Global Search Results (${results.count})${isRegex ? ' <span style="color:#73bf69;">[regex]</span>' : ''} <span style="opacity:.55; font-weight:400;">${elapsed}ms</span></div>` + rows;
473
+ try { outEl.scrollIntoView({ behavior:'smooth', block:'center' }); } catch { /* ignore */ }
474
+ } catch(e){ outEl.innerHTML = '<span style="color:#f2495c;">Global search error: '+ (e.message||e) +'</span>'; }
475
+ }
476
+
477
+ function attachGlobalSearchHandlers(){
478
+ const btn = document.getElementById('instruction-global-search-btn');
479
+ const input = document.getElementById('instruction-global-search');
480
+ if(btn) btn.onclick = ()=> performGlobalInstructionSearch(input.value);
481
+ if(input) input.addEventListener('keydown', e=>{ if(e.key==='Enter'){ performGlobalInstructionSearch(input.value); }});
482
+ }
483
+
484
+ // fallback integration: if local filter returns zero but name filter has >=3 chars run global
485
+ function maybeTriggerGlobalFallback(){
486
+ try {
487
+ const nameFilter = (document.getElementById('instruction-filter')?.value||'').trim();
488
+ const list = globals.allInstructions || [];
489
+ const filtered = getFilteredInstructions(list);
490
+ if(filtered.length === 0 && nameFilter.length >= 3){
491
+ performGlobalInstructionSearch(nameFilter);
492
+ }
493
+ } catch{/* ignore */
494
+ }
495
+ }
496
+
497
+ // Override legacy global renderer with new chip-based implementation.
498
+ // The inline <script> block in admin.html (legacy) runs before deferred external scripts,
499
+ // so its renderInstructionList is captured here (if present). We intentionally DO NOT
500
+ // call the legacy renderer because it emits the old stacked meta layout. Instead we
501
+ // invoke our enhanced local renderInstructionList and optionally fall back to the legacy
502
+ // one only if an unexpected error occurs (defensive resilience).
503
+ const legacyRenderInstructionList = window.renderInstructionList;
504
+ window.renderInstructionList = function(list){
505
+ if (globals.instructionPage == null || Number.isNaN(globals.instructionPage)) globals.instructionPage = 1;
506
+ if (globals.instructionPageSize == null) globals.instructionPageSize = 25;
507
+ try {
508
+ renderInstructionList(list);
509
+ } catch(e){
510
+ try { legacyRenderInstructionList && legacyRenderInstructionList(list); } catch { /* ignore */ }
511
+ }
512
+ const filtered = getFilteredInstructions(list||[]);
513
+ if(filtered.length === 0) maybeTriggerGlobalFallback();
514
+ };
515
+
516
+ // If the legacy script already fetched instructions and populated window.allInstructions,
517
+ // force a re-render so the UI upgrades to the new chip styling without requiring user action.
518
+ try {
519
+ if(Array.isArray(window.allInstructions) && window.allInstructions.length){
520
+ setTimeout(()=>{ try { window.renderInstructionList(window.allInstructions); } catch { /* ignore */ } }, 0);
521
+ }
522
+ } catch { /* ignore */ }
523
+
524
+ // Expose key functions used by inline HTML event handlers (oninput/onclick) so they
525
+ // continue to work after the extraction + IIFE encapsulation.
526
+ try {
527
+ Object.assign(window, {
528
+ filterInstructions,
529
+ editInstruction,
530
+ deleteInstruction,
531
+ showCreateInstruction,
532
+ changeInstructionPage,
533
+ loadInstructions, // optional manual trigger
534
+ saveInstruction,
535
+ formatInstructionJson,
536
+ toggleInstructionDiff,
537
+ toggleInstructionPreview,
538
+ applyInstructionTemplate,
539
+ cancelEditInstruction,
540
+ updateInstructionEditorDiagnostics
541
+ });
542
+ } catch { /* ignore */ }
543
+
544
+ // Expose for manual trigger if needed
545
+ window.performGlobalInstructionSearch = performGlobalInstructionSearch;
546
+
547
+ // Hook after DOM ready if instructions section becomes active later
548
+ document.addEventListener('DOMContentLoaded', attachGlobalSearchHandlers);
549
+ // If script injected after DOMContentLoaded (defer), also call immediately
550
+ if(document.readyState === 'interactive' || document.readyState === 'complete') attachGlobalSearchHandlers();
551
+
552
+ })();