@hyperdrive.bot/gut 0.1.8 → 0.1.10

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 (111) hide show
  1. package/README.md +1048 -1
  2. package/dist/base-command.d.ts +22 -0
  3. package/dist/base-command.js +99 -0
  4. package/dist/commands/add.d.ts +14 -0
  5. package/dist/commands/add.js +70 -0
  6. package/dist/commands/affected.d.ts +23 -0
  7. package/dist/commands/affected.js +323 -0
  8. package/dist/commands/audit.d.ts +33 -0
  9. package/dist/commands/audit.js +594 -0
  10. package/dist/commands/back.d.ts +6 -0
  11. package/dist/commands/back.js +29 -0
  12. package/dist/commands/checkout.d.ts +14 -0
  13. package/dist/commands/checkout.js +124 -0
  14. package/dist/commands/commit.d.ts +11 -0
  15. package/dist/commands/commit.js +107 -0
  16. package/dist/commands/context.d.ts +6 -0
  17. package/dist/commands/context.js +32 -0
  18. package/dist/commands/contexts.d.ts +7 -0
  19. package/dist/commands/contexts.js +88 -0
  20. package/dist/commands/deps.d.ts +10 -0
  21. package/dist/commands/deps.js +100 -0
  22. package/dist/commands/entity/add.d.ts +16 -0
  23. package/dist/commands/entity/add.js +103 -0
  24. package/dist/commands/entity/clone-all.d.ts +18 -0
  25. package/dist/commands/entity/clone-all.js +166 -0
  26. package/dist/commands/entity/clone.d.ts +17 -0
  27. package/dist/commands/entity/clone.js +132 -0
  28. package/dist/commands/entity/list.d.ts +11 -0
  29. package/dist/commands/entity/list.js +80 -0
  30. package/dist/commands/entity/remove.d.ts +12 -0
  31. package/dist/commands/entity/remove.js +54 -0
  32. package/dist/commands/extract.d.ts +35 -0
  33. package/dist/commands/extract.js +483 -0
  34. package/dist/commands/focus.d.ts +19 -0
  35. package/dist/commands/focus.js +137 -0
  36. package/dist/commands/graph.d.ts +18 -0
  37. package/dist/commands/graph.js +273 -0
  38. package/dist/commands/init.d.ts +11 -0
  39. package/dist/commands/init.js +75 -0
  40. package/dist/commands/insights.d.ts +21 -0
  41. package/dist/commands/insights.js +465 -0
  42. package/dist/commands/patterns.d.ts +40 -0
  43. package/dist/commands/patterns.js +405 -0
  44. package/dist/commands/pull.d.ts +11 -0
  45. package/dist/commands/pull.js +121 -0
  46. package/dist/commands/push.d.ts +11 -0
  47. package/dist/commands/push.js +97 -0
  48. package/dist/commands/quick-setup.d.ts +20 -0
  49. package/dist/commands/quick-setup.js +417 -0
  50. package/dist/commands/recent.d.ts +9 -0
  51. package/dist/commands/recent.js +51 -0
  52. package/dist/commands/related.d.ts +23 -0
  53. package/dist/commands/related.js +255 -0
  54. package/dist/commands/repos.d.ts +17 -0
  55. package/dist/commands/repos.js +184 -0
  56. package/dist/commands/stack.d.ts +10 -0
  57. package/dist/commands/stack.js +78 -0
  58. package/dist/commands/status.d.ts +13 -0
  59. package/dist/commands/status.js +193 -0
  60. package/dist/commands/sync.d.ts +11 -0
  61. package/dist/commands/sync.js +139 -0
  62. package/dist/commands/ticket/focus.d.ts +20 -0
  63. package/dist/commands/ticket/focus.js +217 -0
  64. package/dist/commands/ticket/get.d.ts +15 -0
  65. package/dist/commands/ticket/get.js +168 -0
  66. package/dist/commands/ticket/hint.d.ts +16 -0
  67. package/dist/commands/ticket/hint.js +147 -0
  68. package/dist/commands/ticket/index.d.ts +10 -0
  69. package/dist/commands/ticket/index.js +60 -0
  70. package/dist/commands/ticket/list.d.ts +13 -0
  71. package/dist/commands/ticket/list.js +120 -0
  72. package/dist/commands/ticket/sync.d.ts +14 -0
  73. package/dist/commands/ticket/sync.js +85 -0
  74. package/dist/commands/ticket/update.d.ts +17 -0
  75. package/dist/commands/ticket/update.js +142 -0
  76. package/dist/commands/unfocus.d.ts +6 -0
  77. package/dist/commands/unfocus.js +19 -0
  78. package/dist/commands/used-by.d.ts +13 -0
  79. package/dist/commands/used-by.js +110 -0
  80. package/dist/commands/workspace.d.ts +22 -0
  81. package/dist/commands/workspace.js +372 -0
  82. package/dist/index.d.ts +14 -0
  83. package/dist/index.js +16 -0
  84. package/dist/models/entity.model.d.ts +234 -0
  85. package/dist/models/entity.model.js +1 -0
  86. package/dist/models/ticket.model.d.ts +117 -0
  87. package/dist/models/ticket.model.js +43 -0
  88. package/dist/services/auth.service.d.ts +15 -0
  89. package/dist/services/auth.service.js +26 -0
  90. package/dist/services/config.service.d.ts +34 -0
  91. package/dist/services/config.service.js +234 -0
  92. package/dist/services/entity.service.d.ts +20 -0
  93. package/dist/services/entity.service.js +127 -0
  94. package/dist/services/focus.service.d.ts +71 -0
  95. package/dist/services/focus.service.js +614 -0
  96. package/dist/services/git.service.d.ts +39 -0
  97. package/dist/services/git.service.js +188 -0
  98. package/dist/services/gut-api.service.d.ts +53 -0
  99. package/dist/services/gut-api.service.js +99 -0
  100. package/dist/services/ticket.service.d.ts +84 -0
  101. package/dist/services/ticket.service.js +207 -0
  102. package/dist/utils/display.d.ts +26 -0
  103. package/dist/utils/display.js +145 -0
  104. package/dist/utils/filesystem.d.ts +32 -0
  105. package/dist/utils/filesystem.js +198 -0
  106. package/dist/utils/index.d.ts +13 -0
  107. package/dist/utils/index.js +14 -0
  108. package/dist/utils/validation.d.ts +22 -0
  109. package/dist/utils/validation.js +192 -0
  110. package/oclif.manifest.json +2006 -0
  111. package/package.json +11 -2
