create-claude-context 1.0.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.
Files changed (88) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +146 -0
  3. package/bin/create-claude-context.js +61 -0
  4. package/lib/detector.js +373 -0
  5. package/lib/index.js +170 -0
  6. package/lib/installer.js +362 -0
  7. package/lib/placeholder.js +208 -0
  8. package/lib/prompts.js +287 -0
  9. package/lib/spinner.js +60 -0
  10. package/lib/validate.js +147 -0
  11. package/package.json +59 -0
  12. package/templates/CLAUDE.md.template +235 -0
  13. package/templates/base/README.md +257 -0
  14. package/templates/base/RPI_WORKFLOW_PLAN.md +320 -0
  15. package/templates/base/agents/api-developer.md +76 -0
  16. package/templates/base/agents/context-engineer.md +525 -0
  17. package/templates/base/agents/core-architect.md +76 -0
  18. package/templates/base/agents/database-ops.md +76 -0
  19. package/templates/base/agents/deployment-ops.md +76 -0
  20. package/templates/base/agents/integration-hub.md +76 -0
  21. package/templates/base/analytics/README.md +114 -0
  22. package/templates/base/ci-templates/README.md +108 -0
  23. package/templates/base/ci-templates/github-actions/context-check.yml +144 -0
  24. package/templates/base/ci-templates/github-actions/validate-docs.yml +105 -0
  25. package/templates/base/commands/analytics.md +238 -0
  26. package/templates/base/commands/collab.md +194 -0
  27. package/templates/base/commands/help.md +450 -0
  28. package/templates/base/commands/rpi-implement.md +115 -0
  29. package/templates/base/commands/rpi-plan.md +93 -0
  30. package/templates/base/commands/rpi-research.md +88 -0
  31. package/templates/base/commands/validate-all.md +77 -0
  32. package/templates/base/commands/verify-docs-current.md +86 -0
  33. package/templates/base/config/base.json +57 -0
  34. package/templates/base/config/environments/development.json +13 -0
  35. package/templates/base/config/environments/production.json +17 -0
  36. package/templates/base/config/environments/staging.json +13 -0
  37. package/templates/base/config/local.json.example +21 -0
  38. package/templates/base/context/ARCHITECTURE_SNAPSHOT.md +156 -0
  39. package/templates/base/context/CODE_TO_WORKFLOW_MAP.md +94 -0
  40. package/templates/base/context/KNOWN_GOTCHAS.md +195 -0
  41. package/templates/base/context/WORKFLOW_INDEX.md +129 -0
  42. package/templates/base/context/workflows/WORKFLOW_TEMPLATE.md +294 -0
  43. package/templates/base/indexes/agents/CAPABILITY_MATRIX.md +255 -0
  44. package/templates/base/indexes/agents/CATEGORY_INDEX.md +44 -0
  45. package/templates/base/indexes/code/CATEGORY_INDEX.md +38 -0
  46. package/templates/base/indexes/routing/CATEGORY_INDEX.md +39 -0
  47. package/templates/base/indexes/search/CATEGORY_INDEX.md +39 -0
  48. package/templates/base/indexes/workflows/CATEGORY_INDEX.md +38 -0
  49. package/templates/base/knowledge/README.md +98 -0
  50. package/templates/base/knowledge/sessions/README.md +88 -0
  51. package/templates/base/knowledge/sessions/TEMPLATE.md +150 -0
  52. package/templates/base/knowledge/shared/decisions/0001-adopt-context-engineering.md +144 -0
  53. package/templates/base/knowledge/shared/decisions/README.md +49 -0
  54. package/templates/base/knowledge/shared/decisions/TEMPLATE.md +123 -0
  55. package/templates/base/knowledge/shared/patterns/README.md +62 -0
  56. package/templates/base/knowledge/shared/patterns/TEMPLATE.md +120 -0
  57. package/templates/base/plans/PLAN_TEMPLATE.md +250 -0
  58. package/templates/base/plans/active/.gitkeep +0 -0
  59. package/templates/base/plans/completed/.gitkeep +0 -0
  60. package/templates/base/research/RESEARCH_TEMPLATE.md +153 -0
  61. package/templates/base/research/active/.gitkeep +0 -0
  62. package/templates/base/research/completed/.gitkeep +0 -0
  63. package/templates/base/schemas/agent.schema.json +141 -0
  64. package/templates/base/schemas/command.schema.json +134 -0
  65. package/templates/base/schemas/manifest.schema.json +117 -0
  66. package/templates/base/schemas/plan.schema.json +136 -0
  67. package/templates/base/schemas/research.schema.json +115 -0
  68. package/templates/base/schemas/settings.schema.json +244 -0
  69. package/templates/base/schemas/workflow.schema.json +126 -0
  70. package/templates/base/settings.json +57 -0
  71. package/templates/base/standards/COMPATIBILITY.md +219 -0
  72. package/templates/base/standards/EXTENSION_GUIDELINES.md +280 -0
  73. package/templates/base/standards/QUALITY_CHECKLIST.md +211 -0
  74. package/templates/base/standards/README.md +66 -0
  75. package/templates/base/team/README.md +168 -0
  76. package/templates/base/team/config.json +79 -0
  77. package/templates/base/team/roles.json +145 -0
  78. package/templates/base/tools/bin/claude-context.js +151 -0
  79. package/templates/base/tools/lib/config-loader.js +363 -0
  80. package/templates/base/tools/lib/detector.js +350 -0
  81. package/templates/base/tools/lib/diagnose.js +206 -0
  82. package/templates/base/tools/lib/errors.js +199 -0
  83. package/templates/base/tools/lib/index.js +24 -0
  84. package/templates/base/tools/lib/init.js +192 -0
  85. package/templates/base/tools/lib/logger.js +230 -0
  86. package/templates/base/tools/lib/placeholder.js +201 -0
  87. package/templates/base/tools/lib/validate.js +521 -0
  88. package/templates/base/tools/package.json +49 -0
