@hyperdrive.bot/gut 0.1.4 → 0.1.6

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 +1 -779
  2. package/bin/run.js +5 -0
  3. package/package.json +10 -10
  4. package/bin/run +0 -5
  5. package/dist/base-command.d.ts +0 -21
  6. package/dist/base-command.js +0 -110
  7. package/dist/commands/add.d.ts +0 -13
  8. package/dist/commands/add.js +0 -73
  9. package/dist/commands/affected.d.ts +0 -23
  10. package/dist/commands/affected.js +0 -326
  11. package/dist/commands/audit.d.ts +0 -33
  12. package/dist/commands/audit.js +0 -593
  13. package/dist/commands/back.d.ts +0 -6
  14. package/dist/commands/back.js +0 -29
  15. package/dist/commands/commit.d.ts +0 -11
  16. package/dist/commands/commit.js +0 -113
  17. package/dist/commands/context.d.ts +0 -6
  18. package/dist/commands/context.js +0 -36
  19. package/dist/commands/contexts.d.ts +0 -7
  20. package/dist/commands/contexts.js +0 -92
  21. package/dist/commands/deps.d.ts +0 -10
  22. package/dist/commands/deps.js +0 -104
  23. package/dist/commands/entity/add.d.ts +0 -16
  24. package/dist/commands/entity/add.js +0 -105
  25. package/dist/commands/entity/clone-all.d.ts +0 -17
  26. package/dist/commands/entity/clone-all.js +0 -135
  27. package/dist/commands/entity/clone.d.ts +0 -15
  28. package/dist/commands/entity/clone.js +0 -109
  29. package/dist/commands/entity/list.d.ts +0 -11
  30. package/dist/commands/entity/list.js +0 -82
  31. package/dist/commands/entity/remove.d.ts +0 -12
  32. package/dist/commands/entity/remove.js +0 -58
  33. package/dist/commands/focus.d.ts +0 -19
  34. package/dist/commands/focus.js +0 -139
  35. package/dist/commands/graph.d.ts +0 -18
  36. package/dist/commands/graph.js +0 -238
  37. package/dist/commands/init.d.ts +0 -11
  38. package/dist/commands/init.js +0 -84
  39. package/dist/commands/insights.d.ts +0 -21
  40. package/dist/commands/insights.js +0 -434
  41. package/dist/commands/patterns.d.ts +0 -40
  42. package/dist/commands/patterns.js +0 -412
  43. package/dist/commands/pull.d.ts +0 -11
  44. package/dist/commands/pull.js +0 -121
  45. package/dist/commands/push.d.ts +0 -11
  46. package/dist/commands/push.js +0 -101
  47. package/dist/commands/quick-setup.d.ts +0 -20
  48. package/dist/commands/quick-setup.js +0 -422
  49. package/dist/commands/recent.d.ts +0 -9
  50. package/dist/commands/recent.js +0 -55
  51. package/dist/commands/related.d.ts +0 -23
  52. package/dist/commands/related.js +0 -257
  53. package/dist/commands/repos.d.ts +0 -14
  54. package/dist/commands/repos.js +0 -185
  55. package/dist/commands/stack.d.ts +0 -10
  56. package/dist/commands/stack.js +0 -83
  57. package/dist/commands/status.d.ts +0 -14
  58. package/dist/commands/status.js +0 -246
  59. package/dist/commands/sync.d.ts +0 -11
  60. package/dist/commands/sync.js +0 -142
  61. package/dist/commands/unfocus.d.ts +0 -6
  62. package/dist/commands/unfocus.js +0 -23
  63. package/dist/commands/used-by.d.ts +0 -10
  64. package/dist/commands/used-by.js +0 -111
  65. package/dist/commands/workspace.d.ts +0 -20
  66. package/dist/commands/workspace.js +0 -365
  67. package/dist/index.d.ts +0 -1
  68. package/dist/index.js +0 -5
  69. package/dist/models/entity.model.d.ts +0 -81
  70. package/dist/models/entity.model.js +0 -2
  71. package/dist/services/config.service.d.ts +0 -34
  72. package/dist/services/config.service.js +0 -230
  73. package/dist/services/entity.service.d.ts +0 -19
  74. package/dist/services/entity.service.js +0 -130
  75. package/dist/services/focus.service.d.ts +0 -70
  76. package/dist/services/focus.service.js +0 -587
  77. package/dist/services/git.service.d.ts +0 -37
  78. package/dist/services/git.service.js +0 -180
  79. package/dist/utils/display.d.ts +0 -25
  80. package/dist/utils/display.js +0 -150
  81. package/dist/utils/filesystem.d.ts +0 -32
  82. package/dist/utils/filesystem.js +0 -220
  83. package/dist/utils/index.d.ts +0 -13
  84. package/dist/utils/index.js +0 -18
  85. package/dist/utils/validation.d.ts +0 -22
  86. package/dist/utils/validation.js +0 -196
  87. package/oclif.manifest.json +0 -1463
