@scardis/omnifocus-mcp 0.1.0 → 0.1.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/dist/schemas/shapes.d.ts +3 -33
- package/dist/schemas/shapes.d.ts.map +1 -1
- package/dist/schemas/shapes.js +3 -5
- package/dist/schemas/shapes.js.map +1 -1
- package/dist/server.js +3 -5
- package/dist/server.js.map +1 -1
- package/dist/tools/createTask.d.ts +1 -11
- package/dist/tools/createTask.d.ts.map +1 -1
- package/dist/tools/editTask.d.ts +1 -11
- package/dist/tools/editTask.d.ts.map +1 -1
- package/dist/tools/index.d.ts +2 -22
- package/dist/tools/index.d.ts.map +1 -1
- package/package.json +7 -1
- package/src/snippets/edit_task.js +4 -6
- package/.claude/commands/opsx/apply.md +0 -152
- package/.claude/commands/opsx/archive.md +0 -157
- package/.claude/commands/opsx/bulk-archive.md +0 -242
- package/.claude/commands/opsx/continue.md +0 -114
- package/.claude/commands/opsx/explore.md +0 -173
- package/.claude/commands/opsx/ff.md +0 -97
- package/.claude/commands/opsx/new.md +0 -69
- package/.claude/commands/opsx/onboard.md +0 -550
- package/.claude/commands/opsx/propose.md +0 -106
- package/.claude/commands/opsx/sync.md +0 -134
- package/.claude/commands/opsx/verify.md +0 -164
- package/.claude/skills/openspec-apply-change/SKILL.md +0 -156
- package/.claude/skills/openspec-archive-change/SKILL.md +0 -114
- package/.claude/skills/openspec-bulk-archive-change/SKILL.md +0 -246
- package/.claude/skills/openspec-continue-change/SKILL.md +0 -118
- package/.claude/skills/openspec-explore/SKILL.md +0 -288
- package/.claude/skills/openspec-ff-change/SKILL.md +0 -101
- package/.claude/skills/openspec-new-change/SKILL.md +0 -74
- package/.claude/skills/openspec-onboard/SKILL.md +0 -554
- package/.claude/skills/openspec-propose/SKILL.md +0 -110
- package/.claude/skills/openspec-sync-specs/SKILL.md +0 -138
- package/.claude/skills/openspec-verify-change/SKILL.md +0 -168
- package/CONTRIBUTING.md +0 -83
- package/openspec/changes/archive/2026-04-09-bootstrap-omnifocus-mcp/.openspec.yaml +0 -2
- package/openspec/changes/archive/2026-04-09-bootstrap-omnifocus-mcp/design.md +0 -162
- package/openspec/changes/archive/2026-04-09-bootstrap-omnifocus-mcp/proposal.md +0 -49
- package/openspec/changes/archive/2026-04-09-bootstrap-omnifocus-mcp/specs/attachments/spec.md +0 -9
- package/openspec/changes/archive/2026-04-09-bootstrap-omnifocus-mcp/specs/batch-operations/spec.md +0 -9
- package/openspec/changes/archive/2026-04-09-bootstrap-omnifocus-mcp/specs/database-inspection/spec.md +0 -9
- package/openspec/changes/archive/2026-04-09-bootstrap-omnifocus-mcp/specs/execution-runtime/spec.md +0 -69
- package/openspec/changes/archive/2026-04-09-bootstrap-omnifocus-mcp/specs/folder-management/spec.md +0 -25
- package/openspec/changes/archive/2026-04-09-bootstrap-omnifocus-mcp/specs/forecast/spec.md +0 -9
- package/openspec/changes/archive/2026-04-09-bootstrap-omnifocus-mcp/specs/identity-resolution/spec.md +0 -45
- package/openspec/changes/archive/2026-04-09-bootstrap-omnifocus-mcp/specs/perspective-management/spec.md +0 -9
- package/openspec/changes/archive/2026-04-09-bootstrap-omnifocus-mcp/specs/project-management/spec.md +0 -25
- package/openspec/changes/archive/2026-04-09-bootstrap-omnifocus-mcp/specs/recurrence/spec.md +0 -9
- package/openspec/changes/archive/2026-04-09-bootstrap-omnifocus-mcp/specs/settings/spec.md +0 -9
- package/openspec/changes/archive/2026-04-09-bootstrap-omnifocus-mcp/specs/tag-management/spec.md +0 -25
- package/openspec/changes/archive/2026-04-09-bootstrap-omnifocus-mcp/specs/task-management/spec.md +0 -29
- package/openspec/changes/archive/2026-04-09-bootstrap-omnifocus-mcp/specs/url-automation/spec.md +0 -9
- package/openspec/changes/archive/2026-04-09-bootstrap-omnifocus-mcp/specs/window-state/spec.md +0 -9
- package/openspec/changes/archive/2026-04-09-bootstrap-omnifocus-mcp/tasks.md +0 -84
- package/openspec/changes/archive/2026-04-09-folder-crud/.openspec.yaml +0 -2
- package/openspec/changes/archive/2026-04-09-folder-crud/design.md +0 -58
- package/openspec/changes/archive/2026-04-09-folder-crud/proposal.md +0 -28
- package/openspec/changes/archive/2026-04-09-folder-crud/specs/folder-write/spec.md +0 -45
- package/openspec/changes/archive/2026-04-09-folder-crud/tasks.md +0 -41
- package/openspec/changes/archive/2026-04-09-folder-tag-list-filtering/.openspec.yaml +0 -2
- package/openspec/changes/archive/2026-04-09-folder-tag-list-filtering/design.md +0 -38
- package/openspec/changes/archive/2026-04-09-folder-tag-list-filtering/proposal.md +0 -30
- package/openspec/changes/archive/2026-04-09-folder-tag-list-filtering/specs/folder-management/spec.md +0 -21
- package/openspec/changes/archive/2026-04-09-folder-tag-list-filtering/specs/tag-management/spec.md +0 -21
- package/openspec/changes/archive/2026-04-09-folder-tag-list-filtering/tasks.md +0 -35
- package/openspec/changes/archive/2026-04-09-move-operations/.openspec.yaml +0 -2
- package/openspec/changes/archive/2026-04-09-move-operations/design.md +0 -43
- package/openspec/changes/archive/2026-04-09-move-operations/proposal.md +0 -25
- package/openspec/changes/archive/2026-04-09-move-operations/specs/move-operations/spec.md +0 -41
- package/openspec/changes/archive/2026-04-09-move-operations/tasks.md +0 -40
- package/openspec/changes/archive/2026-04-09-project-crud/.openspec.yaml +0 -2
- package/openspec/changes/archive/2026-04-09-project-crud/design.md +0 -60
- package/openspec/changes/archive/2026-04-09-project-crud/proposal.md +0 -29
- package/openspec/changes/archive/2026-04-09-project-crud/specs/project-write/spec.md +0 -74
- package/openspec/changes/archive/2026-04-09-project-crud/tasks.md +0 -48
- package/openspec/changes/archive/2026-04-09-project-filtering/.openspec.yaml +0 -2
- package/openspec/changes/archive/2026-04-09-project-filtering/design.md +0 -52
- package/openspec/changes/archive/2026-04-09-project-filtering/proposal.md +0 -26
- package/openspec/changes/archive/2026-04-09-project-filtering/specs/project-filtering/spec.md +0 -66
- package/openspec/changes/archive/2026-04-09-project-filtering/specs/project-management/spec.md +0 -13
- package/openspec/changes/archive/2026-04-09-project-filtering/tasks.md +0 -41
- package/openspec/changes/archive/2026-04-09-tag-crud/.openspec.yaml +0 -2
- package/openspec/changes/archive/2026-04-09-tag-crud/design.md +0 -45
- package/openspec/changes/archive/2026-04-09-tag-crud/proposal.md +0 -28
- package/openspec/changes/archive/2026-04-09-tag-crud/specs/tag-write/spec.md +0 -49
- package/openspec/changes/archive/2026-04-09-tag-crud/tasks.md +0 -41
- package/openspec/changes/archive/2026-04-09-task-crud/.openspec.yaml +0 -2
- package/openspec/changes/archive/2026-04-09-task-crud/design.md +0 -62
- package/openspec/changes/archive/2026-04-09-task-crud/proposal.md +0 -29
- package/openspec/changes/archive/2026-04-09-task-crud/specs/task-management/spec.md +0 -17
- package/openspec/changes/archive/2026-04-09-task-crud/specs/task-write/spec.md +0 -89
- package/openspec/changes/archive/2026-04-09-task-crud/tasks.md +0 -55
- package/openspec/changes/archive/2026-04-09-task-filtering/.openspec.yaml +0 -2
- package/openspec/changes/archive/2026-04-09-task-filtering/design.md +0 -61
- package/openspec/changes/archive/2026-04-09-task-filtering/proposal.md +0 -26
- package/openspec/changes/archive/2026-04-09-task-filtering/specs/task-filtering/spec.md +0 -63
- package/openspec/changes/archive/2026-04-09-task-filtering/specs/task-management/spec.md +0 -17
- package/openspec/changes/archive/2026-04-09-task-filtering/tasks.md +0 -42
- package/openspec/changes/archive/2026-04-10-planned-date/.openspec.yaml +0 -2
- package/openspec/changes/archive/2026-04-10-planned-date/design.md +0 -27
- package/openspec/changes/archive/2026-04-10-planned-date/proposal.md +0 -29
- package/openspec/changes/archive/2026-04-10-planned-date/specs/task-management/spec.md +0 -29
- package/openspec/changes/archive/2026-04-10-planned-date/specs/task-write/spec.md +0 -69
- package/openspec/changes/archive/2026-04-10-planned-date/tasks.md +0 -26
- package/openspec/changes/archive/2026-04-10-task-recurrence/.openspec.yaml +0 -2
- package/openspec/changes/archive/2026-04-10-task-recurrence/design.md +0 -81
- package/openspec/changes/archive/2026-04-10-task-recurrence/proposal.md +0 -28
- package/openspec/changes/archive/2026-04-10-task-recurrence/specs/recurrence/spec.md +0 -47
- package/openspec/changes/archive/2026-04-10-task-recurrence/specs/task-management/spec.md +0 -25
- package/openspec/changes/archive/2026-04-10-task-recurrence/specs/task-write/spec.md +0 -61
- package/openspec/changes/archive/2026-04-10-task-recurrence/tasks.md +0 -39
- package/openspec/config.yaml +0 -20
- package/openspec/specs/attachments/spec.md +0 -15
- package/openspec/specs/batch-operations/spec.md +0 -15
- package/openspec/specs/database-inspection/spec.md +0 -15
- package/openspec/specs/execution-runtime/spec.md +0 -75
- package/openspec/specs/folder-management/spec.md +0 -39
- package/openspec/specs/folder-write/spec.md +0 -45
- package/openspec/specs/forecast/spec.md +0 -15
- package/openspec/specs/identity-resolution/spec.md +0 -51
- package/openspec/specs/move-operations/spec.md +0 -41
- package/openspec/specs/perspective-management/spec.md +0 -15
- package/openspec/specs/project-filtering/spec.md +0 -72
- package/openspec/specs/project-management/spec.md +0 -31
- package/openspec/specs/project-write/spec.md +0 -79
- package/openspec/specs/recurrence/spec.md +0 -51
- package/openspec/specs/settings/spec.md +0 -15
- package/openspec/specs/tag-management/spec.md +0 -39
- package/openspec/specs/tag-write/spec.md +0 -49
- package/openspec/specs/task-filtering/spec.md +0 -63
- package/openspec/specs/task-management/spec.md +0 -51
- package/openspec/specs/task-write/spec.md +0 -115
- package/openspec/specs/url-automation/spec.md +0 -15
- package/openspec/specs/window-state/spec.md +0 -15
- package/scripts/cleanup-fixtures.ts +0 -89
- package/server.json +0 -21
- package/src/runtime/bridge.ts +0 -97
- package/src/runtime/index.ts +0 -4
- package/src/runtime/jxaShim.ts +0 -55
- package/src/runtime/resultProtocol.ts +0 -62
- package/src/runtime/snippetLoader.ts +0 -79
- package/src/schemas/enums.ts +0 -32
- package/src/schemas/index.ts +0 -38
- package/src/schemas/shapes.ts +0 -267
- package/src/server.ts +0 -58
- package/src/tools/completeProject.ts +0 -21
- package/src/tools/completeTask.ts +0 -23
- package/src/tools/createFolder.ts +0 -20
- package/src/tools/createProject.ts +0 -20
- package/src/tools/createTag.ts +0 -20
- package/src/tools/createTask.ts +0 -20
- package/src/tools/deleteFolder.ts +0 -24
- package/src/tools/deleteProject.ts +0 -24
- package/src/tools/deleteTag.ts +0 -24
- package/src/tools/deleteTask.ts +0 -26
- package/src/tools/dropProject.ts +0 -21
- package/src/tools/dropTask.ts +0 -23
- package/src/tools/editFolder.ts +0 -19
- package/src/tools/editProject.ts +0 -20
- package/src/tools/editTag.ts +0 -20
- package/src/tools/editTask.ts +0 -20
- package/src/tools/getFolder.ts +0 -24
- package/src/tools/getProject.ts +0 -24
- package/src/tools/getTag.ts +0 -24
- package/src/tools/getTask.ts +0 -24
- package/src/tools/index.ts +0 -85
- package/src/tools/listFolders.ts +0 -32
- package/src/tools/listProjects.ts +0 -32
- package/src/tools/listTags.ts +0 -32
- package/src/tools/listTasks.ts +0 -56
- package/src/tools/moveProject.ts +0 -20
- package/src/tools/moveTask.ts +0 -20
- package/src/tools/resolveName.ts +0 -37
- package/test/integration/.gitkeep +0 -0
- package/test/integration/completeProject.int.test.ts +0 -25
- package/test/integration/completeTask.int.test.ts +0 -30
- package/test/integration/createFolder.int.test.ts +0 -50
- package/test/integration/createProject.int.test.ts +0 -49
- package/test/integration/createTag.int.test.ts +0 -52
- package/test/integration/createTask.int.test.ts +0 -55
- package/test/integration/deleteFolder.int.test.ts +0 -64
- package/test/integration/deleteProject.int.test.ts +0 -31
- package/test/integration/deleteTag.int.test.ts +0 -61
- package/test/integration/deleteTask.int.test.ts +0 -36
- package/test/integration/dropProject.int.test.ts +0 -24
- package/test/integration/dropTask.int.test.ts +0 -29
- package/test/integration/editFolder.int.test.ts +0 -43
- package/test/integration/editProject.int.test.ts +0 -39
- package/test/integration/editTag.int.test.ts +0 -43
- package/test/integration/editTask.int.test.ts +0 -56
- package/test/integration/fixtures.ts +0 -219
- package/test/integration/getTask.int.test.ts +0 -64
- package/test/integration/listFoldersFiltered.int.test.ts +0 -98
- package/test/integration/listProjects.int.test.ts +0 -73
- package/test/integration/listProjectsFiltered.int.test.ts +0 -96
- package/test/integration/listTagsFiltered.int.test.ts +0 -54
- package/test/integration/listTasksFiltered.int.test.ts +0 -141
- package/test/integration/moveProject.int.test.ts +0 -57
- package/test/integration/moveTask.int.test.ts +0 -61
- package/test/integration/plannedDate.int.test.ts +0 -72
- package/test/integration/preflight.ts +0 -60
- package/test/integration/resolveName.int.test.ts +0 -86
- package/test/integration/taskRecurrence.int.test.ts +0 -106
- package/test/unit/.gitkeep +0 -0
- package/test/unit/bridge.injection.test.ts +0 -66
- package/test/unit/resultProtocol.test.ts +0 -71
- package/test/unit/schemas.createFolder.test.ts +0 -38
- package/test/unit/schemas.createProject.test.ts +0 -115
- package/test/unit/schemas.createTask.test.ts +0 -87
- package/test/unit/schemas.editTag.test.ts +0 -64
- package/test/unit/schemas.folderTagFiltering.test.ts +0 -42
- package/test/unit/schemas.listProjects.test.ts +0 -44
- package/test/unit/schemas.moveOperations.test.ts +0 -60
- package/test/unit/schemas.recurrence.test.ts +0 -120
- package/test/unit/schemas.test.ts +0 -126
- package/test/unit/snippetLoader.test.ts +0 -56
- package/test/unit/tools.deleteTask.test.ts +0 -19
- package/test/unit/tools.listTasks.test.ts +0 -126
- package/tsconfig.json +0 -19
- package/vitest.config.ts +0 -8
- package/vitest.integration.config.ts +0 -18
|
@@ -1,120 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from "vitest";
|
|
2
|
-
import { RepetitionRuleInput, CreateTaskInput, EditTaskInput } from "../../src/schemas/index.js";
|
|
3
|
-
|
|
4
|
-
describe("RepetitionRuleInput schema", () => {
|
|
5
|
-
it("accepts daily repetition", () => {
|
|
6
|
-
expect(() =>
|
|
7
|
-
RepetitionRuleInput.parse({ frequency: "daily", interval: 1, method: "fixed" })
|
|
8
|
-
).not.toThrow();
|
|
9
|
-
});
|
|
10
|
-
|
|
11
|
-
it("accepts weekly repetition with daysOfWeek", () => {
|
|
12
|
-
expect(() =>
|
|
13
|
-
RepetitionRuleInput.parse({
|
|
14
|
-
frequency: "weekly",
|
|
15
|
-
interval: 2,
|
|
16
|
-
daysOfWeek: ["monday", "wednesday", "friday"],
|
|
17
|
-
method: "start",
|
|
18
|
-
})
|
|
19
|
-
).not.toThrow();
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
it("accepts monthly repetition", () => {
|
|
23
|
-
expect(() =>
|
|
24
|
-
RepetitionRuleInput.parse({ frequency: "monthly", interval: 1, method: "dueDate" })
|
|
25
|
-
).not.toThrow();
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
it("accepts yearly repetition", () => {
|
|
29
|
-
expect(() =>
|
|
30
|
-
RepetitionRuleInput.parse({ frequency: "yearly", interval: 1, method: "fixed" })
|
|
31
|
-
).not.toThrow();
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
it("defaults interval to 1 when omitted", () => {
|
|
35
|
-
const result = RepetitionRuleInput.parse({ frequency: "daily", method: "fixed" });
|
|
36
|
-
expect(result.interval).toBe(1);
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
it("rejects daysOfWeek on non-weekly frequency", () => {
|
|
40
|
-
expect(() =>
|
|
41
|
-
RepetitionRuleInput.parse({
|
|
42
|
-
frequency: "daily",
|
|
43
|
-
interval: 1,
|
|
44
|
-
daysOfWeek: ["monday"],
|
|
45
|
-
method: "fixed",
|
|
46
|
-
})
|
|
47
|
-
).toThrow();
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
it("rejects daysOfWeek on monthly frequency", () => {
|
|
51
|
-
expect(() =>
|
|
52
|
-
RepetitionRuleInput.parse({
|
|
53
|
-
frequency: "monthly",
|
|
54
|
-
interval: 1,
|
|
55
|
-
daysOfWeek: ["tuesday"],
|
|
56
|
-
method: "fixed",
|
|
57
|
-
})
|
|
58
|
-
).toThrow();
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
it("rejects interval of zero", () => {
|
|
62
|
-
expect(() =>
|
|
63
|
-
RepetitionRuleInput.parse({ frequency: "daily", interval: 0, method: "fixed" })
|
|
64
|
-
).toThrow();
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
it("rejects negative interval", () => {
|
|
68
|
-
expect(() =>
|
|
69
|
-
RepetitionRuleInput.parse({ frequency: "weekly", interval: -1, method: "fixed" })
|
|
70
|
-
).toThrow();
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
it("rejects invalid frequency", () => {
|
|
74
|
-
expect(() =>
|
|
75
|
-
RepetitionRuleInput.parse({ frequency: "hourly", interval: 1, method: "fixed" })
|
|
76
|
-
).toThrow();
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
it("rejects invalid method", () => {
|
|
80
|
-
expect(() =>
|
|
81
|
-
RepetitionRuleInput.parse({ frequency: "daily", interval: 1, method: "completion" })
|
|
82
|
-
).toThrow();
|
|
83
|
-
});
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
describe("CreateTaskInput with repetitionRule", () => {
|
|
87
|
-
it("accepts task without repetitionRule (backward compat)", () => {
|
|
88
|
-
expect(() => CreateTaskInput.parse({ name: "Buy milk" })).not.toThrow();
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
it("accepts task with repetitionRule", () => {
|
|
92
|
-
expect(() =>
|
|
93
|
-
CreateTaskInput.parse({
|
|
94
|
-
name: "Stand-up",
|
|
95
|
-
repetitionRule: { frequency: "daily", interval: 1, method: "fixed" },
|
|
96
|
-
})
|
|
97
|
-
).not.toThrow();
|
|
98
|
-
});
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
describe("EditTaskInput with repetitionRule", () => {
|
|
102
|
-
it("accepts edit without repetitionRule (leave unchanged)", () => {
|
|
103
|
-
expect(() => EditTaskInput.parse({ id: "abc", flagged: true })).not.toThrow();
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
it("accepts edit with repetitionRule object (set rule)", () => {
|
|
107
|
-
expect(() =>
|
|
108
|
-
EditTaskInput.parse({
|
|
109
|
-
id: "abc",
|
|
110
|
-
repetitionRule: { frequency: "weekly", interval: 1, method: "start" },
|
|
111
|
-
})
|
|
112
|
-
).not.toThrow();
|
|
113
|
-
});
|
|
114
|
-
|
|
115
|
-
it("accepts edit with repetitionRule null (clear rule)", () => {
|
|
116
|
-
expect(() =>
|
|
117
|
-
EditTaskInput.parse({ id: "abc", repetitionRule: null })
|
|
118
|
-
).not.toThrow();
|
|
119
|
-
});
|
|
120
|
-
});
|
|
@@ -1,126 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from "vitest";
|
|
2
|
-
import {
|
|
3
|
-
ProjectType,
|
|
4
|
-
ProjectStatus,
|
|
5
|
-
TaskStatus,
|
|
6
|
-
FolderStatus,
|
|
7
|
-
TagStatus,
|
|
8
|
-
TaskSummary,
|
|
9
|
-
TaskDetail,
|
|
10
|
-
ProjectSummary,
|
|
11
|
-
FolderSummary,
|
|
12
|
-
TagSummary,
|
|
13
|
-
} from "../../src/schemas/index.js";
|
|
14
|
-
|
|
15
|
-
describe("ProjectType enum", () => {
|
|
16
|
-
it("accepts valid values", () => {
|
|
17
|
-
expect(ProjectType.parse("parallel")).toBe("parallel");
|
|
18
|
-
expect(ProjectType.parse("sequential")).toBe("sequential");
|
|
19
|
-
expect(ProjectType.parse("singleActions")).toBe("singleActions");
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
it("rejects boolean (the sequential-as-boolean regression)", () => {
|
|
23
|
-
expect(() => ProjectType.parse(true)).toThrow();
|
|
24
|
-
expect(() => ProjectType.parse(false)).toThrow();
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
it("rejects string 'true' (the sequential-as-string regression)", () => {
|
|
28
|
-
expect(() => ProjectType.parse("true")).toThrow();
|
|
29
|
-
expect(() => ProjectType.parse("false")).toThrow();
|
|
30
|
-
});
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
describe("ProjectSummary schema", () => {
|
|
34
|
-
const valid = {
|
|
35
|
-
id: "abc123",
|
|
36
|
-
name: "My Project",
|
|
37
|
-
folderPath: "Work ▸ Projects",
|
|
38
|
-
folderId: "folder1",
|
|
39
|
-
status: "active",
|
|
40
|
-
type: "parallel",
|
|
41
|
-
flagged: false,
|
|
42
|
-
};
|
|
43
|
-
|
|
44
|
-
it("parses a valid summary", () => {
|
|
45
|
-
expect(() => ProjectSummary.parse(valid)).not.toThrow();
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
it("rejects empty id", () => {
|
|
49
|
-
expect(() => ProjectSummary.parse({ ...valid, id: "" })).toThrow();
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
it("rejects invalid status", () => {
|
|
53
|
-
expect(() =>
|
|
54
|
-
ProjectSummary.parse({ ...valid, status: "unknown" })
|
|
55
|
-
).toThrow();
|
|
56
|
-
});
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
describe("TaskSummary schema", () => {
|
|
60
|
-
const valid = {
|
|
61
|
-
id: "t1",
|
|
62
|
-
name: "Buy milk",
|
|
63
|
-
status: "available",
|
|
64
|
-
flagged: false,
|
|
65
|
-
containerId: "p1",
|
|
66
|
-
containerType: "project",
|
|
67
|
-
dueDate: null,
|
|
68
|
-
tagIds: [],
|
|
69
|
-
};
|
|
70
|
-
|
|
71
|
-
it("parses a valid task summary", () => {
|
|
72
|
-
expect(() => TaskSummary.parse(valid)).not.toThrow();
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
it("rejects items-as-string on TaskDetail (the batch_remove_items regression)", () => {
|
|
76
|
-
// The regression: passing a serialised JSON string where an array is expected
|
|
77
|
-
const detailBase = {
|
|
78
|
-
id: "t1", name: "Buy milk", note: "", status: "available",
|
|
79
|
-
flagged: false, deferDate: null, dueDate: null, completionDate: null,
|
|
80
|
-
estimatedMinutes: null, containerId: "p1", containerType: "project",
|
|
81
|
-
};
|
|
82
|
-
expect(() =>
|
|
83
|
-
TaskDetail.parse({ ...detailBase, tagIds: '["abc"]' })
|
|
84
|
-
).toThrow();
|
|
85
|
-
});
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
describe("FolderSummary schema", () => {
|
|
89
|
-
it("parses valid folder summary with null parent", () => {
|
|
90
|
-
expect(() =>
|
|
91
|
-
FolderSummary.parse({
|
|
92
|
-
id: "f1",
|
|
93
|
-
name: "Work",
|
|
94
|
-
path: "Work",
|
|
95
|
-
parentId: null,
|
|
96
|
-
status: "active",
|
|
97
|
-
})
|
|
98
|
-
).not.toThrow();
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
it("rejects invalid status", () => {
|
|
102
|
-
expect(() =>
|
|
103
|
-
FolderSummary.parse({
|
|
104
|
-
id: "f1",
|
|
105
|
-
name: "Work",
|
|
106
|
-
path: "Work",
|
|
107
|
-
parentId: null,
|
|
108
|
-
status: "onHold", // folders only have active/dropped
|
|
109
|
-
})
|
|
110
|
-
).toThrow();
|
|
111
|
-
});
|
|
112
|
-
});
|
|
113
|
-
|
|
114
|
-
describe("TagSummary schema", () => {
|
|
115
|
-
it("parses valid tag summary with onHold status", () => {
|
|
116
|
-
expect(() =>
|
|
117
|
-
TagSummary.parse({
|
|
118
|
-
id: "t1",
|
|
119
|
-
name: "Waiting",
|
|
120
|
-
path: "Waiting",
|
|
121
|
-
parentId: null,
|
|
122
|
-
status: "onHold",
|
|
123
|
-
})
|
|
124
|
-
).not.toThrow();
|
|
125
|
-
});
|
|
126
|
-
});
|
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, beforeEach, afterEach } from "vitest";
|
|
2
|
-
import { writeFileSync, mkdirSync, rmSync } from "fs";
|
|
3
|
-
import { join } from "path";
|
|
4
|
-
import { tmpdir } from "os";
|
|
5
|
-
|
|
6
|
-
// We test the loader logic directly by pointing it at temp fixture files.
|
|
7
|
-
// The real loader uses __dirname-relative paths; we test the core validation
|
|
8
|
-
// logic by importing the internals.
|
|
9
|
-
|
|
10
|
-
describe("snippet __ARGS__ validation", () => {
|
|
11
|
-
// Test the validation logic inline (mirrors snippetLoader.ts exactly)
|
|
12
|
-
function validate(content: string, name: string): void {
|
|
13
|
-
const matches = content.split("__ARGS__").length - 1;
|
|
14
|
-
if (matches === 0) {
|
|
15
|
-
throw new Error(`Snippet "${name}" contains no __ARGS__ placeholder.`);
|
|
16
|
-
}
|
|
17
|
-
if (matches > 1) {
|
|
18
|
-
throw new Error(
|
|
19
|
-
`Snippet "${name}" contains ${matches} __ARGS__ placeholders. Exactly one is required.`
|
|
20
|
-
);
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
it("accepts snippet with exactly one __ARGS__", () => {
|
|
25
|
-
expect(() =>
|
|
26
|
-
validate("const args = __ARGS__; return args;", "valid")
|
|
27
|
-
).not.toThrow();
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
it("rejects snippet with zero __ARGS__", () => {
|
|
31
|
-
expect(() =>
|
|
32
|
-
validate("const x = 1; return x;", "no-placeholder")
|
|
33
|
-
).toThrow(/no __ARGS__ placeholder/);
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
it("rejects snippet with two __ARGS__", () => {
|
|
37
|
-
expect(() =>
|
|
38
|
-
validate("const a = __ARGS__; const b = __ARGS__;", "two-placeholders")
|
|
39
|
-
).toThrow(/2 __ARGS__ placeholders/);
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
it("all real snippets contain exactly one __ARGS__", async () => {
|
|
43
|
-
const { readdirSync, readFileSync } = await import("fs");
|
|
44
|
-
const { join: pathJoin } = await import("path");
|
|
45
|
-
const { fileURLToPath } = await import("url");
|
|
46
|
-
const { dirname } = await import("path");
|
|
47
|
-
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
48
|
-
const snippetsDir = pathJoin(__dirname, "../../src/snippets");
|
|
49
|
-
const files = readdirSync(snippetsDir).filter((f) => f.endsWith(".js"));
|
|
50
|
-
expect(files.length).toBeGreaterThan(0);
|
|
51
|
-
for (const file of files) {
|
|
52
|
-
const content = readFileSync(pathJoin(snippetsDir, file), "utf-8");
|
|
53
|
-
expect(() => validate(content, file), `${file}`).not.toThrow();
|
|
54
|
-
}
|
|
55
|
-
});
|
|
56
|
-
});
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from "vitest";
|
|
2
|
-
import { deleteTaskTool } from "../../src/tools/deleteTask.js";
|
|
3
|
-
|
|
4
|
-
describe("deleteTask tool description", () => {
|
|
5
|
-
it("contains confirmation language", () => {
|
|
6
|
-
const desc = deleteTaskTool.description.toLowerCase();
|
|
7
|
-
expect(desc).toMatch(/confirm/);
|
|
8
|
-
});
|
|
9
|
-
|
|
10
|
-
it("mentions permanent deletion", () => {
|
|
11
|
-
const desc = deleteTaskTool.description.toLowerCase();
|
|
12
|
-
expect(desc).toMatch(/permanent/);
|
|
13
|
-
});
|
|
14
|
-
|
|
15
|
-
it("warns about subtasks", () => {
|
|
16
|
-
const desc = deleteTaskTool.description.toLowerCase();
|
|
17
|
-
expect(desc).toMatch(/subtask/);
|
|
18
|
-
});
|
|
19
|
-
});
|
|
@@ -1,126 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from "vitest";
|
|
2
|
-
import { listTasksSchema } from "../../src/tools/listTasks.js";
|
|
3
|
-
import { ListTasksFilter } from "../../src/schemas/index.js";
|
|
4
|
-
|
|
5
|
-
describe("listTasks input validation", () => {
|
|
6
|
-
it("accepts projectId scope", () => {
|
|
7
|
-
expect(() =>
|
|
8
|
-
listTasksSchema.parse({ scope: { projectId: "abc" } })
|
|
9
|
-
).not.toThrow();
|
|
10
|
-
});
|
|
11
|
-
|
|
12
|
-
it("accepts folderId scope", () => {
|
|
13
|
-
expect(() =>
|
|
14
|
-
listTasksSchema.parse({ scope: { folderId: "f1" } })
|
|
15
|
-
).not.toThrow();
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
it("accepts inbox scope", () => {
|
|
19
|
-
expect(() =>
|
|
20
|
-
listTasksSchema.parse({ scope: { inbox: true } })
|
|
21
|
-
).not.toThrow();
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
it("accepts all scope", () => {
|
|
25
|
-
expect(() =>
|
|
26
|
-
listTasksSchema.parse({ scope: { all: true } })
|
|
27
|
-
).not.toThrow();
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
it("rejects scope with both projectId and inbox (mutual exclusivity)", () => {
|
|
31
|
-
expect(() =>
|
|
32
|
-
listTasksSchema.parse({ scope: { projectId: "abc", inbox: true } })
|
|
33
|
-
).toThrow();
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
it("rejects scope with both folderId and all", () => {
|
|
37
|
-
expect(() =>
|
|
38
|
-
listTasksSchema.parse({ scope: { folderId: "f1", all: true } })
|
|
39
|
-
).toThrow();
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
it("rejects empty scope object (no discriminator)", () => {
|
|
43
|
-
expect(() => listTasksSchema.parse({ scope: {} })).toThrow();
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
it("rejects missing scope", () => {
|
|
47
|
-
expect(() => listTasksSchema.parse({})).toThrow();
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
it("rejects empty projectId string", () => {
|
|
51
|
-
expect(() =>
|
|
52
|
-
listTasksSchema.parse({ scope: { projectId: "" } })
|
|
53
|
-
).toThrow();
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
it("accepts filter with flagged", () => {
|
|
57
|
-
expect(() =>
|
|
58
|
-
listTasksSchema.parse({ scope: { all: true }, filter: { flagged: true } })
|
|
59
|
-
).not.toThrow();
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
it("accepts filter with status array", () => {
|
|
63
|
-
expect(() =>
|
|
64
|
-
listTasksSchema.parse({ scope: { all: true }, filter: { status: ["overdue", "dueSoon"] } })
|
|
65
|
-
).not.toThrow();
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
it("accepts filter with tagId", () => {
|
|
69
|
-
expect(() =>
|
|
70
|
-
listTasksSchema.parse({ scope: { all: true }, filter: { tagId: "tag123" } })
|
|
71
|
-
).not.toThrow();
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
it("accepts filter with dueBeforeDate", () => {
|
|
75
|
-
expect(() =>
|
|
76
|
-
listTasksSchema.parse({ scope: { all: true }, filter: { dueBeforeDate: "2026-04-09T23:59:59.000Z" } })
|
|
77
|
-
).not.toThrow();
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
it("accepts limit", () => {
|
|
81
|
-
expect(() =>
|
|
82
|
-
listTasksSchema.parse({ scope: { all: true }, limit: 50 })
|
|
83
|
-
).not.toThrow();
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
it("rejects non-positive limit", () => {
|
|
87
|
-
expect(() =>
|
|
88
|
-
listTasksSchema.parse({ scope: { all: true }, limit: 0 })
|
|
89
|
-
).toThrow();
|
|
90
|
-
});
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
describe("ListTasksFilter schema", () => {
|
|
94
|
-
it("accepts empty filter object", () => {
|
|
95
|
-
expect(() => ListTasksFilter.parse({})).not.toThrow();
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
it("accepts all valid filter fields", () => {
|
|
99
|
-
expect(() =>
|
|
100
|
-
ListTasksFilter.parse({
|
|
101
|
-
flagged: true,
|
|
102
|
-
status: ["available", "overdue"],
|
|
103
|
-
tagId: "tag123",
|
|
104
|
-
dueBeforeDate: "2026-04-09T23:59:59.000Z",
|
|
105
|
-
})
|
|
106
|
-
).not.toThrow();
|
|
107
|
-
});
|
|
108
|
-
|
|
109
|
-
it("rejects invalid status enum in array", () => {
|
|
110
|
-
expect(() =>
|
|
111
|
-
ListTasksFilter.parse({ status: ["available", "flying"] })
|
|
112
|
-
).toThrow();
|
|
113
|
-
});
|
|
114
|
-
|
|
115
|
-
it("rejects non-ISO dueBeforeDate", () => {
|
|
116
|
-
expect(() =>
|
|
117
|
-
ListTasksFilter.parse({ dueBeforeDate: "April 9, 2026" })
|
|
118
|
-
).toThrow();
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
it("rejects flagged: false (must be literal true or absent)", () => {
|
|
122
|
-
expect(() =>
|
|
123
|
-
ListTasksFilter.parse({ flagged: false })
|
|
124
|
-
).toThrow();
|
|
125
|
-
});
|
|
126
|
-
});
|
package/tsconfig.json
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"compilerOptions": {
|
|
3
|
-
"target": "ES2022",
|
|
4
|
-
"module": "ESNext",
|
|
5
|
-
"moduleResolution": "bundler",
|
|
6
|
-
"lib": ["ES2022"],
|
|
7
|
-
"outDir": "dist",
|
|
8
|
-
"rootDir": "src",
|
|
9
|
-
"strict": true,
|
|
10
|
-
"sourceMap": true,
|
|
11
|
-
"declaration": true,
|
|
12
|
-
"declarationMap": true,
|
|
13
|
-
"esModuleInterop": true,
|
|
14
|
-
"skipLibCheck": true,
|
|
15
|
-
"resolveJsonModule": true
|
|
16
|
-
},
|
|
17
|
-
"include": ["src/**/*"],
|
|
18
|
-
"exclude": ["node_modules", "dist", "test"]
|
|
19
|
-
}
|
package/vitest.config.ts
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import { defineConfig } from "vitest/config";
|
|
2
|
-
|
|
3
|
-
export default defineConfig({
|
|
4
|
-
test: {
|
|
5
|
-
include: ["test/integration/**/*.int.test.ts"],
|
|
6
|
-
environment: "node",
|
|
7
|
-
pool: "forks",
|
|
8
|
-
poolOptions: {
|
|
9
|
-
forks: {
|
|
10
|
-
maxForks: 1,
|
|
11
|
-
minForks: 1,
|
|
12
|
-
},
|
|
13
|
-
},
|
|
14
|
-
globalSetup: ["./test/integration/preflight.ts"],
|
|
15
|
-
testTimeout: 60_000,
|
|
16
|
-
hookTimeout: 60_000,
|
|
17
|
-
},
|
|
18
|
-
});
|