@tonycasey/lisa 0.5.13

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 (48) hide show
  1. package/README.md +42 -0
  2. package/dist/cli.js +390 -0
  3. package/dist/lib/interfaces/IDockerClient.js +2 -0
  4. package/dist/lib/interfaces/IMcpClient.js +2 -0
  5. package/dist/lib/interfaces/IServices.js +2 -0
  6. package/dist/lib/interfaces/ITemplateCopier.js +2 -0
  7. package/dist/lib/mcp.js +35 -0
  8. package/dist/lib/services.js +57 -0
  9. package/dist/package.json +36 -0
  10. package/dist/templates/agents/.sample.env +12 -0
  11. package/dist/templates/agents/docs/STORAGE_SETUP.md +161 -0
  12. package/dist/templates/agents/skills/common/group-id.js +193 -0
  13. package/dist/templates/agents/skills/init-review/SKILL.md +119 -0
  14. package/dist/templates/agents/skills/init-review/scripts/ai-enrich.js +258 -0
  15. package/dist/templates/agents/skills/init-review/scripts/init-review.js +769 -0
  16. package/dist/templates/agents/skills/lisa/SKILL.md +92 -0
  17. package/dist/templates/agents/skills/lisa/cache/.gitkeep +0 -0
  18. package/dist/templates/agents/skills/lisa/scripts/storage.js +374 -0
  19. package/dist/templates/agents/skills/memory/SKILL.md +31 -0
  20. package/dist/templates/agents/skills/memory/scripts/memory.js +533 -0
  21. package/dist/templates/agents/skills/prompt/SKILL.md +19 -0
  22. package/dist/templates/agents/skills/prompt/scripts/prompt.js +184 -0
  23. package/dist/templates/agents/skills/tasks/SKILL.md +31 -0
  24. package/dist/templates/agents/skills/tasks/scripts/tasks.js +489 -0
  25. package/dist/templates/claude/config.js +40 -0
  26. package/dist/templates/claude/hooks/README.md +158 -0
  27. package/dist/templates/claude/hooks/common/complexity-rater.js +290 -0
  28. package/dist/templates/claude/hooks/common/context.js +263 -0
  29. package/dist/templates/claude/hooks/common/group-id.js +188 -0
  30. package/dist/templates/claude/hooks/common/mcp-client.js +131 -0
  31. package/dist/templates/claude/hooks/common/transcript-parser.js +256 -0
  32. package/dist/templates/claude/hooks/common/zep-client.js +175 -0
  33. package/dist/templates/claude/hooks/session-start.js +401 -0
  34. package/dist/templates/claude/hooks/session-stop-worker.js +341 -0
  35. package/dist/templates/claude/hooks/session-stop.js +122 -0
  36. package/dist/templates/claude/hooks/user-prompt-submit.js +256 -0
  37. package/dist/templates/claude/settings.json +46 -0
  38. package/dist/templates/docker/.env.lisa.example +17 -0
  39. package/dist/templates/docker/docker-compose.graphiti.yml +45 -0
  40. package/dist/templates/rules/shared/clean-architecture.md +333 -0
  41. package/dist/templates/rules/shared/code-quality-rules.md +469 -0
  42. package/dist/templates/rules/shared/git-rules.md +64 -0
  43. package/dist/templates/rules/shared/testing-principles.md +469 -0
  44. package/dist/templates/rules/typescript/coding-standards.md +751 -0
  45. package/dist/templates/rules/typescript/testing.md +629 -0
  46. package/dist/templates/rules/typescript/typescript-config-guide.md +465 -0
  47. package/package.json +64 -0
  48. package/scripts/postinstall.js +710 -0
