gyrus 0.1.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.
Files changed (43) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +273 -0
  3. package/package.json +78 -0
  4. package/src/commands/adr.ts +482 -0
  5. package/src/commands/doctor.ts +263 -0
  6. package/src/commands/init.ts +293 -0
  7. package/src/commands/knowledge.ts +446 -0
  8. package/src/commands/list.ts +51 -0
  9. package/src/commands/mcp.ts +94 -0
  10. package/src/commands/use.ts +65 -0
  11. package/src/config/index.ts +262 -0
  12. package/src/config/schema.ts +139 -0
  13. package/src/formatters/adr.ts +295 -0
  14. package/src/formatters/index.ts +44 -0
  15. package/src/formatters/knowledge.ts +282 -0
  16. package/src/formatters/workspace.ts +149 -0
  17. package/src/index.ts +153 -0
  18. package/src/operations/adr.ts +312 -0
  19. package/src/operations/index.ts +58 -0
  20. package/src/operations/knowledge.ts +324 -0
  21. package/src/operations/types.ts +199 -0
  22. package/src/operations/workspace.ts +108 -0
  23. package/src/server/mcp-stdio.ts +526 -0
  24. package/src/services/adr.ts +511 -0
  25. package/src/services/knowledge.ts +516 -0
  26. package/src/services/markdown.ts +155 -0
  27. package/src/services/workspace.ts +377 -0
  28. package/src/templates/knowledge/README.md +199 -0
  29. package/src/tools/adr-create.ts +99 -0
  30. package/src/tools/adr-list.ts +72 -0
  31. package/src/tools/adr-read.ts +54 -0
  32. package/src/tools/adr-search.ts +79 -0
  33. package/src/tools/adr-update.ts +95 -0
  34. package/src/tools/gyrus-list.ts +41 -0
  35. package/src/tools/gyrus-switch.ts +45 -0
  36. package/src/tools/index.ts +91 -0
  37. package/src/tools/knowledge-create.ts +75 -0
  38. package/src/tools/knowledge-list.ts +60 -0
  39. package/src/tools/knowledge-read.ts +62 -0
  40. package/src/tools/knowledge-search.ts +65 -0
  41. package/src/tools/knowledge-update.ts +76 -0
  42. package/src/types/index.ts +343 -0
  43. package/tsconfig.json +26 -0
