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,168 @@
1
+ /**
2
+ * Structured work item types for the conductor state system.
3
+ *
4
+ * These types replace free-form markdown as the queryable source of truth.
5
+ * The indexer (scripts/index-state.ts) reads .context/ and produces JSON
6
+ * arrays of these records. The dashboard watches and serves them.
7
+ */
8
+ import { z } from 'zod';
9
+ // ---------------------------------------------------------------------------
10
+ // Enums
11
+ // ---------------------------------------------------------------------------
12
+ export const WorkItemType = z.enum([
13
+ 'feature',
14
+ 'task',
15
+ 'backlog-item',
16
+ 'directive',
17
+ 'report',
18
+ 'discussion',
19
+ 'research',
20
+ ]);
21
+ export const LifecycleState = z.enum([
22
+ 'pending',
23
+ 'in_progress',
24
+ 'blocked',
25
+ 'deferred',
26
+ 'completed',
27
+ 'abandoned',
28
+ ]);
29
+ export const Priority = z.enum(['P0', 'P1', 'P2']);
30
+ // ---------------------------------------------------------------------------
31
+ // Base schema
32
+ // ---------------------------------------------------------------------------
33
+ export const BaseWorkItem = z.object({
34
+ id: z.string(),
35
+ type: WorkItemType,
36
+ title: z.string(),
37
+ status: LifecycleState,
38
+ parentId: z.string().optional(),
39
+ category: z.string().optional(),
40
+ createdAt: z.string(), // ISO date
41
+ updatedAt: z.string(), // ISO date
42
+ tags: z.array(z.string()).optional(),
43
+ });
44
+ // ---------------------------------------------------------------------------
45
+ // Extended types (discriminated on `type`)
46
+ // ---------------------------------------------------------------------------
47
+ export const FeatureRecord = BaseWorkItem.extend({
48
+ type: z.literal('feature'),
49
+ category: z.string().optional(),
50
+ taskCount: z.number(),
51
+ completedTaskCount: z.number(),
52
+ hasSpec: z.boolean(),
53
+ hasDesign: z.boolean(),
54
+ specSummary: z.string().optional(),
55
+ repoId: z.string().optional(),
56
+ repoName: z.string().optional(),
57
+ });
58
+ export const TaskRecord = BaseWorkItem.extend({
59
+ type: z.literal('task'),
60
+ featureId: z.string(),
61
+ deps: z.array(z.string()),
62
+ files: z.array(z.string()),
63
+ role: z.string().optional(),
64
+ });
65
+ export const BacklogRecord = BaseWorkItem.extend({
66
+ type: z.literal('backlog-item'),
67
+ category: z.string().optional(),
68
+ priority: Priority.optional(),
69
+ description: z.string().optional(),
70
+ trigger: z.string().optional(),
71
+ sourceContext: z.string().optional(),
72
+ sourceDirective: z.string().optional(),
73
+ repoId: z.string().optional(),
74
+ repoName: z.string().optional(),
75
+ });
76
+ export const DirectiveRecord = BaseWorkItem.extend({
77
+ type: z.literal('directive'),
78
+ projects: z.array(z.string()),
79
+ checkpoint: z.string().optional(),
80
+ reportPath: z.string().optional(),
81
+ // Structured fields from directive.json
82
+ weight: z.string().optional(),
83
+ category: z.string().optional(),
84
+ producedFeatures: z.array(z.string()).optional(),
85
+ report: z.string().nullable().optional(),
86
+ backlogSources: z.array(z.string()).optional(),
87
+ artifacts: z.array(z.string()).optional(),
88
+ });
89
+ export const LessonRecord = z.object({
90
+ id: z.string(),
91
+ title: z.string(),
92
+ filePath: z.string(),
93
+ contentSummary: z.string().optional(),
94
+ topics: z.array(z.string()).optional(),
95
+ updatedAt: z.string(),
96
+ });
97
+ export const ArtifactRecord = BaseWorkItem.extend({
98
+ type: z.literal('report').or(z.literal('discussion')).or(z.literal('research')),
99
+ participants: z.array(z.string()).optional(),
100
+ sourceDirective: z.string().optional(),
101
+ filePath: z.string(),
102
+ contentSummary: z.string().optional(),
103
+ });
104
+ // ---------------------------------------------------------------------------
105
+ // Discriminated union
106
+ // ---------------------------------------------------------------------------
107
+ export const WorkItem = z.discriminatedUnion('type', [
108
+ FeatureRecord,
109
+ TaskRecord,
110
+ BacklogRecord,
111
+ DirectiveRecord,
112
+ // ArtifactRecord covers report, discussion, research — but discriminatedUnion
113
+ // needs literal type values, so we split:
114
+ BaseWorkItem.extend({
115
+ type: z.literal('report'),
116
+ participants: z.array(z.string()).optional(),
117
+ sourceDirective: z.string().optional(),
118
+ filePath: z.string(),
119
+ contentSummary: z.string().optional(),
120
+ }),
121
+ BaseWorkItem.extend({
122
+ type: z.literal('discussion'),
123
+ participants: z.array(z.string()).optional(),
124
+ sourceDirective: z.string().optional(),
125
+ filePath: z.string(),
126
+ contentSummary: z.string().optional(),
127
+ }),
128
+ BaseWorkItem.extend({
129
+ type: z.literal('research'),
130
+ participants: z.array(z.string()).optional(),
131
+ sourceDirective: z.string().optional(),
132
+ filePath: z.string(),
133
+ contentSummary: z.string().optional(),
134
+ }),
135
+ ]);
136
+ // ---------------------------------------------------------------------------
137
+ // State file schemas (what the JSON files contain)
138
+ // ---------------------------------------------------------------------------
139
+ export const FeaturesState = z.object({
140
+ generated: z.string(),
141
+ features: z.array(FeatureRecord),
142
+ });
143
+ export const BacklogsState = z.object({
144
+ generated: z.string(),
145
+ items: z.array(BacklogRecord),
146
+ });
147
+ export const ConductorState = z.object({
148
+ generated: z.string(),
149
+ directives: z.array(DirectiveRecord),
150
+ reports: z.array(ArtifactRecord),
151
+ discussions: z.array(ArtifactRecord),
152
+ research: z.array(ArtifactRecord),
153
+ lessons: z.array(LessonRecord).optional(),
154
+ });
155
+ export const IndexState = z.object({
156
+ generated: z.string(),
157
+ counts: z.object({
158
+ activeFeatures: z.number(),
159
+ doneFeatures: z.number(),
160
+ pendingTasks: z.number(),
161
+ completedTasks: z.number(),
162
+ backlogItems: z.number(),
163
+ directives: z.number(),
164
+ reports: z.number(),
165
+ discussions: z.number(),
166
+ lessons: z.number().optional(),
167
+ }),
168
+ });
@@ -0,0 +1,237 @@
1
+ export interface Team {
2
+ name: string;
3
+ description: string;
4
+ members: TeamMember[];
5
+ createdAt: string;
6
+ leadAgentId: string;
7
+ leadSessionId: string;
8
+ stale: boolean;
9
+ }
10
+ export interface TeamMember {
11
+ name: string;
12
+ agentId: string;
13
+ agentType: string;
14
+ model: string;
15
+ tmuxPaneId: string;
16
+ cwd: string;
17
+ color: string;
18
+ isActive: boolean;
19
+ backendType: string;
20
+ joinedAt: string;
21
+ }
22
+ export interface TeamTask {
23
+ id: string;
24
+ subject: string;
25
+ description: string;
26
+ activeForm: string;
27
+ status: 'pending' | 'in_progress' | 'completed';
28
+ owner: string;
29
+ blocks: string[];
30
+ blockedBy: string[];
31
+ }
32
+ export interface Session {
33
+ id: string;
34
+ project: string;
35
+ projectDir: string;
36
+ status: 'working' | 'waiting-approval' | 'waiting-input' | 'done' | 'paused' | 'idle' | 'error';
37
+ lastActivity: string;
38
+ feature?: string;
39
+ model?: string;
40
+ cwd?: string;
41
+ gitBranch?: string;
42
+ version?: string;
43
+ slug?: string;
44
+ initialPrompt?: string;
45
+ latestPrompt?: string;
46
+ tasksId?: string;
47
+ paneId?: string;
48
+ terminalApp?: 'iterm2' | 'warp' | 'terminal' | 'tmux' | 'unknown';
49
+ isSubagent: boolean;
50
+ parentSessionId?: string;
51
+ agentId?: string;
52
+ agentName?: string;
53
+ agentRole?: string;
54
+ subagentIds: string[];
55
+ fileSize: number;
56
+ /** True when a subagent has error/waiting status that needs parent attention */
57
+ subagentAttention?: boolean;
58
+ /** Names of active subagents (propagated from working parent) */
59
+ activeSubagentNames?: string[];
60
+ }
61
+ export interface ProjectGroup {
62
+ name: string;
63
+ dirName: string;
64
+ sessions: Session[];
65
+ }
66
+ export interface HookEvent {
67
+ id: string;
68
+ type: string;
69
+ sessionId: string;
70
+ timestamp: string;
71
+ message: string;
72
+ project?: string;
73
+ metadata?: Record<string, unknown>;
74
+ }
75
+ export interface DirectiveProjectTask {
76
+ title: string;
77
+ status: string;
78
+ agent?: string;
79
+ dod?: Array<{
80
+ criterion: string;
81
+ met: boolean;
82
+ }>;
83
+ }
84
+ export interface DirectiveProject {
85
+ id: string;
86
+ title: string;
87
+ status: 'pending' | 'in_progress' | 'completed' | 'skipped' | 'failed';
88
+ phase: 'audit' | 'design' | 'build' | 'review' | null;
89
+ totalTasks?: number;
90
+ completedTasks?: number;
91
+ tasks?: DirectiveProjectTask[];
92
+ }
93
+ export type PipelineStepStatus = 'pending' | 'active' | 'completed' | 'skipped' | 'failed';
94
+ export interface PipelineStep {
95
+ id: string;
96
+ label: string;
97
+ status: PipelineStepStatus;
98
+ artifacts?: Record<string, string>;
99
+ needsAction?: boolean;
100
+ startedAt?: string;
101
+ }
102
+ export interface DirectiveState {
103
+ directiveName: string;
104
+ /** Human-readable title (from directive .json or .md heading) */
105
+ title?: string;
106
+ status: 'pending' | 'in_progress' | 'awaiting_completion' | 'completed' | 'failed';
107
+ totalProjects: number;
108
+ currentProject: number;
109
+ currentPhase: string;
110
+ projects: DirectiveProject[];
111
+ startedAt: string;
112
+ lastUpdated: string;
113
+ /** Pipeline step progress derived from checkpoint data */
114
+ pipelineSteps?: PipelineStep[];
115
+ /** Current pipeline step ID */
116
+ currentStepId?: string;
117
+ /** Directive weight class */
118
+ weight?: string;
119
+ /** Directive category (e.g. game, ui, framework) */
120
+ category?: string;
121
+ /** Triage rationale — why this weight was assigned */
122
+ triageRationale?: string;
123
+ /** CEO approval status */
124
+ approvalStatus?: string;
125
+ /** Brainstorm summary from brainstorm step output */
126
+ brainstormSummary?: string;
127
+ /** Plan summary from plan step output */
128
+ planSummary?: string;
129
+ /** Full brainstorm markdown content */
130
+ brainstormContent?: string;
131
+ /** Full directive brief markdown content (often contains plan) */
132
+ directiveBrief?: string;
133
+ }
134
+ export interface DashboardState {
135
+ teams: Team[];
136
+ sessions: Session[];
137
+ projects: ProjectGroup[];
138
+ tasksByTeam: Record<string, TeamTask[]>;
139
+ tasksBySession: Record<string, TeamTask[]>;
140
+ events: HookEvent[];
141
+ sessionActivities: Record<string, SessionActivity>;
142
+ directiveState: DirectiveState | null;
143
+ directiveHistory: DirectiveState[];
144
+ activeDirectives: DirectiveState[];
145
+ lastUpdated: string;
146
+ }
147
+ export interface ProjectConfig {
148
+ name: string;
149
+ path: string;
150
+ /** Source of this project config: 'config' (from config.json) or 'discovered' (from ~/.claude/projects/) */
151
+ source?: 'config' | 'discovered';
152
+ }
153
+ export interface NotificationConfig {
154
+ macOS: boolean;
155
+ browser: boolean;
156
+ }
157
+ export interface ConductorConfig {
158
+ projects: ProjectConfig[];
159
+ claudeHome: string;
160
+ server: {
161
+ port: number;
162
+ };
163
+ notifications: NotificationConfig;
164
+ }
165
+ export interface SendInputRequest {
166
+ paneId: string;
167
+ input: string;
168
+ type: 'approve' | 'reject' | 'abort' | 'text';
169
+ }
170
+ export interface SessionActivity {
171
+ sessionId: string;
172
+ tool?: string;
173
+ detail?: string;
174
+ thinking?: boolean;
175
+ model?: string;
176
+ lastSeen: string;
177
+ active: boolean;
178
+ }
179
+ export type WsMessageType = 'full_state' | 'sessions_updated' | 'projects_updated' | 'teams_updated' | 'tasks_updated' | 'event_added' | 'events_updated' | 'session_activities_updated' | 'notification_fired' | 'directive_updated' | 'state_updated';
180
+ export interface WsMessage {
181
+ version: 1;
182
+ type: WsMessageType;
183
+ payload: unknown;
184
+ }
185
+ export interface IntelligenceAgentStats {
186
+ agent: string;
187
+ domain: string;
188
+ totalFindings: number;
189
+ findingsByUrgency: Record<string, number>;
190
+ findingsByType: Record<string, number>;
191
+ proposalsSubmitted: number;
192
+ proposalsAccepted: number;
193
+ acceptanceRate: number;
194
+ topProducts: string[];
195
+ }
196
+ export interface IntelligenceTopicCluster {
197
+ topic: string;
198
+ keywords: string[];
199
+ mentionCount: number;
200
+ agents: string[];
201
+ urgencyMax: string;
202
+ items: Array<{
203
+ id: string;
204
+ title: string;
205
+ agent: string;
206
+ urgency: string;
207
+ }>;
208
+ }
209
+ export interface IntelligenceCrossScoutSignal {
210
+ topic: string;
211
+ agentCount: number;
212
+ agents: string[];
213
+ totalMentions: number;
214
+ highestUrgency: string;
215
+ items: Array<{
216
+ id: string;
217
+ title: string;
218
+ agent: string;
219
+ urgency: string;
220
+ }>;
221
+ strength: 'strong' | 'moderate' | 'weak';
222
+ shouldPromote: boolean;
223
+ }
224
+ export interface IntelligenceTrendsResult {
225
+ generated: string;
226
+ scoutDate: string | null;
227
+ totalFindings: number;
228
+ totalProposals: number;
229
+ totalAccepted: number;
230
+ overallAcceptanceRate: number;
231
+ agentStats: IntelligenceAgentStats[];
232
+ topTopics: IntelligenceTopicCluster[];
233
+ crossScoutSignals: IntelligenceCrossScoutSignal[];
234
+ urgencyBreakdown: Record<string, number>;
235
+ typeBreakdown: Record<string, number>;
236
+ productHeatmap: Record<string, number>;
237
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,17 @@
1
+ import type { AggregatorHandle } from '../platform/types.js';
2
+ export declare class ClaudeWatcher {
3
+ private watcher;
4
+ private aggregator;
5
+ private claudeHome;
6
+ private teamsDebounceTimer;
7
+ private tasksDebounceTimers;
8
+ private pollTimer;
9
+ private lastTeamsMtime;
10
+ private _ready;
11
+ constructor(aggregator: AggregatorHandle, claudeHome: string);
12
+ start(): void;
13
+ get ready(): boolean;
14
+ stop(): Promise<void>;
15
+ private startPollFallback;
16
+ private handleChange;
17
+ }
@@ -0,0 +1,130 @@
1
+ import path from 'node:path';
2
+ import fs from 'node:fs';
3
+ import { watch } from 'chokidar';
4
+ const UUID_DIR_REGEX = /^[0-9a-f]{8}-/;
5
+ export class ClaudeWatcher {
6
+ watcher = null;
7
+ aggregator;
8
+ claudeHome;
9
+ teamsDebounceTimer = null;
10
+ tasksDebounceTimers = new Map();
11
+ pollTimer = null;
12
+ lastTeamsMtime = 0;
13
+ _ready = false;
14
+ constructor(aggregator, claudeHome) {
15
+ this.aggregator = aggregator;
16
+ this.claudeHome = claudeHome;
17
+ }
18
+ start() {
19
+ const teamsDir = path.join(this.claudeHome, 'teams');
20
+ // If directory doesn't exist yet, just start poll fallback (will pick it up when created)
21
+ if (!fs.existsSync(teamsDir)) {
22
+ console.log(`[claude-watcher] Teams directory not found: ${teamsDir}, polling only`);
23
+ this._ready = true;
24
+ this.startPollFallback();
25
+ return;
26
+ }
27
+ const watchPaths = [teamsDir];
28
+ // Also watch tasks dir if it exists
29
+ const tasksDir = path.join(this.claudeHome, 'tasks');
30
+ if (fs.existsSync(tasksDir)) {
31
+ watchPaths.push(tasksDir);
32
+ }
33
+ console.log(`[claude-watcher] Watching ${watchPaths.join(', ')}`);
34
+ this.watcher = watch(watchPaths, {
35
+ ignoreInitial: true,
36
+ persistent: true,
37
+ awaitWriteFinish: {
38
+ stabilityThreshold: 200,
39
+ pollInterval: 50,
40
+ },
41
+ });
42
+ this.watcher.on('all', (_event, filePath) => {
43
+ // Only care about JSON files
44
+ if (!filePath.endsWith('.json'))
45
+ return;
46
+ this.handleChange(filePath);
47
+ });
48
+ this.watcher.on('ready', () => {
49
+ this._ready = true;
50
+ console.log(`[claude-watcher] Ready`);
51
+ });
52
+ this.watcher.on('error', (err) => {
53
+ console.error(`[claude-watcher] Error:`, err);
54
+ });
55
+ this.startPollFallback();
56
+ }
57
+ get ready() {
58
+ return this._ready;
59
+ }
60
+ async stop() {
61
+ if (this.teamsDebounceTimer) {
62
+ clearTimeout(this.teamsDebounceTimer);
63
+ }
64
+ if (this.pollTimer) {
65
+ clearInterval(this.pollTimer);
66
+ this.pollTimer = null;
67
+ }
68
+ for (const timer of this.tasksDebounceTimers.values()) {
69
+ clearTimeout(timer);
70
+ }
71
+ this.tasksDebounceTimers.clear();
72
+ if (this.watcher) {
73
+ await this.watcher.close();
74
+ this.watcher = null;
75
+ }
76
+ }
77
+ startPollFallback() {
78
+ if (this.pollTimer)
79
+ return;
80
+ this.pollTimer = setInterval(() => {
81
+ const teamsDir = path.join(this.claudeHome, 'teams');
82
+ try {
83
+ const stat = fs.statSync(teamsDir);
84
+ if (stat.mtimeMs !== this.lastTeamsMtime) {
85
+ this.lastTeamsMtime = stat.mtimeMs;
86
+ this.aggregator.refreshTeams();
87
+ }
88
+ }
89
+ catch {
90
+ // teams dir may not exist yet
91
+ }
92
+ }, 5000);
93
+ }
94
+ handleChange(filePath) {
95
+ const teamsDir = path.join(this.claudeHome, 'teams');
96
+ const tasksDir = path.join(this.claudeHome, 'tasks');
97
+ if (filePath.startsWith(teamsDir)) {
98
+ // Teams config changed — debounce per teams dir
99
+ if (this.teamsDebounceTimer) {
100
+ clearTimeout(this.teamsDebounceTimer);
101
+ }
102
+ this.teamsDebounceTimer = setTimeout(() => {
103
+ console.log(`[claude-watcher] Teams config changed, refreshing teams`);
104
+ this.aggregator.refreshTeams();
105
+ this.teamsDebounceTimer = null;
106
+ }, 300);
107
+ }
108
+ else if (filePath.startsWith(tasksDir)) {
109
+ // Task file changed — extract teamName and debounce per team
110
+ const relative = path.relative(tasksDir, filePath);
111
+ const parts = relative.split(path.sep);
112
+ if (parts.length < 1)
113
+ return;
114
+ const teamName = parts[0];
115
+ // Skip UUID-named directories
116
+ if (UUID_DIR_REGEX.test(teamName))
117
+ return;
118
+ // Debounce per team name
119
+ const existing = this.tasksDebounceTimers.get(teamName);
120
+ if (existing) {
121
+ clearTimeout(existing);
122
+ }
123
+ this.tasksDebounceTimers.set(teamName, setTimeout(() => {
124
+ console.log(`[claude-watcher] Tasks changed for team "${teamName}", refreshing tasks`);
125
+ this.aggregator.refreshTasks(teamName);
126
+ this.tasksDebounceTimers.delete(teamName);
127
+ }, 300));
128
+ }
129
+ }
130
+ }
@@ -0,0 +1,22 @@
1
+ import type { ConductorConfig } from '../types.js';
2
+ /**
3
+ * Watches .context/ for changes to source files (md, json, tasks.json)
4
+ * and automatically re-runs the state indexer to keep .context/state/ fresh.
5
+ *
6
+ * Pipeline: .context/*.md changes → ContextWatcher → runs index-state.ts →
7
+ * outputs .context/state/*.json → StateWatcher picks up → dashboard updates
8
+ */
9
+ export declare class ContextWatcher {
10
+ private watchers;
11
+ private config;
12
+ private debounceTimer;
13
+ private indexing;
14
+ private pendingReindex;
15
+ private _ready;
16
+ constructor(config: ConductorConfig);
17
+ start(): void;
18
+ get ready(): boolean;
19
+ stop(): Promise<void>;
20
+ private handleChange;
21
+ private runIndexer;
22
+ }
@@ -0,0 +1,125 @@
1
+ import path from 'node:path';
2
+ import fs from 'node:fs';
3
+ import { execFile } from 'node:child_process';
4
+ import { fileURLToPath } from 'node:url';
5
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
6
+ import { watch } from 'chokidar';
7
+ /**
8
+ * Watches .context/ for changes to source files (md, json, tasks.json)
9
+ * and automatically re-runs the state indexer to keep .context/state/ fresh.
10
+ *
11
+ * Pipeline: .context/*.md changes → ContextWatcher → runs index-state.ts →
12
+ * outputs .context/state/*.json → StateWatcher picks up → dashboard updates
13
+ */
14
+ export class ContextWatcher {
15
+ watchers = [];
16
+ config;
17
+ debounceTimer = null;
18
+ indexing = false;
19
+ pendingReindex = false;
20
+ _ready = false;
21
+ constructor(config) {
22
+ this.config = config;
23
+ }
24
+ start() {
25
+ if (this.config.projects.length === 0) {
26
+ console.log('[context-watcher] No projects configured, skipping');
27
+ this._ready = true;
28
+ return;
29
+ }
30
+ for (const project of this.config.projects) {
31
+ const contextDir = path.join(project.path, '.context');
32
+ if (!fs.existsSync(contextDir)) {
33
+ console.log(`[context-watcher] No .context/ dir for ${project.name}, skipping`);
34
+ continue;
35
+ }
36
+ console.log(`[context-watcher] Watching ${contextDir} (${project.name})`);
37
+ const watcher = watch(contextDir, {
38
+ ignoreInitial: true,
39
+ persistent: true,
40
+ // Ignore state/ directory (that's our output, not input)
41
+ ignored: [
42
+ path.join(contextDir, 'state', '**'),
43
+ '**/node_modules/**',
44
+ ],
45
+ awaitWriteFinish: {
46
+ stabilityThreshold: 500,
47
+ pollInterval: 100,
48
+ },
49
+ // Don't watch too deep to avoid performance issues
50
+ depth: 6,
51
+ });
52
+ watcher.on('all', (_event, filePath) => {
53
+ // Only care about relevant file types
54
+ if (!filePath.endsWith('.md') && !filePath.endsWith('.json'))
55
+ return;
56
+ this.handleChange(project.path);
57
+ });
58
+ watcher.on('ready', () => {
59
+ console.log(`[context-watcher] Ready for ${project.name}`);
60
+ });
61
+ watcher.on('error', (err) => {
62
+ console.error(`[context-watcher] Error for ${project.name}:`, err);
63
+ });
64
+ this.watchers.push(watcher);
65
+ }
66
+ this._ready = true;
67
+ }
68
+ get ready() {
69
+ return this._ready;
70
+ }
71
+ async stop() {
72
+ if (this.debounceTimer) {
73
+ clearTimeout(this.debounceTimer);
74
+ this.debounceTimer = null;
75
+ }
76
+ for (const watcher of this.watchers) {
77
+ await watcher.close();
78
+ }
79
+ this.watchers = [];
80
+ }
81
+ handleChange(projectPath) {
82
+ // Debounce: wait 2 seconds after last change before indexing
83
+ // (multiple files often change together during a directive)
84
+ if (this.debounceTimer) {
85
+ clearTimeout(this.debounceTimer);
86
+ }
87
+ this.debounceTimer = setTimeout(() => {
88
+ this.debounceTimer = null;
89
+ this.runIndexer(projectPath);
90
+ }, 2000);
91
+ }
92
+ runIndexer(projectPath) {
93
+ // If already indexing, queue a re-run
94
+ if (this.indexing) {
95
+ this.pendingReindex = true;
96
+ return;
97
+ }
98
+ this.indexing = true;
99
+ const conductorRoot = path.resolve(__dirname, '../..');
100
+ const scriptPath = path.join(conductorRoot, 'scripts', 'index-state.ts');
101
+ if (!fs.existsSync(scriptPath)) {
102
+ console.log(`[context-watcher] No indexer script at ${scriptPath}, skipping`);
103
+ this.indexing = false;
104
+ return;
105
+ }
106
+ console.log('[context-watcher] Re-indexing .context/ ...');
107
+ execFile('npx', ['tsx', scriptPath, '--context-path', path.join(projectPath, '.context')], {
108
+ cwd: conductorRoot,
109
+ timeout: 30000,
110
+ }, (error, _stdout, stderr) => {
111
+ this.indexing = false;
112
+ if (error) {
113
+ console.error('[context-watcher] Indexer failed:', stderr || error.message);
114
+ }
115
+ else {
116
+ console.log('[context-watcher] Re-index complete');
117
+ }
118
+ // If changes came in while indexing, run again
119
+ if (this.pendingReindex) {
120
+ this.pendingReindex = false;
121
+ this.runIndexer(projectPath);
122
+ }
123
+ });
124
+ }
125
+ }