@loom-framework/core 0.1.0-alpha.9 → 0.1.0-alpha.91
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.
- package/builtin-skills/antd/SKILL.md +266 -0
- package/builtin-skills/antv-g2-chart/SKILL.md +1067 -0
- package/builtin-skills/antv-g2-chart/references/animations/g2-animation-intro.md +176 -0
- package/builtin-skills/antv-g2-chart/references/animations/g2-animation-keyframe.md +149 -0
- package/builtin-skills/antv-g2-chart/references/animations/g2-animation-types.md +253 -0
- package/builtin-skills/antv-g2-chart/references/components/g2-comp-annotation.md +237 -0
- package/builtin-skills/antv-g2-chart/references/components/g2-comp-axis-config.md +648 -0
- package/builtin-skills/antv-g2-chart/references/components/g2-comp-axis-radar.md +219 -0
- package/builtin-skills/antv-g2-chart/references/components/g2-comp-label-config.md +174 -0
- package/builtin-skills/antv-g2-chart/references/components/g2-comp-legend-category.md +278 -0
- package/builtin-skills/antv-g2-chart/references/components/g2-comp-legend-config.md +363 -0
- package/builtin-skills/antv-g2-chart/references/components/g2-comp-legend-continuous.md +264 -0
- package/builtin-skills/antv-g2-chart/references/components/g2-comp-scrollbar.md +276 -0
- package/builtin-skills/antv-g2-chart/references/components/g2-comp-slider.md +333 -0
- package/builtin-skills/antv-g2-chart/references/components/g2-comp-title.md +149 -0
- package/builtin-skills/antv-g2-chart/references/components/g2-comp-tooltip-config.md +358 -0
- package/builtin-skills/antv-g2-chart/references/compositions/g2-comp-facet-circle.md +97 -0
- package/builtin-skills/antv-g2-chart/references/compositions/g2-comp-facet-rect.md +241 -0
- package/builtin-skills/antv-g2-chart/references/compositions/g2-comp-geo-map.md +210 -0
- package/builtin-skills/antv-g2-chart/references/compositions/g2-comp-geoview.md +143 -0
- package/builtin-skills/antv-g2-chart/references/compositions/g2-comp-repeat-matrix.md +171 -0
- package/builtin-skills/antv-g2-chart/references/compositions/g2-comp-space-flex.md +142 -0
- package/builtin-skills/antv-g2-chart/references/compositions/g2-comp-space-layer.md +149 -0
- package/builtin-skills/antv-g2-chart/references/compositions/g2-comp-timing-keyframe.md +91 -0
- package/builtin-skills/antv-g2-chart/references/compositions/g2-comp-view.md +309 -0
- package/builtin-skills/antv-g2-chart/references/concepts/g2-concept-chart-selection.md +235 -0
- package/builtin-skills/antv-g2-chart/references/concepts/g2-concept-color-theory.md +287 -0
- package/builtin-skills/antv-g2-chart/references/concepts/g2-concept-visual-channels.md +180 -0
- package/builtin-skills/antv-g2-chart/references/coordinates/g2-coord-cartesian.md +131 -0
- package/builtin-skills/antv-g2-chart/references/coordinates/g2-coord-fisheye.md +131 -0
- package/builtin-skills/antv-g2-chart/references/coordinates/g2-coord-helix.md +141 -0
- package/builtin-skills/antv-g2-chart/references/coordinates/g2-coord-parallel.md +129 -0
- package/builtin-skills/antv-g2-chart/references/coordinates/g2-coord-polar.md +143 -0
- package/builtin-skills/antv-g2-chart/references/coordinates/g2-coord-radial.md +214 -0
- package/builtin-skills/antv-g2-chart/references/coordinates/g2-coord-theta.md +171 -0
- package/builtin-skills/antv-g2-chart/references/coordinates/g2-coord-transpose.md +195 -0
- package/builtin-skills/antv-g2-chart/references/core/g2-core-chart-init.md +266 -0
- package/builtin-skills/antv-g2-chart/references/core/g2-core-encode-channel.md +197 -0
- package/builtin-skills/antv-g2-chart/references/core/g2-core-view-composition.md +224 -0
- package/builtin-skills/antv-g2-chart/references/data/g2-data-ema.md +190 -0
- package/builtin-skills/antv-g2-chart/references/data/g2-data-fetch.md +167 -0
- package/builtin-skills/antv-g2-chart/references/data/g2-data-filter.md +212 -0
- package/builtin-skills/antv-g2-chart/references/data/g2-data-fold.md +260 -0
- package/builtin-skills/antv-g2-chart/references/data/g2-data-format-tabular.md +280 -0
- package/builtin-skills/antv-g2-chart/references/data/g2-data-kde.md +426 -0
- package/builtin-skills/antv-g2-chart/references/data/g2-data-log.md +140 -0
- package/builtin-skills/antv-g2-chart/references/data/g2-data-slice.md +147 -0
- package/builtin-skills/antv-g2-chart/references/data/g2-data-sort.md +251 -0
- package/builtin-skills/antv-g2-chart/references/data/g2-data-sortby.md +183 -0
- package/builtin-skills/antv-g2-chart/references/data/g2-data-transform-patterns.md +394 -0
- package/builtin-skills/antv-g2-chart/references/interactions/g2-interaction-adaptive-filter.md +118 -0
- package/builtin-skills/antv-g2-chart/references/interactions/g2-interaction-brush-axis.md +112 -0
- package/builtin-skills/antv-g2-chart/references/interactions/g2-interaction-brush-filter.md +122 -0
- package/builtin-skills/antv-g2-chart/references/interactions/g2-interaction-brush-x-y-highlight.md +127 -0
- package/builtin-skills/antv-g2-chart/references/interactions/g2-interaction-brush-xy.md +118 -0
- package/builtin-skills/antv-g2-chart/references/interactions/g2-interaction-brush.md +184 -0
- package/builtin-skills/antv-g2-chart/references/interactions/g2-interaction-brushx-filter.md +170 -0
- package/builtin-skills/antv-g2-chart/references/interactions/g2-interaction-brushx-highlight.md +186 -0
- package/builtin-skills/antv-g2-chart/references/interactions/g2-interaction-brushy-filter.md +170 -0
- package/builtin-skills/antv-g2-chart/references/interactions/g2-interaction-brushy-highlight.md +185 -0
- package/builtin-skills/antv-g2-chart/references/interactions/g2-interaction-chart-index.md +127 -0
- package/builtin-skills/antv-g2-chart/references/interactions/g2-interaction-drilldown.md +149 -0
- package/builtin-skills/antv-g2-chart/references/interactions/g2-interaction-element-highlight-by.md +125 -0
- package/builtin-skills/antv-g2-chart/references/interactions/g2-interaction-element-highlight.md +235 -0
- package/builtin-skills/antv-g2-chart/references/interactions/g2-interaction-element-hover-scale.md +109 -0
- package/builtin-skills/antv-g2-chart/references/interactions/g2-interaction-element-point-move.md +152 -0
- package/builtin-skills/antv-g2-chart/references/interactions/g2-interaction-element-select-by.md +133 -0
- package/builtin-skills/antv-g2-chart/references/interactions/g2-interaction-element-select.md +249 -0
- package/builtin-skills/antv-g2-chart/references/interactions/g2-interaction-fisheye.md +117 -0
- package/builtin-skills/antv-g2-chart/references/interactions/g2-interaction-legend-filter.md +130 -0
- package/builtin-skills/antv-g2-chart/references/interactions/g2-interaction-legend-highlight.md +130 -0
- package/builtin-skills/antv-g2-chart/references/interactions/g2-interaction-poptip.md +105 -0
- package/builtin-skills/antv-g2-chart/references/interactions/g2-interaction-scrollbar-filter.md +144 -0
- package/builtin-skills/antv-g2-chart/references/interactions/g2-interaction-slider-filter.md +231 -0
- package/builtin-skills/antv-g2-chart/references/interactions/g2-interaction-slider-wheel.md +138 -0
- package/builtin-skills/antv-g2-chart/references/interactions/g2-interaction-tooltip.md +192 -0
- package/builtin-skills/antv-g2-chart/references/interactions/g2-interaction-treemap-drilldown.md +152 -0
- package/builtin-skills/antv-g2-chart/references/label-transform/g2-label-transform-contrast-reverse.md +170 -0
- package/builtin-skills/antv-g2-chart/references/label-transform/g2-label-transform-exceed-adjust.md +146 -0
- package/builtin-skills/antv-g2-chart/references/label-transform/g2-label-transform-overflow-hide.md +185 -0
- package/builtin-skills/antv-g2-chart/references/label-transform/g2-label-transform-overflow-stroke.md +147 -0
- package/builtin-skills/antv-g2-chart/references/label-transform/g2-label-transform-overlap-dodge-y.md +225 -0
- package/builtin-skills/antv-g2-chart/references/label-transform/g2-label-transform-overlap-hide.md +213 -0
- package/builtin-skills/antv-g2-chart/references/marks/g2-mark-arc-diagram.md +196 -0
- package/builtin-skills/antv-g2-chart/references/marks/g2-mark-arc-donut.md +148 -0
- package/builtin-skills/antv-g2-chart/references/marks/g2-mark-arc-pie.md +212 -0
- package/builtin-skills/antv-g2-chart/references/marks/g2-mark-area-basic.md +168 -0
- package/builtin-skills/antv-g2-chart/references/marks/g2-mark-area-stacked.md +150 -0
- package/builtin-skills/antv-g2-chart/references/marks/g2-mark-beeswarm.md +134 -0
- package/builtin-skills/antv-g2-chart/references/marks/g2-mark-bi-directional-bar.md +297 -0
- package/builtin-skills/antv-g2-chart/references/marks/g2-mark-box-boxplot.md +163 -0
- package/builtin-skills/antv-g2-chart/references/marks/g2-mark-boxplot.md +340 -0
- package/builtin-skills/antv-g2-chart/references/marks/g2-mark-bullet.md +203 -0
- package/builtin-skills/antv-g2-chart/references/marks/g2-mark-cell-heatmap.md +370 -0
- package/builtin-skills/antv-g2-chart/references/marks/g2-mark-chord.md +341 -0
- package/builtin-skills/antv-g2-chart/references/marks/g2-mark-connector.md +126 -0
- package/builtin-skills/antv-g2-chart/references/marks/g2-mark-contourline.md +243 -0
- package/builtin-skills/antv-g2-chart/references/marks/g2-mark-density.md +457 -0
- package/builtin-skills/antv-g2-chart/references/marks/g2-mark-distribution-curve.md +298 -0
- package/builtin-skills/antv-g2-chart/references/marks/g2-mark-funnel.md +329 -0
- package/builtin-skills/antv-g2-chart/references/marks/g2-mark-gantt.md +208 -0
- package/builtin-skills/antv-g2-chart/references/marks/g2-mark-gauge.md +247 -0
- package/builtin-skills/antv-g2-chart/references/marks/g2-mark-heatmap.md +130 -0
- package/builtin-skills/antv-g2-chart/references/marks/g2-mark-histogram.md +186 -0
- package/builtin-skills/antv-g2-chart/references/marks/g2-mark-image.md +129 -0
- package/builtin-skills/antv-g2-chart/references/marks/g2-mark-interval-basic.md +528 -0
- package/builtin-skills/antv-g2-chart/references/marks/g2-mark-interval-grouped.md +161 -0
- package/builtin-skills/antv-g2-chart/references/marks/g2-mark-interval-normalized.md +147 -0
- package/builtin-skills/antv-g2-chart/references/marks/g2-mark-interval-stacked.md +183 -0
- package/builtin-skills/antv-g2-chart/references/marks/g2-mark-k-chart.md +237 -0
- package/builtin-skills/antv-g2-chart/references/marks/g2-mark-line-basic.md +353 -0
- package/builtin-skills/antv-g2-chart/references/marks/g2-mark-line-multi.md +156 -0
- package/builtin-skills/antv-g2-chart/references/marks/g2-mark-linex-liney.md +176 -0
- package/builtin-skills/antv-g2-chart/references/marks/g2-mark-link.md +134 -0
- package/builtin-skills/antv-g2-chart/references/marks/g2-mark-liquid.md +125 -0
- package/builtin-skills/antv-g2-chart/references/marks/g2-mark-mosaic.md +257 -0
- package/builtin-skills/antv-g2-chart/references/marks/g2-mark-pack.md +424 -0
- package/builtin-skills/antv-g2-chart/references/marks/g2-mark-parallel.md +195 -0
- package/builtin-skills/antv-g2-chart/references/marks/g2-mark-partition.md +275 -0
- package/builtin-skills/antv-g2-chart/references/marks/g2-mark-path.md +135 -0
- package/builtin-skills/antv-g2-chart/references/marks/g2-mark-point-bubble.md +138 -0
- package/builtin-skills/antv-g2-chart/references/marks/g2-mark-point-scatter.md +177 -0
- package/builtin-skills/antv-g2-chart/references/marks/g2-mark-polygon.md +130 -0
- package/builtin-skills/antv-g2-chart/references/marks/g2-mark-radar.md +231 -0
- package/builtin-skills/antv-g2-chart/references/marks/g2-mark-radial-bar.md +226 -0
- package/builtin-skills/antv-g2-chart/references/marks/g2-mark-range-rangey.md +253 -0
- package/builtin-skills/antv-g2-chart/references/marks/g2-mark-rangex.md +293 -0
- package/builtin-skills/antv-g2-chart/references/marks/g2-mark-rect.md +115 -0
- package/builtin-skills/antv-g2-chart/references/marks/g2-mark-regression-curve.md +276 -0
- package/builtin-skills/antv-g2-chart/references/marks/g2-mark-rose.md +182 -0
- package/builtin-skills/antv-g2-chart/references/marks/g2-mark-sankey.md +217 -0
- package/builtin-skills/antv-g2-chart/references/marks/g2-mark-shape.md +160 -0
- package/builtin-skills/antv-g2-chart/references/marks/g2-mark-spiral.md +257 -0
- package/builtin-skills/antv-g2-chart/references/marks/g2-mark-sunburst.md +327 -0
- package/builtin-skills/antv-g2-chart/references/marks/g2-mark-text.md +309 -0
- package/builtin-skills/antv-g2-chart/references/marks/g2-mark-tree.md +235 -0
- package/builtin-skills/antv-g2-chart/references/marks/g2-mark-treemap.md +426 -0
- package/builtin-skills/antv-g2-chart/references/marks/g2-mark-vector.md +108 -0
- package/builtin-skills/antv-g2-chart/references/marks/g2-mark-venn.md +204 -0
- package/builtin-skills/antv-g2-chart/references/marks/g2-mark-violin.md +307 -0
- package/builtin-skills/antv-g2-chart/references/marks/g2-mark-wordcloud.md +155 -0
- package/builtin-skills/antv-g2-chart/references/palette/g2-palette-category10.md +196 -0
- package/builtin-skills/antv-g2-chart/references/palette/g2-palette-category20.md +238 -0
- package/builtin-skills/antv-g2-chart/references/patterns/g2-pattern-performance.md +181 -0
- package/builtin-skills/antv-g2-chart/references/patterns/g2-pattern-responsive.md +232 -0
- package/builtin-skills/antv-g2-chart/references/patterns/g2-pattern-v4-to-v5.md +223 -0
- package/builtin-skills/antv-g2-chart/references/scales/g2-scale-band.md +127 -0
- package/builtin-skills/antv-g2-chart/references/scales/g2-scale-linear.md +259 -0
- package/builtin-skills/antv-g2-chart/references/scales/g2-scale-log.md +226 -0
- package/builtin-skills/antv-g2-chart/references/scales/g2-scale-ordinal.md +135 -0
- package/builtin-skills/antv-g2-chart/references/scales/g2-scale-point.md +242 -0
- package/builtin-skills/antv-g2-chart/references/scales/g2-scale-pow-sqrt.md +124 -0
- package/builtin-skills/antv-g2-chart/references/scales/g2-scale-quantile-quantize.md +119 -0
- package/builtin-skills/antv-g2-chart/references/scales/g2-scale-sequential.md +267 -0
- package/builtin-skills/antv-g2-chart/references/scales/g2-scale-threshold.md +133 -0
- package/builtin-skills/antv-g2-chart/references/scales/g2-scale-time.md +159 -0
- package/builtin-skills/antv-g2-chart/references/themes/g2-theme-builtin.md +192 -0
- package/builtin-skills/antv-g2-chart/references/themes/g2-theme-custom.md +183 -0
- package/builtin-skills/antv-g2-chart/references/transforms/g2-transform-bin.md +153 -0
- package/builtin-skills/antv-g2-chart/references/transforms/g2-transform-binx.md +133 -0
- package/builtin-skills/antv-g2-chart/references/transforms/g2-transform-diffy.md +130 -0
- package/builtin-skills/antv-g2-chart/references/transforms/g2-transform-dodgex.md +90 -0
- package/builtin-skills/antv-g2-chart/references/transforms/g2-transform-flexx.md +115 -0
- package/builtin-skills/antv-g2-chart/references/transforms/g2-transform-group.md +233 -0
- package/builtin-skills/antv-g2-chart/references/transforms/g2-transform-groupcolor.md +182 -0
- package/builtin-skills/antv-g2-chart/references/transforms/g2-transform-groupx.md +265 -0
- package/builtin-skills/antv-g2-chart/references/transforms/g2-transform-groupy.md +167 -0
- package/builtin-skills/antv-g2-chart/references/transforms/g2-transform-jitter.md +149 -0
- package/builtin-skills/antv-g2-chart/references/transforms/g2-transform-jitterx.md +177 -0
- package/builtin-skills/antv-g2-chart/references/transforms/g2-transform-jittery.md +177 -0
- package/builtin-skills/antv-g2-chart/references/transforms/g2-transform-normalizey.md +83 -0
- package/builtin-skills/antv-g2-chart/references/transforms/g2-transform-pack.md +164 -0
- package/builtin-skills/antv-g2-chart/references/transforms/g2-transform-sample.md +143 -0
- package/builtin-skills/antv-g2-chart/references/transforms/g2-transform-select.md +147 -0
- package/builtin-skills/antv-g2-chart/references/transforms/g2-transform-selectx.md +179 -0
- package/builtin-skills/antv-g2-chart/references/transforms/g2-transform-selecty.md +179 -0
- package/builtin-skills/antv-g2-chart/references/transforms/g2-transform-sort-color.md +115 -0
- package/builtin-skills/antv-g2-chart/references/transforms/g2-transform-sortx.md +285 -0
- package/builtin-skills/antv-g2-chart/references/transforms/g2-transform-sorty.md +117 -0
- package/builtin-skills/antv-g2-chart/references/transforms/g2-transform-stack-enter.md +158 -0
- package/builtin-skills/antv-g2-chart/references/transforms/g2-transform-stacky.md +218 -0
- package/builtin-skills/antv-g2-chart/references/transforms/g2-transform-symmetryy.md +122 -0
- package/builtin-skills/app-skill/SKILL.md +27 -0
- package/builtin-skills/app-skill/references/data-semantics.md +44 -0
- package/builtin-skills/app-skill/references/models.md +31 -0
- package/builtin-skills/chart-visualization/SKILL.md +101 -0
- package/builtin-skills/loom/SKILL.md +168 -0
- package/builtin-skills/loom/references/README.md +128 -0
- package/builtin-skills/loom/references/dashboard.md +196 -0
- package/builtin-skills/loom/references/data-model.md +78 -0
- package/dist/adapter-base.d.ts +29 -0
- package/dist/adapter-base.d.ts.map +1 -0
- package/dist/adapter-base.js +62 -0
- package/dist/adapter-base.js.map +1 -0
- package/dist/adapter-factory.d.ts +8 -0
- package/dist/adapter-factory.d.ts.map +1 -0
- package/dist/adapter-factory.js +25 -0
- package/dist/adapter-factory.js.map +1 -0
- package/dist/adapter-filesystem.d.ts +6 -11
- package/dist/adapter-filesystem.d.ts.map +1 -1
- package/dist/adapter-filesystem.js +56 -41
- package/dist/adapter-filesystem.js.map +1 -1
- package/dist/adapter-sqlite.d.ts +6 -23
- package/dist/adapter-sqlite.d.ts.map +1 -1
- package/dist/adapter-sqlite.js +65 -50
- package/dist/adapter-sqlite.js.map +1 -1
- package/dist/backend/ai/button-resolver.d.ts +18 -0
- package/dist/backend/ai/button-resolver.d.ts.map +1 -0
- package/dist/backend/ai/button-resolver.js +58 -0
- package/dist/backend/ai/button-resolver.js.map +1 -0
- package/dist/backend/ai/engine.d.ts +52 -0
- package/dist/backend/ai/engine.d.ts.map +1 -0
- package/dist/backend/ai/engine.js +186 -0
- package/dist/backend/ai/engine.js.map +1 -0
- package/dist/backend/ai/index.d.ts +11 -0
- package/dist/backend/ai/index.d.ts.map +1 -0
- package/dist/backend/ai/index.js +8 -0
- package/dist/backend/ai/index.js.map +1 -0
- package/dist/backend/ai/output-parser.d.ts +29 -0
- package/dist/backend/ai/output-parser.d.ts.map +1 -0
- package/dist/backend/ai/output-parser.js +247 -0
- package/dist/backend/ai/output-parser.js.map +1 -0
- package/dist/backend/ai/session-manager.d.ts +103 -0
- package/dist/backend/ai/session-manager.d.ts.map +1 -0
- package/dist/backend/ai/session-manager.js +298 -0
- package/dist/backend/ai/session-manager.js.map +1 -0
- package/dist/backend/index.d.ts +61 -0
- package/dist/backend/index.d.ts.map +1 -0
- package/dist/backend/index.js +161 -0
- package/dist/backend/index.js.map +1 -0
- package/dist/backend/observe/index.d.ts +6 -0
- package/dist/backend/observe/index.d.ts.map +1 -0
- package/dist/backend/observe/index.js +5 -0
- package/dist/backend/observe/index.js.map +1 -0
- package/dist/backend/observe/logger.d.ts +28 -0
- package/dist/backend/observe/logger.d.ts.map +1 -0
- package/dist/backend/observe/logger.js +80 -0
- package/dist/backend/observe/logger.js.map +1 -0
- package/dist/backend/observe/types.d.ts +26 -0
- package/dist/backend/observe/types.d.ts.map +1 -0
- package/dist/backend/observe/types.js +7 -0
- package/dist/backend/observe/types.js.map +1 -0
- package/dist/backend/routes/chat.d.ts +31 -0
- package/dist/backend/routes/chat.d.ts.map +1 -0
- package/dist/backend/routes/chat.js +426 -0
- package/dist/backend/routes/chat.js.map +1 -0
- package/dist/backend/routes/data.d.ts +13 -0
- package/dist/backend/routes/data.d.ts.map +1 -0
- package/dist/backend/routes/data.js +134 -0
- package/dist/backend/routes/data.js.map +1 -0
- package/dist/backend/routes/health.d.ts +7 -0
- package/dist/backend/routes/health.d.ts.map +1 -0
- package/dist/backend/routes/health.js +15 -0
- package/dist/backend/routes/health.js.map +1 -0
- package/dist/backend/routes/index.d.ts +11 -0
- package/dist/backend/routes/index.d.ts.map +1 -0
- package/dist/backend/routes/index.js +9 -0
- package/dist/backend/routes/index.js.map +1 -0
- package/dist/backend/routes/skills.d.ts +16 -0
- package/dist/backend/routes/skills.d.ts.map +1 -0
- package/dist/backend/routes/skills.js +590 -0
- package/dist/backend/routes/skills.js.map +1 -0
- package/dist/backend/routes/upload.d.ts +24 -0
- package/dist/backend/routes/upload.d.ts.map +1 -0
- package/dist/backend/routes/upload.js +67 -0
- package/dist/backend/routes/upload.js.map +1 -0
- package/dist/bin.d.ts +8 -0
- package/dist/bin.d.ts.map +1 -0
- package/dist/bin.js +12 -0
- package/dist/bin.js.map +1 -0
- package/dist/capability-generator.d.ts +21 -6
- package/dist/capability-generator.d.ts.map +1 -1
- package/dist/capability-generator.js +88 -261
- package/dist/capability-generator.js.map +1 -1
- package/dist/cli/commands/build.d.ts +11 -0
- package/dist/cli/commands/build.d.ts.map +1 -0
- package/dist/cli/commands/build.js +170 -0
- package/dist/cli/commands/build.js.map +1 -0
- package/dist/cli/commands/data.d.ts +12 -0
- package/dist/cli/commands/data.d.ts.map +1 -0
- package/dist/cli/commands/data.js +158 -0
- package/dist/cli/commands/data.js.map +1 -0
- package/dist/cli/commands/dev.d.ts +9 -0
- package/dist/cli/commands/dev.d.ts.map +1 -0
- package/dist/cli/commands/dev.js +114 -0
- package/dist/cli/commands/dev.js.map +1 -0
- package/dist/cli/commands/generate-capabilities.d.ts +8 -0
- package/dist/cli/commands/generate-capabilities.d.ts.map +1 -0
- package/dist/cli/commands/generate-capabilities.js +40 -0
- package/dist/cli/commands/generate-capabilities.js.map +1 -0
- package/dist/cli/commands/generate-cli-command.d.ts +8 -0
- package/dist/cli/commands/generate-cli-command.d.ts.map +1 -0
- package/dist/cli/commands/generate-cli-command.js +64 -0
- package/dist/cli/commands/generate-cli-command.js.map +1 -0
- package/dist/cli/commands/generate-dashboard.d.ts +9 -0
- package/dist/cli/commands/generate-dashboard.d.ts.map +1 -0
- package/dist/cli/commands/generate-dashboard.js +466 -0
- package/dist/cli/commands/generate-dashboard.js.map +1 -0
- package/dist/cli/commands/generate-page.d.ts +9 -0
- package/dist/cli/commands/generate-page.d.ts.map +1 -0
- package/dist/cli/commands/generate-page.js +352 -0
- package/dist/cli/commands/generate-page.js.map +1 -0
- package/dist/cli/commands/generate-skill.d.ts +8 -0
- package/dist/cli/commands/generate-skill.d.ts.map +1 -0
- package/dist/cli/commands/generate-skill.js +75 -0
- package/dist/cli/commands/generate-skill.js.map +1 -0
- package/dist/cli/commands/generate.d.ts +6 -0
- package/dist/cli/commands/generate.d.ts.map +1 -0
- package/dist/cli/commands/generate.js +19 -0
- package/dist/cli/commands/generate.js.map +1 -0
- package/dist/cli/commands/init.d.ts +8 -0
- package/dist/cli/commands/init.d.ts.map +1 -0
- package/dist/cli/commands/init.js +301 -0
- package/dist/cli/commands/init.js.map +1 -0
- package/dist/cli/commands/observe.d.ts +9 -0
- package/dist/cli/commands/observe.d.ts.map +1 -0
- package/dist/cli/commands/observe.js +142 -0
- package/dist/cli/commands/observe.js.map +1 -0
- package/dist/cli/commands/skill.d.ts +9 -0
- package/dist/cli/commands/skill.d.ts.map +1 -0
- package/dist/cli/commands/skill.js +186 -0
- package/dist/cli/commands/skill.js.map +1 -0
- package/dist/cli/generators/capability-generator.d.ts +35 -0
- package/dist/cli/generators/capability-generator.d.ts.map +1 -0
- package/dist/cli/generators/capability-generator.js +115 -0
- package/dist/cli/generators/capability-generator.js.map +1 -0
- package/dist/cli/helpers/app-tsx-wiring.d.ts +19 -0
- package/dist/cli/helpers/app-tsx-wiring.d.ts.map +1 -0
- package/dist/cli/helpers/app-tsx-wiring.js +143 -0
- package/dist/cli/helpers/app-tsx-wiring.js.map +1 -0
- package/dist/cli/helpers/column-template.d.ts +18 -0
- package/dist/cli/helpers/column-template.d.ts.map +1 -0
- package/dist/cli/helpers/column-template.js +151 -0
- package/dist/cli/helpers/column-template.js.map +1 -0
- package/dist/cli/helpers/duration.d.ts +5 -0
- package/dist/cli/helpers/duration.d.ts.map +1 -0
- package/dist/cli/helpers/duration.js +19 -0
- package/dist/cli/helpers/duration.js.map +1 -0
- package/dist/cli/helpers/field-template.d.ts +14 -0
- package/dist/cli/helpers/field-template.d.ts.map +1 -0
- package/dist/cli/helpers/field-template.js +101 -0
- package/dist/cli/helpers/field-template.js.map +1 -0
- package/dist/cli/helpers/naming.d.ts +12 -0
- package/dist/cli/helpers/naming.d.ts.map +1 -0
- package/dist/cli/helpers/naming.js +25 -0
- package/dist/cli/helpers/naming.js.map +1 -0
- package/dist/cli/index.d.ts +9 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +33 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/templates/backend-entry.d.ts +5 -0
- package/dist/cli/templates/backend-entry.d.ts.map +1 -0
- package/dist/cli/templates/backend-entry.js +17 -0
- package/dist/cli/templates/backend-entry.js.map +1 -0
- package/dist/cli/templates/frontend-entry.d.ts +11 -0
- package/dist/cli/templates/frontend-entry.d.ts.map +1 -0
- package/dist/cli/templates/frontend-entry.js +70 -0
- package/dist/cli/templates/frontend-entry.js.map +1 -0
- package/dist/cli/templates/gitignore.d.ts +5 -0
- package/dist/cli/templates/gitignore.d.ts.map +1 -0
- package/dist/cli/templates/gitignore.js +13 -0
- package/dist/cli/templates/gitignore.js.map +1 -0
- package/dist/cli/templates/index.d.ts +12 -0
- package/dist/cli/templates/index.d.ts.map +1 -0
- package/dist/cli/templates/index.js +12 -0
- package/dist/cli/templates/index.js.map +1 -0
- package/dist/cli/templates/loom-config.d.ts +10 -0
- package/dist/cli/templates/loom-config.d.ts.map +1 -0
- package/dist/cli/templates/loom-config.js +31 -0
- package/dist/cli/templates/loom-config.js.map +1 -0
- package/dist/cli/templates/package-json.d.ts +9 -0
- package/dist/cli/templates/package-json.d.ts.map +1 -0
- package/dist/cli/templates/package-json.js +40 -0
- package/dist/cli/templates/package-json.js.map +1 -0
- package/dist/cli/templates/readme.d.ts +9 -0
- package/dist/cli/templates/readme.d.ts.map +1 -0
- package/dist/cli/templates/readme.js +47 -0
- package/dist/cli/templates/readme.js.map +1 -0
- package/dist/cli/templates/tsconfig.d.ts +6 -0
- package/dist/cli/templates/tsconfig.d.ts.map +1 -0
- package/dist/cli/templates/tsconfig.js +41 -0
- package/dist/cli/templates/tsconfig.js.map +1 -0
- package/dist/cli/templates/vite-config.d.ts +5 -0
- package/dist/cli/templates/vite-config.d.ts.map +1 -0
- package/dist/cli/templates/vite-config.js +24 -0
- package/dist/cli/templates/vite-config.js.map +1 -0
- package/dist/cli/utils.d.ts +10 -0
- package/dist/cli/utils.d.ts.map +1 -0
- package/dist/cli/utils.js +31 -0
- package/dist/cli/utils.js.map +1 -0
- package/dist/commands.d.ts +1 -5
- package/dist/commands.d.ts.map +1 -1
- package/dist/commands.js +0 -11
- package/dist/commands.js.map +1 -1
- package/dist/config.d.ts +21 -52
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +8 -9
- package/dist/config.js.map +1 -1
- package/dist/dashboard-config.d.ts +117 -0
- package/dist/dashboard-config.d.ts.map +1 -0
- package/dist/dashboard-config.js +59 -0
- package/dist/dashboard-config.js.map +1 -0
- package/dist/index.d.ts +9 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -2
- package/dist/index.js.map +1 -1
- package/dist/server-bin.d.ts +12 -0
- package/dist/server-bin.d.ts.map +1 -0
- package/dist/server-bin.js +43 -0
- package/dist/server-bin.js.map +1 -0
- package/dist/types/adapter.d.ts +28 -0
- package/dist/types/adapter.d.ts.map +1 -0
- package/dist/types/adapter.js +5 -0
- package/dist/types/adapter.js.map +1 -0
- package/dist/types/ai.d.ts +52 -0
- package/dist/types/ai.d.ts.map +1 -0
- package/dist/types/ai.js +5 -0
- package/dist/types/ai.js.map +1 -0
- package/dist/types/chat.d.ts +46 -0
- package/dist/types/chat.d.ts.map +1 -0
- package/dist/types/chat.js +5 -0
- package/dist/types/chat.js.map +1 -0
- package/dist/types/command.d.ts +25 -0
- package/dist/types/command.d.ts.map +1 -0
- package/dist/types/command.js +5 -0
- package/dist/types/command.js.map +1 -0
- package/dist/types/config.d.ts +30 -0
- package/dist/types/config.d.ts.map +1 -0
- package/dist/types/config.js +5 -0
- package/dist/types/config.js.map +1 -0
- package/dist/types/dashboard.d.ts +42 -0
- package/dist/types/dashboard.d.ts.map +1 -0
- package/dist/types/dashboard.js +5 -0
- package/dist/types/dashboard.js.map +1 -0
- package/dist/types/index.d.ts +14 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +8 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/model.d.ts +59 -0
- package/dist/types/model.d.ts.map +1 -0
- package/dist/types/model.js +5 -0
- package/dist/types/model.js.map +1 -0
- package/dist/types.d.ts +4 -199
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +4 -3
- package/dist/types.js.map +1 -1
- package/package.json +18 -3
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Claude Code Output Parser
|
|
3
|
+
*
|
|
4
|
+
* Parses `claude -p --output-format stream-json --verbose --include-partial-messages` output stream.
|
|
5
|
+
* Supports real-time streaming with thinking, tool calls, and text deltas.
|
|
6
|
+
*
|
|
7
|
+
* Key insight: Tool input comes via input_json_delta events, need to accumulate them.
|
|
8
|
+
*/
|
|
9
|
+
/** ANSI escape code pattern */
|
|
10
|
+
const ANSI_ESCAPE = /\x1b\[[0-9;]*m/g;
|
|
11
|
+
/** Check if a parsed toolInput is empty (no meaningful content) */
|
|
12
|
+
function isEmptyToolInput(input) {
|
|
13
|
+
if (input === undefined || input === null)
|
|
14
|
+
return true;
|
|
15
|
+
if (typeof input === 'object' && Object.keys(input).length === 0)
|
|
16
|
+
return true;
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Parse Claude Code stdout stream into typed AIChunk objects
|
|
21
|
+
*
|
|
22
|
+
* Strategy:
|
|
23
|
+
* 1. Process lines as they arrive (true streaming)
|
|
24
|
+
* 2. Strip ANSI escape codes
|
|
25
|
+
* 3. Parse each line as JSON
|
|
26
|
+
* 4. Map to appropriate AIChunk types
|
|
27
|
+
* 5. Accumulate tool input from input_json_delta events
|
|
28
|
+
*/
|
|
29
|
+
export async function* parseClaudeOutput(stdout, logger) {
|
|
30
|
+
let buffer = '';
|
|
31
|
+
const state = {
|
|
32
|
+
toolInputBuffers: new Map(),
|
|
33
|
+
activeToolId: null,
|
|
34
|
+
activeToolName: null,
|
|
35
|
+
emittedToolIds: new Set(),
|
|
36
|
+
};
|
|
37
|
+
for await (const chunk of stdout) {
|
|
38
|
+
buffer += chunk.toString();
|
|
39
|
+
// Process complete lines
|
|
40
|
+
const lines = buffer.split('\n');
|
|
41
|
+
buffer = lines.pop() || '';
|
|
42
|
+
for (const line of lines) {
|
|
43
|
+
const trimmed = line.trim();
|
|
44
|
+
if (!trimmed)
|
|
45
|
+
continue;
|
|
46
|
+
const chunks = parseLine(trimmed, logger, state);
|
|
47
|
+
for (const c of chunks) {
|
|
48
|
+
yield c;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
// Process any remaining buffer
|
|
53
|
+
if (buffer.trim()) {
|
|
54
|
+
const cleanOutput = buffer.replace(ANSI_ESCAPE, '').trim();
|
|
55
|
+
const chunks = parseLine(cleanOutput, logger, state);
|
|
56
|
+
for (const c of chunks)
|
|
57
|
+
yield c;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
function parseLine(line, logger, state) {
|
|
61
|
+
try {
|
|
62
|
+
const data = JSON.parse(line);
|
|
63
|
+
logger?.({ type: 'LINE_PARSED', timestamp: new Date().toISOString(), message: `Parsed ${data.type}` });
|
|
64
|
+
return mapToChunks(data, state);
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
logger?.({ type: 'PARSE_ERROR', timestamp: new Date().toISOString(), message: 'Failed to parse line', data: line.slice(0, 200) });
|
|
68
|
+
return [];
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
function mapToChunks(data, state) {
|
|
72
|
+
switch (data.type) {
|
|
73
|
+
case 'system':
|
|
74
|
+
if (data.subtype === 'init' && data.session_id) {
|
|
75
|
+
return [{ type: 'session_info', sessionId: data.session_id }];
|
|
76
|
+
}
|
|
77
|
+
return [];
|
|
78
|
+
case 'stream_event':
|
|
79
|
+
return handleStreamEvent(data, state);
|
|
80
|
+
case 'assistant':
|
|
81
|
+
// Process assistant message - it contains complete tool_use with full input
|
|
82
|
+
return handleAssistantMessage(data, state);
|
|
83
|
+
case 'user':
|
|
84
|
+
return handleUserMessage(data);
|
|
85
|
+
case 'result':
|
|
86
|
+
return handleResult(data);
|
|
87
|
+
default:
|
|
88
|
+
return [];
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
function handleStreamEvent(wrapper, state) {
|
|
92
|
+
const { event } = wrapper;
|
|
93
|
+
switch (event.type) {
|
|
94
|
+
case 'content_block_delta':
|
|
95
|
+
// Handle thinking and text deltas
|
|
96
|
+
if (event.delta?.type === 'thinking_delta' && event.delta.thinking) {
|
|
97
|
+
return [{ type: 'thinking', content: event.delta.thinking }];
|
|
98
|
+
}
|
|
99
|
+
if (event.delta?.type === 'text_delta' && event.delta.text) {
|
|
100
|
+
return [{ type: 'content', content: event.delta.text }];
|
|
101
|
+
}
|
|
102
|
+
// Handle tool input JSON delta - accumulate it
|
|
103
|
+
if (event.delta?.type === 'input_json_delta' && event.delta.partial_json) {
|
|
104
|
+
if (state.activeToolId) {
|
|
105
|
+
const current = state.toolInputBuffers.get(state.activeToolId) || '';
|
|
106
|
+
state.toolInputBuffers.set(state.activeToolId, current + event.delta.partial_json);
|
|
107
|
+
}
|
|
108
|
+
return []; // Don't emit until tool call is complete
|
|
109
|
+
}
|
|
110
|
+
return [];
|
|
111
|
+
case 'content_block_start':
|
|
112
|
+
if (event.content_block?.type === 'tool_use') {
|
|
113
|
+
const toolUseId = event.content_block.id || '';
|
|
114
|
+
const toolName = event.content_block.name || 'unknown';
|
|
115
|
+
// Set active tool for accumulation
|
|
116
|
+
state.activeToolId = toolUseId;
|
|
117
|
+
state.activeToolName = toolName;
|
|
118
|
+
// Initialize buffer if there's already input
|
|
119
|
+
if (event.content_block.input !== undefined) {
|
|
120
|
+
state.toolInputBuffers.set(toolUseId, JSON.stringify(event.content_block.input));
|
|
121
|
+
}
|
|
122
|
+
else {
|
|
123
|
+
state.toolInputBuffers.set(toolUseId, '');
|
|
124
|
+
}
|
|
125
|
+
// Don't emit tool_call yet - wait for content_block_stop with complete input
|
|
126
|
+
return [];
|
|
127
|
+
}
|
|
128
|
+
return [];
|
|
129
|
+
case 'content_block_stop':
|
|
130
|
+
// When content block stops, emit the complete tool input if we have one
|
|
131
|
+
if (state.activeToolId && event.index !== undefined) {
|
|
132
|
+
const accumulatedInput = state.toolInputBuffers.get(state.activeToolId);
|
|
133
|
+
if (accumulatedInput && state.activeToolName) {
|
|
134
|
+
try {
|
|
135
|
+
const parsedInput = JSON.parse(accumulatedInput);
|
|
136
|
+
// Skip empty tool inputs (abandoned tool calls from Claude)
|
|
137
|
+
if (isEmptyToolInput(parsedInput)) {
|
|
138
|
+
state.activeToolId = null;
|
|
139
|
+
state.activeToolName = null;
|
|
140
|
+
return [];
|
|
141
|
+
}
|
|
142
|
+
const chunks = [{
|
|
143
|
+
type: 'tool_call',
|
|
144
|
+
toolUseId: state.activeToolId,
|
|
145
|
+
toolName: state.activeToolName,
|
|
146
|
+
toolInput: parsedInput,
|
|
147
|
+
}];
|
|
148
|
+
state.emittedToolIds.add(state.activeToolId);
|
|
149
|
+
state.activeToolId = null;
|
|
150
|
+
state.activeToolName = null;
|
|
151
|
+
return chunks;
|
|
152
|
+
}
|
|
153
|
+
catch {
|
|
154
|
+
// JSON not complete yet, keep state
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
state.activeToolId = null;
|
|
158
|
+
state.activeToolName = null;
|
|
159
|
+
}
|
|
160
|
+
return [];
|
|
161
|
+
default:
|
|
162
|
+
return [];
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
function handleAssistantMessage(data, state) {
|
|
166
|
+
const chunks = [];
|
|
167
|
+
for (const block of data.message.content || []) {
|
|
168
|
+
if (block.type === 'thinking' && block.thinking) {
|
|
169
|
+
// Skip - already sent via thinking_delta
|
|
170
|
+
}
|
|
171
|
+
if (block.type === 'text' && block.text) {
|
|
172
|
+
// Skip - already sent via text_delta
|
|
173
|
+
}
|
|
174
|
+
if (block.type === 'tool_use') {
|
|
175
|
+
// Skip already-emitted tool calls (from content_block_stop)
|
|
176
|
+
const toolUseId = block.id || '';
|
|
177
|
+
if (state.emittedToolIds.has(toolUseId))
|
|
178
|
+
continue;
|
|
179
|
+
// Use the accumulated input if we have it, otherwise use the block's input
|
|
180
|
+
const accumulatedInput = state.toolInputBuffers.get(toolUseId);
|
|
181
|
+
let toolInput;
|
|
182
|
+
if (accumulatedInput) {
|
|
183
|
+
try {
|
|
184
|
+
toolInput = JSON.parse(accumulatedInput);
|
|
185
|
+
}
|
|
186
|
+
catch {
|
|
187
|
+
toolInput = block.input;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
else {
|
|
191
|
+
toolInput = block.input;
|
|
192
|
+
}
|
|
193
|
+
// Skip empty tool inputs (abandoned tool calls from Claude)
|
|
194
|
+
if (isEmptyToolInput(toolInput))
|
|
195
|
+
continue;
|
|
196
|
+
chunks.push({
|
|
197
|
+
type: 'tool_call',
|
|
198
|
+
toolUseId,
|
|
199
|
+
toolName: block.name || 'unknown',
|
|
200
|
+
toolInput,
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
return chunks;
|
|
205
|
+
}
|
|
206
|
+
function handleUserMessage(data) {
|
|
207
|
+
const chunks = [];
|
|
208
|
+
for (const block of data.message.content) {
|
|
209
|
+
if (block.type === 'tool_result') {
|
|
210
|
+
chunks.push({
|
|
211
|
+
type: 'tool_result',
|
|
212
|
+
toolUseId: block.tool_use_id,
|
|
213
|
+
toolResult: block.content,
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
return chunks;
|
|
218
|
+
}
|
|
219
|
+
function handleResult(data) {
|
|
220
|
+
const chunks = [];
|
|
221
|
+
if (data.session_id) {
|
|
222
|
+
chunks.push({
|
|
223
|
+
type: 'session_info',
|
|
224
|
+
sessionId: data.session_id,
|
|
225
|
+
usage: extractStreamUsage(data.usage),
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
if (data.is_error) {
|
|
229
|
+
chunks.push({ type: 'error', error: data.result || 'Unknown error' });
|
|
230
|
+
}
|
|
231
|
+
chunks.push({ type: 'done' });
|
|
232
|
+
return chunks;
|
|
233
|
+
}
|
|
234
|
+
function extractStreamUsage(usage) {
|
|
235
|
+
if (!usage)
|
|
236
|
+
return undefined;
|
|
237
|
+
const firstKey = Object.keys(usage)[0];
|
|
238
|
+
const modelUsage = firstKey ? usage[firstKey] : undefined;
|
|
239
|
+
if (!modelUsage)
|
|
240
|
+
return undefined;
|
|
241
|
+
return {
|
|
242
|
+
inputTokens: modelUsage.inputTokens,
|
|
243
|
+
outputTokens: modelUsage.outputTokens,
|
|
244
|
+
contextWindow: 0,
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
//# sourceMappingURL=output-parser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"output-parser.js","sourceRoot":"","sources":["../../../src/backend/ai/output-parser.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Session Manager
|
|
3
|
+
*
|
|
4
|
+
* Manages AI sessions with lazy creation.
|
|
5
|
+
* Sessions are persisted to .loom/sessions/ as JSON files.
|
|
6
|
+
*/
|
|
7
|
+
import type { AIUsage } from '../../index.js';
|
|
8
|
+
/** On-disk session record */
|
|
9
|
+
export interface SessionRecord {
|
|
10
|
+
id: string;
|
|
11
|
+
claudeSessionId?: string;
|
|
12
|
+
title: string;
|
|
13
|
+
createdAt: string;
|
|
14
|
+
updatedAt: string;
|
|
15
|
+
messages: SessionMessage[];
|
|
16
|
+
usage?: AIUsage;
|
|
17
|
+
}
|
|
18
|
+
export interface ContentBlock {
|
|
19
|
+
type: 'thinking' | 'content' | 'tool_call';
|
|
20
|
+
thinking?: string;
|
|
21
|
+
content?: string;
|
|
22
|
+
tool?: string;
|
|
23
|
+
toolUseId?: string;
|
|
24
|
+
toolInput?: unknown;
|
|
25
|
+
result?: unknown;
|
|
26
|
+
status?: 'started' | 'completed' | 'error';
|
|
27
|
+
error?: string;
|
|
28
|
+
}
|
|
29
|
+
export interface SessionMessage {
|
|
30
|
+
id: string;
|
|
31
|
+
role: 'user' | 'assistant';
|
|
32
|
+
content: string;
|
|
33
|
+
timestamp: string;
|
|
34
|
+
files?: SessionFileRef[];
|
|
35
|
+
contentBlocks?: ContentBlock[];
|
|
36
|
+
}
|
|
37
|
+
export interface SessionFileRef {
|
|
38
|
+
uid: string;
|
|
39
|
+
name: string;
|
|
40
|
+
size: number;
|
|
41
|
+
type: string;
|
|
42
|
+
url?: string;
|
|
43
|
+
}
|
|
44
|
+
export declare class SessionManager {
|
|
45
|
+
private sessionsDir;
|
|
46
|
+
/** In-memory session index for fast listing */
|
|
47
|
+
private sessionIndex;
|
|
48
|
+
/** Per-session mutex locks to prevent read-modify-write race conditions */
|
|
49
|
+
private locks;
|
|
50
|
+
constructor(projectRoot: string);
|
|
51
|
+
/**
|
|
52
|
+
* Acquire a per-key mutex lock. Prevents concurrent read-modify-write on the same session.
|
|
53
|
+
*/
|
|
54
|
+
private withLock;
|
|
55
|
+
/**
|
|
56
|
+
* Ensure sessions directory exists and load index
|
|
57
|
+
*/
|
|
58
|
+
initialize(): Promise<void>;
|
|
59
|
+
/**
|
|
60
|
+
* Create a new session on demand (lazy, no pre-allocation)
|
|
61
|
+
* Safe against race conditions: checks if session already exists before creating.
|
|
62
|
+
*/
|
|
63
|
+
createSession(id?: string): Promise<SessionRecord>;
|
|
64
|
+
/**
|
|
65
|
+
* Read a session by ID
|
|
66
|
+
*/
|
|
67
|
+
readSession(id: string): Promise<SessionRecord | null>;
|
|
68
|
+
/**
|
|
69
|
+
* Add messages to a session and update claudeSessionId
|
|
70
|
+
*/
|
|
71
|
+
addMessages(id: string, messages: SessionMessage[], claudeSessionId?: string, usage?: AIUsage): Promise<SessionRecord>;
|
|
72
|
+
/**
|
|
73
|
+
* List all sessions sorted by most recent first (from index, O(1))
|
|
74
|
+
*/
|
|
75
|
+
listSessions(): Promise<SessionRecord[]>;
|
|
76
|
+
/**
|
|
77
|
+
* List full session records from files (fallback, used during initialization)
|
|
78
|
+
*/
|
|
79
|
+
private listSessionsFromFiles;
|
|
80
|
+
/**
|
|
81
|
+
* Delete a single message from a session
|
|
82
|
+
*/
|
|
83
|
+
deleteMessage(sessionId: string, messageId: string): Promise<SessionRecord | null>;
|
|
84
|
+
/**
|
|
85
|
+
* Truncate all messages from a given message index onward (inclusive).
|
|
86
|
+
* Returns the updated session, or null if session not found.
|
|
87
|
+
*/
|
|
88
|
+
truncateMessagesFromIndex(sessionId: string, fromIndex: number): Promise<SessionRecord | null>;
|
|
89
|
+
/**
|
|
90
|
+
* Delete a session
|
|
91
|
+
*/
|
|
92
|
+
deleteSession(id: string): Promise<boolean>;
|
|
93
|
+
/**
|
|
94
|
+
* Get or create a session by ID
|
|
95
|
+
*/
|
|
96
|
+
getOrCreate(id: string): Promise<SessionRecord>;
|
|
97
|
+
private getSessionPath;
|
|
98
|
+
private writeSessionFile;
|
|
99
|
+
private getIndexFilePath;
|
|
100
|
+
private loadIndex;
|
|
101
|
+
private saveIndex;
|
|
102
|
+
}
|
|
103
|
+
//# sourceMappingURL=session-manager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-manager.d.ts","sourceRoot":"","sources":["../../../src/backend/ai/session-manager.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Session Manager
|
|
3
|
+
*
|
|
4
|
+
* Manages AI sessions with lazy creation.
|
|
5
|
+
* Sessions are persisted to .loom/sessions/ as JSON files.
|
|
6
|
+
*/
|
|
7
|
+
import { promises as fs } from 'fs';
|
|
8
|
+
import path from 'path';
|
|
9
|
+
export class SessionManager {
|
|
10
|
+
sessionsDir;
|
|
11
|
+
/** In-memory session index for fast listing */
|
|
12
|
+
sessionIndex = { sessions: [], lastUpdated: '' };
|
|
13
|
+
/** Per-session mutex locks to prevent read-modify-write race conditions */
|
|
14
|
+
locks = new Map();
|
|
15
|
+
constructor(projectRoot) {
|
|
16
|
+
this.sessionsDir = path.join(projectRoot, '.loom', 'sessions');
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Acquire a per-key mutex lock. Prevents concurrent read-modify-write on the same session.
|
|
20
|
+
*/
|
|
21
|
+
async withLock(key, fn) {
|
|
22
|
+
// Wait for any existing lock to release
|
|
23
|
+
while (this.locks.has(key)) {
|
|
24
|
+
await this.locks.get(key);
|
|
25
|
+
}
|
|
26
|
+
// Acquire lock
|
|
27
|
+
let resolve;
|
|
28
|
+
const promise = new Promise((r) => { resolve = r; });
|
|
29
|
+
this.locks.set(key, promise);
|
|
30
|
+
try {
|
|
31
|
+
return await fn();
|
|
32
|
+
}
|
|
33
|
+
finally {
|
|
34
|
+
this.locks.delete(key);
|
|
35
|
+
resolve();
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Ensure sessions directory exists and load index
|
|
40
|
+
*/
|
|
41
|
+
async initialize() {
|
|
42
|
+
await fs.mkdir(this.sessionsDir, { recursive: true });
|
|
43
|
+
// Try to load index from disk
|
|
44
|
+
const indexLoaded = await this.loadIndex();
|
|
45
|
+
if (!indexLoaded) {
|
|
46
|
+
// Fallback: rebuild index from session files
|
|
47
|
+
const sessions = await this.listSessionsFromFiles();
|
|
48
|
+
this.sessionIndex = {
|
|
49
|
+
sessions: sessions.map((s) => ({
|
|
50
|
+
id: s.id,
|
|
51
|
+
title: s.title,
|
|
52
|
+
updatedAt: s.updatedAt,
|
|
53
|
+
createdAt: s.createdAt,
|
|
54
|
+
})),
|
|
55
|
+
lastUpdated: new Date().toISOString(),
|
|
56
|
+
};
|
|
57
|
+
await this.saveIndex();
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Create a new session on demand (lazy, no pre-allocation)
|
|
62
|
+
* Safe against race conditions: checks if session already exists before creating.
|
|
63
|
+
*/
|
|
64
|
+
async createSession(id) {
|
|
65
|
+
const sessionId = id || generateSessionId();
|
|
66
|
+
return this.withLock(sessionId, async () => {
|
|
67
|
+
// Check if session already exists (handles getOrCreate race condition)
|
|
68
|
+
const existing = await this.readSession(sessionId);
|
|
69
|
+
if (existing)
|
|
70
|
+
return existing;
|
|
71
|
+
const now = new Date().toISOString();
|
|
72
|
+
const session = {
|
|
73
|
+
id: sessionId,
|
|
74
|
+
title: 'New Session',
|
|
75
|
+
createdAt: now,
|
|
76
|
+
updatedAt: now,
|
|
77
|
+
messages: [],
|
|
78
|
+
};
|
|
79
|
+
await this.writeSessionFile(session);
|
|
80
|
+
// Update index (avoid duplicates)
|
|
81
|
+
if (!this.sessionIndex.sessions.some(e => e.id === sessionId)) {
|
|
82
|
+
this.sessionIndex.sessions.push({
|
|
83
|
+
id: sessionId,
|
|
84
|
+
title: session.title,
|
|
85
|
+
updatedAt: session.updatedAt,
|
|
86
|
+
createdAt: session.createdAt,
|
|
87
|
+
});
|
|
88
|
+
this.sessionIndex.lastUpdated = new Date().toISOString();
|
|
89
|
+
await this.saveIndex();
|
|
90
|
+
}
|
|
91
|
+
return session;
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Read a session by ID
|
|
96
|
+
*/
|
|
97
|
+
async readSession(id) {
|
|
98
|
+
const filePath = this.getSessionPath(id);
|
|
99
|
+
try {
|
|
100
|
+
const content = await fs.readFile(filePath, 'utf-8');
|
|
101
|
+
return JSON.parse(content);
|
|
102
|
+
}
|
|
103
|
+
catch {
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Add messages to a session and update claudeSessionId
|
|
109
|
+
*/
|
|
110
|
+
async addMessages(id, messages, claudeSessionId, usage) {
|
|
111
|
+
return this.withLock(id, async () => {
|
|
112
|
+
const session = await this.readSession(id);
|
|
113
|
+
if (!session) {
|
|
114
|
+
throw new Error(`Session not found: ${id}`);
|
|
115
|
+
}
|
|
116
|
+
session.messages.push(...messages);
|
|
117
|
+
session.updatedAt = new Date().toISOString();
|
|
118
|
+
if (claudeSessionId) {
|
|
119
|
+
session.claudeSessionId = claudeSessionId;
|
|
120
|
+
}
|
|
121
|
+
if (usage) {
|
|
122
|
+
session.usage = usage;
|
|
123
|
+
}
|
|
124
|
+
// Auto-title from first user message
|
|
125
|
+
if (session.title === 'New Session' && session.messages.length > 0) {
|
|
126
|
+
const firstUser = session.messages.find((m) => m.role === 'user');
|
|
127
|
+
if (firstUser) {
|
|
128
|
+
session.title = firstUser.content.slice(0, 80) + (firstUser.content.length > 80 ? '...' : '');
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
await this.writeSessionFile(session);
|
|
132
|
+
// Sync index entry (title may have changed)
|
|
133
|
+
const idxEntry = this.sessionIndex.sessions.find((e) => e.id === id);
|
|
134
|
+
if (idxEntry) {
|
|
135
|
+
idxEntry.title = session.title;
|
|
136
|
+
idxEntry.updatedAt = session.updatedAt;
|
|
137
|
+
}
|
|
138
|
+
this.sessionIndex.lastUpdated = new Date().toISOString();
|
|
139
|
+
await this.saveIndex();
|
|
140
|
+
return session;
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* List all sessions sorted by most recent first (from index, O(1))
|
|
145
|
+
*/
|
|
146
|
+
async listSessions() {
|
|
147
|
+
// Use index for fast path: only return summary data
|
|
148
|
+
// For full records, callers should use readSession(id)
|
|
149
|
+
const entries = [...this.sessionIndex.sessions];
|
|
150
|
+
entries.sort((a, b) => new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime());
|
|
151
|
+
// Map index entries to minimal SessionRecord shape
|
|
152
|
+
return entries.map((e) => ({
|
|
153
|
+
id: e.id,
|
|
154
|
+
title: e.title,
|
|
155
|
+
createdAt: e.createdAt,
|
|
156
|
+
updatedAt: e.updatedAt,
|
|
157
|
+
messages: [],
|
|
158
|
+
}));
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* List full session records from files (fallback, used during initialization)
|
|
162
|
+
*/
|
|
163
|
+
async listSessionsFromFiles() {
|
|
164
|
+
try {
|
|
165
|
+
const files = await fs.readdir(this.sessionsDir);
|
|
166
|
+
const sessions = [];
|
|
167
|
+
for (const file of files) {
|
|
168
|
+
if (file === 'index.json')
|
|
169
|
+
continue;
|
|
170
|
+
if (!file.endsWith('.json'))
|
|
171
|
+
continue;
|
|
172
|
+
const filePath = path.join(this.sessionsDir, file);
|
|
173
|
+
try {
|
|
174
|
+
const content = await fs.readFile(filePath, 'utf-8');
|
|
175
|
+
sessions.push(JSON.parse(content));
|
|
176
|
+
}
|
|
177
|
+
catch {
|
|
178
|
+
// Skip malformed files
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
sessions.sort((a, b) => new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime());
|
|
182
|
+
return sessions;
|
|
183
|
+
}
|
|
184
|
+
catch {
|
|
185
|
+
return [];
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Delete a single message from a session
|
|
190
|
+
*/
|
|
191
|
+
async deleteMessage(sessionId, messageId) {
|
|
192
|
+
return this.withLock(sessionId, async () => {
|
|
193
|
+
const session = await this.readSession(sessionId);
|
|
194
|
+
if (!session)
|
|
195
|
+
return null;
|
|
196
|
+
const originalLen = session.messages.length;
|
|
197
|
+
session.messages = session.messages.filter((m) => m.id !== messageId);
|
|
198
|
+
if (session.messages.length === originalLen)
|
|
199
|
+
return null; // message not found
|
|
200
|
+
session.updatedAt = new Date().toISOString();
|
|
201
|
+
await this.writeSessionFile(session);
|
|
202
|
+
// Sync index
|
|
203
|
+
const idxEntry = this.sessionIndex.sessions.find((e) => e.id === sessionId);
|
|
204
|
+
if (idxEntry) {
|
|
205
|
+
idxEntry.updatedAt = session.updatedAt;
|
|
206
|
+
}
|
|
207
|
+
this.sessionIndex.lastUpdated = new Date().toISOString();
|
|
208
|
+
await this.saveIndex();
|
|
209
|
+
return session;
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Truncate all messages from a given message index onward (inclusive).
|
|
214
|
+
* Returns the updated session, or null if session not found.
|
|
215
|
+
*/
|
|
216
|
+
async truncateMessagesFromIndex(sessionId, fromIndex) {
|
|
217
|
+
return this.withLock(sessionId, async () => {
|
|
218
|
+
const session = await this.readSession(sessionId);
|
|
219
|
+
if (!session)
|
|
220
|
+
return null;
|
|
221
|
+
if (fromIndex < 0 || fromIndex >= session.messages.length)
|
|
222
|
+
return null;
|
|
223
|
+
session.messages = session.messages.slice(0, fromIndex);
|
|
224
|
+
session.updatedAt = new Date().toISOString();
|
|
225
|
+
await this.writeSessionFile(session);
|
|
226
|
+
// Sync index
|
|
227
|
+
const idxEntry = this.sessionIndex.sessions.find((e) => e.id === sessionId);
|
|
228
|
+
if (idxEntry) {
|
|
229
|
+
idxEntry.updatedAt = session.updatedAt;
|
|
230
|
+
}
|
|
231
|
+
this.sessionIndex.lastUpdated = new Date().toISOString();
|
|
232
|
+
await this.saveIndex();
|
|
233
|
+
return session;
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Delete a session
|
|
238
|
+
*/
|
|
239
|
+
async deleteSession(id) {
|
|
240
|
+
return this.withLock(id, async () => {
|
|
241
|
+
const filePath = this.getSessionPath(id);
|
|
242
|
+
try {
|
|
243
|
+
await fs.unlink(filePath);
|
|
244
|
+
// Sync index
|
|
245
|
+
this.sessionIndex.sessions = this.sessionIndex.sessions.filter((e) => e.id !== id);
|
|
246
|
+
this.sessionIndex.lastUpdated = new Date().toISOString();
|
|
247
|
+
await this.saveIndex();
|
|
248
|
+
return true;
|
|
249
|
+
}
|
|
250
|
+
catch {
|
|
251
|
+
return false;
|
|
252
|
+
}
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
/**
|
|
256
|
+
* Get or create a session by ID
|
|
257
|
+
*/
|
|
258
|
+
async getOrCreate(id) {
|
|
259
|
+
const existing = await this.readSession(id);
|
|
260
|
+
if (existing)
|
|
261
|
+
return existing;
|
|
262
|
+
return this.createSession(id);
|
|
263
|
+
}
|
|
264
|
+
getSessionPath(id) {
|
|
265
|
+
return path.join(this.sessionsDir, `${id}.json`);
|
|
266
|
+
}
|
|
267
|
+
async writeSessionFile(session) {
|
|
268
|
+
const filePath = this.getSessionPath(session.id);
|
|
269
|
+
await fs.mkdir(this.sessionsDir, { recursive: true });
|
|
270
|
+
await fs.writeFile(filePath, JSON.stringify(session, null, 2), 'utf-8');
|
|
271
|
+
}
|
|
272
|
+
getIndexFilePath() {
|
|
273
|
+
return path.join(this.sessionsDir, 'index.json');
|
|
274
|
+
}
|
|
275
|
+
async loadIndex() {
|
|
276
|
+
try {
|
|
277
|
+
const content = await fs.readFile(this.getIndexFilePath(), 'utf-8');
|
|
278
|
+
this.sessionIndex = JSON.parse(content);
|
|
279
|
+
return true;
|
|
280
|
+
}
|
|
281
|
+
catch {
|
|
282
|
+
return false;
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
async saveIndex() {
|
|
286
|
+
try {
|
|
287
|
+
await fs.mkdir(this.sessionsDir, { recursive: true });
|
|
288
|
+
await fs.writeFile(this.getIndexFilePath(), JSON.stringify(this.sessionIndex, null, 2), 'utf-8');
|
|
289
|
+
}
|
|
290
|
+
catch {
|
|
291
|
+
// Non-critical: index is in-memory, will rebuild on next init
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
function generateSessionId() {
|
|
296
|
+
return `session_${Date.now()}_${Math.random().toString(36).substring(2, 11)}`;
|
|
297
|
+
}
|
|
298
|
+
//# sourceMappingURL=session-manager.js.map
|