squish-memory 1.1.5 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +32 -16
- package/CHANGELOG.md +147 -0
- package/README.md +120 -78
- package/{scripts → bin}/dependency-manager.mjs +217 -217
- package/{scripts → bin}/detect-clients.mjs +78 -78
- package/bin/install-interactive.mjs +321 -0
- package/bin/squish-mcp.mjs +46 -0
- package/bin/squish.mjs +33 -0
- package/config/mcp-migration-map.json +1 -6
- package/config/mcp-mode-semantics.json +19 -23
- package/config/mcp-remote-auth.json +3 -26
- package/config/mcp-universal.schema.json +5 -35
- package/config/settings.json +107 -52
- package/config.js +5 -0
- package/config.ts +218 -0
- package/core/adapters/config/claude-code.ts +133 -0
- package/core/adapters/config/cursor.ts +90 -0
- package/core/adapters/config/opencode.ts +89 -0
- package/core/adapters/config/windsurf.ts +90 -0
- package/core/adapters/index.ts +102 -0
- package/core/adapters/timeline.ts +116 -0
- package/core/adapters/types.ts +166 -0
- package/core/agent-preferences.ts +140 -0
- package/core/algorithms/analytics/token-estimator.ts +216 -0
- package/core/algorithms/detection/hash-filters.ts +260 -0
- package/core/algorithms/detection/semantic-ranker.ts +194 -0
- package/core/algorithms/detection/two-stage-detector.ts +421 -0
- package/core/algorithms/handlers/approve-merge.ts +215 -0
- package/core/algorithms/handlers/detect-duplicates.ts +192 -0
- package/core/algorithms/handlers/get-stats.ts +132 -0
- package/core/algorithms/handlers/list-proposals.ts +130 -0
- package/core/algorithms/handlers/preview-merge.ts +139 -0
- package/core/algorithms/handlers/reject-merge.ts +93 -0
- package/core/algorithms/handlers/reverse-merge.ts +155 -0
- package/core/algorithms/index.ts +39 -0
- package/core/algorithms/operations/cache-maintenance.ts +182 -0
- package/core/algorithms/safety/safety-checks.ts +256 -0
- package/core/algorithms/strategies/merge-strategies.ts +381 -0
- package/core/algorithms/types.ts +140 -0
- package/core/algorithms/utils/response-builder.ts +61 -0
- package/core/associations.ts +363 -0
- package/core/beliefs/decay.ts +289 -0
- package/core/beliefs/extractor.ts +131 -0
- package/core/beliefs/store.ts +557 -0
- package/core/beliefs/types.ts +38 -0
- package/core/commands/mcp-server.ts +5 -0
- package/core/compression.ts +177 -0
- package/core/config.js +2 -0
- package/core/consolidation.ts +330 -0
- package/core/context/agent-context.ts +388 -0
- package/core/context/context-paging.ts +449 -0
- package/core/context/context-window.ts +234 -0
- package/core/context/context.ts +35 -0
- package/core/embeddings/embeddings.ts +616 -0
- package/core/embeddings/google-multimodal.ts +200 -0
- package/{dist/core/local-embeddings.js → core/embeddings/local-embeddings.ts} +12 -11
- package/core/embeddings/qmd-client.ts +495 -0
- package/core/embeddings/transformers-local.ts +261 -0
- package/core/embeddings.js +4 -0
- package/core/error-handling.ts +206 -0
- package/core/external +219 -0
- package/core/graph/entity-deduplicator.ts +232 -0
- package/core/graph/graph-builder.ts +257 -0
- package/core/graph/graph-traversal.ts +490 -0
- package/core/graph/index.ts +24 -0
- package/core/graph/llm-entity-extractor.ts +402 -0
- package/core/graph/multi-hop-retrieval.ts +317 -0
- package/core/graph/relationship-extractor.ts +465 -0
- package/core/hooks/agent-hooks.ts +653 -0
- package/core/hooks/auto-tagger.ts +149 -0
- package/core/hooks/capture-filter.ts +169 -0
- package/core/hot-cache.ts +388 -0
- package/core/index.ts +10 -0
- package/core/ingestion/agent-memory.ts +167 -0
- package/core/ingestion/core-memory.ts +326 -0
- package/core/ingestion/learnings.ts +260 -0
- package/core/ingestion/signal-engine.ts +266 -0
- package/core/integrations/obsidian-vault.ts +197 -0
- package/core/layers/generator.ts +115 -0
- package/core/lib/db-client.ts +168 -0
- package/core/lib/parse-embedding.ts +59 -0
- package/core/lib/schemas.ts +102 -0
- package/core/lib/types.ts +49 -0
- package/core/lib/utils.ts +151 -0
- package/core/lib/validation.ts +180 -0
- package/core/lifecycle.ts +353 -0
- package/core/logger.ts +59 -0
- package/core/memory/bridge-discovery.ts +395 -0
- package/core/memory/categorizer.ts +390 -0
- package/core/memory/conflict-detector.ts +62 -0
- package/core/memory/consolidation.ts +372 -0
- package/core/memory/context-collector.ts +75 -0
- package/core/memory/contradiction-resolver.ts +494 -0
- package/core/memory/edit-workflow.ts +174 -0
- package/core/memory/entity-extractor.ts +426 -0
- package/core/memory/entity-resolver.ts +89 -0
- package/core/memory/explain.ts +112 -0
- package/core/memory/fact-deriver.ts +300 -0
- package/core/memory/fact-extractor.ts +120 -0
- package/core/memory/feedback-tracker.ts +200 -0
- package/core/memory/hooks.ts +230 -0
- package/core/memory/hybrid-retrieval.ts +65 -0
- package/core/memory/hybrid-scorer.ts +325 -0
- package/core/memory/hybrid-search.ts +748 -0
- package/core/memory/importance.ts +319 -0
- package/core/memory/index.ts +11 -0
- package/core/memory/loader.ts +178 -0
- package/core/memory/markdown/markdown-storage.ts +318 -0
- package/core/memory/memories.ts +565 -0
- package/core/memory/memory-lifecycle.ts +51 -0
- package/core/memory/memory-manager.ts +53 -0
- package/core/memory/migrate.ts +173 -0
- package/core/memory/normalization.ts +30 -0
- package/core/memory/path-strengthener.ts +211 -0
- package/core/memory/progressive-disclosure.ts +392 -0
- package/core/memory/query-processor.ts +130 -0
- package/core/memory/query-rewriter.ts +153 -0
- package/core/memory/response-analyzer.ts +81 -0
- package/core/memory/retrieval-feedback.ts +276 -0
- package/core/memory/serialization.ts +83 -0
- package/core/memory/stale-cleaner.ts +147 -0
- package/core/memory/stats.ts +181 -0
- package/core/memory/telemetry.ts +392 -0
- package/core/memory/temporal-facts.ts +356 -0
- package/core/memory/temporal-parser.ts +477 -0
- package/core/memory/trigger-detector.ts +104 -0
- package/core/memory/write-gate.ts +288 -0
- package/core/places/index.ts +14 -0
- package/core/places/memory-places.ts +339 -0
- package/core/places/places.ts +406 -0
- package/core/places/rules.ts +308 -0
- package/core/places/walking.ts +192 -0
- package/core/projects +89 -0
- package/core/projects.ts +131 -0
- package/core/redis.ts +82 -0
- package/core/responses.ts +187 -0
- package/core/runtime/trust-report.ts +195 -0
- package/core/runtime/trust-state.ts +360 -0
- package/core/scheduler/cron-scheduler.ts +581 -0
- package/core/scheduler/heartbeat.ts +91 -0
- package/core/scheduler/index.ts +8 -0
- package/core/scheduler/job-runner.ts +197 -0
- package/core/search/conversations.ts +166 -0
- package/core/search/entities.ts +46 -0
- package/core/search/folder-context.ts +154 -0
- package/core/search/graph-boost.ts +22 -0
- package/core/search/index.ts +4 -0
- package/core/search/qmd-wrapper.ts +84 -0
- package/core/security/encrypt.ts +51 -0
- package/core/security/governance.ts +102 -0
- package/core/security/privacy.ts +108 -0
- package/core/security/secret-detector.ts +122 -0
- package/core/session/auto-load.ts +160 -0
- package/core/session/entity-tracker.ts +363 -0
- package/core/session/index.ts +7 -0
- package/core/session/reference-resolver.ts +158 -0
- package/core/session/self-iteration-job.ts +478 -0
- package/core/session/session-hooks.ts +69 -0
- package/core/session/types.ts +36 -0
- package/core/session/working-set.ts +275 -0
- package/core/snapshots/cleanup.ts +13 -0
- package/core/snapshots/comparison.ts +59 -0
- package/core/snapshots/creation.ts +139 -0
- package/core/snapshots/retrieval.ts +44 -0
- package/core/snapshots/stats.ts +63 -0
- package/core/storage/cache.ts +241 -0
- package/core/storage/database.ts +23 -0
- package/core/summarization/cleanup.ts +13 -0
- package/core/summarization/queries.ts +32 -0
- package/core/summarization/stats.ts +64 -0
- package/core/summarization/strategies.ts +52 -0
- package/core/summarization.ts +248 -0
- package/core/temporal-facts.ts +244 -0
- package/core/tracing/collector.ts +470 -0
- package/core/tracing/visualizer.ts +195 -0
- package/core/utils/cleanup-operations.ts +50 -0
- package/core/utils/content-extraction.ts +95 -0
- package/core/utils/filter-builder.ts +56 -0
- package/core/utils/history-traversal.ts +63 -0
- package/core/utils/memory-operations.ts +56 -0
- package/core/utils/query-operations.ts +83 -0
- package/core/utils/summarization-helpers.ts +45 -0
- package/core/utils/temporal-queries.ts +39 -0
- package/core/utils/vector-operations.ts +135 -0
- package/core/utils/version-management.ts +74 -0
- package/core/worker.ts +324 -0
- package/db/adapter.ts +215 -0
- package/db/bootstrap.ts +1055 -0
- package/db/drizzle/migrations/0000_needy_cerebro.sql +402 -0
- package/db/drizzle/migrations/meta/0000_snapshot.json +3451 -0
- package/db/drizzle/migrations/meta/_journal.json +13 -0
- package/db/drizzle/schema-sqlite.ts +1032 -0
- package/db/drizzle/schema.ts +1128 -0
- package/db/drizzle.config.ts +12 -0
- package/db/index.ts +83 -0
- package/db/init.sql +5 -0
- package/db/migrations/associations.ts +35 -0
- package/db/migrations/beliefs.ts +89 -0
- package/db/migrations/core-memory.ts +35 -0
- package/db/migrations/fts.ts +59 -0
- package/db/migrations/index.ts +54 -0
- package/db/migrations/indexes.ts +36 -0
- package/db/migrations/learnings.ts +34 -0
- package/db/migrations/maintenance.ts +68 -0
- package/db/migrations/memories.ts +22 -0
- package/db/migrations/memory-places.ts +35 -0
- package/db/migrations/places.ts +49 -0
- package/db/migrations/projects.ts +21 -0
- package/db/migrations/tier-conversion.ts +24 -0
- package/db/neon.ts +22 -0
- package/db/schema/beliefs.ts +50 -0
- package/db/schema/generator.ts +159 -0
- package/db/schema/index.ts +58 -0
- package/db/schema/learnings.ts +32 -0
- package/db/schema/memories.ts +83 -0
- package/db/schema/projects.ts +33 -0
- package/db/schema.ts +13 -0
- package/db/supabase.ts +27 -0
- package/dist/config.d.ts +40 -17
- package/dist/config.js +150 -198
- package/dist/core/adapters/types.d.ts +13 -33
- package/dist/core/adapters/types.js +1 -1
- package/dist/core/agent-preferences.d.ts +16 -0
- package/dist/core/agent-preferences.js +124 -0
- package/dist/core/algorithms/safety/safety-checks.d.ts +1 -5
- package/dist/core/algorithms/types.d.ts +0 -8
- package/dist/core/associations.d.ts +3 -1
- package/dist/core/associations.js +37 -1
- package/dist/core/beliefs/decay.d.ts +27 -0
- package/dist/core/beliefs/decay.js +217 -0
- package/dist/core/beliefs/extractor.d.ts +9 -0
- package/dist/core/beliefs/extractor.js +113 -0
- package/dist/core/beliefs/store.d.ts +46 -0
- package/dist/core/beliefs/store.js +466 -0
- package/dist/core/beliefs/types.d.ts +28 -0
- package/dist/core/beliefs/types.js +2 -0
- package/dist/core/commands/mcp-server.d.ts +0 -1
- package/dist/core/commands/mcp-server.js +4 -737
- package/dist/core/commands/remember.d.ts +24 -0
- package/dist/core/commands/remember.js +144 -0
- package/dist/core/{toon.d.ts → compression.d.ts} +6 -4
- package/dist/core/{toon.js → compression.js} +8 -8
- package/dist/core/context/agent-context.js +1 -1
- package/dist/core/embeddings/embeddings.d.ts +29 -0
- package/dist/core/embeddings/embeddings.js +546 -0
- package/dist/core/embeddings/google-multimodal.js +6 -2
- package/dist/core/{local-embeddings.d.ts → embeddings/local-embeddings.d.ts} +1 -1
- package/dist/core/embeddings/local-embeddings.js +11 -0
- package/dist/core/embeddings/qmd-client.js +1 -1
- package/dist/core/embeddings/transformers-local.d.ts +64 -0
- package/dist/core/embeddings/transformers-local.js +213 -0
- package/dist/core/embeddings.d.ts +1 -28
- package/dist/core/embeddings.js +2 -453
- package/dist/core/graph/entity-deduplicator.d.ts +24 -0
- package/dist/core/graph/entity-deduplicator.js +183 -0
- package/dist/core/graph/graph-builder.d.ts +46 -0
- package/dist/core/graph/graph-builder.js +174 -0
- package/dist/core/graph/graph-traversal.d.ts +80 -0
- package/dist/core/graph/graph-traversal.js +315 -0
- package/dist/core/graph/index.d.ts +19 -0
- package/dist/core/graph/index.js +13 -0
- package/dist/core/graph/llm-entity-extractor.d.ts +49 -0
- package/dist/core/graph/llm-entity-extractor.js +313 -0
- package/dist/core/graph/multi-hop-retrieval.d.ts +48 -0
- package/dist/core/graph/multi-hop-retrieval.js +215 -0
- package/dist/core/graph/relationship-extractor.d.ts +48 -0
- package/dist/core/graph/relationship-extractor.js +351 -0
- package/dist/core/hooks/agent-hooks.d.ts +10 -1
- package/dist/core/hooks/agent-hooks.js +301 -24
- package/dist/core/hot-cache.d.ts +86 -0
- package/dist/core/hot-cache.js +285 -0
- package/dist/core/index.d.ts +9 -9
- package/dist/core/index.js +9 -12
- package/dist/core/ingestion/core-memory.d.ts +2 -2
- package/dist/core/ingestion/core-memory.js +3 -3
- package/dist/core/ingestion/learnings.js +3 -0
- package/dist/core/ingestion/signal-engine.d.ts +41 -0
- package/dist/core/ingestion/signal-engine.js +201 -0
- package/dist/core/{obsidian-vault.d.ts → integrations/obsidian-vault.d.ts} +2 -1
- package/dist/core/{obsidian-vault.js → integrations/obsidian-vault.js} +69 -7
- package/dist/core/lib/parse-embedding.d.ts +9 -0
- package/dist/core/lib/parse-embedding.js +58 -0
- package/dist/core/lib/schemas.d.ts +57 -54
- package/dist/core/lib/types.d.ts +45 -0
- package/dist/core/lib/types.js +6 -0
- package/dist/core/lib/utils.d.ts +4 -0
- package/dist/core/lib/utils.js +55 -0
- package/dist/core/lifecycle.d.ts +0 -1
- package/dist/core/lifecycle.js +13 -23
- package/dist/core/logger.d.ts +1 -0
- package/dist/core/logger.js +14 -8
- package/dist/core/mcp/tools.d.ts +0 -2
- package/dist/core/mcp/tools.js +0 -87
- package/dist/core/mcp/types.d.ts +25 -253
- package/dist/core/mcp/types.js +2 -2
- package/dist/core/memory/categorizer.js +1 -0
- package/dist/core/memory/consolidation.js +2 -28
- package/dist/core/memory/entity-extractor.d.ts +4 -0
- package/dist/core/memory/entity-extractor.js +30 -16
- package/dist/core/memory/explain.d.ts +18 -0
- package/dist/core/memory/explain.js +92 -0
- package/dist/core/memory/fact-deriver.d.ts +31 -0
- package/dist/core/memory/fact-deriver.js +236 -0
- package/dist/core/memory/hybrid-retrieval.d.ts +14 -16
- package/dist/core/memory/hybrid-retrieval.js +25 -127
- package/dist/core/memory/hybrid-scorer.js +6 -23
- package/dist/core/memory/hybrid-search.d.ts +10 -7
- package/dist/core/memory/hybrid-search.js +458 -221
- package/dist/core/memory/importance.d.ts +0 -17
- package/dist/core/memory/importance.js +1 -58
- package/dist/core/memory/index.d.ts +1 -0
- package/dist/core/memory/index.js +1 -0
- package/dist/core/memory/memories.d.ts +13 -17
- package/dist/core/memory/memories.js +78 -75
- package/dist/core/memory/memory-lifecycle.d.ts +2 -2
- package/dist/core/memory/memory-lifecycle.js +10 -18
- package/dist/core/memory/normalization.d.ts +1 -16
- package/dist/core/memory/path-strengthener.d.ts +39 -0
- package/dist/core/memory/path-strengthener.js +150 -0
- package/dist/core/memory/query-processor.js +37 -3
- package/dist/core/memory/retrieval-feedback.d.ts +70 -0
- package/dist/core/memory/retrieval-feedback.js +213 -0
- package/dist/core/memory/stale-cleaner.d.ts +26 -0
- package/dist/core/memory/stale-cleaner.js +97 -0
- package/dist/core/memory/stats.d.ts +10 -0
- package/dist/core/memory/stats.js +8 -3
- package/dist/core/memory/trigger-detector.d.ts +8 -1
- package/dist/core/memory/trigger-detector.js +42 -5
- package/dist/core/places/index.d.ts +1 -1
- package/dist/core/places/index.js +1 -1
- package/dist/core/places/places.d.ts +13 -13
- package/dist/core/places/places.js +27 -27
- package/dist/core/places/rules.js +23 -23
- package/dist/core/places/walking.d.ts +3 -3
- package/dist/core/places/walking.js +7 -7
- package/dist/core/projects.js +8 -0
- package/dist/core/runtime/trust-report.d.ts +102 -0
- package/dist/core/runtime/trust-report.js +107 -0
- package/dist/core/runtime/trust-state.d.ts +12 -0
- package/dist/core/runtime/trust-state.js +309 -0
- package/dist/core/scheduler/cron-scheduler.d.ts +1 -1
- package/dist/core/scheduler/cron-scheduler.js +164 -3
- package/dist/core/scheduler/job-runner.js +1 -1
- package/dist/core/search/qmd-wrapper.d.ts +36 -0
- package/dist/core/search/qmd-wrapper.js +58 -0
- package/dist/core/session/auto-load.js +28 -3
- package/dist/core/session/entity-tracker.d.ts +62 -0
- package/dist/core/session/entity-tracker.js +287 -0
- package/dist/core/session/reference-resolver.d.ts +26 -0
- package/dist/core/session/reference-resolver.js +121 -0
- package/dist/core/session/self-iteration-job.d.ts +15 -0
- package/dist/core/session/self-iteration-job.js +163 -58
- package/dist/core/session/working-set.d.ts +50 -0
- package/dist/core/session/working-set.js +212 -0
- package/dist/core/snapshots/creation.d.ts +2 -8
- package/dist/core/snapshots/creation.js +3 -12
- package/dist/core/utils/summarization-helpers.d.ts +0 -4
- package/dist/core/utils/summarization-helpers.js +1 -6
- package/dist/db/bootstrap.d.ts +2 -0
- package/dist/db/bootstrap.js +229 -280
- package/dist/db/drizzle/schema-sqlite.d.ts +702 -1
- package/dist/db/drizzle/schema-sqlite.js +83 -4
- package/dist/db/drizzle/schema.d.ts +653 -1
- package/dist/db/drizzle/schema.js +93 -4
- package/dist/db/migrations/associations.d.ts +6 -0
- package/dist/db/migrations/associations.js +29 -0
- package/dist/db/migrations/beliefs.d.ts +10 -0
- package/dist/db/migrations/beliefs.js +76 -0
- package/dist/db/migrations/core-memory.d.ts +6 -0
- package/dist/db/migrations/core-memory.js +29 -0
- package/dist/db/migrations/fts.d.ts +6 -0
- package/dist/db/migrations/fts.js +52 -0
- package/dist/db/migrations/index.d.ts +25 -0
- package/dist/db/migrations/index.js +51 -0
- package/dist/db/migrations/indexes.d.ts +6 -0
- package/dist/db/migrations/indexes.js +30 -0
- package/dist/db/migrations/learnings.d.ts +7 -0
- package/dist/db/migrations/learnings.js +26 -0
- package/dist/db/migrations/maintenance.d.ts +6 -0
- package/dist/db/migrations/maintenance.js +61 -0
- package/dist/db/migrations/memories.d.ts +7 -0
- package/dist/db/migrations/memories.js +16 -0
- package/dist/db/migrations/memory-places.d.ts +6 -0
- package/dist/db/migrations/memory-places.js +29 -0
- package/dist/db/migrations/places.d.ts +6 -0
- package/dist/db/migrations/places.js +43 -0
- package/dist/db/migrations/projects.d.ts +3 -0
- package/dist/db/migrations/projects.js +13 -0
- package/dist/db/migrations/tier-conversion.d.ts +7 -0
- package/dist/db/migrations/tier-conversion.js +20 -0
- package/dist/db/schema/beliefs.d.ts +9 -0
- package/dist/db/schema/beliefs.js +46 -0
- package/dist/db/schema/generator.d.ts +38 -0
- package/dist/db/schema/generator.js +108 -0
- package/dist/db/schema/index.d.ts +19 -20
- package/dist/db/schema/index.js +25 -79
- package/dist/db/schema/learnings.d.ts +7 -0
- package/dist/db/schema/learnings.js +30 -0
- package/dist/db/schema/memories.d.ts +7 -0
- package/dist/db/schema/memories.js +81 -0
- package/dist/db/schema/projects.d.ts +4 -0
- package/dist/db/schema/projects.js +31 -0
- package/dist/packages/mcp/src/index.d.ts +3 -0
- package/dist/packages/mcp/src/index.js +733 -0
- package/mcp.json.example +8 -11
- package/package.json +57 -76
- package/packages/cli/package.json +22 -0
- package/packages/cli/src/commands/clean.ts +68 -0
- package/packages/cli/src/commands/context.ts +79 -0
- package/packages/cli/src/commands/doctor.ts +357 -0
- package/packages/cli/src/commands/forget.ts +72 -0
- package/packages/cli/src/commands/health.ts +36 -0
- package/packages/cli/src/commands/inspect.ts +41 -0
- package/packages/cli/src/commands/link.ts +50 -0
- package/packages/cli/src/commands/migrate.ts +93 -0
- package/packages/cli/src/commands/recall.ts +99 -0
- package/packages/cli/src/commands/recent.ts +57 -0
- package/packages/cli/src/commands/remember.ts +139 -0
- package/packages/cli/src/commands/run.ts +58 -0
- package/packages/cli/src/commands/stale.ts +43 -0
- package/packages/cli/src/commands/stats.ts +42 -0
- package/packages/cli/src/index.ts +57 -0
- package/packages/cli/tsconfig.json +24 -0
- package/packages/mcp/package.json +26 -0
- package/packages/mcp/src/index.ts +877 -0
- package/packages/mcp/tsconfig.json +20 -0
- package/skills/squish-memory/SKILL.md +38 -35
- package/skills/squish-memory/{scripts/install.sh → install.sh} +1 -1
- package/skills/squish-memory/references/claude-desktop.json +12 -0
- package/skills/squish-memory/references/openclaw.json +13 -0
- package/skills/squish-memory/references/opencode.json +14 -0
- package/config/hooks/claude-code-hooks.json +0 -39
- package/config/hooks/cursor-hooks.json +0 -30
- package/config/hooks/opencode-hooks.json +0 -30
- package/config/hooks/windsurf-hooks.json +0 -30
- package/config/mcp-cli-fallback-policy.json +0 -22
- package/config/mcp.json +0 -38
- package/config/plugin-manifest.json +0 -101
- package/config/plugin-manifest.schema.json +0 -244
- package/config/plugin.json +0 -32
- package/config/remote-memory-policy.json +0 -32
- package/core/commands/context-paging.md +0 -51
- package/core/commands/context-status.md +0 -22
- package/core/commands/context.md +0 -5
- package/core/commands/core-memory.md +0 -56
- package/core/commands/health.md +0 -5
- package/core/commands/init.md +0 -39
- package/core/commands/merge.md +0 -113
- package/core/commands/recall.md +0 -5
- package/core/commands/remember.md +0 -11
- package/core/commands/search.md +0 -10
- package/dist/core/commands/managed-sync.d.ts +0 -10
- package/dist/core/commands/managed-sync.js +0 -64
- package/dist/core/external-folder/index.d.ts +0 -102
- package/dist/core/external-folder/index.js +0 -294
- package/dist/core/namespaces/index.d.ts +0 -71
- package/dist/core/namespaces/index.js +0 -305
- package/dist/core/namespaces/uri-parser.d.ts +0 -31
- package/dist/core/namespaces/uri-parser.js +0 -74
- package/dist/core/search/qmd-search.d.ts +0 -61
- package/dist/core/search/qmd-search.js +0 -178
- package/dist/core/session-hooks/self-iteration-job.d.ts +0 -20
- package/dist/core/session-hooks/self-iteration-job.js +0 -282
- package/dist/core/session-hooks/session-hooks.d.ts +0 -18
- package/dist/core/session-hooks/session-hooks.js +0 -58
- package/dist/core/snapshots.d.ts +0 -29
- package/dist/core/snapshots.js +0 -220
- package/dist/core/sync/qmd-sync.d.ts +0 -94
- package/dist/core/sync/qmd-sync.js +0 -201
- package/dist/index.d.ts +0 -7
- package/dist/index.js +0 -1677
- package/dist/vendor/sql.js/sql-wasm.wasm +0 -0
- package/dist/webui/server.d.ts +0 -5
- package/dist/webui/server.js +0 -642
- package/generated/mcp/manifest.json +0 -23
- package/generated/mcp/mcp-servers.json +0 -25
- package/generated/mcp/mcporter.json +0 -34
- package/generated/mcp/openclaw-memory-qmd.json +0 -17
- package/generated/mcp/runtime.json +0 -12
- package/scripts/README.md +0 -60
- package/scripts/build-release.sh +0 -36
- package/scripts/check-secrets.js +0 -132
- package/scripts/copy-runtime-assets.mjs +0 -26
- package/scripts/generate-mcp.mjs +0 -264
- package/scripts/github-release.sh +0 -77
- package/scripts/init-dirs.mjs +0 -13
- package/scripts/install-claude-code.sh +0 -85
- package/scripts/install-cursor.sh +0 -56
- package/scripts/install-hooks.sh +0 -73
- package/scripts/install-interactive.mjs +0 -357
- package/scripts/install-opencode.sh +0 -75
- package/scripts/install-plugin.mjs +0 -415
- package/scripts/install-windsurf.sh +0 -67
- package/scripts/remote-preflight.mjs +0 -62
- package/scripts/squish-fallback.mjs +0 -132
- package/scripts/test-interactive.mjs +0 -131
- package/scripts/verify-mcp.mjs +0 -214
- package/skills/squish-memory/scripts/install.mjs +0 -335
- package/skills/squish-memory/write_skill.js +0 -2
package/dist/index.js
DELETED
|
@@ -1,1677 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* Squish - Universal Memory Plugin System
|
|
4
|
-
* CLI + MCP server for persistent memory with hybrid search and encryption
|
|
5
|
-
*/
|
|
6
|
-
import 'dotenv/config';
|
|
7
|
-
import fs from 'node:fs';
|
|
8
|
-
import { existsSync } from 'node:fs';
|
|
9
|
-
import os from 'node:os';
|
|
10
|
-
import path from 'node:path';
|
|
11
|
-
import { fileURLToPath } from 'node:url';
|
|
12
|
-
import { spawn, spawnSync } from 'node:child_process';
|
|
13
|
-
import { Command } from 'commander';
|
|
14
|
-
import { logger } from './core/logger.js';
|
|
15
|
-
import { checkDatabaseHealth, config, getDb } from './db/index.js';
|
|
16
|
-
import { getSchema } from './db/schema.js';
|
|
17
|
-
import { eq } from 'drizzle-orm';
|
|
18
|
-
import { checkRedisHealth } from './core/storage/cache.js';
|
|
19
|
-
import { rememberMemory, getMemory, search, getRecent, setConfidence } from './core/memory/memories.js';
|
|
20
|
-
import { serializeTags } from './core/memory/serialization.js';
|
|
21
|
-
import { createLearning } from './core/ingestion/learnings.js';
|
|
22
|
-
import { getMemoryStats } from './core/memory/stats.js';
|
|
23
|
-
import { ensureProject, getAllProjects, getOrCreateProject } from './core/projects.js';
|
|
24
|
-
import { startWebServer } from './webui/server.js';
|
|
25
|
-
import { getRelatedMemories, createAssociation } from './core/associations.js';
|
|
26
|
-
import { pinMemory, unpinMemory } from './core/security/governance.js';
|
|
27
|
-
import { determineOverallStatus } from './core/lib/utils.js';
|
|
28
|
-
import { validateLimit } from './core/lib/validation.js';
|
|
29
|
-
import { runDeduplicationJob, runFullConsolidationJob } from './core/consolidation.js';
|
|
30
|
-
import { ensureDataDirectory } from './db/bootstrap.js';
|
|
31
|
-
import { getDataDir } from './config.js';
|
|
32
|
-
import { handleSessionStart, handlePostToolUse, handleSessionEnd, handlePreCompact, } from './core/hooks/agent-hooks.js';
|
|
33
|
-
import { initializeDefaultPlaces, walkPlace, walkAllPlaces, quickTour, } from './core/places/index.js';
|
|
34
|
-
const VERSION = '1.1.5';
|
|
35
|
-
// Output Formatting Utilities
|
|
36
|
-
// ============================================================================
|
|
37
|
-
function formatOutput(data, pretty = false) {
|
|
38
|
-
if (!pretty) {
|
|
39
|
-
return JSON.stringify(data, null, 2);
|
|
40
|
-
}
|
|
41
|
-
if (Array.isArray(data)) {
|
|
42
|
-
return data.map((item, i) => `${i + 1}. ${formatItem(item)}`).join('\n');
|
|
43
|
-
}
|
|
44
|
-
if (data.results) {
|
|
45
|
-
return data.results.map((item, i) => `${i + 1}. ${formatItem(item)}`).join('\n');
|
|
46
|
-
}
|
|
47
|
-
if (data.matches) {
|
|
48
|
-
return data.matches.map((item, i) => `${i + 1}. ${formatItem(item)}`).join('\n');
|
|
49
|
-
}
|
|
50
|
-
if (data.count !== undefined) {
|
|
51
|
-
let output = `\nFound ${data.count} results:\n`;
|
|
52
|
-
if (data.results) {
|
|
53
|
-
output += data.results.map((item, i) => ` ${i + 1}. ${formatItem(item)}`).join('\n');
|
|
54
|
-
}
|
|
55
|
-
return output;
|
|
56
|
-
}
|
|
57
|
-
return JSON.stringify(data, null, 2);
|
|
58
|
-
}
|
|
59
|
-
function formatItem(item) {
|
|
60
|
-
if (typeof item === 'string')
|
|
61
|
-
return item.substring(0, 100);
|
|
62
|
-
const content = item.content || item.summary || item.memory?.content || '';
|
|
63
|
-
const type = item.type || '';
|
|
64
|
-
return `[${type}] ${content.substring(0, 80)}${content.length > 80 ? '...' : ''}`;
|
|
65
|
-
}
|
|
66
|
-
function printSuccess(message) {
|
|
67
|
-
console.log(`\n ✓ ${message}\n`);
|
|
68
|
-
}
|
|
69
|
-
function printError(message) {
|
|
70
|
-
console.error(`\n ✗ ${message}\n`);
|
|
71
|
-
}
|
|
72
|
-
function printInfo(message) {
|
|
73
|
-
console.log(`\n ℹ ${message}\n`);
|
|
74
|
-
}
|
|
75
|
-
// Config Management (for project path persistence)
|
|
76
|
-
// ============================================================================
|
|
77
|
-
const USER_CONFIG_PATH = path.join(os.homedir(), '.squish', 'config.json');
|
|
78
|
-
function loadUserConfig() {
|
|
79
|
-
try {
|
|
80
|
-
if (fs.existsSync(USER_CONFIG_PATH)) {
|
|
81
|
-
return JSON.parse(fs.readFileSync(USER_CONFIG_PATH, 'utf-8'));
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
catch (e) { }
|
|
85
|
-
return {};
|
|
86
|
-
}
|
|
87
|
-
function saveUserConfig(config) {
|
|
88
|
-
const dir = path.dirname(USER_CONFIG_PATH);
|
|
89
|
-
if (!fs.existsSync(dir))
|
|
90
|
-
fs.mkdirSync(dir, { recursive: true });
|
|
91
|
-
fs.writeFileSync(USER_CONFIG_PATH, JSON.stringify(config, null, 2));
|
|
92
|
-
}
|
|
93
|
-
function getDefaultProjectPath() {
|
|
94
|
-
const userConfig = loadUserConfig();
|
|
95
|
-
if (userConfig.project)
|
|
96
|
-
return userConfig.project;
|
|
97
|
-
return process.cwd();
|
|
98
|
-
}
|
|
99
|
-
function resolveProjectPath(projectOption) {
|
|
100
|
-
if (projectOption)
|
|
101
|
-
return projectOption;
|
|
102
|
-
return getDefaultProjectPath();
|
|
103
|
-
}
|
|
104
|
-
// Date parsing for time-based queries
|
|
105
|
-
// ============================================================================
|
|
106
|
-
function parseDate(input) {
|
|
107
|
-
if (!input)
|
|
108
|
-
return null;
|
|
109
|
-
const now = new Date();
|
|
110
|
-
const lower = input.toLowerCase().trim();
|
|
111
|
-
// Direct date parse
|
|
112
|
-
const parsed = new Date(input);
|
|
113
|
-
if (!isNaN(parsed.getTime()))
|
|
114
|
-
return parsed;
|
|
115
|
-
// Relative parsing
|
|
116
|
-
const dayMatch = lower.match(/(\d+)\s*day/i);
|
|
117
|
-
const weekMatch = lower.match(/(\d+)\s*week/i);
|
|
118
|
-
const monthMatch = lower.match(/(\d+)\s*month/i);
|
|
119
|
-
if (lower === 'today') {
|
|
120
|
-
const d = new Date(now);
|
|
121
|
-
d.setHours(0, 0, 0, 0);
|
|
122
|
-
return d;
|
|
123
|
-
}
|
|
124
|
-
if (lower === 'yesterday')
|
|
125
|
-
return new Date(now.getTime() - 86400000);
|
|
126
|
-
if (lower === 'thisweek' || lower === 'this week') {
|
|
127
|
-
const d = new Date(now);
|
|
128
|
-
d.setDate(d.getDate() - d.getDay());
|
|
129
|
-
d.setHours(0, 0, 0, 0);
|
|
130
|
-
return d;
|
|
131
|
-
}
|
|
132
|
-
if (lower === 'lastweek' || lower === 'last week') {
|
|
133
|
-
const d = new Date(now);
|
|
134
|
-
d.setDate(d.getDate() - d.getDay() - 7);
|
|
135
|
-
return d;
|
|
136
|
-
}
|
|
137
|
-
if (dayMatch)
|
|
138
|
-
return new Date(now.getTime() - parseInt(dayMatch[1]) * 86400000);
|
|
139
|
-
if (weekMatch)
|
|
140
|
-
return new Date(now.getTime() - parseInt(weekMatch[1]) * 604800000);
|
|
141
|
-
if (monthMatch)
|
|
142
|
-
return new Date(now.getTime() - parseInt(monthMatch[1]) * 2592000000);
|
|
143
|
-
return null;
|
|
144
|
-
}
|
|
145
|
-
function filterByDateRange(items, since, until) {
|
|
146
|
-
const sinceDate = parseDate(since || '');
|
|
147
|
-
const untilDate = parseDate(until || '');
|
|
148
|
-
return items.filter(item => {
|
|
149
|
-
if (!item.createdAt)
|
|
150
|
-
return true;
|
|
151
|
-
const created = new Date(item.createdAt);
|
|
152
|
-
if (sinceDate && created < sinceDate)
|
|
153
|
-
return false;
|
|
154
|
-
if (untilDate && created > untilDate)
|
|
155
|
-
return false;
|
|
156
|
-
return true;
|
|
157
|
-
});
|
|
158
|
-
}
|
|
159
|
-
// Load plugin manifest for self-verification
|
|
160
|
-
function loadPluginManifest() {
|
|
161
|
-
try {
|
|
162
|
-
const manifestPath = path.join(getDefaultProjectPath(), 'config', 'plugin-manifest.json');
|
|
163
|
-
if (fs.existsSync(manifestPath)) {
|
|
164
|
-
return JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
catch (error) {
|
|
168
|
-
logger.warn('Could not load plugin manifest:', error?.message || error);
|
|
169
|
-
}
|
|
170
|
-
return null;
|
|
171
|
-
}
|
|
172
|
-
function verifyManifest(manifest) {
|
|
173
|
-
if (!manifest) {
|
|
174
|
-
return { ok: false, errors: ['Manifest not found'] };
|
|
175
|
-
}
|
|
176
|
-
const errors = [];
|
|
177
|
-
const required = ['id', 'name', 'version', 'capabilities', 'targets', 'dependencies'];
|
|
178
|
-
required.forEach((field) => {
|
|
179
|
-
if (!manifest[field]) {
|
|
180
|
-
errors.push(`Missing required field: ${field}`);
|
|
181
|
-
}
|
|
182
|
-
});
|
|
183
|
-
if (manifest.version !== VERSION) {
|
|
184
|
-
errors.push(`Version mismatch: manifest=${manifest.version}, binary=${VERSION}`);
|
|
185
|
-
}
|
|
186
|
-
const expectedTargets = ['claude-code', 'openclaw', 'opencode', 'codex', 'cursor', 'vscode', 'windsurf'];
|
|
187
|
-
expectedTargets.forEach((target) => {
|
|
188
|
-
if (!manifest.targets[target]) {
|
|
189
|
-
errors.push(`Missing target: ${target}`);
|
|
190
|
-
}
|
|
191
|
-
});
|
|
192
|
-
return { ok: errors.length === 0, errors };
|
|
193
|
-
}
|
|
194
|
-
async function buildHealthStatus() {
|
|
195
|
-
const dbHealth = await checkDatabaseHealth();
|
|
196
|
-
const redisHealth = await checkRedisHealth();
|
|
197
|
-
const database = dbHealth ? 'ok' : 'error';
|
|
198
|
-
const cache = config.redisEnabled ? (redisHealth ? 'ok' : 'error') : 'unavailable';
|
|
199
|
-
const overallStatus = config.redisEnabled
|
|
200
|
-
? determineOverallStatus(database, redisHealth)
|
|
201
|
-
: (dbHealth ? 'ok' : 'error');
|
|
202
|
-
return {
|
|
203
|
-
ok: overallStatus === 'ok',
|
|
204
|
-
version: VERSION,
|
|
205
|
-
mode: config.isTeamMode ? 'team' : 'local',
|
|
206
|
-
database,
|
|
207
|
-
cache,
|
|
208
|
-
dataDirectory: config.dataDir,
|
|
209
|
-
dataDirectoryExists: fs.existsSync(config.dataDir),
|
|
210
|
-
status: overallStatus,
|
|
211
|
-
timestamp: new Date().toISOString(),
|
|
212
|
-
};
|
|
213
|
-
}
|
|
214
|
-
// HELPER FUNCTIONS
|
|
215
|
-
// ============================================================================
|
|
216
|
-
function showHelp() {
|
|
217
|
-
console.log(`
|
|
218
|
-
Squish Memory v${VERSION} - Universal Memory Plugin System
|
|
219
|
-
|
|
220
|
-
Usage:
|
|
221
|
-
squish Start interactive wizard
|
|
222
|
-
squish run mcp Start MCP server
|
|
223
|
-
squish run web Start Web UI only
|
|
224
|
-
squish <command> [options] Run CLI commands for agents
|
|
225
|
-
|
|
226
|
-
CLI Commands (for agents):
|
|
227
|
-
squish config [action] Manage Squish configuration
|
|
228
|
-
squish remember <content> Store a memory
|
|
229
|
-
squish note <content> Save a quick note
|
|
230
|
-
squish learn <type> <text> Record learning: success, failure, fix, insight
|
|
231
|
-
squish search <query> Search memories (--pretty for human output)
|
|
232
|
-
squish recall <query> Search or get by ID (--pretty for human output)
|
|
233
|
-
squish recent --period Recent memories (today/yesterday/thisweek/7days/30days)
|
|
234
|
-
squish update <memoryId> Update memory
|
|
235
|
-
squish forget <memoryId> Delete memory (single or bulk with --older-than --search)
|
|
236
|
-
squish pin <memoryId> Pin/unpin memory
|
|
237
|
-
squish confidence <id> Set confidence
|
|
238
|
-
squish tag <action> Manage tags
|
|
239
|
-
squish stale Show stale memories
|
|
240
|
-
squish link <action> Manage links (find/add/list)
|
|
241
|
-
squish migrate Migrate memories between .squish directories
|
|
242
|
-
squish clean Dedup + consolidate (maintenance)
|
|
243
|
-
squish context Show context or list projects
|
|
244
|
-
squish stats View memory statistics
|
|
245
|
-
|
|
246
|
-
Examples:
|
|
247
|
-
squish run mcp # Start MCP server (for agents)
|
|
248
|
-
squish run web # Start Web UI only
|
|
249
|
-
squish config set project /repo/path
|
|
250
|
-
squish remember "Hello" # Store memory
|
|
251
|
-
squish note "Ship v1 first" # Save a quick note
|
|
252
|
-
squish learn observation "Updated auth flow" --action edit
|
|
253
|
-
squish learn fix "Patched auth middleware" --target middleware.ts
|
|
254
|
-
squish search "query" # Search memories
|
|
255
|
-
squish context --list-projects
|
|
256
|
-
squish clean # Run deduplication and consolidation
|
|
257
|
-
|
|
258
|
-
For more info: https://github.com/michielhdoteth/squish
|
|
259
|
-
`);
|
|
260
|
-
}
|
|
261
|
-
async function runInteractiveInstaller() {
|
|
262
|
-
const { select } = await import('@clack/prompts');
|
|
263
|
-
const { isCancel } = await import('@clack/prompts');
|
|
264
|
-
const { log } = await import('@clack/prompts');
|
|
265
|
-
const { intro, outro } = await import('@clack/prompts');
|
|
266
|
-
intro(`Squish Memory v${VERSION}`);
|
|
267
|
-
const options = [
|
|
268
|
-
{ value: 'mcp', label: 'Start MCP Server (for AI Assistants: Claude Code, OpenCode, etc.)' },
|
|
269
|
-
{ value: 'web', label: 'Start Web UI Only' },
|
|
270
|
-
{ value: 'health', label: 'Health & Stats' },
|
|
271
|
-
{ value: 'help', label: 'Show Help' },
|
|
272
|
-
{ value: 'exit', label: 'Exit' }
|
|
273
|
-
];
|
|
274
|
-
const selected = await select({
|
|
275
|
-
message: 'What would you like to do?',
|
|
276
|
-
options: options,
|
|
277
|
-
});
|
|
278
|
-
if (isCancel(selected)) {
|
|
279
|
-
outro('Cancelled');
|
|
280
|
-
process.exit(0);
|
|
281
|
-
return;
|
|
282
|
-
}
|
|
283
|
-
switch (selected) {
|
|
284
|
-
case 'mcp':
|
|
285
|
-
log.step('Starting MCP server...');
|
|
286
|
-
const { spawn } = await import('child_process');
|
|
287
|
-
spawn('npx', ['squish-mcp'], { stdio: 'inherit', shell: true });
|
|
288
|
-
break;
|
|
289
|
-
case 'web':
|
|
290
|
-
log.step('Starting Web UI...');
|
|
291
|
-
await runWebOnly();
|
|
292
|
-
break;
|
|
293
|
-
case 'health':
|
|
294
|
-
log.step('Checking health...');
|
|
295
|
-
await runCliCommand('health');
|
|
296
|
-
await runCliCommand('stats');
|
|
297
|
-
break;
|
|
298
|
-
case 'help':
|
|
299
|
-
showHelp();
|
|
300
|
-
process.exit(0);
|
|
301
|
-
break;
|
|
302
|
-
case 'exit':
|
|
303
|
-
outro('Goodbye! 👋');
|
|
304
|
-
process.exit(0);
|
|
305
|
-
break;
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
async function runCliCommand(command) {
|
|
309
|
-
// Run CLI command programmatically
|
|
310
|
-
const program = new Command();
|
|
311
|
-
program.hook('preAction', async () => {
|
|
312
|
-
await ensureDataDirectory();
|
|
313
|
-
});
|
|
314
|
-
if (command === 'health') {
|
|
315
|
-
const status = await buildHealthStatus();
|
|
316
|
-
console.log(`\n Squish Memory v${VERSION}`);
|
|
317
|
-
console.log(` ====================`);
|
|
318
|
-
console.log(` Mode: ${status.mode}`);
|
|
319
|
-
console.log(` Database: ${status.database}`);
|
|
320
|
-
console.log(` Cache: ${status.cache}`);
|
|
321
|
-
console.log(` Data Dir: ${status.dataDirectory}`);
|
|
322
|
-
console.log(` Status: ${status.ok ? 'HEALTHY' : 'UNHEALTHY'}\n`);
|
|
323
|
-
}
|
|
324
|
-
else if (command === 'stats') {
|
|
325
|
-
const stats = await getMemoryStats(getDefaultProjectPath());
|
|
326
|
-
console.log(JSON.stringify({ ok: true, ...stats }, null, 2));
|
|
327
|
-
}
|
|
328
|
-
}
|
|
329
|
-
async function spawnInstallerWizard() {
|
|
330
|
-
const distDir = path.dirname(fileURLToPath(import.meta.url));
|
|
331
|
-
const packageDir = path.dirname(distDir);
|
|
332
|
-
const installScript = path.join(packageDir, 'scripts', 'install-interactive.mjs');
|
|
333
|
-
if (!fs.existsSync(installScript)) {
|
|
334
|
-
console.error('Installer not found at:', installScript);
|
|
335
|
-
process.exit(1);
|
|
336
|
-
}
|
|
337
|
-
console.log('\nLaunching full installer wizard...\n');
|
|
338
|
-
const result = spawnSync('node', [`"${installScript}"`], {
|
|
339
|
-
stdio: 'inherit',
|
|
340
|
-
shell: true,
|
|
341
|
-
cwd: packageDir
|
|
342
|
-
});
|
|
343
|
-
process.exit(result.status || 0);
|
|
344
|
-
}
|
|
345
|
-
function isDatabaseInitialized() {
|
|
346
|
-
try {
|
|
347
|
-
const dataDir = getDataDir();
|
|
348
|
-
const dbPath = path.join(dataDir, 'squish.db');
|
|
349
|
-
return existsSync(dataDir) && existsSync(dbPath);
|
|
350
|
-
}
|
|
351
|
-
catch (error) {
|
|
352
|
-
return false;
|
|
353
|
-
}
|
|
354
|
-
}
|
|
355
|
-
async function runWebOnly() {
|
|
356
|
-
console.log(`[squish] Starting Web UI only...`);
|
|
357
|
-
await ensureDataDirectory();
|
|
358
|
-
await startWebServer();
|
|
359
|
-
}
|
|
360
|
-
// CLI MODE DETECTION
|
|
361
|
-
// ============================================================================
|
|
362
|
-
const args = process.argv.slice(2);
|
|
363
|
-
const firstArg = args[0];
|
|
364
|
-
// Detect command type
|
|
365
|
-
const isNoArgs = args.length === 0;
|
|
366
|
-
const isRunCommand = firstArg === 'run';
|
|
367
|
-
const isHelpCommand = firstArg === '--help' || firstArg === '-h' || firstArg === 'help';
|
|
368
|
-
if (isNoArgs) {
|
|
369
|
-
// Check if database exists - if not, run installer automatically
|
|
370
|
-
if (!isDatabaseInitialized()) {
|
|
371
|
-
console.log(`[squish] No existing database found. Launching installer wizard...\n`);
|
|
372
|
-
await spawnInstallerWizard();
|
|
373
|
-
}
|
|
374
|
-
else {
|
|
375
|
-
// INTERACTIVE WIZARD (default when no args) ===
|
|
376
|
-
runInteractiveInstaller().catch((e) => {
|
|
377
|
-
console.error('Installer error:', e.message);
|
|
378
|
-
process.exit(1);
|
|
379
|
-
});
|
|
380
|
-
}
|
|
381
|
-
}
|
|
382
|
-
else if (isRunCommand) {
|
|
383
|
-
// RUN SUBCOMMAND ===
|
|
384
|
-
const subcommand = args[1];
|
|
385
|
-
if (subcommand === 'mcp') {
|
|
386
|
-
(async () => {
|
|
387
|
-
try {
|
|
388
|
-
// Initialize data directory before starting MCP
|
|
389
|
-
await ensureDataDirectory();
|
|
390
|
-
// Start MCP server as child process (stdio mode for agents)
|
|
391
|
-
const mcpProcess = spawn('npx', ['squish-mcp'], {
|
|
392
|
-
stdio: 'inherit',
|
|
393
|
-
shell: true
|
|
394
|
-
});
|
|
395
|
-
// Forward MCP exit code when it exits
|
|
396
|
-
mcpProcess.on('exit', (code) => {
|
|
397
|
-
process.exit(code ?? 0);
|
|
398
|
-
});
|
|
399
|
-
// Clean shutdown: forward signals to MCP child
|
|
400
|
-
const cleanup = () => {
|
|
401
|
-
mcpProcess.kill('SIGTERM');
|
|
402
|
-
setTimeout(() => process.exit(0), 100);
|
|
403
|
-
};
|
|
404
|
-
process.on('SIGINT', cleanup);
|
|
405
|
-
process.on('SIGTERM', cleanup);
|
|
406
|
-
}
|
|
407
|
-
catch (error) {
|
|
408
|
-
console.error('[squish] Failed to start MCP server:', error.message);
|
|
409
|
-
process.exit(1);
|
|
410
|
-
}
|
|
411
|
-
})();
|
|
412
|
-
}
|
|
413
|
-
else if (subcommand === 'web') {
|
|
414
|
-
runWebOnly().catch((e) => {
|
|
415
|
-
logger.error('Web server error', e);
|
|
416
|
-
process.exit(1);
|
|
417
|
-
});
|
|
418
|
-
}
|
|
419
|
-
else {
|
|
420
|
-
console.log(`
|
|
421
|
-
Usage: squish run <command>
|
|
422
|
-
|
|
423
|
-
Commands:
|
|
424
|
-
mcp Start MCP server (for agents like Claude Code)
|
|
425
|
-
web Start Web UI only
|
|
426
|
-
|
|
427
|
-
Examples:
|
|
428
|
-
squish run mcp # Start MCP server (agents connect automatically)
|
|
429
|
-
squish run web # Start Web UI at http://localhost:37777
|
|
430
|
-
`);
|
|
431
|
-
process.exit(subcommand ? 1 : 0);
|
|
432
|
-
}
|
|
433
|
-
}
|
|
434
|
-
else if (isHelpCommand) {
|
|
435
|
-
// SHOW HELP ===
|
|
436
|
-
showHelp();
|
|
437
|
-
process.exit(0);
|
|
438
|
-
}
|
|
439
|
-
else {
|
|
440
|
-
// CLI MODE (for agents/OpenClaw) ===
|
|
441
|
-
runCliMode().catch((e) => {
|
|
442
|
-
console.error(JSON.stringify({ error: e.message }, null, 2));
|
|
443
|
-
process.exit(1);
|
|
444
|
-
});
|
|
445
|
-
}
|
|
446
|
-
// CLI MODE (for OpenClaw bash execution)
|
|
447
|
-
// ============================================================================
|
|
448
|
-
async function runCliMode() {
|
|
449
|
-
const program = new Command();
|
|
450
|
-
program
|
|
451
|
-
.name('squish')
|
|
452
|
-
.description('Squish - Persistent memory for AI assistants')
|
|
453
|
-
.version(VERSION);
|
|
454
|
-
// Initialize data directory before any command
|
|
455
|
-
program.hook('preAction', async () => {
|
|
456
|
-
await ensureDataDirectory();
|
|
457
|
-
});
|
|
458
|
-
// squish config [get] [key] or squish config set <key> <value>
|
|
459
|
-
program
|
|
460
|
-
.command('config')
|
|
461
|
-
.description('Manage Squish configuration')
|
|
462
|
-
.argument('[action]', 'get, set, or list', 'list')
|
|
463
|
-
.argument('[key]', 'Config key (e.g., project)')
|
|
464
|
-
.argument('[value]', 'Config value (for set action)')
|
|
465
|
-
.action(async (action, key, value) => {
|
|
466
|
-
const userConfig = loadUserConfig();
|
|
467
|
-
if (action === 'set') {
|
|
468
|
-
if (!key || value === undefined) {
|
|
469
|
-
console.log(JSON.stringify({ ok: false, error: 'Usage: squish config set <key> <value>' }, null, 2));
|
|
470
|
-
process.exit(1);
|
|
471
|
-
}
|
|
472
|
-
userConfig[key] = value;
|
|
473
|
-
saveUserConfig(userConfig);
|
|
474
|
-
console.log(JSON.stringify({ ok: true, message: `Set ${key} = ${value}` }, null, 2));
|
|
475
|
-
}
|
|
476
|
-
else if (action === 'get') {
|
|
477
|
-
if (!key) {
|
|
478
|
-
console.log(JSON.stringify({ ok: false, error: 'Usage: squish config get <key>' }, null, 2));
|
|
479
|
-
process.exit(1);
|
|
480
|
-
}
|
|
481
|
-
console.log(JSON.stringify({ ok: true, [key]: userConfig[key] || null }, null, 2));
|
|
482
|
-
}
|
|
483
|
-
else {
|
|
484
|
-
console.log(JSON.stringify({ ok: true, config: userConfig }, null, 2));
|
|
485
|
-
}
|
|
486
|
-
});
|
|
487
|
-
// squish mount /path/to/folder - Enable external memory
|
|
488
|
-
program
|
|
489
|
-
.command('mount')
|
|
490
|
-
.description('Mount an external folder as memory storage')
|
|
491
|
-
.argument('[path]', 'Path to external folder (or "status" or "unmount")')
|
|
492
|
-
.action(async (pathOrAction) => {
|
|
493
|
-
const { getExternalMemory } = await import('./core/external-folder/index.js');
|
|
494
|
-
const externalMemory = getExternalMemory();
|
|
495
|
-
if (pathOrAction === 'status') {
|
|
496
|
-
// Show mount status
|
|
497
|
-
const status = await externalMemory.getStatus();
|
|
498
|
-
console.log(JSON.stringify({ ok: true, status }, null, 2));
|
|
499
|
-
}
|
|
500
|
-
else if (pathOrAction === 'unmount') {
|
|
501
|
-
// Unmount
|
|
502
|
-
externalMemory.unmount();
|
|
503
|
-
console.log(JSON.stringify({ ok: true, message: 'External memory unmounted' }, null, 2));
|
|
504
|
-
}
|
|
505
|
-
else if (pathOrAction) {
|
|
506
|
-
// Mount at path
|
|
507
|
-
const result = await externalMemory.mount(pathOrAction);
|
|
508
|
-
if (result.success) {
|
|
509
|
-
console.log(JSON.stringify({ ok: true, message: `Mounted at ${pathOrAction}` }, null, 2));
|
|
510
|
-
}
|
|
511
|
-
else {
|
|
512
|
-
console.log(JSON.stringify({ ok: false, error: result.error }, null, 2));
|
|
513
|
-
}
|
|
514
|
-
}
|
|
515
|
-
else {
|
|
516
|
-
console.log(JSON.stringify({ ok: false, error: 'Usage: squish mount <path> or squish mount status' }, null, 2));
|
|
517
|
-
}
|
|
518
|
-
});
|
|
519
|
-
// squish remember "content" --type fact --tags tag1,tag2
|
|
520
|
-
program
|
|
521
|
-
.command('remember <content>')
|
|
522
|
-
.description('Store a memory')
|
|
523
|
-
.option('-t, --type <type>', 'Memory type (observation, fact, decision, context, preference)', 'observation')
|
|
524
|
-
.option('-T, --tags <tags>', 'Comma-separated tags', '')
|
|
525
|
-
.option('-p, --project <project>', 'Project path', getDefaultProjectPath())
|
|
526
|
-
.option('-s, --source <source>', 'Source of this memory (e.g., "voice", "chat", "document")')
|
|
527
|
-
.option('-r, --reasoning <reasoning>', 'Why this memory is important')
|
|
528
|
-
.option('-c, --context <context>', 'What triggered this memory')
|
|
529
|
-
.option('-e, --examples <examples>', 'When to apply this knowledge')
|
|
530
|
-
.option('-x, --exceptions <exceptions>', 'When NOT to apply this')
|
|
531
|
-
.option('-m, --memory', 'Store as markdown file in .squish/memory/ (not in database)', false)
|
|
532
|
-
.option('-H, --hot', 'Store in hot tier (active, high priority) in database', false)
|
|
533
|
-
.option('-C, --cold', 'Store in cold tier (archived, lower priority) in database', false)
|
|
534
|
-
.action(async (content, options) => {
|
|
535
|
-
try {
|
|
536
|
-
// Markdown file storage (not in database)
|
|
537
|
-
if (options.memory) {
|
|
538
|
-
const { saveToMarkdown } = await import('./core/memory/markdown/markdown-storage.js');
|
|
539
|
-
const memoryFile = await saveToMarkdown({
|
|
540
|
-
content,
|
|
541
|
-
type: options.type,
|
|
542
|
-
tags: options.tags ? options.tags.split(',').map((t) => t.trim()) : [],
|
|
543
|
-
project: options.project,
|
|
544
|
-
source: options.source,
|
|
545
|
-
reasoning: options.reasoning,
|
|
546
|
-
memoryContext: options.context,
|
|
547
|
-
examples: options.examples,
|
|
548
|
-
exceptions: options.exceptions,
|
|
549
|
-
});
|
|
550
|
-
// Trigger hooks
|
|
551
|
-
const { triggerMemoryCreated } = await import('./core/memory/hooks.js');
|
|
552
|
-
await triggerMemoryCreated({
|
|
553
|
-
memoryId: memoryFile.id,
|
|
554
|
-
content: memoryFile.content,
|
|
555
|
-
type: memoryFile.type,
|
|
556
|
-
tags: memoryFile.tags,
|
|
557
|
-
project: memoryFile.project,
|
|
558
|
-
source: memoryFile.source,
|
|
559
|
-
tier: 'hot',
|
|
560
|
-
});
|
|
561
|
-
console.log(JSON.stringify({ ok: true, memory: true, ...memoryFile }, null, 2));
|
|
562
|
-
return;
|
|
563
|
-
}
|
|
564
|
-
// Database storage: determine tier
|
|
565
|
-
const tier = options.cold ? 'cold' : 'hot';
|
|
566
|
-
const result = await rememberMemory({
|
|
567
|
-
content,
|
|
568
|
-
type: options.type,
|
|
569
|
-
tags: options.tags ? options.tags.split(',').map((t) => t.trim()) : [],
|
|
570
|
-
project: options.project,
|
|
571
|
-
source: options.source,
|
|
572
|
-
reasoning: options.reasoning,
|
|
573
|
-
memoryContext: options.context,
|
|
574
|
-
examples: options.examples,
|
|
575
|
-
exceptions: options.exceptions,
|
|
576
|
-
tier,
|
|
577
|
-
});
|
|
578
|
-
// Trigger hooks for DB storage
|
|
579
|
-
const { triggerMemoryCreated } = await import('./core/memory/hooks.js');
|
|
580
|
-
await triggerMemoryCreated({
|
|
581
|
-
memoryId: result.id,
|
|
582
|
-
content: result.content,
|
|
583
|
-
type: result.type,
|
|
584
|
-
tags: result.tags,
|
|
585
|
-
project: result.projectId || undefined,
|
|
586
|
-
source: options.source || 'cli',
|
|
587
|
-
tier,
|
|
588
|
-
});
|
|
589
|
-
console.log(JSON.stringify({ ok: true, ...result }, null, 2));
|
|
590
|
-
}
|
|
591
|
-
catch (error) {
|
|
592
|
-
console.log(JSON.stringify({ ok: false, error: error.message }, null, 2));
|
|
593
|
-
process.exit(1);
|
|
594
|
-
}
|
|
595
|
-
});
|
|
596
|
-
// squish search "query" --type fact --limit 10 --since "3 days ago" --place workshop
|
|
597
|
-
program
|
|
598
|
-
.command('search <query>')
|
|
599
|
-
.description('Search memories')
|
|
600
|
-
.option('-t, --type <type>', 'Filter by memory type')
|
|
601
|
-
.option('-l, --limit <number>', 'Max results', '10')
|
|
602
|
-
.option('-p, --project <project>', 'Project path', getDefaultProjectPath())
|
|
603
|
-
.option('-s, --since <date>', 'Filter: created after this date (e.g., "3 days ago", "2026-01-01")')
|
|
604
|
-
.option('-u, --until <date>', 'Filter: created before this date (e.g., "yesterday", "2026-01-15")')
|
|
605
|
-
.option('-P, --pretty', 'Human-friendly output', false)
|
|
606
|
-
.option('-m, --memory', 'Search memory files instead of database', false)
|
|
607
|
-
.option('--place <type>', 'Filter by place type: entry_hall, library, workshop, lab, office, garden, archive')
|
|
608
|
-
.action(async (query, options) => {
|
|
609
|
-
try {
|
|
610
|
-
// Markdown file search
|
|
611
|
-
if (options.memory) {
|
|
612
|
-
const { getMarkdownMemories } = await import('./core/memory/markdown/markdown-storage.js');
|
|
613
|
-
const memoryFiles = await getMarkdownMemories({
|
|
614
|
-
type: options.type,
|
|
615
|
-
project: options.project,
|
|
616
|
-
});
|
|
617
|
-
// Simple text search (can be enhanced with QMD later)
|
|
618
|
-
const searchLower = query.toLowerCase();
|
|
619
|
-
const filtered = memoryFiles.filter(m => m.content.toLowerCase().includes(searchLower) ||
|
|
620
|
-
m.tags.some(t => t.toLowerCase().includes(searchLower))).slice(0, validateLimit(options.limit, 10, 1, 100));
|
|
621
|
-
if (options.pretty) {
|
|
622
|
-
console.log(`\n Memory Search: "${query}"`);
|
|
623
|
-
console.log(` Found ${filtered.length} results:\n`);
|
|
624
|
-
filtered.forEach((r, i) => {
|
|
625
|
-
console.log(` ${i + 1}. [${r.type || 'memory'}] ${(r.content || '').substring(0, 60)}...`);
|
|
626
|
-
});
|
|
627
|
-
console.log('');
|
|
628
|
-
}
|
|
629
|
-
else {
|
|
630
|
-
console.log(JSON.stringify({ ok: true, query, source: 'memory', count: filtered.length, results: filtered }, null, 2));
|
|
631
|
-
}
|
|
632
|
-
return;
|
|
633
|
-
}
|
|
634
|
-
// Database search
|
|
635
|
-
const results = await search({
|
|
636
|
-
query,
|
|
637
|
-
type: options.type,
|
|
638
|
-
limit: validateLimit(options.limit, 10, 1, 100) * 2,
|
|
639
|
-
project: options.project,
|
|
640
|
-
});
|
|
641
|
-
const filtered = filterByDateRange(results, options.since, options.until);
|
|
642
|
-
let limited = filtered.slice(0, validateLimit(options.limit, 10, 1, 100));
|
|
643
|
-
// Add place info to results
|
|
644
|
-
const { getMemoryPlace } = await import('./core/places/index.js');
|
|
645
|
-
const limitedWithPlace = await Promise.all(limited.map(async (r) => {
|
|
646
|
-
const placeId = await getMemoryPlace(r.id);
|
|
647
|
-
return { ...r, placeId };
|
|
648
|
-
}));
|
|
649
|
-
// Filter by place if specified
|
|
650
|
-
if (options.place) {
|
|
651
|
-
const placeFiltered = [];
|
|
652
|
-
for (const r of limitedWithPlace) {
|
|
653
|
-
if (r.placeId) {
|
|
654
|
-
const { getPlace } = await import('./core/places/index.js');
|
|
655
|
-
const place = await getPlace(r.placeId);
|
|
656
|
-
if (place && place.placeType === options.place) {
|
|
657
|
-
placeFiltered.push({ ...r, place: place.name || null, placeType: place.placeType || null });
|
|
658
|
-
}
|
|
659
|
-
}
|
|
660
|
-
}
|
|
661
|
-
limited = placeFiltered;
|
|
662
|
-
}
|
|
663
|
-
else if (limitedWithPlace.length > 0) {
|
|
664
|
-
// Add place info to results even without filter
|
|
665
|
-
for (const r of limitedWithPlace) {
|
|
666
|
-
if (r.placeId) {
|
|
667
|
-
const { getPlace } = await import('./core/places/index.js');
|
|
668
|
-
const place = await getPlace(r.placeId);
|
|
669
|
-
if (place) {
|
|
670
|
-
r.place = place.name || null;
|
|
671
|
-
r.placeType = place.placeType || null;
|
|
672
|
-
}
|
|
673
|
-
}
|
|
674
|
-
}
|
|
675
|
-
limited = limitedWithPlace;
|
|
676
|
-
}
|
|
677
|
-
if (options.pretty) {
|
|
678
|
-
console.log(`\n Search: "${query}"`);
|
|
679
|
-
console.log(` Found ${limited.length} results:\n`);
|
|
680
|
-
limited.forEach((r, i) => {
|
|
681
|
-
const placeTag = r.place ? ` (${r.place})` : '';
|
|
682
|
-
console.log(` ${i + 1}. [${r.type || 'memory'}] ${(r.content || '').substring(0, 60)}...${placeTag}`);
|
|
683
|
-
});
|
|
684
|
-
console.log('');
|
|
685
|
-
}
|
|
686
|
-
else {
|
|
687
|
-
console.log(JSON.stringify({ ok: true, query, count: limited.length, since: options.since, until: options.until, placeFilter: options.place || null, results: limited }, null, 2));
|
|
688
|
-
}
|
|
689
|
-
}
|
|
690
|
-
catch (error) {
|
|
691
|
-
console.log(JSON.stringify({ ok: false, error: error.message }, null, 2));
|
|
692
|
-
process.exit(1);
|
|
693
|
-
}
|
|
694
|
-
});
|
|
695
|
-
// squish forget <memoryId> -- Delete single or bulk delete memories
|
|
696
|
-
program
|
|
697
|
-
.command('forget [memoryId]')
|
|
698
|
-
.description('Delete a memory by ID, or bulk delete with filters')
|
|
699
|
-
.option('-o, --older-than <date>', 'Bulk delete memories older than (e.g., "30 days", "6 months")')
|
|
700
|
-
.option('-t, --type <type>', 'Filter by memory type')
|
|
701
|
-
.option('-s, --search <query>', 'Search query to match specific memories')
|
|
702
|
-
.option('-c, --confirm', 'Actually delete (default is dry-run)', false)
|
|
703
|
-
.option('-l, --limit <number>', 'Max memories to delete', '100')
|
|
704
|
-
.option('-p, --project <project>', 'Project path', getDefaultProjectPath())
|
|
705
|
-
.action(async (memoryId, options) => {
|
|
706
|
-
try {
|
|
707
|
-
// Single memory deletion
|
|
708
|
-
if (memoryId) {
|
|
709
|
-
const db = await getDb();
|
|
710
|
-
const schema = await getSchema();
|
|
711
|
-
const sqliteDb = db;
|
|
712
|
-
// Get memory content before deleting for hook
|
|
713
|
-
const [memory] = await sqliteDb.select().from(schema.memories).where(eq(schema.memories.id, memoryId));
|
|
714
|
-
await sqliteDb.delete(schema.memories).where(eq(schema.memories.id, memoryId));
|
|
715
|
-
// Trigger memoryDeleted hook
|
|
716
|
-
if (memory) {
|
|
717
|
-
const { triggerMemoryDeleted } = await import('./core/memory/hooks.js');
|
|
718
|
-
await triggerMemoryDeleted({
|
|
719
|
-
memoryId: memory.id,
|
|
720
|
-
content: memory.content,
|
|
721
|
-
type: memory.type,
|
|
722
|
-
tags: typeof memory.tags === 'string' ? memory.tags.split(',') : [],
|
|
723
|
-
project: memory.projectId || undefined,
|
|
724
|
-
source: memory.source || undefined,
|
|
725
|
-
tier: memory.tier,
|
|
726
|
-
});
|
|
727
|
-
}
|
|
728
|
-
console.log(JSON.stringify({ ok: true, message: `Memory ${memoryId} deleted` }, null, 2));
|
|
729
|
-
return;
|
|
730
|
-
}
|
|
731
|
-
// Bulk deletion
|
|
732
|
-
if (!options.olderThan && !options.search) {
|
|
733
|
-
console.log(JSON.stringify({ ok: false, error: 'Provide memory ID or use --older-than / --search for bulk delete' }, null, 2));
|
|
734
|
-
process.exit(1);
|
|
735
|
-
}
|
|
736
|
-
const query = options.search || '';
|
|
737
|
-
const limit = validateLimit(options.limit, 100, 1, 100);
|
|
738
|
-
const results = await search({ query, type: options.type, limit, project: options.project });
|
|
739
|
-
let filtered = results;
|
|
740
|
-
if (options.olderThan) {
|
|
741
|
-
filtered = filterByDateRange(results, '', options.olderThan);
|
|
742
|
-
}
|
|
743
|
-
const db = await getDb();
|
|
744
|
-
const schema = await getSchema();
|
|
745
|
-
const sqliteDb = db;
|
|
746
|
-
const deleted = [];
|
|
747
|
-
for (const mem of filtered) {
|
|
748
|
-
await sqliteDb.delete(schema.memories).where(eq(schema.memories.id, mem.id));
|
|
749
|
-
deleted.push(mem.id);
|
|
750
|
-
}
|
|
751
|
-
console.log(JSON.stringify({ ok: true, matched: filtered.length, deleted: deleted.length, dryRun: !options.confirm }, null, 2));
|
|
752
|
-
}
|
|
753
|
-
catch (error) {
|
|
754
|
-
console.log(JSON.stringify({ ok: false, error: error.message }, null, 2));
|
|
755
|
-
process.exit(1);
|
|
756
|
-
}
|
|
757
|
-
});
|
|
758
|
-
// squish link - Unified graph operations (find related, add links, list associations)
|
|
759
|
-
program
|
|
760
|
-
.command('link')
|
|
761
|
-
.description('Manage memory associations: find, add, list')
|
|
762
|
-
.argument('<action>', 'Action: find, add, or list')
|
|
763
|
-
.argument('[args...]', 'Additional arguments')
|
|
764
|
-
.option('-p, --project <project>', 'Project path', getDefaultProjectPath())
|
|
765
|
-
.action(async (action, args, options) => {
|
|
766
|
-
try {
|
|
767
|
-
// link find <memoryId> [--depth N] [--min-weight N]
|
|
768
|
-
if (action === 'find') {
|
|
769
|
-
const memoryId = args[0];
|
|
770
|
-
if (!memoryId) {
|
|
771
|
-
console.log(JSON.stringify({ ok: false, error: 'Usage: squish link find <memoryId> [--depth N] [--min-weight N]' }, null, 2));
|
|
772
|
-
process.exit(1);
|
|
773
|
-
}
|
|
774
|
-
const depth = validateLimit(args[1], 2, 1, 5);
|
|
775
|
-
const minWeight = parseFloat(args[2]) || 0.3;
|
|
776
|
-
const related = await getRelatedMemories(memoryId, depth * 5);
|
|
777
|
-
const filtered = related.filter((r) => r.weight >= minWeight);
|
|
778
|
-
console.log(JSON.stringify({ ok: true, count: filtered.length, related: filtered }, null, 2));
|
|
779
|
-
return;
|
|
780
|
-
}
|
|
781
|
-
// link add <fromId> <toId> <type>
|
|
782
|
-
if (action === 'add') {
|
|
783
|
-
const fromMemoryId = args[0];
|
|
784
|
-
const toMemoryId = args[1];
|
|
785
|
-
const type = args[2] || 'relates_to';
|
|
786
|
-
if (!fromMemoryId || !toMemoryId) {
|
|
787
|
-
console.log(JSON.stringify({ ok: false, error: 'Usage: squish link add <fromId> <toId> <type>' }, null, 2));
|
|
788
|
-
process.exit(1);
|
|
789
|
-
}
|
|
790
|
-
await createAssociation(fromMemoryId, toMemoryId, type, 0.5);
|
|
791
|
-
console.log(JSON.stringify({ ok: true, message: `Linked ${fromMemoryId} -> ${toMemoryId} (${type})` }, null, 2));
|
|
792
|
-
return;
|
|
793
|
-
}
|
|
794
|
-
// link list - List all associations
|
|
795
|
-
if (action === 'list') {
|
|
796
|
-
const db = await getDb();
|
|
797
|
-
const schema = await getSchema();
|
|
798
|
-
const sqliteDb = db;
|
|
799
|
-
const associations = await sqliteDb.select().from(schema.memoryAssociations).limit(100);
|
|
800
|
-
console.log(JSON.stringify({ ok: true, count: associations.length, associations }, null, 2));
|
|
801
|
-
return;
|
|
802
|
-
}
|
|
803
|
-
console.log(JSON.stringify({ ok: false, error: 'Usage: squish link <find|add|list> [args]' }, null, 2));
|
|
804
|
-
}
|
|
805
|
-
catch (error) {
|
|
806
|
-
console.log(JSON.stringify({ ok: false, error: error.message }, null, 2));
|
|
807
|
-
process.exit(1);
|
|
808
|
-
}
|
|
809
|
-
});
|
|
810
|
-
// squish learn <type> <content> - Record learning: success, failure, fix, or insight
|
|
811
|
-
program
|
|
812
|
-
.command('learn <type> <content>')
|
|
813
|
-
.description('Record learning: success, failure, fix, or insight')
|
|
814
|
-
.option('-c, --context <context>', 'Additional context about what happened')
|
|
815
|
-
.option('-a, --action <action>', 'Action performed')
|
|
816
|
-
.option('-t, --target <target>', 'Target file or resource')
|
|
817
|
-
.option('-m, --memory-id <memoryId>', 'Optional memory ID to link this learning to')
|
|
818
|
-
.option('-p, --project <project>', 'Project path', getDefaultProjectPath())
|
|
819
|
-
.action(async (type, content, options) => {
|
|
820
|
-
try {
|
|
821
|
-
const validTypes = ['success', 'failure', 'fix', 'insight'];
|
|
822
|
-
if (!validTypes.includes(type)) {
|
|
823
|
-
console.log(JSON.stringify({ ok: false, error: `Invalid type. Must be: ${validTypes.join(', ')}` }, null, 2));
|
|
824
|
-
process.exit(1);
|
|
825
|
-
}
|
|
826
|
-
const learning = await createLearning({
|
|
827
|
-
type: type,
|
|
828
|
-
content,
|
|
829
|
-
context: options.context,
|
|
830
|
-
action: options.action,
|
|
831
|
-
target: options.target,
|
|
832
|
-
project: options.project,
|
|
833
|
-
memoryId: options.memoryId,
|
|
834
|
-
});
|
|
835
|
-
console.log(JSON.stringify({ ok: true, learning }, null, 2));
|
|
836
|
-
}
|
|
837
|
-
catch (error) {
|
|
838
|
-
console.log(JSON.stringify({ ok: false, error: error.message }, null, 2));
|
|
839
|
-
process.exit(1);
|
|
840
|
-
}
|
|
841
|
-
});
|
|
842
|
-
program
|
|
843
|
-
.command('update <memoryId>')
|
|
844
|
-
.description('Update a memory')
|
|
845
|
-
.option('-c, --content <content>', 'New content')
|
|
846
|
-
.option('-t, --type <type>', 'New type (observation, fact, decision, context, preference)')
|
|
847
|
-
.option('-T, --tags <tags>', 'Comma-separated tags')
|
|
848
|
-
.action(async (memoryId, options) => {
|
|
849
|
-
try {
|
|
850
|
-
const updates = {};
|
|
851
|
-
if (options.content)
|
|
852
|
-
updates.content = options.content;
|
|
853
|
-
if (options.type)
|
|
854
|
-
updates.type = options.type;
|
|
855
|
-
if (options.tags)
|
|
856
|
-
updates.tags = serializeTags(options.tags.split(','));
|
|
857
|
-
const db = await getDb();
|
|
858
|
-
const schema = await getSchema();
|
|
859
|
-
const sqliteDb = db;
|
|
860
|
-
// Get old memory for hook
|
|
861
|
-
const [oldMemory] = await sqliteDb.select().from(schema.memories).where(eq(schema.memories.id, memoryId));
|
|
862
|
-
await sqliteDb.update(schema.memories).set(updates).where(eq(schema.memories.id, memoryId));
|
|
863
|
-
// Trigger memoryUpdated hook
|
|
864
|
-
if (oldMemory) {
|
|
865
|
-
const { triggerMemoryUpdated } = await import('./core/memory/hooks.js');
|
|
866
|
-
const newContent = options.content || oldMemory.content;
|
|
867
|
-
await triggerMemoryUpdated({
|
|
868
|
-
memoryId: oldMemory.id,
|
|
869
|
-
content: newContent,
|
|
870
|
-
type: options.type || oldMemory.type,
|
|
871
|
-
tags: options.tags ? options.tags.split(',') : (typeof oldMemory.tags === 'string' ? oldMemory.tags.split(',') : []),
|
|
872
|
-
project: oldMemory.projectId || undefined,
|
|
873
|
-
source: oldMemory.source || undefined,
|
|
874
|
-
tier: oldMemory.tier,
|
|
875
|
-
importance: oldMemory.importanceScore || oldMemory.relevanceScore || 50,
|
|
876
|
-
}, oldMemory.content);
|
|
877
|
-
}
|
|
878
|
-
console.log(JSON.stringify({ ok: true, message: `Memory ${memoryId} updated` }, null, 2));
|
|
879
|
-
}
|
|
880
|
-
catch (error) {
|
|
881
|
-
console.log(JSON.stringify({ ok: false, error: error.message }, null, 2));
|
|
882
|
-
process.exit(1);
|
|
883
|
-
}
|
|
884
|
-
});
|
|
885
|
-
// squish recall <query or memoryId> - Search or get by ID
|
|
886
|
-
program
|
|
887
|
-
.command('recall <query>')
|
|
888
|
-
.description('Search memories by query or get by ID (if UUID provided)')
|
|
889
|
-
.option('-l, --limit <number>', 'Max results', '5')
|
|
890
|
-
.option('-t, --type <type>', 'Filter by memory type')
|
|
891
|
-
.option('-p, --project <project>', 'Project path', getDefaultProjectPath())
|
|
892
|
-
.option('-s, --since <date>', 'Filter: created after this date (e.g., "3 days ago", "yesterday")')
|
|
893
|
-
.option('-u, --until <date>', 'Filter: created before this date (e.g., "today", "2026-01-15")')
|
|
894
|
-
.option('-P, --pretty', 'Human-friendly output', false)
|
|
895
|
-
.option('--place <type>', 'Filter by place type: entry_hall, library, workshop, lab, office, garden, archive')
|
|
896
|
-
.action(async (query, options) => {
|
|
897
|
-
try {
|
|
898
|
-
const isUUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(query);
|
|
899
|
-
if (isUUID) {
|
|
900
|
-
const memory = await getMemory(query);
|
|
901
|
-
// Add place info to single memory retrieval
|
|
902
|
-
if (memory) {
|
|
903
|
-
const { getMemoryPlace, getPlace } = await import('./core/places/index.js');
|
|
904
|
-
const placeId = await getMemoryPlace(memory.id);
|
|
905
|
-
if (placeId) {
|
|
906
|
-
const place = await getPlace(placeId);
|
|
907
|
-
memory.place = place?.name || null;
|
|
908
|
-
memory.placeType = place?.placeType || null;
|
|
909
|
-
}
|
|
910
|
-
}
|
|
911
|
-
if (options.pretty && memory) {
|
|
912
|
-
const placeInfo = memory.place ? ` (${memory.place})` : '';
|
|
913
|
-
console.log(`\n Memory: ${memory.id}`);
|
|
914
|
-
console.log(` Type: ${memory.type}`);
|
|
915
|
-
console.log(` Content: ${memory.content}\n`);
|
|
916
|
-
if (placeInfo)
|
|
917
|
-
console.log(` Place: ${memory.place}\n`);
|
|
918
|
-
}
|
|
919
|
-
else {
|
|
920
|
-
console.log(JSON.stringify({ ok: true, found: !!memory, memory }, null, 2));
|
|
921
|
-
}
|
|
922
|
-
}
|
|
923
|
-
else {
|
|
924
|
-
const results = await search({
|
|
925
|
-
query,
|
|
926
|
-
type: options.type,
|
|
927
|
-
limit: validateLimit(options.limit, 5, 1, 100) * 2,
|
|
928
|
-
project: options.project,
|
|
929
|
-
});
|
|
930
|
-
const filtered = filterByDateRange(results, options.since, options.until);
|
|
931
|
-
let limited = filtered.slice(0, validateLimit(options.limit, 5, 1, 100));
|
|
932
|
-
// Add place info to results
|
|
933
|
-
const { getMemoryPlace, getPlace } = await import('./core/places/index.js');
|
|
934
|
-
const limitedWithPlace = await Promise.all(limited.map(async (r) => {
|
|
935
|
-
const placeId = await getMemoryPlace(r.id);
|
|
936
|
-
let placeInfo = {};
|
|
937
|
-
if (placeId) {
|
|
938
|
-
const place = await getPlace(placeId);
|
|
939
|
-
placeInfo = { place: place?.name || null, placeType: place?.placeType || null };
|
|
940
|
-
}
|
|
941
|
-
return { ...r, ...placeInfo };
|
|
942
|
-
}));
|
|
943
|
-
// Filter by place if specified
|
|
944
|
-
if (options.place) {
|
|
945
|
-
limited = limitedWithPlace.filter((r) => r.placeType === options.place);
|
|
946
|
-
}
|
|
947
|
-
else {
|
|
948
|
-
limited = limitedWithPlace;
|
|
949
|
-
}
|
|
950
|
-
if (options.pretty) {
|
|
951
|
-
console.log(`\n Recall: "${query}"`);
|
|
952
|
-
console.log(` Found ${limited.length} matches:\n`);
|
|
953
|
-
limited.forEach((r, i) => {
|
|
954
|
-
const placeTag = r.place ? ` (${r.place})` : '';
|
|
955
|
-
console.log(` ${i + 1}. [${r.type || 'memory'}] ${(r.content || '').substring(0, 60)}...${placeTag} (${(r.similarity ?? 0).toFixed(2)})`);
|
|
956
|
-
});
|
|
957
|
-
console.log('');
|
|
958
|
-
}
|
|
959
|
-
else {
|
|
960
|
-
const matches = limited.map((r) => ({
|
|
961
|
-
id: r.id,
|
|
962
|
-
score: r.similarity ?? 0,
|
|
963
|
-
type: r.type,
|
|
964
|
-
content: r.content.length > 200 ? r.content.slice(0, 200) + '...' : r.content,
|
|
965
|
-
tags: r.tags,
|
|
966
|
-
place: r.place,
|
|
967
|
-
placeType: r.placeType,
|
|
968
|
-
}));
|
|
969
|
-
console.log(JSON.stringify({ ok: true, query, count: matches.length, since: options.since, until: options.until, placeFilter: options.place || null, matches }, null, 2));
|
|
970
|
-
}
|
|
971
|
-
}
|
|
972
|
-
}
|
|
973
|
-
catch (error) {
|
|
974
|
-
console.log(JSON.stringify({ ok: false, error: error.message }, null, 2));
|
|
975
|
-
process.exit(1);
|
|
976
|
-
}
|
|
977
|
-
});
|
|
978
|
-
// squish recent --period <period> - Show recent memories
|
|
979
|
-
program
|
|
980
|
-
.command('recent')
|
|
981
|
-
.description('Show recent memories by period')
|
|
982
|
-
.option('-p, --period <period>', 'Period: today, yesterday, thisweek, 7days, 30days, or custom like "3 days"', 'today')
|
|
983
|
-
.option('-s, --since <date>', 'Start date (alternative to --period)')
|
|
984
|
-
.option('-u, --until <date>', 'End date (alternative to --period)')
|
|
985
|
-
.option('-l, --limit <number>', 'Max results', '10')
|
|
986
|
-
.option('-P, --project <project>', 'Project path', getDefaultProjectPath())
|
|
987
|
-
.action(async (options) => {
|
|
988
|
-
try {
|
|
989
|
-
let since, until;
|
|
990
|
-
if (options.since && options.until) {
|
|
991
|
-
since = options.since;
|
|
992
|
-
until = options.until;
|
|
993
|
-
}
|
|
994
|
-
else if (options.since) {
|
|
995
|
-
since = options.since;
|
|
996
|
-
until = 'now';
|
|
997
|
-
}
|
|
998
|
-
else {
|
|
999
|
-
const periodMap = {
|
|
1000
|
-
today: ['today', 'now'],
|
|
1001
|
-
yesterday: ['yesterday', 'today'],
|
|
1002
|
-
thisweek: ['thisweek', 'now'],
|
|
1003
|
-
'7days': ['7 days', 'now'],
|
|
1004
|
-
'14days': ['14 days', 'now'],
|
|
1005
|
-
'30days': ['30 days', 'now'],
|
|
1006
|
-
'90days': ['90 days', 'now'],
|
|
1007
|
-
};
|
|
1008
|
-
const mapped = periodMap[options.period];
|
|
1009
|
-
if (mapped) {
|
|
1010
|
-
[since, until] = mapped;
|
|
1011
|
-
}
|
|
1012
|
-
else {
|
|
1013
|
-
since = options.period;
|
|
1014
|
-
until = 'now';
|
|
1015
|
-
}
|
|
1016
|
-
}
|
|
1017
|
-
const results = await getRecent(options.project, 100);
|
|
1018
|
-
const filtered = filterByDateRange(results, since, until);
|
|
1019
|
-
const limited = filtered.slice(0, validateLimit(options.limit, 10, 1, 100));
|
|
1020
|
-
console.log(JSON.stringify({ ok: true, period: options.period, since, until, count: limited.length, results: limited }, null, 2));
|
|
1021
|
-
}
|
|
1022
|
-
catch (error) {
|
|
1023
|
-
console.log(JSON.stringify({ ok: false, error: error.message }, null, 2));
|
|
1024
|
-
process.exit(1);
|
|
1025
|
-
}
|
|
1026
|
-
});
|
|
1027
|
-
// squish confidence <memoryId> [level] - Set or view confidence level
|
|
1028
|
-
program
|
|
1029
|
-
.command('confidence <memoryId> [level]')
|
|
1030
|
-
.description('Set or view confidence level (certain/speculative/outdated)')
|
|
1031
|
-
.action(async (memoryId, level) => {
|
|
1032
|
-
try {
|
|
1033
|
-
if (!level) {
|
|
1034
|
-
const memory = await getMemory(String(memoryId));
|
|
1035
|
-
if (!memory) {
|
|
1036
|
-
console.log(JSON.stringify({ ok: false, error: 'Memory not found' }, null, 2));
|
|
1037
|
-
process.exit(1);
|
|
1038
|
-
}
|
|
1039
|
-
console.log(JSON.stringify({ ok: true, memoryId, confidenceLevel: memory.confidenceLevel ?? 'certain' }, null, 2));
|
|
1040
|
-
}
|
|
1041
|
-
else {
|
|
1042
|
-
const validLevels = ['certain', 'speculative', 'outdated'];
|
|
1043
|
-
if (!validLevels.includes(level)) {
|
|
1044
|
-
console.log(JSON.stringify({ ok: false, error: 'Invalid level. Use: certain, speculative, or outdated' }, null, 2));
|
|
1045
|
-
process.exit(1);
|
|
1046
|
-
}
|
|
1047
|
-
await setConfidence(String(memoryId), level);
|
|
1048
|
-
console.log(JSON.stringify({ ok: true, memoryId, confidenceLevel: level }, null, 2));
|
|
1049
|
-
}
|
|
1050
|
-
}
|
|
1051
|
-
catch (error) {
|
|
1052
|
-
console.log(JSON.stringify({ ok: false, error: error.message }, null, 2));
|
|
1053
|
-
process.exit(1);
|
|
1054
|
-
}
|
|
1055
|
-
});
|
|
1056
|
-
// squish pin <memoryId> [--unpin]
|
|
1057
|
-
program
|
|
1058
|
-
.command('pin <memoryId>')
|
|
1059
|
-
.description('Pin/unpin a memory to prevent pruning/consolidation')
|
|
1060
|
-
.option('-u, --unpin', 'Unpin the memory instead of pinning', false)
|
|
1061
|
-
.action(async (memoryId, options) => {
|
|
1062
|
-
try {
|
|
1063
|
-
if (options.unpin) {
|
|
1064
|
-
await unpinMemory(String(memoryId));
|
|
1065
|
-
console.log(JSON.stringify({ ok: true, memoryId, pinned: false }, null, 2));
|
|
1066
|
-
}
|
|
1067
|
-
else {
|
|
1068
|
-
await pinMemory(String(memoryId));
|
|
1069
|
-
console.log(JSON.stringify({ ok: true, memoryId, pinned: true }, null, 2));
|
|
1070
|
-
}
|
|
1071
|
-
}
|
|
1072
|
-
catch (error) {
|
|
1073
|
-
console.log(JSON.stringify({ ok: false, error: error.message }, null, 2));
|
|
1074
|
-
process.exit(1);
|
|
1075
|
-
}
|
|
1076
|
-
});
|
|
1077
|
-
// squish tag add <tag> --search <query> --confirm
|
|
1078
|
-
// squish tag remove <tag> --older-than "30 days" --confirm
|
|
1079
|
-
program
|
|
1080
|
-
.command('tag')
|
|
1081
|
-
.description('Manage tags on memories (bulk)')
|
|
1082
|
-
.argument('<action>', 'add or remove')
|
|
1083
|
-
.argument('<tag>', 'Tag name')
|
|
1084
|
-
.option('-s, --search <query>', 'Search query to match memories')
|
|
1085
|
-
.option('-o, --older-than <date>', 'Only tag memories older than (e.g., "30 days")')
|
|
1086
|
-
.option('-t, --type <type>', 'Filter by memory type')
|
|
1087
|
-
.option('-c, --confirm', 'Actually execute the changes (default is dry-run)', false)
|
|
1088
|
-
.option('-l, --limit <number>', 'Max memories to process', '50')
|
|
1089
|
-
.option('-p, --project <project>', 'Project path', getDefaultProjectPath())
|
|
1090
|
-
.action(async (action, tag, options) => {
|
|
1091
|
-
try {
|
|
1092
|
-
if (!options.search && !options.olderThan) {
|
|
1093
|
-
console.log(JSON.stringify({ ok: false, error: 'Provide --search <query> or --older-than <date>' }, null, 2));
|
|
1094
|
-
process.exit(1);
|
|
1095
|
-
}
|
|
1096
|
-
const limit = validateLimit(options.limit, 50, 1, 100);
|
|
1097
|
-
let results;
|
|
1098
|
-
const searchInput = { query: options.search, limit, project: options.project };
|
|
1099
|
-
if (options.type)
|
|
1100
|
-
searchInput.type = options.type;
|
|
1101
|
-
if (options.search) {
|
|
1102
|
-
results = await search(searchInput);
|
|
1103
|
-
}
|
|
1104
|
-
else {
|
|
1105
|
-
results = await getRecent(options.project, limit * 2);
|
|
1106
|
-
}
|
|
1107
|
-
let filtered = results;
|
|
1108
|
-
if (options.olderThan) {
|
|
1109
|
-
filtered = filterByDateRange(results, '', options.olderThan);
|
|
1110
|
-
}
|
|
1111
|
-
const db = await getDb();
|
|
1112
|
-
const schema = await getSchema();
|
|
1113
|
-
if (action === 'add') {
|
|
1114
|
-
const updated = [];
|
|
1115
|
-
for (const mem of filtered) {
|
|
1116
|
-
try {
|
|
1117
|
-
const tags = new Set((mem.tags || []));
|
|
1118
|
-
if (!tags.has(tag)) {
|
|
1119
|
-
tags.add(tag);
|
|
1120
|
-
await db.update(schema.memories)
|
|
1121
|
-
.set({ tags: serializeTags(Array.from(tags)), updatedAt: new Date() })
|
|
1122
|
-
.where(eq(schema.memories.id, mem.id));
|
|
1123
|
-
updated.push(mem.id);
|
|
1124
|
-
}
|
|
1125
|
-
}
|
|
1126
|
-
catch (e) {
|
|
1127
|
-
console.error('DEBUG: error updating', mem.id, e.message);
|
|
1128
|
-
throw e;
|
|
1129
|
-
}
|
|
1130
|
-
}
|
|
1131
|
-
console.log(JSON.stringify({ ok: true, action: 'add', tag, matched: filtered.length, updated: updated.length, dryRun: !options.confirm }, null, 2));
|
|
1132
|
-
}
|
|
1133
|
-
else if (action === 'remove') {
|
|
1134
|
-
const updated = [];
|
|
1135
|
-
for (const mem of filtered) {
|
|
1136
|
-
const tags = new Set((mem.tags || []));
|
|
1137
|
-
if (tags.has(tag)) {
|
|
1138
|
-
tags.delete(tag);
|
|
1139
|
-
await db.update(schema.memories)
|
|
1140
|
-
.set({ tags: serializeTags(Array.from(tags)), updatedAt: new Date() })
|
|
1141
|
-
.where(eq(schema.memories.id, mem.id));
|
|
1142
|
-
updated.push(mem.id);
|
|
1143
|
-
}
|
|
1144
|
-
}
|
|
1145
|
-
console.log(JSON.stringify({ ok: true, action: 'remove', tag, matched: filtered.length, updated: updated.length, dryRun: !options.confirm }, null, 2));
|
|
1146
|
-
}
|
|
1147
|
-
else {
|
|
1148
|
-
console.log(JSON.stringify({ ok: false, error: 'Use: squish tag add <tag> or squish tag remove <tag>' }, null, 2));
|
|
1149
|
-
process.exit(1);
|
|
1150
|
-
}
|
|
1151
|
-
}
|
|
1152
|
-
catch (error) {
|
|
1153
|
-
console.log(JSON.stringify({ ok: false, error: error.message }, null, 2));
|
|
1154
|
-
process.exit(1);
|
|
1155
|
-
}
|
|
1156
|
-
});
|
|
1157
|
-
// squish stale --days 30 - Show old, low-confidence, unaccessed memories
|
|
1158
|
-
program
|
|
1159
|
-
.command('stale')
|
|
1160
|
-
.description('Show stale memories (old, low-confidence, or rarely accessed)')
|
|
1161
|
-
.option('-d, --days <number>', 'Show memories older than N days', '30')
|
|
1162
|
-
.option('-c, --confidence <level>', 'Max confidence level to show (outdated, speculative)', 'speculative')
|
|
1163
|
-
.option('-l, --limit <number>', 'Max results', '20')
|
|
1164
|
-
.option('-p, --project <project>', 'Project path', getDefaultProjectPath())
|
|
1165
|
-
.action(async (options) => {
|
|
1166
|
-
try {
|
|
1167
|
-
const days = validateLimit(options.days, 30, 1, 365);
|
|
1168
|
-
const cutoffDate = new Date(Date.now() - days * 86400000);
|
|
1169
|
-
// Get recent memories - larger limit to find stale ones
|
|
1170
|
-
const results = await getRecent(options.project, 500);
|
|
1171
|
-
const stale = results.filter((m) => {
|
|
1172
|
-
const created = m.createdAt ? new Date(m.createdAt) : null;
|
|
1173
|
-
const isOld = created && created < cutoffDate;
|
|
1174
|
-
const isLowConfidence = m.confidenceLevel === 'outdated' || m.confidenceLevel === 'speculative';
|
|
1175
|
-
const hasLowImportance = (m.importance || 50) < 40;
|
|
1176
|
-
return isOld || isLowConfidence || hasLowImportance;
|
|
1177
|
-
});
|
|
1178
|
-
const limited = stale.slice(0, validateLimit(options.limit, 20, 1, 100));
|
|
1179
|
-
const summary = {
|
|
1180
|
-
totalStale: stale.length,
|
|
1181
|
-
old: stale.filter((m) => m.createdAt && new Date(m.createdAt) < cutoffDate).length,
|
|
1182
|
-
lowConfidence: stale.filter((m) => m.confidenceLevel === 'outdated' || m.confidenceLevel === 'speculative').length,
|
|
1183
|
-
lowImportance: stale.filter((m) => (m.importance || 50) < 40).length,
|
|
1184
|
-
};
|
|
1185
|
-
console.log(JSON.stringify({ ok: true, summary, memories: limited }, null, 2));
|
|
1186
|
-
}
|
|
1187
|
-
catch (error) {
|
|
1188
|
-
console.log(JSON.stringify({ ok: false, error: error.message }, null, 2));
|
|
1189
|
-
process.exit(1);
|
|
1190
|
-
}
|
|
1191
|
-
});
|
|
1192
|
-
// squish stats
|
|
1193
|
-
program
|
|
1194
|
-
.command('stats')
|
|
1195
|
-
.description('View statistics')
|
|
1196
|
-
.option('-p, --project <project>', 'Project path', getDefaultProjectPath())
|
|
1197
|
-
.option('-m, --memory', 'Show memory file storage stats instead of database', false)
|
|
1198
|
-
.action(async (options) => {
|
|
1199
|
-
try {
|
|
1200
|
-
// Memory file stats
|
|
1201
|
-
if (options.memory) {
|
|
1202
|
-
const { getMemoryStats, isMemoryStorageAvailable } = await import('./core/memory/markdown/markdown-storage.js');
|
|
1203
|
-
const available = isMemoryStorageAvailable();
|
|
1204
|
-
if (!available) {
|
|
1205
|
-
console.log(JSON.stringify({ ok: false, error: 'Memory file storage not available' }, null, 2));
|
|
1206
|
-
process.exit(1);
|
|
1207
|
-
}
|
|
1208
|
-
const stats = await getMemoryStats();
|
|
1209
|
-
console.log(JSON.stringify({ ok: true, source: 'memory', ...stats }, null, 2));
|
|
1210
|
-
return;
|
|
1211
|
-
}
|
|
1212
|
-
// Database stats
|
|
1213
|
-
const stats = await getMemoryStats(options.project);
|
|
1214
|
-
console.log(JSON.stringify({ ok: true, ...stats }, null, 2));
|
|
1215
|
-
}
|
|
1216
|
-
catch (error) {
|
|
1217
|
-
console.log(JSON.stringify({ ok: false, error: error.message }, null, 2));
|
|
1218
|
-
process.exit(1);
|
|
1219
|
-
}
|
|
1220
|
-
});
|
|
1221
|
-
// squish install
|
|
1222
|
-
program
|
|
1223
|
-
.command('install')
|
|
1224
|
-
.description('Run the interactive installer wizard')
|
|
1225
|
-
.action(async () => {
|
|
1226
|
-
await spawnInstallerWizard();
|
|
1227
|
-
});
|
|
1228
|
-
// squish note "my thought here" - quick brain dump
|
|
1229
|
-
program
|
|
1230
|
-
.command('note <content>')
|
|
1231
|
-
.description('Quick brain dump - store a raw memory to process later')
|
|
1232
|
-
.option('-p, --project <project>', 'Project path', getDefaultProjectPath())
|
|
1233
|
-
.action(async (content, options) => {
|
|
1234
|
-
try {
|
|
1235
|
-
const result = await rememberMemory({
|
|
1236
|
-
content,
|
|
1237
|
-
type: 'observation',
|
|
1238
|
-
tags: ['note', 'quick'],
|
|
1239
|
-
project: options.project,
|
|
1240
|
-
});
|
|
1241
|
-
console.log(JSON.stringify({ ok: true, message: 'Note saved', id: result.id }, null, 2));
|
|
1242
|
-
}
|
|
1243
|
-
catch (error) {
|
|
1244
|
-
console.log(JSON.stringify({ ok: false, error: error.message }, null, 2));
|
|
1245
|
-
process.exit(1);
|
|
1246
|
-
}
|
|
1247
|
-
});
|
|
1248
|
-
// squish context - Show project context (memories + observations + places)
|
|
1249
|
-
program
|
|
1250
|
-
.command('context')
|
|
1251
|
-
.description('Show project context or list available projects')
|
|
1252
|
-
.option('-p, --project <project>', 'Project path', getDefaultProjectPath())
|
|
1253
|
-
.option('-l, --limit <number>', 'Number of items to show', '10')
|
|
1254
|
-
.option('-i, --include <items>', 'What to include: memories, observations, entities, places', 'memories,observations,places')
|
|
1255
|
-
.option('--list-projects', 'List registered projects instead of loading context', false)
|
|
1256
|
-
.option('-j, --json', 'Output as JSON', false)
|
|
1257
|
-
.option('--place <type>', 'Filter by place type: entry_hall, library, workshop, lab, office, garden, archive')
|
|
1258
|
-
.option('--tier <level>', 'Disclosure level: quick (place names), medium (top 3), full (all)', 'medium')
|
|
1259
|
-
.option('--has-memories', 'Only show places with memories', true)
|
|
1260
|
-
.option('--sync', 'Recalculate memory counts for all places', false)
|
|
1261
|
-
.option('--archive', 'Move memories > 30 days to Archive place', false)
|
|
1262
|
-
.option('--task <description>', 'Task description for auto-place detection (e.g., "fix bug", "design API")')
|
|
1263
|
-
.action(async (options) => {
|
|
1264
|
-
try {
|
|
1265
|
-
// Auto-detect place from task if provided
|
|
1266
|
-
let placeFilter = options.place || null;
|
|
1267
|
-
if (options.task && !placeFilter) {
|
|
1268
|
-
// Simple keyword detection
|
|
1269
|
-
const task = options.task.toLowerCase();
|
|
1270
|
-
if (task.includes('fix') || task.includes('bug') || task.includes('error'))
|
|
1271
|
-
placeFilter = 'workshop';
|
|
1272
|
-
else if (task.includes('design') || task.includes('plan') || task.includes('api'))
|
|
1273
|
-
placeFilter = 'library';
|
|
1274
|
-
else if (task.includes('task') || task.includes('todo') || task.includes('manage'))
|
|
1275
|
-
placeFilter = 'office';
|
|
1276
|
-
else if (task.includes('test') || task.includes('experiment'))
|
|
1277
|
-
placeFilter = 'lab';
|
|
1278
|
-
if (placeFilter)
|
|
1279
|
-
console.log(`Auto-detected place: ${placeFilter}`);
|
|
1280
|
-
}
|
|
1281
|
-
if (options.listProjects) {
|
|
1282
|
-
const projects = await getAllProjects();
|
|
1283
|
-
if (options.json) {
|
|
1284
|
-
console.log(JSON.stringify({ ok: true, count: projects.length, projects }, null, 2));
|
|
1285
|
-
}
|
|
1286
|
-
else {
|
|
1287
|
-
console.log(`\n Registered Projects (${projects.length})`);
|
|
1288
|
-
console.log(` ================================`);
|
|
1289
|
-
for (const project of projects) {
|
|
1290
|
-
console.log(`\n ${project.name}`);
|
|
1291
|
-
console.log(` Path: ${project.path}`);
|
|
1292
|
-
console.log(` ID: ${project.id}`);
|
|
1293
|
-
}
|
|
1294
|
-
console.log('');
|
|
1295
|
-
}
|
|
1296
|
-
return;
|
|
1297
|
-
}
|
|
1298
|
-
// Get project context
|
|
1299
|
-
const projectPath = resolveProjectPath(options.project);
|
|
1300
|
-
await ensureProject(projectPath);
|
|
1301
|
-
const project = await getOrCreateProject(projectPath);
|
|
1302
|
-
if (!project) {
|
|
1303
|
-
console.log(JSON.stringify({ ok: false, error: 'Project not found' }, null, 2));
|
|
1304
|
-
process.exit(1);
|
|
1305
|
-
}
|
|
1306
|
-
const limit = parseInt(options.limit);
|
|
1307
|
-
const include = (options.include || 'memories,observations,places').split(',');
|
|
1308
|
-
const tier = options.tier || 'full';
|
|
1309
|
-
const hasMemoriesOnly = options.hasMemories !== false; // Default true now
|
|
1310
|
-
const existingPlaceFilter = options.place || null;
|
|
1311
|
-
const result = { project: project.name, tier };
|
|
1312
|
-
// Get memories
|
|
1313
|
-
if (include.includes('memories')) {
|
|
1314
|
-
const memories = await getRecent(projectPath, limit);
|
|
1315
|
-
result.memories = memories.map((m) => ({
|
|
1316
|
-
id: m.id,
|
|
1317
|
-
type: m.type,
|
|
1318
|
-
content: m.content?.substring(0, 100),
|
|
1319
|
-
tags: m.tags,
|
|
1320
|
-
}));
|
|
1321
|
-
}
|
|
1322
|
-
// Get observations (learnings)
|
|
1323
|
-
if (include.includes('observations')) {
|
|
1324
|
-
const { getObservations } = await import('./core/ingestion/learnings.js');
|
|
1325
|
-
const observations = await getObservations(projectPath, limit);
|
|
1326
|
-
result.observations = observations.map((o) => ({
|
|
1327
|
-
id: o.id,
|
|
1328
|
-
type: o.type,
|
|
1329
|
-
content: o.content?.substring(0, 100),
|
|
1330
|
-
}));
|
|
1331
|
-
}
|
|
1332
|
-
// Get places (spatial memory) with filtering
|
|
1333
|
-
if (include.includes('places')) {
|
|
1334
|
-
const { initializeDefaultPlaces, getProjectPlaces, walkPlace, getPlaceByType, syncAllPlaceMemoryCounts } = await import('./core/places/index.js');
|
|
1335
|
-
await initializeDefaultPlaces(project.id);
|
|
1336
|
-
// Sync memory counts if requested
|
|
1337
|
-
if (options.sync) {
|
|
1338
|
-
await syncAllPlaceMemoryCounts(project.id);
|
|
1339
|
-
console.log('Synced memory counts for all places.');
|
|
1340
|
-
}
|
|
1341
|
-
// Auto-archive old memories if requested
|
|
1342
|
-
if (options.archive) {
|
|
1343
|
-
const { autoArchiveOldMemories } = await import('./core/places/index.js');
|
|
1344
|
-
const archiveResult = await autoArchiveOldMemories(project.id, 30);
|
|
1345
|
-
console.log(`Archived ${archiveResult.archived} old memories to Archive (${archiveResult.failed} failed).`);
|
|
1346
|
-
}
|
|
1347
|
-
let places = await getProjectPlaces(project.id);
|
|
1348
|
-
// Apply --has-memories filter
|
|
1349
|
-
if (hasMemoriesOnly) {
|
|
1350
|
-
places = places.filter((p) => p.memoryCount > 0);
|
|
1351
|
-
}
|
|
1352
|
-
// Apply --place filter (from --place or --task auto-detect)
|
|
1353
|
-
if (placeFilter) {
|
|
1354
|
-
const filtered = places.filter((p) => p.placeType === placeFilter);
|
|
1355
|
-
if (filtered.length > 0) {
|
|
1356
|
-
places = filtered;
|
|
1357
|
-
}
|
|
1358
|
-
}
|
|
1359
|
-
// Format places based on tier
|
|
1360
|
-
if (tier === 'quick') {
|
|
1361
|
-
// Just place names (~50 tokens)
|
|
1362
|
-
result.places = places.map((p) => ({
|
|
1363
|
-
name: p.name,
|
|
1364
|
-
type: p.placeType,
|
|
1365
|
-
}));
|
|
1366
|
-
}
|
|
1367
|
-
else if (tier === 'medium') {
|
|
1368
|
-
// Top 3 memories per place (~170 tokens)
|
|
1369
|
-
const placesWithMemories = [];
|
|
1370
|
-
for (const p of places) {
|
|
1371
|
-
if (p.memoryCount > 0) {
|
|
1372
|
-
const walkResult = await walkPlace(project.id, p.placeType, {
|
|
1373
|
-
tokenBudget: 170,
|
|
1374
|
-
maxMemoriesPerPlace: 3,
|
|
1375
|
-
compressWithToon: false,
|
|
1376
|
-
});
|
|
1377
|
-
placesWithMemories.push({
|
|
1378
|
-
name: p.name,
|
|
1379
|
-
type: p.placeType,
|
|
1380
|
-
purpose: p.purpose,
|
|
1381
|
-
memories: p.memoryCount,
|
|
1382
|
-
preview: walkResult?.memories.slice(0, 3).map((m) => m.content?.substring(0, 80)) || [],
|
|
1383
|
-
});
|
|
1384
|
-
}
|
|
1385
|
-
}
|
|
1386
|
-
result.places = placesWithMemories;
|
|
1387
|
-
}
|
|
1388
|
-
else {
|
|
1389
|
-
// Full - all memories (~500 tokens)
|
|
1390
|
-
result.places = places.map((p) => ({
|
|
1391
|
-
name: p.name,
|
|
1392
|
-
type: p.placeType,
|
|
1393
|
-
purpose: p.purpose,
|
|
1394
|
-
memories: p.memoryCount,
|
|
1395
|
-
}));
|
|
1396
|
-
}
|
|
1397
|
-
}
|
|
1398
|
-
if (options.json) {
|
|
1399
|
-
console.log(JSON.stringify({ ok: true, ...result }, null, 2));
|
|
1400
|
-
}
|
|
1401
|
-
else {
|
|
1402
|
-
// Human readable output
|
|
1403
|
-
console.log(`\n=== ${project.name} Context ===\n`);
|
|
1404
|
-
if (result.places && result.places.length > 0) {
|
|
1405
|
-
console.log('Spatial Memory Places:');
|
|
1406
|
-
result.places.forEach((p) => {
|
|
1407
|
-
if (tier === 'quick') {
|
|
1408
|
-
console.log(` ${p.name} (${p.type})`);
|
|
1409
|
-
}
|
|
1410
|
-
else if (tier === 'medium') {
|
|
1411
|
-
console.log(` ${p.name} (${p.memories} memories) - ${p.purpose}`);
|
|
1412
|
-
if (p.preview && p.preview.length > 0) {
|
|
1413
|
-
p.preview.forEach((m) => {
|
|
1414
|
-
console.log(` - ${m}...`);
|
|
1415
|
-
});
|
|
1416
|
-
}
|
|
1417
|
-
}
|
|
1418
|
-
else {
|
|
1419
|
-
console.log(` ${p.name} (${p.memories} memories) - ${p.purpose}`);
|
|
1420
|
-
}
|
|
1421
|
-
});
|
|
1422
|
-
console.log('');
|
|
1423
|
-
}
|
|
1424
|
-
if (result.memories && result.memories.length > 0) {
|
|
1425
|
-
console.log('Recent Memories:');
|
|
1426
|
-
result.memories.slice(0, 5).forEach((m) => {
|
|
1427
|
-
console.log(` [${m.type}] ${m.content}`);
|
|
1428
|
-
});
|
|
1429
|
-
console.log('');
|
|
1430
|
-
}
|
|
1431
|
-
}
|
|
1432
|
-
}
|
|
1433
|
-
catch (error) {
|
|
1434
|
-
console.log(JSON.stringify({ ok: false, error: error.message }, null, 2));
|
|
1435
|
-
process.exit(1);
|
|
1436
|
-
}
|
|
1437
|
-
});
|
|
1438
|
-
// squish migrate - Migrate memories between databases
|
|
1439
|
-
program
|
|
1440
|
-
.command('migrate')
|
|
1441
|
-
.description('Migrate memories from one .squish directory to another')
|
|
1442
|
-
.option('-f, --from <path>', 'Source .squish directory (read from)', '')
|
|
1443
|
-
.option('-t, --to <path>', 'Target .squish directory (write to)', '')
|
|
1444
|
-
.option('--delete-source', 'Delete source after migration (use with caution)', false)
|
|
1445
|
-
.option('--dry-run', 'Preview migration without applying', false)
|
|
1446
|
-
.action(async (options) => {
|
|
1447
|
-
try {
|
|
1448
|
-
if (!options.from || !options.to) {
|
|
1449
|
-
console.log(JSON.stringify({
|
|
1450
|
-
ok: false,
|
|
1451
|
-
error: 'Usage: squish migrate --from /path/to/old/.squish --to /path/to/new/.squish'
|
|
1452
|
-
}, null, 2));
|
|
1453
|
-
process.exit(1);
|
|
1454
|
-
}
|
|
1455
|
-
const sourcePath = path.join(options.from, 'squish.db');
|
|
1456
|
-
const targetPath = path.join(options.to, 'squish.db');
|
|
1457
|
-
if (!existsSync(sourcePath)) {
|
|
1458
|
-
console.log(JSON.stringify({ ok: false, error: `Source database not found: ${sourcePath}` }, null, 2));
|
|
1459
|
-
process.exit(1);
|
|
1460
|
-
}
|
|
1461
|
-
if (!existsSync(targetPath)) {
|
|
1462
|
-
console.log(JSON.stringify({ ok: false, error: `Target database not found: ${targetPath}` }, null, 2));
|
|
1463
|
-
process.exit(1);
|
|
1464
|
-
}
|
|
1465
|
-
console.log(`Migrating memories from:\n ${options.from}\nto:\n ${options.to}\n`);
|
|
1466
|
-
// Import database modules dynamically
|
|
1467
|
-
const { migrateMemories } = await import('./core/memory/migrate.js');
|
|
1468
|
-
const result = await migrateMemories(options.from, options.to, {
|
|
1469
|
-
dryRun: options.dryRun,
|
|
1470
|
-
deleteSource: options.deleteSource
|
|
1471
|
-
});
|
|
1472
|
-
console.log(JSON.stringify({ ok: true, ...result }, null, 2));
|
|
1473
|
-
}
|
|
1474
|
-
catch (error) {
|
|
1475
|
-
console.log(JSON.stringify({ ok: false, error: error.message }, null, 2));
|
|
1476
|
-
process.exit(1);
|
|
1477
|
-
}
|
|
1478
|
-
});
|
|
1479
|
-
// squish clean - Run deduplication and consolidation
|
|
1480
|
-
program
|
|
1481
|
-
.command('clean')
|
|
1482
|
-
.description('Run maintenance: deduplication + consolidation')
|
|
1483
|
-
.option('-t, --threshold <number>', 'Similarity threshold for dedup (0-1)', '0.85')
|
|
1484
|
-
.option('-d, --min-age <days>', 'Minimum age for consolidation', '90')
|
|
1485
|
-
.option('-i, --max-importance <number>', 'Max importance to consolidate (0-100)', '30')
|
|
1486
|
-
.option('-c, --min-cluster <number>', 'Minimum cluster size', '3')
|
|
1487
|
-
.option('-p, --project <project>', 'Project path', getDefaultProjectPath())
|
|
1488
|
-
.option('--dry-run', 'Preview changes without applying', false)
|
|
1489
|
-
.action(async (options) => {
|
|
1490
|
-
try {
|
|
1491
|
-
console.log('Running maintenance: deduplication + consolidation...\n');
|
|
1492
|
-
// Step 1: Deduplication
|
|
1493
|
-
console.log('Step 1: Finding duplicate memories...');
|
|
1494
|
-
const dedupResult = await runDeduplicationJob(options.project);
|
|
1495
|
-
console.log(` Found ${dedupResult.duplicatesFound} duplicates, merged ${dedupResult.mergedCount}`);
|
|
1496
|
-
// Step 2: Consolidation
|
|
1497
|
-
console.log('\nStep 2: Consolidating old memories...');
|
|
1498
|
-
const consolidateResult = await runFullConsolidationJob(options.project);
|
|
1499
|
-
console.log(` Clustered ${consolidateResult.clustered}, merged ${consolidateResult.merged}, consolidated ${consolidateResult.consolidated}`);
|
|
1500
|
-
console.log(JSON.stringify({
|
|
1501
|
-
ok: true,
|
|
1502
|
-
dedup: {
|
|
1503
|
-
duplicatesFound: dedupResult.duplicatesFound,
|
|
1504
|
-
mergedCount: dedupResult.mergedCount,
|
|
1505
|
-
tokensRecovered: dedupResult.tokensRecovered
|
|
1506
|
-
},
|
|
1507
|
-
consolidate: {
|
|
1508
|
-
clustered: consolidateResult.clustered,
|
|
1509
|
-
merged: consolidateResult.merged,
|
|
1510
|
-
consolidated: consolidateResult.consolidated
|
|
1511
|
-
}
|
|
1512
|
-
}, null, 2));
|
|
1513
|
-
}
|
|
1514
|
-
catch (error) {
|
|
1515
|
-
console.log(JSON.stringify({ ok: false, error: error.message }, null, 2));
|
|
1516
|
-
}
|
|
1517
|
-
});
|
|
1518
|
-
// squish hooks session-start --agent claude-code --mode startup
|
|
1519
|
-
program
|
|
1520
|
-
.command('hooks')
|
|
1521
|
-
.description('Handle agent hooks (session-start, post-tool-use, session-end, pre-compact)')
|
|
1522
|
-
.argument('<event>', 'Event type: session-start, post-tool-use, session-end, pre-compact')
|
|
1523
|
-
.option('-a, --agent <agent>', 'Agent type: claude-code, opencode, cursor, windsurf', 'claude-code')
|
|
1524
|
-
.option('-m, --mode <mode>', 'Mode for session-start: startup, resume, compact', 'startup')
|
|
1525
|
-
.option('-p, --project <project>', 'Project path', getDefaultProjectPath())
|
|
1526
|
-
.option('-t, --tool <tool>', 'Tool name for post-tool-use')
|
|
1527
|
-
.option('--tool-input <json>', 'Tool input as JSON string')
|
|
1528
|
-
.option('--tool-result <json>', 'Tool result as JSON string')
|
|
1529
|
-
.option('--wip <work>', 'Work in progress for session-end')
|
|
1530
|
-
.action(async (event, options) => {
|
|
1531
|
-
try {
|
|
1532
|
-
const agentType = options.agent;
|
|
1533
|
-
const projectPath = options.project;
|
|
1534
|
-
let result;
|
|
1535
|
-
switch (event) {
|
|
1536
|
-
case 'session-start':
|
|
1537
|
-
result = await handleSessionStart({
|
|
1538
|
-
projectPath,
|
|
1539
|
-
mode: options.mode,
|
|
1540
|
-
agentType,
|
|
1541
|
-
});
|
|
1542
|
-
console.log(JSON.stringify({ ok: true, ...result }, null, 2));
|
|
1543
|
-
break;
|
|
1544
|
-
case 'post-tool-use':
|
|
1545
|
-
if (!options.tool) {
|
|
1546
|
-
console.log(JSON.stringify({ ok: false, error: '--tool required for post-tool-use' }, null, 2));
|
|
1547
|
-
process.exit(1);
|
|
1548
|
-
}
|
|
1549
|
-
const toolInput = options.toolInput ? JSON.parse(options.toolInput) : {};
|
|
1550
|
-
const toolResult = options.toolResult ? JSON.parse(options.toolResult) : {};
|
|
1551
|
-
result = await handlePostToolUse({
|
|
1552
|
-
toolName: options.tool,
|
|
1553
|
-
toolInput,
|
|
1554
|
-
toolResult,
|
|
1555
|
-
projectPath,
|
|
1556
|
-
agentType,
|
|
1557
|
-
});
|
|
1558
|
-
console.log(JSON.stringify({ ok: true, ...result }, null, 2));
|
|
1559
|
-
break;
|
|
1560
|
-
case 'session-end':
|
|
1561
|
-
result = await handleSessionEnd({
|
|
1562
|
-
projectPath,
|
|
1563
|
-
agentType,
|
|
1564
|
-
workInProgress: options.wip,
|
|
1565
|
-
});
|
|
1566
|
-
console.log(JSON.stringify({ ok: true, ...result }, null, 2));
|
|
1567
|
-
break;
|
|
1568
|
-
case 'pre-compact':
|
|
1569
|
-
result = await handlePreCompact({
|
|
1570
|
-
projectPath,
|
|
1571
|
-
agentType,
|
|
1572
|
-
});
|
|
1573
|
-
console.log(JSON.stringify({ ok: true, ...result }, null, 2));
|
|
1574
|
-
break;
|
|
1575
|
-
default:
|
|
1576
|
-
console.log(JSON.stringify({ ok: false, error: `Unknown event: ${event}` }, null, 2));
|
|
1577
|
-
process.exit(1);
|
|
1578
|
-
}
|
|
1579
|
-
}
|
|
1580
|
-
catch (error) {
|
|
1581
|
-
console.log(JSON.stringify({ ok: false, error: error.message }, null, 2));
|
|
1582
|
-
process.exit(1);
|
|
1583
|
-
}
|
|
1584
|
-
});
|
|
1585
|
-
// squish walk - Walk through spatial memory places
|
|
1586
|
-
program
|
|
1587
|
-
.command('walk [place]')
|
|
1588
|
-
.description('Walk through spatial memory places (entry_hall, library, workshop, lab, office, garden, archive, or --all)')
|
|
1589
|
-
.option('-a, --all', 'Walk all places in order', false)
|
|
1590
|
-
.option('-t, --tokens <number>', 'Max tokens budget (default: 170)', '170')
|
|
1591
|
-
.option('-m, --max <number>', 'Max memories per place', '10')
|
|
1592
|
-
.option('-p, --project <project>', 'Project path', getDefaultProjectPath())
|
|
1593
|
-
.option('-q, --quick', 'Quick tour (place names only)', false)
|
|
1594
|
-
.option('-j, --json', 'Output as JSON', false)
|
|
1595
|
-
.action(async (place, options) => {
|
|
1596
|
-
try {
|
|
1597
|
-
const projectPath = resolveProjectPath(options.project);
|
|
1598
|
-
await ensureProject(projectPath);
|
|
1599
|
-
const project = await getOrCreateProject(projectPath);
|
|
1600
|
-
if (!project) {
|
|
1601
|
-
console.log(JSON.stringify({ ok: false, error: 'Project not found' }, null, 2));
|
|
1602
|
-
process.exit(1);
|
|
1603
|
-
}
|
|
1604
|
-
// Ensure places are initialized
|
|
1605
|
-
await initializeDefaultPlaces(project.id);
|
|
1606
|
-
if (options.quick) {
|
|
1607
|
-
const tour = await quickTour(project.id);
|
|
1608
|
-
if (options.json) {
|
|
1609
|
-
console.log(JSON.stringify({ ok: true, ...tour }, null, 2));
|
|
1610
|
-
}
|
|
1611
|
-
else {
|
|
1612
|
-
console.log(`\n=== Spatial Memory Tour ===\n`);
|
|
1613
|
-
console.log(`Total Memories: ${tour.totalMemories}\n`);
|
|
1614
|
-
for (const p of tour.places) {
|
|
1615
|
-
console.log(`${p.name} (${p.memoryCount} memories)`);
|
|
1616
|
-
console.log(` ${p.purpose}\n`);
|
|
1617
|
-
}
|
|
1618
|
-
}
|
|
1619
|
-
return;
|
|
1620
|
-
}
|
|
1621
|
-
const tokenBudget = parseInt(options.tokens);
|
|
1622
|
-
const maxMemories = parseInt(options.max);
|
|
1623
|
-
if (options.all) {
|
|
1624
|
-
const results = await walkAllPlaces(project.id, {
|
|
1625
|
-
tokenBudget: Math.floor(tokenBudget / 7),
|
|
1626
|
-
maxMemoriesPerPlace: maxMemories,
|
|
1627
|
-
compressWithToon: true,
|
|
1628
|
-
});
|
|
1629
|
-
if (options.json) {
|
|
1630
|
-
console.log(JSON.stringify({ ok: true, places: results }, null, 2));
|
|
1631
|
-
}
|
|
1632
|
-
else {
|
|
1633
|
-
console.log(`\n=== Walking All Places ===\n`);
|
|
1634
|
-
for (const r of results) {
|
|
1635
|
-
console.log(`## ${r.place.name} (${r.memories.length} memories, ~${r.totalTokens} tokens)`);
|
|
1636
|
-
r.memories.forEach((m, i) => {
|
|
1637
|
-
console.log(` ${i + 1}. ${m.content.substring(0, 60)}...`);
|
|
1638
|
-
});
|
|
1639
|
-
console.log('');
|
|
1640
|
-
}
|
|
1641
|
-
}
|
|
1642
|
-
}
|
|
1643
|
-
else if (place) {
|
|
1644
|
-
const validPlaces = ['entry_hall', 'library', 'workshop', 'lab', 'office', 'garden', 'archive'];
|
|
1645
|
-
const placeType = validPlaces.includes(place) ? place : 'workshop';
|
|
1646
|
-
const result = await walkPlace(project.id, placeType, {
|
|
1647
|
-
tokenBudget,
|
|
1648
|
-
maxMemoriesPerPlace: maxMemories,
|
|
1649
|
-
compressWithToon: true,
|
|
1650
|
-
});
|
|
1651
|
-
if (options.json) {
|
|
1652
|
-
console.log(JSON.stringify({ ok: true, ...result }, null, 2));
|
|
1653
|
-
}
|
|
1654
|
-
else if (result) {
|
|
1655
|
-
console.log(`\n=== ${result.place.name} ===\n`);
|
|
1656
|
-
console.log(`Purpose: ${result.place.purpose || 'N/A'}\n`);
|
|
1657
|
-
result.memories.forEach((m, i) => {
|
|
1658
|
-
console.log(`${i + 1}. [${m.type}] ${m.content.substring(0, 80)}`);
|
|
1659
|
-
});
|
|
1660
|
-
console.log(`\n~${result.totalTokens} tokens`);
|
|
1661
|
-
}
|
|
1662
|
-
else {
|
|
1663
|
-
console.log(JSON.stringify({ ok: false, error: `Place not found: ${place}` }, null, 2));
|
|
1664
|
-
}
|
|
1665
|
-
}
|
|
1666
|
-
}
|
|
1667
|
-
catch (error) {
|
|
1668
|
-
console.log(JSON.stringify({ ok: false, error: error.message }, null, 2));
|
|
1669
|
-
process.exit(1);
|
|
1670
|
-
}
|
|
1671
|
-
});
|
|
1672
|
-
await program.parseAsync(process.argv);
|
|
1673
|
-
}
|
|
1674
|
-
// MCP server: core/commands/mcp-server.ts
|
|
1675
|
-
// Run with: npx squish-mcp
|
|
1676
|
-
// ============================================================================
|
|
1677
|
-
//# sourceMappingURL=index.js.map
|