cognitive-workflow-mcp 1.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.
- package/.workflow/bucket-builder.html +823 -0
- package/.workflow/bucket-discoverer.html +928 -0
- package/.workflow/bucket-experimenter.html +802 -0
- package/.workflow/bucket-operator.html +951 -0
- package/.workflow/bucket-strategist.html +970 -0
- package/.workflow/bucket-teacher.html +873 -0
- package/.workflow/cognitive-dashboard-enhanced.html +731 -0
- package/.workflow/cognitive-dashboard.html +296 -0
- package/LICENSE +21 -0
- package/README.md +317 -0
- package/bin/cli.js +286 -0
- package/package.json +57 -0
- package/src/analyzer.js +194 -0
- package/src/configurator.js +63 -0
- package/src/generator.js +65 -0
- package/src/index.js +233 -0
- package/src/insights.js +127 -0
- package/src/templates.js +396 -0
package/bin/cli.js
ADDED
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { Command } from 'commander';
|
|
4
|
+
import { analyzeCommits } from '../src/analyzer.js';
|
|
5
|
+
import { generateDashboard } from '../src/generator.js';
|
|
6
|
+
import { configureTerminals } from '../src/configurator.js';
|
|
7
|
+
import { getInsights } from '../src/insights.js';
|
|
8
|
+
import { readFile, access, copyFile, mkdir } from 'fs/promises';
|
|
9
|
+
import { fileURLToPath } from 'url';
|
|
10
|
+
import { dirname, join } from 'path';
|
|
11
|
+
import { execSync } from 'child_process';
|
|
12
|
+
import { existsSync } from 'fs';
|
|
13
|
+
|
|
14
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
15
|
+
const __dirname = dirname(__filename);
|
|
16
|
+
const packageJson = JSON.parse(
|
|
17
|
+
await readFile(join(__dirname, '../package.json'), 'utf-8')
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
// ============================================================================
|
|
21
|
+
// POSTINSTALL: Show welcome + open dashboard
|
|
22
|
+
// ============================================================================
|
|
23
|
+
if (process.argv.includes('--postinstall')) {
|
|
24
|
+
const colors = {
|
|
25
|
+
reset: '\x1b[0m',
|
|
26
|
+
bold: '\x1b[1m',
|
|
27
|
+
blue: '\x1b[34m',
|
|
28
|
+
green: '\x1b[32m',
|
|
29
|
+
yellow: '\x1b[33m',
|
|
30
|
+
purple: '\x1b[35m',
|
|
31
|
+
cyan: '\x1b[36m',
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
console.log(`
|
|
35
|
+
${colors.purple}${colors.bold}
|
|
36
|
+
╔════════════════════════════════════════════════════════════╗
|
|
37
|
+
║ ║
|
|
38
|
+
║ ████████╗██╗ ██╗███████╗████████╗ █████╗ ██████╗ ██████╗ ║
|
|
39
|
+
║ ╚══██╔══╝██║ ██║██╔════╝╚══██╔══╝██╔══██╗██╔════╝██╔═══██╗ ║
|
|
40
|
+
║ ██║ ███████║█████╗ ██║ ███████║██║ ██║ ██║ ║
|
|
41
|
+
║ ██║ ██╔══██║██╔══╝ ██║ ██╔══██║██║ ██║ ██║ ║
|
|
42
|
+
║ ██║ ██║ ██║███████╗ ██║ ██║ ██║╚██████╗╚██████╔╝ ║
|
|
43
|
+
║ ╚═╝ ╚═╝ ╚═╝╚══════╝ ╚═╝ ╚═╝ ╚═╝ ╚═════╝ ╚═════╝ ║
|
|
44
|
+
║ ║
|
|
45
|
+
║ Transform Your Terminal Into a Mental Palace ║
|
|
46
|
+
╚════════════════════════════════════════════════════════════╝
|
|
47
|
+
${colors.reset}
|
|
48
|
+
${colors.green}✓ Cognitive Workflow MCP installed successfully!${colors.reset}
|
|
49
|
+
|
|
50
|
+
${colors.bold}The 6 Cognitive Identities:${colors.reset}
|
|
51
|
+
|
|
52
|
+
${colors.blue}🔨 Builder${colors.reset} feat, fix, refactor → iTerm2
|
|
53
|
+
${colors.yellow}🔍 Discoverer${colors.reset} docs, research → WezTerm
|
|
54
|
+
${colors.green}⚙️ Operator${colors.reset} chore, deploy, ops → Kitty
|
|
55
|
+
${colors.cyan}📚 Teacher${colors.reset} blog, content → Terminal
|
|
56
|
+
${colors.purple}🎯 Strategist${colors.reset} strategy, planning → VS Code
|
|
57
|
+
${colors.purple}🧪 Experimenter${colors.reset} experiment, poc → Cursor
|
|
58
|
+
|
|
59
|
+
${colors.bold}Quick Start:${colors.reset}
|
|
60
|
+
|
|
61
|
+
${colors.cyan}cognitive-workflow analyze${colors.reset} Analyze your git commits
|
|
62
|
+
${colors.cyan}cognitive-workflow generate${colors.reset} Generate dashboard HTML
|
|
63
|
+
${colors.cyan}cognitive-workflow dashboard${colors.reset} Open included dashboard
|
|
64
|
+
|
|
65
|
+
${colors.bold}The Principle:${colors.reset} Tool = Identity = Mindset
|
|
66
|
+
|
|
67
|
+
${colors.bold}Learn more:${colors.reset} ${colors.cyan}https://thetacoach.biz/thetacog${colors.reset}
|
|
68
|
+
`);
|
|
69
|
+
|
|
70
|
+
// Copy bundled dashboard to .workflow if it doesn't exist
|
|
71
|
+
const bundledDashboard = join(__dirname, '../.workflow/cognitive-dashboard-enhanced.html');
|
|
72
|
+
const localWorkflow = join(process.cwd(), '.workflow');
|
|
73
|
+
const localDashboard = join(localWorkflow, 'cognitive-dashboard.html');
|
|
74
|
+
|
|
75
|
+
if (existsSync(bundledDashboard) && !existsSync(localDashboard)) {
|
|
76
|
+
try {
|
|
77
|
+
await mkdir(localWorkflow, { recursive: true });
|
|
78
|
+
await copyFile(bundledDashboard, localDashboard);
|
|
79
|
+
console.log(`${colors.green}✓ Dashboard copied to .workflow/cognitive-dashboard.html${colors.reset}\n`);
|
|
80
|
+
} catch (e) {
|
|
81
|
+
// Silent fail - user can run generate command
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
process.exit(0);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const program = new Command();
|
|
89
|
+
|
|
90
|
+
program
|
|
91
|
+
.name('cognitive-workflow')
|
|
92
|
+
.description('Manage identity-based cognitive workflows with terminal integration')
|
|
93
|
+
.version(packageJson.version);
|
|
94
|
+
|
|
95
|
+
program
|
|
96
|
+
.command('analyze')
|
|
97
|
+
.description('Analyze git commit history and categorize by cognitive identity')
|
|
98
|
+
.option('-d, --days <number>', 'Days of history to analyze', '60')
|
|
99
|
+
.option('-o, --output <path>', 'Save results to file')
|
|
100
|
+
.action(async (options) => {
|
|
101
|
+
console.log('🔍 Analyzing commit history...\n');
|
|
102
|
+
|
|
103
|
+
const analysis = await analyzeCommits(parseInt(options.days));
|
|
104
|
+
|
|
105
|
+
console.log(`📊 Total commits: ${analysis.totalCommits}`);
|
|
106
|
+
console.log(`📅 Period: ${analysis.daysAnalyzed} days\n`);
|
|
107
|
+
|
|
108
|
+
console.log('🎯 Cognitive Identity Distribution:\n');
|
|
109
|
+
for (const [identity, data] of Object.entries(analysis.identities)) {
|
|
110
|
+
const emoji = getIdentityEmoji(identity);
|
|
111
|
+
console.log(`${emoji} ${capitalize(identity).padEnd(15)} ${data.commits} commits (${data.percentage}%)`);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
console.log('\n💡 Insights:\n');
|
|
115
|
+
for (const insight of analysis.insights) {
|
|
116
|
+
console.log(` • ${insight.message}`);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (options.output) {
|
|
120
|
+
const { writeFile } = await import('fs/promises');
|
|
121
|
+
await writeFile(options.output, JSON.stringify(analysis, null, 2));
|
|
122
|
+
console.log(`\n✅ Results saved to ${options.output}`);
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
program
|
|
127
|
+
.command('generate')
|
|
128
|
+
.description('Generate HTML dashboard and room pages')
|
|
129
|
+
.option('-o, --output <directory>', 'Output directory', '.workflow')
|
|
130
|
+
.option('--no-open', 'Do not open dashboard after generation')
|
|
131
|
+
.action(async (options) => {
|
|
132
|
+
console.log('🎨 Generating cognitive workflow dashboard...\n');
|
|
133
|
+
|
|
134
|
+
const result = await generateDashboard(options.output, options.open);
|
|
135
|
+
|
|
136
|
+
console.log('✅ Generated files:');
|
|
137
|
+
for (const file of result.filesCreated) {
|
|
138
|
+
console.log(` • ${file}`);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
if (options.open) {
|
|
142
|
+
console.log(`\n🌐 Opening dashboard: ${result.dashboardUrl}`);
|
|
143
|
+
}
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
program
|
|
147
|
+
.command('configure')
|
|
148
|
+
.description('Configure terminal application mappings')
|
|
149
|
+
.action(async () => {
|
|
150
|
+
console.log('⚙️ Configuring terminal mappings...\n');
|
|
151
|
+
|
|
152
|
+
// Interactive configuration (would use inquirer or prompts)
|
|
153
|
+
console.log('This would open an interactive configurator.');
|
|
154
|
+
console.log('For now, edit .cognitive-workflow.json manually.');
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
program
|
|
158
|
+
.command('insights')
|
|
159
|
+
.description('Get insights about your cognitive work patterns')
|
|
160
|
+
.action(async () => {
|
|
161
|
+
console.log('💡 Analyzing work patterns...\n');
|
|
162
|
+
|
|
163
|
+
const insights = await getInsights(true);
|
|
164
|
+
|
|
165
|
+
console.log(`🎯 Dominant Identity: ${capitalize(insights.dominantIdentity)}`);
|
|
166
|
+
console.log(`⚖️ Balance Score: ${(insights.balanceScore * 100).toFixed(0)}%\n`);
|
|
167
|
+
|
|
168
|
+
if (insights.recommendations.length > 0) {
|
|
169
|
+
console.log('📋 Recommendations:\n');
|
|
170
|
+
for (const rec of insights.recommendations) {
|
|
171
|
+
console.log(` • ${rec}`);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
program
|
|
177
|
+
.command('open')
|
|
178
|
+
.description('Open the cognitive workflow dashboard (local .workflow folder)')
|
|
179
|
+
.action(async () => {
|
|
180
|
+
const dashboardPath = join(process.cwd(), '.workflow', 'cognitive-dashboard.html');
|
|
181
|
+
openFile(dashboardPath);
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
program
|
|
185
|
+
.command('dashboard')
|
|
186
|
+
.description('Open the bundled cognitive dashboard (included with package)')
|
|
187
|
+
.option('-r, --room <name>', 'Open specific room (builder, discoverer, operator, teacher, strategist, experimenter)')
|
|
188
|
+
.action(async (options) => {
|
|
189
|
+
const workflowDir = join(__dirname, '../.workflow');
|
|
190
|
+
|
|
191
|
+
let file = 'cognitive-dashboard-enhanced.html';
|
|
192
|
+
if (options.room) {
|
|
193
|
+
const room = options.room.toLowerCase();
|
|
194
|
+
const validRooms = ['builder', 'discoverer', 'operator', 'teacher', 'strategist', 'experimenter'];
|
|
195
|
+
if (validRooms.includes(room)) {
|
|
196
|
+
file = `bucket-${room}.html`;
|
|
197
|
+
} else {
|
|
198
|
+
console.error(`❌ Unknown room: ${room}`);
|
|
199
|
+
console.log(` Valid rooms: ${validRooms.join(', ')}`);
|
|
200
|
+
process.exit(1);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
const dashboardPath = join(workflowDir, file);
|
|
205
|
+
|
|
206
|
+
if (!existsSync(dashboardPath)) {
|
|
207
|
+
console.error(`❌ Dashboard not found: ${dashboardPath}`);
|
|
208
|
+
console.log(' Opening web version instead...');
|
|
209
|
+
openFile('https://thetacoach.biz/thetacog');
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
console.log(`🌐 Opening ${options.room ? `${options.room} room` : 'dashboard'}...`);
|
|
214
|
+
openFile(dashboardPath);
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
program
|
|
218
|
+
.command('rooms')
|
|
219
|
+
.description('List all cognitive rooms/identities')
|
|
220
|
+
.action(() => {
|
|
221
|
+
console.log(`
|
|
222
|
+
🏗️ The 6 Cognitive Identities (Rooms)
|
|
223
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
224
|
+
|
|
225
|
+
🔨 Builder "I am building. I am shipping. I am solving."
|
|
226
|
+
Triggers: feat, fix, refactor, perf
|
|
227
|
+
Terminal: iTerm2
|
|
228
|
+
|
|
229
|
+
🔍 Discoverer "I am exploring. I am researching. I am learning."
|
|
230
|
+
Triggers: docs, research, analysis
|
|
231
|
+
Terminal: WezTerm
|
|
232
|
+
|
|
233
|
+
⚙️ Operator "I am executing. I am deploying. I am maintaining."
|
|
234
|
+
Triggers: chore, deploy, ops
|
|
235
|
+
Terminal: Kitty
|
|
236
|
+
|
|
237
|
+
📚 Teacher "I am explaining. I am teaching. I am sharing."
|
|
238
|
+
Triggers: blog, content, tutorial
|
|
239
|
+
Terminal: Terminal.app
|
|
240
|
+
|
|
241
|
+
🎯 Strategist "I am positioning. I am planning. I am deciding."
|
|
242
|
+
Triggers: strategy, planning
|
|
243
|
+
Terminal: VS Code
|
|
244
|
+
|
|
245
|
+
🧪 Experimenter "I am testing. I am prototyping. I am breaking."
|
|
246
|
+
Triggers: experiment, prototype, poc
|
|
247
|
+
Terminal: Cursor
|
|
248
|
+
|
|
249
|
+
Open a specific room:
|
|
250
|
+
cognitive-workflow dashboard --room builder
|
|
251
|
+
cognitive-workflow dashboard --room discoverer
|
|
252
|
+
`);
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
program.parse();
|
|
256
|
+
|
|
257
|
+
function openFile(path) {
|
|
258
|
+
try {
|
|
259
|
+
const platform = process.platform;
|
|
260
|
+
if (platform === 'darwin') {
|
|
261
|
+
execSync(`open "${path}"`);
|
|
262
|
+
} else if (platform === 'win32') {
|
|
263
|
+
execSync(`start "" "${path}"`);
|
|
264
|
+
} else {
|
|
265
|
+
execSync(`xdg-open "${path}" || sensible-browser "${path}"`);
|
|
266
|
+
}
|
|
267
|
+
} catch (error) {
|
|
268
|
+
console.log(`Open this URL in your browser:\n ${path}`);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
function getIdentityEmoji(identity) {
|
|
273
|
+
const emojis = {
|
|
274
|
+
builder: '🏗️',
|
|
275
|
+
discoverer: '🔬',
|
|
276
|
+
operator: '🎩',
|
|
277
|
+
teacher: '📣',
|
|
278
|
+
strategist: '🗺️',
|
|
279
|
+
experimenter: '🧪'
|
|
280
|
+
};
|
|
281
|
+
return emojis[identity] || '📌';
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
function capitalize(str) {
|
|
285
|
+
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
286
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "cognitive-workflow-mcp",
|
|
3
|
+
"version": "1.1.0",
|
|
4
|
+
"description": "Transform your terminal into a mental palace. Cognitive workspaces that ground your workflow through git commit analysis. Tool = Identity = Mindset.",
|
|
5
|
+
"main": "src/index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"cognitive-workflow": "bin/cli.js",
|
|
8
|
+
"thetacog": "bin/cli.js"
|
|
9
|
+
},
|
|
10
|
+
"type": "module",
|
|
11
|
+
"files": [
|
|
12
|
+
"src/",
|
|
13
|
+
"bin/",
|
|
14
|
+
".workflow/",
|
|
15
|
+
"README.md"
|
|
16
|
+
],
|
|
17
|
+
"scripts": {
|
|
18
|
+
"start": "node src/index.js",
|
|
19
|
+
"cli": "node bin/cli.js",
|
|
20
|
+
"dashboard": "node bin/cli.js dashboard",
|
|
21
|
+
"postinstall": "node bin/cli.js --postinstall",
|
|
22
|
+
"test": "node --test src/**/*.test.js"
|
|
23
|
+
},
|
|
24
|
+
"keywords": [
|
|
25
|
+
"mcp",
|
|
26
|
+
"cognitive-workflow",
|
|
27
|
+
"terminal",
|
|
28
|
+
"identity",
|
|
29
|
+
"mental-palace",
|
|
30
|
+
"productivity",
|
|
31
|
+
"git-analysis",
|
|
32
|
+
"commit-categorization",
|
|
33
|
+
"thetacog",
|
|
34
|
+
"thetacoach",
|
|
35
|
+
"fim",
|
|
36
|
+
"claude"
|
|
37
|
+
],
|
|
38
|
+
"author": "ThetaDriven <elias@thetadriven.com>",
|
|
39
|
+
"license": "MIT",
|
|
40
|
+
"homepage": "https://thetacoach.biz/thetacog",
|
|
41
|
+
"dependencies": {
|
|
42
|
+
"@modelcontextprotocol/sdk": "^0.5.0",
|
|
43
|
+
"simple-git": "^3.25.0",
|
|
44
|
+
"commander": "^12.0.0"
|
|
45
|
+
},
|
|
46
|
+
"repository": {
|
|
47
|
+
"type": "git",
|
|
48
|
+
"url": "https://github.com/wiber/thetadrivencoach.git",
|
|
49
|
+
"directory": "packages/cognitive-workflow-mcp"
|
|
50
|
+
},
|
|
51
|
+
"publishConfig": {
|
|
52
|
+
"access": "public"
|
|
53
|
+
},
|
|
54
|
+
"engines": {
|
|
55
|
+
"node": ">=18.0.0"
|
|
56
|
+
}
|
|
57
|
+
}
|
package/src/analyzer.js
ADDED
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
import simpleGit from 'simple-git';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Identity patterns for commit categorization
|
|
5
|
+
*/
|
|
6
|
+
const IDENTITY_PATTERNS = {
|
|
7
|
+
builder: {
|
|
8
|
+
keywords: ['feat', 'fix', 'refactor', 'perf', 'build', 'style', 'ui', 'component'],
|
|
9
|
+
paths: ['src/', 'lib/', 'components/', 'pages/', 'app/'],
|
|
10
|
+
description: 'Hands-on implementation and feature building'
|
|
11
|
+
},
|
|
12
|
+
discoverer: {
|
|
13
|
+
keywords: ['docs', 'research', 'analysis', 'study', 'investigate', 'explore', 'book'],
|
|
14
|
+
paths: ['docs/', 'research/', 'notes/', 'books/', 'papers/'],
|
|
15
|
+
description: 'Deep research and first principles thinking'
|
|
16
|
+
},
|
|
17
|
+
operator: {
|
|
18
|
+
keywords: ['chore', 'deploy', 'ops', 'ci', 'cd', 'release', 'config', 'env'],
|
|
19
|
+
paths: ['config/', '.github/', 'scripts/', 'deploy/'],
|
|
20
|
+
description: 'Operations, deployment, and business management'
|
|
21
|
+
},
|
|
22
|
+
teacher: {
|
|
23
|
+
keywords: ['blog', 'content', 'tutorial', 'guide', 'example', 'demo', 'explain'],
|
|
24
|
+
paths: ['blog/', 'content/', 'examples/', 'tutorials/'],
|
|
25
|
+
description: 'Teaching, content creation, and public communication'
|
|
26
|
+
},
|
|
27
|
+
strategist: {
|
|
28
|
+
keywords: ['strategy', 'planning', 'positioning', 'marketing', 'competitor', 'market'],
|
|
29
|
+
paths: ['strategy/', 'planning/', 'marketing/'],
|
|
30
|
+
description: 'Strategic planning and competitive analysis'
|
|
31
|
+
},
|
|
32
|
+
experimenter: {
|
|
33
|
+
keywords: ['experiment', 'prototype', 'poc', 'test', 'try', 'spike', 'wip'],
|
|
34
|
+
paths: ['experiments/', 'prototypes/', 'playground/'],
|
|
35
|
+
description: 'Rapid prototyping and hypothesis testing'
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Analyzes git commit history and categorizes commits by cognitive identity
|
|
41
|
+
*
|
|
42
|
+
* @param {number} daysBack - Number of days of history to analyze
|
|
43
|
+
* @returns {Promise<Object>} Analysis results with commit categorization
|
|
44
|
+
*/
|
|
45
|
+
export async function analyzeCommits(daysBack = 60) {
|
|
46
|
+
const git = simpleGit();
|
|
47
|
+
|
|
48
|
+
// Get commit history
|
|
49
|
+
const since = new Date(Date.now() - daysBack * 24 * 60 * 60 * 1000).toISOString();
|
|
50
|
+
const log = await git.log(['--since', since, '--all']);
|
|
51
|
+
|
|
52
|
+
const commits = log.all;
|
|
53
|
+
const totalCommits = commits.length;
|
|
54
|
+
|
|
55
|
+
// Categorize commits
|
|
56
|
+
const categorized = {
|
|
57
|
+
builder: [],
|
|
58
|
+
discoverer: [],
|
|
59
|
+
operator: [],
|
|
60
|
+
teacher: [],
|
|
61
|
+
strategist: [],
|
|
62
|
+
experimenter: []
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
for (const commit of commits) {
|
|
66
|
+
const message = commit.message.toLowerCase();
|
|
67
|
+
const identity = categorizeCommit(commit, message);
|
|
68
|
+
categorized[identity].push(commit);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Calculate statistics
|
|
72
|
+
const identities = {};
|
|
73
|
+
for (const [identity, commits] of Object.entries(categorized)) {
|
|
74
|
+
const count = commits.length;
|
|
75
|
+
const percentage = Math.round((count / totalCommits) * 100);
|
|
76
|
+
const commitsPerDay = (count / daysBack).toFixed(2);
|
|
77
|
+
|
|
78
|
+
identities[identity] = {
|
|
79
|
+
commits: count,
|
|
80
|
+
percentage,
|
|
81
|
+
commitsPerDay: parseFloat(commitsPerDay),
|
|
82
|
+
description: IDENTITY_PATTERNS[identity].description,
|
|
83
|
+
recentCommits: commits.slice(0, 10).map(c => ({
|
|
84
|
+
hash: c.hash.substring(0, 8),
|
|
85
|
+
message: c.message,
|
|
86
|
+
date: c.date,
|
|
87
|
+
author: c.author_name
|
|
88
|
+
}))
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Sort identities by commit count
|
|
93
|
+
const sortedIdentities = Object.entries(identities)
|
|
94
|
+
.sort((a, b) => b[1].commits - a[1].commits)
|
|
95
|
+
.reduce((acc, [key, value]) => {
|
|
96
|
+
acc[key] = value;
|
|
97
|
+
return acc;
|
|
98
|
+
}, {});
|
|
99
|
+
|
|
100
|
+
// Generate insights
|
|
101
|
+
const insights = generateInsights(sortedIdentities, totalCommits);
|
|
102
|
+
|
|
103
|
+
return {
|
|
104
|
+
totalCommits,
|
|
105
|
+
daysAnalyzed: daysBack,
|
|
106
|
+
identities: sortedIdentities,
|
|
107
|
+
insights,
|
|
108
|
+
generatedAt: new Date().toISOString()
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Categorizes a single commit into a cognitive identity
|
|
114
|
+
*/
|
|
115
|
+
function categorizeCommit(commit, messageLower) {
|
|
116
|
+
// Check each identity's patterns
|
|
117
|
+
for (const [identity, patterns] of Object.entries(IDENTITY_PATTERNS)) {
|
|
118
|
+
// Check keywords in message
|
|
119
|
+
const keywordMatch = patterns.keywords.some(kw => messageLower.includes(kw));
|
|
120
|
+
|
|
121
|
+
// Get changed files for this commit (if available)
|
|
122
|
+
const files = commit.diff?.files || [];
|
|
123
|
+
const pathMatch = files.some(file =>
|
|
124
|
+
patterns.paths.some(path => file.file.includes(path))
|
|
125
|
+
);
|
|
126
|
+
|
|
127
|
+
if (keywordMatch || pathMatch) {
|
|
128
|
+
return identity;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Default to builder if no clear match
|
|
133
|
+
return 'builder';
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Generates insights about work patterns
|
|
138
|
+
*/
|
|
139
|
+
function generateInsights(identities, totalCommits) {
|
|
140
|
+
const insights = [];
|
|
141
|
+
|
|
142
|
+
// Dominant identity
|
|
143
|
+
const dominant = Object.entries(identities)[0];
|
|
144
|
+
if (dominant) {
|
|
145
|
+
insights.push({
|
|
146
|
+
type: 'dominant',
|
|
147
|
+
message: `${capitalize(dominant[0])} is your dominant mode (${dominant[1].percentage}%)`,
|
|
148
|
+
identity: dominant[0]
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Balance score (how evenly distributed)
|
|
153
|
+
const percentages = Object.values(identities).map(i => i.percentage);
|
|
154
|
+
const idealPercentage = 100 / 6; // 16.67% if perfectly balanced
|
|
155
|
+
const balanceScore = 1 - (percentages.reduce((sum, p) => sum + Math.abs(p - idealPercentage), 0) / 100);
|
|
156
|
+
|
|
157
|
+
insights.push({
|
|
158
|
+
type: 'balance',
|
|
159
|
+
message: `Work balance score: ${(balanceScore * 100).toFixed(0)}%`,
|
|
160
|
+
score: balanceScore
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
// Underutilized identities
|
|
164
|
+
const underutilized = Object.entries(identities)
|
|
165
|
+
.filter(([_, data]) => data.percentage < 5)
|
|
166
|
+
.map(([identity]) => identity);
|
|
167
|
+
|
|
168
|
+
if (underutilized.length > 0) {
|
|
169
|
+
insights.push({
|
|
170
|
+
type: 'underutilized',
|
|
171
|
+
message: `Consider more ${underutilized.map(capitalize).join(', ')} time`,
|
|
172
|
+
identities: underutilized
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Productivity patterns
|
|
177
|
+
const highActivity = Object.entries(identities)
|
|
178
|
+
.filter(([_, data]) => data.commitsPerDay > 1)
|
|
179
|
+
.map(([identity]) => identity);
|
|
180
|
+
|
|
181
|
+
if (highActivity.length > 0) {
|
|
182
|
+
insights.push({
|
|
183
|
+
type: 'high-activity',
|
|
184
|
+
message: `High activity in ${highActivity.map(capitalize).join(', ')}`,
|
|
185
|
+
identities: highActivity
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
return insights;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
function capitalize(str) {
|
|
193
|
+
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
194
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { readFile, writeFile } from 'fs/promises';
|
|
2
|
+
import { join } from 'path';
|
|
3
|
+
|
|
4
|
+
const CONFIG_FILE = '.cognitive-workflow.json';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Configures terminal application mappings
|
|
8
|
+
*
|
|
9
|
+
* @param {Object} mappings - Terminal assignments per identity
|
|
10
|
+
* @returns {Promise<Object>} Configuration result
|
|
11
|
+
*/
|
|
12
|
+
export async function configureTerminals(mappings) {
|
|
13
|
+
const configPath = join(process.cwd(), CONFIG_FILE);
|
|
14
|
+
|
|
15
|
+
let config = {};
|
|
16
|
+
|
|
17
|
+
// Load existing config if it exists
|
|
18
|
+
try {
|
|
19
|
+
const existing = await readFile(configPath, 'utf-8');
|
|
20
|
+
config = JSON.parse(existing);
|
|
21
|
+
} catch (error) {
|
|
22
|
+
// File doesn't exist, start fresh
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Update terminal mappings
|
|
26
|
+
config.terminals = {
|
|
27
|
+
...config.terminals,
|
|
28
|
+
...mappings
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
// Save config
|
|
32
|
+
await writeFile(configPath, JSON.stringify(config, null, 2));
|
|
33
|
+
|
|
34
|
+
return {
|
|
35
|
+
configured: true,
|
|
36
|
+
terminals: config.terminals,
|
|
37
|
+
configPath
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Gets current terminal configuration
|
|
43
|
+
*/
|
|
44
|
+
export async function getTerminalConfig() {
|
|
45
|
+
const configPath = join(process.cwd(), CONFIG_FILE);
|
|
46
|
+
|
|
47
|
+
try {
|
|
48
|
+
const config = await readFile(configPath, 'utf-8');
|
|
49
|
+
return JSON.parse(config);
|
|
50
|
+
} catch (error) {
|
|
51
|
+
// Return defaults
|
|
52
|
+
return {
|
|
53
|
+
terminals: {
|
|
54
|
+
builder: 'iTerm2',
|
|
55
|
+
discoverer: 'WezTerm',
|
|
56
|
+
operator: 'Kitty',
|
|
57
|
+
teacher: 'Terminal',
|
|
58
|
+
strategist: 'VSCode',
|
|
59
|
+
experimenter: 'Cursor'
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
}
|
package/src/generator.js
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { mkdir, writeFile } from 'fs/promises';
|
|
2
|
+
import { join } from 'path';
|
|
3
|
+
import { exec } from 'child_process';
|
|
4
|
+
import { promisify } from 'util';
|
|
5
|
+
import { analyzeCommits } from './analyzer.js';
|
|
6
|
+
import { getDashboardTemplate, getRoomTemplate } from './templates.js';
|
|
7
|
+
|
|
8
|
+
const execAsync = promisify(exec);
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Generates HTML dashboard and room pages
|
|
12
|
+
*
|
|
13
|
+
* @param {string} outputDir - Output directory path
|
|
14
|
+
* @param {boolean} openAfter - Whether to open dashboard after generation
|
|
15
|
+
* @returns {Promise<Object>} Generation results
|
|
16
|
+
*/
|
|
17
|
+
export async function generateDashboard(outputDir = '.workflow', openAfter = true) {
|
|
18
|
+
// Ensure output directory exists
|
|
19
|
+
await mkdir(outputDir, { recursive: true });
|
|
20
|
+
|
|
21
|
+
// Analyze commits
|
|
22
|
+
const analysis = await analyzeCommits(60);
|
|
23
|
+
|
|
24
|
+
// Generate central dashboard
|
|
25
|
+
const dashboardHtml = getDashboardTemplate(analysis);
|
|
26
|
+
const dashboardPath = join(outputDir, 'cognitive-dashboard.html');
|
|
27
|
+
await writeFile(dashboardPath, dashboardHtml);
|
|
28
|
+
|
|
29
|
+
const filesCreated = [dashboardPath];
|
|
30
|
+
|
|
31
|
+
// Generate room pages for each identity
|
|
32
|
+
const identities = ['builder', 'discoverer', 'operator', 'teacher', 'strategist', 'experimenter'];
|
|
33
|
+
|
|
34
|
+
for (const identity of identities) {
|
|
35
|
+
const identityData = analysis.identities[identity];
|
|
36
|
+
const roomHtml = getRoomTemplate(identity, identityData, analysis.totalCommits);
|
|
37
|
+
const roomPath = join(outputDir, `bucket-${identity}.html`);
|
|
38
|
+
await writeFile(roomPath, roomHtml);
|
|
39
|
+
filesCreated.push(roomPath);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const result = {
|
|
43
|
+
filesCreated,
|
|
44
|
+
dashboardUrl: `file://${join(process.cwd(), dashboardPath)}`,
|
|
45
|
+
analysis: {
|
|
46
|
+
totalCommits: analysis.totalCommits,
|
|
47
|
+
identities: Object.keys(analysis.identities).map(id => ({
|
|
48
|
+
name: id,
|
|
49
|
+
commits: analysis.identities[id].commits,
|
|
50
|
+
percentage: analysis.identities[id].percentage
|
|
51
|
+
}))
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
// Open dashboard if requested
|
|
56
|
+
if (openAfter) {
|
|
57
|
+
try {
|
|
58
|
+
await execAsync(`open "${join(process.cwd(), dashboardPath)}"`);
|
|
59
|
+
} catch (error) {
|
|
60
|
+
console.error('Could not open dashboard automatically:', error.message);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return result;
|
|
65
|
+
}
|