@git.zone/cli 2.3.0 → 2.7.0

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.
@@ -1,6 +1,6 @@
1
1
  import * as plugins from './mod.plugins.js';
2
2
  import { FormatContext } from './classes.formatcontext.js';
3
- import type { IPlannedChange } from './interfaces.format.js';
3
+ import type { IPlannedChange, ICheckResult } from './interfaces.format.js';
4
4
  import { Project } from '../classes.project.js';
5
5
 
6
6
  export abstract class BaseFormatter {
@@ -79,4 +79,94 @@ export abstract class BaseFormatter {
79
79
  protected async shouldProcessFile(filepath: string): Promise<boolean> {
80
80
  return true;
81
81
  }
82
+
83
+ /**
84
+ * Check for diffs without applying changes
85
+ * Returns information about what would change
86
+ */
87
+ async check(): Promise<ICheckResult> {
88
+ const changes = await this.analyze();
89
+ const diffs: ICheckResult['diffs'] = [];
90
+
91
+ for (const change of changes) {
92
+ // Skip generic changes that don't have actual content
93
+ if (change.path === '<various files>') {
94
+ continue;
95
+ }
96
+
97
+ if (change.type === 'modify' || change.type === 'create') {
98
+ // Read current content if file exists
99
+ let currentContent: string | undefined;
100
+ try {
101
+ currentContent = await plugins.smartfs.file(change.path).encoding('utf8').read() as string;
102
+ } catch {
103
+ // File doesn't exist yet
104
+ currentContent = undefined;
105
+ }
106
+
107
+ const newContent = change.content;
108
+
109
+ // Check if there's an actual diff
110
+ if (currentContent !== newContent && newContent !== undefined) {
111
+ diffs.push({
112
+ path: change.path,
113
+ type: change.type,
114
+ before: currentContent,
115
+ after: newContent,
116
+ });
117
+ }
118
+ } else if (change.type === 'delete') {
119
+ // Check if file exists before marking for deletion
120
+ try {
121
+ const currentContent = await plugins.smartfs.file(change.path).encoding('utf8').read() as string;
122
+ diffs.push({
123
+ path: change.path,
124
+ type: 'delete',
125
+ before: currentContent,
126
+ after: undefined,
127
+ });
128
+ } catch {
129
+ // File doesn't exist, nothing to delete
130
+ }
131
+ }
132
+ }
133
+
134
+ return {
135
+ hasDiff: diffs.length > 0,
136
+ diffs,
137
+ };
138
+ }
139
+
140
+ /**
141
+ * Display a single diff using smartdiff
142
+ */
143
+ displayDiff(diff: ICheckResult['diffs'][0]): void {
144
+ console.log(`\n--- ${diff.path}`);
145
+ if (diff.before && diff.after) {
146
+ console.log(plugins.smartdiff.formatLineDiffForConsole(diff.before, diff.after));
147
+ } else if (diff.after && !diff.before) {
148
+ console.log(' (new file)');
149
+ // Show first few lines of new content
150
+ const lines = diff.after.split('\n').slice(0, 10);
151
+ lines.forEach(line => console.log(` + ${line}`));
152
+ if (diff.after.split('\n').length > 10) {
153
+ console.log(' ... (truncated)');
154
+ }
155
+ } else if (diff.before && !diff.after) {
156
+ console.log(' (file will be deleted)');
157
+ }
158
+ }
159
+
160
+ /**
161
+ * Display all diffs from a check result
162
+ */
163
+ displayAllDiffs(result: ICheckResult): void {
164
+ if (!result.hasDiff) {
165
+ console.log(' No changes detected');
166
+ return;
167
+ }
168
+ for (const diff of result.diffs) {
169
+ this.displayDiff(diff);
170
+ }
171
+ }
82
172
  }
@@ -2,6 +2,7 @@ import * as plugins from './mod.plugins.js';
2
2
  import { Project } from '../classes.project.js';
3
3
  import { FormatContext } from './classes.formatcontext.js';
4
4
  import { FormatPlanner } from './classes.formatplanner.js';
5
+ import { BaseFormatter } from './classes.baseformatter.js';
5
6
  import { logger, setVerboseMode } from '../gitzone.logging.js';
6
7
 
7
8
  // Import wrapper classes for formatters
@@ -18,7 +19,8 @@ import { CopyFormatter } from './formatters/copy.formatter.js';
18
19
 
19
20
  export let run = async (
20
21
  options: {
21
- dryRun?: boolean;
22
+ write?: boolean; // Explicitly write changes (default: false, dry-mode)
23
+ dryRun?: boolean; // Deprecated, kept for compatibility
22
24
  yes?: boolean;
23
25
  planOnly?: boolean;
24
26
  savePlan?: string;
@@ -34,7 +36,11 @@ export let run = async (
34
36
  setVerboseMode(true);
35
37
  }
36
38
 
37
- const project = await Project.fromCwd();
39
+ // Determine if we should write changes
40
+ // Default is dry-mode (no writing) unless --write/-w is specified
41
+ const shouldWrite = options.write ?? (options.dryRun === false);
42
+
43
+ const project = await Project.fromCwd({ requireProjectType: false });
38
44
  const context = new FormatContext();
39
45
  // Cache system removed - no longer needed
40
46
  const planner = new FormatPlanner();
@@ -126,9 +132,9 @@ export let run = async (
126
132
  return;
127
133
  }
128
134
 
129
- // Dry-run mode
130
- if (options.dryRun) {
131
- logger.log('info', 'Dry-run mode - no changes will be made');
135
+ // Dry-run mode (default behavior)
136
+ if (!shouldWrite) {
137
+ logger.log('info', 'Dry-run mode - use --write (-w) to apply changes');
132
138
  return;
133
139
  }
134
140
 
@@ -195,3 +201,68 @@ export const handleCleanBackups = async (): Promise<void> => {
195
201
  'Backup cleaning has been disabled - backup system removed',
196
202
  );
197
203
  };
204
+
205
+ // Import the ICheckResult type for external use
206
+ import type { ICheckResult } from './interfaces.format.js';
207
+ export type { ICheckResult };
208
+
209
+ // Formatters that don't require projectType to be set
210
+ const formattersNotRequiringProjectType = ['npmextra', 'prettier', 'cleanup', 'packagejson'];
211
+
212
+ /**
213
+ * Run a single formatter by name (for use by other modules)
214
+ */
215
+ export const runFormatter = async (
216
+ formatterName: string,
217
+ options: {
218
+ silent?: boolean;
219
+ checkOnly?: boolean; // Only check for diffs, don't apply
220
+ showDiff?: boolean; // Show the diff output
221
+ } = {}
222
+ ): Promise<ICheckResult | void> => {
223
+ // Determine if this formatter requires projectType
224
+ const requireProjectType = !formattersNotRequiringProjectType.includes(formatterName);
225
+ const project = await Project.fromCwd({ requireProjectType });
226
+ const context = new FormatContext();
227
+
228
+ // Map formatter names to classes
229
+ const formatterMap: Record<string, new (ctx: FormatContext, proj: Project) => BaseFormatter> = {
230
+ cleanup: CleanupFormatter,
231
+ npmextra: NpmextraFormatter,
232
+ license: LicenseFormatter,
233
+ packagejson: PackageJsonFormatter,
234
+ templates: TemplatesFormatter,
235
+ gitignore: GitignoreFormatter,
236
+ tsconfig: TsconfigFormatter,
237
+ prettier: PrettierFormatter,
238
+ readme: ReadmeFormatter,
239
+ copy: CopyFormatter,
240
+ };
241
+
242
+ const FormatterClass = formatterMap[formatterName];
243
+ if (!FormatterClass) {
244
+ throw new Error(`Unknown formatter: ${formatterName}`);
245
+ }
246
+
247
+ const formatter = new FormatterClass(context, project);
248
+
249
+ // Check-only mode: just check for diffs and optionally display them
250
+ if (options.checkOnly) {
251
+ const result = await formatter.check();
252
+ if (result.hasDiff && options.showDiff) {
253
+ formatter.displayAllDiffs(result);
254
+ }
255
+ return result;
256
+ }
257
+
258
+ // Normal mode: analyze and apply changes
259
+ const changes = await formatter.analyze();
260
+
261
+ for (const change of changes) {
262
+ await formatter.applyChange(change);
263
+ }
264
+
265
+ if (!options.silent) {
266
+ logger.log('success', `Formatter '${formatterName}' completed`);
267
+ }
268
+ };
@@ -39,7 +39,18 @@ export type IPlannedChange = {
39
39
  path: string;
40
40
  module: string;
41
41
  description: string;
42
- content?: string; // For create/modify operations
42
+ content?: string; // New content for create/modify operations
43
+ originalContent?: string; // Original content for comparison
43
44
  diff?: string;
44
45
  size?: number;
45
46
  };
47
+
48
+ export interface ICheckResult {
49
+ hasDiff: boolean;
50
+ diffs: Array<{
51
+ path: string;
52
+ type: 'create' | 'modify' | 'delete';
53
+ before?: string;
54
+ after?: string;
55
+ }>;
56
+ }
@@ -7,38 +7,85 @@ import * as paths from '../paths.js';
7
7
  import { logger } from '../gitzone.logging.js';
8
8
 
9
9
  export let run = async () => {
10
- const done = plugins.smartpromise.defer();
11
- logger.log('warn', 'no action specified');
10
+ console.log('');
11
+ console.log('╭─────────────────────────────────────────────────────────────╮');
12
+ console.log('│ gitzone - Development Workflow CLI │');
13
+ console.log('╰─────────────────────────────────────────────────────────────╯');
14
+ console.log('');
12
15
 
13
- const dirEntries = await plugins.smartfs.directory(paths.templatesDir).list();
14
- const templates: string[] = [];
15
- for (const entry of dirEntries) {
16
- try {
17
- const stats = await plugins.smartfs
18
- .file(plugins.path.join(paths.templatesDir, entry.path))
19
- .stat();
20
- if (stats.isDirectory) {
21
- templates.push(entry.name);
22
- }
23
- } catch {
24
- // Skip entries that can't be accessed
25
- }
26
- }
16
+ const interactInstance = new plugins.smartinteract.SmartInteract();
17
+ const response = await interactInstance.askQuestion({
18
+ type: 'list',
19
+ name: 'action',
20
+ message: 'What would you like to do?',
21
+ default: 'commit',
22
+ choices: [
23
+ { name: 'Commit changes (semantic versioning)', value: 'commit' },
24
+ { name: 'Format project files', value: 'format' },
25
+ { name: 'Configure release settings', value: 'config' },
26
+ { name: 'Create from template', value: 'template' },
27
+ { name: 'Manage dev services (MongoDB, S3)', value: 'services' },
28
+ { name: 'Open project assets', value: 'open' },
29
+ { name: 'Show help', value: 'help' },
30
+ ],
31
+ });
27
32
 
28
- let projects = `\n`;
29
- for (const template of templates) {
30
- projects += ` - ${template}\n`;
31
- }
33
+ const action = (response as any).value;
32
34
 
33
- logger.log(
34
- 'info',
35
- `
36
- You can do one of the following things:
37
- * create a new project with 'gitzone template [template]'
38
- the following templates exist: ${projects}
39
- * format a project with 'gitzone format'
40
- `,
41
- );
42
- done.resolve();
43
- return done.promise;
35
+ switch (action) {
36
+ case 'commit': {
37
+ const modCommit = await import('../mod_commit/index.js');
38
+ await modCommit.run({ _: ['commit'] });
39
+ break;
40
+ }
41
+ case 'format': {
42
+ const modFormat = await import('../mod_format/index.js');
43
+ await modFormat.run({ interactive: true });
44
+ break;
45
+ }
46
+ case 'config': {
47
+ const modConfig = await import('../mod_config/index.js');
48
+ await modConfig.run({ _: ['config'] });
49
+ break;
50
+ }
51
+ case 'template': {
52
+ const modTemplate = await import('../mod_template/index.js');
53
+ await modTemplate.run({ _: ['template'] });
54
+ break;
55
+ }
56
+ case 'services': {
57
+ const modServices = await import('../mod_services/index.js');
58
+ await modServices.run({ _: ['services'] });
59
+ break;
60
+ }
61
+ case 'open': {
62
+ const modOpen = await import('../mod_open/index.js');
63
+ await modOpen.run({ _: ['open'] });
64
+ break;
65
+ }
66
+ case 'help':
67
+ showHelp();
68
+ break;
69
+ }
44
70
  };
71
+
72
+ function showHelp(): void {
73
+ console.log('');
74
+ console.log('Usage: gitzone <command> [options]');
75
+ console.log('');
76
+ console.log('Commands:');
77
+ console.log(' commit Create a semantic commit with versioning');
78
+ console.log(' format Format and standardize project files');
79
+ console.log(' config Manage release registry configuration');
80
+ console.log(' template Create a new project from template');
81
+ console.log(' services Manage dev services (MongoDB, S3/MinIO)');
82
+ console.log(' open Open project assets (GitLab, npm, etc.)');
83
+ console.log(' docker Docker-related operations');
84
+ console.log(' deprecate Deprecate a package on npm');
85
+ console.log(' meta Run meta commands');
86
+ console.log(' start Start working on a project');
87
+ console.log(' helpers Run helper utilities');
88
+ console.log('');
89
+ console.log('Run gitzone <command> --help for more information on a command.');
90
+ console.log('');
91
+ }