@hyperdrive.bot/gut 0.1.3

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 (87) hide show
  1. package/README.md +809 -0
  2. package/bin/dev +16 -0
  3. package/bin/run +5 -0
  4. package/dist/base-command.d.ts +21 -0
  5. package/dist/base-command.js +110 -0
  6. package/dist/commands/add.d.ts +13 -0
  7. package/dist/commands/add.js +73 -0
  8. package/dist/commands/affected.d.ts +23 -0
  9. package/dist/commands/affected.js +326 -0
  10. package/dist/commands/audit.d.ts +33 -0
  11. package/dist/commands/audit.js +593 -0
  12. package/dist/commands/back.d.ts +6 -0
  13. package/dist/commands/back.js +29 -0
  14. package/dist/commands/commit.d.ts +11 -0
  15. package/dist/commands/commit.js +113 -0
  16. package/dist/commands/context.d.ts +6 -0
  17. package/dist/commands/context.js +36 -0
  18. package/dist/commands/contexts.d.ts +7 -0
  19. package/dist/commands/contexts.js +92 -0
  20. package/dist/commands/deps.d.ts +10 -0
  21. package/dist/commands/deps.js +104 -0
  22. package/dist/commands/entity/add.d.ts +16 -0
  23. package/dist/commands/entity/add.js +105 -0
  24. package/dist/commands/entity/clone-all.d.ts +17 -0
  25. package/dist/commands/entity/clone-all.js +135 -0
  26. package/dist/commands/entity/clone.d.ts +15 -0
  27. package/dist/commands/entity/clone.js +109 -0
  28. package/dist/commands/entity/list.d.ts +11 -0
  29. package/dist/commands/entity/list.js +82 -0
  30. package/dist/commands/entity/remove.d.ts +12 -0
  31. package/dist/commands/entity/remove.js +58 -0
  32. package/dist/commands/focus.d.ts +19 -0
  33. package/dist/commands/focus.js +139 -0
  34. package/dist/commands/graph.d.ts +18 -0
  35. package/dist/commands/graph.js +238 -0
  36. package/dist/commands/init.d.ts +11 -0
  37. package/dist/commands/init.js +84 -0
  38. package/dist/commands/insights.d.ts +21 -0
  39. package/dist/commands/insights.js +434 -0
  40. package/dist/commands/patterns.d.ts +40 -0
  41. package/dist/commands/patterns.js +412 -0
  42. package/dist/commands/pull.d.ts +11 -0
  43. package/dist/commands/pull.js +121 -0
  44. package/dist/commands/push.d.ts +11 -0
  45. package/dist/commands/push.js +101 -0
  46. package/dist/commands/quick-setup.d.ts +20 -0
  47. package/dist/commands/quick-setup.js +422 -0
  48. package/dist/commands/recent.d.ts +9 -0
  49. package/dist/commands/recent.js +55 -0
  50. package/dist/commands/related.d.ts +23 -0
  51. package/dist/commands/related.js +257 -0
  52. package/dist/commands/repos.d.ts +14 -0
  53. package/dist/commands/repos.js +185 -0
  54. package/dist/commands/stack.d.ts +10 -0
  55. package/dist/commands/stack.js +83 -0
  56. package/dist/commands/status.d.ts +14 -0
  57. package/dist/commands/status.js +246 -0
  58. package/dist/commands/sync.d.ts +11 -0
  59. package/dist/commands/sync.js +142 -0
  60. package/dist/commands/unfocus.d.ts +6 -0
  61. package/dist/commands/unfocus.js +23 -0
  62. package/dist/commands/used-by.d.ts +10 -0
  63. package/dist/commands/used-by.js +111 -0
  64. package/dist/commands/workspace.d.ts +20 -0
  65. package/dist/commands/workspace.js +365 -0
  66. package/dist/index.d.ts +1 -0
  67. package/dist/index.js +5 -0
  68. package/dist/models/entity.model.d.ts +81 -0
  69. package/dist/models/entity.model.js +2 -0
  70. package/dist/services/config.service.d.ts +34 -0
  71. package/dist/services/config.service.js +230 -0
  72. package/dist/services/entity.service.d.ts +19 -0
  73. package/dist/services/entity.service.js +130 -0
  74. package/dist/services/focus.service.d.ts +70 -0
  75. package/dist/services/focus.service.js +587 -0
  76. package/dist/services/git.service.d.ts +37 -0
  77. package/dist/services/git.service.js +180 -0
  78. package/dist/utils/display.d.ts +25 -0
  79. package/dist/utils/display.js +150 -0
  80. package/dist/utils/filesystem.d.ts +32 -0
  81. package/dist/utils/filesystem.js +220 -0
  82. package/dist/utils/index.d.ts +13 -0
  83. package/dist/utils/index.js +18 -0
  84. package/dist/utils/validation.d.ts +22 -0
  85. package/dist/utils/validation.js +196 -0
  86. package/oclif.manifest.json +1463 -0
  87. package/package.json +76 -0
