memory-journal-mcp 4.4.2 → 5.0.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/.github/workflows/codeql.yml +1 -6
- package/.github/workflows/docker-publish.yml +15 -49
- package/.github/workflows/lint-and-test.yml +1 -1
- package/.github/workflows/secrets-scanning.yml +4 -3
- package/.github/workflows/security-update.yml +3 -3
- package/CHANGELOG.md +213 -0
- package/CONTRIBUTING.md +132 -97
- package/DOCKER_README.md +184 -235
- package/Dockerfile +27 -24
- package/README.md +218 -190
- package/SECURITY.md +27 -35
- package/dist/cli.js +16 -1
- package/dist/cli.js.map +1 -1
- package/dist/constants/ServerInstructions.d.ts +5 -1
- package/dist/constants/ServerInstructions.d.ts.map +1 -1
- package/dist/constants/ServerInstructions.js +133 -73
- package/dist/constants/ServerInstructions.js.map +1 -1
- package/dist/constants/icons.d.ts +2 -2
- package/dist/constants/icons.d.ts.map +1 -1
- package/dist/constants/icons.js +7 -6
- package/dist/constants/icons.js.map +1 -1
- package/dist/database/SqliteAdapter.d.ts +37 -24
- package/dist/database/SqliteAdapter.d.ts.map +1 -1
- package/dist/database/SqliteAdapter.js +319 -157
- package/dist/database/SqliteAdapter.js.map +1 -1
- package/dist/database/schema.d.ts +45 -0
- package/dist/database/schema.d.ts.map +1 -0
- package/dist/database/schema.js +92 -0
- package/dist/database/schema.js.map +1 -0
- package/dist/filtering/ToolFilter.d.ts +1 -1
- package/dist/filtering/ToolFilter.d.ts.map +1 -1
- package/dist/filtering/ToolFilter.js +13 -2
- package/dist/filtering/ToolFilter.js.map +1 -1
- package/dist/github/GitHubIntegration.d.ts.map +1 -1
- package/dist/github/GitHubIntegration.js +1 -3
- package/dist/github/GitHubIntegration.js.map +1 -1
- package/dist/handlers/prompts/github.d.ts +12 -0
- package/dist/handlers/prompts/github.d.ts.map +1 -0
- package/dist/handlers/prompts/github.js +178 -0
- package/dist/handlers/prompts/github.js.map +1 -0
- package/dist/handlers/prompts/index.d.ts +23 -2
- package/dist/handlers/prompts/index.d.ts.map +1 -1
- package/dist/handlers/prompts/index.js +7 -432
- package/dist/handlers/prompts/index.js.map +1 -1
- package/dist/handlers/prompts/workflow.d.ts +12 -0
- package/dist/handlers/prompts/workflow.d.ts.map +1 -0
- package/dist/handlers/prompts/workflow.js +277 -0
- package/dist/handlers/prompts/workflow.js.map +1 -0
- package/dist/handlers/resources/core.d.ts +11 -0
- package/dist/handlers/resources/core.d.ts.map +1 -0
- package/dist/handlers/resources/core.js +433 -0
- package/dist/handlers/resources/core.js.map +1 -0
- package/dist/handlers/resources/github.d.ts +11 -0
- package/dist/handlers/resources/github.d.ts.map +1 -0
- package/dist/handlers/resources/github.js +314 -0
- package/dist/handlers/resources/github.js.map +1 -0
- package/dist/handlers/resources/graph.d.ts +11 -0
- package/dist/handlers/resources/graph.d.ts.map +1 -0
- package/dist/handlers/resources/graph.js +204 -0
- package/dist/handlers/resources/graph.js.map +1 -0
- package/dist/handlers/resources/index.d.ts +5 -20
- package/dist/handlers/resources/index.d.ts.map +1 -1
- package/dist/handlers/resources/index.js +16 -1278
- package/dist/handlers/resources/index.js.map +1 -1
- package/dist/handlers/resources/shared.d.ts +60 -0
- package/dist/handlers/resources/shared.d.ts.map +1 -0
- package/dist/handlers/resources/shared.js +49 -0
- package/dist/handlers/resources/shared.js.map +1 -0
- package/dist/handlers/resources/team.d.ts +13 -0
- package/dist/handlers/resources/team.d.ts.map +1 -0
- package/dist/handlers/resources/team.js +119 -0
- package/dist/handlers/resources/team.js.map +1 -0
- package/dist/handlers/resources/templates.d.ts +13 -0
- package/dist/handlers/resources/templates.d.ts.map +1 -0
- package/dist/handlers/resources/templates.js +310 -0
- package/dist/handlers/resources/templates.js.map +1 -0
- package/dist/handlers/tools/admin.d.ts +8 -0
- package/dist/handlers/tools/admin.d.ts.map +1 -0
- package/dist/handlers/tools/admin.js +270 -0
- package/dist/handlers/tools/admin.js.map +1 -0
- package/dist/handlers/tools/analytics.d.ts +8 -0
- package/dist/handlers/tools/analytics.d.ts.map +1 -0
- package/dist/handlers/tools/analytics.js +256 -0
- package/dist/handlers/tools/analytics.js.map +1 -0
- package/dist/handlers/tools/backup.d.ts +8 -0
- package/dist/handlers/tools/backup.d.ts.map +1 -0
- package/dist/handlers/tools/backup.js +224 -0
- package/dist/handlers/tools/backup.js.map +1 -0
- package/dist/handlers/tools/core.d.ts +9 -0
- package/dist/handlers/tools/core.d.ts.map +1 -0
- package/dist/handlers/tools/core.js +326 -0
- package/dist/handlers/tools/core.js.map +1 -0
- package/dist/handlers/tools/export.d.ts +8 -0
- package/dist/handlers/tools/export.d.ts.map +1 -0
- package/dist/handlers/tools/export.js +89 -0
- package/dist/handlers/tools/export.js.map +1 -0
- package/dist/handlers/tools/github/helpers.d.ts +34 -0
- package/dist/handlers/tools/github/helpers.d.ts.map +1 -0
- package/dist/handlers/tools/github/helpers.js +52 -0
- package/dist/handlers/tools/github/helpers.js.map +1 -0
- package/dist/handlers/tools/github/insights-tools.d.ts +8 -0
- package/dist/handlers/tools/github/insights-tools.d.ts.map +1 -0
- package/dist/handlers/tools/github/insights-tools.js +104 -0
- package/dist/handlers/tools/github/insights-tools.js.map +1 -0
- package/dist/handlers/tools/github/issue-tools.d.ts +8 -0
- package/dist/handlers/tools/github/issue-tools.d.ts.map +1 -0
- package/dist/handlers/tools/github/issue-tools.js +359 -0
- package/dist/handlers/tools/github/issue-tools.js.map +1 -0
- package/dist/handlers/tools/github/kanban-tools.d.ts +8 -0
- package/dist/handlers/tools/github/kanban-tools.d.ts.map +1 -0
- package/dist/handlers/tools/github/kanban-tools.js +108 -0
- package/dist/handlers/tools/github/kanban-tools.js.map +1 -0
- package/dist/handlers/tools/github/milestone-tools.d.ts +9 -0
- package/dist/handlers/tools/github/milestone-tools.d.ts.map +1 -0
- package/dist/handlers/tools/github/milestone-tools.js +302 -0
- package/dist/handlers/tools/github/milestone-tools.js.map +1 -0
- package/dist/handlers/tools/github/mutation-tools.d.ts +12 -0
- package/dist/handlers/tools/github/mutation-tools.d.ts.map +1 -0
- package/dist/handlers/tools/github/mutation-tools.js +15 -0
- package/dist/handlers/tools/github/mutation-tools.js.map +1 -0
- package/dist/handlers/tools/github/read-tools.d.ts +8 -0
- package/dist/handlers/tools/github/read-tools.d.ts.map +1 -0
- package/dist/handlers/tools/github/read-tools.js +260 -0
- package/dist/handlers/tools/github/read-tools.js.map +1 -0
- package/dist/handlers/tools/github/schemas.d.ts +467 -0
- package/dist/handlers/tools/github/schemas.d.ts.map +1 -0
- package/dist/handlers/tools/github/schemas.js +335 -0
- package/dist/handlers/tools/github/schemas.js.map +1 -0
- package/dist/handlers/tools/github.d.ts +14 -0
- package/dist/handlers/tools/github.d.ts.map +1 -0
- package/dist/handlers/tools/github.js +28 -0
- package/dist/handlers/tools/github.js.map +1 -0
- package/dist/handlers/tools/index.d.ts +15 -20
- package/dist/handlers/tools/index.d.ts.map +1 -1
- package/dist/handlers/tools/index.js +117 -2909
- package/dist/handlers/tools/index.js.map +1 -1
- package/dist/handlers/tools/relationships.d.ts +8 -0
- package/dist/handlers/tools/relationships.d.ts.map +1 -0
- package/dist/handlers/tools/relationships.js +308 -0
- package/dist/handlers/tools/relationships.js.map +1 -0
- package/dist/handlers/tools/schemas.d.ts +108 -0
- package/dist/handlers/tools/schemas.d.ts.map +1 -0
- package/dist/handlers/tools/schemas.js +122 -0
- package/dist/handlers/tools/schemas.js.map +1 -0
- package/dist/handlers/tools/search.d.ts +8 -0
- package/dist/handlers/tools/search.d.ts.map +1 -0
- package/dist/handlers/tools/search.js +282 -0
- package/dist/handlers/tools/search.js.map +1 -0
- package/dist/handlers/tools/team.d.ts +11 -0
- package/dist/handlers/tools/team.d.ts.map +1 -0
- package/dist/handlers/tools/team.js +239 -0
- package/dist/handlers/tools/team.js.map +1 -0
- package/dist/server/McpServer.d.ts +4 -0
- package/dist/server/McpServer.d.ts.map +1 -1
- package/dist/server/McpServer.js +48 -297
- package/dist/server/McpServer.js.map +1 -1
- package/dist/server/Scheduler.d.ts +91 -0
- package/dist/server/Scheduler.d.ts.map +1 -0
- package/dist/server/Scheduler.js +201 -0
- package/dist/server/Scheduler.js.map +1 -0
- package/dist/transports/http.d.ts +66 -0
- package/dist/transports/http.d.ts.map +1 -0
- package/dist/transports/http.js +519 -0
- package/dist/transports/http.js.map +1 -0
- package/dist/types/entities.d.ts +101 -0
- package/dist/types/entities.d.ts.map +1 -0
- package/dist/types/entities.js +5 -0
- package/dist/types/entities.js.map +1 -0
- package/dist/types/filtering.d.ts +34 -0
- package/dist/types/filtering.d.ts.map +1 -0
- package/dist/types/filtering.js +5 -0
- package/dist/types/filtering.js.map +1 -0
- package/dist/types/github.d.ts +166 -0
- package/dist/types/github.d.ts.map +1 -0
- package/dist/types/github.js +5 -0
- package/dist/types/github.js.map +1 -0
- package/dist/types/index.d.ts +35 -292
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +2 -2
- package/dist/types/index.js.map +1 -1
- package/dist/utils/error-helpers.d.ts +37 -0
- package/dist/utils/error-helpers.d.ts.map +1 -0
- package/dist/utils/error-helpers.js +47 -0
- package/dist/utils/error-helpers.js.map +1 -0
- package/dist/utils/logger.d.ts.map +1 -1
- package/dist/utils/logger.js +6 -3
- package/dist/utils/logger.js.map +1 -1
- package/dist/utils/security-utils.d.ts +0 -21
- package/dist/utils/security-utils.d.ts.map +1 -1
- package/dist/utils/security-utils.js +0 -47
- package/dist/utils/security-utils.js.map +1 -1
- package/dist/vector/VectorSearchManager.d.ts.map +1 -1
- package/dist/vector/VectorSearchManager.js +9 -32
- package/dist/vector/VectorSearchManager.js.map +1 -1
- package/docker-compose.yml +11 -2
- package/hooks/README.md +107 -0
- package/hooks/cursor/hooks.json +10 -0
- package/hooks/cursor/memory-journal.mdc +22 -0
- package/hooks/cursor/session-end.sh +19 -0
- package/hooks/kilo-code/session-end-mode.json +11 -0
- package/hooks/kiro/session-end.md +13 -0
- package/mcp-config-example.json +1 -0
- package/package.json +11 -9
- package/playwright.config.ts +29 -0
- package/releases/v4.5.0.md +116 -0
- package/releases/v5.0.0.md +105 -0
- package/scripts/generate-server-instructions.ts +176 -0
- package/scripts/server-instructions-function-body.ts +77 -0
- package/server.json +3 -3
- package/src/cli.ts +45 -1
- package/src/constants/ServerInstructions.ts +133 -73
- package/src/constants/icons.ts +8 -7
- package/src/constants/server-instructions.md +268 -0
- package/src/database/SqliteAdapter.ts +358 -192
- package/src/database/schema.ts +125 -0
- package/src/filtering/ToolFilter.ts +13 -2
- package/src/github/GitHubIntegration.ts +1 -3
- package/src/handlers/prompts/github.ts +209 -0
- package/src/handlers/prompts/index.ts +10 -499
- package/src/handlers/prompts/workflow.ts +314 -0
- package/src/handlers/resources/core.ts +528 -0
- package/src/handlers/resources/github.ts +358 -0
- package/src/handlers/resources/graph.ts +254 -0
- package/src/handlers/resources/index.ts +23 -1570
- package/src/handlers/resources/shared.ts +103 -0
- package/src/handlers/resources/team.ts +133 -0
- package/src/handlers/resources/templates.ts +374 -0
- package/src/handlers/tools/admin.ts +285 -0
- package/src/handlers/tools/analytics.ts +301 -0
- package/src/handlers/tools/backup.ts +242 -0
- package/src/handlers/tools/core.ts +350 -0
- package/src/handlers/tools/export.ts +115 -0
- package/src/handlers/tools/github/helpers.ts +86 -0
- package/src/handlers/tools/github/insights-tools.ts +119 -0
- package/src/handlers/tools/github/issue-tools.ts +439 -0
- package/src/handlers/tools/github/kanban-tools.ts +134 -0
- package/src/handlers/tools/github/milestone-tools.ts +392 -0
- package/src/handlers/tools/github/mutation-tools.ts +17 -0
- package/src/handlers/tools/github/read-tools.ts +328 -0
- package/src/handlers/tools/github/schemas.ts +369 -0
- package/src/handlers/tools/github.ts +36 -0
- package/src/handlers/tools/index.ts +144 -3325
- package/src/handlers/tools/relationships.ts +358 -0
- package/src/handlers/tools/schemas.ts +132 -0
- package/src/handlers/tools/search.ts +343 -0
- package/src/handlers/tools/team.ts +273 -0
- package/src/server/McpServer.ts +63 -358
- package/src/server/Scheduler.ts +278 -0
- package/src/transports/http.ts +635 -0
- package/src/types/entities.ts +145 -0
- package/src/types/filtering.ts +54 -0
- package/src/types/github.ts +180 -0
- package/src/types/index.ts +67 -375
- package/src/utils/error-helpers.ts +52 -0
- package/src/utils/logger.ts +6 -3
- package/src/utils/security-utils.ts +0 -52
- package/src/vector/VectorSearchManager.ts +9 -33
- package/tests/constants/icons.test.ts +1 -2
- package/tests/constants/server-instructions.test.ts +30 -4
- package/tests/database/sqlite-adapter.test.ts +91 -7
- package/tests/e2e/auth.spec.ts +154 -0
- package/tests/e2e/health.spec.ts +63 -0
- package/tests/e2e/protocols.spec.ts +134 -0
- package/tests/e2e/resources.spec.ts +103 -0
- package/tests/e2e/scheduler.spec.ts +79 -0
- package/tests/e2e/security.spec.ts +91 -0
- package/tests/e2e/sessions.spec.ts +95 -0
- package/tests/e2e/stateless.spec.ts +121 -0
- package/tests/e2e/tools.spec.ts +111 -0
- package/tests/filtering/tool-filter.test.ts +46 -0
- package/tests/handlers/error-path-coverage.test.ts +324 -0
- package/tests/handlers/github-resource-handlers.test.ts +453 -0
- package/tests/handlers/github-tool-handlers.test.ts +899 -0
- package/tests/handlers/prompt-handler-coverage.test.ts +106 -0
- package/tests/handlers/prompt-handlers.test.ts +40 -0
- package/tests/handlers/resource-handler-coverage.test.ts +181 -0
- package/tests/handlers/resource-handlers.test.ts +33 -9
- package/tests/handlers/search-tool-handlers.test.ts +272 -0
- package/tests/handlers/targeted-gap-closure.test.ts +387 -0
- package/tests/handlers/team-resource-handlers.test.ts +156 -0
- package/tests/handlers/team-tool-handlers.test.ts +301 -0
- package/tests/handlers/tool-handler-coverage.test.ts +469 -0
- package/tests/handlers/tool-handlers.test.ts +2 -2
- package/tests/security/sql-injection.test.ts +3 -54
- package/tests/server/mcp-server.test.ts +503 -8
- package/tests/server/scheduler.test.ts +400 -0
- package/tests/transports/http-transport.test.ts +620 -0
- package/tests/vector/vector-search-manager.test.ts +60 -0
- package/vitest.config.ts +4 -1
- package/.memory-journal-team.db +0 -0
- package/.vscode/settings.json +0 -84
|
@@ -506,6 +506,53 @@ describe('GitHub Tool Handlers', () => {
|
|
|
506
506
|
expect(result.journalEntry.id).toBeGreaterThan(0)
|
|
507
507
|
})
|
|
508
508
|
|
|
509
|
+
it('should close issue with move_to_done', async () => {
|
|
510
|
+
const github = createMockGitHub({
|
|
511
|
+
getProjectKanban: vi.fn().mockResolvedValue({
|
|
512
|
+
projectId: 'PVT_1',
|
|
513
|
+
projectTitle: 'Board',
|
|
514
|
+
statusFieldId: 'FIELD_1',
|
|
515
|
+
statusOptions: [
|
|
516
|
+
{ id: 'OPT_DONE', name: 'Done' },
|
|
517
|
+
{ id: 'OPT_TODO', name: 'Todo' },
|
|
518
|
+
],
|
|
519
|
+
columns: [
|
|
520
|
+
{
|
|
521
|
+
status: 'Todo',
|
|
522
|
+
items: [
|
|
523
|
+
{
|
|
524
|
+
id: 'PVTITEM_ISSUE1',
|
|
525
|
+
title: 'Test Issue',
|
|
526
|
+
type: 'ISSUE',
|
|
527
|
+
number: 1,
|
|
528
|
+
},
|
|
529
|
+
],
|
|
530
|
+
},
|
|
531
|
+
],
|
|
532
|
+
totalItems: 1,
|
|
533
|
+
}),
|
|
534
|
+
})
|
|
535
|
+
|
|
536
|
+
const result = (await callTool(
|
|
537
|
+
'close_github_issue_with_entry',
|
|
538
|
+
{
|
|
539
|
+
issue_number: 1,
|
|
540
|
+
resolution_notes: 'Done!',
|
|
541
|
+
move_to_done: true,
|
|
542
|
+
project_number: 1,
|
|
543
|
+
},
|
|
544
|
+
db,
|
|
545
|
+
undefined,
|
|
546
|
+
github
|
|
547
|
+
)) as {
|
|
548
|
+
success: boolean
|
|
549
|
+
issue: { number: number }
|
|
550
|
+
kanbanMove?: { success: boolean }
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
expect(result.success).toBe(true)
|
|
554
|
+
})
|
|
555
|
+
|
|
509
556
|
it('should return error when issue not found', async () => {
|
|
510
557
|
const github = createMockGitHub({
|
|
511
558
|
getIssue: vi.fn().mockResolvedValue(null),
|
|
@@ -553,4 +600,856 @@ describe('GitHub Tool Handlers', () => {
|
|
|
553
600
|
expect(result.error).toContain('GitHub integration not available')
|
|
554
601
|
})
|
|
555
602
|
})
|
|
603
|
+
|
|
604
|
+
// ========================================================================
|
|
605
|
+
// Repository Insights
|
|
606
|
+
// ========================================================================
|
|
607
|
+
|
|
608
|
+
describe('get_repo_insights', () => {
|
|
609
|
+
it('should return stars section by default', async () => {
|
|
610
|
+
const github = createMockGitHub({
|
|
611
|
+
getRepoStats: vi.fn().mockResolvedValue({
|
|
612
|
+
stars: 42,
|
|
613
|
+
forks: 5,
|
|
614
|
+
watchers: 3,
|
|
615
|
+
openIssues: 2,
|
|
616
|
+
size: 100,
|
|
617
|
+
defaultBranch: 'main',
|
|
618
|
+
}),
|
|
619
|
+
})
|
|
620
|
+
|
|
621
|
+
const result = (await callTool(
|
|
622
|
+
'get_repo_insights',
|
|
623
|
+
{},
|
|
624
|
+
db,
|
|
625
|
+
undefined,
|
|
626
|
+
github
|
|
627
|
+
)) as Record<string, unknown>
|
|
628
|
+
|
|
629
|
+
expect(result['owner']).toBe('testowner')
|
|
630
|
+
expect(result['repo']).toBe('testrepo')
|
|
631
|
+
expect(result['stars']).toBe(42)
|
|
632
|
+
expect(result['forks']).toBe(5)
|
|
633
|
+
})
|
|
634
|
+
|
|
635
|
+
it('should return traffic section', async () => {
|
|
636
|
+
const github = createMockGitHub({
|
|
637
|
+
getTrafficData: vi.fn().mockResolvedValue({
|
|
638
|
+
views: { total: 100, uniques: 50 },
|
|
639
|
+
clones: { total: 20, uniques: 10 },
|
|
640
|
+
}),
|
|
641
|
+
})
|
|
642
|
+
|
|
643
|
+
const result = (await callTool(
|
|
644
|
+
'get_repo_insights',
|
|
645
|
+
{ sections: 'traffic' },
|
|
646
|
+
db,
|
|
647
|
+
undefined,
|
|
648
|
+
github
|
|
649
|
+
)) as Record<string, unknown>
|
|
650
|
+
|
|
651
|
+
expect(result['traffic']).toBeDefined()
|
|
652
|
+
})
|
|
653
|
+
|
|
654
|
+
it('should return referrers section', async () => {
|
|
655
|
+
const github = createMockGitHub({
|
|
656
|
+
getTopReferrers: vi
|
|
657
|
+
.fn()
|
|
658
|
+
.mockResolvedValue([{ referrer: 'google.com', count: 10, uniques: 5 }]),
|
|
659
|
+
})
|
|
660
|
+
|
|
661
|
+
const result = (await callTool(
|
|
662
|
+
'get_repo_insights',
|
|
663
|
+
{ sections: 'referrers' },
|
|
664
|
+
db,
|
|
665
|
+
undefined,
|
|
666
|
+
github
|
|
667
|
+
)) as Record<string, unknown>
|
|
668
|
+
|
|
669
|
+
expect(result['referrers']).toBeDefined()
|
|
670
|
+
})
|
|
671
|
+
|
|
672
|
+
it('should return paths section', async () => {
|
|
673
|
+
const github = createMockGitHub({
|
|
674
|
+
getPopularPaths: vi
|
|
675
|
+
.fn()
|
|
676
|
+
.mockResolvedValue([
|
|
677
|
+
{ path: '/readme', title: 'README', count: 50, uniques: 25 },
|
|
678
|
+
]),
|
|
679
|
+
})
|
|
680
|
+
|
|
681
|
+
const result = (await callTool(
|
|
682
|
+
'get_repo_insights',
|
|
683
|
+
{ sections: 'paths' },
|
|
684
|
+
db,
|
|
685
|
+
undefined,
|
|
686
|
+
github
|
|
687
|
+
)) as Record<string, unknown>
|
|
688
|
+
|
|
689
|
+
expect(result['paths']).toBeDefined()
|
|
690
|
+
})
|
|
691
|
+
|
|
692
|
+
it('should return all sections', async () => {
|
|
693
|
+
const github = createMockGitHub({
|
|
694
|
+
getRepoStats: vi.fn().mockResolvedValue({
|
|
695
|
+
stars: 42,
|
|
696
|
+
forks: 5,
|
|
697
|
+
watchers: 3,
|
|
698
|
+
openIssues: 2,
|
|
699
|
+
size: 100,
|
|
700
|
+
defaultBranch: 'main',
|
|
701
|
+
}),
|
|
702
|
+
getTrafficData: vi.fn().mockResolvedValue({
|
|
703
|
+
views: { total: 100, uniques: 50 },
|
|
704
|
+
clones: { total: 20, uniques: 10 },
|
|
705
|
+
}),
|
|
706
|
+
getTopReferrers: vi.fn().mockResolvedValue([]),
|
|
707
|
+
getPopularPaths: vi.fn().mockResolvedValue([]),
|
|
708
|
+
})
|
|
709
|
+
|
|
710
|
+
const result = (await callTool(
|
|
711
|
+
'get_repo_insights',
|
|
712
|
+
{ sections: 'all' },
|
|
713
|
+
db,
|
|
714
|
+
undefined,
|
|
715
|
+
github
|
|
716
|
+
)) as Record<string, unknown>
|
|
717
|
+
|
|
718
|
+
expect(result['stars']).toBe(42)
|
|
719
|
+
expect(result['traffic']).toBeDefined()
|
|
720
|
+
expect(result['referrers']).toBeDefined()
|
|
721
|
+
expect(result['paths']).toBeDefined()
|
|
722
|
+
// 'all' section includes size and defaultBranch
|
|
723
|
+
expect(result['size']).toBe(100)
|
|
724
|
+
expect(result['defaultBranch']).toBe('main')
|
|
725
|
+
})
|
|
726
|
+
|
|
727
|
+
it('should return error when no github', async () => {
|
|
728
|
+
const result = (await callTool('get_repo_insights', {}, db, undefined, undefined)) as {
|
|
729
|
+
error: string
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
expect(result.error).toContain('GitHub integration not available')
|
|
733
|
+
})
|
|
734
|
+
|
|
735
|
+
it('should return error when no owner/repo detected', async () => {
|
|
736
|
+
const github = createMockGitHub({
|
|
737
|
+
getRepoInfo: vi.fn().mockResolvedValue({
|
|
738
|
+
owner: null,
|
|
739
|
+
repo: null,
|
|
740
|
+
branch: null,
|
|
741
|
+
}),
|
|
742
|
+
})
|
|
743
|
+
|
|
744
|
+
const result = (await callTool('get_repo_insights', {}, db, undefined, github)) as {
|
|
745
|
+
error: string
|
|
746
|
+
requiresUserInput: boolean
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
expect(result.error).toContain('Could not auto-detect')
|
|
750
|
+
expect(result.requiresUserInput).toBe(true)
|
|
751
|
+
})
|
|
752
|
+
})
|
|
753
|
+
|
|
754
|
+
// ========================================================================
|
|
755
|
+
// Milestone edge cases
|
|
756
|
+
// ========================================================================
|
|
757
|
+
|
|
758
|
+
describe('milestone edge cases', () => {
|
|
759
|
+
it('get_github_milestones should return error when no repo', async () => {
|
|
760
|
+
const github = createMockGitHub({
|
|
761
|
+
getRepoInfo: vi.fn().mockResolvedValue({
|
|
762
|
+
owner: null,
|
|
763
|
+
repo: null,
|
|
764
|
+
branch: null,
|
|
765
|
+
}),
|
|
766
|
+
})
|
|
767
|
+
|
|
768
|
+
const result = (await callTool('get_github_milestones', {}, db, undefined, github)) as {
|
|
769
|
+
error: string
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
expect(result.error).toBeDefined()
|
|
773
|
+
})
|
|
774
|
+
|
|
775
|
+
it('get_github_milestone should return not found', async () => {
|
|
776
|
+
const github = createMockGitHub({
|
|
777
|
+
getMilestone: vi.fn().mockResolvedValue(null),
|
|
778
|
+
})
|
|
779
|
+
|
|
780
|
+
const result = (await callTool(
|
|
781
|
+
'get_github_milestone',
|
|
782
|
+
{ milestone_number: 999 },
|
|
783
|
+
db,
|
|
784
|
+
undefined,
|
|
785
|
+
github
|
|
786
|
+
)) as { error: string }
|
|
787
|
+
|
|
788
|
+
expect(result.error).toContain('not found')
|
|
789
|
+
})
|
|
790
|
+
|
|
791
|
+
it('create_github_milestone should return error when creation fails', async () => {
|
|
792
|
+
const github = createMockGitHub({
|
|
793
|
+
createMilestone: vi.fn().mockResolvedValue(null),
|
|
794
|
+
})
|
|
795
|
+
|
|
796
|
+
const result = (await callTool(
|
|
797
|
+
'create_github_milestone',
|
|
798
|
+
{ title: 'Will fail' },
|
|
799
|
+
db,
|
|
800
|
+
undefined,
|
|
801
|
+
github
|
|
802
|
+
)) as { error: string }
|
|
803
|
+
|
|
804
|
+
expect(result.error).toContain('Failed')
|
|
805
|
+
})
|
|
806
|
+
|
|
807
|
+
it('create_github_milestone with due date', async () => {
|
|
808
|
+
const github = createMockGitHub()
|
|
809
|
+
|
|
810
|
+
const result = (await callTool(
|
|
811
|
+
'create_github_milestone',
|
|
812
|
+
{ title: 'v3.0', due_on: '2026-06-01' },
|
|
813
|
+
db,
|
|
814
|
+
undefined,
|
|
815
|
+
github
|
|
816
|
+
)) as { success: boolean; milestone: { number: number } }
|
|
817
|
+
|
|
818
|
+
expect(result.success).toBe(true)
|
|
819
|
+
})
|
|
820
|
+
|
|
821
|
+
it('update_github_milestone should return error when update fails', async () => {
|
|
822
|
+
const github = createMockGitHub({
|
|
823
|
+
updateMilestone: vi.fn().mockResolvedValue(null),
|
|
824
|
+
})
|
|
825
|
+
|
|
826
|
+
const result = (await callTool(
|
|
827
|
+
'update_github_milestone',
|
|
828
|
+
{ milestone_number: 1, title: 'Will fail' },
|
|
829
|
+
db,
|
|
830
|
+
undefined,
|
|
831
|
+
github
|
|
832
|
+
)) as { error: string }
|
|
833
|
+
|
|
834
|
+
expect(result.error).toContain('Failed')
|
|
835
|
+
})
|
|
836
|
+
|
|
837
|
+
it('delete_github_milestone should return error when delete fails', async () => {
|
|
838
|
+
const github = createMockGitHub({
|
|
839
|
+
deleteMilestone: vi.fn().mockResolvedValue({ success: false }),
|
|
840
|
+
})
|
|
841
|
+
|
|
842
|
+
const result = (await callTool(
|
|
843
|
+
'delete_github_milestone',
|
|
844
|
+
{ milestone_number: 1, confirm: true },
|
|
845
|
+
db,
|
|
846
|
+
undefined,
|
|
847
|
+
github
|
|
848
|
+
)) as { success: boolean; message: string }
|
|
849
|
+
|
|
850
|
+
expect(result.success).toBe(false)
|
|
851
|
+
expect(result.message).toContain('Failed')
|
|
852
|
+
})
|
|
853
|
+
|
|
854
|
+
it('delete_github_milestone without confirm is rejected by zod', async () => {
|
|
855
|
+
const github = createMockGitHub()
|
|
856
|
+
|
|
857
|
+
// confirm must be literal true, passing false should fail
|
|
858
|
+
try {
|
|
859
|
+
await callTool(
|
|
860
|
+
'delete_github_milestone',
|
|
861
|
+
{ milestone_number: 1, confirm: false },
|
|
862
|
+
db,
|
|
863
|
+
undefined,
|
|
864
|
+
github
|
|
865
|
+
)
|
|
866
|
+
// If we get here, check the result for error
|
|
867
|
+
} catch {
|
|
868
|
+
// Expected: zod validation failure
|
|
869
|
+
}
|
|
870
|
+
})
|
|
871
|
+
|
|
872
|
+
it('get_github_milestones should return error when no github', async () => {
|
|
873
|
+
const result = (await callTool(
|
|
874
|
+
'get_github_milestones',
|
|
875
|
+
{},
|
|
876
|
+
db,
|
|
877
|
+
undefined,
|
|
878
|
+
undefined
|
|
879
|
+
)) as { error: string }
|
|
880
|
+
|
|
881
|
+
expect(result.error).toContain('GitHub integration not available')
|
|
882
|
+
})
|
|
883
|
+
})
|
|
884
|
+
|
|
885
|
+
// ========================================================================
|
|
886
|
+
// Backup tools
|
|
887
|
+
// ========================================================================
|
|
888
|
+
|
|
889
|
+
describe('backup_journal', () => {
|
|
890
|
+
it('should create a backup', async () => {
|
|
891
|
+
const result = (await callTool('backup_journal', {}, db)) as {
|
|
892
|
+
success: boolean
|
|
893
|
+
filename: string
|
|
894
|
+
}
|
|
895
|
+
|
|
896
|
+
expect(result.success).toBe(true)
|
|
897
|
+
expect(result.filename).toBeDefined()
|
|
898
|
+
})
|
|
899
|
+
|
|
900
|
+
it('should create a backup with custom name', async () => {
|
|
901
|
+
const result = (await callTool('backup_journal', { name: 'my-test-backup' }, db)) as {
|
|
902
|
+
success: boolean
|
|
903
|
+
filename: string
|
|
904
|
+
}
|
|
905
|
+
|
|
906
|
+
expect(result.success).toBe(true)
|
|
907
|
+
})
|
|
908
|
+
})
|
|
909
|
+
|
|
910
|
+
describe('list_backups', () => {
|
|
911
|
+
it('should list backups', async () => {
|
|
912
|
+
const result = (await callTool('list_backups', {}, db)) as {
|
|
913
|
+
backups: unknown[]
|
|
914
|
+
total: number
|
|
915
|
+
}
|
|
916
|
+
|
|
917
|
+
expect(result.backups).toBeDefined()
|
|
918
|
+
expect(typeof result.total).toBe('number')
|
|
919
|
+
})
|
|
920
|
+
})
|
|
921
|
+
|
|
922
|
+
describe('cleanup_backups', () => {
|
|
923
|
+
it('should cleanup old backups', async () => {
|
|
924
|
+
const result = (await callTool('cleanup_backups', { keep_count: 5 }, db)) as {
|
|
925
|
+
success: boolean
|
|
926
|
+
keptCount: number
|
|
927
|
+
}
|
|
928
|
+
|
|
929
|
+
expect(result.success).toBe(true)
|
|
930
|
+
expect(typeof result.keptCount).toBe('number')
|
|
931
|
+
})
|
|
932
|
+
})
|
|
933
|
+
|
|
934
|
+
// ========================================================================
|
|
935
|
+
// create_github_issue_with_entry - project integration paths
|
|
936
|
+
// ========================================================================
|
|
937
|
+
|
|
938
|
+
describe('create_github_issue_with_entry - project integration', () => {
|
|
939
|
+
it('should add issue to project and set initial status', async () => {
|
|
940
|
+
const github = createMockGitHub({
|
|
941
|
+
getProjectKanban: vi.fn().mockResolvedValue({
|
|
942
|
+
projectId: 'PVT_1',
|
|
943
|
+
projectTitle: 'Board',
|
|
944
|
+
statusFieldId: 'FIELD_1',
|
|
945
|
+
statusOptions: [
|
|
946
|
+
{ id: 'OPT_BACKLOG', name: 'Backlog' },
|
|
947
|
+
{ id: 'OPT_DONE', name: 'Done' },
|
|
948
|
+
],
|
|
949
|
+
columns: [],
|
|
950
|
+
totalItems: 0,
|
|
951
|
+
}),
|
|
952
|
+
addProjectItem: vi.fn().mockResolvedValue({ success: true, itemId: 'PVTITEM_NEW' }),
|
|
953
|
+
moveProjectItem: vi.fn().mockResolvedValue({ success: true }),
|
|
954
|
+
})
|
|
955
|
+
|
|
956
|
+
const result = (await callTool(
|
|
957
|
+
'create_github_issue_with_entry',
|
|
958
|
+
{
|
|
959
|
+
title: 'Project Issue',
|
|
960
|
+
journal_content: 'Added to project',
|
|
961
|
+
project_number: 1,
|
|
962
|
+
initial_status: 'Backlog',
|
|
963
|
+
},
|
|
964
|
+
db,
|
|
965
|
+
undefined,
|
|
966
|
+
github
|
|
967
|
+
)) as {
|
|
968
|
+
success: boolean
|
|
969
|
+
project?: { added: boolean; initialStatus?: { set: boolean } }
|
|
970
|
+
}
|
|
971
|
+
|
|
972
|
+
expect(result.success).toBe(true)
|
|
973
|
+
expect(result.project?.added).toBe(true)
|
|
974
|
+
expect(result.project?.initialStatus?.set).toBe(true)
|
|
975
|
+
})
|
|
976
|
+
|
|
977
|
+
it('should handle project not found when adding issue', async () => {
|
|
978
|
+
const github = createMockGitHub({
|
|
979
|
+
getProjectKanban: vi.fn().mockResolvedValue(null),
|
|
980
|
+
})
|
|
981
|
+
|
|
982
|
+
const result = (await callTool(
|
|
983
|
+
'create_github_issue_with_entry',
|
|
984
|
+
{
|
|
985
|
+
title: 'Issue for missing project',
|
|
986
|
+
journal_content: 'Test',
|
|
987
|
+
project_number: 999,
|
|
988
|
+
},
|
|
989
|
+
db,
|
|
990
|
+
undefined,
|
|
991
|
+
github
|
|
992
|
+
)) as { success: boolean; project?: { added: boolean; error: string } }
|
|
993
|
+
|
|
994
|
+
expect(result.success).toBe(true) // Issue still created
|
|
995
|
+
expect(result.project?.added).toBe(false)
|
|
996
|
+
expect(result.project?.error).toContain('not found')
|
|
997
|
+
})
|
|
998
|
+
|
|
999
|
+
it('should handle addProjectItem failure', async () => {
|
|
1000
|
+
const github = createMockGitHub({
|
|
1001
|
+
getProjectKanban: vi.fn().mockResolvedValue({
|
|
1002
|
+
projectId: 'PVT_1',
|
|
1003
|
+
projectTitle: 'Board',
|
|
1004
|
+
statusFieldId: 'FIELD_1',
|
|
1005
|
+
statusOptions: [],
|
|
1006
|
+
columns: [],
|
|
1007
|
+
totalItems: 0,
|
|
1008
|
+
}),
|
|
1009
|
+
addProjectItem: vi
|
|
1010
|
+
.fn()
|
|
1011
|
+
.mockResolvedValue({ success: false, error: 'Permission denied' }),
|
|
1012
|
+
})
|
|
1013
|
+
|
|
1014
|
+
const result = (await callTool(
|
|
1015
|
+
'create_github_issue_with_entry',
|
|
1016
|
+
{
|
|
1017
|
+
title: 'Issue add fail',
|
|
1018
|
+
journal_content: 'Test',
|
|
1019
|
+
project_number: 1,
|
|
1020
|
+
},
|
|
1021
|
+
db,
|
|
1022
|
+
undefined,
|
|
1023
|
+
github
|
|
1024
|
+
)) as { success: boolean; project?: { added: boolean; error: string } }
|
|
1025
|
+
|
|
1026
|
+
expect(result.success).toBe(true)
|
|
1027
|
+
expect(result.project?.added).toBe(false)
|
|
1028
|
+
})
|
|
1029
|
+
|
|
1030
|
+
it('should handle initial_status not found on board', async () => {
|
|
1031
|
+
const github = createMockGitHub({
|
|
1032
|
+
getProjectKanban: vi.fn().mockResolvedValue({
|
|
1033
|
+
projectId: 'PVT_1',
|
|
1034
|
+
projectTitle: 'Board',
|
|
1035
|
+
statusFieldId: 'FIELD_1',
|
|
1036
|
+
statusOptions: [{ id: 'OPT_TODO', name: 'Todo' }],
|
|
1037
|
+
columns: [],
|
|
1038
|
+
totalItems: 0,
|
|
1039
|
+
}),
|
|
1040
|
+
addProjectItem: vi.fn().mockResolvedValue({ success: true, itemId: 'PVTITEM_NEW' }),
|
|
1041
|
+
})
|
|
1042
|
+
|
|
1043
|
+
const result = (await callTool(
|
|
1044
|
+
'create_github_issue_with_entry',
|
|
1045
|
+
{
|
|
1046
|
+
title: 'Issue bad status',
|
|
1047
|
+
journal_content: 'Test',
|
|
1048
|
+
project_number: 1,
|
|
1049
|
+
initial_status: 'Nonexistent',
|
|
1050
|
+
},
|
|
1051
|
+
db,
|
|
1052
|
+
undefined,
|
|
1053
|
+
github
|
|
1054
|
+
)) as {
|
|
1055
|
+
success: boolean
|
|
1056
|
+
project?: { added: boolean; initialStatus?: { set: boolean; error: string } }
|
|
1057
|
+
}
|
|
1058
|
+
|
|
1059
|
+
expect(result.success).toBe(true)
|
|
1060
|
+
expect(result.project?.added).toBe(true)
|
|
1061
|
+
expect(result.project?.initialStatus?.set).toBe(false)
|
|
1062
|
+
expect(result.project?.initialStatus?.error).toContain('not found')
|
|
1063
|
+
})
|
|
1064
|
+
|
|
1065
|
+
it('should handle moveProjectItem failure for initial status', async () => {
|
|
1066
|
+
const github = createMockGitHub({
|
|
1067
|
+
getProjectKanban: vi.fn().mockResolvedValue({
|
|
1068
|
+
projectId: 'PVT_1',
|
|
1069
|
+
projectTitle: 'Board',
|
|
1070
|
+
statusFieldId: 'FIELD_1',
|
|
1071
|
+
statusOptions: [{ id: 'OPT_BACKLOG', name: 'Backlog' }],
|
|
1072
|
+
columns: [],
|
|
1073
|
+
totalItems: 0,
|
|
1074
|
+
}),
|
|
1075
|
+
addProjectItem: vi.fn().mockResolvedValue({ success: true, itemId: 'PVTITEM_NEW' }),
|
|
1076
|
+
moveProjectItem: vi
|
|
1077
|
+
.fn()
|
|
1078
|
+
.mockResolvedValue({ success: false, error: 'Move failed' }),
|
|
1079
|
+
})
|
|
1080
|
+
|
|
1081
|
+
const result = (await callTool(
|
|
1082
|
+
'create_github_issue_with_entry',
|
|
1083
|
+
{
|
|
1084
|
+
title: 'Issue move fail',
|
|
1085
|
+
journal_content: 'Test',
|
|
1086
|
+
project_number: 1,
|
|
1087
|
+
initial_status: 'Backlog',
|
|
1088
|
+
},
|
|
1089
|
+
db,
|
|
1090
|
+
undefined,
|
|
1091
|
+
github
|
|
1092
|
+
)) as {
|
|
1093
|
+
success: boolean
|
|
1094
|
+
project?: { added: boolean; initialStatus?: { set: boolean; error: string } }
|
|
1095
|
+
}
|
|
1096
|
+
|
|
1097
|
+
expect(result.success).toBe(true)
|
|
1098
|
+
expect(result.project?.initialStatus?.set).toBe(false)
|
|
1099
|
+
})
|
|
1100
|
+
|
|
1101
|
+
it('should handle createIssue returning null', async () => {
|
|
1102
|
+
const github = createMockGitHub({
|
|
1103
|
+
createIssue: vi.fn().mockResolvedValue(null),
|
|
1104
|
+
})
|
|
1105
|
+
|
|
1106
|
+
const result = (await callTool(
|
|
1107
|
+
'create_github_issue_with_entry',
|
|
1108
|
+
{ title: 'Will fail', journal_content: 'Test' },
|
|
1109
|
+
db,
|
|
1110
|
+
undefined,
|
|
1111
|
+
github
|
|
1112
|
+
)) as { error: string }
|
|
1113
|
+
|
|
1114
|
+
expect(result.error).toContain('Failed to create')
|
|
1115
|
+
})
|
|
1116
|
+
})
|
|
1117
|
+
|
|
1118
|
+
// ========================================================================
|
|
1119
|
+
// close_github_issue_with_entry - Kanban edge cases
|
|
1120
|
+
// ========================================================================
|
|
1121
|
+
|
|
1122
|
+
describe('close_github_issue_with_entry - Kanban edge cases', () => {
|
|
1123
|
+
it('should return kanban error when move_to_done with no project_number', async () => {
|
|
1124
|
+
const github = createMockGitHub()
|
|
1125
|
+
|
|
1126
|
+
const result = (await callTool(
|
|
1127
|
+
'close_github_issue_with_entry',
|
|
1128
|
+
{
|
|
1129
|
+
issue_number: 1,
|
|
1130
|
+
resolution_notes: 'Done!',
|
|
1131
|
+
move_to_done: true,
|
|
1132
|
+
// No project_number and no defaultProjectNumber
|
|
1133
|
+
},
|
|
1134
|
+
db,
|
|
1135
|
+
undefined,
|
|
1136
|
+
github
|
|
1137
|
+
)) as { success: boolean; kanban?: { moved: boolean; error: string } }
|
|
1138
|
+
|
|
1139
|
+
expect(result.success).toBe(true) // Issue still closes
|
|
1140
|
+
expect(result.kanban?.moved).toBe(false)
|
|
1141
|
+
expect(result.kanban?.error).toContain('project_number required')
|
|
1142
|
+
})
|
|
1143
|
+
|
|
1144
|
+
it('should handle kanban board not found', async () => {
|
|
1145
|
+
const github = createMockGitHub({
|
|
1146
|
+
getProjectKanban: vi.fn().mockResolvedValue(null),
|
|
1147
|
+
})
|
|
1148
|
+
|
|
1149
|
+
const result = (await callTool(
|
|
1150
|
+
'close_github_issue_with_entry',
|
|
1151
|
+
{
|
|
1152
|
+
issue_number: 1,
|
|
1153
|
+
resolution_notes: 'Done!',
|
|
1154
|
+
move_to_done: true,
|
|
1155
|
+
project_number: 999,
|
|
1156
|
+
},
|
|
1157
|
+
db,
|
|
1158
|
+
undefined,
|
|
1159
|
+
github
|
|
1160
|
+
)) as { success: boolean; kanban?: { moved: boolean; error: string } }
|
|
1161
|
+
|
|
1162
|
+
expect(result.success).toBe(true)
|
|
1163
|
+
expect(result.kanban?.moved).toBe(false)
|
|
1164
|
+
expect(result.kanban?.error).toContain('not found')
|
|
1165
|
+
})
|
|
1166
|
+
|
|
1167
|
+
it('should handle issue not found on project board', async () => {
|
|
1168
|
+
const github = createMockGitHub({
|
|
1169
|
+
getProjectKanban: vi.fn().mockResolvedValue({
|
|
1170
|
+
projectId: 'PVT_1',
|
|
1171
|
+
projectTitle: 'Board',
|
|
1172
|
+
statusFieldId: 'FIELD_1',
|
|
1173
|
+
statusOptions: [{ id: 'OPT_DONE', name: 'Done' }],
|
|
1174
|
+
columns: [
|
|
1175
|
+
{
|
|
1176
|
+
status: 'Todo',
|
|
1177
|
+
items: [
|
|
1178
|
+
{ id: 'PVTITEM_OTHER', title: 'Other', type: 'ISSUE', number: 99 },
|
|
1179
|
+
],
|
|
1180
|
+
},
|
|
1181
|
+
],
|
|
1182
|
+
totalItems: 1,
|
|
1183
|
+
}),
|
|
1184
|
+
})
|
|
1185
|
+
|
|
1186
|
+
const result = (await callTool(
|
|
1187
|
+
'close_github_issue_with_entry',
|
|
1188
|
+
{
|
|
1189
|
+
issue_number: 1,
|
|
1190
|
+
resolution_notes: 'Done!',
|
|
1191
|
+
move_to_done: true,
|
|
1192
|
+
project_number: 1,
|
|
1193
|
+
},
|
|
1194
|
+
db,
|
|
1195
|
+
undefined,
|
|
1196
|
+
github
|
|
1197
|
+
)) as { success: boolean; kanban?: { moved: boolean; error: string } }
|
|
1198
|
+
|
|
1199
|
+
expect(result.success).toBe(true)
|
|
1200
|
+
expect(result.kanban?.moved).toBe(false)
|
|
1201
|
+
expect(result.kanban?.error).toContain('not found on project board')
|
|
1202
|
+
})
|
|
1203
|
+
|
|
1204
|
+
it('should handle "Done" column not found on board', async () => {
|
|
1205
|
+
const github = createMockGitHub({
|
|
1206
|
+
getProjectKanban: vi.fn().mockResolvedValue({
|
|
1207
|
+
projectId: 'PVT_1',
|
|
1208
|
+
projectTitle: 'Board',
|
|
1209
|
+
statusFieldId: 'FIELD_1',
|
|
1210
|
+
statusOptions: [{ id: 'OPT_TODO', name: 'Todo' }], // No "Done" status
|
|
1211
|
+
columns: [
|
|
1212
|
+
{
|
|
1213
|
+
status: 'Todo',
|
|
1214
|
+
items: [
|
|
1215
|
+
{
|
|
1216
|
+
id: 'PVTITEM_ISSUE1',
|
|
1217
|
+
title: 'Test Issue',
|
|
1218
|
+
type: 'ISSUE',
|
|
1219
|
+
number: 1,
|
|
1220
|
+
},
|
|
1221
|
+
],
|
|
1222
|
+
},
|
|
1223
|
+
],
|
|
1224
|
+
totalItems: 1,
|
|
1225
|
+
}),
|
|
1226
|
+
})
|
|
1227
|
+
|
|
1228
|
+
const result = (await callTool(
|
|
1229
|
+
'close_github_issue_with_entry',
|
|
1230
|
+
{
|
|
1231
|
+
issue_number: 1,
|
|
1232
|
+
resolution_notes: 'Done!',
|
|
1233
|
+
move_to_done: true,
|
|
1234
|
+
project_number: 1,
|
|
1235
|
+
},
|
|
1236
|
+
db,
|
|
1237
|
+
undefined,
|
|
1238
|
+
github
|
|
1239
|
+
)) as { success: boolean; kanban?: { moved: boolean; error: string } }
|
|
1240
|
+
|
|
1241
|
+
expect(result.success).toBe(true)
|
|
1242
|
+
expect(result.kanban?.moved).toBe(false)
|
|
1243
|
+
expect(result.kanban?.error).toContain('"Done" status column not found')
|
|
1244
|
+
})
|
|
1245
|
+
|
|
1246
|
+
it('should handle closeIssue returning null (API failure)', async () => {
|
|
1247
|
+
const github = createMockGitHub({
|
|
1248
|
+
closeIssue: vi.fn().mockResolvedValue(null),
|
|
1249
|
+
})
|
|
1250
|
+
|
|
1251
|
+
const result = (await callTool(
|
|
1252
|
+
'close_github_issue_with_entry',
|
|
1253
|
+
{ issue_number: 1, resolution_notes: 'Will fail' },
|
|
1254
|
+
db,
|
|
1255
|
+
undefined,
|
|
1256
|
+
github
|
|
1257
|
+
)) as { error: string }
|
|
1258
|
+
|
|
1259
|
+
expect(result.error).toContain('Failed to close')
|
|
1260
|
+
})
|
|
1261
|
+
|
|
1262
|
+
it('should handle no-repo detection on close', async () => {
|
|
1263
|
+
const github = createMockGitHub({
|
|
1264
|
+
getRepoInfo: vi.fn().mockResolvedValue({
|
|
1265
|
+
owner: null,
|
|
1266
|
+
repo: null,
|
|
1267
|
+
branch: null,
|
|
1268
|
+
}),
|
|
1269
|
+
})
|
|
1270
|
+
|
|
1271
|
+
const result = (await callTool(
|
|
1272
|
+
'close_github_issue_with_entry',
|
|
1273
|
+
{ issue_number: 1 },
|
|
1274
|
+
db,
|
|
1275
|
+
undefined,
|
|
1276
|
+
github
|
|
1277
|
+
)) as { error: string; requiresUserInput: boolean }
|
|
1278
|
+
|
|
1279
|
+
expect(result.error).toContain('Could not auto-detect')
|
|
1280
|
+
expect(result.requiresUserInput).toBe(true)
|
|
1281
|
+
})
|
|
1282
|
+
})
|
|
1283
|
+
|
|
1284
|
+
// ========================================================================
|
|
1285
|
+
// Milestone tools - no-github and no-repo error paths
|
|
1286
|
+
// ========================================================================
|
|
1287
|
+
|
|
1288
|
+
describe('milestone tools - additional error paths', () => {
|
|
1289
|
+
it('get_github_milestone should return error when no github', async () => {
|
|
1290
|
+
const result = (await callTool(
|
|
1291
|
+
'get_github_milestone',
|
|
1292
|
+
{ milestone_number: 1 },
|
|
1293
|
+
db,
|
|
1294
|
+
undefined,
|
|
1295
|
+
undefined
|
|
1296
|
+
)) as { error: string }
|
|
1297
|
+
|
|
1298
|
+
expect(result.error).toContain('GitHub integration not available')
|
|
1299
|
+
})
|
|
1300
|
+
|
|
1301
|
+
it('get_github_milestone should return error when no repo detected', async () => {
|
|
1302
|
+
const github = createMockGitHub({
|
|
1303
|
+
getRepoInfo: vi.fn().mockResolvedValue({
|
|
1304
|
+
owner: null,
|
|
1305
|
+
repo: null,
|
|
1306
|
+
branch: null,
|
|
1307
|
+
}),
|
|
1308
|
+
})
|
|
1309
|
+
|
|
1310
|
+
const result = (await callTool(
|
|
1311
|
+
'get_github_milestone',
|
|
1312
|
+
{ milestone_number: 1 },
|
|
1313
|
+
db,
|
|
1314
|
+
undefined,
|
|
1315
|
+
github
|
|
1316
|
+
)) as { error: string; requiresUserInput: boolean }
|
|
1317
|
+
|
|
1318
|
+
expect(result.error).toContain('Could not auto-detect')
|
|
1319
|
+
expect(result.requiresUserInput).toBe(true)
|
|
1320
|
+
})
|
|
1321
|
+
|
|
1322
|
+
it('create_github_milestone should return error when no repo detected', async () => {
|
|
1323
|
+
const github = createMockGitHub({
|
|
1324
|
+
getRepoInfo: vi.fn().mockResolvedValue({
|
|
1325
|
+
owner: null,
|
|
1326
|
+
repo: null,
|
|
1327
|
+
branch: null,
|
|
1328
|
+
}),
|
|
1329
|
+
})
|
|
1330
|
+
|
|
1331
|
+
const result = (await callTool(
|
|
1332
|
+
'create_github_milestone',
|
|
1333
|
+
{ title: 'No repo' },
|
|
1334
|
+
db,
|
|
1335
|
+
undefined,
|
|
1336
|
+
github
|
|
1337
|
+
)) as { error: string; requiresUserInput: boolean }
|
|
1338
|
+
|
|
1339
|
+
expect(result.error).toContain('Could not auto-detect')
|
|
1340
|
+
expect(result.requiresUserInput).toBe(true)
|
|
1341
|
+
})
|
|
1342
|
+
|
|
1343
|
+
it('create_github_milestone should return error when no github', async () => {
|
|
1344
|
+
const result = (await callTool(
|
|
1345
|
+
'create_github_milestone',
|
|
1346
|
+
{ title: 'No github' },
|
|
1347
|
+
db,
|
|
1348
|
+
undefined,
|
|
1349
|
+
undefined
|
|
1350
|
+
)) as { error: string }
|
|
1351
|
+
|
|
1352
|
+
expect(result.error).toContain('GitHub integration not available')
|
|
1353
|
+
})
|
|
1354
|
+
|
|
1355
|
+
it('update_github_milestone should return error when no github', async () => {
|
|
1356
|
+
const result = (await callTool(
|
|
1357
|
+
'update_github_milestone',
|
|
1358
|
+
{ milestone_number: 1, title: 'No github' },
|
|
1359
|
+
db,
|
|
1360
|
+
undefined,
|
|
1361
|
+
undefined
|
|
1362
|
+
)) as { error: string }
|
|
1363
|
+
|
|
1364
|
+
expect(result.error).toContain('GitHub integration not available')
|
|
1365
|
+
})
|
|
1366
|
+
|
|
1367
|
+
it('update_github_milestone should return error when no repo detected', async () => {
|
|
1368
|
+
const github = createMockGitHub({
|
|
1369
|
+
getRepoInfo: vi.fn().mockResolvedValue({
|
|
1370
|
+
owner: null,
|
|
1371
|
+
repo: null,
|
|
1372
|
+
branch: null,
|
|
1373
|
+
}),
|
|
1374
|
+
})
|
|
1375
|
+
|
|
1376
|
+
const result = (await callTool(
|
|
1377
|
+
'update_github_milestone',
|
|
1378
|
+
{ milestone_number: 1, title: 'No repo' },
|
|
1379
|
+
db,
|
|
1380
|
+
undefined,
|
|
1381
|
+
github
|
|
1382
|
+
)) as { error: string; requiresUserInput: boolean }
|
|
1383
|
+
|
|
1384
|
+
expect(result.error).toContain('Could not auto-detect')
|
|
1385
|
+
expect(result.requiresUserInput).toBe(true)
|
|
1386
|
+
})
|
|
1387
|
+
|
|
1388
|
+
it('delete_github_milestone should return error when no github', async () => {
|
|
1389
|
+
const result = (await callTool(
|
|
1390
|
+
'delete_github_milestone',
|
|
1391
|
+
{ milestone_number: 1, confirm: true },
|
|
1392
|
+
db,
|
|
1393
|
+
undefined,
|
|
1394
|
+
undefined
|
|
1395
|
+
)) as { error: string }
|
|
1396
|
+
|
|
1397
|
+
expect(result.error).toContain('GitHub integration not available')
|
|
1398
|
+
})
|
|
1399
|
+
|
|
1400
|
+
it('delete_github_milestone should return error when no repo detected', async () => {
|
|
1401
|
+
const github = createMockGitHub({
|
|
1402
|
+
getRepoInfo: vi.fn().mockResolvedValue({
|
|
1403
|
+
owner: null,
|
|
1404
|
+
repo: null,
|
|
1405
|
+
branch: null,
|
|
1406
|
+
}),
|
|
1407
|
+
})
|
|
1408
|
+
|
|
1409
|
+
const result = (await callTool(
|
|
1410
|
+
'delete_github_milestone',
|
|
1411
|
+
{ milestone_number: 1, confirm: true },
|
|
1412
|
+
db,
|
|
1413
|
+
undefined,
|
|
1414
|
+
github
|
|
1415
|
+
)) as { error: string; requiresUserInput: boolean }
|
|
1416
|
+
|
|
1417
|
+
expect(result.error).toContain('Could not auto-detect')
|
|
1418
|
+
expect(result.requiresUserInput).toBe(true)
|
|
1419
|
+
})
|
|
1420
|
+
})
|
|
1421
|
+
|
|
1422
|
+
// ========================================================================
|
|
1423
|
+
// restore_backup
|
|
1424
|
+
// ========================================================================
|
|
1425
|
+
|
|
1426
|
+
describe('restore_backup', () => {
|
|
1427
|
+
it('should restore from a backup file', async () => {
|
|
1428
|
+
// First create a backup to restore from
|
|
1429
|
+
const backupResult = (await callTool(
|
|
1430
|
+
'backup_journal',
|
|
1431
|
+
{ name: 'restore-test' },
|
|
1432
|
+
db
|
|
1433
|
+
)) as { success: boolean; filename: string }
|
|
1434
|
+
|
|
1435
|
+
expect(backupResult.success).toBe(true)
|
|
1436
|
+
|
|
1437
|
+
const result = (await callTool(
|
|
1438
|
+
'restore_backup',
|
|
1439
|
+
{ filename: backupResult.filename, confirm: true },
|
|
1440
|
+
db
|
|
1441
|
+
)) as {
|
|
1442
|
+
success: boolean
|
|
1443
|
+
message: string
|
|
1444
|
+
restoredFrom: string
|
|
1445
|
+
previousEntryCount: number
|
|
1446
|
+
newEntryCount: number
|
|
1447
|
+
}
|
|
1448
|
+
|
|
1449
|
+
expect(result.success).toBe(true)
|
|
1450
|
+
expect(result.message).toContain('restored')
|
|
1451
|
+
expect(typeof result.previousEntryCount).toBe('number')
|
|
1452
|
+
expect(typeof result.newEntryCount).toBe('number')
|
|
1453
|
+
})
|
|
1454
|
+
})
|
|
556
1455
|
})
|