azcodr 2.0.0 → 2.1.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 (71) hide show
  1. package/.agents/hooks.json +3 -3
  2. package/.github/workflows/publish.yml +0 -4
  3. package/bin/azcodr.js +9 -4
  4. package/lib/cli-parse.d.ts +32 -0
  5. package/lib/cli-parse.d.ts.map +1 -0
  6. package/lib/cli-parse.js +45 -41
  7. package/lib/cli-parse.js.map +1 -0
  8. package/lib/cli-target.d.ts +66 -0
  9. package/lib/cli-target.d.ts.map +1 -0
  10. package/lib/cli-target.js +89 -96
  11. package/lib/cli-target.js.map +1 -0
  12. package/lib/cli.d.ts +40 -0
  13. package/lib/cli.d.ts.map +1 -0
  14. package/lib/cli.js +122 -136
  15. package/lib/cli.js.map +1 -0
  16. package/lib/errors.d.ts +39 -0
  17. package/lib/errors.d.ts.map +1 -0
  18. package/lib/errors.js +16 -18
  19. package/lib/errors.js.map +1 -0
  20. package/lib/git.d.ts +15 -0
  21. package/lib/git.d.ts.map +1 -0
  22. package/lib/git.js +25 -22
  23. package/lib/git.js.map +1 -0
  24. package/lib/guards.d.ts +35 -0
  25. package/lib/guards.d.ts.map +1 -0
  26. package/lib/guards.js +62 -63
  27. package/lib/guards.js.map +1 -0
  28. package/lib/index.d.ts +6 -199
  29. package/lib/index.d.ts.map +1 -0
  30. package/lib/index.js +4 -5
  31. package/lib/index.js.map +1 -0
  32. package/lib/links.d.ts +28 -0
  33. package/lib/links.d.ts.map +1 -0
  34. package/lib/links.js +105 -99
  35. package/lib/links.js.map +1 -0
  36. package/lib/permissions.d.ts +9 -0
  37. package/lib/permissions.d.ts.map +1 -0
  38. package/lib/permissions.js +36 -36
  39. package/lib/permissions.js.map +1 -0
  40. package/lib/repo.d.ts +20 -0
  41. package/lib/repo.d.ts.map +1 -0
  42. package/lib/repo.js +73 -71
  43. package/lib/repo.js.map +1 -0
  44. package/lib/scaffold.d.ts +80 -0
  45. package/lib/scaffold.d.ts.map +1 -0
  46. package/lib/scaffold.js +159 -196
  47. package/lib/scaffold.js.map +1 -0
  48. package/memory.md +16 -0
  49. package/package.json +13 -3
  50. package/scripts/test_coverage.js +2 -2
  51. package/scripts/validate/adr.js +11 -7
  52. package/scripts/validate/io.js +7 -9
  53. package/scripts/validate/links.js +6 -7
  54. package/scripts/validate/parity.js +5 -7
  55. package/scripts/validate/root.js +6 -7
  56. package/scripts/validate/rules.js +7 -9
  57. package/scripts/validate/skills.js +8 -10
  58. package/scripts/validate/text.js +3 -5
  59. package/scripts/validate-cli.js +1 -2
  60. package/scripts/validate.js +37 -19
  61. package/src/cli-parse.ts +77 -0
  62. package/src/cli-target.ts +167 -0
  63. package/src/cli.ts +240 -0
  64. package/src/errors.ts +35 -0
  65. package/src/git.ts +34 -0
  66. package/src/guards.ts +101 -0
  67. package/src/index.ts +39 -0
  68. package/src/links.ts +139 -0
  69. package/src/permissions.ts +42 -0
  70. package/src/repo.ts +94 -0
  71. package/src/scaffold.ts +273 -0
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "safety-guard": {
3
- "enabled": true,
3
+ "enabled": false,
4
4
  "PreToolUse": [
5
5
  {
6
6
  "matcher": "run_command",
@@ -15,7 +15,7 @@
15
15
  ]
16
16
  },
17
17
  "post-tool-lint": {
18
- "enabled": true,
18
+ "enabled": false,
19
19
  "PostToolUse": [
20
20
  {
21
21
  "matcher": "run_command",
@@ -30,7 +30,7 @@
30
30
  ]
31
31
  },
32
32
  "stop-verifier": {
33
- "enabled": true,
33
+ "enabled": false,
34
34
  "Stop": [
35
35
  {
36
36
  "type": "command",
@@ -181,10 +181,6 @@ jobs:
181
181
  publish-jsr:
182
182
  name: Publish to JSR
183
183
  needs: verify
184
- # Requires a native ESM entrypoint. The npm package is CommonJS, which JSR
185
- # explicitly forbids ("You cannot publish CommonJS modules"). Disabled with
186
- # a stated reason rather than failing every release.
187
- if: false
188
184
  runs-on: ubuntu-24.04
189
185
  timeout-minutes: 15
190
186
  steps:
package/bin/azcodr.js CHANGED
@@ -1,14 +1,19 @@
1
1
  #!/usr/bin/env node
2
- 'use strict';
3
2
 
4
3
  /**
5
4
  * CLI entry point. All logic lives in lib/cli.js so it is unit-testable;
6
5
  * this file only wires the process boundary (argv in, exit code out).
7
6
  */
8
- const cli = require('../lib/cli.js');
7
+ import fs from 'node:fs';
8
+ import path from 'node:path';
9
+ import process from 'node:process';
10
+ import * as cli from '../lib/cli.js';
9
11
 
10
- if (require.main === module) {
12
+ const isDirectRun = process.argv[1] ? fs.realpathSync(path.resolve(process.argv[1])).toLowerCase() === fs.realpathSync(import.meta.filename).toLowerCase() : false;
13
+
14
+ if (isDirectRun) {
11
15
  cli.main();
12
16
  }
13
17
 
14
- module.exports = cli;
18
+ export * from '../lib/cli.js';
19
+ export default cli;
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Argument parsing for the azcodr CLI.
3
+ *
4
+ * Table-driven: every boolean flag is one Map entry, so adding a flag cannot
5
+ * raise the complexity of the parser itself.
6
+ */
7
+ export interface CliParsedOptions {
8
+ targetDir: string | null;
9
+ force: boolean;
10
+ noGit: boolean;
11
+ dryRun: boolean;
12
+ silent: boolean;
13
+ terminal: 'help' | 'version' | 'unknown-flag' | 'extra-arg' | null;
14
+ message?: string;
15
+ }
16
+ export type BooleanFlagKey = 'force' | 'noGit' | 'dryRun' | 'silent';
17
+ export declare function emptyOptions(): Omit<CliParsedOptions, 'terminal' | 'message'>;
18
+ export declare const BOOLEAN_FLAGS: Map<string, BooleanFlagKey>;
19
+ export interface ArgOutcome {
20
+ terminal: 'help' | 'version' | 'unknown-flag' | 'extra-arg' | null;
21
+ message?: string;
22
+ }
23
+ export declare function applyArg(state: Omit<CliParsedOptions, 'terminal' | 'message'>, arg: string): ArgOutcome;
24
+ export declare function parseArgs(rawArgs: readonly string[]): CliParsedOptions;
25
+ declare const _default: {
26
+ emptyOptions: typeof emptyOptions;
27
+ BOOLEAN_FLAGS: Map<string, BooleanFlagKey>;
28
+ applyArg: typeof applyArg;
29
+ parseArgs: typeof parseArgs;
30
+ };
31
+ export default _default;
32
+ //# sourceMappingURL=cli-parse.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli-parse.d.ts","sourceRoot":"","sources":["../src/cli-parse.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,WAAW,gBAAgB;IAC/B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,KAAK,EAAE,OAAO,CAAC;IACf,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,EAAE,OAAO,CAAC;IAChB,MAAM,EAAE,OAAO,CAAC;IAChB,QAAQ,EAAE,MAAM,GAAG,SAAS,GAAG,cAAc,GAAG,WAAW,GAAG,IAAI,CAAC;IACnE,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,MAAM,cAAc,GAAG,OAAO,GAAG,OAAO,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAErE,wBAAgB,YAAY,IAAI,IAAI,CAAC,gBAAgB,EAAE,UAAU,GAAG,SAAS,CAAC,CAE7E;AAED,eAAO,MAAM,aAAa,6BAQxB,CAAC;AAEH,MAAM,WAAW,UAAU;IACzB,QAAQ,EAAE,MAAM,GAAG,SAAS,GAAG,cAAc,GAAG,WAAW,GAAG,IAAI,CAAC;IACnE,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,wBAAgB,QAAQ,CACtB,KAAK,EAAE,IAAI,CAAC,gBAAgB,EAAE,UAAU,GAAG,SAAS,CAAC,EACrD,GAAG,EAAE,MAAM,GACV,UAAU,CAsBZ;AAED,wBAAgB,SAAS,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,GAAG,gBAAgB,CAStE;;;;;;;AAED,wBAAoE"}
package/lib/cli-parse.js CHANGED
@@ -1,51 +1,55 @@
1
- 'use strict';
2
-
3
1
  /**
4
2
  * Argument parsing for the azcodr CLI.
5
3
  *
6
4
  * Table-driven: every boolean flag is one Map entry, so adding a flag cannot
7
5
  * raise the complexity of the parser itself.
8
6
  */
9
-
10
- function emptyOptions() {
11
- return { targetDir: null, force: false, noGit: false, dryRun: false, silent: false };
7
+ export function emptyOptions() {
8
+ return { targetDir: null, force: false, noGit: false, dryRun: false, silent: false };
12
9
  }
13
-
14
- const BOOLEAN_FLAGS = new Map([
15
- ['-f', 'force'],
16
- ['--force', 'force'],
17
- ['--no-git', 'noGit'],
18
- ['-d', 'dryRun'],
19
- ['--dry-run', 'dryRun'],
20
- ['-s', 'silent'],
21
- ['--silent', 'silent']
10
+ export const BOOLEAN_FLAGS = new Map([
11
+ ['-f', 'force'],
12
+ ['--force', 'force'],
13
+ ['--no-git', 'noGit'],
14
+ ['-d', 'dryRun'],
15
+ ['--dry-run', 'dryRun'],
16
+ ['-s', 'silent'],
17
+ ['--silent', 'silent']
22
18
  ]);
23
-
24
- function applyArg(state, arg) {
25
- if (arg === '-h' || arg === '--help') return { terminal: 'help' };
26
- if (arg === '-v' || arg === '--version') return { terminal: 'version' };
27
- const flag = BOOLEAN_FLAGS.get(arg);
28
- if (flag !== undefined) {
29
- state[flag] = true;
30
- return { terminal: null };
31
- }
32
- if (arg.startsWith('-')) {
33
- return { terminal: 'unknown-flag', message: `Unknown argument '${arg}'. Run 'npx azcodr --help' for available options.` };
34
- }
35
- if (!state.targetDir) {
36
- state.targetDir = arg;
37
- return { terminal: null };
38
- }
39
- return { terminal: 'extra-arg', message: `Unexpected argument '${arg}'. Run 'npx azcodr --help' for available options.` };
19
+ export function applyArg(state, arg) {
20
+ if (arg === '-h' || arg === '--help')
21
+ return { terminal: 'help' };
22
+ if (arg === '-v' || arg === '--version')
23
+ return { terminal: 'version' };
24
+ const flag = BOOLEAN_FLAGS.get(arg);
25
+ if (flag !== undefined) {
26
+ state[flag] = true;
27
+ return { terminal: null };
28
+ }
29
+ if (arg.startsWith('-')) {
30
+ return {
31
+ terminal: 'unknown-flag',
32
+ message: `Unknown argument '${arg}'. Run 'npx azcodr --help' for available options.`
33
+ };
34
+ }
35
+ if (!state.targetDir) {
36
+ state.targetDir = arg;
37
+ return { terminal: null };
38
+ }
39
+ return {
40
+ terminal: 'extra-arg',
41
+ message: `Unexpected argument '${arg}'. Run 'npx azcodr --help' for available options.`
42
+ };
40
43
  }
41
-
42
- function parseArgs(rawArgs) {
43
- const state = emptyOptions();
44
- for (const arg of rawArgs) {
45
- const outcome = applyArg(state, arg);
46
- if (outcome.terminal !== null) return { ...state, terminal: outcome.terminal, message: outcome.message };
47
- }
48
- return { ...state, terminal: null, message: undefined };
44
+ export function parseArgs(rawArgs) {
45
+ const state = emptyOptions();
46
+ for (const arg of rawArgs) {
47
+ const outcome = applyArg(state, arg);
48
+ if (outcome.terminal !== null) {
49
+ return { ...state, terminal: outcome.terminal, message: outcome.message };
50
+ }
51
+ }
52
+ return { ...state, terminal: null, message: undefined };
49
53
  }
50
-
51
- module.exports = { emptyOptions, BOOLEAN_FLAGS, applyArg, parseArgs };
54
+ export default { emptyOptions, BOOLEAN_FLAGS, applyArg, parseArgs };
55
+ //# sourceMappingURL=cli-parse.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli-parse.js","sourceRoot":"","sources":["../src/cli-parse.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAcH,MAAM,UAAU,YAAY;IAC1B,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC;AACvF,CAAC;AAED,MAAM,CAAC,MAAM,aAAa,GAAG,IAAI,GAAG,CAAyB;IAC3D,CAAC,IAAI,EAAE,OAAO,CAAC;IACf,CAAC,SAAS,EAAE,OAAO,CAAC;IACpB,CAAC,UAAU,EAAE,OAAO,CAAC;IACrB,CAAC,IAAI,EAAE,QAAQ,CAAC;IAChB,CAAC,WAAW,EAAE,QAAQ,CAAC;IACvB,CAAC,IAAI,EAAE,QAAQ,CAAC;IAChB,CAAC,UAAU,EAAE,QAAQ,CAAC;CACvB,CAAC,CAAC;AAOH,MAAM,UAAU,QAAQ,CACtB,KAAqD,EACrD,GAAW;IAEX,IAAI,GAAG,KAAK,IAAI,IAAI,GAAG,KAAK,QAAQ;QAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC;IAClE,IAAI,GAAG,KAAK,IAAI,IAAI,GAAG,KAAK,WAAW;QAAE,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC;IACxE,MAAM,IAAI,GAAG,aAAa,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IACpC,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;QACvB,KAAK,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;QACnB,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IAC5B,CAAC;IACD,IAAI,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QACxB,OAAO;YACL,QAAQ,EAAE,cAAc;YACxB,OAAO,EAAE,qBAAqB,GAAG,mDAAmD;SACrF,CAAC;IACJ,CAAC;IACD,IAAI,CAAC,KAAK,CAAC,SAAS,EAAE,CAAC;QACrB,KAAK,CAAC,SAAS,GAAG,GAAG,CAAC;QACtB,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IAC5B,CAAC;IACD,OAAO;QACL,QAAQ,EAAE,WAAW;QACrB,OAAO,EAAE,wBAAwB,GAAG,mDAAmD;KACxF,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,OAA0B;IAClD,MAAM,KAAK,GAAG,YAAY,EAAE,CAAC;IAC7B,KAAK,MAAM,GAAG,IAAI,OAAO,EAAE,CAAC;QAC1B,MAAM,OAAO,GAAG,QAAQ,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;QACrC,IAAI,OAAO,CAAC,QAAQ,KAAK,IAAI,EAAE,CAAC;YAC9B,OAAO,EAAE,GAAG,KAAK,EAAE,QAAQ,EAAE,OAAO,CAAC,QAAQ,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,CAAC;QAC5E,CAAC;IACH,CAAC;IACD,OAAO,EAAE,GAAG,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC;AAC1D,CAAC;AAED,eAAe,EAAE,YAAY,EAAE,aAAa,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC"}
@@ -0,0 +1,66 @@
1
+ export interface AskQuestionOptions {
2
+ input?: NodeJS.ReadableStream;
3
+ output?: NodeJS.WritableStream;
4
+ }
5
+ export declare function askQuestion(query: string, { input, output }?: AskQuestionOptions): Promise<string>;
6
+ export interface PromptIo {
7
+ stdin: NodeJS.ReadStream | (NodeJS.ReadableStream & {
8
+ isTTY?: boolean;
9
+ });
10
+ stdout: NodeJS.WriteStream | NodeJS.WritableStream;
11
+ }
12
+ export declare function promptForTargetDir(io: PromptIo): Promise<string>;
13
+ export declare function rejectBadTarget(resolvedTarget: string, templateDir: string): string | null;
14
+ export interface ResolveTargetIo extends PromptIo {
15
+ cwd: string;
16
+ templateDir: string;
17
+ }
18
+ export interface ResolveTargetResult {
19
+ resolvedTarget: string;
20
+ chosen: string;
21
+ error: string | null;
22
+ }
23
+ export declare function resolveTargetDir(targetDir: string | null, io: ResolveTargetIo): Promise<ResolveTargetResult>;
24
+ export type TargetDirectoryStatus = 'missing' | 'not-a-directory' | 'empty' | 'non-empty';
25
+ export interface TargetStatusResult {
26
+ status: TargetDirectoryStatus;
27
+ count: number;
28
+ }
29
+ export declare function targetStatus(resolvedTarget: string): TargetStatusResult;
30
+ export interface ConfirmOverwriteIo extends PromptIo {
31
+ out: (msg: string) => void;
32
+ }
33
+ export interface ConfirmOverwriteResult {
34
+ confirmed: boolean;
35
+ aborted: boolean;
36
+ }
37
+ export declare function confirmOverwrite(targetDir: string, count: number, io: ConfirmOverwriteIo): Promise<ConfirmOverwriteResult>;
38
+ export interface EnsureWritableOptions {
39
+ chosen: string;
40
+ resolvedTarget: string;
41
+ force: boolean;
42
+ io: ConfirmOverwriteIo & {
43
+ err: (msg: string) => void;
44
+ };
45
+ }
46
+ export interface EnsureWritableResult {
47
+ force?: boolean;
48
+ exitCode?: number;
49
+ message?: string | null;
50
+ }
51
+ /**
52
+ * Ensures the target may be written. Returns { force } on success or
53
+ * { exitCode, message? } when the CLI must stop before scaffolding.
54
+ */
55
+ export declare function ensureWritableTarget(options: EnsureWritableOptions): Promise<EnsureWritableResult>;
56
+ declare const _default: {
57
+ askQuestion: typeof askQuestion;
58
+ promptForTargetDir: typeof promptForTargetDir;
59
+ rejectBadTarget: typeof rejectBadTarget;
60
+ resolveTargetDir: typeof resolveTargetDir;
61
+ targetStatus: typeof targetStatus;
62
+ confirmOverwrite: typeof confirmOverwrite;
63
+ ensureWritableTarget: typeof ensureWritableTarget;
64
+ };
65
+ export default _default;
66
+ //# sourceMappingURL=cli-target.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli-target.d.ts","sourceRoot":"","sources":["../src/cli-target.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,kBAAkB;IACjC,KAAK,CAAC,EAAE,MAAM,CAAC,cAAc,CAAC;IAC9B,MAAM,CAAC,EAAE,MAAM,CAAC,cAAc,CAAC;CAChC;AAED,wBAAgB,WAAW,CACzB,KAAK,EAAE,MAAM,EACb,EAAE,KAAqB,EAAE,MAAuB,EAAE,GAAE,kBAAuB,GAC1E,OAAO,CAAC,MAAM,CAAC,CAmBjB;AAED,MAAM,WAAW,QAAQ;IACvB,KAAK,EAAE,MAAM,CAAC,UAAU,GAAG,CAAC,MAAM,CAAC,cAAc,GAAG;QAAE,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;IACzE,MAAM,EAAE,MAAM,CAAC,WAAW,GAAG,MAAM,CAAC,cAAc,CAAC;CACpD;AAED,wBAAsB,kBAAkB,CAAC,EAAE,EAAE,QAAQ,GAAG,OAAO,CAAC,MAAM,CAAC,CAQtE;AAED,wBAAgB,eAAe,CAAC,cAAc,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAQ1F;AAED,MAAM,WAAW,eAAgB,SAAQ,QAAQ;IAC/C,GAAG,EAAE,MAAM,CAAC;IACZ,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,mBAAmB;IAClC,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACtB;AAED,wBAAsB,gBAAgB,CACpC,SAAS,EAAE,MAAM,GAAG,IAAI,EACxB,EAAE,EAAE,eAAe,GAClB,OAAO,CAAC,mBAAmB,CAAC,CAO9B;AAED,MAAM,MAAM,qBAAqB,GAAG,SAAS,GAAG,iBAAiB,GAAG,OAAO,GAAG,WAAW,CAAC;AAE1F,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,qBAAqB,CAAC;IAC9B,KAAK,EAAE,MAAM,CAAC;CACf;AAED,wBAAgB,YAAY,CAAC,cAAc,EAAE,MAAM,GAAG,kBAAkB,CAKvE;AAED,MAAM,WAAW,kBAAmB,SAAQ,QAAQ;IAClD,GAAG,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;CAC5B;AAED,MAAM,WAAW,sBAAsB;IACrC,SAAS,EAAE,OAAO,CAAC;IACnB,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,wBAAsB,gBAAgB,CACpC,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,MAAM,EACb,EAAE,EAAE,kBAAkB,GACrB,OAAO,CAAC,sBAAsB,CAAC,CAYjC;AAED,MAAM,WAAW,qBAAqB;IACpC,MAAM,EAAE,MAAM,CAAC;IACf,cAAc,EAAE,MAAM,CAAC;IACvB,KAAK,EAAE,OAAO,CAAC;IACf,EAAE,EAAE,kBAAkB,GAAG;QAAE,GAAG,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAA;KAAE,CAAC;CACzD;AAED,MAAM,WAAW,oBAAoB;IACnC,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB;AAED;;;GAGG;AACH,wBAAsB,oBAAoB,CACxC,OAAO,EAAE,qBAAqB,GAC7B,OAAO,CAAC,oBAAoB,CAAC,CAa/B;;;;;;;;;;AAED,wBAQE"}
package/lib/cli-target.js CHANGED
@@ -1,109 +1,102 @@
1
- 'use strict';
2
-
3
- const path = require('node:path');
4
- const readline = require('node:readline');
5
- const fs = require('node:fs');
6
- const { isProtectedTarget } = require('./guards.js');
7
-
8
- function askQuestion(query, { input = process.stdin, output = process.stdout } = {}) {
9
- const rl = readline.createInterface({ input, output });
10
-
11
- return new Promise((resolve) => {
12
- let resolved = false;
13
- rl.question(query, (answer) => {
14
- if (!resolved) {
15
- resolved = true;
16
- rl.close();
17
- resolve(answer.trim());
18
- }
1
+ import path from 'node:path';
2
+ import readline from 'node:readline';
3
+ import fs from 'node:fs';
4
+ import process from 'node:process';
5
+ import { isProtectedTarget } from './guards.js';
6
+ export function askQuestion(query, { input = process.stdin, output = process.stdout } = {}) {
7
+ const rl = readline.createInterface({ input, output });
8
+ return new Promise((resolve) => {
9
+ let resolved = false;
10
+ rl.question(query, (answer) => {
11
+ if (!resolved) {
12
+ resolved = true;
13
+ rl.close();
14
+ resolve(answer.trim());
15
+ }
16
+ });
17
+ rl.on('close', () => {
18
+ if (!resolved) {
19
+ resolved = true;
20
+ resolve('');
21
+ }
22
+ });
19
23
  });
20
- rl.on('close', () => {
21
- if (!resolved) {
22
- resolved = true;
23
- resolve('');
24
- }
25
- });
26
- });
27
24
  }
28
-
29
- async function promptForTargetDir(io) {
30
- const { stdin, stdout } = io;
31
- if (!stdin.isTTY) return '.';
32
- const answer = await askQuestion('? Where would you like to initialize your project? (./) ', {
33
- input: stdin,
34
- output: stdout
35
- });
36
- return answer || '.';
25
+ export async function promptForTargetDir(io) {
26
+ const { stdin, stdout } = io;
27
+ if (!stdin.isTTY)
28
+ return '.';
29
+ const answer = await askQuestion('? Where would you like to initialize your project? (./) ', {
30
+ input: stdin,
31
+ output: stdout
32
+ });
33
+ return answer || '.';
37
34
  }
38
-
39
- function rejectBadTarget(resolvedTarget, templateDir) {
40
- if (resolvedTarget === templateDir) {
41
- return `Cannot scaffold into the template directory itself: ${resolvedTarget}`;
42
- }
43
- // Fail fast on protected locations BEFORE the non-empty prompt. The prompt
44
- // asks "Continue? (y/N)" for ordinary project directories; answering "y"
45
- // for ~ or / must not be possible. The library enforces this again in
46
- // validateTarget, so this is defence in depth, not the sole check.
47
- if (isProtectedTarget(resolvedTarget, { templateDir })) {
48
- return `Refusing to scaffold into protected directory: ${resolvedTarget}. Choose a project subdirectory instead.`;
49
- }
50
- return null;
35
+ export function rejectBadTarget(resolvedTarget, templateDir) {
36
+ if (resolvedTarget === templateDir) {
37
+ return `Cannot scaffold into the template directory itself: ${resolvedTarget}`;
38
+ }
39
+ if (isProtectedTarget(resolvedTarget, { templateDir })) {
40
+ return `Refusing to scaffold into protected directory: ${resolvedTarget}. Choose a project subdirectory instead.`;
41
+ }
42
+ return null;
51
43
  }
52
-
53
- async function resolveTargetDir(targetDir, io) {
54
- const { cwd, templateDir } = io;
55
- const chosen = targetDir || await promptForTargetDir(io);
56
- const resolvedTarget = path.resolve(cwd, chosen);
57
- const rejection = rejectBadTarget(resolvedTarget, templateDir);
58
- if (rejection !== null) return { resolvedTarget, chosen, error: rejection };
59
- return { resolvedTarget, chosen, error: null };
44
+ export async function resolveTargetDir(targetDir, io) {
45
+ const { cwd, templateDir } = io;
46
+ const chosen = targetDir || (await promptForTargetDir(io));
47
+ const resolvedTarget = path.resolve(cwd, chosen);
48
+ const rejection = rejectBadTarget(resolvedTarget, templateDir);
49
+ if (rejection !== null)
50
+ return { resolvedTarget, chosen, error: rejection };
51
+ return { resolvedTarget, chosen, error: null };
60
52
  }
61
-
62
- function targetStatus(resolvedTarget) {
63
- if (!fs.existsSync(resolvedTarget)) return { status: 'missing', count: 0 };
64
- if (!fs.statSync(resolvedTarget).isDirectory()) return { status: 'not-a-directory', count: 0 };
65
- const count = fs.readdirSync(resolvedTarget).length;
66
- return { status: count === 0 ? 'empty' : 'non-empty', count };
53
+ export function targetStatus(resolvedTarget) {
54
+ if (!fs.existsSync(resolvedTarget))
55
+ return { status: 'missing', count: 0 };
56
+ if (!fs.statSync(resolvedTarget).isDirectory())
57
+ return { status: 'not-a-directory', count: 0 };
58
+ const count = fs.readdirSync(resolvedTarget).length;
59
+ return { status: count === 0 ? 'empty' : 'non-empty', count };
67
60
  }
68
-
69
- async function confirmOverwrite(targetDir, count, io) {
70
- const { stdin, stdout, out } = io;
71
- if (!stdin.isTTY) return { confirmed: false, aborted: false };
72
- const confirm = await askQuestion(
73
- `⚠️ Target directory '${targetDir}' is not empty (${count} items). Continue? (y/N) `,
74
- { input: stdin, output: stdout }
75
- );
76
- if (confirm.toLowerCase() !== 'y' && confirm.toLowerCase() !== 'yes') {
77
- out('Scaffolding aborted.');
78
- return { confirmed: false, aborted: true };
79
- }
80
- return { confirmed: true, aborted: false };
61
+ export async function confirmOverwrite(targetDir, count, io) {
62
+ const { stdin, stdout, out } = io;
63
+ if (!stdin.isTTY)
64
+ return { confirmed: false, aborted: false };
65
+ const confirm = await askQuestion(`⚠️ Target directory '${targetDir}' is not empty (${count} items). Continue? (y/N) `, { input: stdin, output: stdout });
66
+ if (confirm.toLowerCase() !== 'y' && confirm.toLowerCase() !== 'yes') {
67
+ out('Scaffolding aborted.');
68
+ return { confirmed: false, aborted: true };
69
+ }
70
+ return { confirmed: true, aborted: false };
81
71
  }
82
-
83
72
  /**
84
73
  * Ensures the target may be written. Returns { force } on success or
85
74
  * { exitCode, message? } when the CLI must stop before scaffolding.
86
75
  */
87
- async function ensureWritableTarget({ chosen, resolvedTarget, force, io }) {
88
- const { err } = io;
89
- const { status, count } = targetStatus(resolvedTarget);
90
- if (status === 'missing' || status === 'empty' || force) return { force };
91
- if (status === 'not-a-directory') {
92
- return { exitCode: 1, message: `Target '${resolvedTarget}' already exists and is not a directory.` };
93
- }
94
- const { confirmed, aborted } = await confirmOverwrite(chosen, count, io);
95
- if (aborted) return { exitCode: 0, message: null };
96
- if (confirmed) return { force: true };
97
- err(`Target directory '${resolvedTarget}' is not empty. Use --force to proceed.`);
98
- return { exitCode: 1, message: null };
76
+ export async function ensureWritableTarget(options) {
77
+ const { chosen, resolvedTarget, force, io } = options;
78
+ const { err } = io;
79
+ const { status, count } = targetStatus(resolvedTarget);
80
+ if (status === 'missing' || status === 'empty' || force)
81
+ return { force };
82
+ if (status === 'not-a-directory') {
83
+ return { exitCode: 1, message: `Target '${resolvedTarget}' already exists and is not a directory.` };
84
+ }
85
+ const { confirmed, aborted } = await confirmOverwrite(chosen, count, io);
86
+ if (aborted)
87
+ return { exitCode: 0, message: null };
88
+ if (confirmed)
89
+ return { force: true };
90
+ err(`Target directory '${resolvedTarget}' is not empty. Use --force to proceed.`);
91
+ return { exitCode: 1, message: null };
99
92
  }
100
-
101
- module.exports = {
102
- askQuestion,
103
- promptForTargetDir,
104
- rejectBadTarget,
105
- resolveTargetDir,
106
- targetStatus,
107
- confirmOverwrite,
108
- ensureWritableTarget
93
+ export default {
94
+ askQuestion,
95
+ promptForTargetDir,
96
+ rejectBadTarget,
97
+ resolveTargetDir,
98
+ targetStatus,
99
+ confirmOverwrite,
100
+ ensureWritableTarget
109
101
  };
102
+ //# sourceMappingURL=cli-target.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli-target.js","sourceRoot":"","sources":["../src/cli-target.ts"],"names":[],"mappings":"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"}
package/lib/cli.d.ts ADDED
@@ -0,0 +1,40 @@
1
+ import type { ScaffoldOptions, ScaffoldResult } from './scaffold.js';
2
+ import { askQuestion } from './cli-target.js';
3
+ export declare function printHelp(out?: (msg: string) => void): void;
4
+ export declare function printVersion(out?: (msg: string) => void): void;
5
+ export interface CliIo {
6
+ out?: (msg: string) => void;
7
+ err?: (msg: string) => void;
8
+ exit?: (code: number) => void | number;
9
+ stdin?: NodeJS.ReadableStream & {
10
+ isTTY?: boolean;
11
+ };
12
+ stdout?: NodeJS.WritableStream;
13
+ cwd?: string;
14
+ templateDir?: string;
15
+ scaffold?: (options?: ScaffoldOptions) => ScaffoldResult;
16
+ }
17
+ export interface NormalizedCliIo {
18
+ out: (msg: string) => void;
19
+ err: (msg: string) => void;
20
+ exit: (code: number) => void | number;
21
+ stdin: NodeJS.ReadableStream & {
22
+ isTTY?: boolean;
23
+ };
24
+ stdout: NodeJS.WritableStream;
25
+ cwd: string;
26
+ templateDir: string;
27
+ scaffoldFn: (options?: ScaffoldOptions) => ScaffoldResult;
28
+ }
29
+ export declare function runCli(rawArgs?: string[], io?: CliIo): Promise<void | number>;
30
+ export declare function main(): Promise<void>;
31
+ export { askQuestion };
32
+ declare const _default: {
33
+ runCli: typeof runCli;
34
+ main: typeof main;
35
+ printHelp: typeof printHelp;
36
+ printVersion: typeof printVersion;
37
+ askQuestion: typeof askQuestion;
38
+ };
39
+ export default _default;
40
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAGrE,OAAO,EAAE,WAAW,EAA0C,MAAM,iBAAiB,CAAC;AAatF,wBAAgB,SAAS,CAAC,GAAG,GAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAkB,GAAG,IAAI,CAwBxE;AAED,wBAAgB,YAAY,CAAC,GAAG,GAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAkB,GAAG,IAAI,CAE3E;AAED,MAAM,WAAW,KAAK;IACpB,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5B,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5B,IAAI,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,GAAG,MAAM,CAAC;IACvC,KAAK,CAAC,EAAE,MAAM,CAAC,cAAc,GAAG;QAAE,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC;IACpD,MAAM,CAAC,EAAE,MAAM,CAAC,cAAc,CAAC;IAC/B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,CAAC,OAAO,CAAC,EAAE,eAAe,KAAK,cAAc,CAAC;CAC1D;AAED,MAAM,WAAW,eAAe;IAC9B,GAAG,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAC3B,GAAG,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAC3B,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,GAAG,MAAM,CAAC;IACtC,KAAK,EAAE,MAAM,CAAC,cAAc,GAAG;QAAE,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC;IACnD,MAAM,EAAE,MAAM,CAAC,cAAc,CAAC;IAC9B,GAAG,EAAE,MAAM,CAAC;IACZ,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,CAAC,OAAO,CAAC,EAAE,eAAe,KAAK,cAAc,CAAC;CAC3D;AA6HD,wBAAsB,MAAM,CAC1B,OAAO,GAAE,MAAM,EAA0B,EACzC,EAAE,GAAE,KAAU,GACb,OAAO,CAAC,IAAI,GAAG,MAAM,CAAC,CAqBxB;AAED,wBAAsB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAO1C;AAED,OAAO,EAAE,WAAW,EAAE,CAAC;;;;;;;;AAEvB,wBAME"}