gru-ai 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 (143) hide show
  1. package/.claude/skills/brainstorm/SKILL.md +340 -0
  2. package/.claude/skills/code-review-excellence/SKILL.md +198 -0
  3. package/.claude/skills/directive/SKILL.md +121 -0
  4. package/.claude/skills/directive/docs/pipeline/00-delegation-and-triage.md +181 -0
  5. package/.claude/skills/directive/docs/pipeline/01-checkpoint.md +34 -0
  6. package/.claude/skills/directive/docs/pipeline/02-read-directive.md +38 -0
  7. package/.claude/skills/directive/docs/pipeline/03-read-context.md +15 -0
  8. package/.claude/skills/directive/docs/pipeline/04-challenge.md +38 -0
  9. package/.claude/skills/directive/docs/pipeline/05-planning.md +64 -0
  10. package/.claude/skills/directive/docs/pipeline/06-technical-audit.md +88 -0
  11. package/.claude/skills/directive/docs/pipeline/07-plan-approval.md +145 -0
  12. package/.claude/skills/directive/docs/pipeline/07b-project-brainstorm.md +85 -0
  13. package/.claude/skills/directive/docs/pipeline/08-worktree-and-state.md +50 -0
  14. package/.claude/skills/directive/docs/pipeline/09-execute-projects.md +709 -0
  15. package/.claude/skills/directive/docs/pipeline/10-wrapup.md +242 -0
  16. package/.claude/skills/directive/docs/pipeline/11-completion-gate.md +75 -0
  17. package/.claude/skills/directive/docs/reference/rules/casting-rules.md +78 -0
  18. package/.claude/skills/directive/docs/reference/rules/failure-handling.md +20 -0
  19. package/.claude/skills/directive/docs/reference/rules/phase-definitions.md +42 -0
  20. package/.claude/skills/directive/docs/reference/rules/scope-and-dod.md +30 -0
  21. package/.claude/skills/directive/docs/reference/schemas/audit-output.md +44 -0
  22. package/.claude/skills/directive/docs/reference/schemas/brainstorm-output.md +52 -0
  23. package/.claude/skills/directive/docs/reference/schemas/challenger-output.md +13 -0
  24. package/.claude/skills/directive/docs/reference/schemas/checkpoint.md +18 -0
  25. package/.claude/skills/directive/docs/reference/schemas/current-json.md +5 -0
  26. package/.claude/skills/directive/docs/reference/schemas/directive-json.md +143 -0
  27. package/.claude/skills/directive/docs/reference/schemas/investigation-output.md +37 -0
  28. package/.claude/skills/directive/docs/reference/schemas/plan-schema.md +103 -0
  29. package/.claude/skills/directive/docs/reference/templates/architect-prompt.md +66 -0
  30. package/.claude/skills/directive/docs/reference/templates/auditor-prompt.md +53 -0
  31. package/.claude/skills/directive/docs/reference/templates/brainstorm-prompt.md +68 -0
  32. package/.claude/skills/directive/docs/reference/templates/challenger-prompt.md +35 -0
  33. package/.claude/skills/directive/docs/reference/templates/digest.md +134 -0
  34. package/.claude/skills/directive/docs/reference/templates/investigator-prompt.md +51 -0
  35. package/.claude/skills/directive/docs/reference/templates/planner-prompt.md +130 -0
  36. package/.claude/skills/frontend-design/SKILL.md +42 -0
  37. package/.claude/skills/gruai-agents/SKILL.md +161 -0
  38. package/.claude/skills/gruai-config/SKILL.md +61 -0
  39. package/.claude/skills/healthcheck/SKILL.md +216 -0
  40. package/.claude/skills/report/SKILL.md +380 -0
  41. package/.claude/skills/scout/SKILL.md +452 -0
  42. package/.claude/skills/seo-audit/SKILL.md +107 -0
  43. package/.claude/skills/walkthrough/SKILL.md +274 -0
  44. package/.claude/skills/webapp-testing/SKILL.md +96 -0
  45. package/LICENSE +21 -0
  46. package/README.md +206 -0
  47. package/cli/templates/CLAUDE.md.template +57 -0
  48. package/cli/templates/agent-roles/backend.md +47 -0
  49. package/cli/templates/agent-roles/cmo.md +52 -0
  50. package/cli/templates/agent-roles/content.md +48 -0
  51. package/cli/templates/agent-roles/coo.md +66 -0
  52. package/cli/templates/agent-roles/cpo.md +52 -0
  53. package/cli/templates/agent-roles/cto.md +63 -0
  54. package/cli/templates/agent-roles/data.md +46 -0
  55. package/cli/templates/agent-roles/design.md +46 -0
  56. package/cli/templates/agent-roles/frontend.md +47 -0
  57. package/cli/templates/agent-roles/fullstack.md +47 -0
  58. package/cli/templates/agent-roles/qa.md +46 -0
  59. package/cli/templates/backlog.json.template +3 -0
  60. package/cli/templates/directive.json.template +9 -0
  61. package/cli/templates/directive.md.template +23 -0
  62. package/cli/templates/goals-index.md +21 -0
  63. package/cli/templates/gruai.config.json.template +12 -0
  64. package/cli/templates/lessons.md +16 -0
  65. package/cli/templates/vision.md +35 -0
  66. package/cli/templates/welcome-directive/directive.json +9 -0
  67. package/cli/templates/welcome-directive/directive.md +53 -0
  68. package/dist/assets/GamePage-C5XQQOQH.js +49 -0
  69. package/dist/assets/README.md +17 -0
  70. package/dist/assets/characters/char_0.png +0 -0
  71. package/dist/assets/characters/char_1.png +0 -0
  72. package/dist/assets/characters/char_10.png +0 -0
  73. package/dist/assets/characters/char_11.png +0 -0
  74. package/dist/assets/characters/char_2.png +0 -0
  75. package/dist/assets/characters/char_3.png +0 -0
  76. package/dist/assets/characters/char_4.png +0 -0
  77. package/dist/assets/characters/char_5.png +0 -0
  78. package/dist/assets/characters/char_6.png +0 -0
  79. package/dist/assets/characters/char_7.png +0 -0
  80. package/dist/assets/characters/char_8.png +0 -0
  81. package/dist/assets/characters/char_9.png +0 -0
  82. package/dist/assets/index-CnTPDqpP.js +12 -0
  83. package/dist/assets/index-gR5q7ikB.css +1 -0
  84. package/dist/assets/office/furniture.png +0 -0
  85. package/dist/assets/office/room-builder.png +0 -0
  86. package/dist/index.html +16 -0
  87. package/dist-server/scripts/intelligence-trends.d.ts +100 -0
  88. package/dist-server/scripts/intelligence-trends.js +365 -0
  89. package/dist-server/server/actions/cleanup.d.ts +4 -0
  90. package/dist-server/server/actions/cleanup.js +30 -0
  91. package/dist-server/server/actions/send-input.d.ts +6 -0
  92. package/dist-server/server/actions/send-input.js +147 -0
  93. package/dist-server/server/actions/terminal.d.ts +4 -0
  94. package/dist-server/server/actions/terminal.js +427 -0
  95. package/dist-server/server/config.d.ts +9 -0
  96. package/dist-server/server/config.js +217 -0
  97. package/dist-server/server/db.d.ts +7 -0
  98. package/dist-server/server/db.js +79 -0
  99. package/dist-server/server/hooks/event-receiver.d.ts +11 -0
  100. package/dist-server/server/hooks/event-receiver.js +36 -0
  101. package/dist-server/server/index.d.ts +1 -0
  102. package/dist-server/server/index.js +552 -0
  103. package/dist-server/server/notifications/macos.d.ts +5 -0
  104. package/dist-server/server/notifications/macos.js +22 -0
  105. package/dist-server/server/notifications/notifier.d.ts +17 -0
  106. package/dist-server/server/notifications/notifier.js +110 -0
  107. package/dist-server/server/parsers/process-discovery.d.ts +39 -0
  108. package/dist-server/server/parsers/process-discovery.js +776 -0
  109. package/dist-server/server/parsers/session-scanner.d.ts +56 -0
  110. package/dist-server/server/parsers/session-scanner.js +390 -0
  111. package/dist-server/server/parsers/session-state.d.ts +68 -0
  112. package/dist-server/server/parsers/session-state.js +696 -0
  113. package/dist-server/server/parsers/session-state.test.d.ts +1 -0
  114. package/dist-server/server/parsers/session-state.test.js +950 -0
  115. package/dist-server/server/parsers/task-parser.d.ts +10 -0
  116. package/dist-server/server/parsers/task-parser.js +97 -0
  117. package/dist-server/server/parsers/team-parser.d.ts +3 -0
  118. package/dist-server/server/parsers/team-parser.js +67 -0
  119. package/dist-server/server/platform/__tests__/claude-code.test.d.ts +1 -0
  120. package/dist-server/server/platform/__tests__/claude-code.test.js +311 -0
  121. package/dist-server/server/platform/claude-code.d.ts +34 -0
  122. package/dist-server/server/platform/claude-code.js +94 -0
  123. package/dist-server/server/platform/index.d.ts +5 -0
  124. package/dist-server/server/platform/index.js +1 -0
  125. package/dist-server/server/platform/types.d.ts +190 -0
  126. package/dist-server/server/platform/types.js +9 -0
  127. package/dist-server/server/state/aggregator.d.ts +42 -0
  128. package/dist-server/server/state/aggregator.js +1080 -0
  129. package/dist-server/server/state/work-item-types.d.ts +555 -0
  130. package/dist-server/server/state/work-item-types.js +168 -0
  131. package/dist-server/server/types.d.ts +237 -0
  132. package/dist-server/server/types.js +1 -0
  133. package/dist-server/server/watchers/claude-watcher.d.ts +17 -0
  134. package/dist-server/server/watchers/claude-watcher.js +130 -0
  135. package/dist-server/server/watchers/context-watcher.d.ts +22 -0
  136. package/dist-server/server/watchers/context-watcher.js +125 -0
  137. package/dist-server/server/watchers/directive-watcher.d.ts +46 -0
  138. package/dist-server/server/watchers/directive-watcher.js +497 -0
  139. package/dist-server/server/watchers/session-watcher.d.ts +18 -0
  140. package/dist-server/server/watchers/session-watcher.js +126 -0
  141. package/dist-server/server/watchers/state-watcher.d.ts +36 -0
  142. package/dist-server/server/watchers/state-watcher.js +369 -0
  143. package/package.json +68 -0
