@contractual/cli 0.1.0-dev.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.
Files changed (74) hide show
  1. package/LICENSE +21 -0
  2. package/bin/cli.js +2 -0
  3. package/dist/commands/breaking.command.d.ts +19 -0
  4. package/dist/commands/breaking.command.d.ts.map +1 -0
  5. package/dist/commands/breaking.command.js +138 -0
  6. package/dist/commands/changeset.command.d.ts +11 -0
  7. package/dist/commands/changeset.command.d.ts.map +1 -0
  8. package/dist/commands/changeset.command.js +65 -0
  9. package/dist/commands/contract.command.d.ts +34 -0
  10. package/dist/commands/contract.command.d.ts.map +1 -0
  11. package/dist/commands/contract.command.js +259 -0
  12. package/dist/commands/diff.command.d.ts +21 -0
  13. package/dist/commands/diff.command.d.ts.map +1 -0
  14. package/dist/commands/diff.command.js +58 -0
  15. package/dist/commands/index.d.ts +7 -0
  16. package/dist/commands/index.d.ts.map +1 -0
  17. package/dist/commands/index.js +6 -0
  18. package/dist/commands/init.command.d.ts +21 -0
  19. package/dist/commands/init.command.d.ts.map +1 -0
  20. package/dist/commands/init.command.js +294 -0
  21. package/dist/commands/lint.command.d.ts +16 -0
  22. package/dist/commands/lint.command.d.ts.map +1 -0
  23. package/dist/commands/lint.command.js +174 -0
  24. package/dist/commands/pre.command.d.ts +14 -0
  25. package/dist/commands/pre.command.d.ts.map +1 -0
  26. package/dist/commands/pre.command.js +141 -0
  27. package/dist/commands/status.command.d.ts +5 -0
  28. package/dist/commands/status.command.d.ts.map +1 -0
  29. package/dist/commands/status.command.js +120 -0
  30. package/dist/commands/version.command.d.ts +16 -0
  31. package/dist/commands/version.command.d.ts.map +1 -0
  32. package/dist/commands/version.command.js +247 -0
  33. package/dist/commands.d.ts +2 -0
  34. package/dist/commands.d.ts.map +1 -0
  35. package/dist/commands.js +84 -0
  36. package/dist/config/index.d.ts +3 -0
  37. package/dist/config/index.d.ts.map +1 -0
  38. package/dist/config/index.js +2 -0
  39. package/dist/config/loader.d.ts +28 -0
  40. package/dist/config/loader.d.ts.map +1 -0
  41. package/dist/config/loader.js +123 -0
  42. package/dist/config/schema.json +121 -0
  43. package/dist/config/validator.d.ts +28 -0
  44. package/dist/config/validator.d.ts.map +1 -0
  45. package/dist/config/validator.js +34 -0
  46. package/dist/core/diff.d.ts +26 -0
  47. package/dist/core/diff.d.ts.map +1 -0
  48. package/dist/core/diff.js +89 -0
  49. package/dist/formatters/diff.d.ts +31 -0
  50. package/dist/formatters/diff.d.ts.map +1 -0
  51. package/dist/formatters/diff.js +139 -0
  52. package/dist/governance/index.d.ts +11 -0
  53. package/dist/governance/index.d.ts.map +1 -0
  54. package/dist/governance/index.js +14 -0
  55. package/dist/index.d.ts +9 -0
  56. package/dist/index.d.ts.map +1 -0
  57. package/dist/index.js +9 -0
  58. package/dist/utils/exec.d.ts +29 -0
  59. package/dist/utils/exec.d.ts.map +1 -0
  60. package/dist/utils/exec.js +66 -0
  61. package/dist/utils/files.d.ts +36 -0
  62. package/dist/utils/files.d.ts.map +1 -0
  63. package/dist/utils/files.js +137 -0
  64. package/dist/utils/index.d.ts +4 -0
  65. package/dist/utils/index.d.ts.map +1 -0
  66. package/dist/utils/index.js +3 -0
  67. package/dist/utils/output.d.ts +25 -0
  68. package/dist/utils/output.d.ts.map +1 -0
  69. package/dist/utils/output.js +73 -0
  70. package/dist/utils/prompts.d.ts +90 -0
  71. package/dist/utils/prompts.d.ts.map +1 -0
  72. package/dist/utils/prompts.js +119 -0
  73. package/package.json +81 -0
  74. package/src/config/schema.json +121 -0
