@pennixrv/trellis 0.7.0-beta.47 → 0.7.0-beta.49
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/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +42 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/commands/backup.d.ts +25 -0
- package/dist/commands/backup.d.ts.map +1 -0
- package/dist/commands/backup.js +152 -0
- package/dist/commands/backup.js.map +1 -0
- package/dist/commands/channel/rm.d.ts.map +1 -1
- package/dist/commands/channel/rm.js +203 -91
- package/dist/commands/channel/rm.js.map +1 -1
- package/dist/commands/update.d.ts.map +1 -1
- package/dist/commands/update.js +279 -264
- package/dist/commands/update.js.map +1 -1
- package/dist/migrations/manifests/0.7.0-beta.48.json +9 -0
- package/dist/migrations/manifests/0.7.0-beta.49.json +13 -0
- package/package.json +2 -2
package/dist/commands/update.js
CHANGED
|
@@ -4,6 +4,7 @@ import path from "node:path";
|
|
|
4
4
|
import chalk from "chalk";
|
|
5
5
|
import inquirer from "inquirer";
|
|
6
6
|
import { DIR_NAMES, FILE_NAMES, PATHS } from "../constants/paths.js";
|
|
7
|
+
import { BACKUP_COMPLETE_MARKER, BACKUP_PRUNE_LOCK } from "./backup.js";
|
|
7
8
|
import { VERSION, PACKAGE_NAME } from "../constants/version.js";
|
|
8
9
|
import { getMigrationsForVersion, getAllMigrations, getMigrationMetadata, getConfigSectionsAddedBetween, } from "../migrations/index.js";
|
|
9
10
|
import { loadHashes, saveHashes, updateHashes, isTemplateModified, removeHash, renameHash, computeHash, shouldExcludeFromHash, } from "../utils/template-hash.js";
|
|
@@ -1028,6 +1029,7 @@ export function shouldExcludeFromBackup(relativePath) {
|
|
|
1028
1029
|
*/
|
|
1029
1030
|
function createFullBackup(cwd) {
|
|
1030
1031
|
const backupDir = createBackupDirPath(cwd);
|
|
1032
|
+
const stagingDir = `${backupDir}.incomplete`;
|
|
1031
1033
|
let hasFiles = false;
|
|
1032
1034
|
for (const dir of BACKUP_DIRS) {
|
|
1033
1035
|
const dirPath = path.join(cwd, dir);
|
|
@@ -1041,10 +1043,10 @@ function createFullBackup(cwd) {
|
|
|
1041
1043
|
continue;
|
|
1042
1044
|
// Create backup
|
|
1043
1045
|
if (!hasFiles) {
|
|
1044
|
-
fs.mkdirSync(
|
|
1046
|
+
fs.mkdirSync(stagingDir, { recursive: true });
|
|
1045
1047
|
hasFiles = true;
|
|
1046
1048
|
}
|
|
1047
|
-
backupFile(cwd,
|
|
1049
|
+
backupFile(cwd, stagingDir, relativePath);
|
|
1048
1050
|
}
|
|
1049
1051
|
}
|
|
1050
1052
|
for (const relativePath of BACKUP_FILES) {
|
|
@@ -1054,10 +1056,14 @@ function createFullBackup(cwd) {
|
|
|
1054
1056
|
if (shouldExcludeFromBackup(relativePath))
|
|
1055
1057
|
continue;
|
|
1056
1058
|
if (!hasFiles) {
|
|
1057
|
-
fs.mkdirSync(
|
|
1059
|
+
fs.mkdirSync(stagingDir, { recursive: true });
|
|
1058
1060
|
hasFiles = true;
|
|
1059
1061
|
}
|
|
1060
|
-
backupFile(cwd,
|
|
1062
|
+
backupFile(cwd, stagingDir, relativePath);
|
|
1063
|
+
}
|
|
1064
|
+
if (hasFiles) {
|
|
1065
|
+
fs.writeFileSync(path.join(stagingDir, BACKUP_COMPLETE_MARKER), VERSION, "utf8");
|
|
1066
|
+
fs.renameSync(stagingDir, backupDir);
|
|
1061
1067
|
}
|
|
1062
1068
|
return hasFiles ? backupDir : null;
|
|
1063
1069
|
}
|
|
@@ -2011,292 +2017,301 @@ export async function update(options) {
|
|
|
2011
2017
|
return;
|
|
2012
2018
|
}
|
|
2013
2019
|
}
|
|
2014
|
-
//
|
|
2015
|
-
const
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
if (hasSafeDeletes) {
|
|
2043
|
-
safeDeleted = executeSafeFileDeletes(safeFileDeletes, cwd);
|
|
2044
|
-
if (safeDeleted > 0) {
|
|
2045
|
-
console.log(chalk.cyan(`\nCleaned up ${safeDeleted} deprecated command file(s)`));
|
|
2046
|
-
}
|
|
2047
|
-
}
|
|
2048
|
-
// Track results
|
|
2049
|
-
let added = 0;
|
|
2050
|
-
let autoUpdated = 0;
|
|
2051
|
-
let updated = 0;
|
|
2052
|
-
let skipped = 0;
|
|
2053
|
-
let createdNew = 0;
|
|
2054
|
-
// Add new files
|
|
2055
|
-
if (changes.newFiles.length > 0) {
|
|
2056
|
-
console.log(chalk.blue("\nAdding new files..."));
|
|
2057
|
-
for (const file of changes.newFiles) {
|
|
2058
|
-
const dir = path.dirname(file.path);
|
|
2059
|
-
fs.mkdirSync(dir, { recursive: true });
|
|
2060
|
-
fs.writeFileSync(file.path, file.newContent);
|
|
2061
|
-
// Make scripts executable
|
|
2062
|
-
if (file.relativePath.endsWith(".sh") ||
|
|
2063
|
-
file.relativePath.endsWith(".py")) {
|
|
2064
|
-
fs.chmodSync(file.path, "755");
|
|
2020
|
+
// Keep the rollback snapshot protected until all update writes finish.
|
|
2021
|
+
const backupLockPath = path.join(cwd, DIR_NAMES.WORKFLOW, BACKUP_PRUNE_LOCK);
|
|
2022
|
+
const backupLock = fs.openSync(backupLockPath, "wx");
|
|
2023
|
+
try {
|
|
2024
|
+
// Create complete backup of all managed platform/workflow directories
|
|
2025
|
+
const backupDir = createFullBackup(cwd);
|
|
2026
|
+
if (backupDir) {
|
|
2027
|
+
console.log(chalk.gray(`\nBackup created: ${path.relative(cwd, backupDir)}/`));
|
|
2028
|
+
}
|
|
2029
|
+
// Execute migrations if --migrate flag is set
|
|
2030
|
+
if (options.migrate && classifiedMigrations) {
|
|
2031
|
+
const migrationResult = await executeMigrations(classifiedMigrations, cwd, {
|
|
2032
|
+
force: options.force,
|
|
2033
|
+
skipAll: options.skipAll,
|
|
2034
|
+
}, templates);
|
|
2035
|
+
printMigrationResult(migrationResult);
|
|
2036
|
+
// Hardcoded: Rename traces-*.md to journal-*.md in workspace directories
|
|
2037
|
+
// Why hardcoded: The migration system only supports fixed path renames, not pattern-based.
|
|
2038
|
+
// traces-*.md files are in .trellis/workspace/{developer}/ with variable developer names
|
|
2039
|
+
// and variable file numbers (traces-1.md, traces-2.md, etc.), so we can't enumerate them
|
|
2040
|
+
// in the migration manifest. This is a one-time migration for the 0.2.0 naming redesign.
|
|
2041
|
+
const workspaceDir = path.join(cwd, PATHS.WORKSPACE);
|
|
2042
|
+
const { renamed: journalRenamed, skipped: journalSkipped } = renameTracesToJournal(workspaceDir);
|
|
2043
|
+
if (journalRenamed > 0) {
|
|
2044
|
+
console.log(chalk.cyan(`Renamed ${journalRenamed} traces file(s) to journal`));
|
|
2045
|
+
}
|
|
2046
|
+
for (const oldPath of journalSkipped) {
|
|
2047
|
+
console.warn(chalk.yellow(`Kept ${path.relative(cwd, oldPath)}: its journal target already exists`));
|
|
2065
2048
|
}
|
|
2066
|
-
console.log(chalk.green(` + ${file.relativePath}`));
|
|
2067
|
-
added++;
|
|
2068
2049
|
}
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
// Make scripts executable
|
|
2076
|
-
if (file.relativePath.endsWith(".sh") ||
|
|
2077
|
-
file.relativePath.endsWith(".py")) {
|
|
2078
|
-
fs.chmodSync(file.path, "755");
|
|
2050
|
+
// Execute safe-file-delete (after backup, before template writes)
|
|
2051
|
+
let safeDeleted = 0;
|
|
2052
|
+
if (hasSafeDeletes) {
|
|
2053
|
+
safeDeleted = executeSafeFileDeletes(safeFileDeletes, cwd);
|
|
2054
|
+
if (safeDeleted > 0) {
|
|
2055
|
+
console.log(chalk.cyan(`\nCleaned up ${safeDeleted} deprecated command file(s)`));
|
|
2079
2056
|
}
|
|
2080
|
-
console.log(chalk.cyan(` ↑ ${file.relativePath}`));
|
|
2081
|
-
autoUpdated++;
|
|
2082
2057
|
}
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
mergeManagedMarkdownBlock(fs.readFileSync(file.path, "utf-8"), file.newContent, markers[0], markers[1]) === null) {
|
|
2101
|
-
throw new Error("Refusing ambiguous managed Markdown markers in " +
|
|
2102
|
-
file.relativePath +
|
|
2103
|
-
"; use --create-new or repair the markers.");
|
|
2058
|
+
// Track results
|
|
2059
|
+
let added = 0;
|
|
2060
|
+
let autoUpdated = 0;
|
|
2061
|
+
let updated = 0;
|
|
2062
|
+
let skipped = 0;
|
|
2063
|
+
let createdNew = 0;
|
|
2064
|
+
// Add new files
|
|
2065
|
+
if (changes.newFiles.length > 0) {
|
|
2066
|
+
console.log(chalk.blue("\nAdding new files..."));
|
|
2067
|
+
for (const file of changes.newFiles) {
|
|
2068
|
+
const dir = path.dirname(file.path);
|
|
2069
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
2070
|
+
fs.writeFileSync(file.path, file.newContent);
|
|
2071
|
+
// Make scripts executable
|
|
2072
|
+
if (file.relativePath.endsWith(".sh") ||
|
|
2073
|
+
file.relativePath.endsWith(".py")) {
|
|
2074
|
+
fs.chmodSync(file.path, "755");
|
|
2104
2075
|
}
|
|
2076
|
+
console.log(chalk.green(` + ${file.relativePath}`));
|
|
2077
|
+
added++;
|
|
2078
|
+
}
|
|
2079
|
+
}
|
|
2080
|
+
// Auto-update files (template updated, user didn't modify)
|
|
2081
|
+
if (changes.autoUpdateFiles.length > 0) {
|
|
2082
|
+
console.log(chalk.blue("\nAuto-updating template files..."));
|
|
2083
|
+
for (const file of changes.autoUpdateFiles) {
|
|
2105
2084
|
fs.writeFileSync(file.path, file.newContent);
|
|
2085
|
+
// Make scripts executable
|
|
2106
2086
|
if (file.relativePath.endsWith(".sh") ||
|
|
2107
2087
|
file.relativePath.endsWith(".py")) {
|
|
2108
2088
|
fs.chmodSync(file.path, "755");
|
|
2109
2089
|
}
|
|
2110
|
-
console.log(chalk.
|
|
2111
|
-
|
|
2090
|
+
console.log(chalk.cyan(` ↑ ${file.relativePath}`));
|
|
2091
|
+
autoUpdated++;
|
|
2112
2092
|
}
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2093
|
+
}
|
|
2094
|
+
// Handle changed files
|
|
2095
|
+
if (changes.changedFiles.length > 0) {
|
|
2096
|
+
console.log(chalk.blue("\n--- Resolving conflicts ---\n"));
|
|
2097
|
+
const applyToAll = { action: null };
|
|
2098
|
+
for (const file of changes.changedFiles) {
|
|
2099
|
+
const action = await promptConflictResolution(file, options, applyToAll);
|
|
2100
|
+
if (action === "overwrite") {
|
|
2101
|
+
const markers = file.relativePath === FILE_NAMES.AGENTS
|
|
2102
|
+
? [TRELLIS_BLOCK_START, TRELLIS_BLOCK_END]
|
|
2103
|
+
: file.relativePath === COPILOT_INSTRUCTIONS_PATH
|
|
2104
|
+
? [
|
|
2105
|
+
COPILOT_INSTRUCTIONS_BLOCK_START,
|
|
2106
|
+
COPILOT_INSTRUCTIONS_BLOCK_END,
|
|
2107
|
+
]
|
|
2108
|
+
: null;
|
|
2109
|
+
if (markers &&
|
|
2110
|
+
mergeManagedMarkdownBlock(fs.readFileSync(file.path, "utf-8"), file.newContent, markers[0], markers[1]) === null) {
|
|
2111
|
+
throw new Error("Refusing ambiguous managed Markdown markers in " +
|
|
2112
|
+
file.relativePath +
|
|
2113
|
+
"; use --create-new or repair the markers.");
|
|
2114
|
+
}
|
|
2115
|
+
fs.writeFileSync(file.path, file.newContent);
|
|
2116
|
+
if (file.relativePath.endsWith(".sh") ||
|
|
2117
|
+
file.relativePath.endsWith(".py")) {
|
|
2118
|
+
fs.chmodSync(file.path, "755");
|
|
2119
|
+
}
|
|
2120
|
+
console.log(chalk.yellow(` ✓ Overwritten: ${file.relativePath}`));
|
|
2121
|
+
updated++;
|
|
2122
|
+
}
|
|
2123
|
+
else if (action === "create-new") {
|
|
2124
|
+
const newPath = file.path + ".new";
|
|
2125
|
+
fs.writeFileSync(newPath, file.newContent);
|
|
2126
|
+
console.log(chalk.blue(` ✓ Created: ${file.relativePath}.new`));
|
|
2127
|
+
createdNew++;
|
|
2128
|
+
}
|
|
2129
|
+
else {
|
|
2130
|
+
console.log(chalk.gray(` ○ Skipped: ${file.relativePath}`));
|
|
2131
|
+
skipped++;
|
|
2132
|
+
}
|
|
2118
2133
|
}
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2134
|
+
}
|
|
2135
|
+
// Append additive config.yaml sections introduced between versions.
|
|
2136
|
+
// Sentinel-gated, so users keep their customizations and re-running update
|
|
2137
|
+
// on already-migrated files is a no-op. Skipped on unknown / downgrade.
|
|
2138
|
+
let configSectionsAppended = 0;
|
|
2139
|
+
if (cliVsProject > 0 && projectVersion !== "unknown") {
|
|
2140
|
+
const sectionEntries = getConfigSectionsAddedBetween(projectVersion, cliVersion);
|
|
2141
|
+
if (sectionEntries.length > 0) {
|
|
2142
|
+
const { appended } = applyConfigSectionsAdded(sectionEntries, cwd, templates);
|
|
2143
|
+
configSectionsAppended = appended;
|
|
2122
2144
|
}
|
|
2123
2145
|
}
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
// Auto-updated files always get new hash
|
|
2145
|
-
for (const file of changes.autoUpdateFiles) {
|
|
2146
|
-
filesToHash.set(file.relativePath, file.newContent);
|
|
2147
|
-
}
|
|
2148
|
-
// Only hash overwritten files (not skipped or .new copies)
|
|
2149
|
-
for (const file of changes.changedFiles) {
|
|
2150
|
-
const fullPath = path.join(cwd, file.relativePath);
|
|
2151
|
-
if (fs.existsSync(fullPath)) {
|
|
2152
|
-
const content = fs.readFileSync(fullPath, "utf-8");
|
|
2153
|
-
if (content === file.newContent) {
|
|
2154
|
-
filesToHash.set(file.relativePath, file.newContent);
|
|
2146
|
+
// Update version file
|
|
2147
|
+
updateVersionFile(cwd);
|
|
2148
|
+
refreshNativeWorkflowProvenance(cwd);
|
|
2149
|
+
// Update template hashes for new, auto-updated, and overwritten files
|
|
2150
|
+
const filesToHash = new Map(unchangedFileHashRepairs);
|
|
2151
|
+
for (const file of changes.newFiles) {
|
|
2152
|
+
filesToHash.set(file.relativePath, file.newContent);
|
|
2153
|
+
}
|
|
2154
|
+
// Auto-updated files always get new hash
|
|
2155
|
+
for (const file of changes.autoUpdateFiles) {
|
|
2156
|
+
filesToHash.set(file.relativePath, file.newContent);
|
|
2157
|
+
}
|
|
2158
|
+
// Only hash overwritten files (not skipped or .new copies)
|
|
2159
|
+
for (const file of changes.changedFiles) {
|
|
2160
|
+
const fullPath = path.join(cwd, file.relativePath);
|
|
2161
|
+
if (fs.existsSync(fullPath)) {
|
|
2162
|
+
const content = fs.readFileSync(fullPath, "utf-8");
|
|
2163
|
+
if (content === file.newContent) {
|
|
2164
|
+
filesToHash.set(file.relativePath, file.newContent);
|
|
2165
|
+
}
|
|
2155
2166
|
}
|
|
2156
2167
|
}
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2168
|
+
if (filesToHash.size > 0) {
|
|
2169
|
+
updateHashes(cwd, filesToHash);
|
|
2170
|
+
}
|
|
2171
|
+
// Print summary
|
|
2172
|
+
console.log(chalk.cyan("\n--- Summary ---\n"));
|
|
2173
|
+
if (added > 0) {
|
|
2174
|
+
console.log(` Added: ${added} file(s)`);
|
|
2175
|
+
}
|
|
2176
|
+
if (autoUpdated > 0) {
|
|
2177
|
+
console.log(` Auto-updated: ${autoUpdated} file(s)`);
|
|
2178
|
+
}
|
|
2179
|
+
if (updated > 0) {
|
|
2180
|
+
console.log(` Updated: ${updated} file(s)`);
|
|
2181
|
+
}
|
|
2182
|
+
if (skipped > 0) {
|
|
2183
|
+
console.log(` Skipped: ${skipped} file(s)`);
|
|
2184
|
+
}
|
|
2185
|
+
if (createdNew > 0) {
|
|
2186
|
+
console.log(` Created .new copies: ${createdNew} file(s)`);
|
|
2187
|
+
}
|
|
2188
|
+
if (safeDeleted > 0) {
|
|
2189
|
+
console.log(` Cleaned up: ${safeDeleted} deprecated file(s)`);
|
|
2190
|
+
}
|
|
2191
|
+
if (configSectionsAppended > 0) {
|
|
2192
|
+
console.log(` Config sections added: ${configSectionsAppended}`);
|
|
2193
|
+
}
|
|
2194
|
+
if (backupDir) {
|
|
2195
|
+
console.log(` Backup: ${path.relative(cwd, backupDir)}/`);
|
|
2196
|
+
}
|
|
2197
|
+
const actionWord = isDowngrade ? "Downgrade" : "Update";
|
|
2198
|
+
console.log(chalk.green(`\n✅ ${actionWord} complete! (${projectVersion} → ${cliVersion})`));
|
|
2199
|
+
if (createdNew > 0) {
|
|
2200
|
+
console.log(chalk.gray("\nTip: Review .new files and merge changes manually if needed."));
|
|
2201
|
+
}
|
|
2202
|
+
// Create migration task if there are breaking changes with migration guides
|
|
2203
|
+
if (cliVsProject > 0 && projectVersion !== "unknown") {
|
|
2204
|
+
const metadata = getMigrationMetadata(projectVersion, cliVersion);
|
|
2205
|
+
if (metadata.breaking && metadata.migrationGuides.length > 0) {
|
|
2206
|
+
// Create task directory
|
|
2207
|
+
const today = new Date();
|
|
2208
|
+
const monthDay = `${String(today.getMonth() + 1).padStart(2, "0")}-${String(today.getDate()).padStart(2, "0")}`;
|
|
2209
|
+
const taskSlug = `migrate-to-${cliVersion}`;
|
|
2210
|
+
const taskDirName = `${monthDay}-${taskSlug}`;
|
|
2211
|
+
const tasksDir = path.join(cwd, DIR_NAMES.WORKFLOW, DIR_NAMES.TASKS);
|
|
2212
|
+
const taskDir = path.join(tasksDir, taskDirName);
|
|
2213
|
+
// Check if task already exists
|
|
2214
|
+
if (!fs.existsSync(taskDir)) {
|
|
2215
|
+
fs.mkdirSync(taskDir, { recursive: true });
|
|
2216
|
+
// Get current developer for assignee.
|
|
2217
|
+
// `.developer` is a key=value file (written by init_developer.py):
|
|
2218
|
+
// name=<developer-name>
|
|
2219
|
+
// initialized_at=<iso8601>
|
|
2220
|
+
// Reading it raw and .trim()-ing embeds the entire file contents
|
|
2221
|
+
// (including the `name=` prefix and the `initialized_at` line) into
|
|
2222
|
+
// the assignee field, producing bogus assignees like
|
|
2223
|
+
// "name=suyuan\ninitialized_at=2026-04-07T23:41:21.978312" that
|
|
2224
|
+
// later break session-start task rendering.
|
|
2225
|
+
const developerFile = path.join(cwd, DIR_NAMES.WORKFLOW, ".developer");
|
|
2226
|
+
let currentDeveloper = "unknown";
|
|
2227
|
+
if (fs.existsSync(developerFile)) {
|
|
2228
|
+
const raw = fs.readFileSync(developerFile, "utf-8");
|
|
2229
|
+
const nameMatch = raw.match(/^\s*name\s*=\s*(.+?)\s*$/m);
|
|
2230
|
+
if (nameMatch) {
|
|
2231
|
+
currentDeveloper = nameMatch[1];
|
|
2232
|
+
}
|
|
2222
2233
|
}
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
prdContent += guide;
|
|
2252
|
-
prdContent += "\n\n";
|
|
2253
|
-
if (aiInstructions) {
|
|
2254
|
-
prdContent += `### AI Assistant Instructions\n\n`;
|
|
2255
|
-
prdContent += `When helping with this migration:\n\n`;
|
|
2256
|
-
prdContent += aiInstructions;
|
|
2234
|
+
// Build task.json — canonical 24-field shape via shared factory.
|
|
2235
|
+
const taskTitle = `Migrate to v${cliVersion}`;
|
|
2236
|
+
const todayStr = today.toISOString().split("T")[0];
|
|
2237
|
+
const taskJson = emptyTaskJson({
|
|
2238
|
+
id: taskSlug,
|
|
2239
|
+
name: taskSlug,
|
|
2240
|
+
title: taskTitle,
|
|
2241
|
+
description: `Breaking change migration from v${projectVersion} to v${cliVersion}`,
|
|
2242
|
+
status: "planning",
|
|
2243
|
+
scope: "migration",
|
|
2244
|
+
priority: "P1",
|
|
2245
|
+
creator: "trellis-update",
|
|
2246
|
+
assignee: currentDeveloper,
|
|
2247
|
+
createdAt: todayStr,
|
|
2248
|
+
});
|
|
2249
|
+
// Write task.json
|
|
2250
|
+
const taskJsonPath = path.join(taskDir, "task.json");
|
|
2251
|
+
fs.writeFileSync(taskJsonPath, JSON.stringify(taskJson, null, 2));
|
|
2252
|
+
// Build PRD content
|
|
2253
|
+
let prdContent = `# Migration Task: Upgrade to v${cliVersion}\n\n`;
|
|
2254
|
+
prdContent += `**Created**: ${todayStr}\n`;
|
|
2255
|
+
prdContent += `**From Version**: ${projectVersion}\n`;
|
|
2256
|
+
prdContent += `**To Version**: ${cliVersion}\n`;
|
|
2257
|
+
prdContent += `**Assignee**: ${currentDeveloper}\n\n`;
|
|
2258
|
+
prdContent += `## Status\n\n- [ ] Review migration guide\n- [ ] Update custom files\n- [ ] Run \`trellis update --migrate\`\n- [ ] Test workflows\n\n`;
|
|
2259
|
+
for (const { version, guide, aiInstructions, } of metadata.migrationGuides) {
|
|
2260
|
+
prdContent += `---\n\n## v${version} Migration Guide\n\n`;
|
|
2261
|
+
prdContent += guide;
|
|
2257
2262
|
prdContent += "\n\n";
|
|
2263
|
+
if (aiInstructions) {
|
|
2264
|
+
prdContent += `### AI Assistant Instructions\n\n`;
|
|
2265
|
+
prdContent += `When helping with this migration:\n\n`;
|
|
2266
|
+
prdContent += aiInstructions;
|
|
2267
|
+
prdContent += "\n\n";
|
|
2268
|
+
}
|
|
2258
2269
|
}
|
|
2270
|
+
// Write PRD
|
|
2271
|
+
const prdPath = path.join(taskDir, "prd.md");
|
|
2272
|
+
fs.writeFileSync(prdPath, prdContent);
|
|
2273
|
+
console.log("");
|
|
2274
|
+
console.log(chalk.bgCyan.black.bold(" 📋 MIGRATION TASK CREATED "));
|
|
2275
|
+
console.log(chalk.cyan(`A task has been created to help you complete the migration:`));
|
|
2276
|
+
console.log(chalk.white(` ${DIR_NAMES.WORKFLOW}/${DIR_NAMES.TASKS}/${taskDirName}/`));
|
|
2277
|
+
console.log("");
|
|
2278
|
+
console.log(chalk.gray("Use AI to help: Ask Claude/Cursor to read the task and fix your custom files."));
|
|
2259
2279
|
}
|
|
2260
|
-
// Write PRD
|
|
2261
|
-
const prdPath = path.join(taskDir, "prd.md");
|
|
2262
|
-
fs.writeFileSync(prdPath, prdContent);
|
|
2263
|
-
console.log("");
|
|
2264
|
-
console.log(chalk.bgCyan.black.bold(" 📋 MIGRATION TASK CREATED "));
|
|
2265
|
-
console.log(chalk.cyan(`A task has been created to help you complete the migration:`));
|
|
2266
|
-
console.log(chalk.white(` ${DIR_NAMES.WORKFLOW}/${DIR_NAMES.TASKS}/${taskDirName}/`));
|
|
2267
|
-
console.log("");
|
|
2268
|
-
console.log(chalk.gray("Use AI to help: Ask Claude/Cursor to read the task and fix your custom files."));
|
|
2269
2280
|
}
|
|
2270
2281
|
}
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
if (finalMetadata.breaking || finalMetadata.changelog.length > 0) {
|
|
2278
|
-
console.log("");
|
|
2279
|
-
console.log(chalk.cyan("═".repeat(60)));
|
|
2280
|
-
if (finalMetadata.breaking) {
|
|
2281
|
-
console.log(chalk.bgRed.white.bold(" ⚠️ BREAKING CHANGES ") +
|
|
2282
|
-
chalk.red.bold(" This update contains breaking changes!"));
|
|
2282
|
+
if (zcodeConfigured)
|
|
2283
|
+
printZcodeSetupHint();
|
|
2284
|
+
// Display breaking change warnings at the very end (so they don't scroll off screen)
|
|
2285
|
+
if (cliVsProject > 0 && projectVersion !== "unknown") {
|
|
2286
|
+
const finalMetadata = getMigrationMetadata(projectVersion, cliVersion);
|
|
2287
|
+
if (finalMetadata.breaking || finalMetadata.changelog.length > 0) {
|
|
2283
2288
|
console.log("");
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
console.log(
|
|
2289
|
+
console.log(chalk.cyan("═".repeat(60)));
|
|
2290
|
+
if (finalMetadata.breaking) {
|
|
2291
|
+
console.log(chalk.bgRed.white.bold(" ⚠️ BREAKING CHANGES ") +
|
|
2292
|
+
chalk.red.bold(" This update contains breaking changes!"));
|
|
2293
|
+
console.log("");
|
|
2289
2294
|
}
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2295
|
+
if (finalMetadata.changelog.length > 0) {
|
|
2296
|
+
console.log(chalk.cyan.bold("📋 What's Changed:"));
|
|
2297
|
+
for (const entry of finalMetadata.changelog) {
|
|
2298
|
+
console.log(chalk.white(` ${entry}`));
|
|
2299
|
+
}
|
|
2300
|
+
console.log("");
|
|
2301
|
+
}
|
|
2302
|
+
if (finalMetadata.recommendMigrate && !options.migrate) {
|
|
2303
|
+
console.log(chalk.bgGreen.black.bold(" 💡 RECOMMENDED ") +
|
|
2304
|
+
chalk.green.bold(" Run with --migrate to complete the migration"));
|
|
2305
|
+
console.log(chalk.gray(" This will remove legacy files and apply all changes."));
|
|
2306
|
+
console.log("");
|
|
2307
|
+
}
|
|
2308
|
+
console.log(chalk.cyan("═".repeat(60)));
|
|
2297
2309
|
}
|
|
2298
|
-
console.log(chalk.cyan("═".repeat(60)));
|
|
2299
2310
|
}
|
|
2300
2311
|
}
|
|
2312
|
+
finally {
|
|
2313
|
+
fs.closeSync(backupLock);
|
|
2314
|
+
fs.rmSync(backupLockPath, { force: true });
|
|
2315
|
+
}
|
|
2301
2316
|
}
|
|
2302
2317
|
//# sourceMappingURL=update.js.map
|