azcodr 2.0.0 → 2.2.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 (73) hide show
  1. package/.agents/hooks.json +3 -3
  2. package/.github/workflows/ci.yml +1 -0
  3. package/.github/workflows/publish.yml +0 -4
  4. package/AGENTS.md +1 -1
  5. package/bin/azcodr.js +11 -4
  6. package/lib/cli-parse.d.ts +32 -0
  7. package/lib/cli-parse.d.ts.map +1 -0
  8. package/lib/cli-parse.js +45 -41
  9. package/lib/cli-parse.js.map +1 -0
  10. package/lib/cli-target.d.ts +66 -0
  11. package/lib/cli-target.d.ts.map +1 -0
  12. package/lib/cli-target.js +89 -96
  13. package/lib/cli-target.js.map +1 -0
  14. package/lib/cli.d.ts +40 -0
  15. package/lib/cli.d.ts.map +1 -0
  16. package/lib/cli.js +122 -136
  17. package/lib/cli.js.map +1 -0
  18. package/lib/errors.d.ts +54 -0
  19. package/lib/errors.d.ts.map +1 -0
  20. package/lib/errors.js +25 -18
  21. package/lib/errors.js.map +1 -0
  22. package/lib/git.d.ts +23 -0
  23. package/lib/git.d.ts.map +1 -0
  24. package/lib/git.js +33 -22
  25. package/lib/git.js.map +1 -0
  26. package/lib/guards.d.ts +50 -0
  27. package/lib/guards.d.ts.map +1 -0
  28. package/lib/guards.js +75 -64
  29. package/lib/guards.js.map +1 -0
  30. package/lib/index.d.ts +14 -188
  31. package/lib/index.d.ts.map +1 -0
  32. package/lib/index.js +17 -5
  33. package/lib/index.js.map +1 -0
  34. package/lib/links.d.ts +42 -0
  35. package/lib/links.d.ts.map +1 -0
  36. package/lib/links.js +116 -99
  37. package/lib/links.js.map +1 -0
  38. package/lib/permissions.d.ts +13 -0
  39. package/lib/permissions.d.ts.map +1 -0
  40. package/lib/permissions.js +40 -36
  41. package/lib/permissions.js.map +1 -0
  42. package/lib/repo.d.ts +30 -0
  43. package/lib/repo.d.ts.map +1 -0
  44. package/lib/repo.js +80 -71
  45. package/lib/repo.js.map +1 -0
  46. package/lib/scaffold.d.ts +109 -0
  47. package/lib/scaffold.d.ts.map +1 -0
  48. package/lib/scaffold.js +165 -197
  49. package/lib/scaffold.js.map +1 -0
  50. package/memory.md +24 -0
  51. package/package.json +14 -5
  52. package/scripts/validate/adr.js +11 -7
  53. package/scripts/validate/io.js +7 -9
  54. package/scripts/validate/links.js +6 -7
  55. package/scripts/validate/parity.js +5 -7
  56. package/scripts/validate/root.js +6 -7
  57. package/scripts/validate/rules.js +7 -9
  58. package/scripts/validate/skills.js +8 -10
  59. package/scripts/validate/text.js +3 -5
  60. package/scripts/validate-cli.js +1 -2
  61. package/scripts/validate.js +37 -19
  62. package/src/cli-parse.ts +77 -0
  63. package/src/cli-target.ts +167 -0
  64. package/src/cli.ts +240 -0
  65. package/src/errors.ts +50 -0
  66. package/src/git.ts +42 -0
  67. package/src/guards.ts +116 -0
  68. package/src/index.ts +60 -0
  69. package/src/links.ts +153 -0
  70. package/src/permissions.ts +46 -0
  71. package/src/repo.ts +104 -0
  72. package/src/scaffold.ts +282 -0
  73. package/scripts/test_coverage.js +0 -66
