186 lines
5.2 KiB
JavaScript
186 lines
5.2 KiB
JavaScript
#!/usr/bin/env node
|
||
/**
|
||
* SessionStart Hook: Display project status (cross-platform version)
|
||
*
|
||
* Event: SessionStart
|
||
* Function: Display project status, Git info, todos, plugins, and commands at session start
|
||
*/
|
||
|
||
const path = require('path');
|
||
const os = require('os');
|
||
const fs = require('fs');
|
||
|
||
// Import shared utility library
|
||
const common = require('./hook-common');
|
||
|
||
// Import package manager detection
|
||
const { getPackageManager, getSelectionPrompt } = require('../scripts/lib/package-manager');
|
||
|
||
// Read stdin input
|
||
let input = {};
|
||
try {
|
||
const stdinData = require('fs').readFileSync(0, 'utf8');
|
||
if (stdinData.trim()) {
|
||
input = JSON.parse(stdinData);
|
||
}
|
||
} catch {
|
||
// Use default empty object
|
||
}
|
||
|
||
const cwd = input.cwd || process.cwd();
|
||
const projectName = path.basename(cwd);
|
||
const homeDir = os.homedir();
|
||
const binding = common.getProjectMemoryBinding(cwd);
|
||
const researchCandidate = common.detectResearchProject(cwd);
|
||
|
||
// Build output
|
||
let output = '';
|
||
|
||
// Session start info
|
||
output += `🚀 ${projectName} Session started\n`;
|
||
output += `▸ Time: ${common.formatDateTime()}\n`;
|
||
output += `▸ Directory: ${cwd}\n\n`;
|
||
|
||
// Git status
|
||
const gitInfo = common.getGitInfo(cwd);
|
||
|
||
if (gitInfo.is_repo) {
|
||
output += `▸ Git branch: ${gitInfo.branch}\n\n`;
|
||
|
||
if (gitInfo.has_changes) {
|
||
output += `⚠️ Uncommitted changes (${gitInfo.changes_count} files):\n`;
|
||
|
||
// Show change list (up to 5)
|
||
const statusIcons = {
|
||
'M': '📝', // Modified
|
||
'A': '➕', // Added
|
||
'D': '❌', // Deleted
|
||
'R': '🔄', // Renamed
|
||
'??': '❓' // Untracked
|
||
};
|
||
|
||
for (let i = 0; i < Math.min(gitInfo.changes.length, 5); i++) {
|
||
const change = gitInfo.changes[i];
|
||
const status = change.substring(0, 2).trim();
|
||
const file = change.substring(3).trim();
|
||
|
||
const icon = statusIcons[status] || '•';
|
||
output += ` ${icon} ${file}\n`;
|
||
}
|
||
|
||
if (gitInfo.changes_count > 5) {
|
||
output += ` ... (${gitInfo.changes_count - 5} more files)\n`;
|
||
}
|
||
} else {
|
||
output += `✅ Working directory clean\n`;
|
||
}
|
||
output += '\n';
|
||
} else {
|
||
output += `▸ Git: Not a repository\n\n`;
|
||
}
|
||
|
||
if (binding.bound) {
|
||
output += '🧠 Obsidian project KB: bound\n';
|
||
output += ` - Project: ${binding.projectId || 'unknown'}\n`;
|
||
output += ` - Status: ${binding.status || 'unknown'}\n`;
|
||
output += ` - Auto-sync: ${binding.autoSync ? 'on' : 'off'}\n`;
|
||
if (binding.vaultRoot) {
|
||
output += ` - Vault root: ${binding.vaultRoot}\n`;
|
||
}
|
||
output += ' - Suggested commands: /kb-status, /kb-sync, /kb-lint\n\n';
|
||
} else if (researchCandidate.candidate) {
|
||
output += '🧠 Obsidian project KB: research repo candidate\n';
|
||
output += ` - Detected markers: ${researchCandidate.markers.join(', ')}\n`;
|
||
output += ' - Suggested command: /kb-init\n\n';
|
||
}
|
||
|
||
// Package manager detection
|
||
try {
|
||
const pm = getPackageManager();
|
||
output += `📦 Package manager: ${pm.name} (${pm.source})\n`;
|
||
|
||
// If detected via fallback, suggest setup
|
||
if (pm.source === 'fallback' || pm.source === 'default') {
|
||
output += `💡 Run /setup-pm to configure preferred package manager\n`;
|
||
}
|
||
} catch (err) {
|
||
// Package manager detection failed, silently ignore
|
||
}
|
||
|
||
output += '\n';
|
||
|
||
// Todos
|
||
output += `📋 Todos:\n`;
|
||
const todoInfo = common.getTodoInfo(cwd);
|
||
|
||
if (todoInfo.found) {
|
||
output += ` - ${todoInfo.pending} pending / ${todoInfo.done} completed\n`;
|
||
|
||
// Show top 5 pending items
|
||
if (fs.existsSync(todoInfo.path)) {
|
||
try {
|
||
const content = fs.readFileSync(todoInfo.path, 'utf8');
|
||
const pendingItems = content.match(/^[\-\*] \[ \].+$/gm) || [];
|
||
|
||
if (pendingItems.length > 0) {
|
||
output += `\n Recent todos:\n`;
|
||
for (let i = 0; i < Math.min(5, pendingItems.length); i++) {
|
||
const item = pendingItems[i].replace(/^[\-\*] \[ \]\s*/, '').substring(0, 60);
|
||
output += ` - ${item}\n`;
|
||
}
|
||
}
|
||
} catch {
|
||
// Ignore errors
|
||
}
|
||
}
|
||
} else {
|
||
output += ` No todo file found (TODO.md, docs/todo.md etc)\n`;
|
||
}
|
||
|
||
output += '\n';
|
||
|
||
// Enabled plugins
|
||
output += `🔌 Enabled plugins:\n`;
|
||
const enabledPlugins = common.getEnabledPlugins(homeDir);
|
||
|
||
if (enabledPlugins.length > 0) {
|
||
for (let i = 0; i < Math.min(5, enabledPlugins.length); i++) {
|
||
output += ` - ${enabledPlugins[i].name}\n`;
|
||
}
|
||
if (enabledPlugins.length > 5) {
|
||
output += ` ... and ${enabledPlugins.length - 5} more plugins\n`;
|
||
}
|
||
} else {
|
||
output += ` None\n`;
|
||
}
|
||
|
||
output += '\n';
|
||
|
||
// Available commands
|
||
output += `💡 Available commands:\n`;
|
||
const availableCommands = common.getAvailableCommands(homeDir);
|
||
|
||
if (availableCommands.length > 0) {
|
||
for (const cmd of availableCommands.slice(0, 5)) {
|
||
const description = common.getCommandDescription(cmd.path) || `${cmd.plugin} command`;
|
||
const truncatedDesc = description.length > 40 ? description.substring(0, 40) + '...' : description;
|
||
output += ` /${cmd.name.padEnd(20)} ${truncatedDesc}\n`;
|
||
}
|
||
|
||
if (availableCommands.length > 5) {
|
||
output += ` ... and ${availableCommands.length - 5} more commands, use /help to list all\n`;
|
||
}
|
||
} else {
|
||
output += ` No commands found\n`;
|
||
}
|
||
|
||
// Output JSON
|
||
const result = {
|
||
continue: true,
|
||
systemMessage: output
|
||
};
|
||
|
||
console.log(JSON.stringify(result));
|
||
|
||
process.exit(0);
|