spidersan 0.2.2
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/CHANGELOG.md +91 -0
- package/LICENSE +21 -0
- package/LICENSE.md +85 -0
- package/README.md +217 -0
- package/dist/bin/spidersan.d.ts +9 -0
- package/dist/bin/spidersan.d.ts.map +1 -0
- package/dist/bin/spidersan.js +152 -0
- package/dist/bin/spidersan.js.map +1 -0
- package/dist/commands/abandon.d.ts +8 -0
- package/dist/commands/abandon.d.ts.map +1 -0
- package/dist/commands/abandon.js +49 -0
- package/dist/commands/abandon.js.map +1 -0
- package/dist/commands/activate.d.ts +9 -0
- package/dist/commands/activate.d.ts.map +1 -0
- package/dist/commands/activate.js +45 -0
- package/dist/commands/activate.js.map +1 -0
- package/dist/commands/active-windows.d.ts +16 -0
- package/dist/commands/active-windows.d.ts.map +1 -0
- package/dist/commands/active-windows.js +373 -0
- package/dist/commands/active-windows.js.map +1 -0
- package/dist/commands/audit-mark.d.ts +12 -0
- package/dist/commands/audit-mark.d.ts.map +1 -0
- package/dist/commands/audit-mark.js +204 -0
- package/dist/commands/audit-mark.js.map +1 -0
- package/dist/commands/cleanup.d.ts +8 -0
- package/dist/commands/cleanup.d.ts.map +1 -0
- package/dist/commands/cleanup.js +41 -0
- package/dist/commands/cleanup.js.map +1 -0
- package/dist/commands/close.d.ts +9 -0
- package/dist/commands/close.d.ts.map +1 -0
- package/dist/commands/close.js +129 -0
- package/dist/commands/close.js.map +1 -0
- package/dist/commands/collab-sync.d.ts +14 -0
- package/dist/commands/collab-sync.d.ts.map +1 -0
- package/dist/commands/collab-sync.js +240 -0
- package/dist/commands/collab-sync.js.map +1 -0
- package/dist/commands/collab.d.ts +6 -0
- package/dist/commands/collab.d.ts.map +1 -0
- package/dist/commands/collab.js +103 -0
- package/dist/commands/collab.js.map +1 -0
- package/dist/commands/completions.d.ts +8 -0
- package/dist/commands/completions.d.ts.map +1 -0
- package/dist/commands/completions.js +83 -0
- package/dist/commands/completions.js.map +1 -0
- package/dist/commands/conflicts.d.ts +12 -0
- package/dist/commands/conflicts.d.ts.map +1 -0
- package/dist/commands/conflicts.js +426 -0
- package/dist/commands/conflicts.js.map +1 -0
- package/dist/commands/demo.d.ts +3 -0
- package/dist/commands/demo.d.ts.map +1 -0
- package/dist/commands/demo.js +113 -0
- package/dist/commands/demo.js.map +1 -0
- package/dist/commands/depends.d.ts +8 -0
- package/dist/commands/depends.d.ts.map +1 -0
- package/dist/commands/depends.js +44 -0
- package/dist/commands/depends.js.map +1 -0
- package/dist/commands/doctor.d.ts +8 -0
- package/dist/commands/doctor.d.ts.map +1 -0
- package/dist/commands/doctor.js +249 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/inbox.d.ts +3 -0
- package/dist/commands/inbox.d.ts.map +1 -0
- package/dist/commands/inbox.js +66 -0
- package/dist/commands/inbox.js.map +1 -0
- package/dist/commands/index.d.ts +43 -0
- package/dist/commands/index.d.ts.map +1 -0
- package/dist/commands/index.js +61 -0
- package/dist/commands/index.js.map +1 -0
- package/dist/commands/init.d.ts +8 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +23 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/intent-scan.d.ts +17 -0
- package/dist/commands/intent-scan.d.ts.map +1 -0
- package/dist/commands/intent-scan.js +336 -0
- package/dist/commands/intent-scan.js.map +1 -0
- package/dist/commands/key-import.d.ts +3 -0
- package/dist/commands/key-import.d.ts.map +1 -0
- package/dist/commands/key-import.js +54 -0
- package/dist/commands/key-import.js.map +1 -0
- package/dist/commands/keygen.d.ts +3 -0
- package/dist/commands/keygen.d.ts.map +1 -0
- package/dist/commands/keygen.js +53 -0
- package/dist/commands/keygen.js.map +1 -0
- package/dist/commands/keys.d.ts +3 -0
- package/dist/commands/keys.d.ts.map +1 -0
- package/dist/commands/keys.js +56 -0
- package/dist/commands/keys.js.map +1 -0
- package/dist/commands/list.d.ts +3 -0
- package/dist/commands/list.d.ts.map +1 -0
- package/dist/commands/list.js +45 -0
- package/dist/commands/list.js.map +1 -0
- package/dist/commands/lock.d.ts +11 -0
- package/dist/commands/lock.d.ts.map +1 -0
- package/dist/commands/lock.js +220 -0
- package/dist/commands/lock.js.map +1 -0
- package/dist/commands/log.d.ts +8 -0
- package/dist/commands/log.d.ts.map +1 -0
- package/dist/commands/log.js +95 -0
- package/dist/commands/log.js.map +1 -0
- package/dist/commands/mcp-health.d.ts +3 -0
- package/dist/commands/mcp-health.d.ts.map +1 -0
- package/dist/commands/mcp-health.js +305 -0
- package/dist/commands/mcp-health.js.map +1 -0
- package/dist/commands/mcp-restart.d.ts +3 -0
- package/dist/commands/mcp-restart.d.ts.map +1 -0
- package/dist/commands/mcp-restart.js +176 -0
- package/dist/commands/mcp-restart.js.map +1 -0
- package/dist/commands/merge-order.d.ts +9 -0
- package/dist/commands/merge-order.d.ts.map +1 -0
- package/dist/commands/merge-order.js +138 -0
- package/dist/commands/merge-order.js.map +1 -0
- package/dist/commands/merged.d.ts +8 -0
- package/dist/commands/merged.d.ts.map +1 -0
- package/dist/commands/merged.js +39 -0
- package/dist/commands/merged.js.map +1 -0
- package/dist/commands/msg-read.d.ts +3 -0
- package/dist/commands/msg-read.d.ts.map +1 -0
- package/dist/commands/msg-read.js +70 -0
- package/dist/commands/msg-read.js.map +1 -0
- package/dist/commands/pulse.d.ts +9 -0
- package/dist/commands/pulse.d.ts.map +1 -0
- package/dist/commands/pulse.js +232 -0
- package/dist/commands/pulse.js.map +1 -0
- package/dist/commands/radar.d.ts +3 -0
- package/dist/commands/radar.d.ts.map +1 -0
- package/dist/commands/radar.js +147 -0
- package/dist/commands/radar.js.map +1 -0
- package/dist/commands/ready-check.d.ts +9 -0
- package/dist/commands/ready-check.d.ts.map +1 -0
- package/dist/commands/ready-check.js +156 -0
- package/dist/commands/ready-check.js.map +1 -0
- package/dist/commands/register.d.ts +3 -0
- package/dist/commands/register.d.ts.map +1 -0
- package/dist/commands/register.js +124 -0
- package/dist/commands/register.js.map +1 -0
- package/dist/commands/send.d.ts +3 -0
- package/dist/commands/send.d.ts.map +1 -0
- package/dist/commands/send.js +134 -0
- package/dist/commands/send.js.map +1 -0
- package/dist/commands/stale.d.ts +8 -0
- package/dist/commands/stale.d.ts.map +1 -0
- package/dist/commands/stale.js +38 -0
- package/dist/commands/stale.js.map +1 -0
- package/dist/commands/status.d.ts +9 -0
- package/dist/commands/status.d.ts.map +1 -0
- package/dist/commands/status.js +41 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/commands/sync-all.d.ts +9 -0
- package/dist/commands/sync-all.d.ts.map +1 -0
- package/dist/commands/sync-all.js +180 -0
- package/dist/commands/sync-all.js.map +1 -0
- package/dist/commands/sync.d.ts +8 -0
- package/dist/commands/sync.d.ts.map +1 -0
- package/dist/commands/sync.js +49 -0
- package/dist/commands/sync.js.map +1 -0
- package/dist/commands/tension.d.ts +12 -0
- package/dist/commands/tension.d.ts.map +1 -0
- package/dist/commands/tension.js +242 -0
- package/dist/commands/tension.js.map +1 -0
- package/dist/commands/torrent.d.ts +9 -0
- package/dist/commands/torrent.d.ts.map +1 -0
- package/dist/commands/torrent.js +431 -0
- package/dist/commands/torrent.js.map +1 -0
- package/dist/commands/tui.d.ts +8 -0
- package/dist/commands/tui.d.ts.map +1 -0
- package/dist/commands/tui.js +15 -0
- package/dist/commands/tui.js.map +1 -0
- package/dist/commands/wake.d.ts +9 -0
- package/dist/commands/wake.d.ts.map +1 -0
- package/dist/commands/wake.js +188 -0
- package/dist/commands/wake.js.map +1 -0
- package/dist/commands/watch.d.ts +3 -0
- package/dist/commands/watch.d.ts.map +1 -0
- package/dist/commands/watch.js +245 -0
- package/dist/commands/watch.js.map +1 -0
- package/dist/commands/who-touched.d.ts +9 -0
- package/dist/commands/who-touched.d.ts.map +1 -0
- package/dist/commands/who-touched.js +186 -0
- package/dist/commands/who-touched.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/agent-errors.d.ts +41 -0
- package/dist/lib/agent-errors.d.ts.map +1 -0
- package/dist/lib/agent-errors.js +143 -0
- package/dist/lib/agent-errors.js.map +1 -0
- package/dist/lib/ast.d.ts +56 -0
- package/dist/lib/ast.d.ts.map +1 -0
- package/dist/lib/ast.js +134 -0
- package/dist/lib/ast.js.map +1 -0
- package/dist/lib/config.d.ts +33 -0
- package/dist/lib/config.d.ts.map +1 -0
- package/dist/lib/config.js +127 -0
- package/dist/lib/config.js.map +1 -0
- package/dist/lib/crdt.d.ts +83 -0
- package/dist/lib/crdt.d.ts.map +1 -0
- package/dist/lib/crdt.js +160 -0
- package/dist/lib/crdt.js.map +1 -0
- package/dist/lib/crypto.d.ts +71 -0
- package/dist/lib/crypto.d.ts.map +1 -0
- package/dist/lib/crypto.js +172 -0
- package/dist/lib/crypto.js.map +1 -0
- package/dist/lib/errors.d.ts +35 -0
- package/dist/lib/errors.d.ts.map +1 -0
- package/dist/lib/errors.js +71 -0
- package/dist/lib/errors.js.map +1 -0
- package/dist/lib/license.d.ts +61 -0
- package/dist/lib/license.d.ts.map +1 -0
- package/dist/lib/license.js +171 -0
- package/dist/lib/license.js.map +1 -0
- package/dist/lib/security.d.ts +53 -0
- package/dist/lib/security.d.ts.map +1 -0
- package/dist/lib/security.js +122 -0
- package/dist/lib/security.js.map +1 -0
- package/dist/lib/update-check.d.ts +11 -0
- package/dist/lib/update-check.d.ts.map +1 -0
- package/dist/lib/update-check.js +113 -0
- package/dist/lib/update-check.js.map +1 -0
- package/dist/storage/adapter.d.ts +58 -0
- package/dist/storage/adapter.d.ts.map +1 -0
- package/dist/storage/adapter.js +8 -0
- package/dist/storage/adapter.js.map +1 -0
- package/dist/storage/factory.d.ts +6 -0
- package/dist/storage/factory.d.ts.map +1 -0
- package/dist/storage/factory.js +22 -0
- package/dist/storage/factory.js.map +1 -0
- package/dist/storage/index.d.ts +5 -0
- package/dist/storage/index.d.ts.map +1 -0
- package/dist/storage/index.js +5 -0
- package/dist/storage/index.js.map +1 -0
- package/dist/storage/local.d.ts +24 -0
- package/dist/storage/local.d.ts.map +1 -0
- package/dist/storage/local.js +101 -0
- package/dist/storage/local.js.map +1 -0
- package/dist/storage/supabase.d.ts +52 -0
- package/dist/storage/supabase.d.ts.map +1 -0
- package/dist/storage/supabase.js +221 -0
- package/dist/storage/supabase.js.map +1 -0
- package/dist/tui/screen.d.ts +14 -0
- package/dist/tui/screen.d.ts.map +1 -0
- package/dist/tui/screen.js +136 -0
- package/dist/tui/screen.js.map +1 -0
- package/package.json +89 -0
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* spidersan wake
|
|
3
|
+
*
|
|
4
|
+
* Start a session with branch context.
|
|
5
|
+
* "Weaving into the Web" - the spider feels its threads.
|
|
6
|
+
*/
|
|
7
|
+
import { Command } from 'commander';
|
|
8
|
+
import { execSync } from 'child_process';
|
|
9
|
+
import { execa } from 'execa';
|
|
10
|
+
import { getStorage } from '../storage/index.js';
|
|
11
|
+
function getCurrentBranch() {
|
|
12
|
+
try {
|
|
13
|
+
return execSync('git rev-parse --abbrev-ref HEAD', { encoding: 'utf-8' }).trim();
|
|
14
|
+
}
|
|
15
|
+
catch {
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
function getGitBranches() {
|
|
20
|
+
try {
|
|
21
|
+
const output = execSync('git branch --format="%(refname:short)"', { encoding: 'utf-8' });
|
|
22
|
+
return output.trim().split('\n').filter(Boolean);
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
return [];
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
function hasMycmail() {
|
|
29
|
+
try {
|
|
30
|
+
execSync('which mycmail', { stdio: 'ignore' });
|
|
31
|
+
return true;
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
async function getInboxCount() {
|
|
38
|
+
try {
|
|
39
|
+
const result = await execa('mycmail', ['inbox', '--count'], {
|
|
40
|
+
timeout: 10000, // 10 second timeout
|
|
41
|
+
reject: false,
|
|
42
|
+
});
|
|
43
|
+
const match = result.stdout?.match(/(\d+)/);
|
|
44
|
+
return match ? parseInt(match[1], 10) : 0;
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
return 0;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
export const wakeCommand = new Command('wake')
|
|
51
|
+
.description('Start a session - weave into the web, feel the threads')
|
|
52
|
+
.option('--json', 'Output as JSON')
|
|
53
|
+
.option('-q, --quiet', 'Minimal output')
|
|
54
|
+
.option('--skip-mail', 'Skip mycmail integration')
|
|
55
|
+
.action(async (options) => {
|
|
56
|
+
const storage = await getStorage();
|
|
57
|
+
const currentBranch = getCurrentBranch();
|
|
58
|
+
const result = {
|
|
59
|
+
synced: 0,
|
|
60
|
+
branches: [],
|
|
61
|
+
conflicts: [],
|
|
62
|
+
inboxCount: 0,
|
|
63
|
+
mycmailCalled: false,
|
|
64
|
+
};
|
|
65
|
+
// Step 1: Sync registry with git (remove orphaned branches)
|
|
66
|
+
if (await storage.isInitialized()) {
|
|
67
|
+
const gitBranches = new Set(getGitBranches());
|
|
68
|
+
const registeredBranches = await storage.list();
|
|
69
|
+
for (const branch of registeredBranches) {
|
|
70
|
+
if (!gitBranches.has(branch.name) && branch.status === 'active') {
|
|
71
|
+
await storage.unregister(branch.name);
|
|
72
|
+
result.synced++;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
// Get current branches after sync
|
|
76
|
+
const branches = await storage.list();
|
|
77
|
+
result.branches = branches
|
|
78
|
+
.filter(b => b.status === 'active')
|
|
79
|
+
.map(b => ({ name: b.name, files: b.files, status: b.status, agent: b.agent }));
|
|
80
|
+
// Step 2: Check conflicts for current branch
|
|
81
|
+
if (currentBranch) {
|
|
82
|
+
const target = await storage.get(currentBranch);
|
|
83
|
+
if (target) {
|
|
84
|
+
for (const branch of branches) {
|
|
85
|
+
if (branch.name === currentBranch || branch.status !== 'active')
|
|
86
|
+
continue;
|
|
87
|
+
const overlappingFiles = branch.files.filter(f => target.files.includes(f));
|
|
88
|
+
if (overlappingFiles.length > 0) {
|
|
89
|
+
result.conflicts.push({ branch: branch.name, files: overlappingFiles, agent: branch.agent });
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
// Step 3: Call mycmail wake (with timeout protection!)
|
|
96
|
+
if (!options.skipMail && hasMycmail()) {
|
|
97
|
+
try {
|
|
98
|
+
await execa('mycmail', ['wake', '--quiet'], {
|
|
99
|
+
timeout: 30000, // 30 second timeout - prevents hanging!
|
|
100
|
+
reject: false, // Don't throw on non-zero exit
|
|
101
|
+
});
|
|
102
|
+
result.mycmailCalled = true;
|
|
103
|
+
result.inboxCount = await getInboxCount();
|
|
104
|
+
}
|
|
105
|
+
catch {
|
|
106
|
+
// Ignore mycmail errors - but we won't hang!
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
// JSON output
|
|
110
|
+
if (options.json) {
|
|
111
|
+
console.log(JSON.stringify(result, null, 2));
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
// Quiet output
|
|
115
|
+
if (options.quiet) {
|
|
116
|
+
console.log(`🕷️ Wake: ${result.branches.length} threads, ${result.conflicts.length} tensions`);
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
// ═══════════════════════════════════════════════════════════
|
|
120
|
+
// CEREMONIAL OUTPUT: "Weaving into the Web"
|
|
121
|
+
// ═══════════════════════════════════════════════════════════
|
|
122
|
+
console.log('');
|
|
123
|
+
console.log('🕷️ Spidersan Awakens...');
|
|
124
|
+
console.log('');
|
|
125
|
+
console.log(' ╭─────────────────────────────────────╮');
|
|
126
|
+
console.log(' │ Feeling the threads... │');
|
|
127
|
+
console.log(' ╰─────────────────────────────────────╯');
|
|
128
|
+
console.log('');
|
|
129
|
+
// Web status
|
|
130
|
+
const agentCount = new Set(result.branches.map(b => b.agent).filter(Boolean)).size;
|
|
131
|
+
console.log(' 📡 Web Status:');
|
|
132
|
+
console.log(` • ${result.branches.length} thread${result.branches.length !== 1 ? 's' : ''} active`);
|
|
133
|
+
if (agentCount > 0) {
|
|
134
|
+
console.log(` • ${agentCount} agent${agentCount !== 1 ? 's' : ''} weaving`);
|
|
135
|
+
}
|
|
136
|
+
if (result.synced > 0) {
|
|
137
|
+
console.log(` • ${result.synced} broken thread${result.synced !== 1 ? 's' : ''} cleared`);
|
|
138
|
+
}
|
|
139
|
+
// Tensions (conflicts)
|
|
140
|
+
if (result.conflicts.length > 0) {
|
|
141
|
+
console.log('');
|
|
142
|
+
console.log(` ⚠️ Tension${result.conflicts.length !== 1 ? 's' : ''} detected:`);
|
|
143
|
+
for (const conflict of result.conflicts) {
|
|
144
|
+
const agentInfo = conflict.agent ? ` (${conflict.agent})` : '';
|
|
145
|
+
console.log(` └─ ${conflict.files[0]}${agentInfo}`);
|
|
146
|
+
if (conflict.files.length > 1) {
|
|
147
|
+
console.log(` +${conflict.files.length - 1} more file${conflict.files.length > 2 ? 's' : ''}`);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
// Active threads
|
|
152
|
+
if (result.branches.length > 0) {
|
|
153
|
+
console.log('');
|
|
154
|
+
console.log(' 🕸️ Active Threads:');
|
|
155
|
+
for (const branch of result.branches.slice(0, 4)) {
|
|
156
|
+
const isCurrent = branch.name === currentBranch ? ' ← you' : '';
|
|
157
|
+
const agentInfo = branch.agent && branch.name !== currentBranch ? ` (${branch.agent})` : '';
|
|
158
|
+
console.log(` • ${branch.name}${agentInfo}${isCurrent}`);
|
|
159
|
+
}
|
|
160
|
+
if (result.branches.length > 4) {
|
|
161
|
+
console.log(` • ...and ${result.branches.length - 4} more`);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
// Messages
|
|
165
|
+
if (result.mycmailCalled) {
|
|
166
|
+
console.log('');
|
|
167
|
+
if (result.inboxCount > 0) {
|
|
168
|
+
console.log(` 📬 ${result.inboxCount} message${result.inboxCount !== 1 ? 's' : ''} waiting`);
|
|
169
|
+
}
|
|
170
|
+
else {
|
|
171
|
+
console.log(' 📭 No new messages');
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
// Closing
|
|
175
|
+
console.log('');
|
|
176
|
+
if (result.conflicts.length === 0) {
|
|
177
|
+
console.log(' ╭─────────────────────────────────────╮');
|
|
178
|
+
console.log(' │ The web is calm. Ready to hunt. 🎯 │');
|
|
179
|
+
console.log(' ╰─────────────────────────────────────╯');
|
|
180
|
+
}
|
|
181
|
+
else {
|
|
182
|
+
console.log(' ╭─────────────────────────────────────╮');
|
|
183
|
+
console.log(' │ Tensions on the web. Tread softly. │');
|
|
184
|
+
console.log(' ╰─────────────────────────────────────╯');
|
|
185
|
+
}
|
|
186
|
+
console.log('');
|
|
187
|
+
});
|
|
188
|
+
//# sourceMappingURL=wake.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wake.js","sourceRoot":"","sources":["../../src/commands/wake.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"watch.d.ts","sourceRoot":"","sources":["../../src/commands/watch.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAqEpC,eAAO,MAAM,YAAY,SAuNnB,CAAC"}
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { execSync } from 'child_process';
|
|
3
|
+
import { getStorage } from '../storage/index.js';
|
|
4
|
+
import * as chokidar from 'chokidar';
|
|
5
|
+
import * as path from 'path';
|
|
6
|
+
import { io } from 'socket.io-client';
|
|
7
|
+
// Config
|
|
8
|
+
const HUB_URL = process.env.HUB_URL || 'https://hub.treebird.uk';
|
|
9
|
+
const DEBOUNCE_MS = 1000; // Debounce file changes
|
|
10
|
+
function getCurrentBranch() {
|
|
11
|
+
try {
|
|
12
|
+
return execSync('git rev-parse --abbrev-ref HEAD', { encoding: 'utf-8' }).trim();
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
throw new Error('Not in a git repository');
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
function getRepoRoot() {
|
|
19
|
+
try {
|
|
20
|
+
return execSync('git rev-parse --show-toplevel', { encoding: 'utf-8' }).trim();
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
return process.cwd();
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
// Post conflict warning to Hub chat API
|
|
27
|
+
async function postConflictToChat(branch, agent, conflicts) {
|
|
28
|
+
const conflictDetails = conflicts
|
|
29
|
+
.map(c => `• **${c.branch}**: ${c.files.join(', ')}`)
|
|
30
|
+
.join('\n');
|
|
31
|
+
const message = `🕷️⚠️ **CONFLICT DETECTED** on branch \`${branch}\`\n\n${conflictDetails}`;
|
|
32
|
+
try {
|
|
33
|
+
const response = await fetch(`${HUB_URL}/api/chat`, {
|
|
34
|
+
method: 'POST',
|
|
35
|
+
headers: { 'Content-Type': 'application/json' },
|
|
36
|
+
body: JSON.stringify({
|
|
37
|
+
agent: 'ssan',
|
|
38
|
+
name: 'Spidersan',
|
|
39
|
+
message,
|
|
40
|
+
glyph: '🕷️'
|
|
41
|
+
})
|
|
42
|
+
});
|
|
43
|
+
if (!response.ok) {
|
|
44
|
+
console.log(`⚠️ Failed to post to Hub chat: ${response.status}`);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
catch (err) {
|
|
48
|
+
// Silently fail - Hub might not be running
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
export const watchCommand = new Command('watch')
|
|
52
|
+
.description('🕷️ Watch files and auto-register changes (daemon mode)')
|
|
53
|
+
.argument('[directory]', 'Directory to watch (default: current repo)')
|
|
54
|
+
.option('-d, --dir <directory>', 'Directory to watch (alias for positional arg)')
|
|
55
|
+
.option('-a, --agent <agent>', 'Agent identifier for registration')
|
|
56
|
+
.option('--hub', 'Connect to Hub and emit real-time conflict warnings')
|
|
57
|
+
.option('--hub-sync', 'Post conflicts to Hub chat via REST API')
|
|
58
|
+
.option('-q, --quiet', 'Only log conflicts, not file changes')
|
|
59
|
+
.option('--legacy', 'Legacy mode: use old watcher settings (more file descriptors)')
|
|
60
|
+
.action(async (directory, options) => {
|
|
61
|
+
const storage = await getStorage();
|
|
62
|
+
if (!await storage.isInitialized()) {
|
|
63
|
+
console.error('❌ Spidersan not initialized. Run: spidersan init');
|
|
64
|
+
process.exit(1);
|
|
65
|
+
}
|
|
66
|
+
const branch = getCurrentBranch();
|
|
67
|
+
const repoRoot = directory || options.dir || getRepoRoot();
|
|
68
|
+
const agent = options.agent || process.env.SPIDERSAN_AGENT || 'unknown';
|
|
69
|
+
console.log(`
|
|
70
|
+
🕷️ SPIDERSAN WATCH MODE
|
|
71
|
+
━━━━━━━━━━━━━━━━━━━━━━━
|
|
72
|
+
Branch: ${branch}
|
|
73
|
+
Agent: ${agent}
|
|
74
|
+
Watching: ${repoRoot}
|
|
75
|
+
Hub: ${options.hub ? HUB_URL : 'disabled'}
|
|
76
|
+
Hub Sync: ${options.hubSync ? 'enabled (posts to chat)' : 'disabled'}
|
|
77
|
+
━━━━━━━━━━━━━━━━━━━━━━━
|
|
78
|
+
Press Ctrl+C to stop.
|
|
79
|
+
`);
|
|
80
|
+
// Hub socket connection (optional)
|
|
81
|
+
let hubSocket = null;
|
|
82
|
+
if (options.hub) {
|
|
83
|
+
try {
|
|
84
|
+
hubSocket = io(HUB_URL, {
|
|
85
|
+
reconnection: true,
|
|
86
|
+
reconnectionAttempts: 5,
|
|
87
|
+
timeout: 5000
|
|
88
|
+
});
|
|
89
|
+
hubSocket.on('connect', () => {
|
|
90
|
+
console.log('🔌 Connected to Hub');
|
|
91
|
+
});
|
|
92
|
+
hubSocket.on('disconnect', () => {
|
|
93
|
+
console.log('🔌 Disconnected from Hub');
|
|
94
|
+
});
|
|
95
|
+
hubSocket.on('connect_error', (err) => {
|
|
96
|
+
if (!options.quiet) {
|
|
97
|
+
console.log(`⚠️ Hub connection failed: ${err.message}`);
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
catch (e) {
|
|
102
|
+
console.log('⚠️ Could not connect to Hub');
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
// Track recently changed files for debouncing
|
|
106
|
+
const pendingFiles = new Set();
|
|
107
|
+
let debounceTimer = null;
|
|
108
|
+
// Process accumulated file changes
|
|
109
|
+
async function processChanges() {
|
|
110
|
+
if (pendingFiles.size === 0)
|
|
111
|
+
return;
|
|
112
|
+
const files = Array.from(pendingFiles);
|
|
113
|
+
pendingFiles.clear();
|
|
114
|
+
// Register files
|
|
115
|
+
const existing = await storage.get(branch);
|
|
116
|
+
const allFiles = existing
|
|
117
|
+
? [...new Set([...existing.files, ...files])]
|
|
118
|
+
: files;
|
|
119
|
+
if (existing) {
|
|
120
|
+
await storage.update(branch, { files: allFiles, agent });
|
|
121
|
+
}
|
|
122
|
+
else {
|
|
123
|
+
await storage.register({
|
|
124
|
+
name: branch,
|
|
125
|
+
files: allFiles,
|
|
126
|
+
status: 'active',
|
|
127
|
+
agent,
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
if (!options.quiet) {
|
|
131
|
+
console.log(`📝 Registered: ${files.join(', ')}`);
|
|
132
|
+
}
|
|
133
|
+
// Check for conflicts
|
|
134
|
+
const allBranches = await storage.list();
|
|
135
|
+
const conflicts = [];
|
|
136
|
+
for (const otherBranch of allBranches) {
|
|
137
|
+
if (otherBranch.name === branch || otherBranch.status !== 'active')
|
|
138
|
+
continue;
|
|
139
|
+
const overlapping = files.filter(f => otherBranch.files.includes(f));
|
|
140
|
+
if (overlapping.length > 0) {
|
|
141
|
+
conflicts.push({ branch: otherBranch.name, files: overlapping });
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
if (conflicts.length > 0) {
|
|
145
|
+
console.log(`\n⚠️ CONFLICT DETECTED!`);
|
|
146
|
+
conflicts.forEach(c => {
|
|
147
|
+
console.log(` 🔴 ${c.branch}: ${c.files.join(', ')}`);
|
|
148
|
+
});
|
|
149
|
+
console.log('');
|
|
150
|
+
// Emit to Hub via socket if connected
|
|
151
|
+
if (hubSocket?.connected) {
|
|
152
|
+
hubSocket.emit('conflicts:warning', {
|
|
153
|
+
branch,
|
|
154
|
+
agent,
|
|
155
|
+
files,
|
|
156
|
+
conflicts,
|
|
157
|
+
timestamp: new Date().toISOString()
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
// Post to Hub chat if --hub-sync enabled
|
|
161
|
+
if (options.hubSync) {
|
|
162
|
+
await postConflictToChat(branch, agent, conflicts);
|
|
163
|
+
console.log('📤 Posted conflict to Hub chat');
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
// Build extended ignore patterns for smart mode
|
|
168
|
+
const smartIgnores = [
|
|
169
|
+
'**/.next/**', '**/build/**', '**/out/**', '**/coverage/**',
|
|
170
|
+
'**/.cache/**', '**/__pycache__/**', '**/target/**',
|
|
171
|
+
'**/vendor/**', '**/bower_components/**', '**/.pnpm/**',
|
|
172
|
+
'**/.idea/**', '**/.vscode/**', '**/*.swp', '**/*.swo',
|
|
173
|
+
'**/.DS_Store', '**/Thumbs.db',
|
|
174
|
+
'**/package-lock.json', '**/yarn.lock', '**/pnpm-lock.yaml',
|
|
175
|
+
'**/*.png', '**/*.jpg', '**/*.jpeg', '**/*.gif', '**/*.ico',
|
|
176
|
+
'**/*.woff', '**/*.woff2', '**/*.ttf', '**/*.pdf',
|
|
177
|
+
];
|
|
178
|
+
if (options.legacy) {
|
|
179
|
+
console.log('⚠️ Legacy mode: Using old watcher settings (more file descriptors)');
|
|
180
|
+
}
|
|
181
|
+
// Start watching
|
|
182
|
+
// SECURITY FIX: Always limit depth to prevent EMFILE errors (Sherlocksan 2026-01-02)
|
|
183
|
+
const watcher = chokidar.watch(repoRoot, {
|
|
184
|
+
ignored: !options.legacy ? [
|
|
185
|
+
...smartIgnores,
|
|
186
|
+
/(^|[/\\])\../, // dotfiles
|
|
187
|
+
'**/node_modules/**',
|
|
188
|
+
'**/dist/**',
|
|
189
|
+
'**/*.log',
|
|
190
|
+
'**/.git/**', // ALWAYS ignore .git
|
|
191
|
+
] : [
|
|
192
|
+
/(^|[/\\])\../, // dotfiles (ALWAYS ignore)
|
|
193
|
+
'**/node_modules/**',
|
|
194
|
+
'**/dist/**',
|
|
195
|
+
'**/*.log',
|
|
196
|
+
'**/.git/**', // ALWAYS ignore .git
|
|
197
|
+
],
|
|
198
|
+
persistent: true,
|
|
199
|
+
ignoreInitial: true,
|
|
200
|
+
awaitWriteFinish: {
|
|
201
|
+
stabilityThreshold: 500,
|
|
202
|
+
pollInterval: 100
|
|
203
|
+
},
|
|
204
|
+
// ALWAYS limit depth to prevent EMFILE (default: 5, legacy: 10)
|
|
205
|
+
depth: !options.legacy ? 5 : 10,
|
|
206
|
+
// Smart mode (default): use polling to reduce file descriptor usage
|
|
207
|
+
...(!options.legacy && {
|
|
208
|
+
usePolling: true,
|
|
209
|
+
interval: 1000, // Check every 1 second
|
|
210
|
+
binaryInterval: 3000,
|
|
211
|
+
})
|
|
212
|
+
});
|
|
213
|
+
watcher.on('change', (filePath) => {
|
|
214
|
+
const relativePath = path.relative(repoRoot, filePath);
|
|
215
|
+
pendingFiles.add(relativePath);
|
|
216
|
+
// Debounce: wait for more changes before processing
|
|
217
|
+
if (debounceTimer)
|
|
218
|
+
clearTimeout(debounceTimer);
|
|
219
|
+
debounceTimer = setTimeout(processChanges, DEBOUNCE_MS);
|
|
220
|
+
});
|
|
221
|
+
watcher.on('add', (filePath) => {
|
|
222
|
+
const relativePath = path.relative(repoRoot, filePath);
|
|
223
|
+
pendingFiles.add(relativePath);
|
|
224
|
+
if (debounceTimer)
|
|
225
|
+
clearTimeout(debounceTimer);
|
|
226
|
+
debounceTimer = setTimeout(processChanges, DEBOUNCE_MS);
|
|
227
|
+
});
|
|
228
|
+
watcher.on('error', (err) => {
|
|
229
|
+
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
230
|
+
console.error(`❌ Watcher error: ${errorMsg}`);
|
|
231
|
+
});
|
|
232
|
+
// Handle graceful shutdown
|
|
233
|
+
process.on('SIGINT', () => {
|
|
234
|
+
console.log('\n🕷️ Watch mode stopped.');
|
|
235
|
+
watcher.close();
|
|
236
|
+
hubSocket?.disconnect();
|
|
237
|
+
process.exit(0);
|
|
238
|
+
});
|
|
239
|
+
process.on('SIGTERM', () => {
|
|
240
|
+
watcher.close();
|
|
241
|
+
hubSocket?.disconnect();
|
|
242
|
+
process.exit(0);
|
|
243
|
+
});
|
|
244
|
+
});
|
|
245
|
+
//# sourceMappingURL=watch.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"watch.js","sourceRoot":"","sources":["../../src/commands/watch.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* spidersan who-touched <file>
|
|
3
|
+
*
|
|
4
|
+
* Show git history of who touched a file, enriched with agent info.
|
|
5
|
+
* Quick forensics for multi-agent coordination.
|
|
6
|
+
*/
|
|
7
|
+
import { Command } from 'commander';
|
|
8
|
+
export declare const whoTouchedCommand: Command;
|
|
9
|
+
//# sourceMappingURL=who-touched.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"who-touched.d.ts","sourceRoot":"","sources":["../../src/commands/who-touched.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA+HpC,eAAO,MAAM,iBAAiB,SAyFxB,CAAC"}
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* spidersan who-touched <file>
|
|
3
|
+
*
|
|
4
|
+
* Show git history of who touched a file, enriched with agent info.
|
|
5
|
+
* Quick forensics for multi-agent coordination.
|
|
6
|
+
*/
|
|
7
|
+
import { Command } from 'commander';
|
|
8
|
+
import { execSync } from 'child_process';
|
|
9
|
+
import { existsSync } from 'fs';
|
|
10
|
+
// Security: Input validation
|
|
11
|
+
const VALID_FILE_PATH = /^[./a-zA-Z0-9][a-zA-Z0-9/_.@-]{0,200}$/;
|
|
12
|
+
function validateFilePath(path) {
|
|
13
|
+
if (!VALID_FILE_PATH.test(path)) {
|
|
14
|
+
throw new Error(`Invalid file path: "${path.slice(0, 50)}..."`);
|
|
15
|
+
}
|
|
16
|
+
return path;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Extract agent ID from commit message or author
|
|
20
|
+
* Looks for patterns like:
|
|
21
|
+
* - 🕷️ Spidersan: ...
|
|
22
|
+
* - [mappersan] ...
|
|
23
|
+
* - Author email containing agent name
|
|
24
|
+
*/
|
|
25
|
+
function extractAgentFromCommit(entry) {
|
|
26
|
+
const message = entry.subject.toLowerCase();
|
|
27
|
+
const author = entry.author.toLowerCase();
|
|
28
|
+
const email = entry.email.toLowerCase();
|
|
29
|
+
// Known agent patterns in commit messages
|
|
30
|
+
const agentPatterns = [
|
|
31
|
+
/🕷️\s*(\w+san)/i,
|
|
32
|
+
/🕵️\s*(\w+san)/i,
|
|
33
|
+
/🗺️\s*(\w+san)/i,
|
|
34
|
+
/🌊\s*(\w+san)/i,
|
|
35
|
+
/🐦\s*(\w+san)/i,
|
|
36
|
+
/🎨\s*(\w+san)/i,
|
|
37
|
+
/📣\s*(\w+san)/i,
|
|
38
|
+
/📿\s*(\w+san)/i,
|
|
39
|
+
/🍄\s*(\w+san)/i,
|
|
40
|
+
/🌳\s*(\w+san)/i,
|
|
41
|
+
/🥷\s*(\w+san)/i,
|
|
42
|
+
/\[(\w+san)\]/i,
|
|
43
|
+
/^(\w+san):/i,
|
|
44
|
+
];
|
|
45
|
+
for (const pattern of agentPatterns) {
|
|
46
|
+
const match = message.match(pattern);
|
|
47
|
+
if (match)
|
|
48
|
+
return match[1].toLowerCase();
|
|
49
|
+
}
|
|
50
|
+
// Check author/email for agent names
|
|
51
|
+
const knownAgents = [
|
|
52
|
+
'spidersan', 'sherlocksan', 'mappersan', 'watsan', 'birdsan',
|
|
53
|
+
'artisan', 'marksan', 'yosef', 'myceliumail', 'sasusan', 'treesan'
|
|
54
|
+
];
|
|
55
|
+
for (const agent of knownAgents) {
|
|
56
|
+
if (author.includes(agent) || email.includes(agent)) {
|
|
57
|
+
return agent;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return undefined;
|
|
61
|
+
}
|
|
62
|
+
function getAgentGlyph(agent) {
|
|
63
|
+
const glyphs = {
|
|
64
|
+
'spidersan': '🕷️',
|
|
65
|
+
'sherlocksan': '🕵️',
|
|
66
|
+
'mappersan': '🗺️',
|
|
67
|
+
'watsan': '🌊',
|
|
68
|
+
'birdsan': '🐦',
|
|
69
|
+
'artisan': '🎨',
|
|
70
|
+
'marksan': '📣',
|
|
71
|
+
'yosef': '📿',
|
|
72
|
+
'myceliumail': '🍄',
|
|
73
|
+
'sasusan': '🥷',
|
|
74
|
+
'treesan': '🌳',
|
|
75
|
+
'treebird': '🌳',
|
|
76
|
+
};
|
|
77
|
+
return glyphs[agent.toLowerCase()] || '👤';
|
|
78
|
+
}
|
|
79
|
+
function getFileHistory(filePath, limit) {
|
|
80
|
+
try {
|
|
81
|
+
// Use %x00 as delimiter for safety
|
|
82
|
+
const format = '%H%x00%h%x00%an%x00%ae%x00%ai%x00%ar%x00%s';
|
|
83
|
+
const output = execSync(`git log -n ${limit} --follow --format="${format}" -- "${filePath}"`, { encoding: 'utf-8', maxBuffer: 1024 * 1024 });
|
|
84
|
+
const entries = [];
|
|
85
|
+
const lines = output.trim().split('\n').filter(l => l);
|
|
86
|
+
for (const line of lines) {
|
|
87
|
+
const parts = line.split('\0');
|
|
88
|
+
if (parts.length >= 7) {
|
|
89
|
+
const entry = {
|
|
90
|
+
hash: parts[0],
|
|
91
|
+
shortHash: parts[1],
|
|
92
|
+
author: parts[2],
|
|
93
|
+
email: parts[3],
|
|
94
|
+
date: parts[4],
|
|
95
|
+
relativeDate: parts[5],
|
|
96
|
+
subject: parts[6],
|
|
97
|
+
};
|
|
98
|
+
entry.agent = extractAgentFromCommit(entry);
|
|
99
|
+
entries.push(entry);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return entries;
|
|
103
|
+
}
|
|
104
|
+
catch (error) {
|
|
105
|
+
throw new Error(`Failed to get git history for "${filePath}"`);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
export const whoTouchedCommand = new Command('who-touched')
|
|
109
|
+
.description('Show who touched a file (git history with agent detection)')
|
|
110
|
+
.argument('<file>', 'File path to check history for')
|
|
111
|
+
.option('-n, --limit <count>', 'Number of commits to show', '10')
|
|
112
|
+
.option('--json', 'Output as JSON')
|
|
113
|
+
.option('--agents-only', 'Only show commits from detected agents')
|
|
114
|
+
.option('--since <date>', 'Show commits since date (e.g., "2 days ago")')
|
|
115
|
+
.action(async (file, options) => {
|
|
116
|
+
// Validate input
|
|
117
|
+
const safePath = validateFilePath(file);
|
|
118
|
+
const limit = parseInt(options.limit, 10) || 10;
|
|
119
|
+
// Check if file exists or is tracked
|
|
120
|
+
if (!existsSync(safePath)) {
|
|
121
|
+
try {
|
|
122
|
+
execSync(`git ls-files --error-unmatch "${safePath}"`, {
|
|
123
|
+
encoding: 'utf-8',
|
|
124
|
+
stdio: 'pipe'
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
catch {
|
|
128
|
+
console.error(`❌ File not found: ${safePath}`);
|
|
129
|
+
process.exit(1);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
let entries = getFileHistory(safePath, limit * 2); // Get extra to filter
|
|
133
|
+
// Filter by --agents-only
|
|
134
|
+
if (options.agentsOnly) {
|
|
135
|
+
entries = entries.filter(e => e.agent);
|
|
136
|
+
}
|
|
137
|
+
// Filter by --since
|
|
138
|
+
if (options.since) {
|
|
139
|
+
try {
|
|
140
|
+
const sinceDate = execSync(`date -d "${options.since}" +%s 2>/dev/null || date -j -f "%Y-%m-%d" "${options.since}" +%s`, { encoding: 'utf-8' }).trim();
|
|
141
|
+
const sinceTimestamp = parseInt(sinceDate, 10);
|
|
142
|
+
entries = entries.filter(e => {
|
|
143
|
+
const commitDate = new Date(e.date).getTime() / 1000;
|
|
144
|
+
return commitDate >= sinceTimestamp;
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
catch {
|
|
148
|
+
// If date parsing fails, skip the filter
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
// Limit results
|
|
152
|
+
entries = entries.slice(0, limit);
|
|
153
|
+
if (entries.length === 0) {
|
|
154
|
+
console.log(`🕷️ No history found for "${safePath}"`);
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
if (options.json) {
|
|
158
|
+
console.log(JSON.stringify({ file: safePath, history: entries }, null, 2));
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
// Count unique authors/agents
|
|
162
|
+
const authors = new Set(entries.map(e => e.author));
|
|
163
|
+
const agents = new Set(entries.filter(e => e.agent).map(e => e.agent));
|
|
164
|
+
console.log(`
|
|
165
|
+
🕷️ WHO TOUCHED: ${safePath}
|
|
166
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
167
|
+
`);
|
|
168
|
+
for (const entry of entries) {
|
|
169
|
+
const glyph = entry.agent ? getAgentGlyph(entry.agent) : '👤';
|
|
170
|
+
const agentTag = entry.agent ? ` (${entry.agent})` : '';
|
|
171
|
+
const subject = entry.subject.length > 50
|
|
172
|
+
? entry.subject.slice(0, 47) + '...'
|
|
173
|
+
: entry.subject;
|
|
174
|
+
console.log(`${glyph} ${entry.shortHash} | ${entry.relativeDate.padEnd(15)} | ${entry.author}${agentTag}`);
|
|
175
|
+
console.log(` └─ ${subject}`);
|
|
176
|
+
}
|
|
177
|
+
console.log(`
|
|
178
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
179
|
+
📊 ${entries.length} commits | ${authors.size} authors | ${agents.size} agents detected
|
|
180
|
+
`);
|
|
181
|
+
if (agents.size > 0) {
|
|
182
|
+
const agentList = Array.from(agents).map(a => `${getAgentGlyph(a)} ${a}`).join(' ');
|
|
183
|
+
console.log(`🤖 Agents: ${agentList}`);
|
|
184
|
+
}
|
|
185
|
+
});
|
|
186
|
+
//# sourceMappingURL=who-touched.js.map
|