@agile-vibe-coding/avc 0.1.0 → 0.1.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.
@@ -0,0 +1,208 @@
1
+ /**
2
+ * Command Logger - Creates individual log files for each command execution
3
+ *
4
+ * Each command creates a timestamped log file in .avc/logs/ directory:
5
+ * - init-2026-01-31-13-05-23.log
6
+ * - sponsor-call-2026-01-31-13-10-45.log
7
+ * - status-2026-01-31-13-15-30.log
8
+ * - remove-2026-01-31-13-20-15.log
9
+ */
10
+
11
+ import fs from 'fs';
12
+ import path from 'path';
13
+
14
+ class CommandLogger {
15
+ constructor(commandName, projectRoot = process.cwd()) {
16
+ this.commandName = commandName;
17
+ this.projectRoot = projectRoot;
18
+ this.logsDir = path.join(projectRoot, '.avc', 'logs');
19
+
20
+ // Create timestamp for this command execution
21
+ const now = new Date();
22
+ const timestamp = now.toISOString()
23
+ .replace(/T/, '-')
24
+ .replace(/:/g, '-')
25
+ .replace(/\..+/, '');
26
+
27
+ this.logFileName = `${commandName}-${timestamp}.log`;
28
+ this.logFilePath = path.join(this.logsDir, this.logFileName);
29
+
30
+ // Store original console methods
31
+ this.originalLog = console.log;
32
+ this.originalError = console.error;
33
+ this.originalWarn = console.warn;
34
+ this.originalInfo = console.info;
35
+
36
+ // Buffer for logs
37
+ this.logBuffer = [];
38
+
39
+ // Initialize log file
40
+ this.initializeLogFile();
41
+ }
42
+
43
+ /**
44
+ * Initialize log file and directory
45
+ */
46
+ initializeLogFile() {
47
+ try {
48
+ // Create logs directory if it doesn't exist
49
+ if (!fs.existsSync(this.logsDir)) {
50
+ fs.mkdirSync(this.logsDir, { recursive: true });
51
+ }
52
+
53
+ // Write header to log file
54
+ const header = [
55
+ '='.repeat(80),
56
+ `AVC Command Log: ${this.commandName}`,
57
+ `Timestamp: ${new Date().toISOString()}`,
58
+ `Project: ${this.projectRoot}`,
59
+ `Log File: ${this.logFileName}`,
60
+ '='.repeat(80),
61
+ ''
62
+ ].join('\n');
63
+
64
+ fs.writeFileSync(this.logFilePath, header, 'utf8');
65
+ } catch (error) {
66
+ // If we can't create the log file, just continue without logging
67
+ this.logFilePath = null;
68
+ }
69
+ }
70
+
71
+ /**
72
+ * Write a log entry to the file
73
+ */
74
+ writeLog(level, ...args) {
75
+ if (!this.logFilePath) return;
76
+
77
+ try {
78
+ const timestamp = new Date().toISOString();
79
+ const message = args.map(arg => {
80
+ if (typeof arg === 'object') {
81
+ return JSON.stringify(arg, null, 2);
82
+ }
83
+ return String(arg);
84
+ }).join(' ');
85
+
86
+ const logEntry = `[${timestamp}] [${level}] ${message}\n`;
87
+
88
+ fs.appendFileSync(this.logFilePath, logEntry, 'utf8');
89
+ } catch (error) {
90
+ // Silently fail if we can't write to log
91
+ }
92
+ }
93
+
94
+ /**
95
+ * Start capturing console output
96
+ */
97
+ start() {
98
+ // Intercept console.log
99
+ console.log = (...args) => {
100
+ this.writeLog('INFO', ...args);
101
+ this.originalLog(...args); // Still output to console
102
+ };
103
+
104
+ // Intercept console.error
105
+ console.error = (...args) => {
106
+ this.writeLog('ERROR', ...args);
107
+ this.originalError(...args);
108
+ };
109
+
110
+ // Intercept console.warn
111
+ console.warn = (...args) => {
112
+ this.writeLog('WARN', ...args);
113
+ this.originalWarn(...args);
114
+ };
115
+
116
+ // Intercept console.info
117
+ console.info = (...args) => {
118
+ this.writeLog('INFO', ...args);
119
+ this.originalInfo(...args);
120
+ };
121
+ }
122
+
123
+ /**
124
+ * Stop capturing console output and write footer
125
+ */
126
+ stop() {
127
+ // Restore original console methods
128
+ console.log = this.originalLog;
129
+ console.error = this.originalError;
130
+ console.warn = this.originalWarn;
131
+ console.info = this.originalInfo;
132
+
133
+ // Write footer
134
+ if (this.logFilePath) {
135
+ try {
136
+ const footer = [
137
+ '',
138
+ '='.repeat(80),
139
+ `Command completed: ${new Date().toISOString()}`,
140
+ `Log saved: ${this.logFilePath}`,
141
+ '='.repeat(80)
142
+ ].join('\n');
143
+
144
+ fs.appendFileSync(this.logFilePath, footer, 'utf8');
145
+ } catch (error) {
146
+ // Silently fail
147
+ }
148
+ }
149
+ }
150
+
151
+ /**
152
+ * Get the path to the log file
153
+ */
154
+ getLogPath() {
155
+ return this.logFilePath;
156
+ }
157
+
158
+ /**
159
+ * Clean up old log files (keep last N logs per command)
160
+ */
161
+ static cleanupOldLogs(projectRoot = process.cwd(), keepCount = 10) {
162
+ const logsDir = path.join(projectRoot, '.avc', 'logs');
163
+
164
+ if (!fs.existsSync(logsDir)) return;
165
+
166
+ try {
167
+ const files = fs.readdirSync(logsDir);
168
+
169
+ // Group files by command name
170
+ const filesByCommand = {};
171
+ files.forEach(file => {
172
+ if (!file.endsWith('.log')) return;
173
+
174
+ const commandName = file.split('-')[0];
175
+ if (!filesByCommand[commandName]) {
176
+ filesByCommand[commandName] = [];
177
+ }
178
+
179
+ filesByCommand[commandName].push({
180
+ name: file,
181
+ path: path.join(logsDir, file),
182
+ mtime: fs.statSync(path.join(logsDir, file)).mtime
183
+ });
184
+ });
185
+
186
+ // For each command, keep only the latest N files
187
+ Object.keys(filesByCommand).forEach(commandName => {
188
+ const files = filesByCommand[commandName];
189
+
190
+ // Sort by modification time (newest first)
191
+ files.sort((a, b) => b.mtime - a.mtime);
192
+
193
+ // Delete old files
194
+ files.slice(keepCount).forEach(file => {
195
+ try {
196
+ fs.unlinkSync(file.path);
197
+ } catch (error) {
198
+ // Ignore deletion errors
199
+ }
200
+ });
201
+ });
202
+ } catch (error) {
203
+ // Silently fail
204
+ }
205
+ }
206
+ }
207
+
208
+ export { CommandLogger };
package/cli/index.js CHANGED
@@ -1,28 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- import { startRepl, executeCommand } from './repl-ink.js';
3
+ import { startRepl } from './repl-ink.js';
4
4
 
5
- /**
6
- * AVC CLI - Main entry point
7
- *
8
- * Supports both interactive REPL mode and direct command execution
9
- *
10
- * Usage:
11
- * avc - Start interactive REPL
12
- * avc help - Show help and exit
13
- * avc init - Run init command and exit
14
- * avc status - Run status command and exit
15
- */
16
-
17
- const args = process.argv.slice(2);
18
-
19
- if (args.length === 0) {
20
- // No arguments - start interactive REPL
21
- startRepl();
22
- } else {
23
- // Command provided - execute non-interactively
24
- const command = args[0].startsWith('/') ? args[0] : `/${args[0]}`;
25
- executeCommand(command).catch(() => {
26
- process.exit(1);
27
- });
28
- }
5
+ // Always start interactive REPL — commands are only available inside it.
6
+ startRepl();