@hyperdrive.bot/gut 0.1.4 → 0.1.8

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
package/bin/run.js ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env node
2
+
3
+ import {execute} from '@oclif/core'
4
+
5
+ await execute({dir: import.meta.url})
package/package.json CHANGED
@@ -1,25 +1,26 @@
1
1
  {
2
2
  "name": "@hyperdrive.bot/gut",
3
- "version": "0.1.4",
3
+ "version": "0.1.8",
4
4
  "description": "Git Unified Tooling - Enhanced git with workspace intelligence for entity-based organization",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
7
7
  "bin": {
8
- "gut": "./bin/run"
8
+ "gut": "./bin/run.js"
9
9
  },
10
+ "type": "module",
10
11
  "files": [
11
12
  "/bin",
12
13
  "/dist",
13
14
  "/oclif.manifest.json"
14
15
  ],
15
16
  "scripts": {
16
- "build": "shx rm -rf dist && tsc -b",
17
+ "build": "rm -rf dist && tsc -b",
17
18
  "dev": "node ./bin/dev",
18
- "lint": "eslint . --ext .ts",
19
- "postpack": "shx rm -f oclif.manifest.json",
20
- "prepack": "npm run build && oclif manifest",
21
- "test": "echo \"Error: no test specified\"",
22
- "version": "oclif readme && git add README.md"
19
+ "lint": "eslint .",
20
+ "postpack": "rm -f oclif.manifest.json",
21
+ "prepack": "npm run build && npx oclif manifest || true",
22
+ "test": "npm run build",
23
+ "version": "npx oclif readme && git add README.md || true"
23
24
  },
24
25
  "keywords": [
25
26
  "git",
@@ -31,7 +32,7 @@
31
32
  "author": "DevSquad",
32
33
  "license": "MIT",
33
34
  "engines": {
34
- "node": ">=14.0.0"
35
+ "node": ">=14.18.0"
35
36
  },
36
37
  "dependencies": {
37
38
  "@oclif/core": "^4.5.2",
@@ -51,7 +52,6 @@
51
52
  "eslint-config-oclif": "^6.0.101",
52
53
  "eslint-config-oclif-typescript": "^3.1.14",
53
54
  "oclif": "^4.22.16",
54
- "shx": "^0.4.0",
55
55
  "ts-node": "^10.9.2",
56
56
  "typescript": "^5.9.2"
57
57
  },
package/bin/run DELETED
@@ -1,5 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- const oclif = require('@oclif/core')
4
-
5
- oclif.run().then(require('@oclif/core/flush')).catch(require('@oclif/core/handle'))
@@ -1,21 +0,0 @@
1
- import { Command } from '@oclif/core';
2
- import { ConfigService } from './services/config.service';
3
- import { FocusService } from './services/focus.service';
4
- import { EntityService } from './services/entity.service';
5
- import { GitService } from './services/git.service';
6
- import { Entity } from './models/entity.model';
7
- export declare abstract class BaseCommand extends Command {
8
- protected configService: ConfigService;
9
- protected focusService: FocusService;
10
- protected entityService: EntityService;
11
- protected gitService: GitService;
12
- init(): Promise<void>;
13
- protected get requiresInit(): boolean;
14
- protected withFocus<T>(callback: (entities: Entity[]) => Promise<T>): Promise<T>;
15
- protected withSpinner<T>(text: string, callback: () => Promise<T>): Promise<T>;
16
- protected printEntityList(entities: Entity[]): void;
17
- protected getTypeEmoji(type: string): string;
18
- protected getStatusIcon(hasChanges: boolean): string;
19
- protected formatPath(path: string): string;
20
- }
21
- export default BaseCommand;
@@ -1,110 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.BaseCommand = void 0;
4
- const core_1 = require("@oclif/core");
5
- const config_service_1 = require("./services/config.service");
6
- const focus_service_1 = require("./services/focus.service");
7
- const entity_service_1 = require("./services/entity.service");
8
- const git_service_1 = require("./services/git.service");
9
- class BaseCommand extends core_1.Command {
10
- configService;
11
- focusService;
12
- entityService;
13
- gitService;
14
- async init() {
15
- await super.init();
16
- // Initialize services
17
- this.configService = new config_service_1.ConfigService();
18
- this.entityService = new entity_service_1.EntityService(this.configService);
19
- this.focusService = new focus_service_1.FocusService(this.configService);
20
- this.gitService = new git_service_1.GitService();
21
- // Check workspace initialization for commands that require it
22
- if (this.requiresInit && !this.configService.isInitialized()) {
23
- this.error('Workspace not initialized. Run "gut init" first.');
24
- }
25
- }
26
- get requiresInit() {
27
- // Override in commands that don't require initialization
28
- return true;
29
- }
30
- async withFocus(callback) {
31
- const entities = await this.focusService.getFocusedEntities();
32
- if (entities.length === 0) {
33
- this.error('No entities focused. Use "gut focus <entity>" first.');
34
- }
35
- return callback(entities);
36
- }
37
- async withSpinner(text, callback) {
38
- // Note: oclif doesn't have built-in spinner, we'll use ora later
39
- this.log(text);
40
- try {
41
- const result = await callback();
42
- this.log('✓ Done');
43
- return result;
44
- }
45
- catch (error) {
46
- this.log('✗ Failed');
47
- throw error;
48
- }
49
- }
50
- printEntityList(entities) {
51
- if (entities.length === 0) {
52
- this.log('No entities configured');
53
- return;
54
- }
55
- // Group by type
56
- const byType = entities.reduce((acc, entity) => {
57
- if (!acc[entity.type]) {
58
- acc[entity.type] = [];
59
- }
60
- acc[entity.type].push(entity);
61
- return acc;
62
- }, {});
63
- // Display grouped
64
- for (const [type, typeEntities] of Object.entries(byType)) {
65
- this.log(`\n${this.getTypeEmoji(type)} ${type.charAt(0).toUpperCase() + type.slice(1)} entities:`);
66
- for (const entity of typeEntities) {
67
- this.log(` • ${entity.name} (${entity.path})`);
68
- if (entity.description) {
69
- this.log(` ${entity.description}`);
70
- }
71
- }
72
- }
73
- }
74
- getTypeEmoji(type) {
75
- switch (type) {
76
- case 'client':
77
- return '🏢';
78
- case 'prospect':
79
- return '🎯';
80
- case 'company':
81
- return '🏛️';
82
- case 'initiative':
83
- return '🚀';
84
- case 'system':
85
- return '⚙️';
86
- case 'delivery':
87
- return '📦';
88
- case 'module':
89
- return '📚';
90
- case 'service':
91
- return '🔧';
92
- case 'tool':
93
- return '🛠️';
94
- default:
95
- return '📁';
96
- }
97
- }
98
- getStatusIcon(hasChanges) {
99
- return hasChanges ? '●' : '✓';
100
- }
101
- formatPath(path) {
102
- const workspaceRoot = this.configService.getWorkspaceRoot();
103
- if (path.startsWith(workspaceRoot)) {
104
- return path.substring(workspaceRoot.length + 1);
105
- }
106
- return path;
107
- }
108
- }
109
- exports.BaseCommand = BaseCommand;
110
- exports.default = BaseCommand;
@@ -1,13 +0,0 @@
1
- import { BaseCommand } from '../base-command';
2
- export default class Add extends BaseCommand {
3
- static description: string;
4
- static examples: string[];
5
- static flags: {
6
- all: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
7
- patch: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
8
- };
9
- static args: {
10
- path: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
11
- };
12
- run(): Promise<void>;
13
- }
@@ -1,73 +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 Add extends base_command_1.BaseCommand {
9
- static description = 'Stage changes in focused entities';
10
- static examples = [
11
- '<%= config.bin %> <%= command.id %>',
12
- '<%= config.bin %> <%= command.id %> .',
13
- '<%= config.bin %> <%= command.id %> src/',
14
- '<%= config.bin %> <%= command.id %> --all',
15
- ];
16
- static flags = {
17
- all: core_1.Flags.boolean({
18
- char: 'A',
19
- description: 'Stage all changes (equivalent to git add -A)',
20
- default: false,
21
- }),
22
- patch: core_1.Flags.boolean({
23
- char: 'p',
24
- description: 'Interactive staging',
25
- default: false,
26
- }),
27
- };
28
- static args = {
29
- path: core_1.Args.string({
30
- description: 'Path(s) to stage',
31
- required: false,
32
- default: '.',
33
- }),
34
- };
35
- async run() {
36
- const { args, flags } = await this.parse(Add);
37
- const focusedEntities = await this.focusService.getFocusedEntities();
38
- if (focusedEntities.length === 0) {
39
- this.error('No entities are focused. Use "gut focus <entity>" first.');
40
- }
41
- this.log(chalk_1.default.bold('\n📝 Staging changes in focused entities\n'));
42
- for (const entity of focusedEntities) {
43
- const spinner = (0, ora_1.default)(`Staging in ${chalk_1.default.cyan(entity.name)}`).start();
44
- try {
45
- let files;
46
- if (flags.all) {
47
- files = ['-A'];
48
- }
49
- else if (flags.patch) {
50
- files = ['-p', args.path];
51
- }
52
- else {
53
- files = [args.path];
54
- }
55
- await this.gitService.add(entity.path, files);
56
- // Get status to check what was staged
57
- const status = await this.gitService.getStatus(entity.path);
58
- const staged = status.changes.length;
59
- if (staged > 0) {
60
- spinner.succeed(chalk_1.default.green(`✓ ${entity.name}: ${staged} file(s) staged`));
61
- }
62
- else {
63
- spinner.info(chalk_1.default.yellow(`${entity.name}: No changes to stage`));
64
- }
65
- }
66
- catch (error) {
67
- spinner.fail(chalk_1.default.red(`✗ ${entity.name}: ${error.message}`));
68
- }
69
- }
70
- this.log(chalk_1.default.dim('\nTip: Use "gut commit" to commit staged changes'));
71
- }
72
- }
73
- exports.default = Add;
@@ -1,23 +0,0 @@
1
- import BaseCommand from '../base-command';
2
- export default class Affected extends BaseCommand {
3
- static description: string;
4
- static examples: string[];
5
- static flags: {
6
- since: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
7
- 'include-tests': import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
8
- 'include-docs': import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
9
- verbose: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
10
- };
11
- static args: {
12
- entity: import("@oclif/core/lib/interfaces").Arg<string | undefined, Record<string, unknown>>;
13
- };
14
- run(): Promise<void>;
15
- private getChangedFiles;
16
- private analyzeAffected;
17
- private checkDependencies;
18
- private checkAPIChanges;
19
- private checkConfigChanges;
20
- private checkSchemaChanges;
21
- private getEntityFiles;
22
- private printAffected;
23
- }
@@ -1,326 +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 = 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
- class Affected extends base_command_1.default {
12
- static description = 'Detect entities potentially affected by changes in current focus';
13
- static examples = [
14
- '<%= config.bin %> <%= command.id %>',
15
- '<%= config.bin %> <%= command.id %> my-app',
16
- '<%= config.bin %> <%= command.id %> --since HEAD~5',
17
- '<%= config.bin %> <%= command.id %> --include-tests',
18
- ];
19
- static flags = {
20
- since: core_1.Flags.string({
21
- char: 's',
22
- description: 'Git reference to compare against',
23
- default: 'HEAD~1',
24
- }),
25
- 'include-tests': core_1.Flags.boolean({
26
- description: 'Include test file changes in analysis',
27
- default: false,
28
- }),
29
- 'include-docs': core_1.Flags.boolean({
30
- description: 'Include documentation changes in analysis',
31
- default: false,
32
- }),
33
- verbose: core_1.Flags.boolean({
34
- char: 'v',
35
- description: 'Show detailed analysis',
36
- default: false,
37
- }),
38
- };
39
- static args = {
40
- entity: core_1.Args.string({
41
- name: 'entity',
42
- required: false,
43
- description: 'Entity to analyze (uses current focus if not provided)',
44
- }),
45
- };
46
- async run() {
47
- const { args, flags } = await this.parse(Affected);
48
- const { since, 'include-tests': includeTests, 'include-docs': includeDocs, verbose } = flags;
49
- const spinner = (0, ora_1.default)();
50
- try {
51
- let sourceEntities = [];
52
- if (args.entity) {
53
- const entity = await this.entityService.getEntity(args.entity);
54
- if (!entity) {
55
- this.error(`Entity '${args.entity}' not found`);
56
- }
57
- sourceEntities = [entity];
58
- }
59
- else {
60
- const focus = await this.focusService.getCurrentFocus();
61
- if (!focus || !focus.entities || focus.entities.length === 0) {
62
- this.error('No entity specified and no current focus');
63
- }
64
- const entities = await Promise.all(focus.entities.map((e) => this.entityService.getEntity(e.name)));
65
- sourceEntities = entities.filter(e => e !== null);
66
- }
67
- spinner.start('Analyzing changes and dependencies');
68
- const changedFiles = await this.getChangedFiles(sourceEntities, since, includeTests, includeDocs);
69
- if (changedFiles.length === 0) {
70
- spinner.succeed('No changes detected');
71
- this.log(chalk_1.default.yellow('No file changes found since ' + since));
72
- return;
73
- }
74
- const allEntities = await this.entityService.listEntities();
75
- const otherEntities = allEntities.filter(e => !sourceEntities.some(se => se.name === e.name));
76
- const affected = await this.analyzeAffected(sourceEntities, otherEntities, changedFiles);
77
- spinner.succeed(`Analysis complete: ${affected.length} entities potentially affected`);
78
- if (affected.length === 0) {
79
- this.log(chalk_1.default.green('No other entities appear to be affected by the changes'));
80
- return;
81
- }
82
- this.printAffected(affected, changedFiles, verbose);
83
- }
84
- catch (error) {
85
- spinner.fail();
86
- this.error(error.message);
87
- }
88
- }
89
- async getChangedFiles(entities, since, includeTests, includeDocs) {
90
- const changedFiles = [];
91
- for (const entity of entities) {
92
- const entityPath = path.resolve(entity.path);
93
- if (!fs.existsSync(path.join(entityPath, '.git'))) {
94
- continue;
95
- }
96
- try {
97
- const diff = (0, child_process_1.execSync)(`git diff ${since} --name-only`, {
98
- cwd: entityPath,
99
- encoding: 'utf-8',
100
- stdio: 'pipe',
101
- }).toString();
102
- const files = diff.split('\n')
103
- .filter(f => f.trim())
104
- .filter(f => {
105
- if (!includeTests && (f.includes('test') || f.includes('spec'))) {
106
- return false;
107
- }
108
- if (!includeDocs && (f.endsWith('.md') || f.includes('docs/'))) {
109
- return false;
110
- }
111
- return true;
112
- })
113
- .map(f => path.join(entityPath, f));
114
- changedFiles.push(...files);
115
- }
116
- catch (error) {
117
- // Ignore git errors
118
- }
119
- }
120
- return [...new Set(changedFiles)];
121
- }
122
- async analyzeAffected(sourceEntities, targetEntities, changedFiles) {
123
- const affected = [];
124
- for (const target of targetEntities) {
125
- const reasons = [];
126
- let confidence = 'low';
127
- // Check direct dependencies
128
- const depReason = await this.checkDependencies(sourceEntities, target);
129
- if (depReason) {
130
- reasons.push(depReason);
131
- confidence = 'high';
132
- }
133
- // Check for API changes
134
- const apiChanges = this.checkAPIChanges(changedFiles, target);
135
- if (apiChanges) {
136
- reasons.push(apiChanges);
137
- if (confidence === 'low')
138
- confidence = 'medium';
139
- }
140
- // Check for shared configuration
141
- const configChanges = this.checkConfigChanges(changedFiles, target);
142
- if (configChanges) {
143
- reasons.push(configChanges);
144
- if (confidence === 'low')
145
- confidence = 'medium';
146
- }
147
- // Check for database/schema changes
148
- const schemaChanges = this.checkSchemaChanges(changedFiles, target);
149
- if (schemaChanges) {
150
- reasons.push(schemaChanges);
151
- confidence = 'high';
152
- }
153
- if (reasons.length > 0) {
154
- affected.push({ entity: target, reason: reasons, confidence });
155
- }
156
- }
157
- // Sort by confidence
158
- affected.sort((a, b) => {
159
- const confidenceOrder = { high: 3, medium: 2, low: 1 };
160
- return confidenceOrder[b.confidence] - confidenceOrder[a.confidence];
161
- });
162
- return affected;
163
- }
164
- async checkDependencies(sources, target) {
165
- const targetPkgPath = path.join(target.path, 'package.json');
166
- if (!fs.existsSync(targetPkgPath)) {
167
- return null;
168
- }
169
- try {
170
- const targetPkg = JSON.parse(fs.readFileSync(targetPkgPath, 'utf-8'));
171
- const dependencies = {
172
- ...targetPkg.dependencies,
173
- ...targetPkg.devDependencies,
174
- ...targetPkg.peerDependencies,
175
- };
176
- for (const source of sources) {
177
- const sourcePkgPath = path.join(source.path, 'package.json');
178
- if (fs.existsSync(sourcePkgPath)) {
179
- const sourcePkg = JSON.parse(fs.readFileSync(sourcePkgPath, 'utf-8'));
180
- if (sourcePkg.name && dependencies[sourcePkg.name]) {
181
- return `Depends on ${sourcePkg.name}`;
182
- }
183
- }
184
- }
185
- }
186
- catch (error) {
187
- // Ignore parsing errors
188
- }
189
- return null;
190
- }
191
- checkAPIChanges(changedFiles, target) {
192
- const apiPatterns = [
193
- /api\//i,
194
- /routes\//i,
195
- /controllers\//i,
196
- /services\//i,
197
- /endpoints\//i,
198
- /graphql\//i,
199
- ];
200
- const apiFiles = changedFiles.filter(f => apiPatterns.some(pattern => pattern.test(f)));
201
- if (apiFiles.length > 0) {
202
- // Check if target might consume these APIs
203
- const targetFiles = this.getEntityFiles(target.path);
204
- const hasApiConsumption = targetFiles.some(f => f.includes('api') || f.includes('client') || f.includes('service'));
205
- if (hasApiConsumption) {
206
- return `API changes detected (${apiFiles.length} files)`;
207
- }
208
- }
209
- return null;
210
- }
211
- checkConfigChanges(changedFiles, target) {
212
- const configPatterns = [
213
- /config\./i,
214
- /\.env/,
215
- /settings\./i,
216
- /constants\./i,
217
- ];
218
- const configFiles = changedFiles.filter(f => configPatterns.some(pattern => pattern.test(path.basename(f))));
219
- if (configFiles.length > 0) {
220
- return `Configuration changes detected (${configFiles.length} files)`;
221
- }
222
- return null;
223
- }
224
- checkSchemaChanges(changedFiles, target) {
225
- const schemaPatterns = [
226
- /schema\./i,
227
- /migration/i,
228
- /\.sql$/,
229
- /models?\//i,
230
- /entities\//i,
231
- ];
232
- const schemaFiles = changedFiles.filter(f => schemaPatterns.some(pattern => pattern.test(f)));
233
- if (schemaFiles.length > 0) {
234
- // Check if target uses database
235
- const targetFiles = this.getEntityFiles(target.path);
236
- const hasDatabase = targetFiles.some(f => f.includes('model') || f.includes('entity') || f.includes('schema') || f.includes('database'));
237
- if (hasDatabase) {
238
- return `Database/Schema changes detected (${schemaFiles.length} files)`;
239
- }
240
- }
241
- return null;
242
- }
243
- getEntityFiles(entityPath) {
244
- const files = [];
245
- if (!fs.existsSync(entityPath))
246
- return files;
247
- const walkDir = (dir) => {
248
- try {
249
- const entries = fs.readdirSync(dir, { withFileTypes: true });
250
- for (const entry of entries) {
251
- if (entry.name === 'node_modules' || entry.name === '.git')
252
- continue;
253
- const fullPath = path.join(dir, entry.name);
254
- if (entry.isDirectory()) {
255
- walkDir(fullPath);
256
- }
257
- else {
258
- files.push(path.relative(entityPath, fullPath));
259
- }
260
- }
261
- }
262
- catch (error) {
263
- // Ignore permission errors
264
- }
265
- };
266
- walkDir(entityPath);
267
- return files;
268
- }
269
- printAffected(affected, changedFiles, verbose) {
270
- this.log('');
271
- this.log(chalk_1.default.bold(`Changed files: ${changedFiles.length}`));
272
- if (verbose) {
273
- this.log(chalk_1.default.dim('Changed:'));
274
- changedFiles.slice(0, 5).forEach(f => {
275
- this.log(chalk_1.default.dim(` - ${path.basename(f)}`));
276
- });
277
- if (changedFiles.length > 5) {
278
- this.log(chalk_1.default.dim(` ... and ${changedFiles.length - 5} more`));
279
- }
280
- }
281
- this.log('');
282
- this.log(chalk_1.default.bold('Potentially affected entities:'));
283
- this.log('');
284
- const groupedByConfidence = {
285
- high: affected.filter(a => a.confidence === 'high'),
286
- medium: affected.filter(a => a.confidence === 'medium'),
287
- low: affected.filter(a => a.confidence === 'low'),
288
- };
289
- if (groupedByConfidence.high.length > 0) {
290
- this.log(chalk_1.default.red.bold('High confidence:'));
291
- groupedByConfidence.high.forEach(a => {
292
- this.log(` ${chalk_1.default.red('●')} ${chalk_1.default.bold(a.entity.name)}`);
293
- a.reason.forEach(r => {
294
- this.log(chalk_1.default.dim(` - ${r}`));
295
- });
296
- });
297
- this.log('');
298
- }
299
- if (groupedByConfidence.medium.length > 0) {
300
- this.log(chalk_1.default.yellow.bold('Medium confidence:'));
301
- groupedByConfidence.medium.forEach(a => {
302
- this.log(` ${chalk_1.default.yellow('●')} ${chalk_1.default.bold(a.entity.name)}`);
303
- if (verbose) {
304
- a.reason.forEach(r => {
305
- this.log(chalk_1.default.dim(` - ${r}`));
306
- });
307
- }
308
- });
309
- this.log('');
310
- }
311
- if (groupedByConfidence.low.length > 0) {
312
- this.log(chalk_1.default.dim('Low confidence:'));
313
- groupedByConfidence.low.forEach(a => {
314
- this.log(` ${chalk_1.default.gray('●')} ${a.entity.name}`);
315
- if (verbose) {
316
- a.reason.forEach(r => {
317
- this.log(chalk_1.default.dim(` - ${r}`));
318
- });
319
- }
320
- });
321
- }
322
- this.log('');
323
- this.log(chalk_1.default.dim('Tip: Use --verbose flag for detailed analysis'));
324
- }
325
- }
326
- exports.default = Affected;
@@ -1,33 +0,0 @@
1
- import { BaseCommand } from '../base-command';
2
- export default class Audit extends BaseCommand {
3
- static description: string;
4
- static examples: string[];
5
- static flags: {
6
- entity: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
7
- security: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
8
- compliance: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
9
- access: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
10
- changes: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
11
- json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
12
- };
13
- run(): Promise<void>;
14
- private performAudit;
15
- private auditEntity;
16
- private auditMetadata;
17
- private auditGitRepository;
18
- private auditFileStructure;
19
- private performSecurityAudit;
20
- private performComplianceAudit;
21
- private performAccessAudit;
22
- private displayAuditResults;
23
- private displayEntityAudit;
24
- private displayRecommendations;
25
- private getAuditTypes;
26
- private hasSpecificAuditType;
27
- private calculateMetadataCompleteness;
28
- private getRequiredMetadataFields;
29
- private hasNestedField;
30
- private getExpectedFiles;
31
- private calculateStructureScore;
32
- private getAllFiles;
33
- }