gemkit-cli 0.2.3 → 0.3.1

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 (160) hide show
  1. package/README.md +141 -7
  2. package/dist/commands/agent/index.d.ts +9 -0
  3. package/dist/commands/agent/index.js +1329 -0
  4. package/dist/commands/cache/index.d.ts +5 -0
  5. package/dist/commands/cache/index.js +43 -0
  6. package/dist/commands/catalog/index.d.ts +2 -0
  7. package/dist/commands/catalog/index.js +57 -0
  8. package/dist/commands/config/index.d.ts +7 -0
  9. package/dist/commands/config/index.js +122 -0
  10. package/dist/commands/convert/index.d.ts +8 -0
  11. package/dist/commands/convert/index.js +391 -0
  12. package/dist/commands/doctor/index.d.ts +2 -0
  13. package/dist/commands/doctor/index.js +243 -0
  14. package/dist/commands/extension/index.d.ts +5 -0
  15. package/dist/commands/extension/index.js +52 -0
  16. package/dist/commands/index.d.ts +5 -0
  17. package/dist/commands/index.js +37 -0
  18. package/dist/commands/init/index.d.ts +6 -0
  19. package/dist/commands/init/index.js +345 -0
  20. package/dist/commands/new/index.d.ts +5 -0
  21. package/dist/commands/new/index.js +49 -0
  22. package/dist/commands/office/index.d.ts +5 -0
  23. package/dist/commands/office/index.js +283 -0
  24. package/dist/commands/paste/index.d.ts +10 -0
  25. package/dist/commands/paste/index.js +533 -0
  26. package/dist/commands/plan/index.d.ts +8 -0
  27. package/dist/commands/plan/index.js +247 -0
  28. package/dist/commands/session/index.d.ts +8 -0
  29. package/dist/commands/session/index.js +289 -0
  30. package/dist/commands/tokens/index.d.ts +6 -0
  31. package/dist/commands/tokens/index.js +148 -0
  32. package/dist/commands/update/index.d.ts +26 -0
  33. package/dist/commands/update/index.js +199 -0
  34. package/dist/commands/versions/index.d.ts +5 -0
  35. package/dist/commands/versions/index.js +39 -0
  36. package/dist/domains/agent/index.d.ts +8 -0
  37. package/dist/domains/agent/index.js +8 -0
  38. package/dist/domains/agent/mappings.d.ts +32 -0
  39. package/dist/domains/agent/mappings.js +164 -0
  40. package/dist/domains/agent/profile.d.ts +26 -0
  41. package/dist/domains/agent/profile.js +225 -0
  42. package/dist/domains/agent/pty-context.d.ts +11 -0
  43. package/dist/domains/agent/pty-context.js +83 -0
  44. package/dist/domains/agent/pty-providers.d.ts +18 -0
  45. package/dist/domains/agent/pty-providers.js +66 -0
  46. package/dist/domains/agent/pty-session.d.ts +33 -0
  47. package/dist/domains/agent/pty-session.js +82 -0
  48. package/dist/domains/agent/pty-types.d.ts +127 -0
  49. package/dist/domains/agent/pty-types.js +4 -0
  50. package/dist/domains/agent/search.d.ts +45 -0
  51. package/dist/domains/agent/search.js +614 -0
  52. package/dist/domains/agent/types.d.ts +78 -0
  53. package/dist/domains/agent/types.js +5 -0
  54. package/dist/domains/agent-office/documents-scanner.d.ts +9 -0
  55. package/dist/domains/agent-office/documents-scanner.js +143 -0
  56. package/dist/domains/agent-office/event-emitter.d.ts +43 -0
  57. package/dist/domains/agent-office/event-emitter.js +86 -0
  58. package/dist/domains/agent-office/file-watcher.d.ts +40 -0
  59. package/dist/domains/agent-office/file-watcher.js +173 -0
  60. package/dist/domains/agent-office/icons.d.ts +11 -0
  61. package/dist/domains/agent-office/icons.js +36 -0
  62. package/dist/domains/agent-office/index.d.ts +12 -0
  63. package/dist/domains/agent-office/index.js +20 -0
  64. package/dist/domains/agent-office/renderer/web/assets.d.ts +11 -0
  65. package/dist/domains/agent-office/renderer/web/assets.js +3419 -0
  66. package/dist/domains/agent-office/renderer/web/server.d.ts +42 -0
  67. package/dist/domains/agent-office/renderer/web/server.js +228 -0
  68. package/dist/domains/agent-office/renderer/web.d.ts +30 -0
  69. package/dist/domains/agent-office/renderer/web.js +111 -0
  70. package/dist/domains/agent-office/session-bridge.d.ts +23 -0
  71. package/dist/domains/agent-office/session-bridge.js +171 -0
  72. package/dist/domains/agent-office/state-machine.d.ts +5 -0
  73. package/dist/domains/agent-office/state-machine.js +82 -0
  74. package/dist/domains/agent-office/types.d.ts +91 -0
  75. package/dist/domains/agent-office/types.js +4 -0
  76. package/dist/domains/cache/index.d.ts +1 -0
  77. package/dist/domains/cache/index.js +1 -0
  78. package/dist/domains/cache/manager.d.ts +22 -0
  79. package/dist/domains/cache/manager.js +84 -0
  80. package/dist/domains/config/index.d.ts +5 -0
  81. package/dist/domains/config/index.js +5 -0
  82. package/dist/domains/config/manager.d.ts +24 -0
  83. package/dist/domains/config/manager.js +85 -0
  84. package/dist/domains/config/schema.d.ts +17 -0
  85. package/dist/domains/config/schema.js +96 -0
  86. package/dist/domains/convert/converter.d.ts +78 -0
  87. package/dist/domains/convert/converter.js +471 -0
  88. package/dist/domains/convert/index.d.ts +5 -0
  89. package/dist/domains/convert/index.js +5 -0
  90. package/dist/domains/convert/types.d.ts +88 -0
  91. package/dist/domains/convert/types.js +18 -0
  92. package/dist/domains/github/download.d.ts +12 -0
  93. package/dist/domains/github/download.js +51 -0
  94. package/dist/domains/github/index.d.ts +2 -0
  95. package/dist/domains/github/index.js +2 -0
  96. package/dist/domains/github/releases.d.ts +16 -0
  97. package/dist/domains/github/releases.js +68 -0
  98. package/dist/domains/installation/conflict.d.ts +13 -0
  99. package/dist/domains/installation/conflict.js +38 -0
  100. package/dist/domains/installation/file-sync.d.ts +16 -0
  101. package/dist/domains/installation/file-sync.js +77 -0
  102. package/dist/domains/installation/index.d.ts +3 -0
  103. package/dist/domains/installation/index.js +3 -0
  104. package/dist/domains/installation/metadata.d.ts +20 -0
  105. package/dist/domains/installation/metadata.js +52 -0
  106. package/dist/domains/plan/index.d.ts +2 -0
  107. package/dist/domains/plan/index.js +2 -0
  108. package/dist/domains/plan/resolver.d.ts +24 -0
  109. package/dist/domains/plan/resolver.js +164 -0
  110. package/dist/domains/plan/types.d.ts +13 -0
  111. package/dist/domains/plan/types.js +4 -0
  112. package/dist/domains/session/env.d.ts +51 -0
  113. package/dist/domains/session/env.js +118 -0
  114. package/dist/domains/session/index.d.ts +8 -0
  115. package/dist/domains/session/index.js +8 -0
  116. package/dist/domains/session/manager.d.ts +56 -0
  117. package/dist/domains/session/manager.js +205 -0
  118. package/dist/domains/session/paths.d.ts +6 -0
  119. package/dist/domains/session/paths.js +6 -0
  120. package/dist/domains/session/types.d.ts +121 -0
  121. package/dist/domains/session/types.js +5 -0
  122. package/dist/domains/session/writer.d.ts +82 -0
  123. package/dist/domains/session/writer.js +431 -0
  124. package/dist/domains/tokens/index.d.ts +5 -0
  125. package/dist/domains/tokens/index.js +5 -0
  126. package/dist/domains/tokens/pricing.d.ts +38 -0
  127. package/dist/domains/tokens/pricing.js +129 -0
  128. package/dist/domains/tokens/scanner.d.ts +42 -0
  129. package/dist/domains/tokens/scanner.js +168 -0
  130. package/dist/index.d.ts +5 -0
  131. package/dist/index.js +90 -59
  132. package/dist/services/aipty.d.ts +76 -0
  133. package/dist/services/aipty.js +276 -0
  134. package/dist/services/archive.d.ts +22 -0
  135. package/dist/services/archive.js +53 -0
  136. package/dist/services/auto-update.d.ts +26 -0
  137. package/dist/services/auto-update.js +117 -0
  138. package/dist/services/hash.d.ts +36 -0
  139. package/dist/services/hash.js +63 -0
  140. package/dist/services/logger.d.ts +28 -0
  141. package/dist/services/logger.js +102 -0
  142. package/dist/services/music.d.ts +67 -0
  143. package/dist/services/music.js +290 -0
  144. package/dist/services/npm.d.ts +22 -0
  145. package/dist/services/npm.js +65 -0
  146. package/dist/services/pty-client.d.ts +66 -0
  147. package/dist/services/pty-client.js +154 -0
  148. package/dist/services/pty-server.d.ts +102 -0
  149. package/dist/services/pty-server.js +613 -0
  150. package/dist/types/index.d.ts +155 -0
  151. package/dist/types/index.js +4 -0
  152. package/dist/utils/colors.d.ts +43 -0
  153. package/dist/utils/colors.js +98 -0
  154. package/dist/utils/errors.d.ts +24 -0
  155. package/dist/utils/errors.js +56 -0
  156. package/dist/utils/paths.d.ts +46 -0
  157. package/dist/utils/paths.js +89 -0
  158. package/dist/utils/platform.d.ts +11 -0
  159. package/dist/utils/platform.js +31 -0
  160. package/package.json +55 -54
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Agent type definitions for MVP
3
+ * Aligned with Python core.py types
4
+ */
5
+ export {};
@@ -0,0 +1,9 @@
1
+ import { PlanDocument, DocumentType } from './types.js';
2
+ /**
3
+ * Scan plan folder for documents
4
+ */
5
+ export declare function scanPlanDocuments(planPath: string): PlanDocument[];
6
+ /**
7
+ * Group documents by type for display
8
+ */
9
+ export declare function groupDocumentsByType(documents: PlanDocument[]): Record<DocumentType, PlanDocument[]>;
@@ -0,0 +1,143 @@
1
+ import { existsSync, readdirSync, statSync } from 'fs';
2
+ import { join, basename, extname, relative } from 'path';
3
+ import { createHash } from 'crypto';
4
+ /**
5
+ * Get icon for document type/extension
6
+ */
7
+ function getDocumentIcon(type, ext) {
8
+ const typeIcons = {
9
+ plan: '\uD83D\uDCCB', // clipboard
10
+ phase: '\uD83D\uDCD1', // bookmark tabs
11
+ research: '\uD83D\uDD0D', // magnifier
12
+ artifact: '\uD83D\uDCE6', // package
13
+ report: '\uD83D\uDCCA', // chart
14
+ other: '\uD83D\uDCC4', // document
15
+ };
16
+ if (type === 'artifact') {
17
+ const extIcons = {
18
+ sql: '\uD83D\uDDC4\uFE0F', // file cabinet
19
+ json: '\uD83D\uDCE6', // package
20
+ png: '\uD83D\uDDBC\uFE0F', // frame
21
+ jpg: '\uD83D\uDDBC\uFE0F', // frame
22
+ ts: '\uD83D\uDCBB', // laptop
23
+ js: '\uD83D\uDCBB', // laptop
24
+ };
25
+ return extIcons[ext] || typeIcons.artifact;
26
+ }
27
+ return typeIcons[type];
28
+ }
29
+ /**
30
+ * Extract phase number from filename
31
+ */
32
+ function extractPhaseNumber(name) {
33
+ const match = name.match(/^phase-(\d+)/);
34
+ return match ? parseInt(match[1], 10) : null;
35
+ }
36
+ /**
37
+ * Format document name for display
38
+ */
39
+ function formatDocumentName(name, type) {
40
+ if (type === 'phase') {
41
+ const num = extractPhaseNumber(name);
42
+ const rest = name.replace(/^phase-\d+-?/, '').replace(/-/g, ' ');
43
+ return num !== null ? `Phase ${num}: ${rest}` : name;
44
+ }
45
+ return name.replace(/-/g, ' ').replace(/\b\w/g, c => c.toUpperCase());
46
+ }
47
+ /**
48
+ * Create document object from file path
49
+ */
50
+ function createDocument(filePath, type, planPath) {
51
+ const stat = statSync(filePath);
52
+ const name = basename(filePath, extname(filePath));
53
+ const ext = extname(filePath).slice(1);
54
+ return {
55
+ id: createHash('md5').update(filePath).digest('hex').slice(0, 8),
56
+ name,
57
+ displayName: formatDocumentName(name, type),
58
+ type,
59
+ icon: getDocumentIcon(type, ext),
60
+ path: filePath,
61
+ relativePath: relative(planPath, filePath),
62
+ modifiedAt: stat.mtimeMs,
63
+ createdAt: stat.birthtimeMs,
64
+ size: stat.size,
65
+ extension: ext,
66
+ phaseNumber: extractPhaseNumber(name),
67
+ };
68
+ }
69
+ /**
70
+ * Scan plan folder for documents
71
+ */
72
+ export function scanPlanDocuments(planPath) {
73
+ if (!existsSync(planPath))
74
+ return [];
75
+ const documents = [];
76
+ // 1. Scan plan.md (main plan)
77
+ const planFile = join(planPath, 'plan.md');
78
+ if (existsSync(planFile)) {
79
+ documents.push(createDocument(planFile, 'plan', planPath));
80
+ }
81
+ // 2. Scan phase-*.md or phase-*/ directories
82
+ const rootFiles = readdirSync(planPath);
83
+ for (const file of rootFiles) {
84
+ const fullPath = join(planPath, file);
85
+ const stat = statSync(fullPath);
86
+ if (file.startsWith('phase-')) {
87
+ if (stat.isDirectory()) {
88
+ // Check for phase.md inside directory
89
+ const phaseFile = join(fullPath, 'phase.md');
90
+ if (existsSync(phaseFile)) {
91
+ documents.push(createDocument(phaseFile, 'phase', planPath));
92
+ }
93
+ }
94
+ else if (file.endsWith('.md')) {
95
+ documents.push(createDocument(fullPath, 'phase', planPath));
96
+ }
97
+ }
98
+ }
99
+ // 3. Scan research/ folder
100
+ const researchDir = join(planPath, 'research');
101
+ if (existsSync(researchDir)) {
102
+ const researchFiles = readdirSync(researchDir).filter(f => f.endsWith('.md'));
103
+ for (const file of researchFiles) {
104
+ documents.push(createDocument(join(researchDir, file), 'research', planPath));
105
+ }
106
+ }
107
+ // 4. Scan artifacts/ folder
108
+ const artifactsDir = join(planPath, 'artifacts');
109
+ if (existsSync(artifactsDir)) {
110
+ const artifactFiles = readdirSync(artifactsDir);
111
+ for (const file of artifactFiles) {
112
+ const fullPath = join(artifactsDir, file);
113
+ if (statSync(fullPath).isFile()) {
114
+ documents.push(createDocument(fullPath, 'artifact', planPath));
115
+ }
116
+ }
117
+ }
118
+ // 5. Scan reports/ folder
119
+ const reportsDir = join(planPath, 'reports');
120
+ if (existsSync(reportsDir)) {
121
+ const reportFiles = readdirSync(reportsDir).filter(f => f.endsWith('.md'));
122
+ for (const file of reportFiles) {
123
+ documents.push(createDocument(join(reportsDir, file), 'report', planPath));
124
+ }
125
+ }
126
+ // Sort by modification time (newest first)
127
+ documents.sort((a, b) => b.modifiedAt - a.modifiedAt);
128
+ return documents;
129
+ }
130
+ /**
131
+ * Group documents by type for display
132
+ */
133
+ export function groupDocumentsByType(documents) {
134
+ return {
135
+ plan: documents.filter(d => d.type === 'plan'),
136
+ phase: documents.filter(d => d.type === 'phase')
137
+ .sort((a, b) => (b.phaseNumber || 0) - (a.phaseNumber || 0)),
138
+ research: documents.filter(d => d.type === 'research'),
139
+ artifact: documents.filter(d => d.type === 'artifact'),
140
+ report: documents.filter(d => d.type === 'report'),
141
+ other: documents.filter(d => d.type === 'other'),
142
+ };
143
+ }
@@ -0,0 +1,43 @@
1
+ import { OfficeState, OfficeEvent } from './types.js';
2
+ type StateListener = (state: OfficeState) => void;
3
+ type EventListener = (event: OfficeEvent) => void;
4
+ export declare class OfficeEventEmitter {
5
+ private state;
6
+ private stateListeners;
7
+ private eventListeners;
8
+ private eventHistory;
9
+ constructor(initialState: OfficeState);
10
+ /**
11
+ * Subscribe to state changes
12
+ */
13
+ onStateChange(listener: StateListener): () => void;
14
+ /**
15
+ * Subscribe to individual events
16
+ */
17
+ onEvent(listener: EventListener): () => void;
18
+ /**
19
+ * Emit an event and update state
20
+ */
21
+ emit(event: OfficeEvent): void;
22
+ /**
23
+ * Update state and notify listeners
24
+ */
25
+ setState(newState: OfficeState): void;
26
+ /**
27
+ * Get current state snapshot
28
+ */
29
+ getState(): OfficeState;
30
+ /**
31
+ * Get event history
32
+ */
33
+ getHistory(): OfficeEvent[];
34
+ /**
35
+ * Replay events from a timestamp
36
+ */
37
+ replay(fromTimestamp: number): OfficeEvent[];
38
+ /**
39
+ * Clear all listeners (for cleanup)
40
+ */
41
+ dispose(): void;
42
+ }
43
+ export {};
@@ -0,0 +1,86 @@
1
+ const MAX_HISTORY_SIZE = 1000;
2
+ export class OfficeEventEmitter {
3
+ state;
4
+ stateListeners = new Set();
5
+ eventListeners = new Set();
6
+ eventHistory = [];
7
+ constructor(initialState) {
8
+ this.state = initialState;
9
+ }
10
+ /**
11
+ * Subscribe to state changes
12
+ */
13
+ onStateChange(listener) {
14
+ this.stateListeners.add(listener);
15
+ // Immediately notify with current state
16
+ listener(this.state);
17
+ return () => this.stateListeners.delete(listener);
18
+ }
19
+ /**
20
+ * Subscribe to individual events
21
+ */
22
+ onEvent(listener) {
23
+ this.eventListeners.add(listener);
24
+ return () => this.eventListeners.delete(listener);
25
+ }
26
+ /**
27
+ * Emit an event and update state
28
+ */
29
+ emit(event) {
30
+ // Store in history
31
+ this.eventHistory.push(event);
32
+ if (this.eventHistory.length > MAX_HISTORY_SIZE) {
33
+ this.eventHistory.shift();
34
+ }
35
+ // Notify event listeners
36
+ for (const listener of this.eventListeners) {
37
+ try {
38
+ listener(event);
39
+ }
40
+ catch (e) {
41
+ console.error('Event listener error:', e);
42
+ }
43
+ }
44
+ }
45
+ /**
46
+ * Update state and notify listeners
47
+ */
48
+ setState(newState) {
49
+ this.state = newState;
50
+ // Notify state listeners
51
+ for (const listener of this.stateListeners) {
52
+ try {
53
+ listener(this.state);
54
+ }
55
+ catch (e) {
56
+ console.error('State listener error:', e);
57
+ }
58
+ }
59
+ }
60
+ /**
61
+ * Get current state snapshot
62
+ */
63
+ getState() {
64
+ return this.state;
65
+ }
66
+ /**
67
+ * Get event history
68
+ */
69
+ getHistory() {
70
+ return [...this.eventHistory];
71
+ }
72
+ /**
73
+ * Replay events from a timestamp
74
+ */
75
+ replay(fromTimestamp) {
76
+ return this.eventHistory.filter(e => e.timestamp >= fromTimestamp);
77
+ }
78
+ /**
79
+ * Clear all listeners (for cleanup)
80
+ */
81
+ dispose() {
82
+ this.stateListeners.clear();
83
+ this.eventListeners.clear();
84
+ this.eventHistory = [];
85
+ }
86
+ }
@@ -0,0 +1,40 @@
1
+ import { GkSession } from '../session/types.js';
2
+ import { OfficeEvent } from './types.js';
3
+ export interface FileWatcherOptions {
4
+ onSessionChange: (session: GkSession) => void;
5
+ onEvent: (event: OfficeEvent) => void;
6
+ onError: (error: Error) => void;
7
+ }
8
+ export declare class SessionFileWatcher {
9
+ private pollInterval;
10
+ private previousSession;
11
+ private previousMtime;
12
+ private options;
13
+ private sessionPath;
14
+ constructor(options: FileWatcherOptions);
15
+ /**
16
+ * Start watching the active session file
17
+ * Uses polling for reliable cross-platform support (fs.watch is unreliable on Windows)
18
+ */
19
+ start(): boolean;
20
+ /**
21
+ * Stop watching
22
+ */
23
+ stop(): void;
24
+ /**
25
+ * Check if file has changed by comparing modification time
26
+ */
27
+ private checkForChanges;
28
+ /**
29
+ * Load session and emit events for changes
30
+ */
31
+ private loadSession;
32
+ /**
33
+ * Diff two sessions and generate events
34
+ */
35
+ private diffSessions;
36
+ /**
37
+ * Create event from agent
38
+ */
39
+ private createEvent;
40
+ }
@@ -0,0 +1,173 @@
1
+ import { existsSync, readFileSync, statSync } from 'fs';
2
+ import { getSessionPath } from '../../utils/paths.js';
3
+ import { readEnv } from '../session/env.js';
4
+ import { getCharacterType } from './session-bridge.js';
5
+ // Polling interval - 200ms for fast detection
6
+ const POLL_INTERVAL_MS = 200;
7
+ export class SessionFileWatcher {
8
+ pollInterval = null;
9
+ previousSession = null;
10
+ previousMtime = 0;
11
+ options;
12
+ sessionPath = null;
13
+ constructor(options) {
14
+ this.options = options;
15
+ }
16
+ /**
17
+ * Start watching the active session file
18
+ * Uses polling for reliable cross-platform support (fs.watch is unreliable on Windows)
19
+ */
20
+ start() {
21
+ const env = readEnv();
22
+ const projectDir = env.PROJECT_DIR;
23
+ const gkSessionId = env.ACTIVE_GK_SESSION_ID;
24
+ if (!projectDir || !gkSessionId) {
25
+ this.options.onError(new Error('No active session found'));
26
+ return false;
27
+ }
28
+ this.sessionPath = getSessionPath(projectDir, gkSessionId);
29
+ if (!existsSync(this.sessionPath)) {
30
+ this.options.onError(new Error(`Session file not found: ${this.sessionPath}`));
31
+ return false;
32
+ }
33
+ // Load initial session and store mtime
34
+ try {
35
+ const stat = statSync(this.sessionPath);
36
+ this.previousMtime = stat.mtimeMs;
37
+ }
38
+ catch (e) {
39
+ // Ignore stat errors on initial load
40
+ }
41
+ this.loadSession();
42
+ // Start polling for file changes (more reliable than fs.watch on Windows)
43
+ this.pollInterval = setInterval(() => {
44
+ this.checkForChanges();
45
+ }, POLL_INTERVAL_MS);
46
+ return true;
47
+ }
48
+ /**
49
+ * Stop watching
50
+ */
51
+ stop() {
52
+ if (this.pollInterval) {
53
+ clearInterval(this.pollInterval);
54
+ this.pollInterval = null;
55
+ }
56
+ }
57
+ /**
58
+ * Check if file has changed by comparing modification time
59
+ */
60
+ checkForChanges() {
61
+ if (!this.sessionPath)
62
+ return;
63
+ try {
64
+ const stat = statSync(this.sessionPath);
65
+ const currentMtime = stat.mtimeMs;
66
+ // Only reload if file was modified
67
+ if (currentMtime > this.previousMtime) {
68
+ this.previousMtime = currentMtime;
69
+ this.loadSession();
70
+ }
71
+ }
72
+ catch (e) {
73
+ // File might be temporarily unavailable during write
74
+ }
75
+ }
76
+ /**
77
+ * Load session and emit events for changes
78
+ */
79
+ loadSession() {
80
+ if (!this.sessionPath)
81
+ return;
82
+ try {
83
+ const content = readFileSync(this.sessionPath, 'utf-8');
84
+ const session = JSON.parse(content);
85
+ // Generate events from diff BEFORE updating state
86
+ const events = this.previousSession
87
+ ? this.diffSessions(this.previousSession, session)
88
+ : [];
89
+ // Update state FIRST so events can reference the new state
90
+ this.previousSession = session;
91
+ this.options.onSessionChange(session);
92
+ // Then emit events (now state.agents will have the agent)
93
+ for (const event of events) {
94
+ this.options.onEvent(event);
95
+ }
96
+ }
97
+ catch (e) {
98
+ this.options.onError(e);
99
+ }
100
+ }
101
+ /**
102
+ * Diff two sessions and generate events
103
+ */
104
+ diffSessions(prev, curr) {
105
+ const events = [];
106
+ const timestamp = Date.now();
107
+ // Map previous agents by ID
108
+ const prevAgents = new Map(prev.agents.map(a => [a.gkSessionId, a]));
109
+ for (const agent of curr.agents) {
110
+ const prevAgent = prevAgents.get(agent.gkSessionId);
111
+ if (!prevAgent) {
112
+ // New agent added
113
+ events.push(this.createEvent('received_work', agent, timestamp));
114
+ if (agent.injected?.skills?.length) {
115
+ events.push(this.createEvent('skill_activated', agent, timestamp, agent.injected.skills[0]));
116
+ }
117
+ }
118
+ else {
119
+ // Check for status changes
120
+ if (prevAgent.status === 'active' && agent.status === 'completed') {
121
+ events.push(this.createEvent('task_complete', agent, timestamp));
122
+ events.push(this.createEvent('delivering', agent, timestamp));
123
+ }
124
+ // Check for skill changes
125
+ const prevSkills = prevAgent.injected?.skills || [];
126
+ const currSkills = agent.injected?.skills || [];
127
+ const newSkills = currSkills.filter(s => !prevSkills.includes(s));
128
+ for (const skill of newSkills) {
129
+ events.push(this.createEvent('skill_activated', agent, timestamp, skill));
130
+ }
131
+ }
132
+ }
133
+ // Check for session completion
134
+ const allCompleted = curr.agents.every(a => a.status === 'completed' || a.status === 'failed');
135
+ const wasActive = prev.agents.some(a => a.status === 'active');
136
+ if (allCompleted && wasActive && curr.agents.length > 0) {
137
+ events.push({
138
+ type: 'session_complete',
139
+ agentId: curr.gkSessionId,
140
+ targetAgentId: null,
141
+ skill: null,
142
+ message: 'All tasks completed',
143
+ timestamp,
144
+ });
145
+ }
146
+ return events;
147
+ }
148
+ /**
149
+ * Create event from agent
150
+ */
151
+ createEvent(type, agent, timestamp, skill) {
152
+ const messages = {
153
+ agent_idle: 'Waiting for work',
154
+ agent_working: 'Working...',
155
+ skill_activated: `Activated skill: ${skill || 'unknown'}`,
156
+ handoff_start: 'Passing work...',
157
+ handoff_complete: 'Handoff complete!',
158
+ received_work: 'Received work',
159
+ delivering: 'Delivering results...',
160
+ task_complete: 'Task complete!',
161
+ session_complete: 'All tasks completed',
162
+ };
163
+ return {
164
+ type,
165
+ agentId: agent.gkSessionId,
166
+ targetAgentId: agent.parentGkSessionId,
167
+ skill: skill || agent.injected?.skills?.[0] || null,
168
+ message: messages[type],
169
+ timestamp,
170
+ characterType: getCharacterType(agent.agentRole || 'coder'),
171
+ };
172
+ }
173
+ }
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Icon mapping utilities for Agent Office
3
+ */
4
+ /**
5
+ * Get icon for agent role
6
+ */
7
+ export declare function getIconForRole(role: string, isOrchestrator?: boolean): string;
8
+ /**
9
+ * Format role name for display
10
+ */
11
+ export declare function formatDisplayName(role: string): string;
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Icon mapping utilities for Agent Office
3
+ */
4
+ // Icon mapping by role keywords
5
+ const ROLE_ICONS = [
6
+ [/research|search|analyze/i, '\uD83D\uDD0D'], // magnifier
7
+ [/code|implement|execute|develop/i, '\uD83D\uDCBB'], // laptop
8
+ [/plan|architect/i, '\uD83D\uDCCB'], // clipboard
9
+ [/debug|fix|troubleshoot/i, '\uD83D\uDC1B'], // bug
10
+ [/test|qa|quality/i, '\uD83E\uDDEA'], // test tube
11
+ [/design|ui|ux/i, '\uD83C\uDFA8'], // palette
12
+ [/review|audit|check/i, '\u2705'], // checkmark
13
+ [/doc|write|content/i, '\uD83D\uDCDD'], // memo
14
+ ];
15
+ const DEFAULT_ICON = '\uD83E\uDD16'; // robot
16
+ const ORCHESTRATOR_ICON = '\uD83D\uDC51'; // crown
17
+ /**
18
+ * Get icon for agent role
19
+ */
20
+ export function getIconForRole(role, isOrchestrator = false) {
21
+ if (isOrchestrator)
22
+ return ORCHESTRATOR_ICON;
23
+ for (const [pattern, icon] of ROLE_ICONS) {
24
+ if (pattern.test(role))
25
+ return icon;
26
+ }
27
+ return DEFAULT_ICON;
28
+ }
29
+ /**
30
+ * Format role name for display
31
+ */
32
+ export function formatDisplayName(role) {
33
+ return role
34
+ .replace(/-/g, ' ')
35
+ .replace(/\b\w/g, c => c.toUpperCase());
36
+ }
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Agent Office domain exports
3
+ * Gamified visualization for multi-agent workflows
4
+ */
5
+ export * from './types.js';
6
+ export { createInitialState, isValidTransition, transitionAgent, processEvent, } from './state-machine.js';
7
+ export { getIconForRole, formatDisplayName, } from './icons.js';
8
+ export { sessionToOfficeState, agentToOfficeAgent, agentToInboxItem, isOrchestrator, } from './session-bridge.js';
9
+ export { scanPlanDocuments, groupDocumentsByType, } from './documents-scanner.js';
10
+ export { OfficeEventEmitter } from './event-emitter.js';
11
+ export { SessionFileWatcher, type FileWatcherOptions } from './file-watcher.js';
12
+ export { WebDashboard, startWebDashboard, type WebDashboardOptions } from './renderer/web.js';
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Agent Office domain exports
3
+ * Gamified visualization for multi-agent workflows
4
+ */
5
+ // Types
6
+ export * from './types.js';
7
+ // State machine
8
+ export { createInitialState, isValidTransition, transitionAgent, processEvent, } from './state-machine.js';
9
+ // Utilities
10
+ export { getIconForRole, formatDisplayName, } from './icons.js';
11
+ // Session bridge
12
+ export { sessionToOfficeState, agentToOfficeAgent, agentToInboxItem, isOrchestrator, } from './session-bridge.js';
13
+ // Documents
14
+ export { scanPlanDocuments, groupDocumentsByType, } from './documents-scanner.js';
15
+ // Event system
16
+ export { OfficeEventEmitter } from './event-emitter.js';
17
+ // File watcher
18
+ export { SessionFileWatcher } from './file-watcher.js';
19
+ // Renderers
20
+ export { WebDashboard, startWebDashboard } from './renderer/web.js';
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Agent Office Gamified UI Assets
3
+ * Auto-generated by build.js - DO NOT EDIT MANUALLY
4
+ *
5
+ * To modify, edit the source files in:
6
+ * - src/core/ (base HTML, CSS, JS)
7
+ * - integrate-to-assets.js (gamified CSS, HTML, character JS)
8
+ *
9
+ * Then run: node build.js
10
+ */
11
+ export declare function getIndexHtml(): string;