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.
- package/.agents/hooks.json +3 -3
- package/.github/workflows/ci.yml +1 -0
- package/.github/workflows/publish.yml +0 -4
- package/AGENTS.md +1 -1
- package/bin/azcodr.js +11 -4
- package/lib/cli-parse.d.ts +32 -0
- package/lib/cli-parse.d.ts.map +1 -0
- package/lib/cli-parse.js +45 -41
- package/lib/cli-parse.js.map +1 -0
- package/lib/cli-target.d.ts +66 -0
- package/lib/cli-target.d.ts.map +1 -0
- package/lib/cli-target.js +89 -96
- package/lib/cli-target.js.map +1 -0
- package/lib/cli.d.ts +40 -0
- package/lib/cli.d.ts.map +1 -0
- package/lib/cli.js +122 -136
- package/lib/cli.js.map +1 -0
- package/lib/errors.d.ts +54 -0
- package/lib/errors.d.ts.map +1 -0
- package/lib/errors.js +25 -18
- package/lib/errors.js.map +1 -0
- package/lib/git.d.ts +23 -0
- package/lib/git.d.ts.map +1 -0
- package/lib/git.js +33 -22
- package/lib/git.js.map +1 -0
- package/lib/guards.d.ts +50 -0
- package/lib/guards.d.ts.map +1 -0
- package/lib/guards.js +75 -64
- package/lib/guards.js.map +1 -0
- package/lib/index.d.ts +14 -188
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +17 -5
- package/lib/index.js.map +1 -0
- package/lib/links.d.ts +42 -0
- package/lib/links.d.ts.map +1 -0
- package/lib/links.js +116 -99
- package/lib/links.js.map +1 -0
- package/lib/permissions.d.ts +13 -0
- package/lib/permissions.d.ts.map +1 -0
- package/lib/permissions.js +40 -36
- package/lib/permissions.js.map +1 -0
- package/lib/repo.d.ts +30 -0
- package/lib/repo.d.ts.map +1 -0
- package/lib/repo.js +80 -71
- package/lib/repo.js.map +1 -0
- package/lib/scaffold.d.ts +109 -0
- package/lib/scaffold.d.ts.map +1 -0
- package/lib/scaffold.js +165 -197
- package/lib/scaffold.js.map +1 -0
- package/memory.md +24 -0
- package/package.json +14 -5
- package/scripts/validate/adr.js +11 -7
- package/scripts/validate/io.js +7 -9
- package/scripts/validate/links.js +6 -7
- package/scripts/validate/parity.js +5 -7
- package/scripts/validate/root.js +6 -7
- package/scripts/validate/rules.js +7 -9
- package/scripts/validate/skills.js +8 -10
- package/scripts/validate/text.js +3 -5
- package/scripts/validate-cli.js +1 -2
- package/scripts/validate.js +37 -19
- package/src/cli-parse.ts +77 -0
- package/src/cli-target.ts +167 -0
- package/src/cli.ts +240 -0
- package/src/errors.ts +50 -0
- package/src/git.ts +42 -0
- package/src/guards.ts +116 -0
- package/src/index.ts +60 -0
- package/src/links.ts +153 -0
- package/src/permissions.ts +46 -0
- package/src/repo.ts +104 -0
- package/src/scaffold.ts +282 -0
- package/scripts/test_coverage.js +0 -66
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { ScaffoldError, ERROR_CODES } from './errors.js';
|
|
2
|
+
import type { ScaffoldErrorCode, ScaffoldErrorShape } from './errors.js';
|
|
3
|
+
import { runGit } from './git.js';
|
|
4
|
+
import { assertInside, isProtectedTarget } from './guards.js';
|
|
5
|
+
import { ensureSymlink, ensureSymlinkOrPointer, isSameCaseInsensitiveFile } from './links.js';
|
|
6
|
+
import type { EnsureSymlinkOptions } from './links.js';
|
|
7
|
+
import { makeScriptsExecutable } from './permissions.js';
|
|
8
|
+
import { isInsideGitWorkTree, initGit } from './repo.js';
|
|
9
|
+
import type { InitGitOptions } from './repo.js';
|
|
10
|
+
/**
|
|
11
|
+
* Canonical list of template files and directories copied into a new workspace.
|
|
12
|
+
*/
|
|
13
|
+
export declare const TEMPLATE_ITEMS: readonly string[];
|
|
14
|
+
/**
|
|
15
|
+
* Configuration options for the primary scaffold orchestration function.
|
|
16
|
+
*/
|
|
17
|
+
export interface ScaffoldOptions {
|
|
18
|
+
targetDir?: string;
|
|
19
|
+
force?: boolean;
|
|
20
|
+
noGit?: boolean;
|
|
21
|
+
templateDir?: string;
|
|
22
|
+
dryRun?: boolean;
|
|
23
|
+
silent?: boolean;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Result object returned upon completion of a scaffold operation.
|
|
27
|
+
*/
|
|
28
|
+
export interface ScaffoldResult {
|
|
29
|
+
success: boolean;
|
|
30
|
+
targetDir: string;
|
|
31
|
+
gitInitialized: boolean;
|
|
32
|
+
dryRun: boolean;
|
|
33
|
+
actions: string[];
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Options controlling pre-scaffold target directory validation.
|
|
37
|
+
*/
|
|
38
|
+
export interface ValidateTargetOptions {
|
|
39
|
+
templateDir?: string;
|
|
40
|
+
force?: boolean;
|
|
41
|
+
dryRun?: boolean;
|
|
42
|
+
allowProtected?: boolean;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Options controlling template file copying and file generation.
|
|
46
|
+
*/
|
|
47
|
+
export interface CopyTemplateOptions {
|
|
48
|
+
dryRun?: boolean;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Resolves the root filesystem directory of the azcodr template files.
|
|
52
|
+
*
|
|
53
|
+
* @returns Absolute filesystem path to the template root directory.
|
|
54
|
+
*/
|
|
55
|
+
export declare function getTemplateDir(): string;
|
|
56
|
+
/**
|
|
57
|
+
* Validates the target directory to ensure it is suitable for scaffolding.
|
|
58
|
+
*
|
|
59
|
+
* @param targetDir - Target directory path to validate.
|
|
60
|
+
* @param options - Validation options including force, dry-run, and protected overrides.
|
|
61
|
+
* @throws {ScaffoldError} When target is invalid, protected, or non-empty without force.
|
|
62
|
+
*/
|
|
63
|
+
export declare function validateTarget(targetDir: string, options?: ValidateTargetOptions): void;
|
|
64
|
+
/**
|
|
65
|
+
* Recursively copies template files into the target directory and sets up symlinks and permissions.
|
|
66
|
+
* Socket note: filesystem access is scoped to templateDir -> targetDir only (assertInside).
|
|
67
|
+
*
|
|
68
|
+
* @param targetDir - Destination directory path.
|
|
69
|
+
* @param templateDir - Source template directory path.
|
|
70
|
+
* @param options - Copy options including dry-run flag.
|
|
71
|
+
* @returns Array of recorded scaffolding action descriptions.
|
|
72
|
+
*/
|
|
73
|
+
export declare function copyTemplate(targetDir: string, templateDir?: string, options?: CopyTemplateOptions): string[];
|
|
74
|
+
/**
|
|
75
|
+
* High-level orchestration function to scaffold the azcodr workspace into targetDir.
|
|
76
|
+
*
|
|
77
|
+
* @param options - Scaffolding options.
|
|
78
|
+
* @returns Result object containing success flag, target directory, and action log.
|
|
79
|
+
*/
|
|
80
|
+
export declare function scaffold(options?: ScaffoldOptions): ScaffoldResult;
|
|
81
|
+
export { ensureSymlink, ensureSymlinkOrPointer, isSameCaseInsensitiveFile, makeScriptsExecutable, isInsideGitWorkTree, initGit, runGit, assertInside, ScaffoldError, ERROR_CODES, isProtectedTarget };
|
|
82
|
+
export type { EnsureSymlinkOptions, InitGitOptions, ScaffoldErrorCode, ScaffoldErrorShape };
|
|
83
|
+
declare const _default: {
|
|
84
|
+
scaffold: typeof scaffold;
|
|
85
|
+
validateTarget: typeof validateTarget;
|
|
86
|
+
copyTemplate: typeof copyTemplate;
|
|
87
|
+
ensureSymlink: typeof ensureSymlink;
|
|
88
|
+
ensureSymlinkOrPointer: typeof ensureSymlinkOrPointer;
|
|
89
|
+
isSameCaseInsensitiveFile: typeof isSameCaseInsensitiveFile;
|
|
90
|
+
makeScriptsExecutable: typeof makeScriptsExecutable;
|
|
91
|
+
isInsideGitWorkTree: typeof isInsideGitWorkTree;
|
|
92
|
+
initGit: typeof initGit;
|
|
93
|
+
getTemplateDir: typeof getTemplateDir;
|
|
94
|
+
runGit: typeof runGit;
|
|
95
|
+
assertInside: typeof assertInside;
|
|
96
|
+
TEMPLATE_ITEMS: readonly string[];
|
|
97
|
+
ScaffoldError: typeof ScaffoldError;
|
|
98
|
+
ERROR_CODES: {
|
|
99
|
+
readonly E_TARGET_IS_TEMPLATE: "Cannot scaffold into the azcodr template directory itself";
|
|
100
|
+
readonly E_TARGET_NOT_EMPTY: "Target directory is not empty";
|
|
101
|
+
readonly E_TARGET_IS_PROTECTED: "Refusing to scaffold into a protected system directory";
|
|
102
|
+
readonly E_GIT_ARGS_INVALID: "runGit requires a non-empty argv array";
|
|
103
|
+
readonly E_GIT_BLOCKED: "Blocked git subcommand";
|
|
104
|
+
readonly E_PATH_ESCAPE: "Path escapes allowed root";
|
|
105
|
+
};
|
|
106
|
+
isProtectedTarget: typeof isProtectedTarget;
|
|
107
|
+
};
|
|
108
|
+
export default _default;
|
|
109
|
+
//# sourceMappingURL=scaffold.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scaffold.d.ts","sourceRoot":"","sources":["../src/scaffold.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AACzD,OAAO,KAAK,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACzE,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAClC,OAAO,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAC9D,OAAO,EAAE,aAAa,EAAE,sBAAsB,EAAE,yBAAyB,EAAE,MAAM,YAAY,CAAC;AAC9F,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AACvD,OAAO,EAAE,qBAAqB,EAAE,MAAM,kBAAkB,CAAC;AACzD,OAAO,EAAE,mBAAmB,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACzD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAEhD;;GAEG;AACH,eAAO,MAAM,cAAc,EAAE,SAAS,MAAM,EAW3C,CAAC;AAEF;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,OAAO,CAAC;IACxB,MAAM,EAAE,OAAO,CAAC;IAChB,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED;;;;GAIG;AACH,wBAAgB,cAAc,IAAI,MAAM,CAEvC;AAkCD;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,GAAE,qBAA0B,GAAG,IAAI,CAW3F;AAwFD;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAC1B,SAAS,EAAE,MAAM,EACjB,WAAW,GAAE,MAAyB,EACtC,OAAO,GAAE,mBAAwB,GAChC,MAAM,EAAE,CAcV;AAED;;;;;GAKG;AACH,wBAAgB,QAAQ,CAAC,OAAO,GAAE,eAAoB,GAAG,cAAc,CActE;AAED,OAAO,EACL,aAAa,EAAE,sBAAsB,EAAE,yBAAyB,EAChE,qBAAqB,EAAE,mBAAmB,EAAE,OAAO,EAAE,MAAM,EAC3D,YAAY,EAAE,aAAa,EAAE,WAAW,EAAE,iBAAiB,EAC5D,CAAC;AAEF,YAAY,EAAE,oBAAoB,EAAE,cAAc,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;AAE5F,wBAKE"}
|
package/lib/scaffold.js
CHANGED
|
@@ -1,238 +1,206 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import process from 'node:process';
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
5
|
+
import { ScaffoldError, ERROR_CODES } from './errors.js';
|
|
6
|
+
import { runGit } from './git.js';
|
|
7
|
+
import { assertInside, isProtectedTarget } from './guards.js';
|
|
8
|
+
import { ensureSymlink, ensureSymlinkOrPointer, isSameCaseInsensitiveFile } from './links.js';
|
|
9
|
+
import { makeScriptsExecutable } from './permissions.js';
|
|
10
|
+
import { isInsideGitWorkTree, initGit } from './repo.js';
|
|
11
|
+
/**
|
|
12
|
+
* Canonical list of template files and directories copied into a new workspace.
|
|
13
|
+
*/
|
|
14
|
+
export const TEMPLATE_ITEMS = [
|
|
15
|
+
'AGENTS.md',
|
|
16
|
+
'memory.md',
|
|
17
|
+
'README.md',
|
|
18
|
+
'docs',
|
|
19
|
+
'.agents',
|
|
20
|
+
'.github',
|
|
21
|
+
'scripts',
|
|
22
|
+
'.gitignore',
|
|
23
|
+
'.editorconfig',
|
|
24
|
+
'LICENSE'
|
|
23
25
|
];
|
|
24
|
-
|
|
25
26
|
/**
|
|
26
|
-
*
|
|
27
|
+
* Resolves the root filesystem directory of the azcodr template files.
|
|
28
|
+
*
|
|
29
|
+
* @returns Absolute filesystem path to the template root directory.
|
|
27
30
|
*/
|
|
28
|
-
function getTemplateDir() {
|
|
29
|
-
|
|
31
|
+
export function getTemplateDir() {
|
|
32
|
+
return path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
|
30
33
|
}
|
|
31
|
-
|
|
32
34
|
function rejectTemplateSelf(resolvedTarget, resolvedTemplate) {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
'E_TARGET_IS_TEMPLATE',
|
|
36
|
-
`${ERROR_CODES.E_TARGET_IS_TEMPLATE}: ${resolvedTarget}`
|
|
37
|
-
);
|
|
35
|
+
if (resolvedTarget !== resolvedTemplate)
|
|
36
|
+
return;
|
|
37
|
+
throw new ScaffoldError('E_TARGET_IS_TEMPLATE', `${ERROR_CODES.E_TARGET_IS_TEMPLATE}: ${resolvedTarget}`);
|
|
38
38
|
}
|
|
39
|
-
|
|
40
39
|
function rejectProtectedTarget(resolvedTarget, templateDir, allowProtected) {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
`${ERROR_CODES.E_TARGET_IS_PROTECTED}: ${resolvedTarget}. ` +
|
|
46
|
-
|
|
47
|
-
'Choose a project subdirectory instead.'
|
|
48
|
-
);
|
|
40
|
+
if (allowProtected)
|
|
41
|
+
return;
|
|
42
|
+
if (!isProtectedTarget(resolvedTarget, { templateDir }))
|
|
43
|
+
return;
|
|
44
|
+
throw new ScaffoldError('E_TARGET_IS_PROTECTED', `${ERROR_CODES.E_TARGET_IS_PROTECTED}: ${resolvedTarget}. ` +
|
|
45
|
+
'Scaffolding there would overwrite personal or system files. Choose a project subdirectory instead.');
|
|
49
46
|
}
|
|
50
|
-
|
|
51
47
|
function rejectNonEmptyTarget(resolvedTarget, force) {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
'E_TARGET_NOT_EMPTY',
|
|
56
|
-
|
|
57
|
-
'Use --force to overwrite existing files.'
|
|
58
|
-
);
|
|
48
|
+
const entries = fs.readdirSync(resolvedTarget);
|
|
49
|
+
if (entries.length === 0 || force)
|
|
50
|
+
return;
|
|
51
|
+
throw new ScaffoldError('E_TARGET_NOT_EMPTY', `${ERROR_CODES.E_TARGET_NOT_EMPTY} (${entries.length} items found: ${resolvedTarget}). ` +
|
|
52
|
+
'Use --force to overwrite existing files.');
|
|
59
53
|
}
|
|
60
|
-
|
|
61
54
|
/**
|
|
62
55
|
* Validates the target directory to ensure it is suitable for scaffolding.
|
|
56
|
+
*
|
|
57
|
+
* @param targetDir - Target directory path to validate.
|
|
58
|
+
* @param options - Validation options including force, dry-run, and protected overrides.
|
|
59
|
+
* @throws {ScaffoldError} When target is invalid, protected, or non-empty without force.
|
|
63
60
|
*/
|
|
64
|
-
function validateTarget(targetDir, options = {}) {
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
61
|
+
export function validateTarget(targetDir, options = {}) {
|
|
62
|
+
const { templateDir = getTemplateDir(), force = false, dryRun = false, allowProtected = false } = options;
|
|
63
|
+
const resolvedTarget = path.resolve(targetDir);
|
|
64
|
+
const resolvedTemplate = path.resolve(templateDir);
|
|
65
|
+
rejectTemplateSelf(resolvedTarget, resolvedTemplate);
|
|
66
|
+
rejectProtectedTarget(resolvedTarget, templateDir, allowProtected);
|
|
67
|
+
if (!fs.existsSync(resolvedTarget)) {
|
|
68
|
+
if (!dryRun)
|
|
69
|
+
fs.mkdirSync(resolvedTarget, { recursive: true });
|
|
70
|
+
return;
|
|
73
71
|
}
|
|
74
|
-
|
|
75
|
-
}
|
|
76
|
-
rejectNonEmptyTarget(resolvedTarget, force);
|
|
72
|
+
rejectNonEmptyTarget(resolvedTarget, force);
|
|
77
73
|
}
|
|
78
|
-
|
|
79
74
|
function resolveTemplateItem(resolvedTemplate, item) {
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
75
|
+
let srcPath = path.join(resolvedTemplate, item);
|
|
76
|
+
if (item === '.gitignore' && !fs.existsSync(srcPath)) {
|
|
77
|
+
const npmIgnorePath = path.join(resolvedTemplate, '.npmignore');
|
|
78
|
+
if (fs.existsSync(npmIgnorePath))
|
|
79
|
+
srcPath = npmIgnorePath;
|
|
85
80
|
}
|
|
86
|
-
|
|
87
|
-
return srcPath;
|
|
81
|
+
return srcPath;
|
|
88
82
|
}
|
|
89
|
-
|
|
90
83
|
function copyTemplateItems(resolvedTarget, resolvedTemplate, dryRun) {
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
84
|
+
const actions = [];
|
|
85
|
+
for (const item of TEMPLATE_ITEMS) {
|
|
86
|
+
const srcPath = resolveTemplateItem(resolvedTemplate, item);
|
|
87
|
+
if (!fs.existsSync(srcPath))
|
|
88
|
+
continue;
|
|
89
|
+
const destPath = path.join(resolvedTarget, item);
|
|
90
|
+
assertInside(resolvedTemplate, srcPath);
|
|
91
|
+
assertInside(resolvedTarget, destPath);
|
|
92
|
+
actions.push(`copy: ${item} -> ${destPath}`);
|
|
93
|
+
if (!dryRun)
|
|
94
|
+
fs.cpSync(srcPath, destPath, { recursive: true, force: true, dereference: false });
|
|
101
95
|
}
|
|
102
|
-
|
|
103
|
-
return actions;
|
|
96
|
+
return actions;
|
|
104
97
|
}
|
|
105
|
-
|
|
106
98
|
function ensureHarnessParity(resolvedTarget, dryRun) {
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
99
|
+
const links = [
|
|
100
|
+
['CLAUDE.md', 'AGENTS.md'],
|
|
101
|
+
['agents.md', 'AGENTS.md'],
|
|
102
|
+
['GEMINI.md', 'AGENTS.md'],
|
|
103
|
+
['.cursorrules', 'AGENTS.md'],
|
|
104
|
+
['.windsurfrules', 'AGENTS.md']
|
|
105
|
+
];
|
|
106
|
+
const actions = [];
|
|
107
|
+
for (const [linkName, targetFileName] of links) {
|
|
108
|
+
actions.push(`symlink: ${linkName} -> ${targetFileName}`);
|
|
109
|
+
ensureSymlink({ targetDir: resolvedTarget, linkName, targetFileName, dryRun });
|
|
110
|
+
}
|
|
111
|
+
return actions;
|
|
120
112
|
}
|
|
121
|
-
|
|
122
113
|
function ensureCopilotParity(resolvedTarget, dryRun) {
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
return ['symlink: .github/copilot-instructions.md -> ../AGENTS.md'];
|
|
114
|
+
const githubDir = path.join(resolvedTarget, '.github');
|
|
115
|
+
if (!dryRun && !fs.existsSync(githubDir))
|
|
116
|
+
fs.mkdirSync(githubDir, { recursive: true });
|
|
117
|
+
ensureSymlinkOrPointer({
|
|
118
|
+
targetDir: githubDir,
|
|
119
|
+
linkName: 'copilot-instructions.md',
|
|
120
|
+
targetFileName: '../AGENTS.md',
|
|
121
|
+
dryRun
|
|
122
|
+
});
|
|
123
|
+
return ['symlink: .github/copilot-instructions.md -> ../AGENTS.md'];
|
|
134
124
|
}
|
|
135
|
-
|
|
136
125
|
function starterPackageJson(resolvedTarget) {
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
126
|
+
const projectName = path.basename(resolvedTarget) || 'my-project';
|
|
127
|
+
return {
|
|
128
|
+
name: projectName,
|
|
129
|
+
version: '0.1.0',
|
|
130
|
+
type: 'module',
|
|
131
|
+
private: true,
|
|
132
|
+
description: 'Scaffolded with azcodr enterprise architecture template',
|
|
133
|
+
scripts: {
|
|
134
|
+
test: 'node --test',
|
|
135
|
+
'test:coverage': 'jest --coverage',
|
|
136
|
+
lint: 'echo "No linter configured yet. Run /lets-build to configure toolchain."',
|
|
137
|
+
validate: 'node scripts/validate-cli.js'
|
|
138
|
+
}
|
|
139
|
+
};
|
|
150
140
|
}
|
|
151
|
-
|
|
152
141
|
function ensureStarterPackageJson(resolvedTarget, dryRun) {
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
142
|
+
const pkgJsonPath = path.join(resolvedTarget, 'package.json');
|
|
143
|
+
if (fs.existsSync(pkgJsonPath))
|
|
144
|
+
return [];
|
|
145
|
+
if (dryRun)
|
|
146
|
+
return ['create: package.json'];
|
|
147
|
+
const starterPkg = starterPackageJson(resolvedTarget);
|
|
148
|
+
fs.writeFileSync(pkgJsonPath, JSON.stringify(starterPkg, null, 2) + '\n', 'utf-8');
|
|
149
|
+
return ['create: package.json'];
|
|
159
150
|
}
|
|
160
|
-
|
|
161
151
|
function chmodTemplateScripts(inspectDir, dryRun) {
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
152
|
+
const actions = [];
|
|
153
|
+
const scripts = makeScriptsExecutable(inspectDir, dryRun);
|
|
154
|
+
for (const script of scripts) {
|
|
155
|
+
actions.push(`chmod: +x ${path.relative(inspectDir, script)}`);
|
|
156
|
+
}
|
|
157
|
+
return actions;
|
|
168
158
|
}
|
|
169
|
-
|
|
170
159
|
/**
|
|
171
160
|
* Recursively copies template files into the target directory and sets up symlinks and permissions.
|
|
172
161
|
* Socket note: filesystem access is scoped to templateDir -> targetDir only (assertInside).
|
|
162
|
+
*
|
|
163
|
+
* @param targetDir - Destination directory path.
|
|
164
|
+
* @param templateDir - Source template directory path.
|
|
165
|
+
* @param options - Copy options including dry-run flag.
|
|
166
|
+
* @returns Array of recorded scaffolding action descriptions.
|
|
173
167
|
*/
|
|
174
|
-
function copyTemplate(targetDir, templateDir = getTemplateDir(), options = {}) {
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
actions.push(...chmodTemplateScripts(inspectDir, dryRun));
|
|
189
|
-
return actions;
|
|
168
|
+
export function copyTemplate(targetDir, templateDir = getTemplateDir(), options = {}) {
|
|
169
|
+
const { dryRun = false } = options;
|
|
170
|
+
const resolvedTarget = path.resolve(targetDir);
|
|
171
|
+
const resolvedTemplate = path.resolve(templateDir);
|
|
172
|
+
if (!dryRun && !fs.existsSync(resolvedTarget)) {
|
|
173
|
+
fs.mkdirSync(resolvedTarget, { recursive: true });
|
|
174
|
+
}
|
|
175
|
+
const actions = copyTemplateItems(resolvedTarget, resolvedTemplate, dryRun);
|
|
176
|
+
actions.push(...ensureHarnessParity(resolvedTarget, dryRun));
|
|
177
|
+
actions.push(...ensureCopilotParity(resolvedTarget, dryRun));
|
|
178
|
+
actions.push(...ensureStarterPackageJson(resolvedTarget, dryRun));
|
|
179
|
+
const inspectDir = dryRun ? resolvedTemplate : resolvedTarget;
|
|
180
|
+
actions.push(...chmodTemplateScripts(inspectDir, dryRun));
|
|
181
|
+
return actions;
|
|
190
182
|
}
|
|
191
|
-
|
|
192
183
|
/**
|
|
193
184
|
* High-level orchestration function to scaffold the azcodr workspace into targetDir.
|
|
185
|
+
*
|
|
186
|
+
* @param options - Scaffolding options.
|
|
187
|
+
* @returns Result object containing success flag, target directory, and action log.
|
|
194
188
|
*/
|
|
195
|
-
function scaffold(options = {}) {
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
force
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
const resolvedTarget = path.resolve(targetDir);
|
|
205
|
-
validateTarget(resolvedTarget, { templateDir, force, dryRun });
|
|
206
|
-
const actions = copyTemplate(resolvedTarget, templateDir, { dryRun });
|
|
207
|
-
const gitInitialized = initGit(resolvedTarget, { noGit, dryRun });
|
|
208
|
-
if (gitInitialized) {
|
|
209
|
-
actions.push('git: initialize repository');
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
return {
|
|
213
|
-
success: true,
|
|
214
|
-
targetDir: resolvedTarget,
|
|
215
|
-
gitInitialized,
|
|
216
|
-
dryRun,
|
|
217
|
-
actions
|
|
218
|
-
};
|
|
189
|
+
export function scaffold(options = {}) {
|
|
190
|
+
const { targetDir = process.cwd(), force = false, noGit = false, templateDir = getTemplateDir(), dryRun = false } = options;
|
|
191
|
+
const resolvedTarget = path.resolve(targetDir);
|
|
192
|
+
validateTarget(resolvedTarget, { templateDir, force, dryRun });
|
|
193
|
+
const actions = copyTemplate(resolvedTarget, templateDir, { dryRun });
|
|
194
|
+
const gitInitialized = initGit(resolvedTarget, { noGit, dryRun });
|
|
195
|
+
if (gitInitialized)
|
|
196
|
+
actions.push('git: initialize repository');
|
|
197
|
+
return { success: true, targetDir: resolvedTarget, gitInitialized, dryRun, actions };
|
|
219
198
|
}
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
ensureSymlinkOrPointer,
|
|
227
|
-
isSameCaseInsensitiveFile,
|
|
228
|
-
makeScriptsExecutable,
|
|
229
|
-
isInsideGitWorkTree,
|
|
230
|
-
initGit,
|
|
231
|
-
getTemplateDir,
|
|
232
|
-
runGit,
|
|
233
|
-
assertInside,
|
|
234
|
-
TEMPLATE_ITEMS,
|
|
235
|
-
ScaffoldError,
|
|
236
|
-
ERROR_CODES,
|
|
237
|
-
isProtectedTarget
|
|
199
|
+
export { ensureSymlink, ensureSymlinkOrPointer, isSameCaseInsensitiveFile, makeScriptsExecutable, isInsideGitWorkTree, initGit, runGit, assertInside, ScaffoldError, ERROR_CODES, isProtectedTarget };
|
|
200
|
+
export default {
|
|
201
|
+
scaffold, validateTarget, copyTemplate, ensureSymlink,
|
|
202
|
+
ensureSymlinkOrPointer, isSameCaseInsensitiveFile, makeScriptsExecutable,
|
|
203
|
+
isInsideGitWorkTree, initGit, getTemplateDir, runGit,
|
|
204
|
+
assertInside, TEMPLATE_ITEMS, ScaffoldError, ERROR_CODES, isProtectedTarget
|
|
238
205
|
};
|
|
206
|
+
//# sourceMappingURL=scaffold.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scaffold.js","sourceRoot":"","sources":["../src/scaffold.ts"],"names":[],"mappings":"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"}
|
package/memory.md
CHANGED
|
@@ -29,6 +29,9 @@
|
|
|
29
29
|
| [ADR-010](./memory.md#adr-010-the-scaffolder-refuses-protected-targets) | The scaffolder refuses protected targets | 2026-10-07 | ACCEPTED | [`security_compliance.md`](./docs/rules/security_compliance.md) |
|
|
30
30
|
| [ADR-011](./memory.md#adr-011-the-bash-scaffolder-refuses-protected-targets) | The bash scaffolder refuses protected targets | 2026-10-07 | ACCEPTED | [`security_compliance.md`](./docs/rules/security_compliance.md) |
|
|
31
31
|
| [ADR-012](./memory.md#adr-012-enforce-the-file-and-function-gates-the-repo-prescribes) | Enforce the file and function gates the repo prescribes | 2026-10-07 | ACCEPTED | [`clean_code.md`](./docs/rules/clean_code.md) |
|
|
32
|
+
| [ADR-013](./memory.md#adr-013-native-esm-port-and-jsr-package-publishing) | Native ESM port and JSR package publishing | 2026-10-07 | ACCEPTED | [`devops_ci_cd.md`](./docs/rules/devops_ci_cd.md) |
|
|
33
|
+
| [ADR-014](./memory.md#adr-014-author-in-typescript-with-tsc-compilation-to-native-esm) | Author in TypeScript with tsc compilation to native ESM | 2026-10-07 | ACCEPTED | [`type_safety.md`](./docs/rules/type_safety.md) |
|
|
34
|
+
| [ADR-015](./memory.md#adr-015-consolidate-testing-on-jest-with-typescript-test-migration) | Consolidate testing on Jest with TypeScript test migration | 2026-10-08 | ACCEPTED | [`test_driven_development.md`](./docs/rules/test_driven_development.md) |
|
|
32
35
|
|
|
33
36
|
---
|
|
34
37
|
|
|
@@ -55,6 +58,27 @@
|
|
|
55
58
|
- **Consequences:** ✅ 87 lint errors → 0, and `npm run lint` now fails the build on any new violation instead of parsing syntax. ✅ Tests went 397 → 412 with zero lost (reconciled per file against HEAD; the +15 is one new stat-failure test, one pre-existing duplicate preserved verbatim, and loop-generated cases from splits). ✅ Coverage gates hold across all 17 source files. ⚠️ Contributors now run `npm install` (dev-only); consumers are unaffected. ⚠️ 2.0.0 is semver-major solely for the two options-object signatures; the CLI surface is unchanged.
|
|
56
59
|
- **Enforced In:** `eslint.config.js`, `package-lock.json`, `lib/`, `scripts/validate/`, `lib/cli-*.js`, `tests/architecture.test.js`, `tests/coverage-config.test.js`, `package.json` (`lint`, `version`)
|
|
57
60
|
|
|
61
|
+
#### ADR-013: Native ESM Port and JSR Package Publishing
|
|
62
|
+
- **Date:** 2026-10-07 | **Status:** ACCEPTED
|
|
63
|
+
- **Context:** ADR-008 blocked JSR publishing because JSR strictly requires native ECMAScript Modules (ESM) and prohibits CommonJS (`module.exports`, `require`). An adversarial review proved that synthetic CJS-to-ESM shims pass `jsr publish --dry-run` but crash on consumer import in pure ESM/Deno environments. Publishing to both npm and JSR (`@azcodr/azcodr`) required migrating the codebase to native ESM (`"type": "module"`).
|
|
64
|
+
- **Decision:** (1) Author the entire codebase as native ESM (`"type": "module"` in `package.json`). All 57 `.js` files across `bin/`, `lib/`, `scripts/`, and `tests/` migrated to ESM `import`/`export` with explicit `.js` extensions, `import.meta.dirname`, and `node:` protocol builtins. (2) Zero-build architecture preserved: no transpilers, bundlers, or runtime dependencies required. Exact-pinned `typescript` (`5.7.3`) and `@types/node` (`22.13.10`) added as devDependencies for typechecking and JSR typings. (3) Root `jsr.json` authored with `@azcodr/azcodr` scope, pointing to `./lib/index.js` with `// @ts-self-types` reference to `./lib/index.d.ts`. (4) Re-enabled `publish-jsr` job in `.github/workflows/publish.yml` with provenance attestation.
|
|
65
|
+
- **Consequences:** ✅ Full cross-runtime parity: runs identically on Node.js (>=22.8.0), Deno, and JSR without bundlers. ✅ JSR package `@azcodr/azcodr` verified with 0 errors/warnings on `jsr publish --dry-run`. ✅ 100% test coverage and Clean Code fitness functions preserved across all files. ✅ npm consumers consume modern native ESM.
|
|
66
|
+
- **Enforced In:** `package.json`, `jsr.json`, `tsconfig.json`, `lib/`, `bin/azcodr.js`, `scripts/`, `tests/`, `.github/workflows/publish.yml`
|
|
67
|
+
|
|
68
|
+
#### ADR-014: Author in TypeScript with tsc Compilation to Native ESM
|
|
69
|
+
- **Date:** 2026-10-07 | **Status:** ACCEPTED
|
|
70
|
+
- **Context:** Following the native ESM migration (ADR-013), authoring in pure JavaScript required manually synchronizing hand-written `.d.ts` declaration files, which risks subtle divergence between runtime behavior and published type contracts. Furthermore, JSR `@azcodr/azcodr` requires strict, complete type exports without slow types. Direct authoring in strict TypeScript (`src/`) with native `tsc` compilation to `lib/` delivers end-to-end type safety, automated type declaration generation, and full JSR/npm dual publishing capability while maintaining zero runtime dependencies.
|
|
71
|
+
- **Decision:** (1) Author all 11 core modules in `src/` as 100% strict TypeScript (`.ts`) complying with `clean_code.md` fitness functions (file length ≤ 300, function length ≤ 30, complexity ≤ 10, max params ≤ 3), explicit `.js` import extensions for NodeNext resolution, and `node:` builtins. (2) Configure `tsconfig.json` with `target: ES2022`, `module: NodeNext`, `moduleResolution: NodeNext`, `rootDir: src`, `outDir: lib`, `declaration: true`, `declarationMap: true`, `sourceMap: true`, and strict compiler flags (`noImplicitAny`, `strictNullChecks`, `noUncheckedIndexedAccess`). (3) Configure `package.json` with `"build": "tsc"`, `"typecheck": "tsc --noEmit"`, `"test:jest": "jest --coverage"`, and update `"prepublishOnly"` to build before testing and validating. Exact-pin devDependencies (`typescript: 5.7.3`, `@types/node: 22.13.10`, `jest: 29.7.0`, `@types/jest: 29.5.14`, `ts-jest: 29.2.6`) and commit `package-lock.json`. (4) Configure `jsr.json` with `"exports": "./src/index.ts"` and `"publish.include": ["src", "README.md", "LICENSE"]`, verified via `jsr publish --dry-run` with 0 slow types and 0 errors. (5) Configure Jest in `jest.config.js` with auto-elevation for `--experimental-vm-modules` to support dual test runners (`node --test` for native suites and Jest for developer workflows).
|
|
72
|
+
- **Consequences:** ✅ 100% strict type coverage across all 11 modules (`src/*.ts`). ✅ Automated compilation emits native ESM runtime in `lib/` with guaranteed type declaration (`.d.ts`) parity. ✅ Zero runtime dependencies invariant preserved. ✅ 100.00% line & function coverage maintained with 416 passing tests. ✅ JSR publish dry-run passes with zero errors and zero slow types.
|
|
73
|
+
- **Enforced In:** `tsconfig.json`, `jsr.json`, `package.json`, `jest.config.js`, `src/`, `lib/`, `tests/`
|
|
74
|
+
|
|
75
|
+
#### ADR-015: Consolidate Testing on Jest with TypeScript Test Migration
|
|
76
|
+
- **Date:** 2026-10-08 | **Status:** ACCEPTED
|
|
77
|
+
- **Context:** Following the TypeScript migration (ADR-014), the repository ran a fragmented dual test runner architecture: native `node:test` via `scripts/test_coverage.js` evaluating compiled JavaScript artifacts in `lib/`, and Jest for developer workflows and specs. Testing compiled output disconnected tests from authored TypeScript source definitions, required manual synchronization between compilation and testing, and prevented strict compile-time type verification of test suites (`tsc --noEmit` did not typecheck `tests/`). Furthermore, `scripts/test_coverage.js` imposed brittle Node version gating (`>=22.8.0`) for experimental V8 flags.
|
|
78
|
+
- **Decision:** (1) Consolidate 100% of testing and coverage on Jest (`jest: 29.7.0`, `ts-jest: 29.2.6`). Retire and delete `scripts/test_coverage.js` and `npm run test:jest`. Update `package.json` so `test` and `test:coverage` invoke Jest with `--experimental-vm-modules`. (2) Migrate all 35 test suites from JavaScript (`.test.js` / `.spec.ts`) to TypeScript (`tests/**/*.test.ts`) with strict typing, Jest lifecycle globals, and typed mocks. (3) Configure `tsconfig.json` with `"noEmit": true`, `"allowJs": true`, and include `["src/**/*", "tests/**/*"]` for comprehensive `npm run typecheck` coverage across both source and tests. Create `tsconfig.build.json` with `rootDir: src` and `outDir: lib` to strictly isolate production compilation from test suites. (4) Configure `jest.config.js` to collect coverage directly from authored TypeScript source and validator scripts (`['src/**/*.ts', 'scripts/validate/**/*.js', 'scripts/validate.js']`), enforcing strict global thresholds: 100% lines, 97% functions, and 98% branches. (5) Update `src/scaffold.ts` and `lib/scaffold.js` to align starter template configurations with the consolidated Jest and TypeScript runner.
|
|
79
|
+
- **Consequences:** ✅ Unified, single-tool test runner across developer and CI pipelines. ✅ End-to-end type safety: `tsc --noEmit` verifies all source and 35 test files without error. ✅ Direct source instrumentation: 100.00% line coverage, 100.00% function coverage, and 98.44% branch coverage achieved on authored TypeScript (`src/`) and scripts. ✅ Clean build separation: `npm run build` strictly emits compiled production code in `lib/` without test artifacts. ✅ Zero external runtime dependencies preserved.
|
|
80
|
+
- **Enforced In:** `package.json`, `tsconfig.json`, `tsconfig.build.json`, `jest.config.js`, `tests/**/*.test.ts`, `src/scaffold.ts`, `lib/scaffold.js`
|
|
81
|
+
|
|
58
82
|
#### ADR-011: The Bash Scaffolder Refuses Protected Targets
|
|
59
83
|
- **Date:** 2026-10-07 | **Status:** ACCEPTED
|
|
60
84
|
- **Context:** ADR-010 closed the protected-target hole for the Node scaffolder (`lib/scaffold.js`), but the same hole existed in `.agents/skills/lets-build/scripts/bootstrap_workspace.sh`, which runs with no guard at all: invoked with `/` or `~` as the workspace root it would create `src/`, `tests/`, `deploy/`, `specs/` trees there. Its blast radius is smaller than the Node scaffolder's (every file write is create-only — `tokens.json` and `smoke_test.sh` are written only if absent, and `memory.md` is append-only since ADR-009 — except the explicit `AZCODR_RESET_MEMORY=1` reset), so this is pollution rather than destruction. Pollution of `/` or `~` is still unacceptable for a script agents run autonomously.
|
package/package.json
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "azcodr",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.2.0",
|
|
4
4
|
"description": "Enterprise Architecture & Agentic Engineering Starter Template",
|
|
5
|
+
"type": "module",
|
|
5
6
|
"bin": {
|
|
6
7
|
"azcodr": "bin/azcodr.js"
|
|
7
8
|
},
|
|
@@ -17,6 +18,7 @@
|
|
|
17
18
|
"files": [
|
|
18
19
|
"bin",
|
|
19
20
|
"lib",
|
|
21
|
+
"src",
|
|
20
22
|
"scripts",
|
|
21
23
|
"data",
|
|
22
24
|
".github",
|
|
@@ -53,13 +55,20 @@
|
|
|
53
55
|
"node": ">=22.8.0"
|
|
54
56
|
},
|
|
55
57
|
"scripts": {
|
|
56
|
-
"
|
|
57
|
-
"test
|
|
58
|
+
"build": "tsc -p tsconfig.build.json",
|
|
59
|
+
"test": "node --experimental-vm-modules node_modules/jest/bin/jest.js",
|
|
60
|
+
"test:coverage": "node --experimental-vm-modules node_modules/jest/bin/jest.js --coverage",
|
|
58
61
|
"lint": "eslint .",
|
|
62
|
+
"typecheck": "tsc --noEmit",
|
|
59
63
|
"validate": "node scripts/validate-cli.js",
|
|
60
|
-
"prepublishOnly": "npm run lint && npm run test:coverage && npm run validate"
|
|
64
|
+
"prepublishOnly": "npm run build && npm run lint && npm run test:coverage && npm run validate"
|
|
61
65
|
},
|
|
62
66
|
"devDependencies": {
|
|
63
|
-
"
|
|
67
|
+
"@types/jest": "29.5.14",
|
|
68
|
+
"@types/node": "22.13.10",
|
|
69
|
+
"eslint": "10.12.0",
|
|
70
|
+
"jest": "29.7.0",
|
|
71
|
+
"ts-jest": "29.2.6",
|
|
72
|
+
"typescript": "5.7.3"
|
|
64
73
|
}
|
|
65
74
|
}
|