@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.
- package/README.md +809 -0
- package/bin/dev +16 -0
- package/bin/run +5 -0
- package/dist/base-command.d.ts +21 -0
- package/dist/base-command.js +110 -0
- package/dist/commands/add.d.ts +13 -0
- package/dist/commands/add.js +73 -0
- package/dist/commands/affected.d.ts +23 -0
- package/dist/commands/affected.js +326 -0
- package/dist/commands/audit.d.ts +33 -0
- package/dist/commands/audit.js +593 -0
- package/dist/commands/back.d.ts +6 -0
- package/dist/commands/back.js +29 -0
- package/dist/commands/commit.d.ts +11 -0
- package/dist/commands/commit.js +113 -0
- package/dist/commands/context.d.ts +6 -0
- package/dist/commands/context.js +36 -0
- package/dist/commands/contexts.d.ts +7 -0
- package/dist/commands/contexts.js +92 -0
- package/dist/commands/deps.d.ts +10 -0
- package/dist/commands/deps.js +104 -0
- package/dist/commands/entity/add.d.ts +16 -0
- package/dist/commands/entity/add.js +105 -0
- package/dist/commands/entity/clone-all.d.ts +17 -0
- package/dist/commands/entity/clone-all.js +135 -0
- package/dist/commands/entity/clone.d.ts +15 -0
- package/dist/commands/entity/clone.js +109 -0
- package/dist/commands/entity/list.d.ts +11 -0
- package/dist/commands/entity/list.js +82 -0
- package/dist/commands/entity/remove.d.ts +12 -0
- package/dist/commands/entity/remove.js +58 -0
- package/dist/commands/focus.d.ts +19 -0
- package/dist/commands/focus.js +139 -0
- package/dist/commands/graph.d.ts +18 -0
- package/dist/commands/graph.js +238 -0
- package/dist/commands/init.d.ts +11 -0
- package/dist/commands/init.js +84 -0
- package/dist/commands/insights.d.ts +21 -0
- package/dist/commands/insights.js +434 -0
- package/dist/commands/patterns.d.ts +40 -0
- package/dist/commands/patterns.js +412 -0
- package/dist/commands/pull.d.ts +11 -0
- package/dist/commands/pull.js +121 -0
- package/dist/commands/push.d.ts +11 -0
- package/dist/commands/push.js +101 -0
- package/dist/commands/quick-setup.d.ts +20 -0
- package/dist/commands/quick-setup.js +422 -0
- package/dist/commands/recent.d.ts +9 -0
- package/dist/commands/recent.js +55 -0
- package/dist/commands/related.d.ts +23 -0
- package/dist/commands/related.js +257 -0
- package/dist/commands/repos.d.ts +14 -0
- package/dist/commands/repos.js +185 -0
- package/dist/commands/stack.d.ts +10 -0
- package/dist/commands/stack.js +83 -0
- package/dist/commands/status.d.ts +14 -0
- package/dist/commands/status.js +246 -0
- package/dist/commands/sync.d.ts +11 -0
- package/dist/commands/sync.js +142 -0
- package/dist/commands/unfocus.d.ts +6 -0
- package/dist/commands/unfocus.js +23 -0
- package/dist/commands/used-by.d.ts +10 -0
- package/dist/commands/used-by.js +111 -0
- package/dist/commands/workspace.d.ts +20 -0
- package/dist/commands/workspace.js +365 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +5 -0
- package/dist/models/entity.model.d.ts +81 -0
- package/dist/models/entity.model.js +2 -0
- package/dist/services/config.service.d.ts +34 -0
- package/dist/services/config.service.js +230 -0
- package/dist/services/entity.service.d.ts +19 -0
- package/dist/services/entity.service.js +130 -0
- package/dist/services/focus.service.d.ts +70 -0
- package/dist/services/focus.service.js +587 -0
- package/dist/services/git.service.d.ts +37 -0
- package/dist/services/git.service.js +180 -0
- package/dist/utils/display.d.ts +25 -0
- package/dist/utils/display.js +150 -0
- package/dist/utils/filesystem.d.ts +32 -0
- package/dist/utils/filesystem.js +220 -0
- package/dist/utils/index.d.ts +13 -0
- package/dist/utils/index.js +18 -0
- package/dist/utils/validation.d.ts +22 -0
- package/dist/utils/validation.js +196 -0
- package/oclif.manifest.json +1463 -0
- 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
|
+
}
|