@@ -0,0 +1,273 @@
1
+ import { Flags } from '@oclif/core';
2
+ import chalk from 'chalk';
3
+ import { BaseCommand } from '../base-command.js';
4
+ export default class Graph extends BaseCommand {
5
+ static description = 'Visualize repository relationships and dependencies';
6
+ static examples = [
7
+ '<%= config.bin %> <%= command.id %>',
8
+ '<%= config.bin %> <%= command.id %> --format ascii',
9
+ '<%= config.bin %> <%= command.id %> --focus-only',
10
+ ];
11
+ static flags = {
12
+ depth: Flags.integer({
13
+ char: 'd',
14
+ default: 2,
15
+ description: 'maximum depth of relationships to show',
16
+ }),
17
+ 'focus-only': Flags.boolean({
18
+ default: false,
19
+ description: 'show only focused entities and their relationships',
20
+ }),
21
+ format: Flags.string({
22
+ char: 'f',
23
+ default: 'ascii',
24
+ description: 'output format',
25
+ options: ['ascii', 'dot', 'json'],
26
+ }),
27
+ };
28
+ async run() {
29
+ const { flags } = await this.parse(Graph);
30
+ const entities = flags['focus-only']
31
+ ? await this.focusService.getFocusedEntities()
32
+ : this.entityService.getAllEntities();
33
+ if (entities.length === 0) {
34
+ this.error('No entities found. Add entities first or set focus.');
35
+ }
36
+ this.log(chalk.bold('\nšŸ•øļø Repository Relationship Graph'));
37
+ this.log(chalk.dim('─'.repeat(50)));
38
+ const graph = await this.buildRelationshipGraph(entities, flags.depth);
39
+ switch (flags.format) {
40
+ case 'ascii': {
41
+ this.displayAsciiGraph(graph);
42
+ break;
43
+ }
44
+ case 'dot': {
45
+ this.displayDotGraph(graph);
46
+ break;
47
+ }
48
+ case 'json': {
49
+ this.log(JSON.stringify(graph, null, 2));
50
+ break;
51
+ }
52
+ }
53
+ }
54
+ async buildRelationshipGraph(entities, _maxDepth) {
55
+ const nodes = new Map();
56
+ const edges = [];
57
+ // Build nodes
58
+ for (const entity of entities) {
59
+ nodes.set(entity.name, {
60
+ metadata: entity.metadata || {},
61
+ name: entity.name,
62
+ path: entity.path,
63
+ type: entity.type,
64
+ });
65
+ }
66
+ // Build edges from metadata relationships
67
+ for (const entity of entities) {
68
+ const { metadata } = entity;
69
+ if (!metadata?.relationships)
70
+ continue;
71
+ // Direct dependencies
72
+ if (metadata.relationships.dependent_systems) {
73
+ for (const dep of metadata.relationships.dependent_systems) {
74
+ edges.push({
75
+ from: entity.name,
76
+ to: dep,
77
+ type: 'depends_on',
78
+ weight: 1,
79
+ });
80
+ }
81
+ }
82
+ // Similar entities
83
+ if (metadata.relationships.similar_entities) {
84
+ for (const similar of metadata.relationships.similar_entities) {
85
+ edges.push({
86
+ from: entity.name,
87
+ to: similar,
88
+ type: 'similar_to',
89
+ weight: 0.5,
90
+ });
91
+ }
92
+ }
93
+ // Related initiatives
94
+ if (metadata.relationships.related_initiatives) {
95
+ for (const initiative of metadata.relationships.related_initiatives) {
96
+ edges.push({
97
+ from: entity.name,
98
+ to: initiative,
99
+ type: 'part_of',
100
+ weight: 0.7,
101
+ });
102
+ }
103
+ }
104
+ }
105
+ // Infer additional relationships
106
+ this.inferRelationships([...nodes.values()], edges);
107
+ return {
108
+ edges,
109
+ nodes: [...nodes.values()],
110
+ };
111
+ }
112
+ displayAsciiGraph(graph) {
113
+ const { edges, nodes } = graph;
114
+ // Group nodes by type
115
+ const byType = {};
116
+ for (const node of nodes) {
117
+ if (!byType[node.type])
118
+ byType[node.type] = [];
119
+ byType[node.type].push(node);
120
+ }
121
+ // Display nodes by type
122
+ const typeOrder = ['client', 'prospect', 'company', 'initiative', 'system'];
123
+ for (const type of typeOrder) {
124
+ if (!byType[type])
125
+ continue;
126
+ this.log(`\n${chalk.bold(type.toUpperCase())}:`);
127
+ for (const node of byType[type]) {
128
+ const emoji = this.getTypeEmoji(node.type);
129
+ this.log(` ${emoji} ${chalk.cyan(node.name)}`);
130
+ // Show outgoing relationships
131
+ const outgoing = edges.filter((e) => e.from === node.name);
132
+ for (const edge of outgoing) {
133
+ const relationshipIcon = this.getRelationshipIcon(edge.type);
134
+ const targetNode = nodes.find((n) => n.name === edge.to);
135
+ const targetEmoji = targetNode ? this.getTypeEmoji(targetNode.type) : 'šŸ“';
136
+ this.log(` ${relationshipIcon} ${targetEmoji} ${edge.to} ${chalk.dim(`(${edge.type})`)}`);
137
+ }
138
+ }
139
+ }
140
+ // Show relationship summary
141
+ this.log(chalk.bold('\nšŸ“Š Relationship Summary:'));
142
+ const relationshipCounts = {};
143
+ for (const edge of edges) {
144
+ relationshipCounts[edge.type] = (relationshipCounts[edge.type] || 0) + 1;
145
+ }
146
+ for (const [type, count] of Object.entries(relationshipCounts)) {
147
+ this.log(` ${this.getRelationshipIcon(type)} ${type}: ${count}`);
148
+ }
149
+ }
150
+ displayDotGraph(graph) {
151
+ const { edges, nodes } = graph;
152
+ this.log('digraph EntityGraph {');
153
+ this.log(' rankdir=TB;');
154
+ this.log(' node [shape=box, style=rounded];');
155
+ this.log('');
156
+ // Nodes
157
+ for (const node of nodes) {
158
+ const color = this.getNodeColor(node.type);
159
+ this.log(` "${node.name}" [label="${node.name}\\n(${node.type})", fillcolor="${color}", style=filled];`);
160
+ }
161
+ this.log('');
162
+ // Edges
163
+ for (const edge of edges) {
164
+ const style = edge.type.includes('likely') ? 'dashed' : 'solid';
165
+ const color = this.getEdgeColor(edge.type);
166
+ this.log(` "${edge.from}" -> "${edge.to}" [label="${edge.type}", style=${style}, color="${color}"];`);
167
+ }
168
+ this.log('}');
169
+ }
170
+ getEdgeColor(type) {
171
+ switch (type) {
172
+ case 'depends_on': {
173
+ return 'red';
174
+ }
175
+ case 'likely_depends_on': {
176
+ return 'orange';
177
+ }
178
+ case 'part_of': {
179
+ return 'green';
180
+ }
181
+ case 'similar_to': {
182
+ return 'blue';
183
+ }
184
+ case 'uses_shared': {
185
+ return 'purple';
186
+ }
187
+ default: {
188
+ return 'black';
189
+ }
190
+ }
191
+ }
192
+ getNodeColor(type) {
193
+ switch (type) {
194
+ case 'client': {
195
+ return 'lightblue';
196
+ }
197
+ case 'company': {
198
+ return 'lightgreen';
199
+ }
200
+ case 'initiative': {
201
+ return 'lightcoral';
202
+ }
203
+ case 'prospect': {
204
+ return 'lightyellow';
205
+ }
206
+ case 'system': {
207
+ return 'lightgray';
208
+ }
209
+ default: {
210
+ return 'white';
211
+ }
212
+ }
213
+ }
214
+ getRelationshipIcon(type) {
215
+ switch (type) {
216
+ case 'depends_on': {
217
+ return 'šŸ”—';
218
+ }
219
+ case 'likely_depends_on': {
220
+ return 'šŸ”—';
221
+ }
222
+ case 'part_of': {
223
+ return 'šŸ“‹';
224
+ }
225
+ case 'similar_to': {
226
+ return 'šŸ”„';
227
+ }
228
+ case 'uses_shared': {
229
+ return 'šŸ“š';
230
+ }
231
+ default: {
232
+ return '→';
233
+ }
234
+ }
235
+ }
236
+ inferRelationships(nodes, edges) {
237
+ // Infer system dependencies
238
+ const systems = nodes.filter(n => n.type === 'system');
239
+ const clients = nodes.filter(n => n.type === 'client');
240
+ // All clients likely depend on systems
241
+ for (const client of clients) {
242
+ for (const system of systems) {
243
+ // Don't add if relationship already exists
244
+ const exists = edges.some(e => e.from === client.name && e.to === system.name);
245
+ if (!exists) {
246
+ edges.push({
247
+ from: client.name,
248
+ to: system.name,
249
+ type: 'likely_depends_on',
250
+ weight: 0.3,
251
+ });
252
+ }
253
+ }
254
+ }
255
+ // Infer shared dependencies
256
+ const sharedSystems = systems.filter(s => s.name.includes('shared') || s.name.includes('common'));
257
+ for (const shared of sharedSystems) {
258
+ for (const entity of nodes) {
259
+ if (entity.type === 'client' || entity.type === 'delivery') {
260
+ const exists = edges.some(e => e.from === entity.name && e.to === shared.name);
261
+ if (!exists) {
262
+ edges.push({
263
+ from: entity.name,
264
+ to: shared.name,
265
+ type: 'uses_shared',
266
+ weight: 0.4,
267
+ });
268
+ }
269
+ }
270
+ }
271
+ }
272
+ }
273
+ }
@@ -0,0 +1,11 @@
1
+ import { BaseCommand } from '../base-command.js';
2
+ export default class Init extends BaseCommand {
3
+ static description: string;
4
+ static examples: string[];
5
+ static flags: {
6
+ force: import("@oclif/core/interfaces").BooleanFlag<boolean>;
7
+ workspace: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
8
+ };
9
+ protected get requiresInit(): boolean;
10
+ run(): Promise<void>;
11
+ }
@@ -0,0 +1,75 @@
1
+ import { Flags } from '@oclif/core';
2
+ import * as fs from 'node:fs';
3
+ import path from 'node:path';
4
+ import { BaseCommand } from '../base-command.js';
5
+ export default class Init extends BaseCommand {
6
+ static description = 'Initialize a gut workspace';
7
+ static examples = [
8
+ '<%= config.bin %> <%= command.id %>',
9
+ '<%= config.bin %> <%= command.id %> --force',
10
+ ];
11
+ static flags = {
12
+ force: Flags.boolean({
13
+ char: 'f',
14
+ description: 'force initialization even if already initialized',
15
+ }),
16
+ workspace: Flags.string({
17
+ char: 'w',
18
+ default: process.cwd(),
19
+ description: 'workspace root directory',
20
+ }),
21
+ };
22
+ get requiresInit() {
23
+ return false; // This command doesn't require initialization
24
+ }
25
+ async run() {
26
+ const { flags } = await this.parse(Init);
27
+ // Check if already initialized
28
+ if (this.configService.isInitialized() && !flags.force) {
29
+ this.log('Workspace already initialized. Use --force to reinitialize.');
30
+ return;
31
+ }
32
+ this.log('šŸš€ Initializing gut workspace...\n');
33
+ // Create .gut directory
34
+ const gutDir = this.configService.getGutDir();
35
+ if (!fs.existsSync(gutDir)) {
36
+ fs.mkdirSync(gutDir, { recursive: true });
37
+ this.log('āœ“ Created .gut directory');
38
+ }
39
+ // Discover entities
40
+ this.log('\nšŸ” Discovering entities...');
41
+ const discovered = this.entityService.discoverEntities();
42
+ if (discovered.length > 0) {
43
+ this.log(`Found ${discovered.length} potential entities:`);
44
+ this.printEntityList(discovered);
45
+ }
46
+ // Save configuration with discovered entities (or empty list)
47
+ const config = {
48
+ entities: discovered,
49
+ initialized: true,
50
+ workspace: flags.workspace,
51
+ };
52
+ this.configService.saveConfig(config);
53
+ if (discovered.length > 0) {
54
+ this.log('\nāœ“ Configuration saved');
55
+ }
56
+ else {
57
+ this.log('No entities discovered. You can add them manually with "gut entity add"');
58
+ }
59
+ // Add .gut to .gitignore if it exists
60
+ const gitignorePath = path.join(flags.workspace, '.gitignore');
61
+ if (fs.existsSync(gitignorePath)) {
62
+ const gitignore = fs.readFileSync(gitignorePath, 'utf8');
63
+ if (!gitignore.includes('.gut')) {
64
+ fs.appendFileSync(gitignorePath, '\n# Gut workspace files\n.gut/\n');
65
+ this.log('āœ“ Added .gut to .gitignore');
66
+ }
67
+ }
68
+ this.log('\nāœ… Workspace initialized successfully!');
69
+ this.log('\nNext steps:');
70
+ this.log(' • Review discovered entities: gut entity list');
71
+ this.log(' • Add more entities: gut entity add <type> <name> --path <path>');
72
+ this.log(' • Set focus: gut focus <entity>');
73
+ this.log(' • Get help: gut help');
74
+ }
75
+ }
@@ -0,0 +1,21 @@
1
+ import { BaseCommand } from '../base-command.js';
2
+ export default class Insights extends BaseCommand {
3
+ static description: string;
4
+ static examples: string[];
5
+ static flags: {
6
+ detailed: import("@oclif/core/interfaces").BooleanFlag<boolean>;
7
+ format: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
8
+ 'include-ignored': import("@oclif/core/interfaces").BooleanFlag<boolean>;
9
+ };
10
+ run(): Promise<void>;
11
+ private analyzeEntity;
12
+ private calculateWorkspaceStats;
13
+ private countFiles;
14
+ private formatSize;
15
+ private getLanguageFromExt;
16
+ private isTextFile;
17
+ private parseRelativeDate;
18
+ private printJSON;
19
+ private printSummary;
20
+ private printTable;
21
+ }