@@ -0,0 +1,36 @@
1
+ import type { Aggregator } from '../state/aggregator.js';
2
+ import type { ConductorConfig } from '../types.js';
3
+ import type { FullWorkState } from '../state/work-item-types.js';
4
+ /**
5
+ * Watches .context/ source files (directive.json, project.json,
6
+ * reports, lessons) and builds FullWorkState directly.
7
+ *
8
+ * Replaces the old two-stage pipeline (ContextWatcher -> indexer -> state/*.json -> StateWatcher).
9
+ * Now reads source files directly via glob patterns.
10
+ */
11
+ export declare class StateWatcher {
12
+ private watchers;
13
+ private aggregator;
14
+ private config;
15
+ private debounceTimer;
16
+ private _ready;
17
+ private _state;
18
+ constructor(aggregator: Aggregator, config: ConductorConfig);
19
+ start(): void;
20
+ get ready(): boolean;
21
+ stop(): Promise<void>;
22
+ readCurrentState(): FullWorkState;
23
+ /** Force a re-read of all source files */
24
+ refresh(): void;
25
+ private handleChange;
26
+ private readAndUpdate;
27
+ private mapProjectStatus;
28
+ private mapDirectiveStatus;
29
+ private mapBacklogStatus;
30
+ private mapPriority;
31
+ private readJson;
32
+ private listDirs;
33
+ private listFiles;
34
+ private extractFirstHeading;
35
+ private fileMtime;
36
+ }
@@ -0,0 +1,369 @@
1
+ import path from 'node:path';
2
+ import fs from 'node:fs';
3
+ import { watch } from 'chokidar';
4
+ /**
5
+ * Watches .context/ source files (directive.json, project.json,
6
+ * reports, lessons) and builds FullWorkState directly.
7
+ *
8
+ * Replaces the old two-stage pipeline (ContextWatcher -> indexer -> state/*.json -> StateWatcher).
9
+ * Now reads source files directly via glob patterns.
10
+ */
11
+ export class StateWatcher {
12
+ watchers = [];
13
+ aggregator;
14
+ config;
15
+ debounceTimer = null;
16
+ _ready = false;
17
+ _state = {
18
+ features: null,
19
+ backlogs: null,
20
+ conductor: null,
21
+ index: null,
22
+ };
23
+ constructor(aggregator, config) {
24
+ this.aggregator = aggregator;
25
+ this.config = config;
26
+ }
27
+ start() {
28
+ if (this.config.projects.length === 0) {
29
+ console.log('[state-watcher] No projects configured, skipping');
30
+ this._ready = true;
31
+ return;
32
+ }
33
+ // Read initial state
34
+ this.readAndUpdate();
35
+ for (const project of this.config.projects) {
36
+ const contextDir = path.join(project.path, '.context');
37
+ if (!fs.existsSync(contextDir)) {
38
+ console.log(`[state-watcher] No .context/ dir for ${project.name}, skipping`);
39
+ continue;
40
+ }
41
+ console.log(`[state-watcher] Watching ${contextDir} (${project.name})`);
42
+ const watcher = watch(contextDir, {
43
+ ignoreInitial: true,
44
+ persistent: true,
45
+ ignored: [
46
+ '**/node_modules/**',
47
+ // Ignore checkpoints dir to avoid feedback loops
48
+ path.join(contextDir, 'directives', 'checkpoints', '**'),
49
+ ],
50
+ awaitWriteFinish: {
51
+ stabilityThreshold: 300,
52
+ pollInterval: 50,
53
+ },
54
+ depth: 5,
55
+ });
56
+ watcher.on('all', (_event, filePath) => {
57
+ if (!filePath.endsWith('.json') && !filePath.endsWith('.md'))
58
+ return;
59
+ this.handleChange();
60
+ });
61
+ watcher.on('ready', () => {
62
+ console.log(`[state-watcher] Ready for ${project.name}`);
63
+ });
64
+ watcher.on('error', (err) => {
65
+ console.error(`[state-watcher] Error for ${project.name}:`, err);
66
+ });
67
+ this.watchers.push(watcher);
68
+ }
69
+ this._ready = true;
70
+ }
71
+ get ready() {
72
+ return this._ready;
73
+ }
74
+ async stop() {
75
+ if (this.debounceTimer) {
76
+ clearTimeout(this.debounceTimer);
77
+ this.debounceTimer = null;
78
+ }
79
+ for (const watcher of this.watchers) {
80
+ await watcher.close();
81
+ }
82
+ this.watchers = [];
83
+ }
84
+ readCurrentState() {
85
+ return this._state;
86
+ }
87
+ /** Force a re-read of all source files */
88
+ refresh() {
89
+ this.readAndUpdate();
90
+ }
91
+ handleChange() {
92
+ if (this.debounceTimer) {
93
+ clearTimeout(this.debounceTimer);
94
+ }
95
+ this.debounceTimer = setTimeout(() => {
96
+ this.debounceTimer = null;
97
+ this.readAndUpdate();
98
+ }, 500);
99
+ }
100
+ readAndUpdate() {
101
+ const state = {
102
+ features: null,
103
+ backlogs: null,
104
+ conductor: null,
105
+ index: null,
106
+ };
107
+ const generated = new Date().toISOString();
108
+ const allProjects = [];
109
+ const allBacklog = [];
110
+ const allDirectives = [];
111
+ const allReports = [];
112
+ const allLessons = [];
113
+ for (const project of this.config.projects) {
114
+ const contextDir = path.join(project.path, '.context');
115
+ if (!fs.existsSync(contextDir))
116
+ continue;
117
+ // Derive repoId from the project path (last directory component, lowercased)
118
+ const repoId = path.basename(project.path).toLowerCase();
119
+ const repoName = project.name;
120
+ // --- Directives (directory format: {id}/directive.json) ---
121
+ const directivesDir = path.join(contextDir, 'directives');
122
+ if (fs.existsSync(directivesDir)) {
123
+ const directiveDirs = this.listDirs(directivesDir);
124
+ for (const dirId of directiveDirs) {
125
+ const filePath = path.join(directivesDir, dirId, 'directive.json');
126
+ const dirJson = this.readJson(filePath);
127
+ if (!dirJson)
128
+ continue;
129
+ const dirStatus = this.mapDirectiveStatus(String(dirJson.status ?? 'pending'));
130
+ const category = dirJson.category ? String(dirJson.category) : undefined;
131
+ allDirectives.push({
132
+ id: dirId,
133
+ type: 'directive',
134
+ title: String(dirJson.title ?? dirId),
135
+ status: dirStatus,
136
+ createdAt: String(dirJson.created ?? generated),
137
+ updatedAt: String(dirJson.updated ?? dirJson.created ?? generated),
138
+ projects: [],
139
+ weight: dirJson.weight ? String(dirJson.weight) : undefined,
140
+ category,
141
+ producedFeatures: Array.isArray(dirJson.produced_features) ? dirJson.produced_features.map(String) : undefined,
142
+ report: dirJson.report != null ? String(dirJson.report) : null,
143
+ backlogSources: Array.isArray(dirJson.backlog_sources) ? dirJson.backlog_sources.map(String) : undefined,
144
+ });
145
+ // --- Projects under this directive ---
146
+ const projectsDir = path.join(directivesDir, dirId, 'projects');
147
+ if (fs.existsSync(projectsDir)) {
148
+ const projectIds = this.listDirs(projectsDir);
149
+ for (const projId of projectIds) {
150
+ const projJsonPath = path.join(projectsDir, projId, 'project.json');
151
+ const projJson = this.readJson(projJsonPath);
152
+ if (!projJson)
153
+ continue;
154
+ const tasks = Array.isArray(projJson.tasks) ? projJson.tasks : [];
155
+ const taskCount = tasks.length;
156
+ const completedTaskCount = tasks.filter((t) => t.status === 'completed' || t.status === 'done').length;
157
+ const projStatus = this.mapProjectStatus(String(projJson.status ?? 'pending'));
158
+ const featureId = `${dirId}/${projId}`;
159
+ const record = {
160
+ id: featureId,
161
+ type: 'feature',
162
+ title: String(projJson.title ?? projId),
163
+ status: projStatus,
164
+ category,
165
+ createdAt: String(projJson.created ?? generated),
166
+ updatedAt: String(projJson.updated ?? generated),
167
+ taskCount,
168
+ completedTaskCount,
169
+ hasSpec: false,
170
+ hasDesign: false,
171
+ specSummary: projJson.description
172
+ ? String(projJson.description).slice(0, 200)
173
+ : undefined,
174
+ repoId,
175
+ repoName,
176
+ };
177
+ allProjects.push(record);
178
+ }
179
+ }
180
+ }
181
+ }
182
+ // --- Reports ---
183
+ const reportsDir = path.join(contextDir, 'reports');
184
+ if (fs.existsSync(reportsDir)) {
185
+ const mdFiles = this.listFiles(reportsDir, '.md');
186
+ for (const file of mdFiles) {
187
+ const filePath = path.join(reportsDir, file);
188
+ const title = this.extractFirstHeading(filePath) || file.replace('.md', '');
189
+ const mtime = this.fileMtime(filePath);
190
+ const directiveMatch = file.match(/^(.+?)(?:-v\d+)?-\d{4}-\d{2}-\d{2}\.md$/);
191
+ const sourceDirective = directiveMatch ? directiveMatch[1] : undefined;
192
+ allReports.push({
193
+ id: `report/${file.replace('.md', '')}`,
194
+ type: 'report',
195
+ title,
196
+ status: 'done',
197
+ createdAt: mtime,
198
+ updatedAt: mtime,
199
+ filePath: `reports/${file}`,
200
+ sourceDirective,
201
+ });
202
+ }
203
+ }
204
+ // --- Lessons ---
205
+ const lessonsDir = path.join(contextDir, 'lessons');
206
+ if (fs.existsSync(lessonsDir)) {
207
+ const mdFiles = this.listFiles(lessonsDir, '.md');
208
+ for (const file of mdFiles) {
209
+ const filePath = path.join(lessonsDir, file);
210
+ const title = this.extractFirstHeading(filePath) || file.replace('.md', '');
211
+ const lessonId = file.replace('.md', '');
212
+ allLessons.push({
213
+ id: lessonId,
214
+ title,
215
+ filePath: `lessons/${file}`,
216
+ topics: [lessonId],
217
+ updatedAt: this.fileMtime(filePath),
218
+ });
219
+ }
220
+ }
221
+ }
222
+ // Build state objects
223
+ state.features = { generated, features: allProjects };
224
+ state.backlogs = { generated, items: allBacklog };
225
+ state.conductor = {
226
+ generated,
227
+ directives: allDirectives,
228
+ reports: allReports,
229
+ discussions: [],
230
+ research: [],
231
+ lessons: allLessons,
232
+ };
233
+ state.index = {
234
+ generated,
235
+ counts: {
236
+ activeFeatures: allProjects.filter((f) => f.status !== 'completed').length,
237
+ doneFeatures: allProjects.filter((f) => f.status === 'completed').length,
238
+ pendingTasks: 0, // TODO: aggregate from project tasks
239
+ completedTasks: 0,
240
+ backlogItems: allBacklog.filter((b) => b.status !== 'completed').length,
241
+ directives: allDirectives.length,
242
+ reports: allReports.length,
243
+ discussions: 0,
244
+ lessons: allLessons.length,
245
+ },
246
+ };
247
+ this._state = state;
248
+ const projectCount = allProjects.length;
249
+ const backlogCount = allBacklog.length;
250
+ const directiveCount = allDirectives.length;
251
+ console.log(`[state-watcher] Direct read: ${projectCount} projects, ${backlogCount} backlog items, ${directiveCount} directives`);
252
+ this.aggregator.updateWorkState(state);
253
+ }
254
+ // --- Status Mappers ---
255
+ mapProjectStatus(status) {
256
+ switch (status) {
257
+ case 'in_progress': return 'in_progress';
258
+ case 'active': return 'in_progress'; // legacy
259
+ case 'pending': return 'pending';
260
+ case 'proposed': return 'pending';
261
+ case 'completed': return 'completed';
262
+ case 'blocked': return 'blocked';
263
+ case 'deferred': return 'deferred';
264
+ case 'abandoned': return 'abandoned';
265
+ default: return 'pending';
266
+ }
267
+ }
268
+ mapDirectiveStatus(status) {
269
+ switch (status) {
270
+ case 'pending': return 'pending';
271
+ case 'triaged': return 'pending';
272
+ case 'in_progress': return 'in_progress';
273
+ case 'executing': return 'in_progress'; // legacy
274
+ case 'awaiting_completion': return 'in_progress';
275
+ case 'completed': return 'completed';
276
+ case 'cancelled': return 'abandoned';
277
+ case 'rejected': return 'abandoned';
278
+ default: return 'pending';
279
+ }
280
+ }
281
+ mapBacklogStatus(status) {
282
+ switch (status) {
283
+ case 'pending': return 'pending';
284
+ case 'proposed': return 'pending';
285
+ case 'approved': return 'pending';
286
+ case 'promoted': return 'completed';
287
+ case 'in_progress': return 'in_progress';
288
+ case 'completed': return 'completed';
289
+ case 'deferred': return 'deferred';
290
+ case 'blocked': return 'blocked';
291
+ case 'rejected': return 'abandoned';
292
+ default: return 'pending';
293
+ }
294
+ }
295
+ mapPriority(p) {
296
+ const s = String(p ?? '').toUpperCase();
297
+ if (s === 'P0')
298
+ return 'P0';
299
+ if (s === 'P1')
300
+ return 'P1';
301
+ if (s === 'P2')
302
+ return 'P2';
303
+ return undefined;
304
+ }
305
+ // --- File Helpers ---
306
+ readJson(filePath) {
307
+ try {
308
+ const raw = fs.readFileSync(filePath, 'utf-8');
309
+ return JSON.parse(raw);
310
+ }
311
+ catch {
312
+ return null;
313
+ }
314
+ }
315
+ listDirs(dirPath) {
316
+ try {
317
+ return fs.readdirSync(dirPath).filter((name) => {
318
+ if (name.startsWith('.') || name.startsWith('_'))
319
+ return false;
320
+ try {
321
+ return fs.statSync(path.join(dirPath, name)).isDirectory();
322
+ }
323
+ catch {
324
+ return false;
325
+ }
326
+ });
327
+ }
328
+ catch {
329
+ return [];
330
+ }
331
+ }
332
+ listFiles(dirPath, ext) {
333
+ try {
334
+ return fs.readdirSync(dirPath).filter((name) => {
335
+ if (name.startsWith('.'))
336
+ return false;
337
+ if (ext && !name.endsWith(ext))
338
+ return false;
339
+ try {
340
+ return fs.statSync(path.join(dirPath, name)).isFile();
341
+ }
342
+ catch {
343
+ return false;
344
+ }
345
+ });
346
+ }
347
+ catch {
348
+ return [];
349
+ }
350
+ }
351
+ extractFirstHeading(filePath) {
352
+ try {
353
+ const content = fs.readFileSync(filePath, 'utf-8');
354
+ const match = content.match(/^#\s+(.+)/m);
355
+ return match ? match[1].trim() : '';
356
+ }
357
+ catch {
358
+ return '';
359
+ }
360
+ }
361
+ fileMtime(filePath) {
362
+ try {
363
+ return fs.statSync(filePath).mtime.toISOString().split('T')[0];
364
+ }
365
+ catch {
366
+ return new Date().toISOString().split('T')[0];
367
+ }
368
+ }
369
+ }
package/package.json ADDED
@@ -0,0 +1,68 @@
1
+ {
2
+ "name": "gru-ai",
3
+ "private": false,
4
+ "version": "0.1.0",
5
+ "type": "module",
6
+ "files": [
7
+ "dist/",
8
+ "dist-server/",
9
+ "cli/templates/",
10
+ ".claude/skills/",
11
+ "LICENSE",
12
+ "README.md"
13
+ ],
14
+ "scripts": {
15
+ "dev": "concurrently \"tsx watch server/index.ts\" \"vite\"",
16
+ "dev:server": "tsx watch server/index.ts",
17
+ "dev:client": "vite",
18
+ "build:client": "vite build",
19
+ "build:server": "tsc -p server/tsconfig.json",
20
+ "build": "npm run build:client && npm run build:server",
21
+ "prepublishOnly": "npm run build",
22
+ "start": "node dist-server/server/index.js",
23
+ "lint": "eslint .",
24
+ "type-check": "tsc --noEmit",
25
+ "preview": "vite preview"
26
+ },
27
+ "dependencies": {
28
+ "@modelcontextprotocol/sdk": "^1.27.1",
29
+ "@radix-ui/react-dialog": "^1.1.14",
30
+ "@radix-ui/react-scroll-area": "^1.2.10",
31
+ "@radix-ui/react-slot": "^1.2.4",
32
+ "@tanstack/react-virtual": "^3.13.18",
33
+ "better-sqlite3": "^12.6.2",
34
+ "chokidar": "^5.0.0",
35
+ "class-variance-authority": "^0.7.1",
36
+ "clsx": "^2.1.1",
37
+ "cmdk": "^1.1.1",
38
+ "concurrently": "^9.2.1",
39
+ "lucide-react": "^0.575.0",
40
+ "radix-ui": "^1.4.3",
41
+ "react": "^19.2.0",
42
+ "react-dom": "^19.2.0",
43
+ "react-router-dom": "^7.13.0",
44
+ "tailwind-merge": "^3.5.0",
45
+ "ws": "^8.19.0",
46
+ "zustand": "^5.0.11"
47
+ },
48
+ "devDependencies": {
49
+ "@eslint/js": "^9.39.1",
50
+ "@tailwindcss/vite": "^4.2.0",
51
+ "@types/better-sqlite3": "^7.6.13",
52
+ "@types/node": "^24.10.1",
53
+ "@types/react": "^19.2.7",
54
+ "@types/react-dom": "^19.2.3",
55
+ "@types/ws": "^8.18.1",
56
+ "@vitejs/plugin-react": "^5.1.1",
57
+ "eslint": "^9.39.1",
58
+ "eslint-plugin-react-hooks": "^7.0.1",
59
+ "eslint-plugin-react-refresh": "^0.4.24",
60
+ "globals": "^16.5.0",
61
+ "pngjs": "^7.0.0",
62
+ "tailwindcss": "^4.2.0",
63
+ "tsx": "^4.21.0",
64
+ "typescript": "~5.9.3",
65
+ "typescript-eslint": "^8.48.0",
66
+ "vite": "^7.3.1"
67
+ }
68
+ }