@lumenflow/core 1.0.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/LICENSE +190 -0
- package/README.md +119 -0
- package/dist/active-wu-detector.d.ts +33 -0
- package/dist/active-wu-detector.js +106 -0
- package/dist/adapters/filesystem-metrics.adapter.d.ts +108 -0
- package/dist/adapters/filesystem-metrics.adapter.js +519 -0
- package/dist/adapters/terminal-renderer.adapter.d.ts +106 -0
- package/dist/adapters/terminal-renderer.adapter.js +337 -0
- package/dist/arg-parser.d.ts +63 -0
- package/dist/arg-parser.js +560 -0
- package/dist/backlog-editor.d.ts +98 -0
- package/dist/backlog-editor.js +179 -0
- package/dist/backlog-generator.d.ts +111 -0
- package/dist/backlog-generator.js +381 -0
- package/dist/backlog-parser.d.ts +45 -0
- package/dist/backlog-parser.js +102 -0
- package/dist/backlog-sync-validator.d.ts +78 -0
- package/dist/backlog-sync-validator.js +294 -0
- package/dist/branch-drift.d.ts +34 -0
- package/dist/branch-drift.js +51 -0
- package/dist/cleanup-install-config.d.ts +33 -0
- package/dist/cleanup-install-config.js +37 -0
- package/dist/cleanup-lock.d.ts +139 -0
- package/dist/cleanup-lock.js +313 -0
- package/dist/code-path-validator.d.ts +146 -0
- package/dist/code-path-validator.js +537 -0
- package/dist/code-paths-overlap.d.ts +55 -0
- package/dist/code-paths-overlap.js +245 -0
- package/dist/commands-logger.d.ts +77 -0
- package/dist/commands-logger.js +254 -0
- package/dist/commit-message-utils.d.ts +25 -0
- package/dist/commit-message-utils.js +41 -0
- package/dist/compliance-parser.d.ts +150 -0
- package/dist/compliance-parser.js +507 -0
- package/dist/constants/backlog-patterns.d.ts +20 -0
- package/dist/constants/backlog-patterns.js +23 -0
- package/dist/constants/dora-constants.d.ts +49 -0
- package/dist/constants/dora-constants.js +53 -0
- package/dist/constants/gate-constants.d.ts +15 -0
- package/dist/constants/gate-constants.js +15 -0
- package/dist/constants/linter-constants.d.ts +16 -0
- package/dist/constants/linter-constants.js +16 -0
- package/dist/constants/tokenizer-constants.d.ts +15 -0
- package/dist/constants/tokenizer-constants.js +15 -0
- package/dist/core/scope-checker.d.ts +97 -0
- package/dist/core/scope-checker.js +163 -0
- package/dist/core/tool-runner.d.ts +161 -0
- package/dist/core/tool-runner.js +393 -0
- package/dist/core/tool.constants.d.ts +105 -0
- package/dist/core/tool.constants.js +101 -0
- package/dist/core/tool.schemas.d.ts +226 -0
- package/dist/core/tool.schemas.js +226 -0
- package/dist/core/worktree-guard.d.ts +130 -0
- package/dist/core/worktree-guard.js +242 -0
- package/dist/coverage-gate.d.ts +108 -0
- package/dist/coverage-gate.js +196 -0
- package/dist/date-utils.d.ts +75 -0
- package/dist/date-utils.js +140 -0
- package/dist/dependency-graph.d.ts +142 -0
- package/dist/dependency-graph.js +550 -0
- package/dist/dependency-guard.d.ts +54 -0
- package/dist/dependency-guard.js +142 -0
- package/dist/dependency-validator.d.ts +105 -0
- package/dist/dependency-validator.js +154 -0
- package/dist/docs-path-validator.d.ts +36 -0
- package/dist/docs-path-validator.js +95 -0
- package/dist/domain/orchestration.constants.d.ts +99 -0
- package/dist/domain/orchestration.constants.js +97 -0
- package/dist/domain/orchestration.schemas.d.ts +280 -0
- package/dist/domain/orchestration.schemas.js +211 -0
- package/dist/domain/orchestration.types.d.ts +133 -0
- package/dist/domain/orchestration.types.js +12 -0
- package/dist/error-handler.d.ts +116 -0
- package/dist/error-handler.js +136 -0
- package/dist/file-classifiers.d.ts +62 -0
- package/dist/file-classifiers.js +108 -0
- package/dist/gates-agent-mode.d.ts +81 -0
- package/dist/gates-agent-mode.js +94 -0
- package/dist/generate-traceability.d.ts +107 -0
- package/dist/generate-traceability.js +411 -0
- package/dist/git-adapter.d.ts +395 -0
- package/dist/git-adapter.js +649 -0
- package/dist/git-staged-validator.d.ts +32 -0
- package/dist/git-staged-validator.js +48 -0
- package/dist/hardcoded-strings.d.ts +61 -0
- package/dist/hardcoded-strings.js +270 -0
- package/dist/incremental-lint.d.ts +78 -0
- package/dist/incremental-lint.js +129 -0
- package/dist/incremental-test.d.ts +39 -0
- package/dist/incremental-test.js +61 -0
- package/dist/index.d.ts +42 -0
- package/dist/index.js +61 -0
- package/dist/invariants/check-automated-tests.d.ts +50 -0
- package/dist/invariants/check-automated-tests.js +166 -0
- package/dist/invariants-runner.d.ts +103 -0
- package/dist/invariants-runner.js +527 -0
- package/dist/lane-checker.d.ts +50 -0
- package/dist/lane-checker.js +319 -0
- package/dist/lane-inference.d.ts +39 -0
- package/dist/lane-inference.js +195 -0
- package/dist/lane-lock.d.ts +211 -0
- package/dist/lane-lock.js +474 -0
- package/dist/lane-validator.d.ts +48 -0
- package/dist/lane-validator.js +114 -0
- package/dist/logs-lib.d.ts +104 -0
- package/dist/logs-lib.js +207 -0
- package/dist/lumenflow-config-schema.d.ts +272 -0
- package/dist/lumenflow-config-schema.js +207 -0
- package/dist/lumenflow-config.d.ts +95 -0
- package/dist/lumenflow-config.js +236 -0
- package/dist/manual-test-validator.d.ts +80 -0
- package/dist/manual-test-validator.js +200 -0
- package/dist/merge-lock.d.ts +115 -0
- package/dist/merge-lock.js +251 -0
- package/dist/micro-worktree.d.ts +159 -0
- package/dist/micro-worktree.js +427 -0
- package/dist/migration-deployer.d.ts +69 -0
- package/dist/migration-deployer.js +151 -0
- package/dist/orchestration-advisory-loader.d.ts +28 -0
- package/dist/orchestration-advisory-loader.js +87 -0
- package/dist/orchestration-advisory.d.ts +58 -0
- package/dist/orchestration-advisory.js +94 -0
- package/dist/orchestration-di.d.ts +48 -0
- package/dist/orchestration-di.js +57 -0
- package/dist/orchestration-rules.d.ts +57 -0
- package/dist/orchestration-rules.js +201 -0
- package/dist/orphan-detector.d.ts +131 -0
- package/dist/orphan-detector.js +226 -0
- package/dist/path-classifiers.d.ts +57 -0
- package/dist/path-classifiers.js +93 -0
- package/dist/piped-command-detector.d.ts +34 -0
- package/dist/piped-command-detector.js +64 -0
- package/dist/ports/dashboard-renderer.port.d.ts +112 -0
- package/dist/ports/dashboard-renderer.port.js +25 -0
- package/dist/ports/metrics-collector.port.d.ts +132 -0
- package/dist/ports/metrics-collector.port.js +26 -0
- package/dist/process-detector.d.ts +84 -0
- package/dist/process-detector.js +172 -0
- package/dist/prompt-linter.d.ts +72 -0
- package/dist/prompt-linter.js +312 -0
- package/dist/prompt-monitor.d.ts +15 -0
- package/dist/prompt-monitor.js +205 -0
- package/dist/rebase-artifact-cleanup.d.ts +145 -0
- package/dist/rebase-artifact-cleanup.js +433 -0
- package/dist/retry-strategy.d.ts +189 -0
- package/dist/retry-strategy.js +283 -0
- package/dist/risk-detector.d.ts +108 -0
- package/dist/risk-detector.js +252 -0
- package/dist/rollback-utils.d.ts +76 -0
- package/dist/rollback-utils.js +104 -0
- package/dist/section-headings.d.ts +43 -0
- package/dist/section-headings.js +49 -0
- package/dist/spawn-escalation.d.ts +90 -0
- package/dist/spawn-escalation.js +253 -0
- package/dist/spawn-monitor.d.ts +229 -0
- package/dist/spawn-monitor.js +672 -0
- package/dist/spawn-recovery.d.ts +82 -0
- package/dist/spawn-recovery.js +298 -0
- package/dist/spawn-registry-schema.d.ts +98 -0
- package/dist/spawn-registry-schema.js +108 -0
- package/dist/spawn-registry-store.d.ts +146 -0
- package/dist/spawn-registry-store.js +273 -0
- package/dist/spawn-tree.d.ts +121 -0
- package/dist/spawn-tree.js +285 -0
- package/dist/stamp-status-validator.d.ts +84 -0
- package/dist/stamp-status-validator.js +134 -0
- package/dist/stamp-utils.d.ts +100 -0
- package/dist/stamp-utils.js +229 -0
- package/dist/state-machine.d.ts +26 -0
- package/dist/state-machine.js +83 -0
- package/dist/system-map-validator.d.ts +80 -0
- package/dist/system-map-validator.js +272 -0
- package/dist/telemetry.d.ts +80 -0
- package/dist/telemetry.js +213 -0
- package/dist/token-counter.d.ts +51 -0
- package/dist/token-counter.js +145 -0
- package/dist/usecases/get-dashboard-data.usecase.d.ts +52 -0
- package/dist/usecases/get-dashboard-data.usecase.js +61 -0
- package/dist/usecases/get-suggestions.usecase.d.ts +100 -0
- package/dist/usecases/get-suggestions.usecase.js +153 -0
- package/dist/user-normalizer.d.ts +41 -0
- package/dist/user-normalizer.js +141 -0
- package/dist/validators/phi-constants.d.ts +97 -0
- package/dist/validators/phi-constants.js +152 -0
- package/dist/validators/phi-scanner.d.ts +58 -0
- package/dist/validators/phi-scanner.js +215 -0
- package/dist/worktree-ownership.d.ts +50 -0
- package/dist/worktree-ownership.js +74 -0
- package/dist/worktree-scanner.d.ts +103 -0
- package/dist/worktree-scanner.js +168 -0
- package/dist/worktree-symlink.d.ts +99 -0
- package/dist/worktree-symlink.js +359 -0
- package/dist/wu-backlog-updater.d.ts +17 -0
- package/dist/wu-backlog-updater.js +37 -0
- package/dist/wu-checkpoint.d.ts +124 -0
- package/dist/wu-checkpoint.js +233 -0
- package/dist/wu-claim-helpers.d.ts +26 -0
- package/dist/wu-claim-helpers.js +63 -0
- package/dist/wu-claim-resume.d.ts +106 -0
- package/dist/wu-claim-resume.js +276 -0
- package/dist/wu-consistency-checker.d.ts +95 -0
- package/dist/wu-consistency-checker.js +567 -0
- package/dist/wu-constants.d.ts +1275 -0
- package/dist/wu-constants.js +1382 -0
- package/dist/wu-create-validators.d.ts +42 -0
- package/dist/wu-create-validators.js +93 -0
- package/dist/wu-done-branch-only.d.ts +63 -0
- package/dist/wu-done-branch-only.js +191 -0
- package/dist/wu-done-messages.d.ts +119 -0
- package/dist/wu-done-messages.js +185 -0
- package/dist/wu-done-pr.d.ts +72 -0
- package/dist/wu-done-pr.js +174 -0
- package/dist/wu-done-retry-helpers.d.ts +85 -0
- package/dist/wu-done-retry-helpers.js +172 -0
- package/dist/wu-done-ui.d.ts +37 -0
- package/dist/wu-done-ui.js +69 -0
- package/dist/wu-done-validators.d.ts +411 -0
- package/dist/wu-done-validators.js +1229 -0
- package/dist/wu-done-worktree.d.ts +182 -0
- package/dist/wu-done-worktree.js +1097 -0
- package/dist/wu-helpers.d.ts +128 -0
- package/dist/wu-helpers.js +248 -0
- package/dist/wu-lint.d.ts +70 -0
- package/dist/wu-lint.js +234 -0
- package/dist/wu-paths.d.ts +171 -0
- package/dist/wu-paths.js +178 -0
- package/dist/wu-preflight-validators.d.ts +86 -0
- package/dist/wu-preflight-validators.js +251 -0
- package/dist/wu-recovery.d.ts +138 -0
- package/dist/wu-recovery.js +341 -0
- package/dist/wu-repair-core.d.ts +131 -0
- package/dist/wu-repair-core.js +669 -0
- package/dist/wu-schema-normalization.d.ts +17 -0
- package/dist/wu-schema-normalization.js +82 -0
- package/dist/wu-schema.d.ts +793 -0
- package/dist/wu-schema.js +881 -0
- package/dist/wu-spawn-helpers.d.ts +121 -0
- package/dist/wu-spawn-helpers.js +271 -0
- package/dist/wu-spawn.d.ts +158 -0
- package/dist/wu-spawn.js +1306 -0
- package/dist/wu-state-schema.d.ts +213 -0
- package/dist/wu-state-schema.js +156 -0
- package/dist/wu-state-store.d.ts +264 -0
- package/dist/wu-state-store.js +691 -0
- package/dist/wu-status-transition.d.ts +63 -0
- package/dist/wu-status-transition.js +382 -0
- package/dist/wu-status-updater.d.ts +25 -0
- package/dist/wu-status-updater.js +116 -0
- package/dist/wu-transaction-collectors.d.ts +116 -0
- package/dist/wu-transaction-collectors.js +272 -0
- package/dist/wu-transaction.d.ts +170 -0
- package/dist/wu-transaction.js +273 -0
- package/dist/wu-validation-constants.d.ts +60 -0
- package/dist/wu-validation-constants.js +66 -0
- package/dist/wu-validation.d.ts +118 -0
- package/dist/wu-validation.js +243 -0
- package/dist/wu-validator.d.ts +62 -0
- package/dist/wu-validator.js +325 -0
- package/dist/wu-yaml-fixer.d.ts +97 -0
- package/dist/wu-yaml-fixer.js +264 -0
- package/dist/wu-yaml.d.ts +86 -0
- package/dist/wu-yaml.js +222 -0
- package/package.json +114 -0
|
@@ -0,0 +1,427 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Micro-Worktree Operations
|
|
3
|
+
*
|
|
4
|
+
* Race-safe micro-worktree isolation pattern extracted from wu-create.mjs (WU-1262).
|
|
5
|
+
* Provides shared infrastructure for commands that need to modify main branch
|
|
6
|
+
* atomically without switching checkout.
|
|
7
|
+
*
|
|
8
|
+
* Part of WU-1262: Race-safe WU creation using micro-worktrees
|
|
9
|
+
* Extended in WU-1274: Add wu:edit command for spec-only changes
|
|
10
|
+
* Extended in WU-1439: Migrate initiative:create and wu:create to shared helper
|
|
11
|
+
*
|
|
12
|
+
* Pattern:
|
|
13
|
+
* 1. Create temp branch without switching main checkout
|
|
14
|
+
* 2. Create micro-worktree in /tmp pointing to temp branch
|
|
15
|
+
* 3. Perform operations in micro-worktree
|
|
16
|
+
* 4. FF-only merge temp branch to main (retry with rebase if main moved)
|
|
17
|
+
* 5. Push to origin
|
|
18
|
+
* 6. Cleanup (always, even on failure)
|
|
19
|
+
*
|
|
20
|
+
* Benefits:
|
|
21
|
+
* - Main checkout never switches branches (no impact on other agents)
|
|
22
|
+
* - Race conditions handled via rebase+retry (up to MAX_MERGE_RETRIES attempts)
|
|
23
|
+
* - Cleanup guaranteed even on failure
|
|
24
|
+
*
|
|
25
|
+
* Consumers:
|
|
26
|
+
* @see {@link tools/wu-create.mjs} - WU creation (WU-1262, WU-1439)
|
|
27
|
+
* @see {@link tools/wu-edit.mjs} - Spec edits (WU-1274)
|
|
28
|
+
* @see {@link tools/initiative-create.mjs} - Initiative creation (WU-1439)
|
|
29
|
+
*/
|
|
30
|
+
import { getGitForCwd, createGitForPath } from './git-adapter.js';
|
|
31
|
+
import { existsSync, rmSync, mkdtempSync } from 'node:fs';
|
|
32
|
+
import { execSync } from 'node:child_process';
|
|
33
|
+
import { tmpdir } from 'node:os';
|
|
34
|
+
import { join } from 'node:path';
|
|
35
|
+
import { BRANCHES, REMOTES, GIT_REFS, PKG_MANAGER, SCRIPTS, PRETTIER_FLAGS, STDIO_MODES, } from './wu-constants.js';
|
|
36
|
+
/**
|
|
37
|
+
* Maximum retry attempts for ff-only merge when main moves
|
|
38
|
+
*
|
|
39
|
+
* This handles race conditions when multiple agents run wu:create or wu:edit
|
|
40
|
+
* concurrently. Each retry fetches latest main and rebases.
|
|
41
|
+
*/
|
|
42
|
+
export const MAX_MERGE_RETRIES = 3;
|
|
43
|
+
/**
|
|
44
|
+
* Default log prefix for micro-worktree operations
|
|
45
|
+
*
|
|
46
|
+
* Extracted to constant to satisfy sonarjs/no-duplicate-string rule.
|
|
47
|
+
*/
|
|
48
|
+
export const DEFAULT_LOG_PREFIX = '[micro-wt]';
|
|
49
|
+
/**
|
|
50
|
+
* Temp branch prefix for micro-worktree operations
|
|
51
|
+
*
|
|
52
|
+
* @param {string} operation - Operation name (e.g., 'wu-create', 'wu-edit')
|
|
53
|
+
* @param {string} id - WU ID (e.g., 'wu-123')
|
|
54
|
+
* @returns {string} Temp branch name (e.g., 'tmp/wu-create/wu-123')
|
|
55
|
+
*/
|
|
56
|
+
export function getTempBranchName(operation, id) {
|
|
57
|
+
return `${BRANCHES.TEMP_PREFIX}${operation}/${id.toLowerCase()}`;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Create micro-worktree in /tmp directory
|
|
61
|
+
*
|
|
62
|
+
* @param {string} prefix - Directory prefix (e.g., 'wu-create-', 'wu-edit-')
|
|
63
|
+
* @returns {string} Path to created micro-worktree directory
|
|
64
|
+
*/
|
|
65
|
+
export function createMicroWorktreeDir(prefix) {
|
|
66
|
+
return mkdtempSync(join(tmpdir(), prefix));
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Parse git worktree list output to find worktrees by branch
|
|
70
|
+
*
|
|
71
|
+
* WU-2237: Helper to parse porcelain format output from `git worktree list --porcelain`
|
|
72
|
+
*
|
|
73
|
+
* @param {string} worktreeListOutput - Output from git worktree list --porcelain
|
|
74
|
+
* @param {string} branchName - Branch name to search for (e.g., 'tmp/wu-create/wu-123')
|
|
75
|
+
* @returns {string|null} Worktree path if found, null otherwise
|
|
76
|
+
*/
|
|
77
|
+
export function findWorktreeByBranch(worktreeListOutput, branchName) {
|
|
78
|
+
const branchRef = `refs/heads/${branchName}`;
|
|
79
|
+
const lines = worktreeListOutput.split('\n');
|
|
80
|
+
let currentWorktreePath = null;
|
|
81
|
+
for (const line of lines) {
|
|
82
|
+
if (line.startsWith('worktree ')) {
|
|
83
|
+
currentWorktreePath = line.substring('worktree '.length);
|
|
84
|
+
}
|
|
85
|
+
else if (line.startsWith('branch ') && line.substring('branch '.length) === branchRef) {
|
|
86
|
+
return currentWorktreePath;
|
|
87
|
+
}
|
|
88
|
+
else if (line === '') {
|
|
89
|
+
currentWorktreePath = null;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Clean up orphaned micro-worktree and temp branch from a previous interrupted operation
|
|
96
|
+
*
|
|
97
|
+
* WU-2237: Before creating a new micro-worktree, detect and clean any existing temp
|
|
98
|
+
* branch/worktree for the same operation+WU ID. This handles scenarios where:
|
|
99
|
+
* - A previous wu:create/wu:edit was interrupted (timeout/crash)
|
|
100
|
+
* - The temp branch and /tmp worktree were left behind
|
|
101
|
+
* - A subsequent operation would fail with 'branch already exists'
|
|
102
|
+
*
|
|
103
|
+
* This function is idempotent - safe to call even when no orphans exist.
|
|
104
|
+
*
|
|
105
|
+
* @param {string} operation - Operation name (e.g., 'wu-create', 'wu-edit')
|
|
106
|
+
* @param {string} id - WU ID (e.g., 'WU-123')
|
|
107
|
+
* @param {Object} gitAdapter - GitAdapter instance to use (for testability)
|
|
108
|
+
* @param {string} logPrefix - Log prefix for console output
|
|
109
|
+
* @returns {Promise<{cleanedWorktree: boolean, cleanedBranch: boolean}>} Cleanup status
|
|
110
|
+
*/
|
|
111
|
+
export async function cleanupOrphanedMicroWorktree(operation, id, gitAdapter, logPrefix = DEFAULT_LOG_PREFIX) {
|
|
112
|
+
const tempBranchName = getTempBranchName(operation, id);
|
|
113
|
+
let cleanedWorktree = false;
|
|
114
|
+
let cleanedBranch = false;
|
|
115
|
+
// Step 1: Check git worktree list for any worktree on this temp branch
|
|
116
|
+
try {
|
|
117
|
+
const worktreeListOutput = await gitAdapter.worktreeList();
|
|
118
|
+
const orphanWorktreePath = findWorktreeByBranch(worktreeListOutput, tempBranchName);
|
|
119
|
+
if (orphanWorktreePath) {
|
|
120
|
+
console.log(`${logPrefix} Found orphaned worktree for ${tempBranchName}: ${orphanWorktreePath}`);
|
|
121
|
+
try {
|
|
122
|
+
await gitAdapter.worktreeRemove(orphanWorktreePath, { force: true });
|
|
123
|
+
console.log(`${logPrefix} ✅ Removed orphaned worktree: ${orphanWorktreePath}`);
|
|
124
|
+
cleanedWorktree = true;
|
|
125
|
+
}
|
|
126
|
+
catch (err) {
|
|
127
|
+
console.warn(`${logPrefix} ⚠️ Could not remove orphaned worktree: ${err.message}`);
|
|
128
|
+
// Try filesystem cleanup as fallback
|
|
129
|
+
tryFilesystemCleanup(orphanWorktreePath);
|
|
130
|
+
cleanedWorktree = true;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
catch (err) {
|
|
135
|
+
console.warn(`${logPrefix} ⚠️ Could not check worktree list: ${err.message}`);
|
|
136
|
+
}
|
|
137
|
+
// Step 2: Check if the temp branch exists and delete it
|
|
138
|
+
try {
|
|
139
|
+
const branchExists = await gitAdapter.branchExists(tempBranchName);
|
|
140
|
+
if (branchExists) {
|
|
141
|
+
console.log(`${logPrefix} Found orphaned temp branch: ${tempBranchName}`);
|
|
142
|
+
await gitAdapter.deleteBranch(tempBranchName, { force: true });
|
|
143
|
+
console.log(`${logPrefix} ✅ Deleted orphaned temp branch: ${tempBranchName}`);
|
|
144
|
+
cleanedBranch = true;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
catch (err) {
|
|
148
|
+
console.warn(`${logPrefix} ⚠️ Could not delete orphaned branch: ${err.message}`);
|
|
149
|
+
}
|
|
150
|
+
return { cleanedWorktree, cleanedBranch };
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Try to remove a worktree path via filesystem as fallback
|
|
154
|
+
*
|
|
155
|
+
* WU-2237: Extracted helper to reduce cognitive complexity.
|
|
156
|
+
*
|
|
157
|
+
* @param {string} worktreePath - Path to remove
|
|
158
|
+
*/
|
|
159
|
+
function tryFilesystemCleanup(worktreePath) {
|
|
160
|
+
try {
|
|
161
|
+
// eslint-disable-next-line security/detect-non-literal-fs-filename -- CLI tool with validated worktree path
|
|
162
|
+
if (existsSync(worktreePath)) {
|
|
163
|
+
rmSync(worktreePath, { recursive: true, force: true });
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
catch {
|
|
167
|
+
// Ignore filesystem cleanup errors
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Remove a worktree using git, with filesystem fallback
|
|
172
|
+
*
|
|
173
|
+
* WU-2237: Extracted helper to reduce cognitive complexity.
|
|
174
|
+
*
|
|
175
|
+
* @param {Object} gitAdapter - Git adapter instance
|
|
176
|
+
* @param {string} worktreePath - Path to worktree
|
|
177
|
+
* @param {string} logPrefix - Log prefix
|
|
178
|
+
* @param {string} [contextLabel] - Optional label for logging (e.g., 'registered')
|
|
179
|
+
*/
|
|
180
|
+
async function removeWorktreeSafe(gitAdapter, worktreePath, logPrefix, contextLabel = '') {
|
|
181
|
+
const label = contextLabel ? ` ${contextLabel}` : '';
|
|
182
|
+
try {
|
|
183
|
+
await gitAdapter.worktreeRemove(worktreePath, { force: true });
|
|
184
|
+
if (contextLabel) {
|
|
185
|
+
console.log(`${logPrefix} ✅ Removed${label} worktree: ${worktreePath}`);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
catch (err) {
|
|
189
|
+
console.warn(`${logPrefix} ⚠️ Could not remove${label} worktree: ${err.message}`);
|
|
190
|
+
tryFilesystemCleanup(worktreePath);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Cleanup micro-worktree and temp branch
|
|
195
|
+
*
|
|
196
|
+
* Runs even on failure to prevent orphaned resources.
|
|
197
|
+
* Safe to call multiple times (idempotent).
|
|
198
|
+
*
|
|
199
|
+
* WU-2237: Enhanced to also check git worktree list for registered worktrees
|
|
200
|
+
* on the temp branch, in case the worktree path differs from what was expected.
|
|
201
|
+
*
|
|
202
|
+
* @param {string} worktreePath - Path to micro-worktree
|
|
203
|
+
* @param {string} branchName - Temp branch name
|
|
204
|
+
* @param {string} logPrefix - Log prefix for console output
|
|
205
|
+
*/
|
|
206
|
+
export async function cleanupMicroWorktree(worktreePath, branchName, logPrefix = DEFAULT_LOG_PREFIX) {
|
|
207
|
+
console.log(`${logPrefix} Cleaning up micro-worktree...`);
|
|
208
|
+
const mainGit = getGitForCwd();
|
|
209
|
+
// Remove the known worktree path first (must be done before deleting branch)
|
|
210
|
+
// eslint-disable-next-line security/detect-non-literal-fs-filename -- CLI tool with validated worktree path
|
|
211
|
+
if (existsSync(worktreePath)) {
|
|
212
|
+
await removeWorktreeSafe(mainGit, worktreePath, logPrefix);
|
|
213
|
+
}
|
|
214
|
+
// WU-2237: Also check git worktree list for any registered worktrees on this branch
|
|
215
|
+
await cleanupRegisteredWorktreeForBranch(mainGit, branchName, worktreePath, logPrefix);
|
|
216
|
+
// Delete temp branch
|
|
217
|
+
await deleteBranchSafe(mainGit, branchName, logPrefix);
|
|
218
|
+
console.log(`${logPrefix} ✅ Cleanup complete`);
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* Clean up any registered worktree for a branch that differs from the expected path
|
|
222
|
+
*
|
|
223
|
+
* WU-2237: Extracted helper to reduce cognitive complexity.
|
|
224
|
+
*
|
|
225
|
+
* @param {Object} gitAdapter - Git adapter instance
|
|
226
|
+
* @param {string} branchName - Branch name to search for
|
|
227
|
+
* @param {string} expectedPath - Expected worktree path (skip if matches)
|
|
228
|
+
* @param {string} logPrefix - Log prefix
|
|
229
|
+
*/
|
|
230
|
+
async function cleanupRegisteredWorktreeForBranch(gitAdapter, branchName, expectedPath, logPrefix) {
|
|
231
|
+
try {
|
|
232
|
+
const worktreeListOutput = await gitAdapter.worktreeList();
|
|
233
|
+
const registeredPath = findWorktreeByBranch(worktreeListOutput, branchName);
|
|
234
|
+
if (registeredPath && registeredPath !== expectedPath) {
|
|
235
|
+
console.log(`${logPrefix} Found additional registered worktree: ${registeredPath}`);
|
|
236
|
+
await removeWorktreeSafe(gitAdapter, registeredPath, logPrefix, 'registered');
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
catch (err) {
|
|
240
|
+
console.warn(`${logPrefix} ⚠️ Could not check worktree list: ${err.message}`);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* Delete a branch safely, ignoring errors
|
|
245
|
+
*
|
|
246
|
+
* WU-2237: Extracted helper to reduce cognitive complexity.
|
|
247
|
+
*
|
|
248
|
+
* @param {Object} gitAdapter - Git adapter instance
|
|
249
|
+
* @param {string} branchName - Branch to delete
|
|
250
|
+
* @param {string} logPrefix - Log prefix
|
|
251
|
+
*/
|
|
252
|
+
async function deleteBranchSafe(gitAdapter, branchName, logPrefix) {
|
|
253
|
+
try {
|
|
254
|
+
const branchExists = await gitAdapter.branchExists(branchName);
|
|
255
|
+
if (branchExists) {
|
|
256
|
+
await gitAdapter.deleteBranch(branchName, { force: true });
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
catch (err) {
|
|
260
|
+
console.warn(`${logPrefix} ⚠️ Could not delete branch: ${err.message}`);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* Stage changes including deletions in micro-worktree
|
|
265
|
+
*
|
|
266
|
+
* WU-1813: Uses addWithDeletions to properly stage tracked file deletions.
|
|
267
|
+
* This replaces the previous pattern of using gitWorktree.add(files) which
|
|
268
|
+
* could miss deletions when files were removed.
|
|
269
|
+
*
|
|
270
|
+
* @param {Object} gitWorktree - GitAdapter instance for the worktree
|
|
271
|
+
* @param {string[]|undefined} files - Files to stage (undefined/empty = stage all)
|
|
272
|
+
* @returns {Promise<void>}
|
|
273
|
+
*/
|
|
274
|
+
export async function stageChangesWithDeletions(gitWorktree, files) {
|
|
275
|
+
// Normalise undefined/null to empty array for addWithDeletions
|
|
276
|
+
const filesToStage = files || [];
|
|
277
|
+
await gitWorktree.addWithDeletions(filesToStage);
|
|
278
|
+
}
|
|
279
|
+
/**
|
|
280
|
+
* Format files using prettier before committing
|
|
281
|
+
*
|
|
282
|
+
* WU-1435: Ensures committed files pass format gates.
|
|
283
|
+
* Runs prettier --write on specified files within the micro-worktree.
|
|
284
|
+
*
|
|
285
|
+
* @param {string[]} files - Relative file paths to format
|
|
286
|
+
* @param {string} worktreePath - Path to the micro-worktree
|
|
287
|
+
* @param {string} logPrefix - Log prefix for console output
|
|
288
|
+
*/
|
|
289
|
+
export async function formatFiles(files, worktreePath, logPrefix = DEFAULT_LOG_PREFIX) {
|
|
290
|
+
if (!files || files.length === 0) {
|
|
291
|
+
return;
|
|
292
|
+
}
|
|
293
|
+
console.log(`${logPrefix} Formatting ${files.length} file(s)...`);
|
|
294
|
+
// Build absolute paths within the worktree
|
|
295
|
+
const absolutePaths = files.map((f) => join(worktreePath, f));
|
|
296
|
+
const pathArgs = absolutePaths.map((p) => JSON.stringify(p)).join(' ');
|
|
297
|
+
try {
|
|
298
|
+
execSync(`${PKG_MANAGER} ${SCRIPTS.PRETTIER} ${PRETTIER_FLAGS.WRITE} ${pathArgs}`, {
|
|
299
|
+
encoding: 'utf-8',
|
|
300
|
+
stdio: STDIO_MODES.PIPE,
|
|
301
|
+
cwd: worktreePath,
|
|
302
|
+
});
|
|
303
|
+
console.log(`${logPrefix} ✅ Files formatted`);
|
|
304
|
+
}
|
|
305
|
+
catch (err) {
|
|
306
|
+
// Log warning but don't fail - some files may not need formatting
|
|
307
|
+
console.warn(`${logPrefix} ⚠️ Formatting warning: ${err.message}`);
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
/**
|
|
311
|
+
* Merge temp branch to main with ff-only and retry logic
|
|
312
|
+
*
|
|
313
|
+
* Handles race conditions when main branch advances between operation start
|
|
314
|
+
* and merge attempt. Retries with rebase up to MAX_MERGE_RETRIES times.
|
|
315
|
+
*
|
|
316
|
+
* @param {string} tempBranchName - Temp branch to merge
|
|
317
|
+
* @param {string} microWorktreePath - Path to micro-worktree (for rebase)
|
|
318
|
+
* @param {string} logPrefix - Log prefix for console output
|
|
319
|
+
* @throws {Error} If merge fails after all retries
|
|
320
|
+
*/
|
|
321
|
+
export async function mergeWithRetry(tempBranchName, microWorktreePath, logPrefix = DEFAULT_LOG_PREFIX) {
|
|
322
|
+
const gitWorktree = createGitForPath(microWorktreePath);
|
|
323
|
+
const mainGit = getGitForCwd();
|
|
324
|
+
for (let attempt = 1; attempt <= MAX_MERGE_RETRIES; attempt++) {
|
|
325
|
+
try {
|
|
326
|
+
console.log(`${logPrefix} Merging to main (attempt ${attempt}/${MAX_MERGE_RETRIES})...`);
|
|
327
|
+
await mainGit.merge(tempBranchName, { ffOnly: true });
|
|
328
|
+
console.log(`${logPrefix} ✅ Merged to main`);
|
|
329
|
+
return;
|
|
330
|
+
}
|
|
331
|
+
catch (mergeErr) {
|
|
332
|
+
if (attempt < MAX_MERGE_RETRIES) {
|
|
333
|
+
console.log(`${logPrefix} ⚠️ FF-only merge failed (main moved). Rebasing...`);
|
|
334
|
+
// Fetch latest main and rebase temp branch
|
|
335
|
+
await mainGit.fetch(REMOTES.ORIGIN, BRANCHES.MAIN);
|
|
336
|
+
await mainGit.merge(`${REMOTES.ORIGIN}/${BRANCHES.MAIN}`, { ffOnly: true }); // Update local main
|
|
337
|
+
await gitWorktree.rebase(BRANCHES.MAIN);
|
|
338
|
+
}
|
|
339
|
+
else {
|
|
340
|
+
throw new Error(`FF-only merge failed after ${MAX_MERGE_RETRIES} attempts. ` +
|
|
341
|
+
`Main branch may have significant divergence.\n` +
|
|
342
|
+
`Error: ${mergeErr.message}`);
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
/**
|
|
348
|
+
* Execute an operation in a micro-worktree with full isolation
|
|
349
|
+
*
|
|
350
|
+
* This is the main entry point for micro-worktree operations.
|
|
351
|
+
* Handles the full lifecycle: create temp branch, create worktree,
|
|
352
|
+
* execute operation, merge, push, and cleanup.
|
|
353
|
+
*
|
|
354
|
+
* WU-1435: Added pushOnly option to keep local main pristine.
|
|
355
|
+
* WU-2237: Added pre-creation cleanup of orphaned temp branches/worktrees.
|
|
356
|
+
*
|
|
357
|
+
* @param {Object} options - Options for the operation
|
|
358
|
+
* @param {string} options.operation - Operation name (e.g., 'wu-create', 'wu-edit')
|
|
359
|
+
* @param {string} options.id - WU ID (e.g., 'WU-123')
|
|
360
|
+
* @param {string} options.logPrefix - Log prefix for console output
|
|
361
|
+
* @param {boolean} [options.pushOnly=false] - Skip local main merge, push directly to origin/main
|
|
362
|
+
* @param {Function} options.execute - Async function to execute in micro-worktree
|
|
363
|
+
* Receives: { worktreePath: string, gitWorktree: GitAdapter }
|
|
364
|
+
* Should return: { commitMessage: string, files: string[] }
|
|
365
|
+
* @returns {Promise<Object>} Result with ref property for worktree creation
|
|
366
|
+
* @throws {Error} If any step fails (cleanup still runs)
|
|
367
|
+
*/
|
|
368
|
+
export async function withMicroWorktree(options) {
|
|
369
|
+
const { operation, id, logPrefix = `[${operation}]`, execute, pushOnly = false } = options;
|
|
370
|
+
const mainGit = getGitForCwd();
|
|
371
|
+
// WU-2237: Clean up any orphaned temp branch/worktree from previous interrupted operations
|
|
372
|
+
// This makes the operation idempotent - a retry after crash/timeout will succeed
|
|
373
|
+
await cleanupOrphanedMicroWorktree(operation, id, mainGit, logPrefix);
|
|
374
|
+
const tempBranchName = getTempBranchName(operation, id);
|
|
375
|
+
const microWorktreePath = createMicroWorktreeDir(`${operation}-`);
|
|
376
|
+
console.log(`${logPrefix} Using micro-worktree isolation (WU-1262)`);
|
|
377
|
+
console.log(`${logPrefix} Temp branch: ${tempBranchName}`);
|
|
378
|
+
console.log(`${logPrefix} Micro-worktree: ${microWorktreePath}`);
|
|
379
|
+
if (pushOnly) {
|
|
380
|
+
console.log(`${logPrefix} Push-only mode: local main will not be modified (WU-1435)`);
|
|
381
|
+
}
|
|
382
|
+
try {
|
|
383
|
+
// Step 1: Create temp branch without switching
|
|
384
|
+
console.log(`${logPrefix} Creating temp branch...`);
|
|
385
|
+
await mainGit.createBranchNoCheckout(tempBranchName, BRANCHES.MAIN);
|
|
386
|
+
// Step 2: Create micro-worktree pointing to temp branch
|
|
387
|
+
console.log(`${logPrefix} Creating micro-worktree...`);
|
|
388
|
+
await mainGit.worktreeAddExisting(microWorktreePath, tempBranchName);
|
|
389
|
+
// Step 3: Execute the operation in micro-worktree
|
|
390
|
+
const gitWorktree = createGitForPath(microWorktreePath);
|
|
391
|
+
const result = await execute({ worktreePath: microWorktreePath, gitWorktree });
|
|
392
|
+
// Step 4: Format files before committing (WU-1435)
|
|
393
|
+
await formatFiles(result.files, microWorktreePath, logPrefix);
|
|
394
|
+
// Step 5: Stage and commit in micro-worktree
|
|
395
|
+
// WU-1813: Use stageChangesWithDeletions to properly handle file deletions
|
|
396
|
+
console.log(`${logPrefix} Staging changes (including deletions)...`);
|
|
397
|
+
await stageChangesWithDeletions(gitWorktree, result.files);
|
|
398
|
+
console.log(`${logPrefix} Committing in micro-worktree...`);
|
|
399
|
+
await gitWorktree.commit(result.commitMessage);
|
|
400
|
+
console.log(`${logPrefix} ✅ Committed: ${result.commitMessage}`);
|
|
401
|
+
// Step 6: Push to origin (different paths for pushOnly vs standard)
|
|
402
|
+
if (pushOnly) {
|
|
403
|
+
// WU-1435: Push directly to origin/main without touching local main
|
|
404
|
+
console.log(`${logPrefix} Pushing directly to ${REMOTES.ORIGIN}/${BRANCHES.MAIN} (push-only)...`);
|
|
405
|
+
await gitWorktree.pushRefspec(REMOTES.ORIGIN, tempBranchName, BRANCHES.MAIN);
|
|
406
|
+
console.log(`${logPrefix} ✅ Pushed to ${REMOTES.ORIGIN}/${BRANCHES.MAIN}`);
|
|
407
|
+
// Fetch to update remote tracking ref (FETCH_HEAD)
|
|
408
|
+
console.log(`${logPrefix} Fetching ${REMOTES.ORIGIN}/${BRANCHES.MAIN}...`);
|
|
409
|
+
await mainGit.fetch(REMOTES.ORIGIN, BRANCHES.MAIN);
|
|
410
|
+
console.log(`${logPrefix} ✅ Fetched ${REMOTES.ORIGIN}/${BRANCHES.MAIN}`);
|
|
411
|
+
// Return FETCH_HEAD as ref for worktree creation
|
|
412
|
+
return { ...result, ref: GIT_REFS.FETCH_HEAD };
|
|
413
|
+
}
|
|
414
|
+
else {
|
|
415
|
+
// Standard path: merge to local main, then push
|
|
416
|
+
await mergeWithRetry(tempBranchName, microWorktreePath, logPrefix);
|
|
417
|
+
console.log(`${logPrefix} Pushing to ${REMOTES.ORIGIN}/${BRANCHES.MAIN}...`);
|
|
418
|
+
await mainGit.push(REMOTES.ORIGIN, BRANCHES.MAIN);
|
|
419
|
+
console.log(`${logPrefix} ✅ Pushed to ${REMOTES.ORIGIN}/${BRANCHES.MAIN}`);
|
|
420
|
+
return { ...result, ref: BRANCHES.MAIN };
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
finally {
|
|
424
|
+
// Cleanup (always runs)
|
|
425
|
+
await cleanupMicroWorktree(microWorktreePath, tempBranchName, logPrefix);
|
|
426
|
+
}
|
|
427
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file migration-deployer.mjs
|
|
3
|
+
* @description Migration deployment utilities for Supabase schema sync
|
|
4
|
+
* WU-1983: Sync production schema and establish migration deployment workflow
|
|
5
|
+
*
|
|
6
|
+
* Provides:
|
|
7
|
+
* - Local migration file discovery
|
|
8
|
+
* - Migration name extraction (timestamp + name)
|
|
9
|
+
* - Integration point for MCP-based deployment
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* Default migrations directory path (relative to repo root)
|
|
13
|
+
*/
|
|
14
|
+
export declare const MIGRATIONS_DIR = "supabase/supabase/migrations";
|
|
15
|
+
/**
|
|
16
|
+
* Extract migration info from filename
|
|
17
|
+
* @param {string} filename - Migration filename (e.g., '20251224125733_patient_documents_storage.sql')
|
|
18
|
+
* @returns {{ timestamp: string, name: string, fullName: string } | null}
|
|
19
|
+
*/
|
|
20
|
+
export declare function parseMigrationFilename(filename: any): {
|
|
21
|
+
timestamp: string;
|
|
22
|
+
name: string;
|
|
23
|
+
fullName: string;
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* Discover local migration files
|
|
27
|
+
* @param {string} baseDir - Base directory (repo root)
|
|
28
|
+
* @returns {{ files: Array<{ filename: string, timestamp: string, name: string, fullName: string, path: string }>, errors: string[] }}
|
|
29
|
+
*/
|
|
30
|
+
export declare function discoverLocalMigrations(baseDir: any): {
|
|
31
|
+
files: any[];
|
|
32
|
+
errors: any[];
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
35
|
+
* Read migration SQL content
|
|
36
|
+
* @param {string} migrationPath - Full path to migration file
|
|
37
|
+
* @returns {string} SQL content
|
|
38
|
+
*/
|
|
39
|
+
export declare function readMigrationContent(migrationPath: any): string;
|
|
40
|
+
/**
|
|
41
|
+
* Check if WU code_paths includes Supabase migrations
|
|
42
|
+
* @param {string[]} codePaths - Array of code paths from WU YAML
|
|
43
|
+
* @returns {boolean} True if migrations are in scope
|
|
44
|
+
*/
|
|
45
|
+
export declare function hasMigrationChanges(codePaths: any): boolean;
|
|
46
|
+
/**
|
|
47
|
+
* Compare local migrations with production (for drift detection)
|
|
48
|
+
* @param {Array<{ fullName: string }>} localMigrations - Local migration list
|
|
49
|
+
* @param {Array<{ name: string }>} productionMigrations - Production migration list (from MCP)
|
|
50
|
+
* @returns {{ missing: string[], extra: string[], synced: boolean }}
|
|
51
|
+
*/
|
|
52
|
+
export declare function compareMigrations(localMigrations: any, productionMigrations: any): {
|
|
53
|
+
missing: any;
|
|
54
|
+
extra: any;
|
|
55
|
+
synced: boolean;
|
|
56
|
+
};
|
|
57
|
+
/**
|
|
58
|
+
* Format migration deployment report
|
|
59
|
+
* @param {{ missing: string[], extra: string[], synced: boolean }} comparison
|
|
60
|
+
* @returns {string} Formatted report
|
|
61
|
+
*/
|
|
62
|
+
export declare function formatMigrationReport(comparison: any): string;
|
|
63
|
+
/**
|
|
64
|
+
* Build MCP migration deployment command (for documentation/agents)
|
|
65
|
+
* @param {string} migrationName - Full migration name (e.g., '20251224125733_patient_documents_storage')
|
|
66
|
+
* @param {string} sql - Migration SQL content
|
|
67
|
+
* @returns {string} MCP command description
|
|
68
|
+
*/
|
|
69
|
+
export declare function buildMCPDeploymentHint(migrationName: any, sql: any): string;
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file migration-deployer.mjs
|
|
3
|
+
* @description Migration deployment utilities for Supabase schema sync
|
|
4
|
+
* WU-1983: Sync production schema and establish migration deployment workflow
|
|
5
|
+
*
|
|
6
|
+
* Provides:
|
|
7
|
+
* - Local migration file discovery
|
|
8
|
+
* - Migration name extraction (timestamp + name)
|
|
9
|
+
* - Integration point for MCP-based deployment
|
|
10
|
+
*/
|
|
11
|
+
import { existsSync, readdirSync, readFileSync } from 'node:fs';
|
|
12
|
+
import path from 'node:path';
|
|
13
|
+
/**
|
|
14
|
+
* Default migrations directory path (relative to repo root)
|
|
15
|
+
*/
|
|
16
|
+
export const MIGRATIONS_DIR = 'supabase/supabase/migrations';
|
|
17
|
+
/**
|
|
18
|
+
* Migration file pattern (YYYYMMDDHHMMSS_name.sql)
|
|
19
|
+
* @type {RegExp}
|
|
20
|
+
*/
|
|
21
|
+
const MIGRATION_FILE_PATTERN = /^(\d{14})_(.+)\.sql$/;
|
|
22
|
+
/**
|
|
23
|
+
* Extract migration info from filename
|
|
24
|
+
* @param {string} filename - Migration filename (e.g., '20251224125733_patient_documents_storage.sql')
|
|
25
|
+
* @returns {{ timestamp: string, name: string, fullName: string } | null}
|
|
26
|
+
*/
|
|
27
|
+
export function parseMigrationFilename(filename) {
|
|
28
|
+
const match = MIGRATION_FILE_PATTERN.exec(filename);
|
|
29
|
+
if (!match)
|
|
30
|
+
return null;
|
|
31
|
+
return {
|
|
32
|
+
timestamp: match[1],
|
|
33
|
+
name: match[2],
|
|
34
|
+
fullName: `${match[1]}_${match[2]}`,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Discover local migration files
|
|
39
|
+
* @param {string} baseDir - Base directory (repo root)
|
|
40
|
+
* @returns {{ files: Array<{ filename: string, timestamp: string, name: string, fullName: string, path: string }>, errors: string[] }}
|
|
41
|
+
*/
|
|
42
|
+
export function discoverLocalMigrations(baseDir) {
|
|
43
|
+
const migrationsPath = path.join(baseDir, MIGRATIONS_DIR);
|
|
44
|
+
const errors = [];
|
|
45
|
+
const files = [];
|
|
46
|
+
if (!existsSync(migrationsPath)) {
|
|
47
|
+
errors.push(`Migrations directory not found: ${migrationsPath}`);
|
|
48
|
+
return { files, errors };
|
|
49
|
+
}
|
|
50
|
+
try {
|
|
51
|
+
const entries = readdirSync(migrationsPath, { withFileTypes: true });
|
|
52
|
+
for (const entry of entries) {
|
|
53
|
+
if (!entry.isFile() || !entry.name.endsWith('.sql'))
|
|
54
|
+
continue;
|
|
55
|
+
const parsed = parseMigrationFilename(entry.name);
|
|
56
|
+
if (!parsed) {
|
|
57
|
+
errors.push(`Invalid migration filename format: ${entry.name}`);
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
files.push({
|
|
61
|
+
filename: entry.name,
|
|
62
|
+
timestamp: parsed.timestamp,
|
|
63
|
+
name: parsed.name,
|
|
64
|
+
fullName: parsed.fullName,
|
|
65
|
+
path: path.join(migrationsPath, entry.name),
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
// Sort by timestamp (ascending)
|
|
69
|
+
files.sort((a, b) => a.timestamp.localeCompare(b.timestamp));
|
|
70
|
+
}
|
|
71
|
+
catch (err) {
|
|
72
|
+
errors.push(`Error reading migrations directory: ${err.message}`);
|
|
73
|
+
}
|
|
74
|
+
return { files, errors };
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Read migration SQL content
|
|
78
|
+
* @param {string} migrationPath - Full path to migration file
|
|
79
|
+
* @returns {string} SQL content
|
|
80
|
+
*/
|
|
81
|
+
export function readMigrationContent(migrationPath) {
|
|
82
|
+
return readFileSync(migrationPath, 'utf8');
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Check if WU code_paths includes Supabase migrations
|
|
86
|
+
* @param {string[]} codePaths - Array of code paths from WU YAML
|
|
87
|
+
* @returns {boolean} True if migrations are in scope
|
|
88
|
+
*/
|
|
89
|
+
export function hasMigrationChanges(codePaths) {
|
|
90
|
+
if (!Array.isArray(codePaths))
|
|
91
|
+
return false;
|
|
92
|
+
const migrationPatterns = ['supabase/', 'supabase/supabase/migrations/', MIGRATIONS_DIR];
|
|
93
|
+
return codePaths.some((codePath) => migrationPatterns.some((pattern) => codePath.startsWith(pattern) || codePath === pattern.slice(0, -1)));
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Compare local migrations with production (for drift detection)
|
|
97
|
+
* @param {Array<{ fullName: string }>} localMigrations - Local migration list
|
|
98
|
+
* @param {Array<{ name: string }>} productionMigrations - Production migration list (from MCP)
|
|
99
|
+
* @returns {{ missing: string[], extra: string[], synced: boolean }}
|
|
100
|
+
*/
|
|
101
|
+
export function compareMigrations(localMigrations, productionMigrations) {
|
|
102
|
+
const localNames = new Set(localMigrations.map((m) => m.fullName));
|
|
103
|
+
const prodNames = new Set(productionMigrations.map((m) => m.name));
|
|
104
|
+
// Migrations in local but not in production
|
|
105
|
+
const missing = localMigrations.filter((m) => !prodNames.has(m.fullName)).map((m) => m.fullName);
|
|
106
|
+
// Migrations in production but not in local (unusual but possible)
|
|
107
|
+
const extra = productionMigrations.filter((m) => !localNames.has(m.name)).map((m) => m.name);
|
|
108
|
+
return {
|
|
109
|
+
missing,
|
|
110
|
+
extra,
|
|
111
|
+
synced: missing.length === 0 && extra.length === 0,
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Format migration deployment report
|
|
116
|
+
* @param {{ missing: string[], extra: string[], synced: boolean }} comparison
|
|
117
|
+
* @returns {string} Formatted report
|
|
118
|
+
*/
|
|
119
|
+
export function formatMigrationReport(comparison) {
|
|
120
|
+
const lines = [];
|
|
121
|
+
if (comparison.synced) {
|
|
122
|
+
lines.push('Migrations are in sync between local and production.');
|
|
123
|
+
return lines.join('\n');
|
|
124
|
+
}
|
|
125
|
+
if (comparison.missing.length > 0) {
|
|
126
|
+
lines.push('Migrations missing from production:');
|
|
127
|
+
comparison.missing.forEach((name) => lines.push(` - ${name}`));
|
|
128
|
+
lines.push('');
|
|
129
|
+
}
|
|
130
|
+
if (comparison.extra.length > 0) {
|
|
131
|
+
lines.push('Migrations in production but not local (unexpected):');
|
|
132
|
+
comparison.extra.forEach((name) => lines.push(` - ${name}`));
|
|
133
|
+
lines.push('');
|
|
134
|
+
}
|
|
135
|
+
return lines.join('\n');
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Build MCP migration deployment command (for documentation/agents)
|
|
139
|
+
* @param {string} migrationName - Full migration name (e.g., '20251224125733_patient_documents_storage')
|
|
140
|
+
* @param {string} sql - Migration SQL content
|
|
141
|
+
* @returns {string} MCP command description
|
|
142
|
+
*/
|
|
143
|
+
export function buildMCPDeploymentHint(migrationName, sql) {
|
|
144
|
+
return `To deploy ${migrationName} to production:
|
|
145
|
+
|
|
146
|
+
Use mcp__supabase__apply_migration with:
|
|
147
|
+
- name: "${migrationName}"
|
|
148
|
+
- sql: <content of migration file>
|
|
149
|
+
|
|
150
|
+
Or use mcp__supabase__execute_sql for individual statements.`;
|
|
151
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Orchestration Advisory Loader
|
|
3
|
+
*
|
|
4
|
+
* Pure JavaScript implementation of mandatory agent advisory.
|
|
5
|
+
* Uses minimatch for glob pattern matching (same as TypeScript version).
|
|
6
|
+
*
|
|
7
|
+
* @module orchestration-advisory-loader
|
|
8
|
+
* @see {@link ./orchestration-advisory.mjs} - TypeScript version for tests
|
|
9
|
+
* @see {@link ./domain/orchestration.constants.mjs} - Pattern definitions
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* Emit mandatory agent advisory based on code paths.
|
|
13
|
+
*
|
|
14
|
+
* @param {string[]} codePaths - Array of file paths
|
|
15
|
+
* @param {string} wuId - Work Unit ID
|
|
16
|
+
*/
|
|
17
|
+
export declare function emitMandatoryAgentAdvisory(codePaths: any, wuId: any): void;
|
|
18
|
+
/**
|
|
19
|
+
* Check mandatory agent compliance.
|
|
20
|
+
*
|
|
21
|
+
* @param {string[]} codePaths - Array of file paths
|
|
22
|
+
* @param {string} _wuId - Work Unit ID (reserved for future telemetry lookup)
|
|
23
|
+
* @returns {{compliant: boolean, missing: string[]}}
|
|
24
|
+
*/
|
|
25
|
+
export declare function checkMandatoryAgentsCompliance(codePaths: any, _wuId: any): {
|
|
26
|
+
compliant: boolean;
|
|
27
|
+
missing: unknown[];
|
|
28
|
+
};
|