package/lib/links.js CHANGED
@@ -1,123 +1,140 @@
1
- 'use strict';
2
-
3
- const fs = require('node:fs');
4
- const path = require('node:path');
5
-
6
- /**
7
- * Detects whether linkName and targetFileName refer to the same entry on a case-insensitive filesystem.
8
- * Uses exact directory listing (not existsSync, which lies on case-insensitive systems).
9
- */
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
10
3
  function namesDifferOnlyByCase(linkName, targetFileName) {
11
- if (linkName.toLowerCase() !== targetFileName.toLowerCase()) return false;
12
- return linkName !== targetFileName;
4
+ if (linkName.toLowerCase() !== targetFileName.toLowerCase())
5
+ return false;
6
+ return linkName !== targetFileName;
13
7
  }
14
-
15
8
  function readDirEntries(targetDir) {
16
- try {
17
- return fs.readdirSync(targetDir);
18
- } catch {
19
- return null;
20
- }
9
+ try {
10
+ return fs.readdirSync(targetDir);
11
+ }
12
+ catch {
13
+ return null;
14
+ }
21
15
  }
22
-
23
- function bothEntriesExist({ targetDir, linkName, targetFileName, entries }) {
24
- if (!entries.includes(targetFileName) || !entries.includes(linkName)) return false;
25
- try {
26
- fs.lstatSync(path.join(targetDir, targetFileName));
27
- fs.lstatSync(path.join(targetDir, linkName));
28
- return true;
29
- } catch {
30
- return false;
31
- }
16
+ function bothEntriesExist(options) {
17
+ const { targetDir, linkName, targetFileName, entries } = options;
18
+ if (!entries.includes(targetFileName) || !entries.includes(linkName))
19
+ return false;
20
+ try {
21
+ fs.lstatSync(path.join(targetDir, targetFileName));
22
+ fs.lstatSync(path.join(targetDir, linkName));
23
+ return true;
24
+ }
25
+ catch {
26
+ return false;
27
+ }
32
28
  }
33
-
34
29
  function accessiblePair(targetPath, linkPath) {
35
- // Case-insensitive collision: the listing holds one spelling while both
36
- // paths resolve. Verify the entries are accessible (throws on disk error).
37
- if (!fs.existsSync(targetPath) || !fs.existsSync(linkPath)) return false;
38
- try {
39
- fs.lstatSync(targetPath);
40
- fs.lstatSync(linkPath);
41
- } catch {
42
- return false;
43
- }
44
- return true;
45
- }
46
-
47
- function isSameCaseInsensitiveFile(targetDir, linkName, targetFileName) {
48
- if (linkName === targetFileName) return true;
49
- if (!namesDifferOnlyByCase(linkName, targetFileName)) return false;
50
- const entries = readDirEntries(targetDir);
51
- if (entries === null) return false;
52
- if (bothEntriesExist({ targetDir, linkName, targetFileName, entries })) {
53
- return !fs.lstatSync(path.join(targetDir, linkName)).isSymbolicLink();
54
- }
55
- const targetPath = path.join(targetDir, targetFileName);
56
- const linkPath = path.join(targetDir, linkName);
57
- return accessiblePair(targetPath, linkPath);
30
+ if (!fs.existsSync(targetPath) || !fs.existsSync(linkPath))
31
+ return false;
32
+ try {
33
+ fs.lstatSync(targetPath);
34
+ fs.lstatSync(linkPath);
35
+ return true;
36
+ }
37
+ catch {
38
+ return false;
39
+ }
58
40
  }
59
-
60
41
  function removeIfExists(linkPath) {
61
- try {
62
- fs.lstatSync(linkPath);
63
- fs.rmSync(linkPath, { force: true });
64
- } catch {
65
- // Path does not exist, proceed
66
- }
42
+ try {
43
+ fs.lstatSync(linkPath);
44
+ fs.rmSync(linkPath, { force: true });
45
+ }
46
+ catch {
47
+ // Path does not exist, proceed
48
+ }
67
49
  }
68
-
69
50
  function copyFallback(targetDir, linkName, targetFileName) {
70
- // Fallback if environment (e.g., certain Windows configs) prevents symlink creation
71
- const sourceFile = path.resolve(targetDir, targetFileName);
72
- if (fs.existsSync(sourceFile)) {
73
- fs.copyFileSync(sourceFile, path.join(targetDir, linkName));
74
- }
51
+ const sourceFile = path.resolve(targetDir, targetFileName);
52
+ if (fs.existsSync(sourceFile)) {
53
+ fs.copyFileSync(sourceFile, path.join(targetDir, linkName));
54
+ }
55
+ }
56
+ /**
57
+ * Detects whether linkName and targetFileName refer to the same entry on a case-insensitive filesystem.
58
+ * Uses exact directory listing (not existsSync, which lies on case-insensitive systems).
59
+ *
60
+ * @param targetDir - Directory containing the file entries.
61
+ * @param linkName - Name of the proposed symlink or alias entry.
62
+ * @param targetFileName - Target file name to match against.
63
+ * @returns True if both names resolve to the same disk file on a case-insensitive filesystem.
64
+ */
65
+ export function isSameCaseInsensitiveFile(targetDir, linkName, targetFileName) {
66
+ if (linkName === targetFileName)
67
+ return true;
68
+ if (!namesDifferOnlyByCase(linkName, targetFileName))
69
+ return false;
70
+ const entries = readDirEntries(targetDir);
71
+ if (entries === null)
72
+ return false;
73
+ if (bothEntriesExist({ targetDir, linkName, targetFileName, entries })) {
74
+ return !fs.lstatSync(path.join(targetDir, linkName)).isSymbolicLink();
75
+ }
76
+ const targetPath = path.join(targetDir, targetFileName);
77
+ const linkPath = path.join(targetDir, linkName);
78
+ return accessiblePair(targetPath, linkPath);
75
79
  }
76
-
77
80
  /**
78
81
  * Safely creates or updates a symbolic link, falling back to a file copy if symlinks are unsupported.
82
+ *
83
+ * @param options - Options specifying target directory, link name, target file name, and dry-run mode.
84
+ * @returns True if the symlink or copy fallback was successfully established.
79
85
  */
80
- function ensureSymlink({ targetDir, linkName, targetFileName, dryRun = false }) {
81
- if (dryRun) return true;
82
- if (isSameCaseInsensitiveFile(targetDir, linkName, targetFileName)) return true;
83
- const linkPath = path.join(targetDir, linkName);
84
- removeIfExists(linkPath);
85
- try {
86
- fs.symlinkSync(targetFileName, linkPath, 'file');
87
- return true;
88
- } catch {
89
- copyFallback(targetDir, linkName, targetFileName);
90
- return true;
91
- }
86
+ export function ensureSymlink(options) {
87
+ const { targetDir, linkName, targetFileName, dryRun = false } = options;
88
+ if (dryRun)
89
+ return true;
90
+ if (isSameCaseInsensitiveFile(targetDir, linkName, targetFileName))
91
+ return true;
92
+ const linkPath = path.join(targetDir, linkName);
93
+ removeIfExists(linkPath);
94
+ try {
95
+ fs.symlinkSync(targetFileName, linkPath, 'file');
96
+ return true;
97
+ }
98
+ catch {
99
+ copyFallback(targetDir, linkName, targetFileName);
100
+ return true;
101
+ }
92
102
  }
93
-
94
103
  /**
95
104
  * Creates a symlink, falling back to a text pointer (not a full copy).
96
105
  * Used for .github/copilot-instructions.md where a full AGENTS.md copy would
97
106
  * break relative markdown links (they resolve from .github/, not root).
107
+ *
108
+ * @param options - Options specifying target directory, link name, target file name, and dry-run mode.
109
+ * @returns True if the symlink or pointer fallback was successfully established.
98
110
  */
99
- function ensureSymlinkOrPointer({ targetDir, linkName, targetFileName, dryRun = false }) {
100
- if (dryRun) return true;
101
- const linkPath = path.join(targetDir, linkName);
102
- try {
111
+ export function ensureSymlinkOrPointer(options) {
112
+ const { targetDir, linkName, targetFileName, dryRun = false } = options;
113
+ if (dryRun)
114
+ return true;
115
+ const linkPath = path.join(targetDir, linkName);
103
116
  try {
104
- fs.lstatSync(linkPath);
105
- const existing = fs.existsSync(linkPath) ? fs.readFileSync(linkPath, 'utf-8') : '';
106
- if (existing.trim() === targetFileName) return true;
107
- fs.rmSync(linkPath, { force: true });
108
- } catch {
109
- // Path does not exist, proceed
117
+ try {
118
+ fs.lstatSync(linkPath);
119
+ const existing = fs.existsSync(linkPath) ? fs.readFileSync(linkPath, 'utf-8') : '';
120
+ if (existing.trim() === targetFileName)
121
+ return true;
122
+ fs.rmSync(linkPath, { force: true });
123
+ }
124
+ catch {
125
+ // Path does not exist, proceed
126
+ }
127
+ fs.symlinkSync(targetFileName, linkPath, 'file');
128
+ return true;
129
+ }
130
+ catch {
131
+ fs.writeFileSync(linkPath, `${targetFileName}\n`, 'utf-8');
132
+ return true;
110
133
  }
111
- fs.symlinkSync(targetFileName, linkPath, 'file');
112
- return true;
113
- } catch {
114
- fs.writeFileSync(linkPath, `${targetFileName}\n`, 'utf-8');
115
- return true;
116
- }
117
134
  }
118
-
119
- module.exports = {
120
- isSameCaseInsensitiveFile,
121
- ensureSymlink,
122
- ensureSymlinkOrPointer
135
+ export default {
136
+ isSameCaseInsensitiveFile,
137
+ ensureSymlink,
138
+ ensureSymlinkOrPointer
123
139
  };
140
+ //# sourceMappingURL=links.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"links.js","sourceRoot":"","sources":["../src/links.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Ensures all bash scripts in agent and skill directories have executable permissions (0o755).
3
+ *
4
+ * @param targetDir - Root directory containing .agents and skills.
5
+ * @param dryRun - When true, simulates permission changes without modifying disk.
6
+ * @returns Array of file paths that were modified or would be modified.
7
+ */
8
+ export declare function makeScriptsExecutable(targetDir: string, dryRun?: boolean): string[];
9
+ declare const _default: {
10
+ makeScriptsExecutable: typeof makeScriptsExecutable;
11
+ };
12
+ export default _default;
13
+ //# sourceMappingURL=permissions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"permissions.d.ts","sourceRoot":"","sources":["../src/permissions.ts"],"names":[],"mappings":"AAyBA;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,UAAQ,GAAG,MAAM,EAAE,CAWjF;;;;AAED,wBAAyC"}
@@ -1,44 +1,48 @@
1
- 'use strict';
2
-
3
- const fs = require('node:fs');
4
- const path = require('node:path');
5
-
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
6
3
  function chmodShFile(filePath, dryRun) {
7
- if (dryRun) return;
8
- try {
9
- fs.chmodSync(filePath, 0o755);
10
- } catch {
11
- // Non-critical if filesystem does not support POSIX permissions
12
- }
4
+ if (dryRun)
5
+ return;
6
+ try {
7
+ fs.chmodSync(filePath, 0o755);
8
+ }
9
+ catch {
10
+ // Non-critical if filesystem does not support POSIX permissions
11
+ }
13
12
  }
14
-
15
13
  function chmodShFilesIn(scriptsDir, dryRun) {
16
- const modified = [];
17
- if (!fs.existsSync(scriptsDir) || !fs.statSync(scriptsDir).isDirectory()) return modified;
18
- const files = fs.readdirSync(scriptsDir);
19
- for (const file of files) {
20
- if (!file.endsWith('.sh')) continue;
21
- const filePath = path.join(scriptsDir, file);
22
- modified.push(filePath);
23
- chmodShFile(filePath, dryRun);
24
- }
25
- return modified;
14
+ const modified = [];
15
+ if (!fs.existsSync(scriptsDir) || !fs.statSync(scriptsDir).isDirectory())
16
+ return modified;
17
+ const files = fs.readdirSync(scriptsDir);
18
+ for (const file of files) {
19
+ if (!file.endsWith('.sh'))
20
+ continue;
21
+ const filePath = path.join(scriptsDir, file);
22
+ modified.push(filePath);
23
+ chmodShFile(filePath, dryRun);
24
+ }
25
+ return modified;
26
26
  }
27
-
28
27
  /**
29
28
  * Ensures all bash scripts in agent and skill directories have executable permissions (0o755).
29
+ *
30
+ * @param targetDir - Root directory containing .agents and skills.
31
+ * @param dryRun - When true, simulates permission changes without modifying disk.
32
+ * @returns Array of file paths that were modified or would be modified.
30
33
  */
31
- function makeScriptsExecutable(targetDir, dryRun = false) {
32
- const agentScriptsDir = path.join(targetDir, '.agents', 'scripts');
33
- const modified = chmodShFilesIn(agentScriptsDir, dryRun);
34
- const skillsDir = path.join(targetDir, '.agents', 'skills');
35
- if (!fs.existsSync(skillsDir)) return modified;
36
- const skills = fs.readdirSync(skillsDir);
37
- for (const skill of skills) {
38
- const scriptsDir = path.join(skillsDir, skill, 'scripts');
39
- modified.push(...chmodShFilesIn(scriptsDir, dryRun));
40
- }
41
- return modified;
34
+ export function makeScriptsExecutable(targetDir, dryRun = false) {
35
+ const agentScriptsDir = path.join(targetDir, '.agents', 'scripts');
36
+ const modified = chmodShFilesIn(agentScriptsDir, dryRun);
37
+ const skillsDir = path.join(targetDir, '.agents', 'skills');
38
+ if (!fs.existsSync(skillsDir))
39
+ return modified;
40
+ const skills = fs.readdirSync(skillsDir);
41
+ for (const skill of skills) {
42
+ const scriptsDir = path.join(skillsDir, skill, 'scripts');
43
+ modified.push(...chmodShFilesIn(scriptsDir, dryRun));
44
+ }
45
+ return modified;
42
46
  }
43
-
44
- module.exports = { makeScriptsExecutable };
47
+ export default { makeScriptsExecutable };
48
+ //# sourceMappingURL=permissions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"permissions.js","sourceRoot":"","sources":["../src/permissions.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,SAAS,WAAW,CAAC,QAAgB,EAAE,MAAe;IACpD,IAAI,MAAM;QAAE,OAAO;IACnB,IAAI,CAAC;QACH,EAAE,CAAC,SAAS,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;IAChC,CAAC;IAAC,MAAM,CAAC;QACP,gEAAgE;IAClE,CAAC;AACH,CAAC;AAED,SAAS,cAAc,CAAC,UAAkB,EAAE,MAAe;IACzD,MAAM,QAAQ,GAAa,EAAE,CAAC;IAC9B,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,WAAW,EAAE;QAAE,OAAO,QAAQ,CAAC;IAC1F,MAAM,KAAK,GAAG,EAAE,CAAC,WAAW,CAAC,UAAU,CAAC,CAAC;IACzC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC;YAAE,SAAS;QACpC,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC;QAC7C,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACxB,WAAW,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IAChC,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,qBAAqB,CAAC,SAAiB,EAAE,MAAM,GAAG,KAAK;IACrE,MAAM,eAAe,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,SAAS,EAAE,SAAS,CAAC,CAAC;IACnE,MAAM,QAAQ,GAAG,cAAc,CAAC,eAAe,EAAE,MAAM,CAAC,CAAC;IACzD,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,SAAS,EAAE,QAAQ,CAAC,CAAC;IAC5D,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,SAAS,CAAC;QAAE,OAAO,QAAQ,CAAC;IAC/C,MAAM,MAAM,GAAG,EAAE,CAAC,WAAW,CAAC,SAAS,CAAC,CAAC;IACzC,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,KAAK,EAAE,SAAS,CAAC,CAAC;QAC1D,QAAQ,CAAC,IAAI,CAAC,GAAG,cAAc,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC,CAAC;IACvD,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,eAAe,EAAE,qBAAqB,EAAE,CAAC"}
package/lib/repo.d.ts ADDED
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Options controlling git repository initialization.
3
+ */
4
+ export interface InitGitOptions {
5
+ noGit?: boolean;
6
+ dryRun?: boolean;
7
+ }
8
+ /**
9
+ * Detects whether targetDir is already inside an existing Git worktree.
10
+ * Socket note: shell access is limited to `git rev-parse` via runGit (no shell).
11
+ *
12
+ * @param targetDir - Directory path to inspect.
13
+ * @returns True if target directory is within a git worktree, false otherwise.
14
+ */
15
+ export declare function isInsideGitWorkTree(targetDir: string): boolean;
16
+ /**
17
+ * Initializes a git repository in the target directory if not already inside one.
18
+ * Socket note: only allowlisted `git init/branch/add/commit` via execFileSync, cwd scoped.
19
+ *
20
+ * @param targetDir - Directory where git repository should be initialized.
21
+ * @param options - Configuration options for git initialization.
22
+ * @returns True if repository was initialized and committed, false otherwise.
23
+ */
24
+ export declare function initGit(targetDir: string, options?: InitGitOptions): boolean;
25
+ declare const _default: {
26
+ isInsideGitWorkTree: typeof isInsideGitWorkTree;
27
+ initGit: typeof initGit;
28
+ };
29
+ export default _default;
30
+ //# sourceMappingURL=repo.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"repo.d.ts","sourceRoot":"","sources":["../src/repo.ts"],"names":[],"mappings":"AAOA;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAW9D;AA8CD;;;;;;;GAOG;AACH,wBAAgB,OAAO,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,GAAE,cAAmB,GAAG,OAAO,CAchF;;;;;AAED,wBAAgD"}
package/lib/repo.js CHANGED
@@ -1,90 +1,99 @@
1
- 'use strict';
2
-
3
- const fs = require('node:fs');
4
- const path = require('node:path');
5
- const { runGit } = require('./git.js');
6
-
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import process from 'node:process';
4
+ import { runGit } from './git.js';
7
5
  const INITIAL_COMMIT_MESSAGE = 'chore: initial scaffold from azcodr template';
8
-
9
6
  /**
10
7
  * Detects whether targetDir is already inside an existing Git worktree.
11
8
  * Socket note: shell access is limited to `git rev-parse` via runGit (no shell).
9
+ *
10
+ * @param targetDir - Directory path to inspect.
11
+ * @returns True if target directory is within a git worktree, false otherwise.
12
12
  */
13
- function isInsideGitWorkTree(targetDir) {
14
- try {
15
- const checkDir = fs.existsSync(targetDir) ? targetDir : path.dirname(targetDir);
16
- const out = runGit(['rev-parse', '--is-inside-work-tree'], {
17
- cwd: checkDir,
18
- stdio: ['ignore', 'pipe', 'ignore']
19
- });
20
- return String(out).trim() === 'true';
21
- } catch {
22
- return false;
23
- }
13
+ export function isInsideGitWorkTree(targetDir) {
14
+ try {
15
+ const checkDir = fs.existsSync(targetDir) ? targetDir : path.dirname(targetDir);
16
+ const out = runGit(['rev-parse', '--is-inside-work-tree'], {
17
+ cwd: checkDir,
18
+ stdio: ['ignore', 'pipe', 'ignore']
19
+ });
20
+ return String(out).trim() === 'true';
21
+ }
22
+ catch {
23
+ return false;
24
+ }
24
25
  }
25
-
26
26
  function renameBranchMain(targetDir) {
27
- try {
28
- runGit(['branch', '-m', 'main'], { cwd: targetDir, stdio: 'ignore' });
29
- } catch {
30
- // Non-critical if branch rename fails
31
- }
27
+ try {
28
+ runGit(['branch', '-m', 'main'], { cwd: targetDir, stdio: 'ignore' });
29
+ }
30
+ catch {
31
+ // Non-critical if branch rename fails
32
+ }
32
33
  }
33
-
34
34
  function initRepository(targetDir) {
35
- try {
36
- runGit(['init', '-b', 'main', '-q'], { cwd: targetDir, stdio: 'ignore' });
37
- } catch {
38
- runGit(['init', '-q'], { cwd: targetDir, stdio: 'ignore' });
39
- renameBranchMain(targetDir);
40
- }
35
+ try {
36
+ runGit(['init', '-b', 'main', '-q'], { cwd: targetDir, stdio: 'ignore' });
37
+ }
38
+ catch {
39
+ runGit(['init', '-q'], { cwd: targetDir, stdio: 'ignore' });
40
+ renameBranchMain(targetDir);
41
+ }
41
42
  }
42
-
43
43
  function commitWithFallbackIdentity(targetDir) {
44
- const options = { cwd: targetDir, stdio: 'ignore' };
45
- try {
46
- runGit(['commit', '-q', '-m', INITIAL_COMMIT_MESSAGE], options);
47
- } catch {
48
- runGit(['commit', '-q', '-m', INITIAL_COMMIT_MESSAGE], {
49
- ...options,
50
- env: {
51
- ...process.env,
52
- GIT_AUTHOR_NAME: 'Subodh Khanal',
53
- GIT_AUTHOR_EMAIL: 'prosubodh@gmail.com',
54
- GIT_COMMITTER_NAME: 'Subodh Khanal',
55
- GIT_COMMITTER_EMAIL: 'prosubodh@gmail.com'
56
- }
57
- });
58
- }
44
+ const options = { cwd: targetDir, stdio: 'ignore' };
45
+ try {
46
+ runGit(['commit', '-q', '-m', INITIAL_COMMIT_MESSAGE], options);
47
+ }
48
+ catch {
49
+ runGit(['commit', '-q', '-m', INITIAL_COMMIT_MESSAGE], {
50
+ ...options,
51
+ env: {
52
+ ...process.env,
53
+ GIT_AUTHOR_NAME: 'Subodh Khanal',
54
+ GIT_AUTHOR_EMAIL: 'prosubodh@gmail.com',
55
+ GIT_COMMITTER_NAME: 'Subodh Khanal',
56
+ GIT_COMMITTER_EMAIL: 'prosubodh@gmail.com'
57
+ }
58
+ });
59
+ }
59
60
  }
60
-
61
61
  function initialCommit(targetDir) {
62
- try {
63
- runGit(['add', '-A'], { cwd: targetDir, stdio: 'ignore' });
64
- commitWithFallbackIdentity(targetDir);
65
- } catch {
66
- // Non-critical if initial commit fails
67
- }
62
+ try {
63
+ runGit(['add', '-A'], { cwd: targetDir, stdio: 'ignore' });
64
+ commitWithFallbackIdentity(targetDir);
65
+ }
66
+ catch {
67
+ // Non-critical if initial commit fails
68
+ }
68
69
  }
69
-
70
70
  /**
71
71
  * Initializes a git repository in the target directory if not already inside one.
72
72
  * Socket note: only allowlisted `git init/branch/add/commit` via execFileSync, cwd scoped.
73
+ *
74
+ * @param targetDir - Directory where git repository should be initialized.
75
+ * @param options - Configuration options for git initialization.
76
+ * @returns True if repository was initialized and committed, false otherwise.
73
77
  */
74
- function initGit(targetDir, options = {}) {
75
- const { noGit = false, dryRun = false } = options;
76
- if (noGit) return false;
77
- const gitDir = path.join(targetDir, '.git');
78
- if (fs.existsSync(gitDir)) return false;
79
- if (isInsideGitWorkTree(targetDir)) return false;
80
- if (dryRun) return true;
81
- try {
82
- initRepository(targetDir);
83
- initialCommit(targetDir);
84
- return true;
85
- } catch {
86
- return false;
87
- }
78
+ export function initGit(targetDir, options = {}) {
79
+ const { noGit = false, dryRun = false } = options;
80
+ if (noGit)
81
+ return false;
82
+ const gitDir = path.join(targetDir, '.git');
83
+ if (fs.existsSync(gitDir))
84
+ return false;
85
+ if (isInsideGitWorkTree(targetDir))
86
+ return false;
87
+ if (dryRun)
88
+ return true;
89
+ try {
90
+ initRepository(targetDir);
91
+ initialCommit(targetDir);
92
+ return true;
93
+ }
94
+ catch {
95
+ return false;
96
+ }
88
97
  }
89
-
90
- module.exports = { isInsideGitWorkTree, initGit };
98
+ export default { isInsideGitWorkTree, initGit };
99
+ //# sourceMappingURL=repo.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"repo.js","sourceRoot":"","sources":["../src/repo.ts"],"names":[],"mappings":"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"}