@@ -0,0 +1,199 @@
1
+ /**
2
+ * Claude Context Engineering - Error Classes
3
+ *
4
+ * Structured error handling for the context engineering system.
5
+ * All errors are categorized and include recovery information.
6
+ */
7
+
8
+ /**
9
+ * Base error class for all Claude Context errors
10
+ */
11
+ class ClaudeContextError extends Error {
12
+ constructor(message, code, recoverable = true, details = {}) {
13
+ super(message);
14
+ this.name = 'ClaudeContextError';
15
+ this.code = code;
16
+ this.recoverable = recoverable;
17
+ this.details = details;
18
+ this.timestamp = new Date().toISOString();
19
+
20
+ // Capture stack trace
21
+ if (Error.captureStackTrace) {
22
+ Error.captureStackTrace(this, this.constructor);
23
+ }
24
+ }
25
+
26
+ toJSON() {
27
+ return {
28
+ name: this.name,
29
+ message: this.message,
30
+ code: this.code,
31
+ recoverable: this.recoverable,
32
+ details: this.details,
33
+ timestamp: this.timestamp,
34
+ };
35
+ }
36
+ }
37
+
38
+ /**
39
+ * Configuration-related errors
40
+ */
41
+ class ConfigurationError extends ClaudeContextError {
42
+ constructor(message, details = {}) {
43
+ super(message, ConfigurationError.codes[details.type] || 'E001', true, details);
44
+ this.name = 'ConfigurationError';
45
+ }
46
+
47
+ static codes = {
48
+ MISSING: 'E001',
49
+ INVALID: 'E002',
50
+ SCHEMA_VIOLATION: 'E003',
51
+ MERGE_CONFLICT: 'E004',
52
+ };
53
+ }
54
+
55
+ /**
56
+ * Validation-related errors
57
+ */
58
+ class ValidationError extends ClaudeContextError {
59
+ constructor(message, details = {}) {
60
+ super(message, ValidationError.codes[details.type] || 'E010', true, details);
61
+ this.name = 'ValidationError';
62
+ }
63
+
64
+ static codes = {
65
+ SCHEMA: 'E010',
66
+ LINK_BROKEN: 'E011',
67
+ PLACEHOLDER_REMAINING: 'E012',
68
+ LINE_NUMBER_STALE: 'E013',
69
+ STRUCTURE: 'E014',
70
+ };
71
+ }
72
+
73
+ /**
74
+ * File system-related errors
75
+ */
76
+ class FileSystemError extends ClaudeContextError {
77
+ constructor(message, details = {}) {
78
+ super(message, FileSystemError.codes[details.type] || 'E020', true, details);
79
+ this.name = 'FileSystemError';
80
+ }
81
+
82
+ static codes = {
83
+ NOT_FOUND: 'E020',
84
+ PERMISSION_DENIED: 'E021',
85
+ READ_ERROR: 'E022',
86
+ WRITE_ERROR: 'E023',
87
+ DIRECTORY_ERROR: 'E024',
88
+ };
89
+ }
90
+
91
+ /**
92
+ * Initialization-related errors
93
+ */
94
+ class InitializationError extends ClaudeContextError {
95
+ constructor(message, details = {}) {
96
+ super(message, InitializationError.codes[details.type] || 'E030', true, details);
97
+ this.name = 'InitializationError';
98
+ }
99
+
100
+ static codes = {
101
+ INCOMPLETE: 'E030',
102
+ TECH_STACK_UNKNOWN: 'E031',
103
+ WORKFLOW_DISCOVERY_FAILED: 'E032',
104
+ PLACEHOLDER_REPLACEMENT_FAILED: 'E033',
105
+ ALREADY_INITIALIZED: 'E034',
106
+ };
107
+ }
108
+
109
+ /**
110
+ * Agent/Command execution errors
111
+ */
112
+ class ExecutionError extends ClaudeContextError {
113
+ constructor(message, details = {}) {
114
+ super(message, ExecutionError.codes[details.type] || 'E040', details.recoverable ?? true, details);
115
+ this.name = 'ExecutionError';
116
+ }
117
+
118
+ static codes = {
119
+ AGENT_NOT_FOUND: 'E040',
120
+ COMMAND_NOT_FOUND: 'E041',
121
+ TIMEOUT: 'E042',
122
+ CONTEXT_EXCEEDED: 'E043',
123
+ DEPENDENCY_MISSING: 'E044',
124
+ };
125
+ }
126
+
127
+ /**
128
+ * Error code reference table
129
+ */
130
+ const ERROR_CODES = {
131
+ // Configuration (E001-E009)
132
+ E001: { name: 'CONFIG_MISSING', description: 'Configuration file not found' },
133
+ E002: { name: 'CONFIG_INVALID', description: 'Configuration file is invalid JSON' },
134
+ E003: { name: 'SCHEMA_VIOLATION', description: 'Configuration does not match schema' },
135
+ E004: { name: 'MERGE_CONFLICT', description: 'Configuration merge conflict' },
136
+
137
+ // Validation (E010-E019)
138
+ E010: { name: 'SCHEMA_ERROR', description: 'JSON schema validation failed' },
139
+ E011: { name: 'LINK_BROKEN', description: 'Markdown link does not resolve' },
140
+ E012: { name: 'PLACEHOLDER_REMAINING', description: 'Unresolved {{PLACEHOLDER}} found' },
141
+ E013: { name: 'LINE_NUMBER_STALE', description: 'Line number reference is outdated' },
142
+ E014: { name: 'STRUCTURE_ERROR', description: 'Required file or directory missing' },
143
+
144
+ // File System (E020-E029)
145
+ E020: { name: 'FILE_NOT_FOUND', description: 'File does not exist' },
146
+ E021: { name: 'PERMISSION_DENIED', description: 'Insufficient permissions' },
147
+ E022: { name: 'READ_ERROR', description: 'Failed to read file' },
148
+ E023: { name: 'WRITE_ERROR', description: 'Failed to write file' },
149
+ E024: { name: 'DIRECTORY_ERROR', description: 'Directory operation failed' },
150
+
151
+ // Initialization (E030-E039)
152
+ E030: { name: 'INIT_INCOMPLETE', description: 'Initialization did not complete' },
153
+ E031: { name: 'TECH_STACK_UNKNOWN', description: 'Could not detect technology stack' },
154
+ E032: { name: 'WORKFLOW_DISCOVERY_FAILED', description: 'Failed to discover workflows' },
155
+ E033: { name: 'PLACEHOLDER_REPLACEMENT_FAILED', description: 'Failed to replace placeholders' },
156
+ E034: { name: 'ALREADY_INITIALIZED', description: 'System already initialized' },
157
+
158
+ // Execution (E040-E049)
159
+ E040: { name: 'AGENT_NOT_FOUND', description: 'Agent not found' },
160
+ E041: { name: 'COMMAND_NOT_FOUND', description: 'Command not found' },
161
+ E042: { name: 'TIMEOUT', description: 'Operation timed out' },
162
+ E043: { name: 'CONTEXT_EXCEEDED', description: 'Context budget exceeded' },
163
+ E044: { name: 'DEPENDENCY_MISSING', description: 'Required dependency not available' },
164
+ };
165
+
166
+ /**
167
+ * Get error details by code
168
+ */
169
+ function getErrorInfo(code) {
170
+ return ERROR_CODES[code] || { name: 'UNKNOWN', description: 'Unknown error' };
171
+ }
172
+
173
+ /**
174
+ * Format error for display
175
+ */
176
+ function formatError(error) {
177
+ if (error instanceof ClaudeContextError) {
178
+ const info = getErrorInfo(error.code);
179
+ return `[${error.code}] ${error.name}: ${error.message}\n` +
180
+ ` Type: ${info.name}\n` +
181
+ ` Recoverable: ${error.recoverable ? 'Yes' : 'No'}\n` +
182
+ (Object.keys(error.details).length > 0
183
+ ? ` Details: ${JSON.stringify(error.details, null, 2)}\n`
184
+ : '');
185
+ }
186
+ return error.toString();
187
+ }
188
+
189
+ module.exports = {
190
+ ClaudeContextError,
191
+ ConfigurationError,
192
+ ValidationError,
193
+ FileSystemError,
194
+ InitializationError,
195
+ ExecutionError,
196
+ ERROR_CODES,
197
+ getErrorInfo,
198
+ formatError,
199
+ };
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Claude Context Engineering Tools - Main Entry
3
+ *
4
+ * Exports all public APIs from the tools library.
5
+ */
6
+
7
+ const { init } = require('./init');
8
+ const { validate } = require('./validate');
9
+ const { diagnose } = require('./diagnose');
10
+ const { configLoader } = require('./config-loader');
11
+ const { logger } = require('./logger');
12
+ const errors = require('./errors');
13
+
14
+ module.exports = {
15
+ // Commands
16
+ init,
17
+ validate,
18
+ diagnose,
19
+
20
+ // Utilities
21
+ configLoader,
22
+ logger,
23
+ errors,
24
+ };
@@ -0,0 +1,192 @@
1
+ /**
2
+ * Claude Context Engineering - Initialization Module
3
+ *
4
+ * Handles initialization of context engineering for a repository.
5
+ * Includes tech stack detection, workflow discovery, and template population.
6
+ */
7
+
8
+ const fs = require('fs');
9
+ const path = require('path');
10
+ const chalk = require('chalk');
11
+ const { configLoader, findClaudeDir } = require('./config-loader');
12
+ const { logger } = require('./logger');
13
+ const { InitializationError, FileSystemError } = require('./errors');
14
+ const { detectTechStack } = require('./detector');
15
+ const { replacePlaceholders } = require('./placeholder');
16
+
17
+ /**
18
+ * Initialize context engineering for a repository
19
+ */
20
+ async function init(options = {}) {
21
+ const {
22
+ config: configPath,
23
+ interactive = true,
24
+ resume = false,
25
+ validateOnly = false,
26
+ techStack = null,
27
+ projectName = null,
28
+ } = options;
29
+
30
+ const op = logger.startOperation('init');
31
+
32
+ try {
33
+ console.log(chalk.cyan('\n📦 Initializing Claude Context Engineering...\n'));
34
+
35
+ // Find or create .claude directory
36
+ const claudeDir = findClaudeDir();
37
+ const projectRoot = path.dirname(claudeDir);
38
+
39
+ // Check for existing initialization
40
+ const progressFile = path.join(claudeDir, 'INIT_PROGRESS.json');
41
+ let progress = null;
42
+
43
+ if (fs.existsSync(progressFile)) {
44
+ progress = JSON.parse(fs.readFileSync(progressFile, 'utf8'));
45
+
46
+ if (!resume && progress.status !== 'COMPLETED') {
47
+ console.log(chalk.yellow('⚠️ Previous initialization found. Use --resume to continue.'));
48
+ console.log(chalk.gray(` Status: ${progress.status}`));
49
+ console.log(chalk.gray(` Last phase: ${progress.current_phase}`));
50
+ return;
51
+ }
52
+ }
53
+
54
+ // Validate-only mode
55
+ if (validateOnly) {
56
+ console.log(chalk.cyan('🔍 Validating existing initialization...\n'));
57
+ return await validateInitialization(claudeDir);
58
+ }
59
+
60
+ // Resume mode
61
+ if (resume && progress) {
62
+ console.log(chalk.cyan(`🔄 Resuming from phase: ${progress.current_phase}\n`));
63
+ return await resumeInitialization(claudeDir, progress, options);
64
+ }
65
+
66
+ // Start fresh initialization
67
+ console.log(chalk.white('Phase 1: Repository Analysis'));
68
+ console.log(chalk.gray('─'.repeat(50)));
69
+
70
+ // Detect tech stack
71
+ console.log(' Detecting technology stack...');
72
+ const detected = await detectTechStack(projectRoot, { hint: techStack });
73
+
74
+ console.log(chalk.green(` ✓ Tech stack: ${detected.stack}`));
75
+ console.log(chalk.gray(` Languages: ${detected.languages.join(', ')}`));
76
+ console.log(chalk.gray(` Frameworks: ${detected.frameworks.join(', ')}`));
77
+ console.log(chalk.gray(` Files: ${detected.fileCount}`));
78
+
79
+ // Create progress file
80
+ progress = {
81
+ version: '1.0.0',
82
+ started_at: new Date().toISOString(),
83
+ last_updated: new Date().toISOString(),
84
+ status: 'IN_PROGRESS',
85
+ current_phase: 'analysis',
86
+ phases: {
87
+ analysis: { status: 'COMPLETE', duration_ms: 0 },
88
+ discovery: { status: 'PENDING' },
89
+ population: { status: 'PENDING' },
90
+ validation: { status: 'PENDING' },
91
+ finalization: { status: 'PENDING' },
92
+ },
93
+ detected: {
94
+ tech_stack: detected.stack,
95
+ languages: detected.languages,
96
+ frameworks: detected.frameworks,
97
+ file_count: detected.fileCount,
98
+ loc: detected.loc || 0,
99
+ },
100
+ project_name: projectName || detected.projectName || path.basename(projectRoot),
101
+ errors: [],
102
+ };
103
+
104
+ saveProgress(progressFile, progress);
105
+
106
+ // Phase 2: Workflow Discovery
107
+ console.log(chalk.white('\nPhase 2: Workflow Discovery'));
108
+ console.log(chalk.gray('─'.repeat(50)));
109
+ console.log(chalk.yellow(' ⚠️ Full workflow discovery requires Claude Code agent.'));
110
+ console.log(chalk.gray(' Run: @context-engineer "Initialize context engineering"'));
111
+ console.log(chalk.gray(' The agent will discover 8-15 workflows automatically.\n'));
112
+
113
+ progress.current_phase = 'discovery';
114
+ progress.phases.discovery.status = 'PENDING_AGENT';
115
+ saveProgress(progressFile, progress);
116
+
117
+ // Phase 3: Template Population (partial - placeholders only)
118
+ console.log(chalk.white('Phase 3: Template Population (Partial)'));
119
+ console.log(chalk.gray('─'.repeat(50)));
120
+
121
+ const placeholderValues = {
122
+ PROJECT_NAME: progress.project_name,
123
+ TECH_STACK: detected.stack,
124
+ DATE: new Date().toISOString().split('T')[0],
125
+ WORKFLOWS_COUNT: '{{WORKFLOWS_COUNT}}', // To be filled by agent
126
+ // Add more known values
127
+ };
128
+
129
+ const claudeMdPath = path.join(projectRoot, 'CLAUDE.md');
130
+ if (fs.existsSync(claudeMdPath)) {
131
+ const result = await replacePlaceholders(claudeMdPath, placeholderValues, { dryRun: false });
132
+ console.log(chalk.green(` ✓ Replaced ${result.replaced} placeholders in CLAUDE.md`));
133
+ console.log(chalk.gray(` Remaining: ${result.remaining} placeholders`));
134
+ }
135
+
136
+ progress.current_phase = 'population';
137
+ progress.phases.population.status = 'PARTIAL';
138
+ saveProgress(progressFile, progress);
139
+
140
+ // Summary
141
+ console.log(chalk.white('\n📋 Initialization Summary'));
142
+ console.log(chalk.gray('─'.repeat(50)));
143
+ console.log(chalk.green(' ✓ Repository analyzed'));
144
+ console.log(chalk.green(' ✓ Tech stack detected'));
145
+ console.log(chalk.yellow(' ⏳ Workflow discovery pending (run agent)'));
146
+ console.log(chalk.yellow(' ⏳ Template population partial'));
147
+
148
+ console.log(chalk.white('\n📌 Next Steps:'));
149
+ console.log(chalk.cyan(' 1. Run: @context-engineer "Initialize context engineering"'));
150
+ console.log(chalk.gray(' This will discover workflows and complete the setup.'));
151
+ console.log(chalk.cyan(' 2. Review generated workflow documentation'));
152
+ console.log(chalk.cyan(' 3. Run: npx claude-context validate'));
153
+
154
+ op.success();
155
+
156
+ } catch (error) {
157
+ op.fail(error);
158
+ throw error;
159
+ }
160
+ }
161
+
162
+ /**
163
+ * Save initialization progress
164
+ */
165
+ function saveProgress(filePath, progress) {
166
+ progress.last_updated = new Date().toISOString();
167
+ fs.writeFileSync(filePath, JSON.stringify(progress, null, 2));
168
+ }
169
+
170
+ /**
171
+ * Resume an interrupted initialization
172
+ */
173
+ async function resumeInitialization(claudeDir, progress, options) {
174
+ console.log(chalk.cyan(`Resuming from phase: ${progress.current_phase}`));
175
+ // Implementation would continue from the last phase
176
+ console.log(chalk.yellow('⚠️ Resume functionality requires full agent support.'));
177
+ console.log(chalk.gray(' Run: @context-engineer "resume initialization"'));
178
+ }
179
+
180
+ /**
181
+ * Validate an existing initialization
182
+ */
183
+ async function validateInitialization(claudeDir) {
184
+ const { validate } = require('./validate');
185
+ return await validate({ all: true, claudeDir });
186
+ }
187
+
188
+ module.exports = {
189
+ init,
190
+ resumeInitialization,
191
+ validateInitialization,
192
+ };
@@ -0,0 +1,230 @@
1
+ /**
2
+ * Claude Context Engineering - Logger
3
+ *
4
+ * Structured logging with levels, file output, and operation tracking.
5
+ */
6
+
7
+ const fs = require('fs');
8
+ const path = require('path');
9
+ const chalk = require('chalk');
10
+
11
+ const LOG_LEVELS = {
12
+ debug: 0,
13
+ info: 1,
14
+ warn: 2,
15
+ error: 3,
16
+ };
17
+
18
+ const LEVEL_COLORS = {
19
+ debug: chalk.gray,
20
+ info: chalk.blue,
21
+ warn: chalk.yellow,
22
+ error: chalk.red,
23
+ };
24
+
25
+ const LEVEL_ICONS = {
26
+ debug: '🔍',
27
+ info: 'ℹ️ ',
28
+ warn: '⚠️ ',
29
+ error: '❌',
30
+ };
31
+
32
+ /**
33
+ * Create a logger instance
34
+ */
35
+ function createLogger(options = {}) {
36
+ const config = {
37
+ level: options.level || 'info',
38
+ file: options.file || null,
39
+ maxSizeMb: options.maxSizeMb || 10,
40
+ console: options.console !== false,
41
+ timestamps: options.timestamps !== false,
42
+ colors: options.colors !== false,
43
+ };
44
+
45
+ // Ensure log directory exists if file logging enabled
46
+ if (config.file) {
47
+ const logDir = path.dirname(config.file);
48
+ if (!fs.existsSync(logDir)) {
49
+ fs.mkdirSync(logDir, { recursive: true });
50
+ }
51
+ }
52
+
53
+ /**
54
+ * Check if we should log at this level
55
+ */
56
+ function shouldLog(level) {
57
+ return LOG_LEVELS[level] >= LOG_LEVELS[config.level];
58
+ }
59
+
60
+ /**
61
+ * Format a log message
62
+ */
63
+ function formatMessage(level, message, meta = {}) {
64
+ const timestamp = new Date().toISOString();
65
+ const parts = [];
66
+
67
+ if (config.timestamps) {
68
+ parts.push(`[${timestamp}]`);
69
+ }
70
+ parts.push(`[${level.toUpperCase()}]`);
71
+ parts.push(message);
72
+
73
+ if (Object.keys(meta).length > 0) {
74
+ parts.push(JSON.stringify(meta));
75
+ }
76
+
77
+ return parts.join(' ');
78
+ }
79
+
80
+ /**
81
+ * Write to console with colors
82
+ */
83
+ function writeConsole(level, message, meta) {
84
+ if (!config.console) return;
85
+
86
+ const colorFn = config.colors ? LEVEL_COLORS[level] : (s) => s;
87
+ const icon = LEVEL_ICONS[level];
88
+ const timestamp = config.timestamps
89
+ ? chalk.gray(`[${new Date().toISOString()}] `)
90
+ : '';
91
+
92
+ let output = `${timestamp}${icon} ${colorFn(message)}`;
93
+
94
+ if (Object.keys(meta).length > 0) {
95
+ output += '\n' + chalk.gray(JSON.stringify(meta, null, 2));
96
+ }
97
+
98
+ if (level === 'error') {
99
+ console.error(output);
100
+ } else {
101
+ console.log(output);
102
+ }
103
+ }
104
+
105
+ /**
106
+ * Write to file
107
+ */
108
+ function writeFile(level, message, meta) {
109
+ if (!config.file) return;
110
+
111
+ try {
112
+ const formatted = formatMessage(level, message, meta) + '\n';
113
+ fs.appendFileSync(config.file, formatted);
114
+
115
+ // Check file size and rotate if needed
116
+ const stats = fs.statSync(config.file);
117
+ const sizeMb = stats.size / (1024 * 1024);
118
+ if (sizeMb > config.maxSizeMb) {
119
+ rotateLog();
120
+ }
121
+ } catch (error) {
122
+ // Silently fail file logging
123
+ }
124
+ }
125
+
126
+ /**
127
+ * Rotate log file
128
+ */
129
+ function rotateLog() {
130
+ if (!config.file || !fs.existsSync(config.file)) return;
131
+
132
+ const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
133
+ const rotatedPath = config.file.replace('.log', `-${timestamp}.log`);
134
+
135
+ try {
136
+ fs.renameSync(config.file, rotatedPath);
137
+ } catch (error) {
138
+ // Silently fail rotation
139
+ }
140
+ }
141
+
142
+ /**
143
+ * Core log function
144
+ */
145
+ function log(level, message, meta = {}) {
146
+ if (!shouldLog(level)) return;
147
+
148
+ writeConsole(level, message, meta);
149
+ writeFile(level, message, meta);
150
+ }
151
+
152
+ /**
153
+ * Start an operation and return tracking object
154
+ */
155
+ function startOperation(name) {
156
+ const operationId = generateId();
157
+ const startTime = Date.now();
158
+
159
+ log('info', `Operation started: ${name}`, { operation_id: operationId });
160
+
161
+ return {
162
+ id: operationId,
163
+
164
+ /**
165
+ * Log progress
166
+ */
167
+ progress: (message, meta = {}) => {
168
+ log('debug', `[${name}] ${message}`, { operation_id: operationId, ...meta });
169
+ },
170
+
171
+ /**
172
+ * Mark operation as successful
173
+ */
174
+ success: (message = 'completed', meta = {}) => {
175
+ const duration = Date.now() - startTime;
176
+ log('info', `Operation completed: ${name} - ${message}`, {
177
+ operation_id: operationId,
178
+ duration_ms: duration,
179
+ ...meta,
180
+ });
181
+ return { success: true, duration };
182
+ },
183
+
184
+ /**
185
+ * Mark operation as failed
186
+ */
187
+ fail: (error, meta = {}) => {
188
+ const duration = Date.now() - startTime;
189
+ const errorMessage = error instanceof Error ? error.message : error;
190
+ log('error', `Operation failed: ${name} - ${errorMessage}`, {
191
+ operation_id: operationId,
192
+ duration_ms: duration,
193
+ error: error instanceof Error ? { name: error.name, stack: error.stack } : error,
194
+ ...meta,
195
+ });
196
+ return { success: false, duration, error };
197
+ },
198
+ };
199
+ }
200
+
201
+ /**
202
+ * Generate a short unique ID
203
+ */
204
+ function generateId() {
205
+ return Math.random().toString(36).substring(2, 10);
206
+ }
207
+
208
+ // Return logger interface
209
+ return {
210
+ debug: (message, meta) => log('debug', message, meta),
211
+ info: (message, meta) => log('info', message, meta),
212
+ warn: (message, meta) => log('warn', message, meta),
213
+ error: (message, meta) => log('error', message, meta),
214
+ startOperation,
215
+ setLevel: (level) => { config.level = level; },
216
+ getLevel: () => config.level,
217
+ };
218
+ }
219
+
220
+ // Default logger instance
221
+ const logger = createLogger({
222
+ level: process.env.CLAUDE_LOG_LEVEL || 'info',
223
+ file: process.env.CLAUDE_LOG_FILE || null,
224
+ });
225
+
226
+ module.exports = {
227
+ createLogger,
228
+ logger,
229
+ LOG_LEVELS,
230
+ };