@@ -0,0 +1,422 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const tslib_1 = require("tslib");
4
+ const core_1 = require("@oclif/core");
5
+ const base_command_1 = tslib_1.__importDefault(require("../base-command"));
6
+ const fs = tslib_1.__importStar(require("fs"));
7
+ const path = tslib_1.__importStar(require("path"));
8
+ const child_process_1 = require("child_process");
9
+ const chalk_1 = tslib_1.__importDefault(require("chalk"));
10
+ const ora_1 = tslib_1.__importDefault(require("ora"));
11
+ const inquirer_1 = tslib_1.__importDefault(require("inquirer"));
12
+ class QuickSetup extends base_command_1.default {
13
+ static description = 'Automated workspace setup based on common patterns';
14
+ static examples = [
15
+ '<%= config.bin %> <%= command.id %>',
16
+ '<%= config.bin %> <%= command.id %> --auto',
17
+ '<%= config.bin %> <%= command.id %> --profile monorepo',
18
+ '<%= config.bin %> <%= command.id %> --scan-depth 3',
19
+ ];
20
+ static flags = {
21
+ auto: core_1.Flags.boolean({
22
+ char: 'a',
23
+ description: 'Auto-detect and configure without prompts',
24
+ default: false,
25
+ }),
26
+ profile: core_1.Flags.string({
27
+ char: 'p',
28
+ description: 'Use specific setup profile',
29
+ options: ['monorepo', 'microservices', 'fullstack', 'library'],
30
+ }),
31
+ 'scan-depth': core_1.Flags.integer({
32
+ description: 'Directory depth to scan for entities',
33
+ default: 2,
34
+ }),
35
+ 'clone-missing': core_1.Flags.boolean({
36
+ description: 'Clone missing repositories if found',
37
+ default: false,
38
+ }),
39
+ };
40
+ profiles = [
41
+ {
42
+ name: 'monorepo',
43
+ description: 'Monorepo with packages or apps',
44
+ patterns: {
45
+ client: ['clients/*/package.json', 'entities/clients/*/package.json'],
46
+ prospect: ['prospects/*/package.json', 'entities/prospects/*/package.json'],
47
+ company: ['companies/*/package.json', 'entities/companies/*/package.json'],
48
+ initiative: ['initiatives/*/package.json', 'entities/initiatives/*/package.json'],
49
+ system: ['systems/*/package.json', 'shared/*/package.json'],
50
+ delivery: ['packages/*/package.json', 'apps/*/package.json'],
51
+ module: ['packages/*/package.json', 'libs/*/package.json'],
52
+ service: ['services/*/package.json', 'apis/*/package.json'],
53
+ tool: ['tools/*/package.json', 'scripts/*/package.json'],
54
+ },
55
+ repositories: {
56
+ pattern: /package\.json/,
57
+ transform: (match) => {
58
+ const pkg = JSON.parse(fs.readFileSync(match, 'utf-8'));
59
+ return pkg.repository?.url || pkg.repository || '';
60
+ },
61
+ },
62
+ },
63
+ {
64
+ name: 'microservices',
65
+ description: 'Microservices architecture',
66
+ patterns: {
67
+ client: ['clients/*/package.json'],
68
+ prospect: ['prospects/*/package.json'],
69
+ company: ['companies/*/package.json'],
70
+ initiative: ['initiatives/*/package.json'],
71
+ system: ['shared/*/package.json', 'common/*/package.json'],
72
+ service: ['services/*/package.json', 'services/*/pom.xml', 'services/*/go.mod'],
73
+ delivery: ['frontend/package.json', 'web/package.json', 'mobile/package.json'],
74
+ module: ['shared/*/package.json', 'common/*/package.json'],
75
+ tool: ['infrastructure/*/package.json', 'deployment/*/'],
76
+ },
77
+ },
78
+ {
79
+ name: 'fullstack',
80
+ description: 'Full-stack application',
81
+ patterns: {
82
+ client: ['clients/*/package.json'],
83
+ prospect: ['prospects/*/package.json'],
84
+ company: ['companies/*/package.json'],
85
+ initiative: ['initiatives/*/package.json'],
86
+ system: ['shared/package.json', 'common/package.json'],
87
+ delivery: ['frontend/package.json', 'client/package.json', 'web/package.json'],
88
+ service: ['backend/package.json', 'server/package.json', 'api/package.json'],
89
+ module: ['shared/package.json', 'common/package.json'],
90
+ tool: ['scripts/package.json', 'tools/package.json'],
91
+ },
92
+ },
93
+ {
94
+ name: 'library',
95
+ description: 'Library or package development',
96
+ patterns: {
97
+ client: [],
98
+ prospect: [],
99
+ company: [],
100
+ initiative: [],
101
+ system: ['package.json'],
102
+ module: ['src/package.json', 'lib/package.json', 'package.json'],
103
+ delivery: ['examples/*/package.json', 'demo/*/package.json'],
104
+ service: [],
105
+ tool: ['scripts/package.json', 'tools/package.json'],
106
+ },
107
+ },
108
+ ];
109
+ async run() {
110
+ const { flags } = await this.parse(QuickSetup);
111
+ const { auto, profile, 'scan-depth': scanDepth, 'clone-missing': cloneMissing } = flags;
112
+ const spinner = (0, ora_1.default)();
113
+ try {
114
+ // Check if workspace is already initialized
115
+ const config = await this.configService.loadConfig();
116
+ if (config.entities && Object.keys(config.entities).length > 0) {
117
+ const { proceed } = await inquirer_1.default.prompt([{
118
+ type: 'confirm',
119
+ name: 'proceed',
120
+ message: 'Workspace already has entities configured. Continue setup?',
121
+ default: false,
122
+ }]);
123
+ if (!proceed) {
124
+ this.log(chalk_1.default.yellow('Setup cancelled'));
125
+ return;
126
+ }
127
+ }
128
+ spinner.start('Scanning workspace structure');
129
+ // Detect workspace structure
130
+ const detectedEntities = await this.scanWorkspace(scanDepth);
131
+ spinner.succeed(`Found ${detectedEntities.length} potential entities`);
132
+ // Select or detect profile
133
+ let selectedProfile;
134
+ if (profile) {
135
+ selectedProfile = this.profiles.find(p => p.name === profile);
136
+ if (!selectedProfile) {
137
+ this.error(`Profile '${profile}' not found`);
138
+ }
139
+ }
140
+ else if (auto) {
141
+ selectedProfile = this.detectProfile(detectedEntities);
142
+ this.log(chalk_1.default.blue(`Auto-detected profile: ${selectedProfile.name}`));
143
+ }
144
+ else {
145
+ const { selectedProfileName } = await inquirer_1.default.prompt([{
146
+ type: 'list',
147
+ name: 'selectedProfileName',
148
+ message: 'Select workspace profile:',
149
+ choices: this.profiles.map(p => ({
150
+ name: `${p.name} - ${p.description}`,
151
+ value: p.name,
152
+ })),
153
+ }]);
154
+ selectedProfile = this.profiles.find(p => p.name === selectedProfileName);
155
+ }
156
+ // Categorize entities
157
+ const categorizedEntities = this.categorizeEntities(detectedEntities, selectedProfile);
158
+ // Show and confirm entities
159
+ if (!auto) {
160
+ this.printDetectedEntities(categorizedEntities);
161
+ const { confirmSetup } = await inquirer_1.default.prompt([{
162
+ type: 'confirm',
163
+ name: 'confirmSetup',
164
+ message: 'Proceed with this configuration?',
165
+ default: true,
166
+ }]);
167
+ if (!confirmSetup) {
168
+ this.log(chalk_1.default.yellow('Setup cancelled'));
169
+ return;
170
+ }
171
+ }
172
+ // Initialize workspace if needed
173
+ if (!config.workspace) {
174
+ spinner.start('Initializing workspace');
175
+ await this.configService.initWorkspace(process.cwd());
176
+ spinner.succeed('Workspace initialized');
177
+ }
178
+ // Add entities
179
+ spinner.start('Adding entities to configuration');
180
+ let addedCount = 0;
181
+ let skippedCount = 0;
182
+ for (const [type, entities] of Object.entries(categorizedEntities)) {
183
+ for (const entity of entities) {
184
+ if (!entity.name || !entity.path) {
185
+ continue;
186
+ }
187
+ const existing = await this.entityService.getEntity(entity.name);
188
+ if (existing) {
189
+ skippedCount++;
190
+ continue;
191
+ }
192
+ await this.entityService.addEntity(entity.name, type, entity.path, entity.repository);
193
+ addedCount++;
194
+ }
195
+ }
196
+ spinner.succeed(`Added ${addedCount} entities (${skippedCount} skipped)`);
197
+ // Clone missing repositories if requested
198
+ if (cloneMissing) {
199
+ const missingEntities = await this.findMissingEntities();
200
+ if (missingEntities.length > 0) {
201
+ spinner.start(`Cloning ${missingEntities.length} missing repositories`);
202
+ for (const entity of missingEntities) {
203
+ if (entity.repository) {
204
+ try {
205
+ (0, child_process_1.execSync)(`git clone ${entity.repository} ${entity.path}`, {
206
+ stdio: 'pipe',
207
+ });
208
+ }
209
+ catch (error) {
210
+ // Continue on error
211
+ }
212
+ }
213
+ }
214
+ spinner.succeed('Cloning complete');
215
+ }
216
+ }
217
+ // Set initial focus
218
+ if (addedCount > 0) {
219
+ const allEntities = await this.entityService.listEntities();
220
+ const mainEntity = allEntities.find(e => e.name.includes('main') || e.name.includes('core') || e.name.includes('app')) || allEntities[0];
221
+ if (mainEntity) {
222
+ await this.focusService.setFocus([mainEntity.name]);
223
+ this.log(chalk_1.default.green(`āœ“ Initial focus set to: ${mainEntity.name}`));
224
+ }
225
+ }
226
+ // Print summary
227
+ this.printSetupSummary(addedCount, skippedCount);
228
+ // Show next steps
229
+ this.printNextSteps();
230
+ }
231
+ catch (error) {
232
+ spinner.fail();
233
+ this.error(error.message);
234
+ }
235
+ }
236
+ async scanWorkspace(maxDepth) {
237
+ const entities = [];
238
+ const seen = new Set();
239
+ const scan = (dir, depth) => {
240
+ if (depth > maxDepth)
241
+ return;
242
+ try {
243
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
244
+ for (const entry of entries) {
245
+ if (!entry.isDirectory())
246
+ continue;
247
+ const name = entry.name;
248
+ if (name.startsWith('.') || name === 'node_modules' || name === 'dist' || name === 'build') {
249
+ continue;
250
+ }
251
+ const fullPath = path.join(dir, name);
252
+ const relativePath = path.relative(process.cwd(), fullPath);
253
+ // Check for entity indicators
254
+ const indicators = [
255
+ 'package.json',
256
+ 'pom.xml',
257
+ 'go.mod',
258
+ 'Cargo.toml',
259
+ 'requirements.txt',
260
+ 'Gemfile',
261
+ 'build.gradle',
262
+ ];
263
+ const hasIndicator = indicators.some(ind => fs.existsSync(path.join(fullPath, ind)));
264
+ if (hasIndicator && !seen.has(relativePath)) {
265
+ seen.add(relativePath);
266
+ // Try to determine name and repository
267
+ let entityName = name;
268
+ let repository;
269
+ const pkgPath = path.join(fullPath, 'package.json');
270
+ if (fs.existsSync(pkgPath)) {
271
+ try {
272
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
273
+ entityName = pkg.name || name;
274
+ repository = pkg.repository?.url || pkg.repository;
275
+ }
276
+ catch (error) {
277
+ // Use directory name
278
+ }
279
+ }
280
+ entities.push({
281
+ name: entityName,
282
+ path: relativePath,
283
+ repository,
284
+ });
285
+ }
286
+ // Recurse
287
+ scan(fullPath, depth + 1);
288
+ }
289
+ }
290
+ catch (error) {
291
+ // Ignore permission errors
292
+ }
293
+ };
294
+ scan(process.cwd(), 0);
295
+ return entities;
296
+ }
297
+ detectProfile(entities) {
298
+ const pathPatterns = entities.map(e => e.path || '');
299
+ // Check for monorepo patterns
300
+ const hasPackages = pathPatterns.some(p => p.startsWith('packages/'));
301
+ const hasApps = pathPatterns.some(p => p.startsWith('apps/'));
302
+ if (hasPackages || hasApps) {
303
+ return this.profiles[0]; // monorepo
304
+ }
305
+ // Check for microservices patterns
306
+ const hasServices = pathPatterns.some(p => p.startsWith('services/'));
307
+ if (hasServices) {
308
+ return this.profiles[1]; // microservices
309
+ }
310
+ // Check for fullstack patterns
311
+ const hasFrontend = pathPatterns.some(p => p.includes('frontend') || p.includes('client') || p.includes('web'));
312
+ const hasBackend = pathPatterns.some(p => p.includes('backend') || p.includes('server') || p.includes('api'));
313
+ if (hasFrontend && hasBackend) {
314
+ return this.profiles[2]; // fullstack
315
+ }
316
+ // Default to library
317
+ return this.profiles[3];
318
+ }
319
+ categorizeEntities(entities, profile) {
320
+ const categorized = {
321
+ client: [],
322
+ prospect: [],
323
+ company: [],
324
+ initiative: [],
325
+ system: [],
326
+ delivery: [],
327
+ module: [],
328
+ service: [],
329
+ tool: [],
330
+ };
331
+ for (const entity of entities) {
332
+ let matched = false;
333
+ // Try to match against profile patterns
334
+ for (const [type, patterns] of Object.entries(profile.patterns)) {
335
+ for (const pattern of patterns) {
336
+ const glob = pattern.replace(/\*/g, '[^/]+');
337
+ const regex = new RegExp(`^${glob}$`);
338
+ if (regex.test(entity.path + '/package.json')) {
339
+ categorized[type].push(entity);
340
+ matched = true;
341
+ break;
342
+ }
343
+ }
344
+ if (matched)
345
+ break;
346
+ }
347
+ // If no match, categorize based on path/name
348
+ if (!matched) {
349
+ const pathLower = (entity.path || '').toLowerCase();
350
+ const nameLower = (entity.name || '').toLowerCase();
351
+ if (pathLower.includes('frontend') || pathLower.includes('client') ||
352
+ pathLower.includes('web') || pathLower.includes('app')) {
353
+ categorized.delivery.push(entity);
354
+ }
355
+ else if (pathLower.includes('service') || pathLower.includes('api') ||
356
+ pathLower.includes('backend') || pathLower.includes('server')) {
357
+ categorized.service.push(entity);
358
+ }
359
+ else if (pathLower.includes('lib') || pathLower.includes('shared') ||
360
+ pathLower.includes('common') || pathLower.includes('core')) {
361
+ categorized.module.push(entity);
362
+ }
363
+ else if (pathLower.includes('tool') || pathLower.includes('script') ||
364
+ pathLower.includes('util') || pathLower.includes('infra')) {
365
+ categorized.tool.push(entity);
366
+ }
367
+ else {
368
+ // Default to module
369
+ categorized.module.push(entity);
370
+ }
371
+ }
372
+ }
373
+ return categorized;
374
+ }
375
+ async findMissingEntities() {
376
+ const entities = await this.entityService.listEntities();
377
+ return entities.filter(e => !fs.existsSync(e.path));
378
+ }
379
+ printDetectedEntities(categorized) {
380
+ this.log('');
381
+ this.log(chalk_1.default.bold('Detected Entities:'));
382
+ this.log('');
383
+ for (const [type, entities] of Object.entries(categorized)) {
384
+ if (entities.length === 0)
385
+ continue;
386
+ this.log(chalk_1.default.cyan(` ${type}:`));
387
+ entities.forEach(e => {
388
+ const repo = e.repository ? chalk_1.default.dim(` (${e.repository})`) : '';
389
+ this.log(` - ${e.name} ${chalk_1.default.dim(`[${e.path}]`)}${repo}`);
390
+ });
391
+ }
392
+ }
393
+ printSetupSummary(added, skipped) {
394
+ this.log('');
395
+ this.log(chalk_1.default.bold.green('āœ“ Setup Complete!'));
396
+ this.log('');
397
+ this.log(chalk_1.default.dim('Summary:'));
398
+ this.log(` • Entities added: ${added}`);
399
+ this.log(` • Entities skipped: ${skipped}`);
400
+ this.log(` • Total entities: ${added + skipped}`);
401
+ }
402
+ printNextSteps() {
403
+ this.log('');
404
+ this.log(chalk_1.default.bold('Next Steps:'));
405
+ this.log('');
406
+ this.log(' 1. List all entities:');
407
+ this.log(chalk_1.default.dim(' gut entity list'));
408
+ this.log('');
409
+ this.log(' 2. Set focus on specific entities:');
410
+ this.log(chalk_1.default.dim(' gut focus [entity-name]'));
411
+ this.log('');
412
+ this.log(' 3. Check status across focused entities:');
413
+ this.log(chalk_1.default.dim(' gut status'));
414
+ this.log('');
415
+ this.log(' 4. View workspace insights:');
416
+ this.log(chalk_1.default.dim(' gut insights'));
417
+ this.log('');
418
+ this.log(' 5. Clone missing repositories:');
419
+ this.log(chalk_1.default.dim(' gut entity clone-all'));
420
+ }
421
+ }
422
+ exports.default = QuickSetup;
@@ -0,0 +1,9 @@
1
+ import { BaseCommand } from '../base-command';
2
+ export default class Recent extends BaseCommand {
3
+ static description: string;
4
+ static examples: string[];
5
+ static flags: {
6
+ limit: import("@oclif/core/lib/interfaces").OptionFlag<number, import("@oclif/core/lib/interfaces").CustomOptions>;
7
+ };
8
+ run(): Promise<void>;
9
+ }
@@ -0,0 +1,55 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const tslib_1 = require("tslib");
4
+ const base_command_1 = require("../base-command");
5
+ const chalk_1 = tslib_1.__importDefault(require("chalk"));
6
+ const core_1 = require("@oclif/core");
7
+ class Recent extends base_command_1.BaseCommand {
8
+ static description = 'Show recent focus history';
9
+ static examples = [
10
+ '<%= config.bin %> <%= command.id %>',
11
+ '<%= config.bin %> <%= command.id %> --limit 10',
12
+ ];
13
+ static flags = {
14
+ limit: core_1.Flags.integer({
15
+ char: 'l',
16
+ description: 'Number of recent items to show',
17
+ default: 5,
18
+ }),
19
+ };
20
+ async run() {
21
+ const { flags } = await this.parse(Recent);
22
+ const configService = this.configService;
23
+ const history = await configService.getHistory();
24
+ const focusedEntities = await this.focusService.getFocusedEntities();
25
+ const currentFocusNames = focusedEntities.map(e => e.name);
26
+ if (history.length === 0) {
27
+ this.log(chalk_1.default.yellow('No focus history available'));
28
+ return;
29
+ }
30
+ this.log(chalk_1.default.bold('\nšŸ“œ Recent Focus History'));
31
+ this.log(chalk_1.default.dim('─'.repeat(50)));
32
+ const limit = Math.min(flags.limit, history.length);
33
+ for (let i = 0; i < limit; i++) {
34
+ const entry = history[i];
35
+ const isCurrent = i === 0 &&
36
+ currentFocusNames.length === entry.entities.length &&
37
+ currentFocusNames.every(e => entry.entities.includes(e));
38
+ const marker = isCurrent ? chalk_1.default.green('ā–ø') : chalk_1.default.dim('•');
39
+ const label = isCurrent ? chalk_1.default.green(' (current)') : '';
40
+ const timestamp = new Date(entry.timestamp).toLocaleString();
41
+ this.log(`\n${marker} ${chalk_1.default.bold(entry.entities.join(', '))}${label}`);
42
+ this.log(` ${chalk_1.default.dim('Time:')} ${timestamp}`);
43
+ if (i === 0 && isCurrent) {
44
+ this.log(` ${chalk_1.default.dim('Status:')} ${chalk_1.default.green('Active')}`);
45
+ }
46
+ }
47
+ this.log(chalk_1.default.dim('\n─'.repeat(50)));
48
+ this.log(`${chalk_1.default.dim('Total history entries:')} ${history.length}`);
49
+ if (history.length > limit) {
50
+ this.log(chalk_1.default.dim(`Showing ${limit} of ${history.length} entries`));
51
+ }
52
+ this.log(chalk_1.default.dim('\nTip: Use "gut back" to switch to previous focus'));
53
+ }
54
+ }
55
+ exports.default = Recent;
@@ -0,0 +1,23 @@
1
+ import BaseCommand from '../base-command';
2
+ export default class Related extends BaseCommand {
3
+ static description: string;
4
+ static examples: string[];
5
+ static flags: {
6
+ threshold: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
7
+ type: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
8
+ detailed: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
9
+ };
10
+ static args: {
11
+ entity: import("@oclif/core/lib/interfaces").Arg<string | undefined, Record<string, unknown>>;
12
+ };
13
+ run(): Promise<void>;
14
+ private analyzeRelation;
15
+ private findDependencyRelations;
16
+ private extractDependencies;
17
+ private findSharedFiles;
18
+ private getFileList;
19
+ private isSimilarFile;
20
+ private findGitReferences;
21
+ private calculateScore;
22
+ private printRelations;
23
+ }