@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,312 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Prompt Linter with 3-Tier Token Budget Enforcement
|
|
3
|
+
*
|
|
4
|
+
* Enforces token budget constraints on LLM prompts:
|
|
5
|
+
* - BLOCK: >450 tokens OR +>120 delta (exit 1)
|
|
6
|
+
* - WARN: ≥400 tokens OR +>50 delta (continue, log warning)
|
|
7
|
+
* - LOG: Always log tokenCount, delta, hash, top 3 longest lines
|
|
8
|
+
*
|
|
9
|
+
* Uses proper telemetry via getLogger() (no console spam).
|
|
10
|
+
*
|
|
11
|
+
* Part of WU-676: Single-Call LLM Orchestrator token budget enforcement.
|
|
12
|
+
*/
|
|
13
|
+
import { analyzePrompt, getLongestLines } from './token-counter.js';
|
|
14
|
+
import { readFile, writeFile, mkdir, appendFile, access } from 'node:fs/promises';
|
|
15
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
16
|
+
import { resolve, dirname } from 'path';
|
|
17
|
+
import { fileURLToPath } from 'url';
|
|
18
|
+
import { glob } from 'glob';
|
|
19
|
+
import yaml from 'yaml';
|
|
20
|
+
import { EXIT_CODES, STRING_LITERALS } from './wu-constants.js';
|
|
21
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
22
|
+
const __dirname = dirname(__filename);
|
|
23
|
+
const ROOT_DIR = resolve(__dirname, '../..');
|
|
24
|
+
// Default config path
|
|
25
|
+
const DEFAULT_CONFIG_PATH = resolve(ROOT_DIR, 'config/prompts/linter.yml');
|
|
26
|
+
// Telemetry cache path (for storing previous metrics)
|
|
27
|
+
const METRICS_CACHE_PATH = resolve(ROOT_DIR, '.beacon/telemetry/prompt-metrics.json');
|
|
28
|
+
/**
|
|
29
|
+
* Load config from YAML file with fallback to defaults
|
|
30
|
+
* @param {string} configPath - Path to config file (optional)
|
|
31
|
+
* @returns {Object} Configuration object
|
|
32
|
+
*/
|
|
33
|
+
export function loadConfig(configPath = DEFAULT_CONFIG_PATH) {
|
|
34
|
+
// Default configuration (fallback if file missing or invalid)
|
|
35
|
+
const defaults = {
|
|
36
|
+
version: 1,
|
|
37
|
+
token_budgets: {
|
|
38
|
+
default: {
|
|
39
|
+
hard_cap: 450,
|
|
40
|
+
warn_threshold: 400,
|
|
41
|
+
},
|
|
42
|
+
retry: {
|
|
43
|
+
hard_cap: 150,
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
delta_budgets: {
|
|
47
|
+
warn: 50,
|
|
48
|
+
block: 120,
|
|
49
|
+
},
|
|
50
|
+
retry_pattern: 'retry',
|
|
51
|
+
};
|
|
52
|
+
try {
|
|
53
|
+
if (!existsSync(configPath)) {
|
|
54
|
+
// Config file not found, use defaults
|
|
55
|
+
return defaults;
|
|
56
|
+
}
|
|
57
|
+
const content = readFileSync(configPath, { encoding: 'utf-8' });
|
|
58
|
+
const parsed = yaml.parse(content);
|
|
59
|
+
// Merge parsed config with defaults (handles incomplete configs)
|
|
60
|
+
return {
|
|
61
|
+
version: parsed.version ?? defaults.version,
|
|
62
|
+
token_budgets: {
|
|
63
|
+
default: {
|
|
64
|
+
hard_cap: parsed.token_budgets?.default?.hard_cap ?? defaults.token_budgets.default.hard_cap,
|
|
65
|
+
warn_threshold: parsed.token_budgets?.default?.warn_threshold ??
|
|
66
|
+
defaults.token_budgets.default.warn_threshold,
|
|
67
|
+
},
|
|
68
|
+
retry: {
|
|
69
|
+
hard_cap: parsed.token_budgets?.retry?.hard_cap ?? defaults.token_budgets.retry.hard_cap,
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
delta_budgets: {
|
|
73
|
+
warn: parsed.delta_budgets?.warn ?? defaults.delta_budgets.warn,
|
|
74
|
+
block: parsed.delta_budgets?.block ?? defaults.delta_budgets.block,
|
|
75
|
+
},
|
|
76
|
+
retry_pattern: parsed.retry_pattern ?? defaults.retry_pattern,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
catch (_error) {
|
|
80
|
+
// YAML parsing error or other failure, use defaults
|
|
81
|
+
console.error(`⚠️ Failed to load config from ${configPath}: ${_error.message}`);
|
|
82
|
+
console.error(` Using default token budgets.`);
|
|
83
|
+
return defaults;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Load previous metrics from cache
|
|
88
|
+
* @returns {Promise<Object>} Previous metrics by file path
|
|
89
|
+
*/
|
|
90
|
+
async function loadPreviousMetrics() {
|
|
91
|
+
try {
|
|
92
|
+
const fileExists = await access(METRICS_CACHE_PATH)
|
|
93
|
+
.then(() => true)
|
|
94
|
+
.catch(() => false);
|
|
95
|
+
if (fileExists) {
|
|
96
|
+
const data = await readFile(METRICS_CACHE_PATH, { encoding: 'utf-8' });
|
|
97
|
+
return JSON.parse(data);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
catch {
|
|
101
|
+
// Ignore errors, return empty metrics
|
|
102
|
+
}
|
|
103
|
+
return {};
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Save current metrics to cache
|
|
107
|
+
* @param {Object} metrics - Metrics by file path
|
|
108
|
+
* @returns {Promise<void>}
|
|
109
|
+
*/
|
|
110
|
+
async function savemetrics(metrics) {
|
|
111
|
+
try {
|
|
112
|
+
const dir = dirname(METRICS_CACHE_PATH);
|
|
113
|
+
const dirExists = await access(dir)
|
|
114
|
+
.then(() => true)
|
|
115
|
+
.catch(() => false);
|
|
116
|
+
if (!dirExists) {
|
|
117
|
+
await mkdir(dir, { recursive: true });
|
|
118
|
+
}
|
|
119
|
+
await writeFile(METRICS_CACHE_PATH, JSON.stringify(metrics, null, 2));
|
|
120
|
+
}
|
|
121
|
+
catch {
|
|
122
|
+
// Ignore errors (cache is optional)
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Log via proper telemetry (simulated getLogger for CLI context)
|
|
127
|
+
* In production, this would use apps/web/src/lib/logger.ts
|
|
128
|
+
* @param {string} level - Log level (info, warn, error)
|
|
129
|
+
* @param {string} event - Event name
|
|
130
|
+
* @param {Object} data - Structured data
|
|
131
|
+
* @param {LogOutputOptions} [output] - Output mode
|
|
132
|
+
* @returns {Promise<void>}
|
|
133
|
+
*/
|
|
134
|
+
async function log(level, event, data, output = {}) {
|
|
135
|
+
const timestamp = new Date().toISOString();
|
|
136
|
+
const entry = {
|
|
137
|
+
timestamp,
|
|
138
|
+
level,
|
|
139
|
+
event,
|
|
140
|
+
...data,
|
|
141
|
+
};
|
|
142
|
+
// For CLI, write to .beacon/telemetry/prompt-lint.ndjson
|
|
143
|
+
const ndjsonPath = resolve(ROOT_DIR, '.beacon/telemetry/prompt-lint.ndjson');
|
|
144
|
+
const line = `${JSON.stringify(entry)}${STRING_LITERALS.NEWLINE}`;
|
|
145
|
+
try {
|
|
146
|
+
const dir = dirname(ndjsonPath);
|
|
147
|
+
const dirExists = await access(dir)
|
|
148
|
+
.then(() => true)
|
|
149
|
+
.catch(() => false);
|
|
150
|
+
if (!dirExists) {
|
|
151
|
+
await mkdir(dir, { recursive: true });
|
|
152
|
+
}
|
|
153
|
+
await appendFile(ndjsonPath, line);
|
|
154
|
+
}
|
|
155
|
+
catch {
|
|
156
|
+
// Fallback to stderr if file write fails
|
|
157
|
+
console.error(JSON.stringify(entry));
|
|
158
|
+
}
|
|
159
|
+
const shouldPrintToConsole = output.verbose ? true : output.quiet ? level !== 'info' : true;
|
|
160
|
+
// Also output human-readable to stderr
|
|
161
|
+
const levelEmoji = {
|
|
162
|
+
info: '📊',
|
|
163
|
+
warn: '⚠️ ',
|
|
164
|
+
error: '❌',
|
|
165
|
+
}[level] || ' ';
|
|
166
|
+
if (shouldPrintToConsole) {
|
|
167
|
+
const message = `${levelEmoji} [${event}] ${data.file || ''} ${data.tokenCount ? `${data.tokenCount} tokens` : ''}`;
|
|
168
|
+
console.error(message);
|
|
169
|
+
if (data.delta !== undefined) {
|
|
170
|
+
console.error(` Delta: ${data.delta > 0 ? '+' : ''}${data.delta} tokens`);
|
|
171
|
+
}
|
|
172
|
+
if (data.message) {
|
|
173
|
+
console.error(` ${data.message}`);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Lint a single prompt file
|
|
179
|
+
* @param {string} filePath - Absolute path to prompt file
|
|
180
|
+
* @param {Object} previousMetrics - Previous metrics for delta calculation
|
|
181
|
+
* @param {string} mode - Mode (pre-commit, pre-push, ci, local)
|
|
182
|
+
* @param {Object} config - Configuration object from loadConfig()
|
|
183
|
+
* @param {{quiet?: boolean, verbose?: boolean}} output - Output mode
|
|
184
|
+
* @returns {Promise<{passed: boolean, tokenCount: number, delta: number, hash: string}>}
|
|
185
|
+
*/
|
|
186
|
+
async function lintPromptFile(filePath, previousMetrics, mode, config, output) {
|
|
187
|
+
// Analyze prompt
|
|
188
|
+
const { tokenCount, hash, text } = analyzePrompt(filePath);
|
|
189
|
+
// Calculate delta from previous metrics
|
|
190
|
+
const previous = previousMetrics[filePath];
|
|
191
|
+
const delta = previous ? tokenCount - previous.tokenCount : 0;
|
|
192
|
+
// Determine cap based on file name and config pattern
|
|
193
|
+
const isRetryPrompt = filePath.includes(config.retry_pattern);
|
|
194
|
+
const cap = isRetryPrompt
|
|
195
|
+
? config.token_budgets.retry.hard_cap
|
|
196
|
+
: config.token_budgets.default.hard_cap;
|
|
197
|
+
// Get top 3 longest lines for cleanup targeting
|
|
198
|
+
const longestLines = getLongestLines(text, 3);
|
|
199
|
+
// BLOCK: hard cap or sudden bloat
|
|
200
|
+
if (tokenCount > cap || delta > config.delta_budgets.block) {
|
|
201
|
+
await log('error', 'prompt.lint.blocked', {
|
|
202
|
+
file: filePath,
|
|
203
|
+
tokenCount,
|
|
204
|
+
delta,
|
|
205
|
+
hash,
|
|
206
|
+
cap,
|
|
207
|
+
mode,
|
|
208
|
+
message: `Exceeds ${cap} token cap or delta >${config.delta_budgets.block}`,
|
|
209
|
+
longestLines: longestLines.map((l) => `Line ${l.number}: ${l.length} chars`),
|
|
210
|
+
}, output);
|
|
211
|
+
return { passed: false, tokenCount, delta, hash };
|
|
212
|
+
}
|
|
213
|
+
// WARN: approaching cap or gradual creep
|
|
214
|
+
if (tokenCount >= config.token_budgets.default.warn_threshold ||
|
|
215
|
+
delta > config.delta_budgets.warn) {
|
|
216
|
+
await log('warn', 'prompt.lint.warning', {
|
|
217
|
+
file: filePath,
|
|
218
|
+
tokenCount,
|
|
219
|
+
delta,
|
|
220
|
+
hash,
|
|
221
|
+
threshold: config.token_budgets.default.warn_threshold,
|
|
222
|
+
mode,
|
|
223
|
+
message: 'Approaching token budget cap',
|
|
224
|
+
longestLines: longestLines.map((l) => `Line ${l.number}: ${l.length} chars`),
|
|
225
|
+
}, output);
|
|
226
|
+
}
|
|
227
|
+
// LOG: always log metrics
|
|
228
|
+
await log('info', 'prompt.lint.measured', {
|
|
229
|
+
file: filePath,
|
|
230
|
+
tokenCount,
|
|
231
|
+
delta,
|
|
232
|
+
hash,
|
|
233
|
+
mode,
|
|
234
|
+
longestLines: longestLines.map((l) => `Line ${l.number}: ${l.length} chars`),
|
|
235
|
+
}, output);
|
|
236
|
+
return { passed: true, tokenCount, delta, hash };
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* Main linter function
|
|
240
|
+
* @param {string[]} filePaths - Prompt files to lint (optional, finds all if empty)
|
|
241
|
+
* @param {string} mode - Mode (pre-commit, pre-push, ci, local)
|
|
242
|
+
* @param {string} configPath - Optional config file path
|
|
243
|
+
* @param {LintPromptsOptions} [options] - Output options
|
|
244
|
+
* @returns {Promise<{passed: boolean, results: Array, config: Object}>}
|
|
245
|
+
*/
|
|
246
|
+
export async function lintPrompts(filePaths = [], mode = 'local', configPath = undefined, options = {}) {
|
|
247
|
+
// Load configuration
|
|
248
|
+
const config = loadConfig(configPath);
|
|
249
|
+
const output = { quiet: options.quiet === true, verbose: options.verbose === true };
|
|
250
|
+
// If no files provided, find all orchestrator prompt files (WU-676 scope only)
|
|
251
|
+
if (filePaths.length === 0) {
|
|
252
|
+
const pattern = 'packages/@patientpath/prompts/orchestrator-*/**/*.yaml';
|
|
253
|
+
filePaths = await glob(pattern, { cwd: ROOT_DIR, absolute: true });
|
|
254
|
+
}
|
|
255
|
+
// Load previous metrics for delta calculation
|
|
256
|
+
const previousMetrics = await loadPreviousMetrics();
|
|
257
|
+
// Lint each file
|
|
258
|
+
const results = [];
|
|
259
|
+
let allPassed = true;
|
|
260
|
+
for (const filePath of filePaths) {
|
|
261
|
+
const result = await lintPromptFile(filePath, previousMetrics, mode, config, output);
|
|
262
|
+
results.push({ filePath, ...result });
|
|
263
|
+
if (!result.passed) {
|
|
264
|
+
allPassed = false;
|
|
265
|
+
}
|
|
266
|
+
// Update metrics cache
|
|
267
|
+
previousMetrics[filePath] = {
|
|
268
|
+
tokenCount: result.tokenCount,
|
|
269
|
+
hash: result.hash,
|
|
270
|
+
timestamp: new Date().toISOString(),
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
// Save updated metrics
|
|
274
|
+
await savemetrics(previousMetrics);
|
|
275
|
+
return { passed: allPassed, results, config };
|
|
276
|
+
}
|
|
277
|
+
/**
|
|
278
|
+
* CLI entry point
|
|
279
|
+
*/
|
|
280
|
+
async function main() {
|
|
281
|
+
const args = process.argv.slice(2);
|
|
282
|
+
const modeFlag = args.find((arg) => arg.startsWith('--mode='));
|
|
283
|
+
const mode = modeFlag ? modeFlag.split('=')[1] : 'local';
|
|
284
|
+
const quiet = args.includes('--quiet');
|
|
285
|
+
const verbose = args.includes('--verbose');
|
|
286
|
+
// Get files to lint (from args or find all)
|
|
287
|
+
const files = args.filter((arg) => !arg.startsWith('--'));
|
|
288
|
+
console.error(`\n🔍 Linting prompts (mode: ${mode})...\n`);
|
|
289
|
+
const { passed, results, config } = await lintPrompts(files, mode, undefined, { quiet, verbose });
|
|
290
|
+
// Summary
|
|
291
|
+
const total = results.length;
|
|
292
|
+
const blocked = results.filter((r) => !r.passed).length;
|
|
293
|
+
const warned = results.filter((r) => r.passed && r.tokenCount >= config.token_budgets.default.warn_threshold).length;
|
|
294
|
+
console.error(`\n📋 Summary: ${total} prompts analyzed`);
|
|
295
|
+
if (blocked > 0) {
|
|
296
|
+
console.error(` ❌ ${blocked} BLOCKED (>${config.token_budgets.default.hard_cap} tokens or delta >${config.delta_budgets.block})`);
|
|
297
|
+
}
|
|
298
|
+
if (warned > 0) {
|
|
299
|
+
console.error(` ⚠️ ${warned} WARNING (≥${config.token_budgets.default.warn_threshold} tokens or delta >${config.delta_budgets.warn})`);
|
|
300
|
+
}
|
|
301
|
+
if (blocked === 0 && warned === 0) {
|
|
302
|
+
console.error(` ✅ All prompts within budget`);
|
|
303
|
+
}
|
|
304
|
+
process.exit(passed ? EXIT_CODES.SUCCESS : EXIT_CODES.ERROR);
|
|
305
|
+
}
|
|
306
|
+
// Run CLI if executed directly
|
|
307
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
308
|
+
main().catch((error) => {
|
|
309
|
+
console.error('Prompt linter failed:', error);
|
|
310
|
+
process.exit(EXIT_CODES.ERROR);
|
|
311
|
+
});
|
|
312
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Nightly Prompt Monitor
|
|
3
|
+
*
|
|
4
|
+
* Monitors all prompts for token budget drift and hash changes.
|
|
5
|
+
* Designed to run as a GitHub Actions cron job (nightly at 2 AM).
|
|
6
|
+
*
|
|
7
|
+
* Logs to .beacon/telemetry/prompt-nightly.ndjson and Axiom.
|
|
8
|
+
* Alerts if:
|
|
9
|
+
* - Any prompt ≥400 tokens (approaching cap)
|
|
10
|
+
* - Any delta >50 tokens since yesterday
|
|
11
|
+
* - rules_hash changed outside a WU (unintentional drift)
|
|
12
|
+
*
|
|
13
|
+
* Part of WU-676: Single-Call LLM Orchestrator token budget enforcement.
|
|
14
|
+
*/
|
|
15
|
+
export {};
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Nightly Prompt Monitor
|
|
3
|
+
*
|
|
4
|
+
* Monitors all prompts for token budget drift and hash changes.
|
|
5
|
+
* Designed to run as a GitHub Actions cron job (nightly at 2 AM).
|
|
6
|
+
*
|
|
7
|
+
* Logs to .beacon/telemetry/prompt-nightly.ndjson and Axiom.
|
|
8
|
+
* Alerts if:
|
|
9
|
+
* - Any prompt ≥400 tokens (approaching cap)
|
|
10
|
+
* - Any delta >50 tokens since yesterday
|
|
11
|
+
* - rules_hash changed outside a WU (unintentional drift)
|
|
12
|
+
*
|
|
13
|
+
* Part of WU-676: Single-Call LLM Orchestrator token budget enforcement.
|
|
14
|
+
*/
|
|
15
|
+
import { analyzePrompt } from './token-counter.js';
|
|
16
|
+
import { readFile, writeFile, mkdir, appendFile, access } from 'node:fs/promises';
|
|
17
|
+
import { resolve, dirname } from 'path';
|
|
18
|
+
import { fileURLToPath } from 'url';
|
|
19
|
+
import { glob } from 'glob';
|
|
20
|
+
import { EXIT_CODES, STRING_LITERALS } from './wu-constants.js';
|
|
21
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
22
|
+
const __dirname = dirname(__filename);
|
|
23
|
+
const ROOT_DIR = resolve(__dirname, '../..');
|
|
24
|
+
// Paths
|
|
25
|
+
const YESTERDAY_METRICS_PATH = resolve(ROOT_DIR, '.beacon/telemetry/prompt-metrics-yesterday.json');
|
|
26
|
+
const TODAY_METRICS_PATH = resolve(ROOT_DIR, '.beacon/telemetry/prompt-metrics.json');
|
|
27
|
+
const NDJSON_LOG_PATH = resolve(ROOT_DIR, '.beacon/telemetry/prompt-nightly.ndjson');
|
|
28
|
+
// Alert thresholds
|
|
29
|
+
const WARN_THRESHOLD = 400;
|
|
30
|
+
const DELTA_THRESHOLD = 50;
|
|
31
|
+
/**
|
|
32
|
+
* Load yesterday's metrics
|
|
33
|
+
*/
|
|
34
|
+
async function loadYesterdayMetrics() {
|
|
35
|
+
try {
|
|
36
|
+
const exists = await access(YESTERDAY_METRICS_PATH)
|
|
37
|
+
.then(() => true)
|
|
38
|
+
.catch(() => false);
|
|
39
|
+
if (exists) {
|
|
40
|
+
const data = await readFile(YESTERDAY_METRICS_PATH, { encoding: 'utf-8' });
|
|
41
|
+
return JSON.parse(data);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
catch (error) {
|
|
45
|
+
console.error('Failed to load yesterday metrics:', error);
|
|
46
|
+
}
|
|
47
|
+
return {};
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Save today's metrics
|
|
51
|
+
*/
|
|
52
|
+
async function saveMetrics(metrics) {
|
|
53
|
+
try {
|
|
54
|
+
const dir = dirname(TODAY_METRICS_PATH);
|
|
55
|
+
const dirExists = await access(dir)
|
|
56
|
+
.then(() => true)
|
|
57
|
+
.catch(() => false);
|
|
58
|
+
if (!dirExists) {
|
|
59
|
+
await mkdir(dir, { recursive: true });
|
|
60
|
+
}
|
|
61
|
+
await writeFile(TODAY_METRICS_PATH, JSON.stringify(metrics, null, 2));
|
|
62
|
+
}
|
|
63
|
+
catch (error) {
|
|
64
|
+
console.error('Failed to save metrics:', error);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Log to NDJSON
|
|
69
|
+
*/
|
|
70
|
+
async function log(event, data) {
|
|
71
|
+
const entry = {
|
|
72
|
+
timestamp: new Date().toISOString(),
|
|
73
|
+
event,
|
|
74
|
+
...data,
|
|
75
|
+
};
|
|
76
|
+
const line = `${JSON.stringify(entry)}${STRING_LITERALS.NEWLINE}`;
|
|
77
|
+
try {
|
|
78
|
+
const dir = dirname(NDJSON_LOG_PATH);
|
|
79
|
+
const dirExists = await access(dir)
|
|
80
|
+
.then(() => true)
|
|
81
|
+
.catch(() => false);
|
|
82
|
+
if (!dirExists) {
|
|
83
|
+
await mkdir(dir, { recursive: true });
|
|
84
|
+
}
|
|
85
|
+
await appendFile(NDJSON_LOG_PATH, line);
|
|
86
|
+
}
|
|
87
|
+
catch (error) {
|
|
88
|
+
console.error('Failed to log:', error);
|
|
89
|
+
}
|
|
90
|
+
// Also output to stdout for GitHub Actions logs
|
|
91
|
+
console.log(JSON.stringify(entry));
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Main monitor function
|
|
95
|
+
*/
|
|
96
|
+
async function monitor() {
|
|
97
|
+
console.log('\n🌙 Nightly Prompt Monitor Starting...\n');
|
|
98
|
+
// Find all prompt files
|
|
99
|
+
const pattern = 'packages/@patientpath/prompts/**/*.yaml';
|
|
100
|
+
const promptFiles = await glob(pattern, { cwd: ROOT_DIR, absolute: true });
|
|
101
|
+
console.log(`Found ${promptFiles.length} prompt files to analyze\n`);
|
|
102
|
+
// Load yesterday's metrics for delta calculation
|
|
103
|
+
const yesterdayMetrics = await loadYesterdayMetrics();
|
|
104
|
+
const todayMetrics = {};
|
|
105
|
+
let totalAlerts = 0;
|
|
106
|
+
for (const filePath of promptFiles) {
|
|
107
|
+
try {
|
|
108
|
+
const { tokenCount, hash } = analyzePrompt(filePath);
|
|
109
|
+
const yesterday = yesterdayMetrics[filePath];
|
|
110
|
+
// Calculate delta
|
|
111
|
+
const delta = yesterday ? tokenCount - yesterday.tokenCount : 0;
|
|
112
|
+
const hashChanged = yesterday ? hash !== yesterday.hash : false;
|
|
113
|
+
// Store today's metrics
|
|
114
|
+
todayMetrics[filePath] = {
|
|
115
|
+
tokenCount,
|
|
116
|
+
hash,
|
|
117
|
+
timestamp: new Date().toISOString(),
|
|
118
|
+
};
|
|
119
|
+
// Log metrics
|
|
120
|
+
await log('prompt.nightly.metrics', {
|
|
121
|
+
prompt: filePath,
|
|
122
|
+
tokenCount,
|
|
123
|
+
hash,
|
|
124
|
+
delta,
|
|
125
|
+
hashChanged,
|
|
126
|
+
});
|
|
127
|
+
// Alert: approaching cap
|
|
128
|
+
if (tokenCount >= WARN_THRESHOLD) {
|
|
129
|
+
totalAlerts++;
|
|
130
|
+
await log('prompt.nightly.approaching_cap', {
|
|
131
|
+
prompt: filePath,
|
|
132
|
+
tokenCount,
|
|
133
|
+
cap: 450,
|
|
134
|
+
message: `Prompt at ${tokenCount} tokens (approaching 450 cap)`,
|
|
135
|
+
});
|
|
136
|
+
console.error(`⚠️ ${filePath}: ${tokenCount} tokens (approaching cap)`);
|
|
137
|
+
}
|
|
138
|
+
// Alert: significant delta
|
|
139
|
+
if (Math.abs(delta) > DELTA_THRESHOLD) {
|
|
140
|
+
totalAlerts++;
|
|
141
|
+
await log('prompt.nightly.significant_delta', {
|
|
142
|
+
prompt: filePath,
|
|
143
|
+
tokenCount,
|
|
144
|
+
delta,
|
|
145
|
+
message: `Delta ${delta > 0 ? '+' : ''}${delta} tokens exceeds threshold`,
|
|
146
|
+
});
|
|
147
|
+
console.error(`⚠️ ${filePath}: Delta ${delta > 0 ? '+' : ''}${delta} tokens`);
|
|
148
|
+
}
|
|
149
|
+
// Alert: hash changed
|
|
150
|
+
if (hashChanged) {
|
|
151
|
+
totalAlerts++;
|
|
152
|
+
await log('prompt.nightly.hash_changed', {
|
|
153
|
+
prompt: filePath,
|
|
154
|
+
oldHash: yesterday.hash,
|
|
155
|
+
newHash: hash,
|
|
156
|
+
message: 'Prompt hash changed - investigate if intentional',
|
|
157
|
+
});
|
|
158
|
+
console.error(`⚠️ ${filePath}: Hash changed (${yesterday.hash} → ${hash})`);
|
|
159
|
+
}
|
|
160
|
+
// Success log (no alerts)
|
|
161
|
+
if (tokenCount < WARN_THRESHOLD && Math.abs(delta) <= DELTA_THRESHOLD && !hashChanged) {
|
|
162
|
+
console.log(`✅ ${filePath}: ${tokenCount} tokens (delta: ${delta})`);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
catch (error) {
|
|
166
|
+
console.error(`❌ Failed to analyze ${filePath}:`, error);
|
|
167
|
+
await log('prompt.nightly.analysis_failed', {
|
|
168
|
+
prompt: filePath,
|
|
169
|
+
error: error instanceof Error ? error.message : String(error),
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
// Save today's metrics for tomorrow's delta calculation
|
|
174
|
+
await saveMetrics(todayMetrics);
|
|
175
|
+
// Rotate metrics (today becomes yesterday)
|
|
176
|
+
const todayExists = await access(TODAY_METRICS_PATH)
|
|
177
|
+
.then(() => true)
|
|
178
|
+
.catch(() => false);
|
|
179
|
+
if (todayExists) {
|
|
180
|
+
try {
|
|
181
|
+
const todayData = await readFile(TODAY_METRICS_PATH, { encoding: 'utf-8' });
|
|
182
|
+
await writeFile(YESTERDAY_METRICS_PATH, todayData);
|
|
183
|
+
}
|
|
184
|
+
catch (error) {
|
|
185
|
+
console.error('Failed to rotate metrics:', error);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
// Summary
|
|
189
|
+
console.log(`\n📊 Nightly Monitor Complete`);
|
|
190
|
+
console.log(` Total prompts: ${promptFiles.length}`);
|
|
191
|
+
console.log(` Alerts: ${totalAlerts}`);
|
|
192
|
+
if (totalAlerts > 0) {
|
|
193
|
+
console.log(`\n⚠️ Review alerts above and investigate if changes were intentional\n`);
|
|
194
|
+
process.exit(EXIT_CODES.ERROR); // Exit with error to trigger GitHub Actions notification
|
|
195
|
+
}
|
|
196
|
+
else {
|
|
197
|
+
console.log(`\n✅ All prompts within budget and stable\n`);
|
|
198
|
+
process.exit(EXIT_CODES.SUCCESS);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
// Run monitor
|
|
202
|
+
monitor().catch((error) => {
|
|
203
|
+
console.error('Nightly monitor failed:', error);
|
|
204
|
+
process.exit(EXIT_CODES.ERROR);
|
|
205
|
+
});
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rebase Artifact Cleanup
|
|
3
|
+
*
|
|
4
|
+
* Detects and cleans up completion artifacts (stamps, status=done)
|
|
5
|
+
* that appear in worktree after rebasing from main.
|
|
6
|
+
*
|
|
7
|
+
* This prevents contradictory state where an in_progress WU has
|
|
8
|
+
* completion markers from a previous completion cycle on main.
|
|
9
|
+
*
|
|
10
|
+
* Part of WU-1371: Post-rebase artifact cleanup
|
|
11
|
+
* WU-1449: Extended to handle backlog/status duplicates after rebase
|
|
12
|
+
*
|
|
13
|
+
* @see {@link tools/wu-done.mjs} - Creates completion artifacts
|
|
14
|
+
* @see {@link tools/lib/stamp-utils.mjs} - Stamp file utilities
|
|
15
|
+
* @see {@link tools/lib/wu-recovery.mjs} - Related zombie state handling
|
|
16
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* Detect rebased completion artifacts in a worktree
|
|
19
|
+
*
|
|
20
|
+
* WU-1817: Now verifies artifacts exist on origin/main before flagging.
|
|
21
|
+
* Only artifacts that exist on BOTH worktree AND origin/main are true
|
|
22
|
+
* rebased artifacts. Artifacts that exist only locally (created by the
|
|
23
|
+
* lane branch itself) should NOT be cleaned - this was the WU-1816 bug.
|
|
24
|
+
*
|
|
25
|
+
* Checks for:
|
|
26
|
+
* 1. Stamp files (.beacon/stamps/WU-{id}.done) that exist on origin/main
|
|
27
|
+
* 2. WU YAML with status=done that also has status=done on origin/main
|
|
28
|
+
*
|
|
29
|
+
* @param {string} worktreePath - Path to the worktree directory
|
|
30
|
+
* @param {string} wuId - WU ID (e.g., 'WU-1371')
|
|
31
|
+
* @param {object} gitAdapter - Git adapter instance with raw() method
|
|
32
|
+
* @returns {Promise<object>} Detection result
|
|
33
|
+
* @returns {string[]} result.stamps - Array of detected stamp file paths (only if on origin/main)
|
|
34
|
+
* @returns {boolean} result.yamlStatusDone - True if YAML has status=done AND origin/main has done
|
|
35
|
+
* @returns {boolean} result.hasArtifacts - True if any rebased artifacts detected
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* const result = await detectRebasedArtifacts(worktreePath, wuId, gitAdapter);
|
|
39
|
+
* if (result.hasArtifacts) {
|
|
40
|
+
* console.log('Found rebased artifacts, cleaning up...');
|
|
41
|
+
* }
|
|
42
|
+
*/
|
|
43
|
+
export declare function detectRebasedArtifacts(worktreePath: any, wuId: any, gitAdapter: any): Promise<{
|
|
44
|
+
stamps: any[];
|
|
45
|
+
yamlStatusDone: boolean;
|
|
46
|
+
hasArtifacts: boolean;
|
|
47
|
+
}>;
|
|
48
|
+
/**
|
|
49
|
+
* Clean up rebased completion artifacts from a worktree
|
|
50
|
+
*
|
|
51
|
+
* Actions:
|
|
52
|
+
* 1. Remove stamp files that shouldn't exist
|
|
53
|
+
* 2. Reset YAML status from done to in_progress
|
|
54
|
+
* 3. Remove locked and completed_at fields from YAML
|
|
55
|
+
* 4. Log warnings explaining cleanup actions
|
|
56
|
+
*
|
|
57
|
+
* Idempotent: Safe to call multiple times, won't throw if artifacts don't exist.
|
|
58
|
+
*
|
|
59
|
+
* @param {string} worktreePath - Path to the worktree directory
|
|
60
|
+
* @param {string} wuId - WU ID (e.g., 'WU-1371')
|
|
61
|
+
* @returns {Promise<object>} Cleanup result
|
|
62
|
+
* @returns {string[]} result.stampsCleaned - WU IDs whose stamps were removed
|
|
63
|
+
* @returns {boolean} result.yamlReset - True if YAML status was reset
|
|
64
|
+
* @returns {string[]} result.errors - Any errors encountered (non-fatal)
|
|
65
|
+
* @returns {boolean} result.cleaned - True if any cleanup was performed
|
|
66
|
+
*
|
|
67
|
+
* @example
|
|
68
|
+
* const result = await cleanupRebasedArtifacts(worktreePath, wuId);
|
|
69
|
+
* if (result.cleaned) {
|
|
70
|
+
* console.log('Cleaned rebased artifacts:', result);
|
|
71
|
+
* }
|
|
72
|
+
*/
|
|
73
|
+
export declare function cleanupRebasedArtifacts(worktreePath: any, wuId: any): Promise<{
|
|
74
|
+
stampsCleaned: any[];
|
|
75
|
+
yamlReset: boolean;
|
|
76
|
+
backlogCleaned: boolean;
|
|
77
|
+
statusCleaned: boolean;
|
|
78
|
+
errors: any[];
|
|
79
|
+
cleaned: boolean;
|
|
80
|
+
}>;
|
|
81
|
+
/**
|
|
82
|
+
* Detect WU duplicates in backlog/status files after rebase
|
|
83
|
+
*
|
|
84
|
+
* Checks if a WU appears in both:
|
|
85
|
+
* - "In Progress" AND "Done" sections of backlog.md
|
|
86
|
+
* - "In Progress" AND "Completed" sections of status.md
|
|
87
|
+
*
|
|
88
|
+
* This state occurs when main advanced with WU completion,
|
|
89
|
+
* then rebase merged main's "Done" state into the worktree
|
|
90
|
+
* while the worktree already had the WU in "In Progress".
|
|
91
|
+
*
|
|
92
|
+
* Part of WU-1449: Extend rebase cleanup to remove backlog/status duplicates
|
|
93
|
+
*
|
|
94
|
+
* @param {string} worktreePath - Path to the worktree directory
|
|
95
|
+
* @param {string} wuId - WU ID (e.g., 'WU-1449')
|
|
96
|
+
* @returns {Promise<object>} Detection result
|
|
97
|
+
* @returns {boolean} result.backlogDuplicate - True if WU in both In Progress and Done in backlog.md
|
|
98
|
+
* @returns {boolean} result.statusDuplicate - True if WU in both In Progress and Completed in status.md
|
|
99
|
+
* @returns {boolean} result.hasDuplicates - True if any duplicates detected
|
|
100
|
+
*
|
|
101
|
+
* @example
|
|
102
|
+
* const result = await detectBacklogDuplicates(worktreePath, wuId);
|
|
103
|
+
* if (result.hasDuplicates) {
|
|
104
|
+
* console.log('Found backlog duplicates, cleaning up...');
|
|
105
|
+
* }
|
|
106
|
+
*/
|
|
107
|
+
export declare function detectBacklogDuplicates(worktreePath: any, wuId: any): Promise<{
|
|
108
|
+
backlogDuplicate: boolean;
|
|
109
|
+
statusDuplicate: boolean;
|
|
110
|
+
hasDuplicates: boolean;
|
|
111
|
+
}>;
|
|
112
|
+
/**
|
|
113
|
+
* Remove WU from In Progress sections when already in Done/Completed after rebase
|
|
114
|
+
*
|
|
115
|
+
* This handles the specific case where:
|
|
116
|
+
* 1. WU is completing (wu:done in progress)
|
|
117
|
+
* 2. Auto-rebase pulls main's completion state
|
|
118
|
+
* 3. WU now appears in BOTH In Progress AND Done sections
|
|
119
|
+
* 4. This function removes the duplicate from In Progress (keeps Done)
|
|
120
|
+
*
|
|
121
|
+
* Applies to both backlog.md and status.md.
|
|
122
|
+
* Idempotent: Safe to call multiple times.
|
|
123
|
+
*
|
|
124
|
+
* Part of WU-1449: Extend rebase cleanup to remove backlog/status duplicates
|
|
125
|
+
*
|
|
126
|
+
* @param {string} worktreePath - Path to the worktree directory
|
|
127
|
+
* @param {string} wuId - WU ID (e.g., 'WU-1449')
|
|
128
|
+
* @returns {Promise<object>} Cleanup result
|
|
129
|
+
* @returns {boolean} result.backlogCleaned - True if WU removed from backlog.md In Progress
|
|
130
|
+
* @returns {boolean} result.statusCleaned - True if WU removed from status.md In Progress
|
|
131
|
+
* @returns {boolean} result.cleaned - True if any cleanup was performed
|
|
132
|
+
* @returns {string[]} result.errors - Any errors encountered (non-fatal)
|
|
133
|
+
*
|
|
134
|
+
* @example
|
|
135
|
+
* const result = await deduplicateBacklogAfterRebase(worktreePath, wuId);
|
|
136
|
+
* if (result.cleaned) {
|
|
137
|
+
* console.log('Cleaned backlog duplicates:', result);
|
|
138
|
+
* }
|
|
139
|
+
*/
|
|
140
|
+
export declare function deduplicateBacklogAfterRebase(worktreePath: any, wuId: any): Promise<{
|
|
141
|
+
backlogCleaned: boolean;
|
|
142
|
+
statusCleaned: boolean;
|
|
143
|
+
cleaned: boolean;
|
|
144
|
+
errors: any[];
|
|
145
|
+
}>;
|