@@ -0,0 +1,36 @@
1
+ import type { ContractType } from '@contractual/types';
2
+ import { CHANGESETS_DIR, SNAPSHOTS_DIR, VERSIONS_FILE } from '@contractual/changesets';
3
+ /**
4
+ * The .contractual directory name
5
+ */
6
+ export declare const CONTRACTUAL_DIR = ".contractual";
7
+ export { CHANGESETS_DIR, SNAPSHOTS_DIR, VERSIONS_FILE };
8
+ /**
9
+ * Find the .contractual directory starting from a given path
10
+ */
11
+ export declare function findContractualDir(startDir?: string): string | null;
12
+ /**
13
+ * Ensure the .contractual directory structure exists
14
+ */
15
+ export declare function ensureContractualDir(baseDir: string): string;
16
+ /**
17
+ * Read and parse a spec file (YAML or JSON)
18
+ */
19
+ export declare function readSpecFile(filePath: string): unknown;
20
+ /**
21
+ * Detect spec type from file path and content
22
+ */
23
+ export declare function detectSpecType(filePath: string): ContractType | null;
24
+ /**
25
+ * Get the file extension for a contract type
26
+ */
27
+ export declare function getSpecExtension(filePath: string): string;
28
+ /**
29
+ * Copy a spec file to the snapshots directory
30
+ */
31
+ export declare function copyToSnapshots(specPath: string, contractName: string, contractualDir: string): string;
32
+ /**
33
+ * Get the snapshot path for a contract
34
+ */
35
+ export declare function getSnapshotPath(contractName: string, contractualDir: string): string | null;
36
+ //# sourceMappingURL=files.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"files.d.ts","sourceRoot":"","sources":["../../src/utils/files.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAEvF;;GAEG;AACH,eAAO,MAAM,eAAe,iBAAiB,CAAC;AAG9C,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,aAAa,EAAE,CAAC;AAExD;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,GAAE,MAAsB,GAAG,MAAM,GAAG,IAAI,CAqBlF;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAyB5D;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAUtD;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,YAAY,GAAG,IAAI,CAgCpE;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAGzD;AAED;;GAEG;AACH,wBAAgB,eAAe,CAC7B,QAAQ,EAAE,MAAM,EAChB,YAAY,EAAE,MAAM,EACpB,cAAc,EAAE,MAAM,GACrB,MAAM,CAKR;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,YAAY,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAY3F"}
@@ -0,0 +1,137 @@
1
+ import { existsSync, readFileSync, mkdirSync, writeFileSync, copyFileSync } from 'node:fs';
2
+ import { dirname, extname, join, resolve } from 'node:path';
3
+ import { parse as parseYaml } from 'yaml';
4
+ import { CHANGESETS_DIR, SNAPSHOTS_DIR, VERSIONS_FILE } from '@contractual/changesets';
5
+ /**
6
+ * The .contractual directory name
7
+ */
8
+ export const CONTRACTUAL_DIR = '.contractual';
9
+ // Re-export constants from @contractual/changesets for backward compatibility
10
+ export { CHANGESETS_DIR, SNAPSHOTS_DIR, VERSIONS_FILE };
11
+ /**
12
+ * Find the .contractual directory starting from a given path
13
+ */
14
+ export function findContractualDir(startDir = process.cwd()) {
15
+ let currentDir = resolve(startDir);
16
+ const root = dirname(currentDir);
17
+ while (currentDir !== root) {
18
+ const contractualDir = join(currentDir, CONTRACTUAL_DIR);
19
+ if (existsSync(contractualDir)) {
20
+ return contractualDir;
21
+ }
22
+ const parentDir = dirname(currentDir);
23
+ if (parentDir === currentDir)
24
+ break;
25
+ currentDir = parentDir;
26
+ }
27
+ // Check root directory too
28
+ const contractualDir = join(currentDir, CONTRACTUAL_DIR);
29
+ if (existsSync(contractualDir)) {
30
+ return contractualDir;
31
+ }
32
+ return null;
33
+ }
34
+ /**
35
+ * Ensure the .contractual directory structure exists
36
+ */
37
+ export function ensureContractualDir(baseDir) {
38
+ const contractualDir = join(baseDir, CONTRACTUAL_DIR);
39
+ const changesetsDir = join(contractualDir, CHANGESETS_DIR);
40
+ const snapshotsDir = join(contractualDir, SNAPSHOTS_DIR);
41
+ mkdirSync(changesetsDir, { recursive: true });
42
+ mkdirSync(snapshotsDir, { recursive: true });
43
+ // Create versions.json if it doesn't exist
44
+ const versionsPath = join(contractualDir, VERSIONS_FILE);
45
+ if (!existsSync(versionsPath)) {
46
+ writeFileSync(versionsPath, '{}', 'utf-8');
47
+ }
48
+ // Create .gitkeep files
49
+ const changesetsGitkeep = join(changesetsDir, '.gitkeep');
50
+ const snapshotsGitkeep = join(snapshotsDir, '.gitkeep');
51
+ if (!existsSync(changesetsGitkeep)) {
52
+ writeFileSync(changesetsGitkeep, '', 'utf-8');
53
+ }
54
+ if (!existsSync(snapshotsGitkeep)) {
55
+ writeFileSync(snapshotsGitkeep, '', 'utf-8');
56
+ }
57
+ return contractualDir;
58
+ }
59
+ /**
60
+ * Read and parse a spec file (YAML or JSON)
61
+ */
62
+ export function readSpecFile(filePath) {
63
+ const content = readFileSync(filePath, 'utf-8');
64
+ const ext = extname(filePath).toLowerCase();
65
+ if (ext === '.json') {
66
+ return JSON.parse(content);
67
+ }
68
+ // YAML (handles .yaml, .yml)
69
+ return parseYaml(content);
70
+ }
71
+ /**
72
+ * Detect spec type from file path and content
73
+ */
74
+ export function detectSpecType(filePath) {
75
+ // Check extension patterns first
76
+ if (/\.openapi\.(ya?ml|json)$/i.test(filePath))
77
+ return 'openapi';
78
+ if (/\.asyncapi\.(ya?ml|json)$/i.test(filePath))
79
+ return 'asyncapi';
80
+ if (/\.odcs\.ya?ml$/i.test(filePath))
81
+ return 'odcs';
82
+ if (/\.schema\.json$/i.test(filePath))
83
+ return 'json-schema';
84
+ // Content sniffing
85
+ try {
86
+ const spec = readSpecFile(filePath);
87
+ if (!spec || typeof spec !== 'object')
88
+ return null;
89
+ // OpenAPI detection
90
+ if ('openapi' in spec || 'swagger' in spec)
91
+ return 'openapi';
92
+ // AsyncAPI detection
93
+ if ('asyncapi' in spec)
94
+ return 'asyncapi';
95
+ // ODCS detection
96
+ if ('dataContractSpecification' in spec || spec.kind === 'DataContract')
97
+ return 'odcs';
98
+ // JSON Schema detection
99
+ if (('$schema' in spec && String(spec.$schema).includes('json-schema')) ||
100
+ ('type' in spec && 'properties' in spec))
101
+ return 'json-schema';
102
+ return null;
103
+ }
104
+ catch {
105
+ return null;
106
+ }
107
+ }
108
+ /**
109
+ * Get the file extension for a contract type
110
+ */
111
+ export function getSpecExtension(filePath) {
112
+ const ext = extname(filePath).toLowerCase();
113
+ return ext || '.yaml';
114
+ }
115
+ /**
116
+ * Copy a spec file to the snapshots directory
117
+ */
118
+ export function copyToSnapshots(specPath, contractName, contractualDir) {
119
+ const ext = getSpecExtension(specPath);
120
+ const snapshotPath = join(contractualDir, SNAPSHOTS_DIR, `${contractName}${ext}`);
121
+ copyFileSync(specPath, snapshotPath);
122
+ return snapshotPath;
123
+ }
124
+ /**
125
+ * Get the snapshot path for a contract
126
+ */
127
+ export function getSnapshotPath(contractName, contractualDir) {
128
+ const snapshotsDir = join(contractualDir, SNAPSHOTS_DIR);
129
+ const extensions = ['.yaml', '.yml', '.json'];
130
+ for (const ext of extensions) {
131
+ const snapshotPath = join(snapshotsDir, `${contractName}${ext}`);
132
+ if (existsSync(snapshotPath)) {
133
+ return snapshotPath;
134
+ }
135
+ }
136
+ return null;
137
+ }
@@ -0,0 +1,4 @@
1
+ export * from './exec.js';
2
+ export * from './files.js';
3
+ export * from './output.js';
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,cAAc,WAAW,CAAC;AAC1B,cAAc,YAAY,CAAC;AAC3B,cAAc,aAAa,CAAC"}
@@ -0,0 +1,3 @@
1
+ export * from './exec.js';
2
+ export * from './files.js';
3
+ export * from './output.js';
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Print a success message with green checkmark
3
+ */
4
+ export declare function printSuccess(message: string): void;
5
+ /**
6
+ * Print an error message with red X
7
+ */
8
+ export declare function printError(message: string): void;
9
+ /**
10
+ * Print a warning message with yellow warning symbol
11
+ */
12
+ export declare function printWarning(message: string): void;
13
+ /**
14
+ * Print an info message with blue info symbol
15
+ */
16
+ export declare function printInfo(message: string): void;
17
+ /**
18
+ * Print a simple ASCII table
19
+ */
20
+ export declare function printTable(headers: string[], rows: string[][]): void;
21
+ /**
22
+ * Format severity level with appropriate color
23
+ */
24
+ export declare function formatSeverity(severity: 'breaking' | 'non-breaking' | 'patch' | 'unknown'): string;
25
+ //# sourceMappingURL=output.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"output.d.ts","sourceRoot":"","sources":["../../src/utils/output.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAElD;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAEhD;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAElD;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAE/C;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,GAAG,IAAI,CAkCpE;AAED;;GAEG;AACH,wBAAgB,cAAc,CAC5B,QAAQ,EAAE,UAAU,GAAG,cAAc,GAAG,OAAO,GAAG,SAAS,GAC1D,MAAM,CAYR"}
@@ -0,0 +1,73 @@
1
+ import chalk from 'chalk';
2
+ /**
3
+ * Print a success message with green checkmark
4
+ */
5
+ export function printSuccess(message) {
6
+ console.log(chalk.green('✓') + ' ' + message);
7
+ }
8
+ /**
9
+ * Print an error message with red X
10
+ */
11
+ export function printError(message) {
12
+ console.log(chalk.red('✗') + ' ' + message);
13
+ }
14
+ /**
15
+ * Print a warning message with yellow warning symbol
16
+ */
17
+ export function printWarning(message) {
18
+ console.log(chalk.yellow('⚠') + ' ' + message);
19
+ }
20
+ /**
21
+ * Print an info message with blue info symbol
22
+ */
23
+ export function printInfo(message) {
24
+ console.log(chalk.blue('ℹ') + ' ' + message);
25
+ }
26
+ /**
27
+ * Print a simple ASCII table
28
+ */
29
+ export function printTable(headers, rows) {
30
+ if (headers.length === 0) {
31
+ return;
32
+ }
33
+ // Calculate column widths
34
+ const columnWidths = headers.map((header, index) => {
35
+ const maxRowWidth = rows.reduce((max, row) => {
36
+ const cellLength = row[index]?.length ?? 0;
37
+ return Math.max(max, cellLength);
38
+ }, 0);
39
+ return Math.max(header.length, maxRowWidth);
40
+ });
41
+ // Create separator line
42
+ const separator = '+' + columnWidths.map((width) => '-'.repeat(width + 2)).join('+') + '+';
43
+ // Format a row
44
+ const formatRow = (cells) => {
45
+ return ('|' +
46
+ cells.map((cell, index) => ' ' + (cell ?? '').padEnd(columnWidths[index]) + ' ').join('|') +
47
+ '|');
48
+ };
49
+ // Print table
50
+ console.log(separator);
51
+ console.log(formatRow(headers));
52
+ console.log(separator);
53
+ for (const row of rows) {
54
+ console.log(formatRow(row));
55
+ }
56
+ console.log(separator);
57
+ }
58
+ /**
59
+ * Format severity level with appropriate color
60
+ */
61
+ export function formatSeverity(severity) {
62
+ switch (severity) {
63
+ case 'breaking':
64
+ return chalk.red.bold('BREAKING');
65
+ case 'non-breaking':
66
+ return chalk.yellow('NON-BREAKING');
67
+ case 'patch':
68
+ return chalk.green('PATCH');
69
+ case 'unknown':
70
+ default:
71
+ return chalk.gray('UNKNOWN');
72
+ }
73
+ }
@@ -0,0 +1,90 @@
1
+ /**
2
+ * Check if running in interactive mode (TTY available)
3
+ */
4
+ export declare function isInteractive(): boolean;
5
+ /**
6
+ * Check if running in CI environment
7
+ */
8
+ export declare function isCI(): boolean;
9
+ /**
10
+ * Options for prompt functions
11
+ */
12
+ export interface PromptOptions {
13
+ /** Skip prompts and use defaults (--yes flag) */
14
+ yes?: boolean;
15
+ /** Force interactive mode even in CI */
16
+ interactive?: boolean;
17
+ }
18
+ /**
19
+ * Determine if prompts should be shown
20
+ */
21
+ export declare function shouldPrompt(options: PromptOptions): boolean;
22
+ /**
23
+ * Prompt for a selection from a list of choices
24
+ */
25
+ export declare function promptSelect<T extends string>(message: string, choices: Array<{
26
+ value: T;
27
+ name: string;
28
+ description?: string;
29
+ }>, defaultValue: T, options?: PromptOptions): Promise<T>;
30
+ /**
31
+ * Prompt for text input
32
+ */
33
+ export declare function promptInput(message: string, defaultValue: string, options?: PromptOptions): Promise<string>;
34
+ /**
35
+ * Prompt for confirmation (yes/no)
36
+ */
37
+ export declare function promptConfirm(message: string, defaultValue: boolean, options?: PromptOptions): Promise<boolean>;
38
+ /**
39
+ * Prompt for version input with validation
40
+ */
41
+ export declare function promptVersion(message: string, defaultValue: string, options?: PromptOptions): Promise<string>;
42
+ /**
43
+ * Common version choices for init
44
+ */
45
+ export declare const VERSION_CHOICES: readonly [{
46
+ readonly value: "0.0.0";
47
+ readonly name: "0.0.0";
48
+ readonly description: "Start fresh (recommended for new projects)";
49
+ }, {
50
+ readonly value: "1.0.0";
51
+ readonly name: "1.0.0";
52
+ readonly description: "Production-ready";
53
+ }, {
54
+ readonly value: "custom";
55
+ readonly name: "Custom";
56
+ readonly description: "Enter a custom version";
57
+ }];
58
+ /**
59
+ * Common versioning mode choices
60
+ */
61
+ export declare const VERSIONING_MODE_CHOICES: readonly [{
62
+ readonly value: "independent";
63
+ readonly name: "Independent";
64
+ readonly description: "Each contract versioned separately";
65
+ }, {
66
+ readonly value: "fixed";
67
+ readonly name: "Fixed";
68
+ readonly description: "All contracts share same version";
69
+ }];
70
+ /**
71
+ * Contract type choices
72
+ */
73
+ export declare const CONTRACT_TYPE_CHOICES: readonly [{
74
+ readonly value: "openapi";
75
+ readonly name: "OpenAPI";
76
+ readonly description: "OpenAPI/Swagger specification";
77
+ }, {
78
+ readonly value: "asyncapi";
79
+ readonly name: "AsyncAPI";
80
+ readonly description: "AsyncAPI specification";
81
+ }, {
82
+ readonly value: "json-schema";
83
+ readonly name: "JSON Schema";
84
+ readonly description: "JSON Schema definition";
85
+ }, {
86
+ readonly value: "odcs";
87
+ readonly name: "ODCS";
88
+ readonly description: "Open Data Contract Standard";
89
+ }];
90
+ //# sourceMappingURL=prompts.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prompts.d.ts","sourceRoot":"","sources":["../../src/utils/prompts.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,wBAAgB,aAAa,IAAI,OAAO,CAEvC;AAED;;GAEG;AACH,wBAAgB,IAAI,IAAI,OAAO,CAS9B;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,iDAAiD;IACjD,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,wCAAwC;IACxC,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAK5D;AAED;;GAEG;AACH,wBAAsB,YAAY,CAAC,CAAC,SAAS,MAAM,EACjD,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,KAAK,CAAC;IAAE,KAAK,EAAE,CAAC,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,EAChE,YAAY,EAAE,CAAC,EACf,OAAO,GAAE,aAAkB,GAC1B,OAAO,CAAC,CAAC,CAAC,CAcZ;AAED;;GAEG;AACH,wBAAsB,WAAW,CAC/B,OAAO,EAAE,MAAM,EACf,YAAY,EAAE,MAAM,EACpB,OAAO,GAAE,aAAkB,GAC1B,OAAO,CAAC,MAAM,CAAC,CASjB;AAED;;GAEG;AACH,wBAAsB,aAAa,CACjC,OAAO,EAAE,MAAM,EACf,YAAY,EAAE,OAAO,EACrB,OAAO,GAAE,aAAkB,GAC1B,OAAO,CAAC,OAAO,CAAC,CASlB;AAED;;GAEG;AACH,wBAAsB,aAAa,CACjC,OAAO,EAAE,MAAM,EACf,YAAY,EAAE,MAAM,EACpB,OAAO,GAAE,aAAkB,GAC1B,OAAO,CAAC,MAAM,CAAC,CAkBjB;AAED;;GAEG;AACH,eAAO,MAAM,eAAe;;;;;;;;;;;;EAIlB,CAAC;AAEX;;GAEG;AACH,eAAO,MAAM,uBAAuB;;;;;;;;EAO1B,CAAC;AAEX;;GAEG;AACH,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;EAKxB,CAAC"}
@@ -0,0 +1,119 @@
1
+ import { select, input, confirm } from '@inquirer/prompts';
2
+ /**
3
+ * Check if running in interactive mode (TTY available)
4
+ */
5
+ export function isInteractive() {
6
+ return process.stdin.isTTY === true && process.stdout.isTTY === true;
7
+ }
8
+ /**
9
+ * Check if running in CI environment
10
+ */
11
+ export function isCI() {
12
+ return (process.env.CI === 'true' ||
13
+ process.env.CI === '1' ||
14
+ process.env.CONTINUOUS_INTEGRATION === 'true' ||
15
+ process.env.GITHUB_ACTIONS === 'true' ||
16
+ process.env.GITLAB_CI === 'true' ||
17
+ process.env.CIRCLECI === 'true');
18
+ }
19
+ /**
20
+ * Determine if prompts should be shown
21
+ */
22
+ export function shouldPrompt(options) {
23
+ if (options.yes)
24
+ return false;
25
+ if (options.interactive)
26
+ return true;
27
+ if (isCI())
28
+ return false;
29
+ return isInteractive();
30
+ }
31
+ /**
32
+ * Prompt for a selection from a list of choices
33
+ */
34
+ export async function promptSelect(message, choices, defaultValue, options = {}) {
35
+ if (!shouldPrompt(options)) {
36
+ return defaultValue;
37
+ }
38
+ return select({
39
+ message,
40
+ choices: choices.map((c) => ({
41
+ value: c.value,
42
+ name: c.name,
43
+ description: c.description,
44
+ })),
45
+ default: defaultValue,
46
+ });
47
+ }
48
+ /**
49
+ * Prompt for text input
50
+ */
51
+ export async function promptInput(message, defaultValue, options = {}) {
52
+ if (!shouldPrompt(options)) {
53
+ return defaultValue;
54
+ }
55
+ return input({
56
+ message,
57
+ default: defaultValue,
58
+ });
59
+ }
60
+ /**
61
+ * Prompt for confirmation (yes/no)
62
+ */
63
+ export async function promptConfirm(message, defaultValue, options = {}) {
64
+ if (!shouldPrompt(options)) {
65
+ return defaultValue;
66
+ }
67
+ return confirm({
68
+ message,
69
+ default: defaultValue,
70
+ });
71
+ }
72
+ /**
73
+ * Prompt for version input with validation
74
+ */
75
+ export async function promptVersion(message, defaultValue, options = {}) {
76
+ if (!shouldPrompt(options)) {
77
+ return defaultValue;
78
+ }
79
+ const result = await input({
80
+ message,
81
+ default: defaultValue,
82
+ validate: (value) => {
83
+ const semverRegex = /^\d+\.\d+\.\d+(-[a-zA-Z0-9.-]+)?(\+[a-zA-Z0-9.-]+)?$/;
84
+ if (semverRegex.test(value)) {
85
+ return true;
86
+ }
87
+ return 'Please enter a valid semver version (e.g., 0.0.0, 1.2.3-beta.1)';
88
+ },
89
+ });
90
+ return result;
91
+ }
92
+ /**
93
+ * Common version choices for init
94
+ */
95
+ export const VERSION_CHOICES = [
96
+ { value: '0.0.0', name: '0.0.0', description: 'Start fresh (recommended for new projects)' },
97
+ { value: '1.0.0', name: '1.0.0', description: 'Production-ready' },
98
+ { value: 'custom', name: 'Custom', description: 'Enter a custom version' },
99
+ ];
100
+ /**
101
+ * Common versioning mode choices
102
+ */
103
+ export const VERSIONING_MODE_CHOICES = [
104
+ {
105
+ value: 'independent',
106
+ name: 'Independent',
107
+ description: 'Each contract versioned separately',
108
+ },
109
+ { value: 'fixed', name: 'Fixed', description: 'All contracts share same version' },
110
+ ];
111
+ /**
112
+ * Contract type choices
113
+ */
114
+ export const CONTRACT_TYPE_CHOICES = [
115
+ { value: 'openapi', name: 'OpenAPI', description: 'OpenAPI/Swagger specification' },
116
+ { value: 'asyncapi', name: 'AsyncAPI', description: 'AsyncAPI specification' },
117
+ { value: 'json-schema', name: 'JSON Schema', description: 'JSON Schema definition' },
118
+ { value: 'odcs', name: 'ODCS', description: 'Open Data Contract Standard' },
119
+ ];
package/package.json ADDED
@@ -0,0 +1,81 @@
1
+ {
2
+ "name": "@contractual/cli",
3
+ "private": false,
4
+ "version": "0.1.0-dev.0",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "main": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "bin": {
10
+ "contractual": "./bin/cli.js"
11
+ },
12
+ "exports": {
13
+ ".": {
14
+ "types": "./dist/index.d.ts",
15
+ "import": "./dist/index.js"
16
+ }
17
+ },
18
+ "repository": {
19
+ "type": "git",
20
+ "url": "https://github.com/contractual-dev/contractual.git",
21
+ "directory": "packages/cli"
22
+ },
23
+ "homepage": "https://contractual.dev",
24
+ "bugs": {
25
+ "url": "https://github.com/contractual-dev/contractual/issues"
26
+ },
27
+ "contributors": [
28
+ {
29
+ "name": "Omer Morad",
30
+ "email": "omer.moradd@gmail.com"
31
+ }
32
+ ],
33
+ "funding": [
34
+ {
35
+ "type": "github",
36
+ "url": "https://github.com/sponsors/contractual-dev"
37
+ },
38
+ {
39
+ "type": "opencollective",
40
+ "url": "https://opencollective.com/contractual-dev"
41
+ }
42
+ ],
43
+ "engines": {
44
+ "node": ">=20.0.0"
45
+ },
46
+ "scripts": {
47
+ "prebuild": "pnpm rimraf dist",
48
+ "build": "tsc -p tsconfig.build.json",
49
+ "build:watch": "tsc -p tsconfig.build.json --watch",
50
+ "tester": "jest --coverage --verbose",
51
+ "lint": "eslint '{src,test}/**/*.ts'"
52
+ },
53
+ "files": [
54
+ "bin",
55
+ "dist",
56
+ "src/config/schema.json",
57
+ "README.md"
58
+ ],
59
+ "dependencies": {
60
+ "@contractual/changesets": "0.1.0-dev.0",
61
+ "@contractual/governance": "0.1.0-dev.0",
62
+ "@contractual/types": "0.1.0-dev.0",
63
+ "@inquirer/prompts": "^8.1.0",
64
+ "ajv": "^8.17.1",
65
+ "ajv-formats": "^3.0.1",
66
+ "chalk": "^5.4.1",
67
+ "commander": "^12.1.0",
68
+ "fast-glob": "^3.3.3",
69
+ "ora": "^8.1.1",
70
+ "yaml": "^2.7.0"
71
+ },
72
+ "publishConfig": {
73
+ "access": "public",
74
+ "provenance": true
75
+ },
76
+ "devDependencies": {
77
+ "@vitest/coverage-v8": "^3.0.0",
78
+ "vitest": "^3.0.3"
79
+ },
80
+ "gitHead": "a18dcec2397cf45cbd4ba86461d378ccd71ba657"
81
+ }