specforge-mcp 0.1.1 → 0.2.2
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 +31 -1
- package/dist/config/version.d.ts +2 -0
- package/dist/config/version.d.ts.map +1 -0
- package/dist/config/version.js +9 -0
- package/dist/config/version.js.map +1 -0
- package/dist/config/version.ts +11 -0
- package/dist/engine/detectors/library-detector-langs2.d.ts.map +1 -1
- package/dist/engine/detectors/library-detector-langs2.js.map +1 -1
- package/dist/engine/mermaid-generator.d.ts.map +1 -1
- package/dist/engine/mermaid-generator.js +12 -2
- package/dist/engine/mermaid-generator.js.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/tools/create-spec-hu/hu-body-generators.d.ts.map +1 -1
- package/dist/tools/create-spec-hu/hu-body-generators.js +7 -1
- package/dist/tools/create-spec-hu/hu-body-generators.js.map +1 -1
- package/dist/tools/create-spec-tech/ficha-content.d.ts.map +1 -1
- package/dist/tools/create-spec-tech/ficha-content.js +4 -2
- package/dist/tools/create-spec-tech/ficha-content.js.map +1 -1
- package/dist/tools/create-spec.d.ts.map +1 -1
- package/dist/tools/create-spec.js +4 -3
- package/dist/tools/create-spec.js.map +1 -1
- package/dist/tools/register-spec-tools/core-spec-tools.d.ts.map +1 -1
- package/dist/tools/register-spec-tools/core-spec-tools.js +4 -0
- package/dist/tools/register-spec-tools/core-spec-tools.js.map +1 -1
- package/dist/types/spec/core.d.ts +1 -0
- package/dist/types/spec/core.d.ts.map +1 -1
- package/dist/types/spec/inputs.d.ts +1 -0
- package/dist/types/spec/inputs.d.ts.map +1 -1
- package/package.json +27 -18
- package/src/config/version.ts +11 -0
- package/dist/engine/agent-generator.test.d.ts +0 -2
- package/dist/engine/agent-generator.test.d.ts.map +0 -1
- package/dist/engine/agent-generator.test.js +0 -556
- package/dist/engine/agent-generator.test.js.map +0 -1
- package/dist/engine/analyzer.test.d.ts +0 -2
- package/dist/engine/analyzer.test.d.ts.map +0 -1
- package/dist/engine/analyzer.test.js +0 -1461
- package/dist/engine/analyzer.test.js.map +0 -1
- package/dist/engine/auditor.test.d.ts +0 -2
- package/dist/engine/auditor.test.d.ts.map +0 -1
- package/dist/engine/auditor.test.js +0 -2075
- package/dist/engine/auditor.test.js.map +0 -1
- package/dist/engine/doc-generator.test.d.ts +0 -2
- package/dist/engine/doc-generator.test.d.ts.map +0 -1
- package/dist/engine/doc-generator.test.js +0 -961
- package/dist/engine/doc-generator.test.js.map +0 -1
- package/dist/engine/estimator.test.d.ts +0 -2
- package/dist/engine/estimator.test.d.ts.map +0 -1
- package/dist/engine/estimator.test.js +0 -334
- package/dist/engine/estimator.test.js.map +0 -1
- package/dist/engine/skill-generator.test.d.ts +0 -2
- package/dist/engine/skill-generator.test.d.ts.map +0 -1
- package/dist/engine/skill-generator.test.js +0 -742
- package/dist/engine/skill-generator.test.js.map +0 -1
- package/dist/engine/validator.test.d.ts +0 -2
- package/dist/engine/validator.test.d.ts.map +0 -1
- package/dist/engine/validator.test.js +0 -2371
- package/dist/engine/validator.test.js.map +0 -1
- package/dist/engine/web-fetcher.test.d.ts +0 -2
- package/dist/engine/web-fetcher.test.d.ts.map +0 -1
- package/dist/engine/web-fetcher.test.js +0 -360
- package/dist/engine/web-fetcher.test.js.map +0 -1
- package/dist/i18n/index.test.d.ts +0 -2
- package/dist/i18n/index.test.d.ts.map +0 -1
- package/dist/i18n/index.test.js +0 -375
- package/dist/i18n/index.test.js.map +0 -1
- package/dist/index.test.d.ts +0 -2
- package/dist/index.test.d.ts.map +0 -1
- package/dist/index.test.js +0 -124
- package/dist/index.test.js.map +0 -1
- package/dist/resources/patterns.test.d.ts +0 -2
- package/dist/resources/patterns.test.d.ts.map +0 -1
- package/dist/resources/patterns.test.js +0 -142
- package/dist/resources/patterns.test.js.map +0 -1
- package/dist/resources/process.test.d.ts +0 -2
- package/dist/resources/process.test.d.ts.map +0 -1
- package/dist/resources/process.test.js +0 -48
- package/dist/resources/process.test.js.map +0 -1
- package/dist/resources/registry.test.d.ts +0 -2
- package/dist/resources/registry.test.d.ts.map +0 -1
- package/dist/resources/registry.test.js +0 -138
- package/dist/resources/registry.test.js.map +0 -1
- package/dist/resources/specs.test.d.ts +0 -2
- package/dist/resources/specs.test.d.ts.map +0 -1
- package/dist/resources/specs.test.js +0 -130
- package/dist/resources/specs.test.js.map +0 -1
- package/dist/resources/templates.test.d.ts +0 -2
- package/dist/resources/templates.test.d.ts.map +0 -1
- package/dist/resources/templates.test.js +0 -119
- package/dist/resources/templates.test.js.map +0 -1
- package/dist/smoke.test.d.ts +0 -2
- package/dist/smoke.test.d.ts.map +0 -1
- package/dist/smoke.test.js +0 -229
- package/dist/smoke.test.js.map +0 -1
- package/dist/storage/base-store.test.d.ts +0 -2
- package/dist/storage/base-store.test.d.ts.map +0 -1
- package/dist/storage/base-store.test.js +0 -180
- package/dist/storage/base-store.test.js.map +0 -1
- package/dist/storage/global-store.test.d.ts +0 -2
- package/dist/storage/global-store.test.d.ts.map +0 -1
- package/dist/storage/global-store.test.js +0 -327
- package/dist/storage/global-store.test.js.map +0 -1
- package/dist/storage/index.test.d.ts +0 -2
- package/dist/storage/index.test.d.ts.map +0 -1
- package/dist/storage/index.test.js +0 -56
- package/dist/storage/index.test.js.map +0 -1
- package/dist/storage/knowledge-store.test.d.ts +0 -2
- package/dist/storage/knowledge-store.test.d.ts.map +0 -1
- package/dist/storage/knowledge-store.test.js +0 -368
- package/dist/storage/knowledge-store.test.js.map +0 -1
- package/dist/storage/metrics-store.test.d.ts +0 -2
- package/dist/storage/metrics-store.test.d.ts.map +0 -1
- package/dist/storage/metrics-store.test.js +0 -212
- package/dist/storage/metrics-store.test.js.map +0 -1
- package/dist/storage/pattern-store.test.d.ts +0 -2
- package/dist/storage/pattern-store.test.d.ts.map +0 -1
- package/dist/storage/pattern-store.test.js +0 -224
- package/dist/storage/pattern-store.test.js.map +0 -1
- package/dist/storage/spec-store.test.d.ts +0 -2
- package/dist/storage/spec-store.test.d.ts.map +0 -1
- package/dist/storage/spec-store.test.js +0 -227
- package/dist/storage/spec-store.test.js.map +0 -1
- package/dist/tools/audit.test.d.ts +0 -2
- package/dist/tools/audit.test.d.ts.map +0 -1
- package/dist/tools/audit.test.js +0 -169
- package/dist/tools/audit.test.js.map +0 -1
- package/dist/tools/challenge-spec.test.d.ts +0 -2
- package/dist/tools/challenge-spec.test.d.ts.map +0 -1
- package/dist/tools/challenge-spec.test.js +0 -782
- package/dist/tools/challenge-spec.test.js.map +0 -1
- package/dist/tools/check-versions.test.d.ts +0 -2
- package/dist/tools/check-versions.test.d.ts.map +0 -1
- package/dist/tools/check-versions.test.js +0 -214
- package/dist/tools/check-versions.test.js.map +0 -1
- package/dist/tools/clarify-requirements.test.d.ts +0 -2
- package/dist/tools/clarify-requirements.test.d.ts.map +0 -1
- package/dist/tools/clarify-requirements.test.js +0 -161
- package/dist/tools/clarify-requirements.test.js.map +0 -1
- package/dist/tools/consult-docs.test.d.ts +0 -2
- package/dist/tools/consult-docs.test.d.ts.map +0 -1
- package/dist/tools/consult-docs.test.js +0 -140
- package/dist/tools/consult-docs.test.js.map +0 -1
- package/dist/tools/create-spec.test.d.ts +0 -2
- package/dist/tools/create-spec.test.d.ts.map +0 -1
- package/dist/tools/create-spec.test.js +0 -233
- package/dist/tools/create-spec.test.js.map +0 -1
- package/dist/tools/define-ui-contract.test.d.ts +0 -2
- package/dist/tools/define-ui-contract.test.d.ts.map +0 -1
- package/dist/tools/define-ui-contract.test.js +0 -479
- package/dist/tools/define-ui-contract.test.js.map +0 -1
- package/dist/tools/design-schema.test.d.ts +0 -2
- package/dist/tools/design-schema.test.d.ts.map +0 -1
- package/dist/tools/design-schema.test.js +0 -301
- package/dist/tools/design-schema.test.js.map +0 -1
- package/dist/tools/detect-agent.test.d.ts +0 -2
- package/dist/tools/detect-agent.test.d.ts.map +0 -1
- package/dist/tools/detect-agent.test.js +0 -133
- package/dist/tools/detect-agent.test.js.map +0 -1
- package/dist/tools/detect-drift.test.d.ts +0 -2
- package/dist/tools/detect-drift.test.d.ts.map +0 -1
- package/dist/tools/detect-drift.test.js +0 -312
- package/dist/tools/detect-drift.test.js.map +0 -1
- package/dist/tools/discover-mcps.test.d.ts +0 -2
- package/dist/tools/discover-mcps.test.d.ts.map +0 -1
- package/dist/tools/discover-mcps.test.js +0 -345
- package/dist/tools/discover-mcps.test.js.map +0 -1
- package/dist/tools/estimate.test.d.ts +0 -2
- package/dist/tools/estimate.test.d.ts.map +0 -1
- package/dist/tools/estimate.test.js +0 -137
- package/dist/tools/estimate.test.js.map +0 -1
- package/dist/tools/generate-adr.test.d.ts +0 -2
- package/dist/tools/generate-adr.test.d.ts.map +0 -1
- package/dist/tools/generate-adr.test.js +0 -206
- package/dist/tools/generate-adr.test.js.map +0 -1
- package/dist/tools/generate-checklist.test.d.ts +0 -2
- package/dist/tools/generate-checklist.test.d.ts.map +0 -1
- package/dist/tools/generate-checklist.test.js +0 -201
- package/dist/tools/generate-checklist.test.js.map +0 -1
- package/dist/tools/generate-docs.test.d.ts +0 -2
- package/dist/tools/generate-docs.test.d.ts.map +0 -1
- package/dist/tools/generate-docs.test.js +0 -183
- package/dist/tools/generate-docs.test.js.map +0 -1
- package/dist/tools/generate-execution-plan.test.d.ts +0 -2
- package/dist/tools/generate-execution-plan.test.d.ts.map +0 -1
- package/dist/tools/generate-execution-plan.test.js +0 -643
- package/dist/tools/generate-execution-plan.test.js.map +0 -1
- package/dist/tools/generate-rules.test.d.ts +0 -2
- package/dist/tools/generate-rules.test.d.ts.map +0 -1
- package/dist/tools/generate-rules.test.js +0 -148
- package/dist/tools/generate-rules.test.js.map +0 -1
- package/dist/tools/generate-skill.test.d.ts +0 -2
- package/dist/tools/generate-skill.test.d.ts.map +0 -1
- package/dist/tools/generate-skill.test.js +0 -138
- package/dist/tools/generate-skill.test.js.map +0 -1
- package/dist/tools/generate-sub-agent.test.d.ts +0 -2
- package/dist/tools/generate-sub-agent.test.d.ts.map +0 -1
- package/dist/tools/generate-sub-agent.test.js +0 -162
- package/dist/tools/generate-sub-agent.test.js.map +0 -1
- package/dist/tools/generate-tests.test.d.ts +0 -2
- package/dist/tools/generate-tests.test.d.ts.map +0 -1
- package/dist/tools/generate-tests.test.js +0 -222
- package/dist/tools/generate-tests.test.js.map +0 -1
- package/dist/tools/init-constitution.test.d.ts +0 -2
- package/dist/tools/init-constitution.test.d.ts.map +0 -1
- package/dist/tools/init-constitution.test.js +0 -398
- package/dist/tools/init-constitution.test.js.map +0 -1
- package/dist/tools/init-project.test.d.ts +0 -2
- package/dist/tools/init-project.test.d.ts.map +0 -1
- package/dist/tools/init-project.test.js +0 -158
- package/dist/tools/init-project.test.js.map +0 -1
- package/dist/tools/integrate-pm.test.d.ts +0 -2
- package/dist/tools/integrate-pm.test.d.ts.map +0 -1
- package/dist/tools/integrate-pm.test.js +0 -558
- package/dist/tools/integrate-pm.test.js.map +0 -1
- package/dist/tools/learn.test.d.ts +0 -2
- package/dist/tools/learn.test.d.ts.map +0 -1
- package/dist/tools/learn.test.js +0 -123
- package/dist/tools/learn.test.js.map +0 -1
- package/dist/tools/list-specs.test.d.ts +0 -2
- package/dist/tools/list-specs.test.d.ts.map +0 -1
- package/dist/tools/list-specs.test.js +0 -110
- package/dist/tools/list-specs.test.js.map +0 -1
- package/dist/tools/manage-context.test.d.ts +0 -2
- package/dist/tools/manage-context.test.d.ts.map +0 -1
- package/dist/tools/manage-context.test.js +0 -359
- package/dist/tools/manage-context.test.js.map +0 -1
- package/dist/tools/manage-git.test.d.ts +0 -2
- package/dist/tools/manage-git.test.d.ts.map +0 -1
- package/dist/tools/manage-git.test.js +0 -882
- package/dist/tools/manage-git.test.js.map +0 -1
- package/dist/tools/orchestrate.test.d.ts +0 -2
- package/dist/tools/orchestrate.test.d.ts.map +0 -1
- package/dist/tools/orchestrate.test.js +0 -1117
- package/dist/tools/orchestrate.test.js.map +0 -1
- package/dist/tools/reconcile-spec.test.d.ts +0 -2
- package/dist/tools/reconcile-spec.test.d.ts.map +0 -1
- package/dist/tools/reconcile-spec.test.js +0 -259
- package/dist/tools/reconcile-spec.test.js.map +0 -1
- package/dist/tools/register-platform-tools.test.d.ts +0 -2
- package/dist/tools/register-platform-tools.test.d.ts.map +0 -1
- package/dist/tools/register-platform-tools.test.js +0 -404
- package/dist/tools/register-platform-tools.test.js.map +0 -1
- package/dist/tools/register-spec-tools.test.d.ts +0 -2
- package/dist/tools/register-spec-tools.test.d.ts.map +0 -1
- package/dist/tools/register-spec-tools.test.js +0 -407
- package/dist/tools/register-spec-tools.test.js.map +0 -1
- package/dist/tools/reverse-engineer.test.d.ts +0 -2
- package/dist/tools/reverse-engineer.test.d.ts.map +0 -1
- package/dist/tools/reverse-engineer.test.js +0 -206
- package/dist/tools/reverse-engineer.test.js.map +0 -1
- package/dist/tools/schemas.d.ts +0 -20
- package/dist/tools/schemas.d.ts.map +0 -1
- package/dist/tools/schemas.js +0 -133
- package/dist/tools/schemas.js.map +0 -1
- package/dist/tools/schemas.test.d.ts +0 -2
- package/dist/tools/schemas.test.d.ts.map +0 -1
- package/dist/tools/schemas.test.js +0 -245
- package/dist/tools/schemas.test.js.map +0 -1
- package/dist/tools/set-locale.test.d.ts +0 -2
- package/dist/tools/set-locale.test.d.ts.map +0 -1
- package/dist/tools/set-locale.test.js +0 -74
- package/dist/tools/set-locale.test.js.map +0 -1
- package/dist/tools/suggest-mcps.test.d.ts +0 -2
- package/dist/tools/suggest-mcps.test.d.ts.map +0 -1
- package/dist/tools/suggest-mcps.test.js +0 -198
- package/dist/tools/suggest-mcps.test.js.map +0 -1
- package/dist/tools/suggest-stack.test.d.ts +0 -2
- package/dist/tools/suggest-stack.test.d.ts.map +0 -1
- package/dist/tools/suggest-stack.test.js +0 -181
- package/dist/tools/suggest-stack.test.js.map +0 -1
- package/dist/tools/suggest-tooling.test.d.ts +0 -2
- package/dist/tools/suggest-tooling.test.d.ts.map +0 -1
- package/dist/tools/suggest-tooling.test.js +0 -213
- package/dist/tools/suggest-tooling.test.js.map +0 -1
- package/dist/tools/summarize-spec.test.d.ts +0 -2
- package/dist/tools/summarize-spec.test.d.ts.map +0 -1
- package/dist/tools/summarize-spec.test.js +0 -180
- package/dist/tools/summarize-spec.test.js.map +0 -1
- package/dist/tools/update-status.test.d.ts +0 -2
- package/dist/tools/update-status.test.d.ts.map +0 -1
- package/dist/tools/update-status.test.js +0 -142
- package/dist/tools/update-status.test.js.map +0 -1
- package/dist/tools/validate.test.d.ts +0 -2
- package/dist/tools/validate.test.d.ts.map +0 -1
- package/dist/tools/validate.test.js +0 -137
- package/dist/tools/validate.test.js.map +0 -1
package/dist/tools/learn.test.js
DELETED
|
@@ -1,123 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
2
|
-
vi.mock('../i18n/index.js', () => ({
|
|
3
|
-
t: vi.fn((key) => `t:${key}`),
|
|
4
|
-
ti: vi.fn((_key, params) => `translated:${JSON.stringify(params)}`),
|
|
5
|
-
}));
|
|
6
|
-
vi.mock('../storage/index.js', () => ({
|
|
7
|
-
patternStore: {
|
|
8
|
-
listPatterns: vi.fn(),
|
|
9
|
-
addPattern: vi.fn(),
|
|
10
|
-
incrementPatternUsage: vi.fn(),
|
|
11
|
-
},
|
|
12
|
-
knowledgeStore: {
|
|
13
|
-
getKnowledge: vi.fn(),
|
|
14
|
-
},
|
|
15
|
-
}));
|
|
16
|
-
import { handleLearn } from './learn.js';
|
|
17
|
-
import { patternStore, knowledgeStore } from '../storage/index.js';
|
|
18
|
-
beforeEach(() => {
|
|
19
|
-
vi.clearAllMocks();
|
|
20
|
-
});
|
|
21
|
-
describe('handleLearn', () => {
|
|
22
|
-
it('should return error when project not found', async () => {
|
|
23
|
-
vi.mocked(knowledgeStore.getKnowledge).mockResolvedValue(null);
|
|
24
|
-
const result = await handleLearn({
|
|
25
|
-
projectId: 'proj-123',
|
|
26
|
-
type: 'architecture',
|
|
27
|
-
pattern: 'Use repository pattern',
|
|
28
|
-
});
|
|
29
|
-
expect(result.isError).toBe(true);
|
|
30
|
-
});
|
|
31
|
-
it('should create new pattern successfully', async () => {
|
|
32
|
-
vi.mocked(knowledgeStore.getKnowledge).mockResolvedValue({});
|
|
33
|
-
vi.mocked(patternStore.listPatterns).mockResolvedValue([]);
|
|
34
|
-
vi.mocked(patternStore.addPattern).mockResolvedValue(undefined);
|
|
35
|
-
const result = await handleLearn({
|
|
36
|
-
projectId: 'proj-123',
|
|
37
|
-
type: 'architecture',
|
|
38
|
-
pattern: 'Use repository pattern',
|
|
39
|
-
});
|
|
40
|
-
expect(result.isError).toBeUndefined();
|
|
41
|
-
expect(result.content).toHaveLength(2);
|
|
42
|
-
const parsed = JSON.parse(result.content[1].text);
|
|
43
|
-
expect(parsed.action).toBe('created');
|
|
44
|
-
expect(parsed.type).toBe('architecture');
|
|
45
|
-
expect(parsed.pattern).toBe('Use repository pattern');
|
|
46
|
-
expect(parsed.totalPatterns).toBe(1);
|
|
47
|
-
expect(patternStore.addPattern).toHaveBeenCalled();
|
|
48
|
-
});
|
|
49
|
-
it('should increment usage count for duplicate pattern', async () => {
|
|
50
|
-
vi.mocked(knowledgeStore.getKnowledge).mockResolvedValue({});
|
|
51
|
-
vi.mocked(patternStore.listPatterns).mockResolvedValue([
|
|
52
|
-
{
|
|
53
|
-
id: 'PAT-ARC-123',
|
|
54
|
-
projectId: 'proj-123',
|
|
55
|
-
type: 'architecture',
|
|
56
|
-
pattern: 'Use repository pattern',
|
|
57
|
-
source: 'user-defined',
|
|
58
|
-
learnedAt: '2024-01-01',
|
|
59
|
-
confidence: 0.8,
|
|
60
|
-
usageCount: 3,
|
|
61
|
-
},
|
|
62
|
-
]);
|
|
63
|
-
vi.mocked(patternStore.incrementPatternUsage).mockResolvedValue({
|
|
64
|
-
usageCount: 4,
|
|
65
|
-
});
|
|
66
|
-
const result = await handleLearn({
|
|
67
|
-
projectId: 'proj-123',
|
|
68
|
-
type: 'architecture',
|
|
69
|
-
pattern: 'Use repository pattern',
|
|
70
|
-
});
|
|
71
|
-
expect(result.isError).toBeUndefined();
|
|
72
|
-
const parsed = JSON.parse(result.content[1].text);
|
|
73
|
-
expect(parsed.action).toBe('incremented');
|
|
74
|
-
expect(parsed.usageCount).toBe(4);
|
|
75
|
-
expect(patternStore.addPattern).not.toHaveBeenCalled();
|
|
76
|
-
});
|
|
77
|
-
it('should use default source when not provided', async () => {
|
|
78
|
-
vi.mocked(knowledgeStore.getKnowledge).mockResolvedValue({});
|
|
79
|
-
vi.mocked(patternStore.listPatterns).mockResolvedValue([]);
|
|
80
|
-
vi.mocked(patternStore.addPattern).mockResolvedValue(undefined);
|
|
81
|
-
await handleLearn({
|
|
82
|
-
projectId: 'proj-123',
|
|
83
|
-
type: 'convention',
|
|
84
|
-
pattern: 'Use camelCase',
|
|
85
|
-
});
|
|
86
|
-
const addCall = vi.mocked(patternStore.addPattern).mock.calls[0];
|
|
87
|
-
const savedPattern = addCall[1];
|
|
88
|
-
expect(savedPattern.source).toBe('user-defined');
|
|
89
|
-
});
|
|
90
|
-
it('should use custom source when provided', async () => {
|
|
91
|
-
vi.mocked(knowledgeStore.getKnowledge).mockResolvedValue({});
|
|
92
|
-
vi.mocked(patternStore.listPatterns).mockResolvedValue([]);
|
|
93
|
-
vi.mocked(patternStore.addPattern).mockResolvedValue(undefined);
|
|
94
|
-
await handleLearn({
|
|
95
|
-
projectId: 'proj-123',
|
|
96
|
-
type: 'quality',
|
|
97
|
-
pattern: 'Always write tests',
|
|
98
|
-
source: 'code-review',
|
|
99
|
-
});
|
|
100
|
-
const addCall = vi.mocked(patternStore.addPattern).mock.calls[0];
|
|
101
|
-
const savedPattern = addCall[1];
|
|
102
|
-
expect(savedPattern.source).toBe('code-review');
|
|
103
|
-
});
|
|
104
|
-
it('should build pattern summary correctly', async () => {
|
|
105
|
-
vi.mocked(knowledgeStore.getKnowledge).mockResolvedValue({});
|
|
106
|
-
vi.mocked(patternStore.listPatterns).mockResolvedValue([
|
|
107
|
-
{ type: 'architecture', pattern: 'Layered' },
|
|
108
|
-
{ type: 'architecture', pattern: 'Repository' },
|
|
109
|
-
{ type: 'convention', pattern: 'CamelCase' },
|
|
110
|
-
]);
|
|
111
|
-
vi.mocked(patternStore.addPattern).mockResolvedValue(undefined);
|
|
112
|
-
const result = await handleLearn({
|
|
113
|
-
projectId: 'proj-123',
|
|
114
|
-
type: 'stack',
|
|
115
|
-
pattern: 'Use TypeScript',
|
|
116
|
-
});
|
|
117
|
-
const parsed = JSON.parse(result.content[1].text);
|
|
118
|
-
expect(parsed.patternsByType.architecture).toBe(2);
|
|
119
|
-
expect(parsed.patternsByType.convention).toBe(1);
|
|
120
|
-
expect(parsed.patternsByType.stack).toBe(1);
|
|
121
|
-
});
|
|
122
|
-
});
|
|
123
|
-
//# sourceMappingURL=learn.test.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"learn.test.js","sourceRoot":"","sources":["../../src/tools/learn.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"list-specs.test.d.ts","sourceRoot":"","sources":["../../src/tools/list-specs.test.ts"],"names":[],"mappings":""}
|
|
@@ -1,110 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
2
|
-
vi.mock('../i18n/index.js', () => ({
|
|
3
|
-
ti: vi.fn((_key, params) => `translated:${JSON.stringify(params)}`),
|
|
4
|
-
}));
|
|
5
|
-
vi.mock('../storage/index.js', () => ({
|
|
6
|
-
specStore: {
|
|
7
|
-
listSpecs: vi.fn(),
|
|
8
|
-
},
|
|
9
|
-
knowledgeStore: {
|
|
10
|
-
getKnowledge: vi.fn(),
|
|
11
|
-
},
|
|
12
|
-
}));
|
|
13
|
-
import { handleListSpecs } from './list-specs.js';
|
|
14
|
-
import { specStore, knowledgeStore } from '../storage/index.js';
|
|
15
|
-
const makeSpec = (overrides = {}) => ({
|
|
16
|
-
id: 'SPEC-001',
|
|
17
|
-
title: 'Test Spec',
|
|
18
|
-
type: 'feature',
|
|
19
|
-
scope: 'feature',
|
|
20
|
-
status: 'draft',
|
|
21
|
-
difficulty: 3,
|
|
22
|
-
risk: 'medium',
|
|
23
|
-
target: 'fullstack',
|
|
24
|
-
tags: ['test'],
|
|
25
|
-
gitBranch: 'feat/spec-001-test',
|
|
26
|
-
estimation: {
|
|
27
|
-
devHours: 8,
|
|
28
|
-
reviewHours: 2,
|
|
29
|
-
totalCostUsd: 500,
|
|
30
|
-
recommendedModel: 'sonnet',
|
|
31
|
-
tokenOptimization: { mode: 'local' },
|
|
32
|
-
},
|
|
33
|
-
createdAt: '2024-01-01',
|
|
34
|
-
updatedAt: '2024-01-02',
|
|
35
|
-
...overrides,
|
|
36
|
-
});
|
|
37
|
-
beforeEach(() => {
|
|
38
|
-
vi.clearAllMocks();
|
|
39
|
-
});
|
|
40
|
-
describe('handleListSpecs', () => {
|
|
41
|
-
it('should return error when project not found', async () => {
|
|
42
|
-
vi.mocked(knowledgeStore.getKnowledge).mockResolvedValue(null);
|
|
43
|
-
const result = await handleListSpecs({ projectId: 'proj-123' });
|
|
44
|
-
expect(result.isError).toBe(true);
|
|
45
|
-
});
|
|
46
|
-
it('should list all specs without filters', async () => {
|
|
47
|
-
vi.mocked(knowledgeStore.getKnowledge).mockResolvedValue({});
|
|
48
|
-
vi.mocked(specStore.listSpecs).mockResolvedValue([
|
|
49
|
-
makeSpec({ id: 'SPEC-001', status: 'draft', type: 'feature' }),
|
|
50
|
-
makeSpec({ id: 'SPEC-002', status: 'approved', type: 'bugfix' }),
|
|
51
|
-
]);
|
|
52
|
-
const result = await handleListSpecs({ projectId: 'proj-123' });
|
|
53
|
-
expect(result.isError).toBeUndefined();
|
|
54
|
-
const parsed = JSON.parse(result.content[0].text);
|
|
55
|
-
expect(parsed.count).toBe(2);
|
|
56
|
-
expect(parsed.totalCount).toBe(2);
|
|
57
|
-
expect(parsed.specs).toHaveLength(2);
|
|
58
|
-
expect(parsed.filters.status).toBe('all');
|
|
59
|
-
expect(parsed.filters.type).toBe('all');
|
|
60
|
-
});
|
|
61
|
-
it('should filter by status', async () => {
|
|
62
|
-
vi.mocked(knowledgeStore.getKnowledge).mockResolvedValue({});
|
|
63
|
-
vi.mocked(specStore.listSpecs).mockResolvedValue([
|
|
64
|
-
makeSpec({ id: 'SPEC-001', status: 'draft', type: 'feature' }),
|
|
65
|
-
makeSpec({ id: 'SPEC-002', status: 'approved', type: 'bugfix' }),
|
|
66
|
-
]);
|
|
67
|
-
const result = await handleListSpecs({ projectId: 'proj-123', status: 'draft' });
|
|
68
|
-
const parsed = JSON.parse(result.content[0].text);
|
|
69
|
-
expect(parsed.count).toBe(1);
|
|
70
|
-
expect(parsed.specs[0].id).toBe('SPEC-001');
|
|
71
|
-
});
|
|
72
|
-
it('should filter by type', async () => {
|
|
73
|
-
vi.mocked(knowledgeStore.getKnowledge).mockResolvedValue({});
|
|
74
|
-
vi.mocked(specStore.listSpecs).mockResolvedValue([
|
|
75
|
-
makeSpec({ id: 'SPEC-001', status: 'draft', type: 'feature' }),
|
|
76
|
-
makeSpec({ id: 'SPEC-002', status: 'approved', type: 'bugfix' }),
|
|
77
|
-
]);
|
|
78
|
-
const result = await handleListSpecs({ projectId: 'proj-123', type: 'bugfix' });
|
|
79
|
-
const parsed = JSON.parse(result.content[0].text);
|
|
80
|
-
expect(parsed.count).toBe(1);
|
|
81
|
-
expect(parsed.specs[0].id).toBe('SPEC-002');
|
|
82
|
-
});
|
|
83
|
-
it('should return empty list when no specs', async () => {
|
|
84
|
-
vi.mocked(knowledgeStore.getKnowledge).mockResolvedValue({});
|
|
85
|
-
vi.mocked(specStore.listSpecs).mockResolvedValue([]);
|
|
86
|
-
const result = await handleListSpecs({ projectId: 'proj-123' });
|
|
87
|
-
const parsed = JSON.parse(result.content[0].text);
|
|
88
|
-
expect(parsed.count).toBe(0);
|
|
89
|
-
expect(parsed.specs).toHaveLength(0);
|
|
90
|
-
expect(parsed.summary.totalDevHours).toBe(0);
|
|
91
|
-
});
|
|
92
|
-
it('should calculate aggregated estimation totals', async () => {
|
|
93
|
-
vi.mocked(knowledgeStore.getKnowledge).mockResolvedValue({});
|
|
94
|
-
vi.mocked(specStore.listSpecs).mockResolvedValue([
|
|
95
|
-
makeSpec({ estimation: { devHours: 8, reviewHours: 2, totalCostUsd: 500 } }),
|
|
96
|
-
makeSpec({ estimation: { devHours: 16, reviewHours: 4, totalCostUsd: 1000 } }),
|
|
97
|
-
]);
|
|
98
|
-
const result = await handleListSpecs({ projectId: 'proj-123' });
|
|
99
|
-
const parsed = JSON.parse(result.content[0].text);
|
|
100
|
-
expect(parsed.summary.totalDevHours).toBe(24);
|
|
101
|
-
expect(parsed.summary.totalReviewHours).toBe(6);
|
|
102
|
-
expect(parsed.summary.totalCostUsd).toBe(1500);
|
|
103
|
-
});
|
|
104
|
-
it('should handle errors gracefully', async () => {
|
|
105
|
-
vi.mocked(knowledgeStore.getKnowledge).mockRejectedValue(new Error('Storage fail'));
|
|
106
|
-
const result = await handleListSpecs({ projectId: 'proj-123' });
|
|
107
|
-
expect(result.isError).toBe(true);
|
|
108
|
-
});
|
|
109
|
-
});
|
|
110
|
-
//# sourceMappingURL=list-specs.test.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"list-specs.test.js","sourceRoot":"","sources":["../../src/tools/list-specs.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"manage-context.test.d.ts","sourceRoot":"","sources":["../../src/tools/manage-context.test.ts"],"names":[],"mappings":""}
|
|
@@ -1,359 +0,0 @@
|
|
|
1
|
-
// Tests for handleManageContext
|
|
2
|
-
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
3
|
-
vi.mock('../i18n/index.js', () => ({
|
|
4
|
-
t: (key) => key,
|
|
5
|
-
ti: (key, vars) => `${key}:${JSON.stringify(vars)}`,
|
|
6
|
-
}));
|
|
7
|
-
vi.mock('../storage/base-store.js', () => ({
|
|
8
|
-
projectDataDir: (projectId) => `data/projects/${projectId}`,
|
|
9
|
-
}));
|
|
10
|
-
vi.mock('node:fs/promises', () => ({
|
|
11
|
-
readFile: vi.fn(),
|
|
12
|
-
writeFile: vi.fn(),
|
|
13
|
-
readdir: vi.fn(),
|
|
14
|
-
unlink: vi.fn(),
|
|
15
|
-
mkdir: vi.fn(),
|
|
16
|
-
}));
|
|
17
|
-
import { handleManageContext } from './manage-context.js';
|
|
18
|
-
import { readFile, writeFile, readdir, unlink, mkdir } from 'node:fs/promises';
|
|
19
|
-
const mockReadFile = vi.mocked(readFile);
|
|
20
|
-
const mockWriteFile = vi.mocked(writeFile);
|
|
21
|
-
const mockReaddir = vi.mocked(readdir);
|
|
22
|
-
const mockUnlink = vi.mocked(unlink);
|
|
23
|
-
const mockMkdir = vi.mocked(mkdir);
|
|
24
|
-
beforeEach(() => {
|
|
25
|
-
vi.clearAllMocks();
|
|
26
|
-
mockMkdir.mockResolvedValue(undefined);
|
|
27
|
-
mockWriteFile.mockResolvedValue(undefined);
|
|
28
|
-
mockUnlink.mockResolvedValue(undefined);
|
|
29
|
-
});
|
|
30
|
-
describe('handleManageContext', () => {
|
|
31
|
-
// === save action ===
|
|
32
|
-
it('should error when save called without key', async () => {
|
|
33
|
-
const result = await handleManageContext({
|
|
34
|
-
projectId: 'proj-1',
|
|
35
|
-
action: 'save',
|
|
36
|
-
content: 'hello',
|
|
37
|
-
});
|
|
38
|
-
expect(result.isError).toBe(true);
|
|
39
|
-
expect(result.content[0].text).toContain('key');
|
|
40
|
-
});
|
|
41
|
-
it('should error when save called without content', async () => {
|
|
42
|
-
const result = await handleManageContext({
|
|
43
|
-
projectId: 'proj-1',
|
|
44
|
-
action: 'save',
|
|
45
|
-
key: 'test-note',
|
|
46
|
-
});
|
|
47
|
-
expect(result.isError).toBe(true);
|
|
48
|
-
expect(result.content[0].text).toContain('content');
|
|
49
|
-
});
|
|
50
|
-
it('should save a context note successfully', async () => {
|
|
51
|
-
const result = await handleManageContext({
|
|
52
|
-
projectId: 'proj-1',
|
|
53
|
-
action: 'save',
|
|
54
|
-
key: 'meeting-notes',
|
|
55
|
-
content: '# Meeting Notes\nDiscussed architecture.',
|
|
56
|
-
});
|
|
57
|
-
expect(result.isError).toBeUndefined();
|
|
58
|
-
const data = JSON.parse(result.content[0].text);
|
|
59
|
-
expect(data.action).toBe('save');
|
|
60
|
-
expect(data.key).toBe('meeting-notes');
|
|
61
|
-
expect(mockWriteFile).toHaveBeenCalledTimes(2); // .md and .meta.json
|
|
62
|
-
expect(mockMkdir).toHaveBeenCalled();
|
|
63
|
-
});
|
|
64
|
-
it('should save with TTL when ttlDays provided', async () => {
|
|
65
|
-
const result = await handleManageContext({
|
|
66
|
-
projectId: 'proj-1',
|
|
67
|
-
action: 'save',
|
|
68
|
-
key: 'temp-note',
|
|
69
|
-
content: 'Temporary content',
|
|
70
|
-
ttlDays: 7,
|
|
71
|
-
});
|
|
72
|
-
const data = JSON.parse(result.content[0].text);
|
|
73
|
-
expect(data.action).toBe('save');
|
|
74
|
-
// Verify meta includes expiresAt
|
|
75
|
-
const metaCall = mockWriteFile.mock.calls.find((c) => c[0].includes('.meta.json'));
|
|
76
|
-
expect(metaCall).toBeDefined();
|
|
77
|
-
const meta = JSON.parse(metaCall[1]);
|
|
78
|
-
expect(meta.expiresAt).toBeDefined();
|
|
79
|
-
});
|
|
80
|
-
// === load action ===
|
|
81
|
-
it('should error when load called without key', async () => {
|
|
82
|
-
const result = await handleManageContext({
|
|
83
|
-
projectId: 'proj-1',
|
|
84
|
-
action: 'load',
|
|
85
|
-
});
|
|
86
|
-
expect(result.isError).toBe(true);
|
|
87
|
-
});
|
|
88
|
-
it('should load a context note', async () => {
|
|
89
|
-
mockReadFile.mockImplementation(((path) => {
|
|
90
|
-
if (path.endsWith('.md')) {
|
|
91
|
-
return Promise.resolve('# Notes\nSome content');
|
|
92
|
-
}
|
|
93
|
-
if (path.endsWith('.meta.json')) {
|
|
94
|
-
return Promise.resolve(JSON.stringify({ key: 'notes', createdAt: '2025-01-01' }));
|
|
95
|
-
}
|
|
96
|
-
return Promise.reject(new Error('not found'));
|
|
97
|
-
}));
|
|
98
|
-
const result = await handleManageContext({
|
|
99
|
-
projectId: 'proj-1',
|
|
100
|
-
action: 'load',
|
|
101
|
-
key: 'notes',
|
|
102
|
-
});
|
|
103
|
-
expect(result.isError).toBeUndefined();
|
|
104
|
-
const data = JSON.parse(result.content[0].text);
|
|
105
|
-
expect(data.action).toBe('load');
|
|
106
|
-
expect(data.content).toContain('Some content');
|
|
107
|
-
});
|
|
108
|
-
it('should return error when note not found on load', async () => {
|
|
109
|
-
mockReadFile.mockRejectedValue(new Error('ENOENT'));
|
|
110
|
-
const result = await handleManageContext({
|
|
111
|
-
projectId: 'proj-1',
|
|
112
|
-
action: 'load',
|
|
113
|
-
key: 'nonexistent',
|
|
114
|
-
});
|
|
115
|
-
expect(result.isError).toBe(true);
|
|
116
|
-
expect(result.content[0].text).toContain('not found');
|
|
117
|
-
});
|
|
118
|
-
it('should delete and return error for expired note on load', async () => {
|
|
119
|
-
const expiredDate = new Date(Date.now() - 86400000).toISOString();
|
|
120
|
-
mockReadFile.mockImplementation(((path) => {
|
|
121
|
-
if (path.endsWith('.md')) {
|
|
122
|
-
return Promise.resolve('old content');
|
|
123
|
-
}
|
|
124
|
-
if (path.endsWith('.meta.json')) {
|
|
125
|
-
return Promise.resolve(JSON.stringify({
|
|
126
|
-
key: 'expired',
|
|
127
|
-
createdAt: '2025-01-01',
|
|
128
|
-
expiresAt: expiredDate,
|
|
129
|
-
}));
|
|
130
|
-
}
|
|
131
|
-
return Promise.reject(new Error('not found'));
|
|
132
|
-
}));
|
|
133
|
-
const result = await handleManageContext({
|
|
134
|
-
projectId: 'proj-1',
|
|
135
|
-
action: 'load',
|
|
136
|
-
key: 'expired',
|
|
137
|
-
});
|
|
138
|
-
expect(result.isError).toBe(true);
|
|
139
|
-
expect(result.content[0].text).toContain('expired');
|
|
140
|
-
expect(mockUnlink).toHaveBeenCalled();
|
|
141
|
-
});
|
|
142
|
-
// === list action ===
|
|
143
|
-
it('should list context notes', async () => {
|
|
144
|
-
mockReaddir.mockResolvedValue(['note1.md', 'note2.md', 'note1.meta.json']);
|
|
145
|
-
mockReadFile.mockImplementation(((path) => {
|
|
146
|
-
if (path.endsWith('.md')) {
|
|
147
|
-
return Promise.resolve('# Title\nContent here');
|
|
148
|
-
}
|
|
149
|
-
if (path.endsWith('.meta.json')) {
|
|
150
|
-
return Promise.resolve(JSON.stringify({ key: 'note', createdAt: '2025-01-01' }));
|
|
151
|
-
}
|
|
152
|
-
return Promise.reject(new Error('not found'));
|
|
153
|
-
}));
|
|
154
|
-
const result = await handleManageContext({
|
|
155
|
-
projectId: 'proj-1',
|
|
156
|
-
action: 'list',
|
|
157
|
-
});
|
|
158
|
-
const data = JSON.parse(result.content[0].text);
|
|
159
|
-
expect(data.action).toBe('list');
|
|
160
|
-
expect(data.notes.length).toBeGreaterThan(0);
|
|
161
|
-
});
|
|
162
|
-
it('should return empty list when no notes', async () => {
|
|
163
|
-
mockReaddir.mockRejectedValue(new Error('ENOENT'));
|
|
164
|
-
const result = await handleManageContext({
|
|
165
|
-
projectId: 'proj-1',
|
|
166
|
-
action: 'list',
|
|
167
|
-
});
|
|
168
|
-
const data = JSON.parse(result.content[0].text);
|
|
169
|
-
expect(data.notes).toHaveLength(0);
|
|
170
|
-
});
|
|
171
|
-
// === delete action ===
|
|
172
|
-
it('should error when delete called without key', async () => {
|
|
173
|
-
const result = await handleManageContext({
|
|
174
|
-
projectId: 'proj-1',
|
|
175
|
-
action: 'delete',
|
|
176
|
-
});
|
|
177
|
-
expect(result.isError).toBe(true);
|
|
178
|
-
});
|
|
179
|
-
it('should delete a note', async () => {
|
|
180
|
-
mockReadFile.mockResolvedValue(JSON.stringify({ key: 'old-note', createdAt: '2025-01-01' }));
|
|
181
|
-
const result = await handleManageContext({
|
|
182
|
-
projectId: 'proj-1',
|
|
183
|
-
action: 'delete',
|
|
184
|
-
key: 'old-note',
|
|
185
|
-
});
|
|
186
|
-
const data = JSON.parse(result.content[0].text);
|
|
187
|
-
expect(data.action).toBe('delete');
|
|
188
|
-
expect(mockUnlink).toHaveBeenCalled();
|
|
189
|
-
});
|
|
190
|
-
it('should return error when deleting non-existent note', async () => {
|
|
191
|
-
mockReadFile.mockRejectedValue(new Error('not found'));
|
|
192
|
-
const result = await handleManageContext({
|
|
193
|
-
projectId: 'proj-1',
|
|
194
|
-
action: 'delete',
|
|
195
|
-
key: 'nonexistent',
|
|
196
|
-
});
|
|
197
|
-
expect(result.isError).toBe(true);
|
|
198
|
-
});
|
|
199
|
-
// === cleanup action ===
|
|
200
|
-
it('should clean up expired notes', async () => {
|
|
201
|
-
const expiredDate = new Date(Date.now() - 86400000).toISOString();
|
|
202
|
-
mockReaddir.mockResolvedValue(['expired.md', 'valid.md']);
|
|
203
|
-
mockReadFile.mockImplementation(((path) => {
|
|
204
|
-
if (path.includes('expired') && path.endsWith('.meta.json')) {
|
|
205
|
-
return Promise.resolve(JSON.stringify({
|
|
206
|
-
key: 'expired',
|
|
207
|
-
createdAt: '2025-01-01',
|
|
208
|
-
expiresAt: expiredDate,
|
|
209
|
-
}));
|
|
210
|
-
}
|
|
211
|
-
if (path.includes('valid') && path.endsWith('.meta.json')) {
|
|
212
|
-
return Promise.resolve(JSON.stringify({ key: 'valid', createdAt: '2025-01-01' }));
|
|
213
|
-
}
|
|
214
|
-
return Promise.reject(new Error('not found'));
|
|
215
|
-
}));
|
|
216
|
-
const result = await handleManageContext({
|
|
217
|
-
projectId: 'proj-1',
|
|
218
|
-
action: 'cleanup',
|
|
219
|
-
});
|
|
220
|
-
const data = JSON.parse(result.content[0].text);
|
|
221
|
-
expect(data.action).toBe('cleanup');
|
|
222
|
-
expect(data.message).toContain('removed');
|
|
223
|
-
});
|
|
224
|
-
// === list: skip expired notes ===
|
|
225
|
-
it('should skip expired notes in list', async () => {
|
|
226
|
-
const expiredDate = new Date(Date.now() - 86400000).toISOString();
|
|
227
|
-
const validDate = new Date(Date.now() + 86400000).toISOString();
|
|
228
|
-
mockReaddir.mockResolvedValue(['expired.md', 'valid.md']);
|
|
229
|
-
mockReadFile.mockImplementation(((path) => {
|
|
230
|
-
if (path.includes('expired') && path.endsWith('.meta.json')) {
|
|
231
|
-
return Promise.resolve(JSON.stringify({
|
|
232
|
-
key: 'expired',
|
|
233
|
-
createdAt: '2025-01-01',
|
|
234
|
-
expiresAt: expiredDate,
|
|
235
|
-
}));
|
|
236
|
-
}
|
|
237
|
-
if (path.includes('valid') && path.endsWith('.meta.json')) {
|
|
238
|
-
return Promise.resolve(JSON.stringify({
|
|
239
|
-
key: 'valid',
|
|
240
|
-
createdAt: '2025-06-01',
|
|
241
|
-
expiresAt: validDate,
|
|
242
|
-
}));
|
|
243
|
-
}
|
|
244
|
-
if (path.endsWith('.md')) {
|
|
245
|
-
return Promise.resolve('# Valid note content');
|
|
246
|
-
}
|
|
247
|
-
return Promise.reject(new Error('not found'));
|
|
248
|
-
}));
|
|
249
|
-
const result = await handleManageContext({
|
|
250
|
-
projectId: 'proj-1',
|
|
251
|
-
action: 'list',
|
|
252
|
-
});
|
|
253
|
-
const data = JSON.parse(result.content[0].text);
|
|
254
|
-
// Expired note should be skipped, only valid note remains
|
|
255
|
-
expect(data.notes).toHaveLength(1);
|
|
256
|
-
expect(data.notes[0].key).toBe('valid');
|
|
257
|
-
});
|
|
258
|
-
// === list: content preview fallback when file read fails ===
|
|
259
|
-
it('should show fallback preview when content file read fails', async () => {
|
|
260
|
-
mockReaddir.mockResolvedValue(['broken.md']);
|
|
261
|
-
mockReadFile.mockImplementation(((path) => {
|
|
262
|
-
if (path.endsWith('.meta.json')) {
|
|
263
|
-
return Promise.resolve(JSON.stringify({ key: 'broken', createdAt: '2025-01-01' }));
|
|
264
|
-
}
|
|
265
|
-
// .md file always fails
|
|
266
|
-
return Promise.reject(new Error('read error'));
|
|
267
|
-
}));
|
|
268
|
-
const result = await handleManageContext({
|
|
269
|
-
projectId: 'proj-1',
|
|
270
|
-
action: 'list',
|
|
271
|
-
});
|
|
272
|
-
const data = JSON.parse(result.content[0].text);
|
|
273
|
-
expect(data.notes).toHaveLength(1);
|
|
274
|
-
expect(data.notes[0].preview).toBe('(unable to read)');
|
|
275
|
-
});
|
|
276
|
-
// === list: sort comparator for unknown timestamps ===
|
|
277
|
-
it('should sort notes with unknown createdAt to the end', async () => {
|
|
278
|
-
mockReaddir.mockResolvedValue(['old.md', 'unknown.md', 'recent.md']);
|
|
279
|
-
mockReadFile.mockImplementation(((path) => {
|
|
280
|
-
if (path.includes('old') && path.endsWith('.meta.json')) {
|
|
281
|
-
return Promise.resolve(JSON.stringify({ key: 'old', createdAt: '2024-01-01' }));
|
|
282
|
-
}
|
|
283
|
-
if (path.includes('unknown') && path.endsWith('.meta.json')) {
|
|
284
|
-
// No meta file -> createdAt will be 'unknown'
|
|
285
|
-
return Promise.reject(new Error('no meta'));
|
|
286
|
-
}
|
|
287
|
-
if (path.includes('recent') && path.endsWith('.meta.json')) {
|
|
288
|
-
return Promise.resolve(JSON.stringify({ key: 'recent', createdAt: '2025-06-01' }));
|
|
289
|
-
}
|
|
290
|
-
if (path.endsWith('.md')) {
|
|
291
|
-
return Promise.resolve('some content');
|
|
292
|
-
}
|
|
293
|
-
return Promise.reject(new Error('not found'));
|
|
294
|
-
}));
|
|
295
|
-
const result = await handleManageContext({
|
|
296
|
-
projectId: 'proj-1',
|
|
297
|
-
action: 'list',
|
|
298
|
-
});
|
|
299
|
-
const data = JSON.parse(result.content[0].text);
|
|
300
|
-
// Notes with known dates come first (newest first), unknown last
|
|
301
|
-
const keys = data.notes.map((n) => n.key);
|
|
302
|
-
expect(keys[keys.length - 1]).toBe('unknown');
|
|
303
|
-
});
|
|
304
|
-
it('should sort when first note has unknown createdAt', async () => {
|
|
305
|
-
mockReaddir.mockResolvedValue(['a.md', 'b.md']);
|
|
306
|
-
mockReadFile.mockImplementation(((path) => {
|
|
307
|
-
if (path.includes('/a') && path.endsWith('.meta.json')) {
|
|
308
|
-
return Promise.reject(new Error('no meta'));
|
|
309
|
-
}
|
|
310
|
-
if (path.includes('/b') && path.endsWith('.meta.json')) {
|
|
311
|
-
return Promise.resolve(JSON.stringify({ key: 'b-note', createdAt: '2025-06-01' }));
|
|
312
|
-
}
|
|
313
|
-
if (path.endsWith('.md')) {
|
|
314
|
-
return Promise.resolve('content');
|
|
315
|
-
}
|
|
316
|
-
return Promise.reject(new Error('not found'));
|
|
317
|
-
}));
|
|
318
|
-
const result = await handleManageContext({
|
|
319
|
-
projectId: 'proj-1',
|
|
320
|
-
action: 'list',
|
|
321
|
-
});
|
|
322
|
-
const data = JSON.parse(result.content[0].text);
|
|
323
|
-
// The note with a known date should come first
|
|
324
|
-
expect(data.notes[0].createdAt).not.toBe('unknown');
|
|
325
|
-
});
|
|
326
|
-
// === list: preview truncation for long content (lines 92-94) ===
|
|
327
|
-
it('should truncate preview to 120 chars with ellipsis for long content', async () => {
|
|
328
|
-
const longLine = 'A'.repeat(200);
|
|
329
|
-
mockReaddir.mockResolvedValue(['long-note.md']);
|
|
330
|
-
mockReadFile.mockImplementation(((path) => {
|
|
331
|
-
if (path.endsWith('.meta.json')) {
|
|
332
|
-
return Promise.resolve(JSON.stringify({ key: 'long-note', createdAt: '2025-01-01' }));
|
|
333
|
-
}
|
|
334
|
-
if (path.endsWith('.md')) {
|
|
335
|
-
return Promise.resolve(longLine);
|
|
336
|
-
}
|
|
337
|
-
return Promise.reject(new Error('not found'));
|
|
338
|
-
}));
|
|
339
|
-
const result = await handleManageContext({
|
|
340
|
-
projectId: 'proj-1',
|
|
341
|
-
action: 'list',
|
|
342
|
-
});
|
|
343
|
-
const data = JSON.parse(result.content[0].text);
|
|
344
|
-
expect(data.notes).toHaveLength(1);
|
|
345
|
-
// Preview should be truncated: 117 chars + '...'
|
|
346
|
-
expect(data.notes[0].preview.length).toBe(120);
|
|
347
|
-
expect(data.notes[0].preview).toMatch(/\.\.\.$/);
|
|
348
|
-
});
|
|
349
|
-
// === unknown action ===
|
|
350
|
-
it('should return error for unknown action', async () => {
|
|
351
|
-
const result = await handleManageContext({
|
|
352
|
-
projectId: 'proj-1',
|
|
353
|
-
action: 'unknown',
|
|
354
|
-
});
|
|
355
|
-
expect(result.isError).toBe(true);
|
|
356
|
-
expect(result.content[0].text).toContain('Unknown action');
|
|
357
|
-
});
|
|
358
|
-
});
|
|
359
|
-
//# sourceMappingURL=manage-context.test.js.map
|