@@ -1,246 +0,0 @@
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 = require("../base-command");
6
- const chalk_1 = tslib_1.__importDefault(require("chalk"));
7
- class Status extends base_command_1.BaseCommand {
8
- static description = 'Show git status for focused entities';
9
- static examples = [
10
- '<%= config.bin %> <%= command.id %>',
11
- '<%= config.bin %> <%= command.id %> --all',
12
- '<%= config.bin %> <%= command.id %> --verbose',
13
- ];
14
- static flags = {
15
- all: core_1.Flags.boolean({
16
- char: 'a',
17
- description: 'show status for all entities',
18
- }),
19
- verbose: core_1.Flags.boolean({
20
- char: 'v',
21
- description: 'show detailed status',
22
- }),
23
- json: core_1.Flags.boolean({
24
- description: 'output as JSON',
25
- }),
26
- };
27
- async run() {
28
- const { flags } = await this.parse(Status);
29
- // Get entities to check
30
- let entities = flags.all
31
- ? this.entityService.getAllEntities()
32
- : await this.focusService.getFocusedEntities();
33
- if (entities.length === 0) {
34
- if (flags.all) {
35
- this.error('No entities configured');
36
- }
37
- else {
38
- this.error('No entities focused. Use "gut focus <entity>" first.');
39
- }
40
- }
41
- // Collect status for each entity
42
- const statuses = [];
43
- for (const entity of entities) {
44
- const entityPath = this.entityService.resolveEntityPath(entity);
45
- try {
46
- // Check if it's a git repository
47
- const isRepo = await this.gitService.isRepository(entityPath);
48
- if (isRepo) {
49
- const status = await this.gitService.getStatus(entityPath);
50
- statuses.push({
51
- ...status,
52
- entity: entity.name,
53
- type: entity.type,
54
- hasChanges: status.hasChanges,
55
- error: undefined
56
- });
57
- }
58
- else {
59
- statuses.push({
60
- entity: entity.name,
61
- type: entity.type,
62
- path: entityPath,
63
- branch: '',
64
- ahead: 0,
65
- behind: 0,
66
- changes: [],
67
- untracked: [],
68
- hasChanges: false,
69
- error: 'Not a git repository'
70
- });
71
- }
72
- }
73
- catch (error) {
74
- statuses.push({
75
- entity: entity.name,
76
- type: entity.type,
77
- path: entityPath,
78
- branch: '',
79
- ahead: 0,
80
- behind: 0,
81
- changes: [],
82
- untracked: [],
83
- hasChanges: false,
84
- error: error.message
85
- });
86
- }
87
- }
88
- // Output results
89
- if (flags.json) {
90
- this.log(JSON.stringify(statuses, null, 2));
91
- return;
92
- }
93
- // Enhanced workspace-style display
94
- await this.displayEnhancedStatus(statuses, flags.verbose, flags.json);
95
- }
96
- displayStatus(status, verbose) {
97
- const icon = this.getTypeEmoji(status.type);
98
- const statusIcon = status.error
99
- ? 'āŒ'
100
- : status.hasChanges
101
- ? 'ā—'
102
- : 'āœ“';
103
- this.log(`${icon} ${status.entity} ${statusIcon}`);
104
- if (status.error) {
105
- this.log(` Error: ${status.error}`);
106
- return;
107
- }
108
- this.log(` Branch: ${status.branch}`);
109
- // Show ahead/behind
110
- if (status.ahead > 0 || status.behind > 0) {
111
- const ahead = status.ahead > 0 ? `↑${status.ahead}` : '';
112
- const behind = status.behind > 0 ? `↓${status.behind}` : '';
113
- this.log(` Remote: ${ahead} ${behind}`.trim());
114
- }
115
- // Show changes
116
- if (status.changes.length > 0) {
117
- this.log(` Modified: ${status.changes.length} files`);
118
- if (verbose) {
119
- status.changes.slice(0, 5).forEach((change) => {
120
- this.log(` ${change}`);
121
- });
122
- if (status.changes.length > 5) {
123
- this.log(` ... and ${status.changes.length - 5} more`);
124
- }
125
- }
126
- }
127
- // Show untracked
128
- if (status.untracked.length > 0) {
129
- this.log(` Untracked: ${status.untracked.length} files`);
130
- if (verbose) {
131
- status.untracked.slice(0, 5).forEach((file) => {
132
- this.log(` ${file}`);
133
- });
134
- if (status.untracked.length > 5) {
135
- this.log(` ... and ${status.untracked.length - 5} more`);
136
- }
137
- }
138
- }
139
- if (!status.hasChanges && status.ahead === 0 && status.behind === 0) {
140
- this.log(` Clean and up to date`);
141
- }
142
- this.log(''); // Empty line between entities
143
- }
144
- async displayEnhancedStatus(statuses, verbose, json) {
145
- const currentFocus = await this.focusService.getCurrentFocus();
146
- const focusedEntities = await this.focusService.getFocusedEntities();
147
- // Header with workspace context
148
- this.log(chalk_1.default.bold('\n=== DevSquad Workspace Status ==='));
149
- if (currentFocus) {
150
- const focusDescription = await this.focusService.getFocusDescription();
151
- this.log(`šŸ“ Current focus: ${chalk_1.default.cyan(focusDescription)}`);
152
- if (currentFocus.mode) {
153
- this.log(`šŸŽÆ Mode: ${chalk_1.default.yellow(currentFocus.mode)}`);
154
- }
155
- }
156
- else {
157
- this.log(`šŸ“ Current focus: ${chalk_1.default.dim('none')}`);
158
- }
159
- // Last sync info (placeholder for now)
160
- this.log(`šŸ”„ Last sync: ${chalk_1.default.dim('just now')}`);
161
- this.log('');
162
- // Group statuses by entity type
163
- const byType = statuses.reduce((acc, status) => {
164
- if (!acc[status.type])
165
- acc[status.type] = [];
166
- acc[status.type].push(status);
167
- return acc;
168
- }, {});
169
- // Display entities by type
170
- const typeOrder = ['client', 'prospect', 'company', 'initiative', 'system', 'delivery', 'module', 'service', 'tool'];
171
- for (const type of typeOrder) {
172
- if (!byType[type] || byType[type].length === 0)
173
- continue;
174
- const entities = byType[type];
175
- const dirtyCount = entities.filter((e) => e.hasChanges).length;
176
- if (dirtyCount > 0) {
177
- this.log(chalk_1.default.bold(`${type.toUpperCase()}:`));
178
- for (const status of entities) {
179
- if (status.hasChanges) {
180
- this.displayEntityStatus(status, verbose);
181
- }
182
- }
183
- this.log('');
184
- }
185
- }
186
- // Show clean entities if verbose
187
- if (verbose) {
188
- const cleanEntities = statuses.filter(s => !s.hasChanges && !s.error);
189
- if (cleanEntities.length > 0) {
190
- this.log(chalk_1.default.dim('CLEAN:'));
191
- for (const status of cleanEntities) {
192
- this.log(` ${this.getTypeEmoji(status.type)} ${status.entity}/ ${chalk_1.default.dim('(clean)')}`);
193
- }
194
- this.log('');
195
- }
196
- }
197
- // Enhanced summary
198
- const dirtyRepos = statuses.filter(s => s.hasChanges).length;
199
- const totalRepos = statuses.filter(s => !s.error).length;
200
- const readyToCommit = statuses.filter(s => s.hasChanges).map(s => s.entity);
201
- this.log(chalk_1.default.bold('=== Summary ==='));
202
- if (dirtyRepos === 0) {
203
- this.log(`✨ All ${totalRepos} repositories are clean`);
204
- }
205
- else {
206
- this.log(`✨ ${dirtyRepos} ${dirtyRepos === 1 ? 'repo' : 'repos'} with changes`);
207
- if (readyToCommit.length > 0) {
208
- this.log(`šŸš€ Ready to commit across: ${chalk_1.default.cyan(readyToCommit.join(', '))}`);
209
- }
210
- }
211
- // Suggested actions
212
- if (dirtyRepos > 0) {
213
- this.log('');
214
- this.log(chalk_1.default.dim('Suggested actions:'));
215
- this.log(chalk_1.default.dim(`• gut add . # Stage all changes`));
216
- this.log(chalk_1.default.dim(`• gut commit -m "..." # Commit across repos`));
217
- this.log(chalk_1.default.dim(`• gut push # Push to remotes`));
218
- }
219
- }
220
- displayEntityStatus(status, verbose) {
221
- const changesText = status.changes.length > 0 ? `${status.changes.length} changes` : '';
222
- const untrackedText = status.untracked.length > 0 ? `${status.untracked.length} untracked` : '';
223
- const statusText = [changesText, untrackedText].filter(Boolean).join(', ');
224
- this.log(` ${status.entity}/ ${chalk_1.default.green('🟢')} ${statusText}`);
225
- if (verbose) {
226
- // Show specific files
227
- if (status.changes.length > 0) {
228
- status.changes.slice(0, 3).forEach((change) => {
229
- this.log(` M ${change}`);
230
- });
231
- if (status.changes.length > 3) {
232
- this.log(` ... and ${status.changes.length - 3} more`);
233
- }
234
- }
235
- if (status.untracked.length > 0) {
236
- status.untracked.slice(0, 2).forEach((file) => {
237
- this.log(` A ${file}`);
238
- });
239
- if (status.untracked.length > 2) {
240
- this.log(` ... and ${status.untracked.length - 2} more`);
241
- }
242
- }
243
- }
244
- }
245
- }
246
- exports.default = Status;
@@ -1,11 +0,0 @@
1
- import { BaseCommand } from '../base-command';
2
- export default class Sync extends BaseCommand {
3
- static description: string;
4
- static examples: string[];
5
- static flags: {
6
- rebase: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
7
- force: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
8
- 'no-push': import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
9
- };
10
- run(): Promise<void>;
11
- }
@@ -1,142 +0,0 @@
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 = require("../base-command");
6
- const chalk_1 = tslib_1.__importDefault(require("chalk"));
7
- const ora_1 = tslib_1.__importDefault(require("ora"));
8
- class Sync extends base_command_1.BaseCommand {
9
- static description = 'Synchronize repositories with remote (fetch, merge/rebase, push)';
10
- static examples = [
11
- '<%= config.bin %> <%= command.id %>',
12
- '<%= config.bin %> <%= command.id %> --rebase',
13
- '<%= config.bin %> <%= command.id %> --force',
14
- ];
15
- static flags = {
16
- rebase: core_1.Flags.boolean({
17
- char: 'r',
18
- description: 'Use rebase instead of merge',
19
- default: false,
20
- }),
21
- force: core_1.Flags.boolean({
22
- char: 'f',
23
- description: 'Force push after sync',
24
- default: false,
25
- }),
26
- 'no-push': core_1.Flags.boolean({
27
- description: 'Skip the push step',
28
- default: false,
29
- }),
30
- };
31
- async run() {
32
- const { flags } = await this.parse(Sync);
33
- const focusedEntities = await this.focusService.getFocusedEntities();
34
- if (focusedEntities.length === 0) {
35
- this.error('No entities are focused. Use "gut focus <entity>" first.');
36
- }
37
- this.log(chalk_1.default.bold('\nšŸ”„ Synchronizing repositories with remote\n'));
38
- const results = {
39
- success: [],
40
- failed: [],
41
- partial: [],
42
- };
43
- for (const entity of focusedEntities) {
44
- const spinner = (0, ora_1.default)(`Syncing ${chalk_1.default.cyan(entity.name)}`).start();
45
- let currentStep = 'fetch';
46
- try {
47
- // Check if there's a remote configured
48
- const remoteOutput = await this.gitService.exec(['remote', '-v'], { cwd: entity.path });
49
- if (!remoteOutput || remoteOutput.trim().length === 0) {
50
- spinner.warn(chalk_1.default.yellow(`${entity.name}: No remote configured`));
51
- results.failed.push({ repo: entity.name, error: 'No remote configured', step: 'check' });
52
- continue;
53
- }
54
- // Step 1: Fetch
55
- spinner.text = `${chalk_1.default.cyan(entity.name)}: Fetching from remote...`;
56
- await this.gitService.fetch(entity.path);
57
- // Step 2: Check status
58
- currentStep = 'status';
59
- const status = await this.gitService.getStatus(entity.path);
60
- const hasUncommitted = status.changes.length > 0;
61
- if (hasUncommitted) {
62
- spinner.warn(chalk_1.default.yellow(`${entity.name}: Has uncommitted changes (fetch completed)`));
63
- results.partial.push({ repo: entity.name, step: 'Fetched only - uncommitted changes present' });
64
- continue;
65
- }
66
- // Step 3: Pull (merge or rebase)
67
- currentStep = 'pull';
68
- spinner.text = `${chalk_1.default.cyan(entity.name)}: ${flags.rebase ? 'Rebasing' : 'Merging'}...`;
69
- const pullOptions = {
70
- rebase: flags.rebase,
71
- };
72
- await this.gitService.pull(entity.path, pullOptions);
73
- // Step 4: Push (if not skipped)
74
- if (!flags['no-push']) {
75
- currentStep = 'push';
76
- spinner.text = `${chalk_1.default.cyan(entity.name)}: Pushing to remote...`;
77
- const pushOptions = {
78
- force: flags.force,
79
- };
80
- await this.gitService.push(entity.path, pushOptions);
81
- }
82
- spinner.succeed(chalk_1.default.green(`āœ“ ${entity.name}: Fully synchronized`));
83
- results.success.push(entity.name);
84
- }
85
- catch (error) {
86
- const errorMessage = error.message || error.toString();
87
- if (errorMessage.includes('Already up to date') && currentStep === 'pull') {
88
- // Not an error, just nothing to pull
89
- if (!flags['no-push']) {
90
- try {
91
- currentStep = 'push';
92
- spinner.text = `${chalk_1.default.cyan(entity.name)}: Pushing to remote...`;
93
- const pushOptions = { force: flags.force };
94
- await this.gitService.push(entity.path, pushOptions);
95
- spinner.succeed(chalk_1.default.green(`āœ“ ${entity.name}: Fully synchronized`));
96
- results.success.push(entity.name);
97
- }
98
- catch (pushError) {
99
- spinner.warn(chalk_1.default.yellow(`${entity.name}: Up-to-date locally, push failed`));
100
- results.partial.push({ repo: entity.name, step: `Pull up-to-date, push failed: ${pushError.message}` });
101
- }
102
- }
103
- else {
104
- spinner.succeed(chalk_1.default.green(`āœ“ ${entity.name}: Up-to-date`));
105
- results.success.push(entity.name);
106
- }
107
- }
108
- else {
109
- spinner.fail(chalk_1.default.red(`āœ— ${entity.name}: Failed at ${currentStep}`));
110
- results.failed.push({ repo: entity.name, error: errorMessage, step: currentStep });
111
- }
112
- }
113
- }
114
- // Summary
115
- this.log(chalk_1.default.bold('\nšŸ“Š Sync Summary'));
116
- this.log(chalk_1.default.dim('─'.repeat(50)));
117
- if (results.success.length > 0) {
118
- this.log(chalk_1.default.green(`āœ“ Fully synced: ${results.success.length} entities`));
119
- for (const repo of results.success) {
120
- this.log(chalk_1.default.green(` - ${repo}`));
121
- }
122
- }
123
- if (results.partial.length > 0) {
124
- this.log(chalk_1.default.yellow(`⚠ Partially synced: ${results.partial.length} entities`));
125
- for (const partial of results.partial) {
126
- this.log(chalk_1.default.yellow(` - ${partial.repo}: ${partial.step}`));
127
- }
128
- }
129
- if (results.failed.length > 0) {
130
- this.log(chalk_1.default.red(`āœ— Failed: ${results.failed.length} entities`));
131
- for (const failure of results.failed) {
132
- this.log(chalk_1.default.red(` - ${failure.repo}: ${failure.error} (at ${failure.step})`));
133
- }
134
- }
135
- this.log(chalk_1.default.dim('\n─'.repeat(50)));
136
- this.log(chalk_1.default.dim('Sync performs: fetch → pull → push'));
137
- if (results.partial.length > 0 || results.failed.length > 0) {
138
- this.log(chalk_1.default.dim('Tip: Use "gut status" to check repository states'));
139
- }
140
- }
141
- }
142
- exports.default = Sync;
@@ -1,6 +0,0 @@
1
- import { BaseCommand } from '../base-command';
2
- export default class Unfocus extends BaseCommand {
3
- static description: string;
4
- static examples: string[];
5
- run(): Promise<void>;
6
- }
@@ -1,23 +0,0 @@
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
- class Unfocus extends base_command_1.BaseCommand {
7
- static description = 'Clear the current focus';
8
- static examples = [
9
- '<%= config.bin %> <%= command.id %>',
10
- ];
11
- async run() {
12
- await this.parse(Unfocus);
13
- const focusedEntities = await this.focusService.getFocusedEntities();
14
- if (focusedEntities.length === 0) {
15
- this.log(chalk_1.default.yellow('No entities are currently focused'));
16
- return;
17
- }
18
- await this.focusService.clearFocus();
19
- const entities = focusedEntities.map(e => e.name).join(', ');
20
- this.log(chalk_1.default.green(`āœ“ Cleared focus from: ${entities}`));
21
- }
22
- }
23
- exports.default = Unfocus;
@@ -1,10 +0,0 @@
1
- import { BaseCommand } from '../base-command';
2
- export default class UsedBy extends BaseCommand {
3
- static description: string;
4
- static examples: string[];
5
- static args: {
6
- entity: import("@oclif/core/lib/interfaces").Arg<string | undefined, Record<string, unknown>>;
7
- };
8
- run(): Promise<void>;
9
- private analyzeUsage;
10
- }
@@ -1,111 +0,0 @@
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 = require("../base-command");
6
- const chalk_1 = tslib_1.__importDefault(require("chalk"));
7
- class UsedBy extends base_command_1.BaseCommand {
8
- static description = 'Show what entities depend on current focus or specified entity';
9
- static examples = [
10
- '<%= config.bin %> <%= command.id %>',
11
- '<%= config.bin %> <%= command.id %> api',
12
- ];
13
- static args = {
14
- entity: core_1.Args.string({
15
- name: 'entity',
16
- required: false,
17
- description: 'Entity name to analyze usage for',
18
- }),
19
- };
20
- async run() {
21
- const { args } = await this.parse(UsedBy);
22
- let targetEntities = [];
23
- if (args.entity) {
24
- // Analyze specific entity
25
- const entity = this.entityService.findEntity(args.entity);
26
- if (!entity) {
27
- this.error(`Entity '${args.entity}' not found`);
28
- }
29
- targetEntities = [entity];
30
- }
31
- else {
32
- // Analyze current focus
33
- targetEntities = await this.focusService.getFocusedEntities();
34
- if (targetEntities.length === 0) {
35
- this.error('No entities in focus. Use "gut focus <entity>" first or specify an entity name.');
36
- }
37
- }
38
- this.log(chalk_1.default.bold('\nšŸ”„ Usage Analysis'));
39
- this.log(chalk_1.default.dim('─'.repeat(50)));
40
- for (const entity of targetEntities) {
41
- this.log(`\n${chalk_1.default.green('ā–ø')} ${chalk_1.default.bold(entity.name)} (${entity.type})`);
42
- const usage = await this.analyzeUsage(entity);
43
- if (usage.direct.length > 0) {
44
- this.log(`\n ${chalk_1.default.yellow('Direct Dependents:')}`);
45
- usage.direct.forEach(dep => {
46
- this.log(` ${this.getTypeEmoji(dep.type)} ${dep.name} (${dep.type})`);
47
- });
48
- }
49
- if (usage.clients.length > 0) {
50
- this.log(`\n ${chalk_1.default.blue('Client Dependencies:')}`);
51
- usage.clients.forEach(dep => {
52
- this.log(` ${this.getTypeEmoji(dep.type)} ${dep.name} (${dep.type})`);
53
- });
54
- }
55
- if (usage.inferred.length > 0) {
56
- this.log(`\n ${chalk_1.default.dim('Inferred Usage:')}`);
57
- usage.inferred.forEach(dep => {
58
- this.log(` ${this.getTypeEmoji(dep.type)} ${dep.name} (${dep.type}) ${chalk_1.default.dim('- pattern based')}`);
59
- });
60
- }
61
- if (usage.direct.length === 0 && usage.clients.length === 0 && usage.inferred.length === 0) {
62
- this.log(` ${chalk_1.default.dim('No dependents found')}`);
63
- }
64
- // Show impact assessment
65
- const totalDependents = usage.direct.length + usage.clients.length + usage.inferred.length;
66
- if (totalDependents > 0) {
67
- this.log(`\n ${chalk_1.default.cyan('Impact Assessment:')}`);
68
- this.log(` ${chalk_1.default.dim('Total dependents:')} ${totalDependents}`);
69
- const criticalDependents = usage.direct.filter(e => e.type === 'client').length;
70
- if (criticalDependents > 0) {
71
- this.log(` ${chalk_1.default.red('Critical client dependencies:')} ${criticalDependents}`);
72
- }
73
- }
74
- }
75
- this.log(chalk_1.default.dim('\n─'.repeat(50)));
76
- this.log(chalk_1.default.dim('Usage analysis helps assess impact of changes'));
77
- }
78
- async analyzeUsage(entity) {
79
- const allEntities = this.entityService.getAllEntities();
80
- // Direct dependents (entities that explicitly list this as dependency)
81
- const directDependents = allEntities.filter(e => e.metadata?.relationships?.dependent_systems?.includes(entity.name) ||
82
- e.metadata?.relationships?.similar_entities?.includes(entity.name));
83
- // Client dependencies (if this is a system, all clients likely depend on it)
84
- const clientDependents = [];
85
- if (entity.type === 'system' || entity.type === 'module' || entity.type === 'service') {
86
- clientDependents.push(...allEntities.filter(e => e.type === 'client'));
87
- }
88
- // Inferred usage based on patterns
89
- const inferredDependents = [];
90
- // If this is a design system or shared module
91
- if (entity.name.includes('design') || entity.name.includes('shared') || entity.type === 'module') {
92
- const potentialUsers = allEntities.filter(e => (e.type === 'client' || e.type === 'delivery') &&
93
- !clientDependents.includes(e) &&
94
- !directDependents.includes(e));
95
- inferredDependents.push(...potentialUsers);
96
- }
97
- // If this is an API system
98
- if (entity.name.includes('api') || entity.type === 'service') {
99
- const apiUsers = allEntities.filter(e => e.type === 'client' &&
100
- !clientDependents.includes(e) &&
101
- !directDependents.includes(e));
102
- inferredDependents.push(...apiUsers);
103
- }
104
- return {
105
- direct: directDependents,
106
- clients: clientDependents.filter(e => !directDependents.includes(e)),
107
- inferred: inferredDependents.slice(0, 5) // Limit to avoid clutter
108
- };
109
- }
110
- }
111
- exports.default = UsedBy;
@@ -1,20 +0,0 @@
1
- import { BaseCommand } from '../base-command';
2
- export default class Workspace extends BaseCommand {
3
- static description: string;
4
- static examples: string[];
5
- static args: {
6
- action: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
7
- };
8
- static flags: {
9
- force: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
10
- 'entity-type': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
11
- };
12
- run(): Promise<void>;
13
- private initWorkspaceStructure;
14
- private showWorkspaceStructure;
15
- private showDirectoryTree;
16
- private generateEntityMetadata;
17
- private generateWorkspaceConfig;
18
- private generateContextsConfig;
19
- private generateEntityMetadataContent;
20
- }