@@ -0,0 +1,65 @@
1
+ /**
2
+ * knowledge_search MCP tool
3
+ * Search knowledge notes by text query, tags, or category
4
+ */
5
+
6
+ import { z } from "zod";
7
+ import type { WorkspaceManager } from "../services/workspace.ts";
8
+ import { searchKnowledge } from "../operations/knowledge.ts";
9
+ import { formatKnowledgeSearch } from "../formatters/knowledge.ts";
10
+
11
+ /**
12
+ * Tool definition
13
+ */
14
+ export const knowledgeSearchTool = {
15
+ name: "knowledge_search",
16
+ description:
17
+ "Search knowledge notes by text query, tags, or category. The query searches across note titles, content, IDs, and descriptions. Results are sorted by relevance (title matches first) then by last updated date. At least one search parameter must be provided. Use the 'workspace' parameter to target a specific workspace, otherwise uses the active workspace.",
18
+ };
19
+
20
+ /**
21
+ * Input schema
22
+ */
23
+ export const knowledgeSearchSchema = z.object({
24
+ workspace: z
25
+ .string()
26
+ .optional()
27
+ .describe(
28
+ "Target workspace name. If not provided, uses the active workspace.",
29
+ ),
30
+ query: z
31
+ .string()
32
+ .optional()
33
+ .describe("Text to search for in note titles, content, and descriptions"),
34
+ tags: z
35
+ .array(z.string())
36
+ .optional()
37
+ .describe("Filter by tags (matches notes with any of these tags)"),
38
+ category: z
39
+ .enum(["projects", "patterns", "tools", "gotchas", "decisions"])
40
+ .optional()
41
+ .describe("Filter by category folder"),
42
+ });
43
+
44
+ export type KnowledgeSearchInput = z.infer<typeof knowledgeSearchSchema>;
45
+
46
+ /**
47
+ * Execute the tool
48
+ */
49
+ export async function executeKnowledgeSearch(
50
+ manager: WorkspaceManager,
51
+ input: KnowledgeSearchInput,
52
+ ): Promise<string> {
53
+ const result = await searchKnowledge(manager, {
54
+ workspace: input.workspace,
55
+ query: input.query,
56
+ tags: input.tags,
57
+ category: input.category,
58
+ });
59
+
60
+ if (!result.success) {
61
+ return `Error: ${result.error}`;
62
+ }
63
+
64
+ return formatKnowledgeSearch(result.data!);
65
+ }
@@ -0,0 +1,76 @@
1
+ /**
2
+ * knowledge_update MCP tool
3
+ * Update an existing knowledge note
4
+ */
5
+
6
+ import { z } from "zod";
7
+ import type { WorkspaceManager } from "../services/workspace.ts";
8
+ import { updateKnowledge } from "../operations/knowledge.ts";
9
+ import { formatKnowledgeUpdate } from "../formatters/knowledge.ts";
10
+
11
+ /**
12
+ * Tool definition
13
+ */
14
+ export const knowledgeUpdateTool = {
15
+ name: "knowledge_update",
16
+ description:
17
+ "Update an existing knowledge note's content or metadata. Only the fields you provide will be updated; other fields remain unchanged. The 'updated' date will be automatically set to today. The index.md file will be automatically updated if the title or tags change. Use the 'workspace' parameter to target a specific workspace, otherwise uses the active workspace.",
18
+ };
19
+
20
+ /**
21
+ * Input schema
22
+ */
23
+ export const knowledgeUpdateSchema = z.object({
24
+ workspace: z
25
+ .string()
26
+ .optional()
27
+ .describe(
28
+ "Target workspace name. If not provided, uses the active workspace.",
29
+ ),
30
+ id: z.string().describe("The unique ID of the note to update"),
31
+ title: z.string().optional().describe("New title for the note (optional)"),
32
+ content: z
33
+ .string()
34
+ .optional()
35
+ .describe(
36
+ "New markdown content for the note (optional, replaces existing content)",
37
+ ),
38
+ tags: z
39
+ .array(z.string())
40
+ .optional()
41
+ .describe("New tags for the note (optional, replaces existing tags)"),
42
+ related: z
43
+ .array(z.string())
44
+ .optional()
45
+ .describe("New related note IDs (optional, replaces existing related)"),
46
+ description: z
47
+ .string()
48
+ .optional()
49
+ .describe("New description for the note (optional)"),
50
+ });
51
+
52
+ export type KnowledgeUpdateInput = z.infer<typeof knowledgeUpdateSchema>;
53
+
54
+ /**
55
+ * Execute the tool
56
+ */
57
+ export async function executeKnowledgeUpdate(
58
+ manager: WorkspaceManager,
59
+ input: KnowledgeUpdateInput,
60
+ ): Promise<string> {
61
+ const result = await updateKnowledge(manager, {
62
+ workspace: input.workspace,
63
+ id: input.id,
64
+ title: input.title,
65
+ content: input.content,
66
+ tags: input.tags,
67
+ related: input.related,
68
+ description: input.description,
69
+ });
70
+
71
+ if (!result.success) {
72
+ return `Error: ${result.error}`;
73
+ }
74
+
75
+ return formatKnowledgeUpdate(result.data!);
76
+ }
@@ -0,0 +1,343 @@
1
+ /**
2
+ * Core type definitions for Gyrus
3
+ * Model-agnostic knowledge management system
4
+ */
5
+
6
+ // =============================================================================
7
+ // Configuration Types
8
+ // =============================================================================
9
+
10
+ /**
11
+ * A registered workspace in the config
12
+ */
13
+ export interface Workspace {
14
+ /** Unique name for this workspace */
15
+ name: string;
16
+ /** Path to the workspace (supports ~ for home) */
17
+ path: string;
18
+ /** Human-readable description */
19
+ description: string;
20
+ }
21
+
22
+ /**
23
+ * Server settings in config
24
+ */
25
+ export interface ServerSettings {
26
+ serve: {
27
+ port: number;
28
+ };
29
+ daemon: {
30
+ port: number;
31
+ };
32
+ }
33
+
34
+ /**
35
+ * Global Gyrus configuration stored at ~/.config/gyrus/config.json
36
+ */
37
+ export interface GyrusConfig {
38
+ /** Config schema version */
39
+ version: number;
40
+ /** Name of the default workspace */
41
+ default: string;
42
+ /** List of registered workspaces */
43
+ workspaces: Workspace[];
44
+ /** Server settings */
45
+ settings: ServerSettings;
46
+ }
47
+
48
+ // =============================================================================
49
+ // Knowledge Types
50
+ // =============================================================================
51
+
52
+ /**
53
+ * Valid knowledge categories (folder names)
54
+ */
55
+ export type KnowledgeCategory =
56
+ | "projects"
57
+ | "patterns"
58
+ | "tools"
59
+ | "gotchas"
60
+ | "decisions"
61
+ | "root";
62
+
63
+ /**
64
+ * YAML frontmatter fields for knowledge notes
65
+ */
66
+ export interface KnowledgeFrontmatter {
67
+ id: string;
68
+ title: string;
69
+ created: string;
70
+ updated: string;
71
+ tags?: string[];
72
+ related?: string[];
73
+ description?: string;
74
+ }
75
+
76
+ /**
77
+ * A parsed knowledge note with frontmatter and content
78
+ */
79
+ export interface KnowledgeNote {
80
+ /** Unique identifier from frontmatter */
81
+ id: string;
82
+ /** Human-readable title */
83
+ title: string;
84
+ /** Creation date (YYYY-MM-DD) */
85
+ created: string;
86
+ /** Last update date (YYYY-MM-DD) */
87
+ updated: string;
88
+ /** Categorization tags */
89
+ tags: string[];
90
+ /** Related note IDs for cross-referencing */
91
+ related: string[];
92
+ /** Optional description */
93
+ description?: string;
94
+ /** Markdown content (without frontmatter) */
95
+ content: string;
96
+ /** Relative path from knowledge folder */
97
+ path: string;
98
+ /** Category derived from folder */
99
+ category: KnowledgeCategory;
100
+ }
101
+
102
+ /**
103
+ * Input for creating a new knowledge note
104
+ */
105
+ export interface CreateNoteInput {
106
+ /** Unique identifier (kebab-case) */
107
+ id: string;
108
+ /** Human-readable title */
109
+ title: string;
110
+ /** Category folder to place the note in */
111
+ category: Exclude<KnowledgeCategory, "root">;
112
+ /** Markdown content (without frontmatter) */
113
+ content: string;
114
+ /** Optional tags */
115
+ tags?: string[];
116
+ /** Optional related note IDs */
117
+ related?: string[];
118
+ /** Optional description */
119
+ description?: string;
120
+ }
121
+
122
+ /**
123
+ * Input for updating an existing knowledge note
124
+ */
125
+ export interface UpdateNoteInput {
126
+ /** Note ID to update */
127
+ id: string;
128
+ /** New title (optional) */
129
+ title?: string;
130
+ /** New content (optional) */
131
+ content?: string;
132
+ /** New tags (optional, replaces existing) */
133
+ tags?: string[];
134
+ /** New related IDs (optional, replaces existing) */
135
+ related?: string[];
136
+ /** New description (optional) */
137
+ description?: string;
138
+ }
139
+
140
+ /**
141
+ * Search options for knowledge notes
142
+ */
143
+ export interface SearchOptions {
144
+ /** Text to search for in content and title */
145
+ query?: string;
146
+ /** Filter by tags (matches any) */
147
+ tags?: string[];
148
+ /** Filter by category */
149
+ category?: KnowledgeCategory;
150
+ }
151
+
152
+ /**
153
+ * List options for knowledge notes
154
+ */
155
+ export interface ListOptions {
156
+ /** Filter by category */
157
+ category?: KnowledgeCategory;
158
+ /** Filter by tags (matches any) */
159
+ tags?: string[];
160
+ }
161
+
162
+ /**
163
+ * Result from operations that modify notes
164
+ */
165
+ export interface OperationResult {
166
+ success: boolean;
167
+ message: string;
168
+ note?: KnowledgeNote;
169
+ }
170
+
171
+ // =============================================================================
172
+ // ADR (Architecture Decision Record) Types
173
+ // =============================================================================
174
+
175
+ /**
176
+ * Valid ADR status values
177
+ */
178
+ export type AdrStatus =
179
+ | "todo"
180
+ | "in-progress"
181
+ | "in-review"
182
+ | "blocked"
183
+ | "completed"
184
+ | "deprecated";
185
+
186
+ /**
187
+ * Valid ADR type values
188
+ */
189
+ export type AdrType = "enhancement" | "debug" | "research";
190
+
191
+ /**
192
+ * YAML frontmatter fields for ADR records
193
+ */
194
+ export interface AdrFrontmatter {
195
+ title: string;
196
+ description?: string;
197
+ date_created: string;
198
+ date_updated: string;
199
+ status: AdrStatus;
200
+ type: AdrType;
201
+ tags?: string[];
202
+ working_folder: string;
203
+ }
204
+
205
+ /**
206
+ * A parsed ADR record with frontmatter and content
207
+ */
208
+ export interface AdrRecord {
209
+ /** Filename without .md extension */
210
+ name: string;
211
+ /** Human-readable title */
212
+ title: string;
213
+ /** Brief description */
214
+ description?: string;
215
+ /** Creation date (YYYY-MM-DD) */
216
+ dateCreated: string;
217
+ /** Last update date (YYYY-MM-DD) */
218
+ dateUpdated: string;
219
+ /** Current status */
220
+ status: AdrStatus;
221
+ /** ADR type */
222
+ type: AdrType;
223
+ /** Categorization tags */
224
+ tags: string[];
225
+ /** Working folder path */
226
+ workingFolder: string;
227
+ /** Markdown content (without frontmatter) */
228
+ content: string;
229
+ /** Full path to the ADR file */
230
+ path: string;
231
+ }
232
+
233
+ /**
234
+ * Input for creating a new ADR
235
+ */
236
+ export interface CreateAdrInput {
237
+ /** Human-readable title */
238
+ title: string;
239
+ /** Brief description */
240
+ description?: string;
241
+ /** ADR type */
242
+ type: AdrType;
243
+ /** Working folder path */
244
+ workingFolder: string;
245
+ /** Optional tags */
246
+ tags?: string[];
247
+ /** Initial status (defaults to "todo") */
248
+ status?: AdrStatus;
249
+ /** Optional initial content (without frontmatter) */
250
+ content?: string;
251
+ }
252
+
253
+ /**
254
+ * Input for updating an existing ADR
255
+ */
256
+ export interface UpdateAdrInput {
257
+ /** ADR name (filename without .md) to update */
258
+ name: string;
259
+ /** New title (optional) */
260
+ title?: string;
261
+ /** New description (optional) */
262
+ description?: string;
263
+ /** New status (optional) */
264
+ status?: AdrStatus;
265
+ /** New type (optional) */
266
+ type?: AdrType;
267
+ /** New tags (optional, replaces existing) */
268
+ tags?: string[];
269
+ /** New content (optional, replaces existing) */
270
+ content?: string;
271
+ /** New working folder (optional) */
272
+ workingFolder?: string;
273
+ }
274
+
275
+ /**
276
+ * Search options for ADR records
277
+ */
278
+ export interface AdrSearchOptions {
279
+ /** Text to search for in title, description, and content */
280
+ query?: string;
281
+ /** Filter by status */
282
+ status?: AdrStatus;
283
+ /** Filter by type */
284
+ type?: AdrType;
285
+ /** Filter by tags (matches any) */
286
+ tags?: string[];
287
+ }
288
+
289
+ /**
290
+ * List options for ADR records
291
+ */
292
+ export interface AdrListOptions {
293
+ /** Filter by status */
294
+ status?: AdrStatus;
295
+ /** Filter by type */
296
+ type?: AdrType;
297
+ /** Filter by tags (matches any) */
298
+ tags?: string[];
299
+ }
300
+
301
+ /**
302
+ * Result from operations that modify ADRs
303
+ */
304
+ export interface AdrOperationResult {
305
+ success: boolean;
306
+ message: string;
307
+ adr?: AdrRecord;
308
+ }
309
+
310
+ // =============================================================================
311
+ // MCP Session Types
312
+ // =============================================================================
313
+
314
+ /**
315
+ * Session state for MCP server
316
+ */
317
+ export interface McpSession {
318
+ /** Currently active workspace name */
319
+ activeWorkspace: string;
320
+ /** Timestamp of session start */
321
+ startedAt: Date;
322
+ }
323
+
324
+ /**
325
+ * Result from workspace operations
326
+ */
327
+ export interface WorkspaceResult {
328
+ success: boolean;
329
+ message: string;
330
+ workspace?: Workspace;
331
+ }
332
+
333
+ /**
334
+ * Workspace info with runtime state
335
+ */
336
+ export interface WorkspaceInfo extends Workspace {
337
+ /** Whether this is the default workspace */
338
+ isDefault: boolean;
339
+ /** Whether this is the currently active workspace */
340
+ isActive: boolean;
341
+ /** Whether the path exists on disk */
342
+ exists: boolean;
343
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,26 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "ESNext",
5
+ "moduleResolution": "bundler",
6
+ "strict": true,
7
+ "skipLibCheck": true,
8
+ "esModuleInterop": true,
9
+ "allowSyntheticDefaultImports": true,
10
+ "forceConsistentCasingInFileNames": true,
11
+ "resolveJsonModule": true,
12
+ "declaration": true,
13
+ "declarationMap": true,
14
+ "noEmit": true,
15
+ "allowImportingTsExtensions": true,
16
+ "outDir": "./dist",
17
+ "rootDir": "./src",
18
+ "baseUrl": ".",
19
+ "paths": {
20
+ "@/*": ["./src/*"]
21
+ },
22
+ "types": ["bun-types"]
23
+ },
24
+ "include": ["src/**/*"],
25
+ "exclude": ["node_modules", "dist"]
26
+ }