memory-journal-mcp 6.1.2 → 6.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +44 -28
- package/dist/{chunk-X4SWFATC.js → chunk-BI4ZNSKA.js} +38 -24
- package/dist/{chunk-HCEWINSB.js → chunk-N6EBIDN7.js} +99 -102
- package/dist/cli.js +2 -2
- package/dist/index.js +2 -2
- package/dist/tools-WPRY5MJ6.js +2 -0
- package/package.json +10 -1
- package/skills/github-commander/SKILL.md +151 -0
- package/skills/github-commander/config/project-config.example.md +125 -0
- package/skills/github-commander/workflows/code-quality-audit.md +80 -0
- package/skills/github-commander/workflows/full-audit.md +134 -0
- package/skills/github-commander/workflows/issue-triage.md +239 -0
- package/skills/github-commander/workflows/milestone-sprint.md +81 -0
- package/skills/github-commander/workflows/perf-audit.md +142 -0
- package/skills/github-commander/workflows/pr-review.md +123 -0
- package/skills/github-commander/workflows/security-audit.md +170 -0
- package/skills/github-commander/workflows/update-deps.md +109 -0
- package/.dockerignore +0 -139
- package/.gitattributes +0 -20
- package/.github/ISSUE_TEMPLATE/bug_report.md +0 -95
- package/.github/ISSUE_TEMPLATE/config.yml +0 -11
- package/.github/ISSUE_TEMPLATE/feature_request.md +0 -110
- package/.github/ISSUE_TEMPLATE/question.md +0 -78
- package/.github/aw/actions-lock.json +0 -14
- package/.github/copilot-instructions.md +0 -122
- package/.github/dependabot.yml +0 -93
- package/.github/pull_request_template.md +0 -135
- package/.github/workflows/README.md +0 -133
- package/.github/workflows/agentics-maintenance.yml +0 -141
- package/.github/workflows/auto-release.yml +0 -68
- package/.github/workflows/ci-health-monitor.lock.yml +0 -1121
- package/.github/workflows/ci-health-monitor.md +0 -87
- package/.github/workflows/codeql.yml +0 -41
- package/.github/workflows/dependabot-auto-merge.yml +0 -42
- package/.github/workflows/dependency-maintenance.lock.yml +0 -1182
- package/.github/workflows/dependency-maintenance.md +0 -147
- package/.github/workflows/docker-publish.yml +0 -254
- package/.github/workflows/docs-drift-detector.lock.yml +0 -1142
- package/.github/workflows/docs-drift-detector.md +0 -115
- package/.github/workflows/lint-and-test.yml +0 -60
- package/.github/workflows/publish-npm.yml +0 -85
- package/.github/workflows/secrets-scanning.yml +0 -32
- package/.github/workflows/security-update.yml +0 -127
- package/.gitleaks.toml +0 -9
- package/.prettierignore +0 -21
- package/.prettierrc +0 -33
- package/.scout-ignore +0 -12
- package/.trivyignore +0 -21
- package/CHANGELOG.md +0 -1814
- package/CODE_OF_CONDUCT.md +0 -133
- package/CONTRIBUTING.md +0 -263
- package/DOCKER_README.md +0 -331
- package/Dockerfile +0 -128
- package/SECURITY.md +0 -227
- package/UNRELEASED.md +0 -1
- package/dist/tools-T4U5A3X4.js +0 -2
- package/docker-compose.yml +0 -71
- package/docs/README.md +0 -18
- package/docs/agentic-journal-synergy.md +0 -175
- package/docs/copilot-setup.md +0 -72
- package/eslint.config.js +0 -110
- package/mcp-config-example.json +0 -21
- package/playwright.config.ts +0 -35
- package/releases/v2.1.0.md +0 -220
- package/releases/v2.2.0.md +0 -168
- package/releases/v3.0.0.md +0 -237
- package/releases/v3.1.0.md +0 -104
- package/releases/v3.1.1.md +0 -42
- package/releases/v3.1.2.md +0 -40
- package/releases/v3.1.3.md +0 -64
- package/releases/v3.1.4.md +0 -32
- package/releases/v3.1.5.md +0 -44
- package/releases/v4.0.0.md +0 -71
- package/releases/v4.1.0.md +0 -88
- package/releases/v4.2.0.md +0 -90
- package/releases/v4.3.0.md +0 -92
- package/releases/v4.3.1.md +0 -69
- package/releases/v4.4.0.md +0 -120
- package/releases/v4.4.1.md +0 -33
- package/releases/v4.4.2.md +0 -31
- package/releases/v4.5.0.md +0 -116
- package/releases/v5.0.0.md +0 -105
- package/releases/v5.0.1.md +0 -25
- package/releases/v5.1.0.md +0 -83
- package/releases/v5.1.1.md +0 -10
- package/releases/v6.0.0.md +0 -48
- package/releases/v6.0.1.md +0 -36
- package/releases/v6.1.0.md +0 -68
- package/releases/v6.1.1.md +0 -30
- package/releases/v6.1.2.md +0 -23
- package/scripts/generate-server-instructions.ts +0 -306
- package/scripts/server-instructions-function-body.ts +0 -107
- package/scripts/server-instructions-gotchas.ts +0 -45
- package/server.json +0 -42
- package/social-preview.png +0 -0
- package/src/auth/auth-context.ts +0 -78
- package/src/auth/authorization-server-discovery.ts +0 -263
- package/src/auth/errors.ts +0 -215
- package/src/auth/index.ts +0 -58
- package/src/auth/middleware.ts +0 -392
- package/src/auth/oauth-resource-server.ts +0 -170
- package/src/auth/scope-map.ts +0 -46
- package/src/auth/scopes.ts +0 -256
- package/src/auth/token-validator.ts +0 -293
- package/src/auth/transport-agnostic.ts +0 -164
- package/src/auth/types.ts +0 -372
- package/src/cli.ts +0 -279
- package/src/codemode/api-constants.ts +0 -263
- package/src/codemode/api.ts +0 -302
- package/src/codemode/auto-return.ts +0 -65
- package/src/codemode/index.ts +0 -47
- package/src/codemode/sandbox-factory.ts +0 -144
- package/src/codemode/sandbox.ts +0 -220
- package/src/codemode/security.ts +0 -155
- package/src/codemode/types.ts +0 -228
- package/src/codemode/worker-sandbox.ts +0 -277
- package/src/codemode/worker-script.ts +0 -239
- package/src/constants/icons.ts +0 -183
- package/src/constants/server-instructions.md +0 -166
- package/src/constants/server-instructions.ts +0 -514
- package/src/database/adapter-factory.ts +0 -16
- package/src/database/core/entry-columns.ts +0 -10
- package/src/database/core/interfaces.ts +0 -188
- package/src/database/core/schema.ts +0 -152
- package/src/database/sqlite-adapter/backup.ts +0 -167
- package/src/database/sqlite-adapter/entries/crud.ts +0 -233
- package/src/database/sqlite-adapter/entries/importance.ts +0 -76
- package/src/database/sqlite-adapter/entries/index.ts +0 -142
- package/src/database/sqlite-adapter/entries/search.ts +0 -294
- package/src/database/sqlite-adapter/entries/shared.ts +0 -102
- package/src/database/sqlite-adapter/entries/statistics.ts +0 -162
- package/src/database/sqlite-adapter/index.ts +0 -265
- package/src/database/sqlite-adapter/native-connection.ts +0 -301
- package/src/database/sqlite-adapter/relationships.ts +0 -70
- package/src/database/sqlite-adapter/tags.ts +0 -182
- package/src/filtering/tool-filter.ts +0 -312
- package/src/github/github-integration/client.ts +0 -114
- package/src/github/github-integration/index.ts +0 -297
- package/src/github/github-integration/insights.ts +0 -155
- package/src/github/github-integration/issues.ts +0 -213
- package/src/github/github-integration/milestones.ts +0 -262
- package/src/github/github-integration/projects.ts +0 -414
- package/src/github/github-integration/pull-requests.ts +0 -235
- package/src/github/github-integration/repository.ts +0 -110
- package/src/github/github-integration/types.ts +0 -43
- package/src/handlers/prompts/github.ts +0 -210
- package/src/handlers/prompts/index.ts +0 -97
- package/src/handlers/prompts/workflow.ts +0 -361
- package/src/handlers/resources/core/briefing/context-section.ts +0 -182
- package/src/handlers/resources/core/briefing/github-section.ts +0 -354
- package/src/handlers/resources/core/briefing/index.ts +0 -106
- package/src/handlers/resources/core/briefing/user-message.ts +0 -114
- package/src/handlers/resources/core/health.ts +0 -75
- package/src/handlers/resources/core/index.ts +0 -31
- package/src/handlers/resources/core/instructions.ts +0 -45
- package/src/handlers/resources/core/utilities.ts +0 -310
- package/src/handlers/resources/github.ts +0 -340
- package/src/handlers/resources/graph.ts +0 -218
- package/src/handlers/resources/help.ts +0 -410
- package/src/handlers/resources/index.ts +0 -143
- package/src/handlers/resources/shared.ts +0 -219
- package/src/handlers/resources/team.ts +0 -134
- package/src/handlers/resources/templates.ts +0 -334
- package/src/handlers/tools/admin.ts +0 -351
- package/src/handlers/tools/analytics.ts +0 -346
- package/src/handlers/tools/backup.ts +0 -272
- package/src/handlers/tools/codemode.ts +0 -188
- package/src/handlers/tools/core.ts +0 -359
- package/src/handlers/tools/error-fields-mixin.ts +0 -10
- package/src/handlers/tools/export.ts +0 -150
- package/src/handlers/tools/github/copilot-tools.ts +0 -72
- package/src/handlers/tools/github/helpers.ts +0 -125
- package/src/handlers/tools/github/insights-tools.ts +0 -112
- package/src/handlers/tools/github/issue-tools.ts +0 -442
- package/src/handlers/tools/github/kanban-tools.ts +0 -153
- package/src/handlers/tools/github/milestone-tools.ts +0 -371
- package/src/handlers/tools/github/mutation-tools.ts +0 -17
- package/src/handlers/tools/github/read-tools.ts +0 -302
- package/src/handlers/tools/github/schemas.ts +0 -435
- package/src/handlers/tools/github.ts +0 -39
- package/src/handlers/tools/index.ts +0 -255
- package/src/handlers/tools/relationships.ts +0 -390
- package/src/handlers/tools/schemas.ts +0 -165
- package/src/handlers/tools/search.ts +0 -448
- package/src/handlers/tools/team/admin-tools.ts +0 -164
- package/src/handlers/tools/team/analytics-tools.ts +0 -233
- package/src/handlers/tools/team/backup-tools.ts +0 -83
- package/src/handlers/tools/team/core-tools.ts +0 -197
- package/src/handlers/tools/team/export-tools.ts +0 -130
- package/src/handlers/tools/team/helpers.ts +0 -66
- package/src/handlers/tools/team/index.ts +0 -45
- package/src/handlers/tools/team/relationship-tools.ts +0 -219
- package/src/handlers/tools/team/schemas.ts +0 -558
- package/src/handlers/tools/team/search-tools.ts +0 -145
- package/src/handlers/tools/team/vector-tools.ts +0 -261
- package/src/index.ts +0 -57
- package/src/server/mcp-server.ts +0 -446
- package/src/server/registration.ts +0 -141
- package/src/server/scheduler.ts +0 -283
- package/src/transports/http/handlers.ts +0 -78
- package/src/transports/http/index.ts +0 -8
- package/src/transports/http/security.ts +0 -147
- package/src/transports/http/server/index.ts +0 -397
- package/src/transports/http/server/legacy-sse.ts +0 -87
- package/src/transports/http/server/stateful.ts +0 -222
- package/src/transports/http/server/stateless.ts +0 -42
- package/src/transports/http/types.ts +0 -132
- package/src/types/entities.ts +0 -145
- package/src/types/error-types.ts +0 -92
- package/src/types/errors.ts +0 -200
- package/src/types/filtering.ts +0 -55
- package/src/types/github.ts +0 -216
- package/src/types/index.ts +0 -348
- package/src/utils/error-helpers.ts +0 -78
- package/src/utils/errors/error-response-fields.ts +0 -29
- package/src/utils/errors/suggestions.ts +0 -94
- package/src/utils/github-helpers.ts +0 -33
- package/src/utils/logger.ts +0 -107
- package/src/utils/mcp-logger.ts +0 -155
- package/src/utils/progress-utils.ts +0 -100
- package/src/utils/query-helpers.ts +0 -78
- package/src/utils/resource-annotations.ts +0 -75
- package/src/utils/security-utils.ts +0 -198
- package/src/utils/vector-index-helpers.ts +0 -24
- package/src/vector/vector-search-manager.ts +0 -409
- package/src/version.ts +0 -15
- package/test-server/README.md +0 -193
- package/test-server/code-map.md +0 -399
- package/test-server/test-agent-experience.md +0 -213
- package/test-server/test-filter-instructions.mjs +0 -295
- package/test-server/test-instruction-levels.mjs +0 -102
- package/test-server/test-preflight.md +0 -55
- package/test-server/test-prompts.mjs +0 -185
- package/test-server/test-scheduler.mjs +0 -174
- package/test-server/test-tool-annotations.mjs +0 -115
- package/test-server/test-tools-codemode.md +0 -632
- package/test-server/test-tools-codemode2.md +0 -1218
- package/test-server/test-tools-team.md +0 -215
- package/test-server/test-tools.md +0 -429
- package/test-server/test-tools2.md +0 -361
- package/test-server/test-tools3.md +0 -396
- package/test-server/tool-reference.md +0 -231
- package/tests/README.md +0 -54
- package/tests/auth/auth-context.test.ts +0 -162
- package/tests/auth/authorization-server-discovery.test.ts +0 -265
- package/tests/auth/errors.test.ts +0 -170
- package/tests/auth/middleware.test.ts +0 -585
- package/tests/auth/oauth-resource-server.test.ts +0 -173
- package/tests/auth/scope-map.test.ts +0 -66
- package/tests/auth/scopes.test.ts +0 -347
- package/tests/auth/token-validator.test.ts +0 -271
- package/tests/codemode/api.test.ts +0 -396
- package/tests/codemode/auto-return.test.ts +0 -167
- package/tests/codemode/codemode-tool-handlers.test.ts +0 -197
- package/tests/codemode/sandbox-factory.test.ts +0 -152
- package/tests/codemode/sandbox.test.ts +0 -190
- package/tests/codemode/security.test.ts +0 -242
- package/tests/codemode/worker-sandbox.test.ts +0 -106
- package/tests/constants/icons.test.ts +0 -101
- package/tests/constants/server-instructions.test.ts +0 -514
- package/tests/database/crud-workflow-branches.test.ts +0 -418
- package/tests/database/database-branches.test.ts +0 -132
- package/tests/database/entries-auth-branches.test.ts +0 -390
- package/tests/database/native-connection.test.ts +0 -249
- package/tests/database/shared-helpers.test.ts +0 -103
- package/tests/database/sqlite-adapter.bench.ts +0 -63
- package/tests/database/sqlite-adapter.test.ts +0 -690
- package/tests/database/tags.test.ts +0 -134
- package/tests/e2e/README.md +0 -39
- package/tests/e2e/auth.spec.ts +0 -106
- package/tests/e2e/codemode-abuse.spec.ts +0 -75
- package/tests/e2e/health.spec.ts +0 -63
- package/tests/e2e/helpers.ts +0 -139
- package/tests/e2e/oauth-discovery.spec.ts +0 -102
- package/tests/e2e/oauth-scopes.spec.ts +0 -222
- package/tests/e2e/payloads-admin.spec.ts +0 -76
- package/tests/e2e/payloads-analytics.spec.ts +0 -37
- package/tests/e2e/payloads-backup-restore.spec.ts +0 -102
- package/tests/e2e/payloads-backup.spec.ts +0 -44
- package/tests/e2e/payloads-codemode-api.spec.ts +0 -131
- package/tests/e2e/payloads-codemode-readonly.spec.ts +0 -116
- package/tests/e2e/payloads-codemode.spec.ts +0 -116
- package/tests/e2e/payloads-core.spec.ts +0 -82
- package/tests/e2e/payloads-error-contracts.spec.ts +0 -159
- package/tests/e2e/payloads-export.spec.ts +0 -46
- package/tests/e2e/payloads-github-degradation.spec.ts +0 -73
- package/tests/e2e/payloads-github.spec.ts +0 -176
- package/tests/e2e/payloads-relationships.spec.ts +0 -56
- package/tests/e2e/payloads-search.spec.ts +0 -64
- package/tests/e2e/payloads-team-happy.spec.ts +0 -231
- package/tests/e2e/payloads-team.spec.ts +0 -174
- package/tests/e2e/prompts-expanded.spec.ts +0 -137
- package/tests/e2e/prompts.spec.ts +0 -62
- package/tests/e2e/protocols.spec.ts +0 -134
- package/tests/e2e/rate-limiting.spec.ts +0 -291
- package/tests/e2e/resources-briefing-env.spec.ts +0 -106
- package/tests/e2e/resources-complete.spec.ts +0 -180
- package/tests/e2e/resources-expanded.spec.ts +0 -83
- package/tests/e2e/resources-instructions-levels.spec.ts +0 -145
- package/tests/e2e/resources-templates.spec.ts +0 -123
- package/tests/e2e/resources.spec.ts +0 -103
- package/tests/e2e/scheduler.spec.ts +0 -79
- package/tests/e2e/security.spec.ts +0 -112
- package/tests/e2e/session-advanced.spec.ts +0 -152
- package/tests/e2e/sessions.spec.ts +0 -95
- package/tests/e2e/stateless.spec.ts +0 -79
- package/tests/e2e/streaming.spec.ts +0 -176
- package/tests/e2e/tool-filtering-presets.spec.ts +0 -192
- package/tests/e2e/tool-filtering.spec.ts +0 -77
- package/tests/e2e/tools.spec.ts +0 -111
- package/tests/filtering/tool-filter.test.ts +0 -314
- package/tests/github/client-issues-errors.test.ts +0 -433
- package/tests/github/github-integration-branches.test.ts +0 -490
- package/tests/github/github-integration.test.ts +0 -1015
- package/tests/github/github-managers-branches.test.ts +0 -907
- package/tests/github/pull-requests.test.ts +0 -334
- package/tests/handlers/analytics-branches.test.ts +0 -222
- package/tests/handlers/backup-branches.test.ts +0 -270
- package/tests/handlers/briefing-context-section.test.ts +0 -388
- package/tests/handlers/briefing-github-section.test.ts +0 -392
- package/tests/handlers/briefing-user-message.test.ts +0 -405
- package/tests/handlers/codemode-tools.test.ts +0 -85
- package/tests/handlers/copilot-tools.test.ts +0 -126
- package/tests/handlers/error-path-coverage.test.ts +0 -324
- package/tests/handlers/export-tools.test.ts +0 -203
- package/tests/handlers/github-resource-handlers.test.ts +0 -929
- package/tests/handlers/github-tool-handlers.test.ts +0 -1452
- package/tests/handlers/handler-error-branches.test.ts +0 -346
- package/tests/handlers/help-resource.test.ts +0 -92
- package/tests/handlers/prompt-handler-coverage.test.ts +0 -108
- package/tests/handlers/prompt-handlers.test.ts +0 -131
- package/tests/handlers/resource-handler-coverage.test.ts +0 -281
- package/tests/handlers/resource-handlers.test.ts +0 -357
- package/tests/handlers/resource-prompt-branches.test.ts +0 -495
- package/tests/handlers/search-tool-handlers.test.ts +0 -379
- package/tests/handlers/targeted-gap-closure.test.ts +0 -387
- package/tests/handlers/team-admin.test.ts +0 -291
- package/tests/handlers/team-analytics.test.ts +0 -220
- package/tests/handlers/team-core.test.ts +0 -148
- package/tests/handlers/team-data.test.ts +0 -198
- package/tests/handlers/team-relationships.test.ts +0 -271
- package/tests/handlers/team-resource-handlers.test.ts +0 -161
- package/tests/handlers/team-search.test.ts +0 -134
- package/tests/handlers/team-tool-handlers.test.ts +0 -301
- package/tests/handlers/team-vector.test.ts +0 -213
- package/tests/handlers/template-github-branches.test.ts +0 -676
- package/tests/handlers/tool-annotations.test.ts +0 -90
- package/tests/handlers/tool-handler-coverage.test.ts +0 -514
- package/tests/handlers/tool-handlers.test.ts +0 -510
- package/tests/handlers/tool-output-schemas.test.ts +0 -116
- package/tests/handlers/vector-tool-handlers.test.ts +0 -238
- package/tests/security/sql-injection.test.ts +0 -284
- package/tests/server/mcp-server.bench.ts +0 -55
- package/tests/server/mcp-server.test.ts +0 -1326
- package/tests/server/scheduler.test.ts +0 -400
- package/tests/transports/http-legacy-sse.test.ts +0 -275
- package/tests/transports/http-security.test.ts +0 -322
- package/tests/transports/http-stateful.test.ts +0 -487
- package/tests/transports/http-transport-server.test.ts +0 -301
- package/tests/transports/http-transport.test.ts +0 -771
- package/tests/utils/github-helpers.test.ts +0 -58
- package/tests/utils/logger.test.ts +0 -180
- package/tests/utils/mcp-logger.test.ts +0 -211
- package/tests/utils/progress-utils.test.ts +0 -156
- package/tests/utils/query-helpers.test.ts +0 -80
- package/tests/utils/security-utils.test.ts +0 -82
- package/tests/vector/vector-search-branches.test.ts +0 -111
- package/tests/vector/vector-search-manager.test.ts +0 -375
- package/tests/vector/vector-search.bench.ts +0 -48
- package/tsconfig.json +0 -42
- package/tsup.config.ts +0 -19
- package/vitest.config.ts +0 -25
|
@@ -1,294 +0,0 @@
|
|
|
1
|
-
import type { JournalEntry, EntryType } from '../../../types/index.js'
|
|
2
|
-
import {
|
|
3
|
-
ENTRY_COLUMNS,
|
|
4
|
-
ALIASED_ENTRY_COLUMNS,
|
|
5
|
-
type EntriesSharedContext,
|
|
6
|
-
rowsToEntries,
|
|
7
|
-
} from './shared.js'
|
|
8
|
-
|
|
9
|
-
export function getRecentEntries(context: EntriesSharedContext, limit: number): JournalEntry[] {
|
|
10
|
-
const { db, tagsMgr } = context
|
|
11
|
-
const stmt = db.prepare(`
|
|
12
|
-
SELECT ${ENTRY_COLUMNS} FROM memory_journal
|
|
13
|
-
WHERE deleted_at IS NULL
|
|
14
|
-
ORDER BY timestamp DESC, id DESC LIMIT ?
|
|
15
|
-
`)
|
|
16
|
-
const rows = stmt.all([limit])
|
|
17
|
-
return rowsToEntries(tagsMgr, rows)
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export function getEntriesPage(
|
|
21
|
-
context: EntriesSharedContext,
|
|
22
|
-
offset: number,
|
|
23
|
-
limit: number,
|
|
24
|
-
order: 'asc' | 'desc' = 'desc'
|
|
25
|
-
): JournalEntry[] {
|
|
26
|
-
const { db, tagsMgr } = context
|
|
27
|
-
|
|
28
|
-
const sortDir = order === 'asc' ? 'ASC' : 'DESC'
|
|
29
|
-
|
|
30
|
-
const stmt = db.prepare(`
|
|
31
|
-
SELECT ${ENTRY_COLUMNS} FROM memory_journal
|
|
32
|
-
WHERE deleted_at IS NULL
|
|
33
|
-
ORDER BY timestamp ${sortDir}, id ${sortDir}
|
|
34
|
-
LIMIT ? OFFSET ?
|
|
35
|
-
`)
|
|
36
|
-
const rows = stmt.all([limit, offset])
|
|
37
|
-
|
|
38
|
-
return rowsToEntries(tagsMgr, rows)
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
export function searchEntries(
|
|
42
|
-
context: EntriesSharedContext,
|
|
43
|
-
queryStr: string,
|
|
44
|
-
options?: {
|
|
45
|
-
limit?: number
|
|
46
|
-
isPersonal?: boolean
|
|
47
|
-
projectNumber?: number
|
|
48
|
-
issueNumber?: number
|
|
49
|
-
prNumber?: number
|
|
50
|
-
prStatus?: string
|
|
51
|
-
workflowRunId?: number
|
|
52
|
-
}
|
|
53
|
-
): JournalEntry[] {
|
|
54
|
-
const { db, tagsMgr } = context
|
|
55
|
-
|
|
56
|
-
// Try FTS5 first for relevance-ranked results, fall back to LIKE on syntax error
|
|
57
|
-
if (queryStr.length > 0) {
|
|
58
|
-
try {
|
|
59
|
-
const { sql, params } = buildSearchQuery(queryStr, options, true)
|
|
60
|
-
const stmt = db.prepare(sql)
|
|
61
|
-
const rows = stmt.all(params)
|
|
62
|
-
return rowsToEntries(tagsMgr, rows)
|
|
63
|
-
} catch {
|
|
64
|
-
// FTS5 syntax error (e.g. unbalanced quotes, special chars) — fall back to LIKE
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
const { sql, params } = buildSearchQuery(queryStr, options, false)
|
|
69
|
-
const stmt = db.prepare(sql)
|
|
70
|
-
const rows = stmt.all(params)
|
|
71
|
-
return rowsToEntries(tagsMgr, rows)
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
* Builds the SQL query and params for searchEntries.
|
|
76
|
-
* @param useFts - If true, uses FTS5 MATCH with BM25 ranking. If false, uses LIKE substring matching.
|
|
77
|
-
*/
|
|
78
|
-
function buildSearchQuery(
|
|
79
|
-
queryStr: string,
|
|
80
|
-
options:
|
|
81
|
-
| {
|
|
82
|
-
limit?: number
|
|
83
|
-
isPersonal?: boolean
|
|
84
|
-
projectNumber?: number
|
|
85
|
-
issueNumber?: number
|
|
86
|
-
prNumber?: number
|
|
87
|
-
prStatus?: string
|
|
88
|
-
workflowRunId?: number
|
|
89
|
-
}
|
|
90
|
-
| undefined,
|
|
91
|
-
useFts: boolean
|
|
92
|
-
): { sql: string; params: unknown[] } {
|
|
93
|
-
let query: string
|
|
94
|
-
if (useFts) {
|
|
95
|
-
query = `
|
|
96
|
-
SELECT DISTINCT ${ALIASED_ENTRY_COLUMNS}
|
|
97
|
-
FROM memory_journal e
|
|
98
|
-
JOIN fts_content fts ON fts.rowid = e.id
|
|
99
|
-
`
|
|
100
|
-
} else {
|
|
101
|
-
query = `
|
|
102
|
-
SELECT DISTINCT ${ALIASED_ENTRY_COLUMNS}
|
|
103
|
-
FROM memory_journal e
|
|
104
|
-
`
|
|
105
|
-
}
|
|
106
|
-
const params: unknown[] = []
|
|
107
|
-
const conditions: string[] = ['e.deleted_at IS NULL']
|
|
108
|
-
|
|
109
|
-
if (queryStr.length > 0) {
|
|
110
|
-
if (useFts) {
|
|
111
|
-
conditions.push(`fts_content MATCH ?`)
|
|
112
|
-
params.push(sanitizeFtsQuery(queryStr))
|
|
113
|
-
} else {
|
|
114
|
-
conditions.push(`e.content LIKE '%' || ? || '%'`)
|
|
115
|
-
params.push(queryStr)
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
if (options?.isPersonal !== undefined) {
|
|
120
|
-
conditions.push(`e.is_personal = ?`)
|
|
121
|
-
params.push(options.isPersonal ? 1 : 0)
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
if (options?.projectNumber !== undefined) {
|
|
125
|
-
conditions.push(`e.project_number = ?`)
|
|
126
|
-
params.push(options.projectNumber)
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
if (options?.issueNumber !== undefined) {
|
|
130
|
-
conditions.push(`e.issue_number = ?`)
|
|
131
|
-
params.push(options.issueNumber)
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
if (options?.prNumber !== undefined) {
|
|
135
|
-
conditions.push(`e.pr_number = ?`)
|
|
136
|
-
params.push(options.prNumber)
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
if (options?.prStatus !== undefined) {
|
|
140
|
-
conditions.push(`e.pr_status = ?`)
|
|
141
|
-
params.push(options.prStatus)
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
if (options?.workflowRunId !== undefined) {
|
|
145
|
-
conditions.push(`e.workflow_run_id = ?`)
|
|
146
|
-
params.push(options.workflowRunId)
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
if (conditions.length > 0) {
|
|
150
|
-
query += ` WHERE ${conditions.join(' AND ')}`
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
// FTS5: rank by relevance (BM25), then timestamp for tiebreaking
|
|
154
|
-
// LIKE/no-query: rank by timestamp only
|
|
155
|
-
if (useFts) {
|
|
156
|
-
query += ` ORDER BY rank, e.timestamp DESC, e.id DESC`
|
|
157
|
-
} else {
|
|
158
|
-
query += ` ORDER BY e.timestamp DESC, e.id DESC`
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
query += ` LIMIT ?`
|
|
162
|
-
params.push(options?.limit ?? 10)
|
|
163
|
-
|
|
164
|
-
return { sql: query, params }
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
export function searchByDateRange(
|
|
168
|
-
context: EntriesSharedContext,
|
|
169
|
-
startDate: string,
|
|
170
|
-
endDate: string,
|
|
171
|
-
options?: {
|
|
172
|
-
entryType?: EntryType
|
|
173
|
-
tags?: string[]
|
|
174
|
-
isPersonal?: boolean
|
|
175
|
-
projectNumber?: number
|
|
176
|
-
issueNumber?: number
|
|
177
|
-
prNumber?: number
|
|
178
|
-
workflowRunId?: number
|
|
179
|
-
limit?: number
|
|
180
|
-
}
|
|
181
|
-
): JournalEntry[] {
|
|
182
|
-
const { db, tagsMgr } = context
|
|
183
|
-
|
|
184
|
-
let start = startDate
|
|
185
|
-
if (!start.includes('T')) start += 'T00:00:00.000Z'
|
|
186
|
-
|
|
187
|
-
const conditions = ['deleted_at IS NULL', 'timestamp >= ?']
|
|
188
|
-
const params: unknown[] = [start]
|
|
189
|
-
|
|
190
|
-
if (endDate) {
|
|
191
|
-
conditions.push('timestamp <= ?')
|
|
192
|
-
let end = endDate
|
|
193
|
-
if (!end.includes('T')) end += 'T23:59:59.999Z'
|
|
194
|
-
params.push(end)
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
let query = `
|
|
198
|
-
SELECT DISTINCT ${ALIASED_ENTRY_COLUMNS} FROM memory_journal e
|
|
199
|
-
`
|
|
200
|
-
|
|
201
|
-
if (options?.tags && options.tags.length > 0) {
|
|
202
|
-
query += `
|
|
203
|
-
JOIN entry_tags et ON e.id = et.entry_id
|
|
204
|
-
JOIN tags t ON et.tag_id = t.id
|
|
205
|
-
`
|
|
206
|
-
const placeholders = options.tags.map(() => '?').join(',')
|
|
207
|
-
conditions.push(`t.name IN (${placeholders})`)
|
|
208
|
-
params.push(...options.tags)
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
if (options?.entryType !== undefined) {
|
|
212
|
-
conditions.push(`e.entry_type = ?`)
|
|
213
|
-
params.push(options.entryType)
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
if (options?.isPersonal !== undefined) {
|
|
217
|
-
conditions.push(`e.is_personal = ?`)
|
|
218
|
-
params.push(options.isPersonal ? 1 : 0)
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
if (options?.projectNumber !== undefined) {
|
|
222
|
-
conditions.push(`e.project_number = ?`)
|
|
223
|
-
params.push(options.projectNumber)
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
if (options?.issueNumber !== undefined) {
|
|
227
|
-
conditions.push(`e.issue_number = ?`)
|
|
228
|
-
params.push(options.issueNumber)
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
if (options?.prNumber !== undefined) {
|
|
232
|
-
conditions.push(`e.pr_number = ?`)
|
|
233
|
-
params.push(options.prNumber)
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
if (options?.workflowRunId !== undefined) {
|
|
237
|
-
conditions.push(`e.workflow_run_id = ?`)
|
|
238
|
-
params.push(options.workflowRunId)
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
query += ` WHERE ${conditions.join(' AND ')} ORDER BY e.timestamp DESC, e.id DESC`
|
|
242
|
-
|
|
243
|
-
query += ` LIMIT ?`
|
|
244
|
-
params.push(options?.limit ?? 500)
|
|
245
|
-
|
|
246
|
-
const stmt = db.prepare(query)
|
|
247
|
-
const rows = stmt.all(params)
|
|
248
|
-
|
|
249
|
-
return rowsToEntries(tagsMgr, rows)
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
// ============================================================================
|
|
253
|
-
// FTS5 Helpers
|
|
254
|
-
// ============================================================================
|
|
255
|
-
|
|
256
|
-
/**
|
|
257
|
-
* Sanitize an FTS5 query string to handle porter-stemmer phrase mismatch.
|
|
258
|
-
*
|
|
259
|
-
* FTS5 phrase queries (e.g. `"error handling"`) require exact token sequences.
|
|
260
|
-
* However the porter stemmer stores stems: `handling` → `handl`, so the phrase
|
|
261
|
-
* `"error handling"` never matches even when the content contains "error handling".
|
|
262
|
-
*
|
|
263
|
-
* Fix: detect a *pure* quoted phrase (the entire query is one quoted phrase)
|
|
264
|
-
* and rewrite it as AND-joined individual terms. This lets the stemmer apply
|
|
265
|
-
* to each word and correctly finds documents containing all the terms.
|
|
266
|
-
*
|
|
267
|
-
* Non-phrase queries are passed through unchanged.
|
|
268
|
-
*
|
|
269
|
-
* Examples:
|
|
270
|
-
* `"error handling"` → `error AND handling`
|
|
271
|
-
* `deploy OR release` → `deploy OR release` (unchanged)
|
|
272
|
-
* `auth*` → `auth*` (unchanged)
|
|
273
|
-
* `deploy NOT staging` → `deploy NOT staging` (unchanged)
|
|
274
|
-
*/
|
|
275
|
-
function sanitizeFtsQuery(query: string): string {
|
|
276
|
-
const trimmed = query.trim()
|
|
277
|
-
// Pure phrase: starts and ends with double-quote, no other quotes inside
|
|
278
|
-
if (
|
|
279
|
-
trimmed.startsWith('"') &&
|
|
280
|
-
trimmed.endsWith('"') &&
|
|
281
|
-
trimmed.length > 2 &&
|
|
282
|
-
!trimmed.slice(1, -1).includes('"')
|
|
283
|
-
) {
|
|
284
|
-
const inner = trimmed.slice(1, -1).trim()
|
|
285
|
-
// Split on whitespace, drop empty tokens, join with AND
|
|
286
|
-
const words = inner.split(/\s+/).filter(Boolean)
|
|
287
|
-
if (words.length > 1) {
|
|
288
|
-
return words.join(' AND ')
|
|
289
|
-
}
|
|
290
|
-
// Single-word phrase: strip quotes (no AND needed)
|
|
291
|
-
return inner
|
|
292
|
-
}
|
|
293
|
-
return query
|
|
294
|
-
}
|
|
@@ -1,102 +0,0 @@
|
|
|
1
|
-
import type { JournalEntry } from '../../../types/index.js'
|
|
2
|
-
import type { Database } from 'better-sqlite3'
|
|
3
|
-
import type { TagsManager } from '../tags.js'
|
|
4
|
-
import type { NativeConnectionManager } from '../native-connection.js'
|
|
5
|
-
|
|
6
|
-
export const ENTRY_COLUMNS =
|
|
7
|
-
'id, entry_type as entryType, content, timestamp, is_personal as isPersonal, ' +
|
|
8
|
-
'significance_type as significanceType, auto_context as autoContext, ' +
|
|
9
|
-
'project_number as projectNumber, project_owner as projectOwner, ' +
|
|
10
|
-
'issue_number as issueNumber, issue_url as issueUrl, ' +
|
|
11
|
-
'pr_number as prNumber, pr_url as prUrl, pr_status as prStatus, ' +
|
|
12
|
-
'workflow_run_id as workflowRunId, workflow_name as workflowName, ' +
|
|
13
|
-
'workflow_status as workflowStatus, deleted_at as deletedAt'
|
|
14
|
-
|
|
15
|
-
export const ALIASED_ENTRY_COLUMNS =
|
|
16
|
-
'e.id, e.entry_type as entryType, e.content, e.timestamp, ' +
|
|
17
|
-
'e.is_personal as isPersonal, e.significance_type as significanceType, ' +
|
|
18
|
-
'e.auto_context as autoContext, e.project_number as projectNumber, ' +
|
|
19
|
-
'e.project_owner as projectOwner, e.issue_number as issueNumber, ' +
|
|
20
|
-
'e.issue_url as issueUrl, e.pr_number as prNumber, e.pr_url as prUrl, ' +
|
|
21
|
-
'e.pr_status as prStatus, e.workflow_run_id as workflowRunId, ' +
|
|
22
|
-
'e.workflow_name as workflowName, e.workflow_status as workflowStatus, ' +
|
|
23
|
-
'e.deleted_at as deletedAt'
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* Shared context for entries manager operations
|
|
27
|
-
*/
|
|
28
|
-
export interface EntriesSharedContext {
|
|
29
|
-
ctx: NativeConnectionManager
|
|
30
|
-
tagsMgr: TagsManager
|
|
31
|
-
db: Database
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Convert a generic database row to a JournalEntry by attaching its tags
|
|
36
|
-
*/
|
|
37
|
-
export function rowToEntry(tagsMgr: TagsManager, row: unknown): JournalEntry {
|
|
38
|
-
const r = row as Partial<JournalEntry>
|
|
39
|
-
return {
|
|
40
|
-
...r,
|
|
41
|
-
isPersonal: Boolean(r.isPersonal), // SQLite uses 0/1
|
|
42
|
-
tags: tagsMgr.getTagsForEntry(Number(r.id)),
|
|
43
|
-
} as JournalEntry
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Convert multiple generic database rows to JournalEntries
|
|
48
|
-
*/
|
|
49
|
-
export function rowsToEntries(tagsMgr: TagsManager, rows: unknown[]): JournalEntry[] {
|
|
50
|
-
if (rows.length === 0) return []
|
|
51
|
-
|
|
52
|
-
const entries = rows.map((r) => {
|
|
53
|
-
const p = r as Partial<JournalEntry>
|
|
54
|
-
return {
|
|
55
|
-
...p,
|
|
56
|
-
isPersonal: Boolean(p.isPersonal), // SQLite uses 0/1
|
|
57
|
-
tags: [],
|
|
58
|
-
} as JournalEntry
|
|
59
|
-
})
|
|
60
|
-
|
|
61
|
-
const ids = entries.map((e) => e.id)
|
|
62
|
-
const tagsMap = tagsMgr.batchGetTagsForEntries(ids)
|
|
63
|
-
|
|
64
|
-
for (const entry of entries) {
|
|
65
|
-
entry.tags = tagsMap.get(entry.id) ?? []
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
return entries
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
/**
|
|
72
|
-
* Helper: Safely convert a row from execQuery/get to an object.
|
|
73
|
-
*/
|
|
74
|
-
export function rowToObject(row: unknown): Record<string, unknown> | undefined {
|
|
75
|
-
if (row === null || row === undefined) return undefined
|
|
76
|
-
if (typeof row !== 'object') return undefined
|
|
77
|
-
return row as Record<string, unknown>
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
/**
|
|
81
|
-
* Typed wrapper for `db.prepare(sql).get(…params)`.
|
|
82
|
-
* Returns the row cast to a `Record<string, unknown>` for safe access.
|
|
83
|
-
*/
|
|
84
|
-
export function queryRow(
|
|
85
|
-
db: Database,
|
|
86
|
-
sql: string,
|
|
87
|
-
...params: unknown[]
|
|
88
|
-
): Record<string, unknown> | undefined {
|
|
89
|
-
return db.prepare(sql).get(...params) as Record<string, unknown> | undefined
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
/**
|
|
93
|
-
* Typed wrapper for `db.prepare(sql).all(…params)`.
|
|
94
|
-
* Returns rows cast to `Record<string, unknown>[]` for safe access.
|
|
95
|
-
*/
|
|
96
|
-
export function queryRows(
|
|
97
|
-
db: Database,
|
|
98
|
-
sql: string,
|
|
99
|
-
...params: unknown[]
|
|
100
|
-
): Record<string, unknown>[] {
|
|
101
|
-
return db.prepare(sql).all(...params) as Record<string, unknown>[]
|
|
102
|
-
}
|
|
@@ -1,162 +0,0 @@
|
|
|
1
|
-
import type { EntriesSharedContext } from './shared.js'
|
|
2
|
-
import { validateDateFormatPattern } from '../../../utils/security-utils.js'
|
|
3
|
-
|
|
4
|
-
/** Maximum number of period rows returned in the activity timeline */
|
|
5
|
-
const MAX_PERIOD_ROWS = 52
|
|
6
|
-
|
|
7
|
-
export function getStatistics(
|
|
8
|
-
context: EntriesSharedContext,
|
|
9
|
-
groupBy: 'day' | 'week' | 'month' | 'year' = 'week',
|
|
10
|
-
startDate?: string,
|
|
11
|
-
endDate?: string,
|
|
12
|
-
projectBreakdown?: boolean
|
|
13
|
-
): Record<string, unknown> {
|
|
14
|
-
const { db } = context
|
|
15
|
-
|
|
16
|
-
let dateFilter = ''
|
|
17
|
-
const dateParams: unknown[] = []
|
|
18
|
-
if (startDate) {
|
|
19
|
-
dateFilter += ' AND DATE(timestamp) >= DATE(?)'
|
|
20
|
-
dateParams.push(startDate)
|
|
21
|
-
}
|
|
22
|
-
if (endDate) {
|
|
23
|
-
dateFilter += ' AND DATE(timestamp) <= DATE(?)'
|
|
24
|
-
dateParams.push(endDate)
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
let totalEntries: number
|
|
28
|
-
const entriesByType: Record<string, number> = {}
|
|
29
|
-
|
|
30
|
-
if (dateParams.length > 0) {
|
|
31
|
-
const countRow = db
|
|
32
|
-
.prepare(
|
|
33
|
-
`SELECT COUNT(*) as count FROM memory_journal WHERE deleted_at IS NULL${dateFilter}`
|
|
34
|
-
)
|
|
35
|
-
.get(...dateParams) as { count: number }
|
|
36
|
-
totalEntries = countRow?.count ?? 0
|
|
37
|
-
|
|
38
|
-
const typeRows = db
|
|
39
|
-
.prepare(
|
|
40
|
-
`SELECT entry_type, COUNT(*) as count FROM memory_journal WHERE deleted_at IS NULL${dateFilter} GROUP BY entry_type`
|
|
41
|
-
)
|
|
42
|
-
.all(...dateParams) as { entry_type: string; count: number }[]
|
|
43
|
-
for (const row of typeRows) {
|
|
44
|
-
entriesByType[row.entry_type] = row.count
|
|
45
|
-
}
|
|
46
|
-
} else {
|
|
47
|
-
const countRow = db
|
|
48
|
-
.prepare('SELECT COUNT(*) as count FROM memory_journal WHERE deleted_at IS NULL')
|
|
49
|
-
.get() as { count: number }
|
|
50
|
-
totalEntries = countRow?.count ?? 0
|
|
51
|
-
|
|
52
|
-
const typeRows = db
|
|
53
|
-
.prepare(
|
|
54
|
-
`SELECT entry_type, COUNT(*) as count FROM memory_journal WHERE deleted_at IS NULL GROUP BY entry_type`
|
|
55
|
-
)
|
|
56
|
-
.all() as { entry_type: string; count: number }[]
|
|
57
|
-
for (const row of typeRows) {
|
|
58
|
-
entriesByType[row.entry_type] = row.count
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
const dateFormat = validateDateFormatPattern(groupBy === 'year' ? 'month' : groupBy)
|
|
63
|
-
const periodRows = db
|
|
64
|
-
.prepare(
|
|
65
|
-
`SELECT
|
|
66
|
-
strftime('${dateFormat}', timestamp) as period,
|
|
67
|
-
COUNT(*) as total_count,
|
|
68
|
-
SUM(CASE WHEN significance_type IS NOT NULL THEN 1 ELSE 0 END) as significant_count
|
|
69
|
-
FROM memory_journal
|
|
70
|
-
WHERE deleted_at IS NULL${dateFilter}
|
|
71
|
-
GROUP BY period
|
|
72
|
-
ORDER BY period DESC
|
|
73
|
-
LIMIT ${String(MAX_PERIOD_ROWS)}`
|
|
74
|
-
)
|
|
75
|
-
.all(...dateParams) as { period: string; total_count: number; significant_count: number }[]
|
|
76
|
-
|
|
77
|
-
const entriesByPeriod = periodRows.map((r) => ({
|
|
78
|
-
period: r.period,
|
|
79
|
-
count: r.total_count,
|
|
80
|
-
}))
|
|
81
|
-
|
|
82
|
-
const decisionDensity = periodRows
|
|
83
|
-
.filter((r) => r.significant_count > 0)
|
|
84
|
-
.map((r) => ({
|
|
85
|
-
period: r.period,
|
|
86
|
-
significantCount: r.significant_count,
|
|
87
|
-
}))
|
|
88
|
-
|
|
89
|
-
const relCountRow = db.prepare('SELECT COUNT(*) as count FROM relationships').get() as {
|
|
90
|
-
count: number
|
|
91
|
-
}
|
|
92
|
-
const relTypeRows = db
|
|
93
|
-
.prepare(
|
|
94
|
-
`
|
|
95
|
-
SELECT relationship_type, COUNT(*) as count
|
|
96
|
-
FROM relationships
|
|
97
|
-
WHERE relationship_type IN ('blocked_by', 'resolved', 'caused')
|
|
98
|
-
GROUP BY relationship_type
|
|
99
|
-
`
|
|
100
|
-
)
|
|
101
|
-
.all() as { relationship_type: 'blocked_by' | 'resolved' | 'caused'; count: number }[]
|
|
102
|
-
|
|
103
|
-
const totalRelationships = relCountRow?.count ?? 0
|
|
104
|
-
const avgPerEntry = totalEntries > 0 ? totalRelationships / totalEntries : 0
|
|
105
|
-
|
|
106
|
-
const currentPeriod = entriesByPeriod[0]?.period ?? ''
|
|
107
|
-
const previousPeriod = entriesByPeriod[1]?.period ?? ''
|
|
108
|
-
const currentCount = entriesByPeriod[0]?.count ?? 0
|
|
109
|
-
const previousCount = entriesByPeriod[1]?.count ?? 0
|
|
110
|
-
const growthPercent =
|
|
111
|
-
previousCount > 0
|
|
112
|
-
? Math.round(((currentCount - previousCount) / previousCount) * 100)
|
|
113
|
-
: null
|
|
114
|
-
|
|
115
|
-
const causalMetrics = { blocked_by: 0, resolved: 0, caused: 0 }
|
|
116
|
-
for (const row of relTypeRows) {
|
|
117
|
-
causalMetrics[row.relationship_type] = row.count
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
const result: Record<string, unknown> = {
|
|
121
|
-
totalEntries,
|
|
122
|
-
entriesByType,
|
|
123
|
-
entriesByPeriod,
|
|
124
|
-
decisionDensity,
|
|
125
|
-
relationshipComplexity: {
|
|
126
|
-
totalRelationships,
|
|
127
|
-
avgPerEntry: Math.round(avgPerEntry * 100) / 100,
|
|
128
|
-
},
|
|
129
|
-
activityTrend: {
|
|
130
|
-
currentPeriod,
|
|
131
|
-
previousPeriod,
|
|
132
|
-
growthPercent,
|
|
133
|
-
},
|
|
134
|
-
causalMetrics,
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
if (startDate || endDate) {
|
|
138
|
-
result['dateRange'] = {
|
|
139
|
-
startDate: startDate ?? '',
|
|
140
|
-
endDate: endDate ?? '',
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
if (projectBreakdown) {
|
|
145
|
-
const projRows = db
|
|
146
|
-
.prepare(
|
|
147
|
-
`SELECT project_number, COUNT(*) as entry_count
|
|
148
|
-
FROM memory_journal
|
|
149
|
-
WHERE deleted_at IS NULL AND project_number IS NOT NULL${dateFilter}
|
|
150
|
-
GROUP BY project_number
|
|
151
|
-
ORDER BY entry_count DESC`
|
|
152
|
-
)
|
|
153
|
-
.all(...dateParams) as { project_number: number; entry_count: number }[]
|
|
154
|
-
|
|
155
|
-
result['projectBreakdown'] = projRows.map((r) => ({
|
|
156
|
-
project_number: r.project_number,
|
|
157
|
-
entry_count: r.entry_count,
|
|
158
|
-
}))
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
return result
|
|
162
|
-
}
|