@@ -0,0 +1,290 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ // Scoring constants
4
+ const POINTS = {
5
+ FILE_CREATED: 3,
6
+ FILE_EDITED: 2,
7
+ COMMAND_RUN: 1.5,
8
+ DOC_CREATED: 2,
9
+ TEST_MODIFIED: 1.5,
10
+ CONFIG_FILE: 1,
11
+ VERSION_BUMP: 2,
12
+ STRUCTURAL_CHANGE: 2,
13
+ MULTI_DIRECTORY: 3,
14
+ CI_CD_SETUP: 3,
15
+ };
16
+ const MULTIPLIERS = {
17
+ TOOL_DIVERSITY_5: 1.2,
18
+ TOOL_DIVERSITY_8: 1.3,
19
+ };
20
+ // Score thresholds for rating normalization
21
+ const THRESHOLDS = {
22
+ RATING_2: 2,
23
+ RATING_3: 5,
24
+ RATING_4: 10,
25
+ RATING_5: 20,
26
+ };
27
+ // Version files to detect version bumps
28
+ const VERSION_FILES = ['package.json', 'pyproject.toml', 'Cargo.toml', 'setup.py', 'version.py', 'VERSION'];
29
+ // Config files that indicate setup/configuration work
30
+ const CONFIG_PATTERNS = [
31
+ 'tsconfig.json',
32
+ 'tsconfig',
33
+ 'jest.config',
34
+ '.eslintrc',
35
+ '.prettierrc',
36
+ 'webpack.config',
37
+ 'vite.config',
38
+ 'rollup.config',
39
+ '.gitignore',
40
+ '.dockerignore',
41
+ 'Dockerfile',
42
+ 'docker-compose',
43
+ '.github/workflows',
44
+ '.gitlab-ci',
45
+ 'Makefile',
46
+ 'CMakeLists',
47
+ ];
48
+ // CI/CD indicators
49
+ const CI_CD_PATTERNS = ['.github/workflows', '.gitlab-ci', 'Jenkinsfile', '.circleci', '.travis'];
50
+ /**
51
+ * Rate the complexity of work based on signals
52
+ */
53
+ function rateComplexity(work) {
54
+ const signals = extractSignals(work);
55
+ const rawScore = calculateRawScore(signals, work);
56
+ const rating = normalizeToRating(rawScore);
57
+ const signalDescriptions = describeSignals(signals, work);
58
+ const summary = generateSummary(rating, signals, work);
59
+ return {
60
+ rating,
61
+ rawScore: Math.round(rawScore * 10) / 10,
62
+ signals: signalDescriptions,
63
+ summary,
64
+ };
65
+ }
66
+ /**
67
+ * Extract complexity signals from work summary
68
+ */
69
+ function extractSignals(work) {
70
+ const filesModified = work.filesModified.size;
71
+ const filesCreated = work.filesCreated.size;
72
+ const allFiles = [...work.filesModified, ...work.filesCreated];
73
+ return {
74
+ filesModified,
75
+ filesCreated,
76
+ versionBump: hasVersionBump(allFiles),
77
+ docsCreated: countDocs(work.filesCreated),
78
+ testsModified: countTests(allFiles),
79
+ configFiles: countConfigFiles(allFiles),
80
+ structuralChanges: countStructuralChanges(work.commandsRun),
81
+ toolDiversity: work.toolsUsed.size,
82
+ commandCount: work.commandsRun.length,
83
+ };
84
+ }
85
+ /**
86
+ * Calculate raw score from signals
87
+ */
88
+ function calculateRawScore(signals, work) {
89
+ let score = 0;
90
+ // Base points for file operations
91
+ score += signals.filesCreated * POINTS.FILE_CREATED;
92
+ score += signals.filesModified * POINTS.FILE_EDITED;
93
+ // Points for commands (weighted)
94
+ score += signals.commandCount * POINTS.COMMAND_RUN;
95
+ // Bonus points for specific types
96
+ score += signals.docsCreated * POINTS.DOC_CREATED;
97
+ score += signals.testsModified * POINTS.TEST_MODIFIED;
98
+ score += signals.configFiles * POINTS.CONFIG_FILE;
99
+ // Bonus for version bump
100
+ if (signals.versionBump) {
101
+ score += POINTS.VERSION_BUMP;
102
+ }
103
+ // Bonus for structural changes
104
+ score += signals.structuralChanges * POINTS.STRUCTURAL_CHANGE;
105
+ // Bonus for multi-directory work
106
+ if (hasMultiDirectoryWork(work)) {
107
+ score += POINTS.MULTI_DIRECTORY;
108
+ }
109
+ // Bonus for CI/CD setup
110
+ if (hasCICDSetup(work)) {
111
+ score += POINTS.CI_CD_SETUP;
112
+ }
113
+ // Apply multipliers for tool diversity
114
+ if (signals.toolDiversity >= 8) {
115
+ score *= MULTIPLIERS.TOOL_DIVERSITY_8;
116
+ }
117
+ else if (signals.toolDiversity >= 5) {
118
+ score *= MULTIPLIERS.TOOL_DIVERSITY_5;
119
+ }
120
+ return score;
121
+ }
122
+ /**
123
+ * Normalize raw score to 1-5 rating
124
+ */
125
+ function normalizeToRating(rawScore) {
126
+ if (rawScore >= THRESHOLDS.RATING_5)
127
+ return 5;
128
+ if (rawScore >= THRESHOLDS.RATING_4)
129
+ return 4;
130
+ if (rawScore >= THRESHOLDS.RATING_3)
131
+ return 3;
132
+ if (rawScore >= THRESHOLDS.RATING_2)
133
+ return 2;
134
+ return 1;
135
+ }
136
+ /**
137
+ * Check if any version files were modified
138
+ */
139
+ function hasVersionBump(files) {
140
+ return files.some((filePath) => VERSION_FILES.some((vf) => filePath.endsWith(vf) || filePath.includes(vf)));
141
+ }
142
+ /**
143
+ * Count documentation files created
144
+ */
145
+ function countDocs(filesCreated) {
146
+ return Array.from(filesCreated).filter((filePath) => filePath.endsWith('.md') || filePath.includes('/docs/') || filePath.toUpperCase().includes('README')).length;
147
+ }
148
+ /**
149
+ * Count test files modified or created
150
+ */
151
+ function countTests(files) {
152
+ return files.filter((filePath) => /\.(test|spec)\.(ts|js|tsx|jsx|py|rs)$/.test(filePath) ||
153
+ filePath.includes('/tests/') ||
154
+ filePath.includes('/__tests__/') ||
155
+ filePath.includes('/test/')).length;
156
+ }
157
+ /**
158
+ * Count config files modified
159
+ */
160
+ function countConfigFiles(files) {
161
+ return files.filter((filePath) => CONFIG_PATTERNS.some((pattern) => filePath.includes(pattern))).length;
162
+ }
163
+ /**
164
+ * Count structural changes (mkdir commands)
165
+ */
166
+ function countStructuralChanges(commands) {
167
+ return commands.filter((cmd) => cmd.startsWith('mkdir') || cmd.includes('mkdir ')).length;
168
+ }
169
+ /**
170
+ * Check if work spans multiple directories
171
+ */
172
+ function hasMultiDirectoryWork(work) {
173
+ const allFiles = [...work.filesModified, ...work.filesCreated];
174
+ const directories = new Set();
175
+ for (const filePath of allFiles) {
176
+ const parts = filePath.split('/');
177
+ if (parts.length > 1) {
178
+ directories.add(parts[0]);
179
+ }
180
+ }
181
+ return directories.size >= 3;
182
+ }
183
+ /**
184
+ * Check if CI/CD files were created/modified
185
+ */
186
+ function hasCICDSetup(work) {
187
+ const allFiles = [...work.filesModified, ...work.filesCreated];
188
+ return allFiles.some((filePath) => CI_CD_PATTERNS.some((pattern) => filePath.includes(pattern)));
189
+ }
190
+ /**
191
+ * Generate human-readable signal descriptions
192
+ */
193
+ function describeSignals(signals, work) {
194
+ const descriptions = [];
195
+ if (signals.filesCreated > 0) {
196
+ descriptions.push(`${signals.filesCreated} file${signals.filesCreated > 1 ? 's' : ''} created`);
197
+ }
198
+ if (signals.filesModified > 0) {
199
+ descriptions.push(`${signals.filesModified} file${signals.filesModified > 1 ? 's' : ''} modified`);
200
+ }
201
+ if (signals.testsModified > 0) {
202
+ descriptions.push(`${signals.testsModified} test file${signals.testsModified > 1 ? 's' : ''}`);
203
+ }
204
+ if (signals.docsCreated > 0) {
205
+ descriptions.push(`${signals.docsCreated} documentation file${signals.docsCreated > 1 ? 's' : ''}`);
206
+ }
207
+ if (signals.versionBump) {
208
+ descriptions.push('Version bump detected');
209
+ }
210
+ if (signals.configFiles > 0) {
211
+ descriptions.push(`${signals.configFiles} config file${signals.configFiles > 1 ? 's' : ''}`);
212
+ }
213
+ if (signals.structuralChanges > 0) {
214
+ descriptions.push(`${signals.structuralChanges} structural change${signals.structuralChanges > 1 ? 's' : ''}`);
215
+ }
216
+ if (hasMultiDirectoryWork(work)) {
217
+ descriptions.push('Multi-directory changes');
218
+ }
219
+ if (hasCICDSetup(work)) {
220
+ descriptions.push('CI/CD setup');
221
+ }
222
+ if (signals.toolDiversity >= 5) {
223
+ descriptions.push(`${signals.toolDiversity} different tools used`);
224
+ }
225
+ return descriptions;
226
+ }
227
+ /**
228
+ * Generate a summary description of the work
229
+ */
230
+ function generateSummary(rating, signals, work) {
231
+ const ratingLabels = {
232
+ 1: 'Trivial',
233
+ 2: 'Simple',
234
+ 3: 'Moderate',
235
+ 4: 'Complex',
236
+ 5: 'Very Complex',
237
+ };
238
+ const label = ratingLabels[rating] || 'Unknown';
239
+ const totalFiles = signals.filesCreated + signals.filesModified;
240
+ // Generate contextual summary
241
+ if (rating === 1) {
242
+ if (totalFiles === 0) {
243
+ return `${label}: Read-only session or minimal interaction`;
244
+ }
245
+ return `${label}: Minor change to ${totalFiles} file${totalFiles > 1 ? 's' : ''}`;
246
+ }
247
+ if (rating === 2) {
248
+ return `${label}: ${totalFiles} file${totalFiles > 1 ? 's' : ''} affected`;
249
+ }
250
+ if (rating === 3) {
251
+ const extras = [];
252
+ if (signals.testsModified > 0)
253
+ extras.push('tests');
254
+ if (signals.docsCreated > 0)
255
+ extras.push('docs');
256
+ const extra = extras.length > 0 ? ` with ${extras.join(' and ')}` : '';
257
+ return `${label}: Feature implementation${extra}`;
258
+ }
259
+ if (rating === 4) {
260
+ return `${label}: Multi-file refactor or significant feature`;
261
+ }
262
+ // Rating 5
263
+ const highlights = [];
264
+ if (signals.versionBump)
265
+ highlights.push('version bump');
266
+ if (hasCICDSetup(work))
267
+ highlights.push('CI/CD');
268
+ if (signals.docsCreated >= 2)
269
+ highlights.push('documentation');
270
+ const highlight = highlights.length > 0 ? `: ${highlights.join(', ')}` : '';
271
+ return `${label}: Major milestone${highlight}`;
272
+ }
273
+ /**
274
+ * Get rating label
275
+ */
276
+ function getRatingLabel(rating) {
277
+ const labels = {
278
+ 1: 'Trivial',
279
+ 2: 'Simple',
280
+ 3: 'Moderate',
281
+ 4: 'Complex',
282
+ 5: 'Very Complex',
283
+ };
284
+ return labels[rating] || 'Unknown';
285
+ }
286
+ module.exports = {
287
+ rateComplexity,
288
+ getRatingLabel,
289
+ THRESHOLDS,
290
+ };
@@ -0,0 +1,263 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const path = require('path');
4
+ const fs = require('fs');
5
+ const { execSync } = require('child_process');
6
+ const ENV_FILE = '.agents/.env';
7
+ /**
8
+ * Parse a .env file into key-value pairs
9
+ */
10
+ function parseEnvFile(content) {
11
+ const env = {};
12
+ content.split(/\r?\n/).forEach((line) => {
13
+ if (!line || line.startsWith('#'))
14
+ return;
15
+ const idx = line.indexOf('=');
16
+ if (idx === -1)
17
+ return;
18
+ env[line.slice(0, idx).trim()] = line.slice(idx + 1).trim();
19
+ });
20
+ return env;
21
+ }
22
+ /**
23
+ * Read the .agents/.env file
24
+ */
25
+ function readEnvConfig() {
26
+ const envPath = path.join(process.cwd(), ENV_FILE);
27
+ try {
28
+ if (fs.existsSync(envPath)) {
29
+ const content = fs.readFileSync(envPath, 'utf8');
30
+ return parseEnvFile(content);
31
+ }
32
+ }
33
+ catch (_err) {
34
+ // File doesn't exist or is invalid
35
+ }
36
+ return {};
37
+ }
38
+ /**
39
+ * Write or update PROJECT_NAME in .agents/.env
40
+ * Preserves existing entries
41
+ */
42
+ function writeProjectName(projectName, detectedFrom) {
43
+ const envPath = path.join(process.cwd(), ENV_FILE);
44
+ const agentsDir = path.join(process.cwd(), '.agents');
45
+ try {
46
+ // Ensure .agents directory exists
47
+ if (!fs.existsSync(agentsDir)) {
48
+ fs.mkdirSync(agentsDir, { recursive: true });
49
+ }
50
+ // Read existing content
51
+ let lines = [];
52
+ if (fs.existsSync(envPath)) {
53
+ lines = fs.readFileSync(envPath, 'utf8').split(/\r?\n/);
54
+ }
55
+ // Check if PROJECT_NAME already exists
56
+ const hasProjectName = lines.some(line => line.trim().startsWith('PROJECT_NAME='));
57
+ if (!hasProjectName) {
58
+ // Add PROJECT_NAME with a comment about detection source
59
+ const newLines = [
60
+ `# Project name detected from ${detectedFrom}`,
61
+ `PROJECT_NAME=${projectName}`,
62
+ '',
63
+ ];
64
+ // Prepend to existing content
65
+ const content = [...newLines, ...lines].join('\n');
66
+ fs.writeFileSync(envPath, content);
67
+ return true;
68
+ }
69
+ }
70
+ catch (_err) {
71
+ // Failed to write - continue without persisting
72
+ }
73
+ return false;
74
+ }
75
+ /**
76
+ * Detect project name from package.json
77
+ */
78
+ function detectFromPackageJson() {
79
+ try {
80
+ const pkgPath = path.join(process.cwd(), 'package.json');
81
+ if (fs.existsSync(pkgPath)) {
82
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
83
+ if (pkg.name && typeof pkg.name === 'string') {
84
+ // Strip org scope if present (e.g., @org/package -> package)
85
+ return pkg.name.replace(/^@[^/]+\//, '');
86
+ }
87
+ }
88
+ }
89
+ catch (_err) {
90
+ // Ignore parse errors
91
+ }
92
+ return null;
93
+ }
94
+ /**
95
+ * Detect project name from pyproject.toml (Python)
96
+ */
97
+ function detectFromPyproject() {
98
+ try {
99
+ const pyPath = path.join(process.cwd(), 'pyproject.toml');
100
+ if (fs.existsSync(pyPath)) {
101
+ const content = fs.readFileSync(pyPath, 'utf8');
102
+ // Simple regex to find name in [project] or [tool.poetry] section
103
+ const match = content.match(/^\s*name\s*=\s*["']([^"']+)["']/m);
104
+ if (match) {
105
+ return match[1];
106
+ }
107
+ }
108
+ }
109
+ catch (_err) {
110
+ // Ignore errors
111
+ }
112
+ return null;
113
+ }
114
+ /**
115
+ * Detect project name from Cargo.toml (Rust)
116
+ */
117
+ function detectFromCargoToml() {
118
+ try {
119
+ const cargoPath = path.join(process.cwd(), 'Cargo.toml');
120
+ if (fs.existsSync(cargoPath)) {
121
+ const content = fs.readFileSync(cargoPath, 'utf8');
122
+ const match = content.match(/^\s*name\s*=\s*["']([^"']+)["']/m);
123
+ if (match) {
124
+ return match[1];
125
+ }
126
+ }
127
+ }
128
+ catch (_err) {
129
+ // Ignore errors
130
+ }
131
+ return null;
132
+ }
133
+ /**
134
+ * Detect project name from go.mod (Go)
135
+ */
136
+ function detectFromGoMod() {
137
+ try {
138
+ const goModPath = path.join(process.cwd(), 'go.mod');
139
+ if (fs.existsSync(goModPath)) {
140
+ const content = fs.readFileSync(goModPath, 'utf8');
141
+ // Module line like: module github.com/user/project
142
+ const match = content.match(/^\s*module\s+(\S+)/m);
143
+ if (match) {
144
+ // Return just the last part (project name)
145
+ const parts = match[1].split('/');
146
+ return parts[parts.length - 1];
147
+ }
148
+ }
149
+ }
150
+ catch (_err) {
151
+ // Ignore errors
152
+ }
153
+ return null;
154
+ }
155
+ /**
156
+ * Detect project name using the fallback chain
157
+ * Returns { name, source } where source indicates where it was detected from
158
+ */
159
+ function detectProjectName() {
160
+ // 1. Try package.json
161
+ const pkgName = detectFromPackageJson();
162
+ if (pkgName) {
163
+ return { name: pkgName, source: 'package.json' };
164
+ }
165
+ // 2. Try pyproject.toml
166
+ const pyName = detectFromPyproject();
167
+ if (pyName) {
168
+ return { name: pyName, source: 'pyproject.toml' };
169
+ }
170
+ // 3. Try Cargo.toml
171
+ const cargoName = detectFromCargoToml();
172
+ if (cargoName) {
173
+ return { name: cargoName, source: 'Cargo.toml' };
174
+ }
175
+ // 4. Try go.mod
176
+ const goName = detectFromGoMod();
177
+ if (goName) {
178
+ return { name: goName, source: 'go.mod' };
179
+ }
180
+ // 5. Fallback to folder name
181
+ return { name: path.basename(process.cwd()), source: 'folder' };
182
+ }
183
+ /**
184
+ * Get the project name, initializing .agents/.env if needed
185
+ */
186
+ function detectRepo() {
187
+ // 1. Check for PROJECT_NAME in .agents/.env
188
+ const env = readEnvConfig();
189
+ if (env.PROJECT_NAME) {
190
+ return env.PROJECT_NAME;
191
+ }
192
+ // 2. Detect from config files and auto-initialize
193
+ const detected = detectProjectName();
194
+ writeProjectName(detected.name, detected.source);
195
+ return detected.name;
196
+ }
197
+ /**
198
+ * Get project aliases for querying (includes current name and all aliases)
199
+ */
200
+ function getProjectAliases() {
201
+ const env = readEnvConfig();
202
+ const projectName = env.PROJECT_NAME || detectRepo();
203
+ const folderName = path.basename(process.cwd());
204
+ const aliases = new Set();
205
+ aliases.add(projectName);
206
+ // Add explicit aliases from PROJECT_ALIASES
207
+ if (env.PROJECT_ALIASES) {
208
+ env.PROJECT_ALIASES.split(',').forEach(alias => {
209
+ const trimmed = alias.trim();
210
+ if (trimmed)
211
+ aliases.add(trimmed);
212
+ });
213
+ }
214
+ // Auto-include folder name if different from project name
215
+ if (folderName !== projectName) {
216
+ aliases.add(folderName);
217
+ }
218
+ return Array.from(aliases);
219
+ }
220
+ /**
221
+ * Get the env file path
222
+ */
223
+ function getEnvPath() {
224
+ return path.join(process.cwd(), ENV_FILE);
225
+ }
226
+ function detectBranch() {
227
+ try {
228
+ const out = execSync('git rev-parse --abbrev-ref HEAD', { stdio: ['ignore', 'pipe', 'ignore'] });
229
+ return out.toString().trim();
230
+ }
231
+ catch (_err) {
232
+ return null;
233
+ }
234
+ }
235
+ function repoTags({ repo, branch } = {}) {
236
+ const tags = [];
237
+ if (repo)
238
+ tags.push(`repo:${repo}`);
239
+ if (branch)
240
+ tags.push(`branch:${branch}`);
241
+ return tags;
242
+ }
243
+ /**
244
+ * Generate repo tags for all aliases (for querying)
245
+ */
246
+ function repoTagsWithAliases({ branch } = {}) {
247
+ const aliases = getProjectAliases();
248
+ return aliases.map(alias => repoTags({ repo: alias, branch }));
249
+ }
250
+ function getUserName() {
251
+ return process.env.USER || process.env.USERNAME || 'unknown';
252
+ }
253
+ module.exports = {
254
+ detectRepo,
255
+ detectBranch,
256
+ repoTags,
257
+ repoTagsWithAliases,
258
+ getProjectAliases,
259
+ getEnvPath,
260
+ getUserName,
261
+ readEnvConfig,
262
+ ENV_FILE,
263
+ };