cadr-cli 0.0.1
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.
- package/bin/cadr.js +16 -0
- package/dist/git.d.ts +12 -0
- package/dist/git.d.ts.map +1 -0
- package/dist/git.js +47 -0
- package/dist/git.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +80 -0
- package/dist/index.js.map +1 -0
- package/dist/logger.d.ts +7 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +45 -0
- package/dist/logger.js.map +1 -0
- package/package.json +32 -0
- package/src/git.ts +60 -0
- package/src/index.ts +80 -0
- package/src/logger.ts +39 -0
- package/tsconfig.json +9 -0
package/bin/cadr.js
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// Executable wrapper for cadr-cli
|
|
4
|
+
// This file is the entry point when running 'cadr' command
|
|
5
|
+
|
|
6
|
+
const { displayWelcome, processStagedFiles } = require('../dist/index.js');
|
|
7
|
+
|
|
8
|
+
// Display welcome message first
|
|
9
|
+
displayWelcome();
|
|
10
|
+
|
|
11
|
+
// Then process staged files
|
|
12
|
+
processStagedFiles().catch((error) => {
|
|
13
|
+
console.error('Failed to process staged files:', error.message);
|
|
14
|
+
process.exit(1);
|
|
15
|
+
});
|
|
16
|
+
|
package/dist/git.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export declare class GitError extends Error {
|
|
2
|
+
readonly code: 'NOT_GIT_REPO' | 'GIT_NOT_FOUND' | 'GIT_ERROR';
|
|
3
|
+
readonly originalError?: Error | undefined;
|
|
4
|
+
constructor(message: string, code: 'NOT_GIT_REPO' | 'GIT_NOT_FOUND' | 'GIT_ERROR', originalError?: Error | undefined);
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Retrieves the list of staged files in the current Git repository
|
|
8
|
+
* @returns Promise<string[]> Array of staged file paths
|
|
9
|
+
* @throws GitError When Git is not available or repository is invalid
|
|
10
|
+
*/
|
|
11
|
+
export declare function getStagedFiles(): Promise<string[]>;
|
|
12
|
+
//# sourceMappingURL=git.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"git.d.ts","sourceRoot":"","sources":["../src/git.ts"],"names":[],"mappings":"AAKA,qBAAa,QAAS,SAAQ,KAAK;aAGf,IAAI,EAAE,cAAc,GAAG,eAAe,GAAG,WAAW;aACpD,aAAa,CAAC,EAAE,KAAK;gBAFrC,OAAO,EAAE,MAAM,EACC,IAAI,EAAE,cAAc,GAAG,eAAe,GAAG,WAAW,EACpD,aAAa,CAAC,EAAE,KAAK,YAAA;CAKxC;AAED;;;;GAIG;AACH,wBAAsB,cAAc,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC,CAsCxD"}
|
package/dist/git.js
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.GitError = void 0;
|
|
4
|
+
exports.getStagedFiles = getStagedFiles;
|
|
5
|
+
const child_process_1 = require("child_process");
|
|
6
|
+
const util_1 = require("util");
|
|
7
|
+
const execAsync = (0, util_1.promisify)(child_process_1.exec);
|
|
8
|
+
class GitError extends Error {
|
|
9
|
+
code;
|
|
10
|
+
originalError;
|
|
11
|
+
constructor(message, code, originalError) {
|
|
12
|
+
super(message);
|
|
13
|
+
this.code = code;
|
|
14
|
+
this.originalError = originalError;
|
|
15
|
+
this.name = 'GitError';
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
exports.GitError = GitError;
|
|
19
|
+
/**
|
|
20
|
+
* Retrieves the list of staged files in the current Git repository
|
|
21
|
+
* @returns Promise<string[]> Array of staged file paths
|
|
22
|
+
* @throws GitError When Git is not available or repository is invalid
|
|
23
|
+
*/
|
|
24
|
+
async function getStagedFiles() {
|
|
25
|
+
try {
|
|
26
|
+
const { stdout } = await execAsync('git diff --cached --name-only');
|
|
27
|
+
// Split by newlines and filter out empty strings
|
|
28
|
+
const stagedFiles = stdout
|
|
29
|
+
.split('\n')
|
|
30
|
+
.map(file => file.trim())
|
|
31
|
+
.filter(file => file.length > 0);
|
|
32
|
+
return stagedFiles;
|
|
33
|
+
}
|
|
34
|
+
catch (error) {
|
|
35
|
+
// Handle different Git error scenarios
|
|
36
|
+
const errorWithCode = error;
|
|
37
|
+
if (errorWithCode.code === 128) {
|
|
38
|
+
throw new GitError('Not in a Git repository. Please run \'cadr\' from within a Git repository.', 'NOT_GIT_REPO', error instanceof Error ? error : new Error(String(error)));
|
|
39
|
+
}
|
|
40
|
+
if (errorWithCode.code === 127) {
|
|
41
|
+
throw new GitError('Git is not installed. Please install Git and try again.', 'GIT_NOT_FOUND', error instanceof Error ? error : new Error(String(error)));
|
|
42
|
+
}
|
|
43
|
+
// Handle other Git errors (permissions, corruption, etc.)
|
|
44
|
+
throw new GitError('Unable to read Git repository. Please check repository permissions.', 'GIT_ERROR', error instanceof Error ? error : new Error(String(error)));
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=git.js.map
|
package/dist/git.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"git.js","sourceRoot":"","sources":["../src/git.ts"],"names":[],"mappings":";;;AAqBA,wCAsCC;AA3DD,iDAAqC;AACrC,+BAAiC;AAEjC,MAAM,SAAS,GAAG,IAAA,gBAAS,EAAC,oBAAI,CAAC,CAAC;AAElC,MAAa,QAAS,SAAQ,KAAK;IAGf;IACA;IAHlB,YACE,OAAe,EACC,IAAoD,EACpD,aAAqB;QAErC,KAAK,CAAC,OAAO,CAAC,CAAC;QAHC,SAAI,GAAJ,IAAI,CAAgD;QACpD,kBAAa,GAAb,aAAa,CAAQ;QAGrC,IAAI,CAAC,IAAI,GAAG,UAAU,CAAC;IACzB,CAAC;CACF;AATD,4BASC;AAED;;;;GAIG;AACI,KAAK,UAAU,cAAc;IAClC,IAAI,CAAC;QACH,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,SAAS,CAAC,+BAA+B,CAAC,CAAC;QAEpE,iDAAiD;QACjD,MAAM,WAAW,GAAG,MAAM;aACvB,KAAK,CAAC,IAAI,CAAC;aACX,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;aACxB,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;QAEnC,OAAO,WAAW,CAAC;IACrB,CAAC;IAAC,OAAO,KAAc,EAAE,CAAC;QACxB,uCAAuC;QACvC,MAAM,aAAa,GAAG,KAA0B,CAAC;QAEjD,IAAI,aAAa,CAAC,IAAI,KAAK,GAAG,EAAE,CAAC;YAC/B,MAAM,IAAI,QAAQ,CAChB,4EAA4E,EAC5E,cAAc,EACd,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAC1D,CAAC;QACJ,CAAC;QAED,IAAI,aAAa,CAAC,IAAI,KAAK,GAAG,EAAE,CAAC;YAC/B,MAAM,IAAI,QAAQ,CAChB,yDAAyD,EACzD,eAAe,EACf,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAC1D,CAAC;QACJ,CAAC;QAED,0DAA0D;QAC1D,MAAM,IAAI,QAAQ,CAChB,qEAAqE,EACrE,WAAW,EACX,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAC1D,CAAC;IACJ,CAAC;AACH,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAWA,wBAAgB,iBAAiB,IAAI,MAAM,CAY1C;AAED,wBAAgB,cAAc,IAAI,IAAI,CAGrC;AAED,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,IAAI,CAAC,CAmCxD"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getWelcomeMessage = getWelcomeMessage;
|
|
4
|
+
exports.displayWelcome = displayWelcome;
|
|
5
|
+
exports.processStagedFiles = processStagedFiles;
|
|
6
|
+
const git_1 = require("./git");
|
|
7
|
+
const logger_1 = require("./logger");
|
|
8
|
+
// Version constants
|
|
9
|
+
const CORE_VERSION = '0.0.1';
|
|
10
|
+
const CLI_VERSION = '0.0.1';
|
|
11
|
+
// Parse command line arguments
|
|
12
|
+
const args = process.argv.slice(2);
|
|
13
|
+
const isVerbose = args.includes('--verbose') || args.includes('-v');
|
|
14
|
+
function getWelcomeMessage() {
|
|
15
|
+
return `š Hello, cADR!
|
|
16
|
+
|
|
17
|
+
cADR (Continuous Architectural Decision Records) helps you automatically
|
|
18
|
+
capture and document architectural decisions as you code.
|
|
19
|
+
|
|
20
|
+
Version: ${CLI_VERSION}
|
|
21
|
+
Core: ${CORE_VERSION}
|
|
22
|
+
Learn more: https://github.com/YotpoLtd/cADR
|
|
23
|
+
|
|
24
|
+
Get started by running 'cadr --verbose' to see detailed logs
|
|
25
|
+
`;
|
|
26
|
+
}
|
|
27
|
+
function displayWelcome() {
|
|
28
|
+
// Use process.stdout.write instead of console.log (Constitution: no console.log)
|
|
29
|
+
process.stdout.write(getWelcomeMessage());
|
|
30
|
+
}
|
|
31
|
+
async function processStagedFiles() {
|
|
32
|
+
try {
|
|
33
|
+
const stagedFiles = await (0, git_1.getStagedFiles)();
|
|
34
|
+
// Display staged files to user
|
|
35
|
+
if (stagedFiles.length > 0) {
|
|
36
|
+
process.stdout.write(`\nš Found ${stagedFiles.length} staged file${stagedFiles.length === 1 ? '' : 's'}:\n`);
|
|
37
|
+
stagedFiles.forEach((file) => {
|
|
38
|
+
process.stdout.write(` ⢠${file}\n`);
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
process.stdout.write(`\nš No staged files found.\n`);
|
|
43
|
+
}
|
|
44
|
+
// Only log for debugging when verbose mode is enabled
|
|
45
|
+
if (isVerbose) {
|
|
46
|
+
logger_1.loggerInstance.info('Retrieved staged files', {
|
|
47
|
+
staged_files: stagedFiles,
|
|
48
|
+
count: stagedFiles.length
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
catch (error) {
|
|
53
|
+
if (error instanceof git_1.GitError) {
|
|
54
|
+
// Display helpful error message to stdout
|
|
55
|
+
process.stdout.write(`\nā ${error.message}\n`);
|
|
56
|
+
process.exit(1);
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
// Log unexpected errors (always show errors)
|
|
60
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
61
|
+
logger_1.loggerInstance.error('Unexpected error occurred', {
|
|
62
|
+
error: errorMessage
|
|
63
|
+
});
|
|
64
|
+
process.exit(1);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
// Main execution block - run when module is executed directly
|
|
69
|
+
if (require.main === module) {
|
|
70
|
+
// Display welcome message first
|
|
71
|
+
displayWelcome();
|
|
72
|
+
// Then process staged files
|
|
73
|
+
processStagedFiles().catch((error) => {
|
|
74
|
+
logger_1.loggerInstance.error('Failed to process staged files', {
|
|
75
|
+
error: error instanceof Error ? error.message : String(error)
|
|
76
|
+
});
|
|
77
|
+
process.exit(1);
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;AAWA,8CAYC;AAED,wCAGC;AAED,gDAmCC;AAjED,+BAAiD;AACjD,qCAA0C;AAE1C,oBAAoB;AACpB,MAAM,YAAY,GAAG,OAAO,CAAC;AAC7B,MAAM,WAAW,GAAG,OAAO,CAAC;AAE5B,+BAA+B;AAC/B,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AACnC,MAAM,SAAS,GAAG,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;AAEpE,SAAgB,iBAAiB;IAC/B,OAAO;;;;;WAKE,WAAW;QACd,YAAY;;;;CAInB,CAAC;AACF,CAAC;AAED,SAAgB,cAAc;IAC5B,iFAAiF;IACjF,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,iBAAiB,EAAE,CAAC,CAAC;AAC5C,CAAC;AAEM,KAAK,UAAU,kBAAkB;IACtC,IAAI,CAAC;QACH,MAAM,WAAW,GAAG,MAAM,IAAA,oBAAc,GAAE,CAAC;QAE3C,+BAA+B;QAC/B,IAAI,WAAW,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC3B,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,cAAc,WAAW,CAAC,MAAM,eAAe,WAAW,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC;YAC9G,WAAW,CAAC,OAAO,CAAC,CAAC,IAAY,EAAE,EAAE;gBACnC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,OAAO,IAAI,IAAI,CAAC,CAAC;YACxC,CAAC,CAAC,CAAC;QACL,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,+BAA+B,CAAC,CAAC;QACxD,CAAC;QAED,sDAAsD;QACtD,IAAI,SAAS,EAAE,CAAC;YACd,uBAAc,CAAC,IAAI,CAAC,wBAAwB,EAAE;gBAC5C,YAAY,EAAE,WAAW;gBACzB,KAAK,EAAE,WAAW,CAAC,MAAM;aAC1B,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAAC,OAAO,KAAc,EAAE,CAAC;QACxB,IAAI,KAAK,YAAY,cAAQ,EAAE,CAAC;YAC9B,0CAA0C;YAC1C,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,OAAO,KAAK,CAAC,OAAO,IAAI,CAAC,CAAC;YAC/C,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;aAAM,CAAC;YACN,6CAA6C;YAC7C,MAAM,YAAY,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YAC5E,uBAAc,CAAC,KAAK,CAAC,2BAA2B,EAAE;gBAChD,KAAK,EAAE,YAAY;aACpB,CAAC,CAAC;YACH,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;IACH,CAAC;AACH,CAAC;AAED,8DAA8D;AAC9D,IAAI,OAAO,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;IAC5B,gCAAgC;IAChC,cAAc,EAAE,CAAC;IAEjB,4BAA4B;IAC5B,kBAAkB,EAAE,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;QACnC,uBAAc,CAAC,KAAK,CAAC,gCAAgC,EAAE;YACrD,KAAK,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC;SAC9D,CAAC,CAAC;QACH,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC,CAAC,CAAC;AACL,CAAC"}
|
package/dist/logger.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"AAWA,qBAAa,MAAM;IACjB,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI;IAQ7C,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI;IAQ7C,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI;CAO/C;AAGD,eAAO,MAAM,cAAc,QAAe,CAAC"}
|
package/dist/logger.js
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.loggerInstance = exports.Logger = void 0;
|
|
7
|
+
const pino_1 = __importDefault(require("pino"));
|
|
8
|
+
// Configure Pino to output JSON to stderr
|
|
9
|
+
const logger = (0, pino_1.default)({
|
|
10
|
+
level: 'info',
|
|
11
|
+
transport: {
|
|
12
|
+
target: 'pino/file',
|
|
13
|
+
options: { destination: 2 } // stderr
|
|
14
|
+
}
|
|
15
|
+
});
|
|
16
|
+
class Logger {
|
|
17
|
+
info(message, context) {
|
|
18
|
+
if (context) {
|
|
19
|
+
logger.info(context, message);
|
|
20
|
+
}
|
|
21
|
+
else {
|
|
22
|
+
logger.info(message);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
warn(message, context) {
|
|
26
|
+
if (context) {
|
|
27
|
+
logger.warn(context, message);
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
logger.warn(message);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
error(message, context) {
|
|
34
|
+
if (context) {
|
|
35
|
+
logger.error(context, message);
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
logger.error(message);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
exports.Logger = Logger;
|
|
43
|
+
// Export singleton logger instance
|
|
44
|
+
exports.loggerInstance = new Logger();
|
|
45
|
+
//# sourceMappingURL=logger.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.js","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":";;;;;;AAAA,gDAAwB;AAExB,0CAA0C;AAC1C,MAAM,MAAM,GAAG,IAAA,cAAI,EAAC;IAClB,KAAK,EAAE,MAAM;IACb,SAAS,EAAE;QACT,MAAM,EAAE,WAAW;QACnB,OAAO,EAAE,EAAE,WAAW,EAAE,CAAC,EAAE,CAAC,SAAS;KACtC;CACF,CAAC,CAAC;AAEH,MAAa,MAAM;IACjB,IAAI,CAAC,OAAe,EAAE,OAAgB;QACpC,IAAI,OAAO,EAAE,CAAC;YACZ,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QAChC,CAAC;aAAM,CAAC;YACN,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACvB,CAAC;IACH,CAAC;IAED,IAAI,CAAC,OAAe,EAAE,OAAgB;QACpC,IAAI,OAAO,EAAE,CAAC;YACZ,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QAChC,CAAC;aAAM,CAAC;YACN,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACvB,CAAC;IACH,CAAC;IAED,KAAK,CAAC,OAAe,EAAE,OAAgB;QACrC,IAAI,OAAO,EAAE,CAAC;YACZ,MAAM,CAAC,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QACjC,CAAC;aAAM,CAAC;YACN,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QACxB,CAAC;IACH,CAAC;CACF;AAxBD,wBAwBC;AAED,mCAAmC;AACtB,QAAA,cAAc,GAAG,IAAI,MAAM,EAAE,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "cadr-cli",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "Continuous Architectural Decision Records - Automatically capture ADRs as you code",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"cadr": "./bin/cadr.js"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"build": "tsc",
|
|
11
|
+
"test": "jest"
|
|
12
|
+
},
|
|
13
|
+
"keywords": [
|
|
14
|
+
"adr",
|
|
15
|
+
"architecture",
|
|
16
|
+
"cli",
|
|
17
|
+
"decision-records",
|
|
18
|
+
"documentation"
|
|
19
|
+
],
|
|
20
|
+
"author": "",
|
|
21
|
+
"license": "MIT",
|
|
22
|
+
"repository": {
|
|
23
|
+
"type": "git",
|
|
24
|
+
"url": "https://github.com/YotpoLtd/cADR.git"
|
|
25
|
+
},
|
|
26
|
+
"engines": {
|
|
27
|
+
"node": ">=20.0.0"
|
|
28
|
+
},
|
|
29
|
+
"dependencies": {
|
|
30
|
+
"pino": "^8.17.2"
|
|
31
|
+
}
|
|
32
|
+
}
|
package/src/git.ts
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { exec } from 'child_process';
|
|
2
|
+
import { promisify } from 'util';
|
|
3
|
+
|
|
4
|
+
const execAsync = promisify(exec);
|
|
5
|
+
|
|
6
|
+
export class GitError extends Error {
|
|
7
|
+
constructor(
|
|
8
|
+
message: string,
|
|
9
|
+
public readonly code: 'NOT_GIT_REPO' | 'GIT_NOT_FOUND' | 'GIT_ERROR',
|
|
10
|
+
public readonly originalError?: Error
|
|
11
|
+
) {
|
|
12
|
+
super(message);
|
|
13
|
+
this.name = 'GitError';
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Retrieves the list of staged files in the current Git repository
|
|
19
|
+
* @returns Promise<string[]> Array of staged file paths
|
|
20
|
+
* @throws GitError When Git is not available or repository is invalid
|
|
21
|
+
*/
|
|
22
|
+
export async function getStagedFiles(): Promise<string[]> {
|
|
23
|
+
try {
|
|
24
|
+
const { stdout } = await execAsync('git diff --cached --name-only');
|
|
25
|
+
|
|
26
|
+
// Split by newlines and filter out empty strings
|
|
27
|
+
const stagedFiles = stdout
|
|
28
|
+
.split('\n')
|
|
29
|
+
.map(file => file.trim())
|
|
30
|
+
.filter(file => file.length > 0);
|
|
31
|
+
|
|
32
|
+
return stagedFiles;
|
|
33
|
+
} catch (error: unknown) {
|
|
34
|
+
// Handle different Git error scenarios
|
|
35
|
+
const errorWithCode = error as { code?: number };
|
|
36
|
+
|
|
37
|
+
if (errorWithCode.code === 128) {
|
|
38
|
+
throw new GitError(
|
|
39
|
+
'Not in a Git repository. Please run \'cadr\' from within a Git repository.',
|
|
40
|
+
'NOT_GIT_REPO',
|
|
41
|
+
error instanceof Error ? error : new Error(String(error))
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (errorWithCode.code === 127) {
|
|
46
|
+
throw new GitError(
|
|
47
|
+
'Git is not installed. Please install Git and try again.',
|
|
48
|
+
'GIT_NOT_FOUND',
|
|
49
|
+
error instanceof Error ? error : new Error(String(error))
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Handle other Git errors (permissions, corruption, etc.)
|
|
54
|
+
throw new GitError(
|
|
55
|
+
'Unable to read Git repository. Please check repository permissions.',
|
|
56
|
+
'GIT_ERROR',
|
|
57
|
+
error instanceof Error ? error : new Error(String(error))
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { getStagedFiles, GitError } from './git';
|
|
2
|
+
import { loggerInstance } from './logger';
|
|
3
|
+
|
|
4
|
+
// Version constants
|
|
5
|
+
const CORE_VERSION = '0.0.1';
|
|
6
|
+
const CLI_VERSION = '0.0.1';
|
|
7
|
+
|
|
8
|
+
// Parse command line arguments
|
|
9
|
+
const args = process.argv.slice(2);
|
|
10
|
+
const isVerbose = args.includes('--verbose') || args.includes('-v');
|
|
11
|
+
|
|
12
|
+
export function getWelcomeMessage(): string {
|
|
13
|
+
return `š Hello, cADR!
|
|
14
|
+
|
|
15
|
+
cADR (Continuous Architectural Decision Records) helps you automatically
|
|
16
|
+
capture and document architectural decisions as you code.
|
|
17
|
+
|
|
18
|
+
Version: ${CLI_VERSION}
|
|
19
|
+
Core: ${CORE_VERSION}
|
|
20
|
+
Learn more: https://github.com/YotpoLtd/cADR
|
|
21
|
+
|
|
22
|
+
Get started by running 'cadr --verbose' to see detailed logs
|
|
23
|
+
`;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function displayWelcome(): void {
|
|
27
|
+
// Use process.stdout.write instead of console.log (Constitution: no console.log)
|
|
28
|
+
process.stdout.write(getWelcomeMessage());
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export async function processStagedFiles(): Promise<void> {
|
|
32
|
+
try {
|
|
33
|
+
const stagedFiles = await getStagedFiles();
|
|
34
|
+
|
|
35
|
+
// Display staged files to user
|
|
36
|
+
if (stagedFiles.length > 0) {
|
|
37
|
+
process.stdout.write(`\nš Found ${stagedFiles.length} staged file${stagedFiles.length === 1 ? '' : 's'}:\n`);
|
|
38
|
+
stagedFiles.forEach((file: string) => {
|
|
39
|
+
process.stdout.write(` ⢠${file}\n`);
|
|
40
|
+
});
|
|
41
|
+
} else {
|
|
42
|
+
process.stdout.write(`\nš No staged files found.\n`);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Only log for debugging when verbose mode is enabled
|
|
46
|
+
if (isVerbose) {
|
|
47
|
+
loggerInstance.info('Retrieved staged files', {
|
|
48
|
+
staged_files: stagedFiles,
|
|
49
|
+
count: stagedFiles.length
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
} catch (error: unknown) {
|
|
53
|
+
if (error instanceof GitError) {
|
|
54
|
+
// Display helpful error message to stdout
|
|
55
|
+
process.stdout.write(`\nā ${error.message}\n`);
|
|
56
|
+
process.exit(1);
|
|
57
|
+
} else {
|
|
58
|
+
// Log unexpected errors (always show errors)
|
|
59
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
60
|
+
loggerInstance.error('Unexpected error occurred', {
|
|
61
|
+
error: errorMessage
|
|
62
|
+
});
|
|
63
|
+
process.exit(1);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Main execution block - run when module is executed directly
|
|
69
|
+
if (require.main === module) {
|
|
70
|
+
// Display welcome message first
|
|
71
|
+
displayWelcome();
|
|
72
|
+
|
|
73
|
+
// Then process staged files
|
|
74
|
+
processStagedFiles().catch((error) => {
|
|
75
|
+
loggerInstance.error('Failed to process staged files', {
|
|
76
|
+
error: error instanceof Error ? error.message : String(error)
|
|
77
|
+
});
|
|
78
|
+
process.exit(1);
|
|
79
|
+
});
|
|
80
|
+
}
|
package/src/logger.ts
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import pino from 'pino';
|
|
2
|
+
|
|
3
|
+
// Configure Pino to output JSON to stderr
|
|
4
|
+
const logger = pino({
|
|
5
|
+
level: 'info',
|
|
6
|
+
transport: {
|
|
7
|
+
target: 'pino/file',
|
|
8
|
+
options: { destination: 2 } // stderr
|
|
9
|
+
}
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
export class Logger {
|
|
13
|
+
info(message: string, context?: object): void {
|
|
14
|
+
if (context) {
|
|
15
|
+
logger.info(context, message);
|
|
16
|
+
} else {
|
|
17
|
+
logger.info(message);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
warn(message: string, context?: object): void {
|
|
22
|
+
if (context) {
|
|
23
|
+
logger.warn(context, message);
|
|
24
|
+
} else {
|
|
25
|
+
logger.warn(message);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
error(message: string, context?: object): void {
|
|
30
|
+
if (context) {
|
|
31
|
+
logger.error(context, message);
|
|
32
|
+
} else {
|
|
33
|
+
logger.error(message);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Export singleton logger instance
|
|
39
|
+
export const loggerInstance = new Logger();
|