@jakkrichm/create-nexus-devflow 2.0.13 → 2.0.14

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 (52) hide show
  1. package/dist/bin/create-nexus-devflow.d.ts +2 -0
  2. package/dist/bin/create-nexus-devflow.js +233 -0
  3. package/dist/bin/create-nexus-devflow.js.map +1 -0
  4. package/dist/lib/starter-templates.d.ts +6 -0
  5. package/dist/lib/starter-templates.js +8 -0
  6. package/dist/lib/starter-templates.js.map +1 -0
  7. package/dist/lib/update.d.ts +68 -0
  8. package/dist/lib/update.js +325 -0
  9. package/dist/lib/update.js.map +1 -0
  10. package/dist/scripts/clean-template.d.ts +1 -0
  11. package/dist/scripts/clean-template.js +14 -0
  12. package/dist/scripts/clean-template.js.map +1 -0
  13. package/dist/scripts/prepare-template.d.ts +1 -0
  14. package/dist/scripts/prepare-template.js +89 -0
  15. package/dist/scripts/prepare-template.js.map +1 -0
  16. package/package.json +11 -9
  17. package/template/.agents/skills/00-discover/SKILL.md +2 -0
  18. package/template/.agents/skills/60-report/SKILL.md +21 -75
  19. package/template/.agents/skills/70-release/SKILL.md +5 -4
  20. package/template/.agents/skills/check/SKILL.md +68 -0
  21. package/template/.agents/skills/complete/SKILL.md +73 -0
  22. package/template/.agents/skills/devflow/SKILL.md +89 -80
  23. package/template/.agents/skills/idea/SKILL.md +57 -0
  24. package/template/.agents/skills/implement/SKILL.md +64 -0
  25. package/template/.agents/skills/overview/SKILL.md +114 -0
  26. package/template/.agents/skills/report-html/SKILL.md +45 -0
  27. package/template/.agents/skills/spec/SKILL.md +105 -0
  28. package/template/.claude/skills/00-discover/SKILL.md +2 -0
  29. package/template/.claude/skills/60-report/SKILL.md +21 -75
  30. package/template/.claude/skills/70-release/SKILL.md +5 -4
  31. package/template/.claude/skills/check/SKILL.md +68 -0
  32. package/template/.claude/skills/complete/SKILL.md +73 -0
  33. package/template/.claude/skills/devflow/SKILL.md +89 -80
  34. package/template/.claude/skills/idea/SKILL.md +57 -0
  35. package/template/.claude/skills/implement/SKILL.md +64 -0
  36. package/template/.claude/skills/overview/SKILL.md +114 -0
  37. package/template/.claude/skills/report-html/SKILL.md +45 -0
  38. package/template/.claude/skills/spec/SKILL.md +105 -0
  39. package/template/AGENTS.md +71 -74
  40. package/template/devflow/context/ai-interaction.md +1 -0
  41. package/template/devflow/context/coding-standards.md +31 -18
  42. package/template/devflow/context/current-stage.md +3 -3
  43. package/template/devflow/context/findings.md +10 -6
  44. package/template/devflow/context/project-overview.md +29 -11
  45. package/template/devflow/discoveries/.gitkeep +0 -0
  46. package/template/devflow/history/HISTORY.md +4 -0
  47. package/template/devflow/ideas.md +15 -0
  48. package/template/devflow/runs/.gitkeep +0 -0
  49. package/bin/create-nexus-devflow.js +0 -287
  50. package/lib/starter-templates.js +0 -111
  51. package/lib/update.js +0 -393
  52. package/template/.nexus/nexus-devflow.json +0 -44
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
@@ -0,0 +1,233 @@
1
+ #!/usr/bin/env node
2
+ import fsSync from "node:fs";
3
+ import path from "node:path";
4
+ import readline from "node:readline/promises";
5
+ import { fileURLToPath } from "node:url";
6
+ import { applyPreparedUpdate, prepareUpdate } from "../lib/update.js";
7
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
8
+ const packageRoot = path.resolve(__dirname, "..", "..");
9
+ const templateRoot = path.join(packageRoot, "template");
10
+ const adapterChoices = new Set(["codex", "antigravity", "claude", "both", "all"]);
11
+ async function main() {
12
+ const options = parseArgs(process.argv.slice(2));
13
+ if (options.help) {
14
+ printHelp();
15
+ return;
16
+ }
17
+ if (options.version) {
18
+ console.log(readPackageVersion());
19
+ return;
20
+ }
21
+ if (!fsSync.existsSync(templateRoot)) {
22
+ throw new Error("Installer template is missing. Run `npm run prepare-template` before local testing.");
23
+ }
24
+ const targetDir = path.resolve(process.cwd(), options.target || ".");
25
+ const version = readPackageVersion();
26
+ if (options.command === "update") {
27
+ const prepared = await prepareUpdate({
28
+ targetDir,
29
+ templateRoot,
30
+ version,
31
+ adapter: options.adapter
32
+ });
33
+ printUpdatePlan(prepared);
34
+ if (options.dryRun) {
35
+ return;
36
+ }
37
+ const replaceConflicts = options.force || (await confirmUpdateConflicts(prepared, options));
38
+ const result = await applyPreparedUpdate(prepared, { replaceConflicts });
39
+ printUpdateSuccess(prepared, result);
40
+ return;
41
+ }
42
+ const prepared = await prepareUpdate({
43
+ targetDir,
44
+ templateRoot,
45
+ version,
46
+ adapter: options.adapter
47
+ });
48
+ printInstallPlan(prepared);
49
+ if (options.dryRun) {
50
+ return;
51
+ }
52
+ if (prepared.conflictList.length > 0 && !options.force) {
53
+ const proceed = await confirmInstallConflicts(prepared, options);
54
+ if (!proceed) {
55
+ console.log("Install cancelled.");
56
+ return;
57
+ }
58
+ }
59
+ const result = await applyPreparedUpdate(prepared, {
60
+ replaceConflicts: options.force || prepared.conflictList.length > 0
61
+ });
62
+ printInstallSuccess(targetDir, result);
63
+ }
64
+ function parseArgs(args) {
65
+ let command = "install";
66
+ let target = null;
67
+ let adapter = "both";
68
+ let force = false;
69
+ let dryRun = false;
70
+ let help = false;
71
+ let version = false;
72
+ let yes = false;
73
+ const positional = [];
74
+ for (let i = 0; i < args.length; i++) {
75
+ const arg = args[i];
76
+ if (arg === "--help" || arg === "-h") {
77
+ help = true;
78
+ continue;
79
+ }
80
+ if (arg === "--version" || arg === "-v") {
81
+ version = true;
82
+ continue;
83
+ }
84
+ if (arg === "--force" || arg === "-f") {
85
+ force = true;
86
+ continue;
87
+ }
88
+ if (arg === "--dry-run") {
89
+ dryRun = true;
90
+ continue;
91
+ }
92
+ if (arg === "-y" || arg === "--yes") {
93
+ yes = true;
94
+ continue;
95
+ }
96
+ if (arg === "--adapter") {
97
+ const value = args[++i];
98
+ if (!value || !adapterChoices.has(value.toLowerCase())) {
99
+ throw new Error(`Invalid --adapter value "${value}". Expected one of: codex, antigravity, claude, both, all`);
100
+ }
101
+ adapter = value.toLowerCase();
102
+ continue;
103
+ }
104
+ if (arg.startsWith("--adapter=")) {
105
+ const value = arg.slice("--adapter=".length);
106
+ if (!adapterChoices.has(value.toLowerCase())) {
107
+ throw new Error(`Invalid --adapter value "${value}". Expected one of: codex, antigravity, claude, both, all`);
108
+ }
109
+ adapter = value.toLowerCase();
110
+ continue;
111
+ }
112
+ if (arg.startsWith("-")) {
113
+ throw new Error(`Unknown option "${arg}". Use --help to see available options.`);
114
+ }
115
+ positional.push(arg);
116
+ }
117
+ if (positional.length > 0) {
118
+ if (positional[0] === "update") {
119
+ command = "update";
120
+ target = positional[1] || ".";
121
+ }
122
+ else {
123
+ target = positional[0];
124
+ }
125
+ }
126
+ return {
127
+ command,
128
+ target,
129
+ adapter,
130
+ force,
131
+ dryRun,
132
+ help,
133
+ version,
134
+ yes
135
+ };
136
+ }
137
+ function readPackageVersion() {
138
+ const pkgPath = path.join(packageRoot, "package.json");
139
+ const content = fsSync.readFileSync(pkgPath, "utf8");
140
+ return JSON.parse(content).version;
141
+ }
142
+ function printHelp() {
143
+ console.log(`
144
+ Nexus-DevFlow Installer v${readPackageVersion()}
145
+
146
+ Usage:
147
+ npx @jakkrichm/create-nexus-devflow [target-dir] [options]
148
+ npx @jakkrichm/create-nexus-devflow update [target-dir] [options]
149
+
150
+ Options:
151
+ --adapter <name> Tool adapters to install: codex, antigravity, claude, both (default: both)
152
+ --force, -f Overwrite conflicting files without prompting
153
+ --dry-run Preview changes without modifying disk
154
+ -y, --yes Automatically confirm interactive prompts
155
+ --version, -v Show version number
156
+ --help, -h Show help screen
157
+ `);
158
+ }
159
+ function printInstallPlan(prepared) {
160
+ console.log(`\nNexus-DevFlow v${readPackageVersion()}`);
161
+ console.log(`Target Directory: ${prepared.targetDir}`);
162
+ console.log(`Active Adapters : ${prepared.activeAdapters.join(", ")}\n`);
163
+ console.log(`Files to create : ${prepared.createList.length}`);
164
+ console.log(`Files to update : ${prepared.updateList.length}`);
165
+ console.log(`Conflicts found : ${prepared.conflictList.length}`);
166
+ if (prepared.conflictList.length > 0) {
167
+ console.log("\nConflicting files:");
168
+ for (const conflict of prepared.conflictList) {
169
+ console.log(` - ${conflict.relativePath} (${conflict.detail})`);
170
+ }
171
+ }
172
+ }
173
+ function printUpdatePlan(prepared) {
174
+ console.log(`\nNexus-DevFlow Update Plan (v${readPackageVersion()})`);
175
+ console.log(`Target Directory: ${prepared.targetDir}`);
176
+ console.log(`Active Adapters : ${prepared.activeAdapters.join(", ")}\n`);
177
+ console.log(`Files to create : ${prepared.createList.length}`);
178
+ console.log(`Files to update : ${prepared.updateList.length}`);
179
+ console.log(`Orphaned files : ${prepared.orphanedFiles.length}`);
180
+ console.log(`Conflicts found : ${prepared.conflictList.length}`);
181
+ }
182
+ async function confirmInstallConflicts(prepared, options) {
183
+ if (options.yes)
184
+ return true;
185
+ const rl = readline.createInterface({
186
+ input: process.stdin,
187
+ output: process.stdout
188
+ });
189
+ const answer = await rl.question(`\nOverwrite ${prepared.conflictList.length} conflicting file(s)? [y/N] `);
190
+ rl.close();
191
+ return answer.trim().toLowerCase() === "y";
192
+ }
193
+ async function confirmUpdateConflicts(prepared, options) {
194
+ if (options.yes)
195
+ return true;
196
+ const rl = readline.createInterface({
197
+ input: process.stdin,
198
+ output: process.stdout
199
+ });
200
+ const answer = await rl.question(`\nOverwrite ${prepared.conflictList.length} customized file(s) with update? [y/N] `);
201
+ rl.close();
202
+ return answer.trim().toLowerCase() === "y";
203
+ }
204
+ function printNextSteps() {
205
+ console.log("\nNext steps in your AI IDE (Antigravity, Claude Code, Codex, etc.):");
206
+ console.log(" 1. Project Setup & Baseline:");
207
+ console.log(" - Existing project : Run `/adopt` (or `$adopt`) to scan codebase and bootstrap context.");
208
+ console.log(" - Fresh project : Run `/onboard` (or `$onboard`) to configure project baseline.");
209
+ console.log(" 2. System Health & CI:");
210
+ console.log(" - Health check : Run `/doctor` (or `$doctor`) to verify adapters and setup.");
211
+ console.log(" - CI configuration : Run `/ci` (or `$ci`) to setup GitHub Actions workflow.");
212
+ console.log(" 3. Delivery Flow:");
213
+ console.log(" - Interactive guide: Run `/devflow` (or `$devflow`) for state & routing assistance.");
214
+ console.log(" - Start new work : Run `/00-discover` (or `$00-discover`) to begin delivery lifecycle.");
215
+ }
216
+ function printInstallSuccess(targetDir, result) {
217
+ console.log("\nNexus-DevFlow overlay successfully installed!");
218
+ console.log(`Applied ${result.appliedCount} file(s).`);
219
+ printNextSteps();
220
+ }
221
+ function printUpdateSuccess(prepared, result) {
222
+ console.log("\nNexus-DevFlow update successfully applied!");
223
+ console.log(`Applied ${result.appliedCount} file(s), removed ${result.removedCount} orphaned file(s).`);
224
+ printNextSteps();
225
+ }
226
+ if (process.argv[1] &&
227
+ fsSync.realpathSync(process.argv[1]) === fsSync.realpathSync(fileURLToPath(import.meta.url))) {
228
+ main().catch((err) => {
229
+ console.error(`\nError: ${err instanceof Error ? err.message : String(err)}`);
230
+ process.exit(1);
231
+ });
232
+ }
233
+ //# sourceMappingURL=create-nexus-devflow.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create-nexus-devflow.js","sourceRoot":"","sources":["../../bin/create-nexus-devflow.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ export interface StarterTemplate {
2
+ name: string;
3
+ description: string;
4
+ files: Record<string, string>;
5
+ }
6
+ export declare const starterTemplates: Record<string, StarterTemplate>;
@@ -0,0 +1,8 @@
1
+ export const starterTemplates = {
2
+ default: {
3
+ name: "default",
4
+ description: "Default Nexus-DevFlow workspace configuration",
5
+ files: {}
6
+ }
7
+ };
8
+ //# sourceMappingURL=starter-templates.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"starter-templates.js","sourceRoot":"","sources":["../../lib/starter-templates.ts"],"names":[],"mappings":"AAMA,MAAM,CAAC,MAAM,gBAAgB,GAAoC;IAC/D,OAAO,EAAE;QACP,IAAI,EAAE,SAAS;QACf,WAAW,EAAE,+CAA+C;QAC5D,KAAK,EAAE,EAAE;KACV;CACF,CAAC"}
@@ -0,0 +1,68 @@
1
+ export declare const CONTROL_DIR = ".nexus";
2
+ export declare const MANIFEST_PATH = ".nexus/nexus-devflow.json";
3
+ export declare const MANIFEST_SCHEMA_VERSION = 1;
4
+ export declare const MANAGED_ROOTS: Record<string, string[]>;
5
+ export interface Manifest {
6
+ schemaVersion: number;
7
+ name: string;
8
+ package: string;
9
+ version: string;
10
+ repository: string;
11
+ artifactLanguage: string;
12
+ adapters: string[];
13
+ workspace: {
14
+ contextDir: string;
15
+ historyDir: string;
16
+ referenceDir: string;
17
+ runsDir: string;
18
+ discoveriesDir: string;
19
+ };
20
+ lifecycle: {
21
+ mainlineStages: string[];
22
+ companionCommands: string[];
23
+ };
24
+ managedFiles: Record<string, string>;
25
+ }
26
+ export interface TemplateFile {
27
+ source: string;
28
+ hash: string;
29
+ }
30
+ export interface Conflict {
31
+ relativePath: string;
32
+ reason: "symlink" | "not_file" | "customized";
33
+ detail: string;
34
+ }
35
+ export interface PreparedUpdate {
36
+ targetDir: string;
37
+ templateRoot: string;
38
+ previousManifest: Manifest | null;
39
+ nextManifest: Manifest;
40
+ activeAdapters: string[];
41
+ templateFiles: Map<string, TemplateFile>;
42
+ createList: string[];
43
+ updateList: string[];
44
+ conflictList: Conflict[];
45
+ orphanedFiles: string[];
46
+ }
47
+ export declare function adapterListFromMode(adapter?: string): string[];
48
+ export declare function createManifest(version: string, adapters: Iterable<string>, templateFiles: Map<string, TemplateFile>): Manifest;
49
+ export declare function collectManagedTemplateFiles(templateRoot: string, adapters: string[]): Promise<Map<string, TemplateFile>>;
50
+ export declare function readManifest(targetDir: string): Promise<Manifest | null>;
51
+ export declare function prepareUpdate({ targetDir, templateRoot, version, adapter }: {
52
+ targetDir: string;
53
+ templateRoot: string;
54
+ version: string;
55
+ adapter?: string;
56
+ }): Promise<PreparedUpdate>;
57
+ export declare function applyPreparedUpdate(prepared: PreparedUpdate, { replaceConflicts }?: {
58
+ replaceConflicts?: boolean;
59
+ }): Promise<{
60
+ appliedCount: number;
61
+ removedCount: number;
62
+ }>;
63
+ export declare function cleanEmptyParentDirectories(targetDir: string, filePath: string): Promise<void>;
64
+ export declare function writeInstallManifest(targetDir: string, manifest: Manifest): Promise<void>;
65
+ export declare function copyFileAtomic(targetDir: string, relativePath: string, sourcePath: string): Promise<void>;
66
+ export declare function targetPath(targetDir: string, relativePath: string): string;
67
+ export declare function assertNoSymlinkParents(targetDir: string, relativePath: string): Promise<void>;
68
+ export declare function hashFile(filePath: string): Promise<string>;
@@ -0,0 +1,325 @@
1
+ import crypto from "node:crypto";
2
+ import fs from "node:fs/promises";
3
+ import path from "node:path";
4
+ export const CONTROL_DIR = ".nexus";
5
+ export const MANIFEST_PATH = `${CONTROL_DIR}/nexus-devflow.json`;
6
+ export const MANIFEST_SCHEMA_VERSION = 1;
7
+ export const MANAGED_ROOTS = {
8
+ common: ["AGENTS.md", "CLAUDE.md", "devflow", "LICENSE"],
9
+ codex: [".agents/skills"],
10
+ antigravity: [".agents/skills", ".agent/workflows"],
11
+ claude: [".claude/skills"]
12
+ };
13
+ export function adapterListFromMode(adapter) {
14
+ if (!adapter || adapter === "both" || adapter === "all") {
15
+ return ["codex", "claude"];
16
+ }
17
+ if (adapter === "antigravity") {
18
+ return ["codex"];
19
+ }
20
+ if (adapter === "codex" || adapter === "claude") {
21
+ return [adapter];
22
+ }
23
+ throw new Error(`Unknown adapter mode: ${adapter}`);
24
+ }
25
+ export function createManifest(version, adapters, templateFiles) {
26
+ const managedFiles = {};
27
+ for (const [relativePath, file] of [...templateFiles.entries()].sort(([a], [b]) => a.localeCompare(b))) {
28
+ managedFiles[relativePath] = file.hash;
29
+ }
30
+ return {
31
+ schemaVersion: MANIFEST_SCHEMA_VERSION,
32
+ name: "nexus-devflow",
33
+ package: "@jakkrichm/create-nexus-devflow",
34
+ version,
35
+ repository: "https://github.com/Jakkrich/nexus-devflow",
36
+ artifactLanguage: "th",
37
+ adapters: [...adapters].sort(),
38
+ workspace: {
39
+ contextDir: "devflow/context",
40
+ historyDir: "devflow/history",
41
+ referenceDir: "devflow/reference",
42
+ runsDir: "devflow/runs",
43
+ discoveriesDir: "devflow/discoveries"
44
+ },
45
+ lifecycle: {
46
+ mainlineStages: [
47
+ "00-discover",
48
+ "10-define",
49
+ "20-spec",
50
+ "30-plan",
51
+ "40-implement",
52
+ "50-verify",
53
+ "60-report",
54
+ "70-release"
55
+ ],
56
+ companionCommands: [
57
+ "goal",
58
+ "brainstorm",
59
+ "research",
60
+ "debug",
61
+ "prd",
62
+ "issue-triage",
63
+ "security-review",
64
+ "check-for-updates",
65
+ "help"
66
+ ]
67
+ },
68
+ managedFiles
69
+ };
70
+ }
71
+ export async function collectManagedTemplateFiles(templateRoot, adapters) {
72
+ const files = new Map();
73
+ const roots = [
74
+ ...MANAGED_ROOTS.common,
75
+ ...adapters.flatMap((adapter) => MANAGED_ROOTS[adapter] || [])
76
+ ];
77
+ for (const relativeRoot of roots) {
78
+ const sourceRoot = path.join(templateRoot, ...relativeRoot.split("/"));
79
+ await collectSourceFiles(sourceRoot, relativeRoot, files);
80
+ }
81
+ return files;
82
+ }
83
+ async function collectSourceFiles(sourcePath, relativePath, files) {
84
+ try {
85
+ const stats = await fs.lstat(sourcePath);
86
+ if (stats.isSymbolicLink()) {
87
+ throw new Error(`Managed template path cannot be a symbolic link: ${relativePath}`);
88
+ }
89
+ if (stats.isDirectory()) {
90
+ const children = (await fs.readdir(sourcePath)).sort();
91
+ for (const child of children) {
92
+ await collectSourceFiles(path.join(sourcePath, child), `${relativePath}/${child}`, files);
93
+ }
94
+ return;
95
+ }
96
+ if (!stats.isFile()) {
97
+ throw new Error(`Managed template path is not a regular file: ${relativePath}`);
98
+ }
99
+ files.set(relativePath, {
100
+ source: sourcePath,
101
+ hash: await hashFile(sourcePath)
102
+ });
103
+ }
104
+ catch (err) {
105
+ if (err.code === "ENOENT") {
106
+ return;
107
+ }
108
+ throw err;
109
+ }
110
+ }
111
+ export async function readManifest(targetDir) {
112
+ const manifestFile = targetPath(targetDir, MANIFEST_PATH);
113
+ await assertNoSymlinkParents(targetDir, MANIFEST_PATH);
114
+ try {
115
+ const content = await fs.readFile(manifestFile, "utf8");
116
+ const data = JSON.parse(content);
117
+ if (!data || data.schemaVersion !== MANIFEST_SCHEMA_VERSION) {
118
+ throw new Error("Unsupported manifest schema version.");
119
+ }
120
+ return data;
121
+ }
122
+ catch (error) {
123
+ if (error.code === "ENOENT") {
124
+ return null;
125
+ }
126
+ throw error;
127
+ }
128
+ }
129
+ export async function prepareUpdate({ targetDir, templateRoot, version, adapter }) {
130
+ const requestedAdapters = new Set(adapterListFromMode(adapter));
131
+ const previousManifest = await readManifest(targetDir);
132
+ const activeAdapters = new Set([
133
+ ...requestedAdapters,
134
+ ...(previousManifest?.adapters || [])
135
+ ]);
136
+ const templateFiles = await collectManagedTemplateFiles(templateRoot, [...activeAdapters]);
137
+ const nextManifest = createManifest(version, activeAdapters, templateFiles);
138
+ const createList = [];
139
+ const updateList = [];
140
+ const conflictList = [];
141
+ for (const [relativePath, templateFile] of templateFiles) {
142
+ await assertNoSymlinkParents(targetDir, relativePath);
143
+ const targetFile = targetPath(targetDir, relativePath);
144
+ let targetStats = null;
145
+ try {
146
+ targetStats = await fs.lstat(targetFile);
147
+ }
148
+ catch (error) {
149
+ if (error.code !== "ENOENT") {
150
+ throw error;
151
+ }
152
+ }
153
+ if (!targetStats) {
154
+ createList.push(relativePath);
155
+ continue;
156
+ }
157
+ if (targetStats.isSymbolicLink()) {
158
+ conflictList.push({
159
+ relativePath,
160
+ reason: "symlink",
161
+ detail: "Target path is a symbolic link."
162
+ });
163
+ continue;
164
+ }
165
+ if (!targetStats.isFile()) {
166
+ conflictList.push({
167
+ relativePath,
168
+ reason: "not_file",
169
+ detail: "Target path exists but is not a regular file."
170
+ });
171
+ continue;
172
+ }
173
+ const currentHash = await hashFile(targetFile);
174
+ if (currentHash === templateFile.hash) {
175
+ continue;
176
+ }
177
+ const recordedHash = previousManifest?.managedFiles?.[relativePath];
178
+ if (recordedHash && recordedHash === currentHash) {
179
+ updateList.push(relativePath);
180
+ continue;
181
+ }
182
+ conflictList.push({
183
+ relativePath,
184
+ reason: "customized",
185
+ detail: recordedHash
186
+ ? "File was modified locally since last install."
187
+ : "File exists in project and differs from template."
188
+ });
189
+ }
190
+ const orphanedFiles = [];
191
+ if (previousManifest) {
192
+ for (const [relativePath, recordedHash] of Object.entries(previousManifest.managedFiles)) {
193
+ if (templateFiles.has(relativePath)) {
194
+ continue;
195
+ }
196
+ await assertNoSymlinkParents(targetDir, relativePath);
197
+ const targetFile = targetPath(targetDir, relativePath);
198
+ let targetStats = null;
199
+ try {
200
+ targetStats = await fs.lstat(targetFile);
201
+ }
202
+ catch (error) {
203
+ if (error.code !== "ENOENT") {
204
+ throw error;
205
+ }
206
+ }
207
+ if (!targetStats || !targetStats.isFile()) {
208
+ continue;
209
+ }
210
+ const currentHash = await hashFile(targetFile);
211
+ if (currentHash === recordedHash) {
212
+ orphanedFiles.push(relativePath);
213
+ }
214
+ }
215
+ }
216
+ return {
217
+ targetDir,
218
+ templateRoot,
219
+ previousManifest,
220
+ nextManifest,
221
+ activeAdapters: [...activeAdapters].sort(),
222
+ templateFiles,
223
+ createList,
224
+ updateList,
225
+ conflictList,
226
+ orphanedFiles
227
+ };
228
+ }
229
+ export async function applyPreparedUpdate(prepared, { replaceConflicts = false } = {}) {
230
+ if (prepared.conflictList.length > 0 && !replaceConflicts) {
231
+ throw new Error(`Cannot apply update with ${prepared.conflictList.length} conflict(s). Pass force/replace option or resolve conflicts.`);
232
+ }
233
+ const writeTargets = [
234
+ ...prepared.createList,
235
+ ...prepared.updateList,
236
+ ...(replaceConflicts ? prepared.conflictList.map((item) => item.relativePath) : [])
237
+ ];
238
+ let appliedCount = 0;
239
+ for (const relativePath of writeTargets) {
240
+ const templateFile = prepared.templateFiles.get(relativePath);
241
+ if (!templateFile) {
242
+ continue;
243
+ }
244
+ await copyFileAtomic(prepared.targetDir, relativePath, templateFile.source);
245
+ appliedCount++;
246
+ }
247
+ let removedCount = 0;
248
+ for (const relativePath of prepared.orphanedFiles) {
249
+ const fileToRemove = targetPath(prepared.targetDir, relativePath);
250
+ try {
251
+ await fs.unlink(fileToRemove);
252
+ removedCount++;
253
+ await cleanEmptyParentDirectories(prepared.targetDir, fileToRemove);
254
+ }
255
+ catch (error) {
256
+ if (error.code !== "ENOENT") {
257
+ throw error;
258
+ }
259
+ }
260
+ }
261
+ await writeInstallManifest(prepared.targetDir, prepared.nextManifest);
262
+ return {
263
+ appliedCount,
264
+ removedCount
265
+ };
266
+ }
267
+ export async function cleanEmptyParentDirectories(targetDir, filePath) {
268
+ let parentDir = path.dirname(filePath);
269
+ const resolvedTarget = path.resolve(targetDir);
270
+ while (parentDir !== resolvedTarget && parentDir.startsWith(resolvedTarget)) {
271
+ try {
272
+ const entries = await fs.readdir(parentDir);
273
+ if (entries.length === 0) {
274
+ await fs.rmdir(parentDir);
275
+ parentDir = path.dirname(parentDir);
276
+ }
277
+ else {
278
+ break;
279
+ }
280
+ }
281
+ catch {
282
+ break;
283
+ }
284
+ }
285
+ }
286
+ export async function writeInstallManifest(targetDir, manifest) {
287
+ const manifestFile = targetPath(targetDir, MANIFEST_PATH);
288
+ await fs.mkdir(path.dirname(manifestFile), { recursive: true });
289
+ await fs.writeFile(manifestFile, `${JSON.stringify(manifest, null, 2)}\n`, "utf8");
290
+ }
291
+ export async function copyFileAtomic(targetDir, relativePath, sourcePath) {
292
+ await assertNoSymlinkParents(targetDir, relativePath);
293
+ const destinationPath = targetPath(targetDir, relativePath);
294
+ await fs.mkdir(path.dirname(destinationPath), { recursive: true });
295
+ const tempPath = `${destinationPath}.tmp-${process.pid}-${Date.now()}`;
296
+ await fs.copyFile(sourcePath, tempPath);
297
+ await fs.rename(tempPath, destinationPath);
298
+ }
299
+ export function targetPath(targetDir, relativePath) {
300
+ return path.resolve(targetDir, ...relativePath.split("/"));
301
+ }
302
+ export async function assertNoSymlinkParents(targetDir, relativePath) {
303
+ const parts = relativePath.split("/");
304
+ let current = path.resolve(targetDir);
305
+ for (let i = 0; i < parts.length - 1; i++) {
306
+ current = path.join(current, parts[i]);
307
+ try {
308
+ const stats = await fs.lstat(current);
309
+ if (stats.isSymbolicLink()) {
310
+ throw new Error(`Target sub-path "${parts.slice(0, i + 1).join("/")}" is a symbolic link.`);
311
+ }
312
+ }
313
+ catch (error) {
314
+ if (error.code === "ENOENT") {
315
+ break;
316
+ }
317
+ throw error;
318
+ }
319
+ }
320
+ }
321
+ export async function hashFile(filePath) {
322
+ const content = await fs.readFile(filePath);
323
+ return crypto.createHash("sha256").update(content).digest("hex");
324
+ }
325
+ //# sourceMappingURL=update.js.map