@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,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WU YAML Auto-Fixer
|
|
3
|
+
*
|
|
4
|
+
* Detects and auto-repairs common YAML validation issues that would fail at wu:done.
|
|
5
|
+
* Part of WU-1359: Early YAML validation at wu:claim.
|
|
6
|
+
*
|
|
7
|
+
* Common issues fixed:
|
|
8
|
+
* - ISO timestamp dates → YYYY-MM-DD format
|
|
9
|
+
* - Username → email format (with configurable domain)
|
|
10
|
+
* - docs → documentation (type field)
|
|
11
|
+
* - String numbers → number (phase field)
|
|
12
|
+
*
|
|
13
|
+
* @see {@link tools/lib/wu-schema.mjs} - WU schema definition
|
|
14
|
+
* @see {@link tools/wu-claim.mjs} - Consumer
|
|
15
|
+
*/
|
|
16
|
+
/**
|
|
17
|
+
* Issue types that can be detected and auto-fixed
|
|
18
|
+
*/
|
|
19
|
+
export declare const FIXABLE_ISSUES: {
|
|
20
|
+
DATE_ISO_TIMESTAMP: string;
|
|
21
|
+
USERNAME_NOT_EMAIL: string;
|
|
22
|
+
TYPE_ALIAS: string;
|
|
23
|
+
PHASE_STRING: string;
|
|
24
|
+
PRIORITY_LOWERCASE: string;
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* Detects fixable issues in WU YAML data
|
|
28
|
+
*
|
|
29
|
+
* @param {object} doc - Parsed WU YAML data
|
|
30
|
+
* @returns {Array<{type: string, field: string, current: unknown, suggested: unknown, description: string}>}
|
|
31
|
+
*/
|
|
32
|
+
export declare function detectFixableIssues(doc: any): {
|
|
33
|
+
type: string;
|
|
34
|
+
field: string;
|
|
35
|
+
current: any;
|
|
36
|
+
suggested: any;
|
|
37
|
+
description: string;
|
|
38
|
+
}[];
|
|
39
|
+
/**
|
|
40
|
+
* Applies fixes to WU YAML data in-place
|
|
41
|
+
*
|
|
42
|
+
* @param {object} doc - Parsed WU YAML data (will be modified)
|
|
43
|
+
* @param {Array<{type: string, field: string, suggested: unknown}>} issues - Issues to fix
|
|
44
|
+
* @returns {number} Number of fixes applied
|
|
45
|
+
*/
|
|
46
|
+
export declare function applyFixes(doc: any, issues: any): number;
|
|
47
|
+
/**
|
|
48
|
+
* Options for auto-fixing WU YAML
|
|
49
|
+
*/
|
|
50
|
+
export interface AutoFixWUYamlOptions {
|
|
51
|
+
/** If true, report issues without fixing */
|
|
52
|
+
dryRun?: boolean;
|
|
53
|
+
/** If true, create .bak file before fixing */
|
|
54
|
+
backup?: boolean;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Auto-fix WU YAML file
|
|
58
|
+
*
|
|
59
|
+
* @param {string} wuPath - Path to WU YAML file
|
|
60
|
+
* @param {AutoFixWUYamlOptions} options - Options
|
|
61
|
+
* @returns {{fixed: number, issues: Array, backupPath?: string}}
|
|
62
|
+
*/
|
|
63
|
+
export declare function autoFixWUYaml(wuPath: any, options?: AutoFixWUYamlOptions): {
|
|
64
|
+
fixed: number;
|
|
65
|
+
issues: any[];
|
|
66
|
+
wouldFix?: undefined;
|
|
67
|
+
backupPath?: undefined;
|
|
68
|
+
} | {
|
|
69
|
+
fixed: number;
|
|
70
|
+
issues: {
|
|
71
|
+
type: string;
|
|
72
|
+
field: string;
|
|
73
|
+
current: any;
|
|
74
|
+
suggested: any;
|
|
75
|
+
description: string;
|
|
76
|
+
}[];
|
|
77
|
+
wouldFix: number;
|
|
78
|
+
backupPath?: undefined;
|
|
79
|
+
} | {
|
|
80
|
+
fixed: number;
|
|
81
|
+
issues: {
|
|
82
|
+
type: string;
|
|
83
|
+
field: string;
|
|
84
|
+
current: any;
|
|
85
|
+
suggested: any;
|
|
86
|
+
description: string;
|
|
87
|
+
}[];
|
|
88
|
+
backupPath: any;
|
|
89
|
+
wouldFix?: undefined;
|
|
90
|
+
};
|
|
91
|
+
/**
|
|
92
|
+
* Format issues for CLI output
|
|
93
|
+
*
|
|
94
|
+
* @param {Array<{field: string, description: string}>} issues
|
|
95
|
+
* @returns {string}
|
|
96
|
+
*/
|
|
97
|
+
export declare function formatIssues(issues: any): any;
|
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WU YAML Auto-Fixer
|
|
3
|
+
*
|
|
4
|
+
* Detects and auto-repairs common YAML validation issues that would fail at wu:done.
|
|
5
|
+
* Part of WU-1359: Early YAML validation at wu:claim.
|
|
6
|
+
*
|
|
7
|
+
* Common issues fixed:
|
|
8
|
+
* - ISO timestamp dates → YYYY-MM-DD format
|
|
9
|
+
* - Username → email format (with configurable domain)
|
|
10
|
+
* - docs → documentation (type field)
|
|
11
|
+
* - String numbers → number (phase field)
|
|
12
|
+
*
|
|
13
|
+
* @see {@link tools/lib/wu-schema.mjs} - WU schema definition
|
|
14
|
+
* @see {@link tools/wu-claim.mjs} - Consumer
|
|
15
|
+
*/
|
|
16
|
+
import { readFileSync, writeFileSync, copyFileSync } from 'node:fs';
|
|
17
|
+
import { parseYAML, stringifyYAML } from './wu-yaml.js';
|
|
18
|
+
import { STRING_LITERALS } from './wu-constants.js';
|
|
19
|
+
// Valid type values from wu-schema.mjs
|
|
20
|
+
const VALID_TYPES = ['feature', 'bug', 'documentation', 'process', 'tooling', 'chore', 'refactor'];
|
|
21
|
+
// Common type aliases that should be auto-fixed
|
|
22
|
+
const TYPE_ALIASES = {
|
|
23
|
+
docs: 'documentation',
|
|
24
|
+
doc: 'documentation',
|
|
25
|
+
feat: 'feature',
|
|
26
|
+
fix: 'bug',
|
|
27
|
+
bugfix: 'bug',
|
|
28
|
+
tool: 'tooling',
|
|
29
|
+
tools: 'tooling',
|
|
30
|
+
ref: 'refactor',
|
|
31
|
+
proc: 'process',
|
|
32
|
+
};
|
|
33
|
+
// Default email domain for username → email conversion
|
|
34
|
+
const DEFAULT_EMAIL_DOMAIN = 'patientpath.co.uk';
|
|
35
|
+
/**
|
|
36
|
+
* Issue types that can be detected and auto-fixed
|
|
37
|
+
*/
|
|
38
|
+
export const FIXABLE_ISSUES = {
|
|
39
|
+
DATE_ISO_TIMESTAMP: 'DATE_ISO_TIMESTAMP',
|
|
40
|
+
USERNAME_NOT_EMAIL: 'USERNAME_NOT_EMAIL',
|
|
41
|
+
TYPE_ALIAS: 'TYPE_ALIAS',
|
|
42
|
+
PHASE_STRING: 'PHASE_STRING',
|
|
43
|
+
PRIORITY_LOWERCASE: 'PRIORITY_LOWERCASE',
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* Check created field for ISO timestamp issues
|
|
47
|
+
* @param {object} doc - Parsed WU YAML data
|
|
48
|
+
* @returns {object|null} Issue object or null
|
|
49
|
+
*/
|
|
50
|
+
function checkCreatedField(doc) {
|
|
51
|
+
if (!doc.created)
|
|
52
|
+
return null;
|
|
53
|
+
const createdStr = String(doc.created);
|
|
54
|
+
// Match ISO 8601 timestamp (2025-12-02T00:00:00.000Z or similar)
|
|
55
|
+
if (/^\d{4}-\d{2}-\d{2}T/.test(createdStr)) {
|
|
56
|
+
const dateOnly = createdStr.slice(0, 10);
|
|
57
|
+
return {
|
|
58
|
+
type: FIXABLE_ISSUES.DATE_ISO_TIMESTAMP,
|
|
59
|
+
field: 'created',
|
|
60
|
+
current: createdStr,
|
|
61
|
+
suggested: dateOnly,
|
|
62
|
+
description: `ISO timestamp should be YYYY-MM-DD: ${createdStr} → ${dateOnly}`,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
// Handle Date objects that YAML parser may create
|
|
66
|
+
if (doc.created instanceof Date) {
|
|
67
|
+
const dateOnly = doc.created.toISOString().slice(0, 10);
|
|
68
|
+
return {
|
|
69
|
+
type: FIXABLE_ISSUES.DATE_ISO_TIMESTAMP,
|
|
70
|
+
field: 'created',
|
|
71
|
+
current: doc.created.toISOString(),
|
|
72
|
+
suggested: dateOnly,
|
|
73
|
+
description: `Date object should be YYYY-MM-DD string: ${doc.created.toISOString()} → ${dateOnly}`,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Check assigned_to field for username without email domain
|
|
80
|
+
* @param {object} doc - Parsed WU YAML data
|
|
81
|
+
* @returns {object|null} Issue object or null
|
|
82
|
+
*/
|
|
83
|
+
function checkAssignedToField(doc) {
|
|
84
|
+
if (!doc.assigned_to || typeof doc.assigned_to !== 'string')
|
|
85
|
+
return null;
|
|
86
|
+
const assignee = doc.assigned_to.trim();
|
|
87
|
+
if (assignee && !assignee.includes('@')) {
|
|
88
|
+
const suggested = `${assignee}@${DEFAULT_EMAIL_DOMAIN}`;
|
|
89
|
+
return {
|
|
90
|
+
type: FIXABLE_ISSUES.USERNAME_NOT_EMAIL,
|
|
91
|
+
field: 'assigned_to',
|
|
92
|
+
current: assignee,
|
|
93
|
+
suggested,
|
|
94
|
+
description: `Username should be email format: ${assignee} → ${suggested}`,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Check type field for common aliases or typos
|
|
101
|
+
* @param {object} doc - Parsed WU YAML data
|
|
102
|
+
* @returns {object|null} Issue object or null
|
|
103
|
+
*/
|
|
104
|
+
function checkTypeField(doc) {
|
|
105
|
+
if (!doc.type || typeof doc.type !== 'string')
|
|
106
|
+
return null;
|
|
107
|
+
const typeLower = doc.type.toLowerCase();
|
|
108
|
+
if (TYPE_ALIASES[typeLower]) {
|
|
109
|
+
return {
|
|
110
|
+
type: FIXABLE_ISSUES.TYPE_ALIAS,
|
|
111
|
+
field: 'type',
|
|
112
|
+
current: doc.type,
|
|
113
|
+
suggested: TYPE_ALIASES[typeLower],
|
|
114
|
+
description: `Type alias should use canonical form: ${doc.type} → ${TYPE_ALIASES[typeLower]}`,
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
// Check for invalid type not in aliases - try fuzzy match
|
|
118
|
+
if (!VALID_TYPES.includes(typeLower)) {
|
|
119
|
+
const closest = VALID_TYPES.find((t) => t.startsWith(typeLower.slice(0, 3)) || typeLower.startsWith(t.slice(0, 3)));
|
|
120
|
+
if (closest) {
|
|
121
|
+
return {
|
|
122
|
+
type: FIXABLE_ISSUES.TYPE_ALIAS,
|
|
123
|
+
field: 'type',
|
|
124
|
+
current: doc.type,
|
|
125
|
+
suggested: closest,
|
|
126
|
+
description: `Invalid type "${doc.type}" - did you mean "${closest}"?`,
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
return null;
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Check phase field for string instead of number
|
|
134
|
+
* @param {object} doc - Parsed WU YAML data
|
|
135
|
+
* @returns {object|null} Issue object or null
|
|
136
|
+
*/
|
|
137
|
+
function checkPhaseField(doc) {
|
|
138
|
+
if (doc.phase == null || typeof doc.phase !== 'string')
|
|
139
|
+
return null;
|
|
140
|
+
const num = parseInt(doc.phase, 10);
|
|
141
|
+
if (!isNaN(num) && num > 0) {
|
|
142
|
+
return {
|
|
143
|
+
type: FIXABLE_ISSUES.PHASE_STRING,
|
|
144
|
+
field: 'phase',
|
|
145
|
+
current: doc.phase,
|
|
146
|
+
suggested: num,
|
|
147
|
+
description: `Phase should be number: "${doc.phase}" → ${num}`,
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
return null;
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Check priority field for lowercase
|
|
154
|
+
* @param {object} doc - Parsed WU YAML data
|
|
155
|
+
* @returns {object|null} Issue object or null
|
|
156
|
+
*/
|
|
157
|
+
function checkPriorityField(doc) {
|
|
158
|
+
if (!doc.priority || typeof doc.priority !== 'string')
|
|
159
|
+
return null;
|
|
160
|
+
const upper = doc.priority.toUpperCase();
|
|
161
|
+
if (doc.priority !== upper && /^P[0-3]$/i.test(doc.priority)) {
|
|
162
|
+
return {
|
|
163
|
+
type: FIXABLE_ISSUES.PRIORITY_LOWERCASE,
|
|
164
|
+
field: 'priority',
|
|
165
|
+
current: doc.priority,
|
|
166
|
+
suggested: upper,
|
|
167
|
+
description: `Priority should be uppercase: ${doc.priority} → ${upper}`,
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
return null;
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Detects fixable issues in WU YAML data
|
|
174
|
+
*
|
|
175
|
+
* @param {object} doc - Parsed WU YAML data
|
|
176
|
+
* @returns {Array<{type: string, field: string, current: unknown, suggested: unknown, description: string}>}
|
|
177
|
+
*/
|
|
178
|
+
export function detectFixableIssues(doc) {
|
|
179
|
+
const checks = [
|
|
180
|
+
checkCreatedField,
|
|
181
|
+
checkAssignedToField,
|
|
182
|
+
checkTypeField,
|
|
183
|
+
checkPhaseField,
|
|
184
|
+
checkPriorityField,
|
|
185
|
+
];
|
|
186
|
+
return checks.map((check) => check(doc)).filter((issue) => issue !== null);
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Applies fixes to WU YAML data in-place
|
|
190
|
+
*
|
|
191
|
+
* @param {object} doc - Parsed WU YAML data (will be modified)
|
|
192
|
+
* @param {Array<{type: string, field: string, suggested: unknown}>} issues - Issues to fix
|
|
193
|
+
* @returns {number} Number of fixes applied
|
|
194
|
+
*/
|
|
195
|
+
export function applyFixes(doc, issues) {
|
|
196
|
+
let fixed = 0;
|
|
197
|
+
for (const issue of issues) {
|
|
198
|
+
switch (issue.type) {
|
|
199
|
+
case FIXABLE_ISSUES.DATE_ISO_TIMESTAMP:
|
|
200
|
+
doc[issue.field] = issue.suggested;
|
|
201
|
+
fixed++;
|
|
202
|
+
break;
|
|
203
|
+
case FIXABLE_ISSUES.USERNAME_NOT_EMAIL:
|
|
204
|
+
doc[issue.field] = issue.suggested;
|
|
205
|
+
fixed++;
|
|
206
|
+
break;
|
|
207
|
+
case FIXABLE_ISSUES.TYPE_ALIAS:
|
|
208
|
+
doc[issue.field] = issue.suggested;
|
|
209
|
+
fixed++;
|
|
210
|
+
break;
|
|
211
|
+
case FIXABLE_ISSUES.PHASE_STRING:
|
|
212
|
+
doc[issue.field] = issue.suggested;
|
|
213
|
+
fixed++;
|
|
214
|
+
break;
|
|
215
|
+
case FIXABLE_ISSUES.PRIORITY_LOWERCASE:
|
|
216
|
+
doc[issue.field] = issue.suggested;
|
|
217
|
+
fixed++;
|
|
218
|
+
break;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
return fixed;
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Auto-fix WU YAML file
|
|
225
|
+
*
|
|
226
|
+
* @param {string} wuPath - Path to WU YAML file
|
|
227
|
+
* @param {AutoFixWUYamlOptions} options - Options
|
|
228
|
+
* @returns {{fixed: number, issues: Array, backupPath?: string}}
|
|
229
|
+
*/
|
|
230
|
+
export function autoFixWUYaml(wuPath, options = {}) {
|
|
231
|
+
const { dryRun = false, backup = true } = options;
|
|
232
|
+
// Read and parse
|
|
233
|
+
const text = readFileSync(wuPath, { encoding: 'utf-8' });
|
|
234
|
+
const doc = parseYAML(text);
|
|
235
|
+
// Detect issues
|
|
236
|
+
const issues = detectFixableIssues(doc);
|
|
237
|
+
if (issues.length === 0) {
|
|
238
|
+
return { fixed: 0, issues: [] };
|
|
239
|
+
}
|
|
240
|
+
if (dryRun) {
|
|
241
|
+
return { fixed: 0, issues, wouldFix: issues.length };
|
|
242
|
+
}
|
|
243
|
+
// Create backup if requested
|
|
244
|
+
let backupPath;
|
|
245
|
+
if (backup) {
|
|
246
|
+
backupPath = `${wuPath}.bak`;
|
|
247
|
+
copyFileSync(wuPath, backupPath);
|
|
248
|
+
}
|
|
249
|
+
// Apply fixes
|
|
250
|
+
const fixed = applyFixes(doc, issues);
|
|
251
|
+
// Write back
|
|
252
|
+
const newText = stringifyYAML(doc);
|
|
253
|
+
writeFileSync(wuPath, newText, { encoding: 'utf-8' });
|
|
254
|
+
return { fixed, issues, backupPath };
|
|
255
|
+
}
|
|
256
|
+
/**
|
|
257
|
+
* Format issues for CLI output
|
|
258
|
+
*
|
|
259
|
+
* @param {Array<{field: string, description: string}>} issues
|
|
260
|
+
* @returns {string}
|
|
261
|
+
*/
|
|
262
|
+
export function formatIssues(issues) {
|
|
263
|
+
return issues.map((i) => ` - ${i.field}: ${i.description}`).join(STRING_LITERALS.NEWLINE);
|
|
264
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WU-1352: Standardized YAML stringify options.
|
|
3
|
+
*
|
|
4
|
+
* Ensures consistent output across all WU tools:
|
|
5
|
+
* - lineWidth: 100 (wrap long lines)
|
|
6
|
+
* - singleQuote: true (prefer 'single' over "double")
|
|
7
|
+
* - defaultKeyType: PLAIN (unquoted keys when safe)
|
|
8
|
+
*
|
|
9
|
+
* @type {import('yaml').ToStringOptions}
|
|
10
|
+
*/
|
|
11
|
+
export declare const YAML_STRINGIFY_OPTIONS: Readonly<{
|
|
12
|
+
lineWidth: 100;
|
|
13
|
+
singleQuote: true;
|
|
14
|
+
defaultKeyType: "PLAIN";
|
|
15
|
+
}>;
|
|
16
|
+
/**
|
|
17
|
+
* Read and parse WU YAML file.
|
|
18
|
+
*
|
|
19
|
+
* Validates:
|
|
20
|
+
* - File exists
|
|
21
|
+
* - YAML is valid
|
|
22
|
+
* - WU ID matches expected ID
|
|
23
|
+
*
|
|
24
|
+
* @param {string} wuPath - Path to WU YAML file
|
|
25
|
+
* @param {string} expectedId - Expected WU ID (e.g., 'WU-123')
|
|
26
|
+
* @returns {object} Parsed YAML document
|
|
27
|
+
* @throws {Error} If file not found, YAML invalid, or ID mismatch
|
|
28
|
+
*/
|
|
29
|
+
export declare function readWU(wuPath: any, expectedId: any): any;
|
|
30
|
+
/**
|
|
31
|
+
* Parse YAML string to object.
|
|
32
|
+
* WU-1352: Centralized YAML parsing for consistency.
|
|
33
|
+
*
|
|
34
|
+
* @param {string} text - YAML string to parse
|
|
35
|
+
* @returns {object} Parsed object
|
|
36
|
+
* @throws {Error} If YAML is invalid
|
|
37
|
+
*/
|
|
38
|
+
export declare function parseYAML(text: any): any;
|
|
39
|
+
/**
|
|
40
|
+
* Stringify object to YAML with standardized options.
|
|
41
|
+
* WU-1352: Centralized YAML serialization for consistency.
|
|
42
|
+
*
|
|
43
|
+
* @param {object} doc - Object to stringify
|
|
44
|
+
* @param {object} [options] - Additional stringify options (merged with YAML_STRINGIFY_OPTIONS)
|
|
45
|
+
* @returns {string} YAML string
|
|
46
|
+
*/
|
|
47
|
+
export declare function stringifyYAML(doc: any, options?: {}): string;
|
|
48
|
+
/**
|
|
49
|
+
* Read and parse YAML file without ID validation.
|
|
50
|
+
* WU-1352: For cases where you don't know/need to validate the WU ID.
|
|
51
|
+
*
|
|
52
|
+
* @param {string} yamlPath - Path to YAML file
|
|
53
|
+
* @returns {object} Parsed YAML document
|
|
54
|
+
* @throws {Error} If file not found or YAML invalid
|
|
55
|
+
*/
|
|
56
|
+
export declare function readWURaw(yamlPath: any): any;
|
|
57
|
+
/**
|
|
58
|
+
* Write WU YAML file with consistent formatting.
|
|
59
|
+
* WU-1352: Uses YAML_STRINGIFY_OPTIONS for consistent output.
|
|
60
|
+
*
|
|
61
|
+
* @param {string} wuPath - Path to WU YAML file
|
|
62
|
+
* @param {object} doc - YAML document to write
|
|
63
|
+
*/
|
|
64
|
+
export declare function writeWU(wuPath: any, doc: any): void;
|
|
65
|
+
/**
|
|
66
|
+
* Append note to WU document's notes field.
|
|
67
|
+
*
|
|
68
|
+
* Always outputs a string (Zod schema requires string type).
|
|
69
|
+
* Handles various input formats:
|
|
70
|
+
* - undefined/null/empty: Set note directly as string
|
|
71
|
+
* - string: Append with newline separator
|
|
72
|
+
* - array: Convert to newline-separated string, then append
|
|
73
|
+
* - other: Replace with note
|
|
74
|
+
*
|
|
75
|
+
* @param {object} doc - WU document
|
|
76
|
+
* @param {string} note - Note to append
|
|
77
|
+
*/
|
|
78
|
+
export declare function appendNote(doc: any, note: any): void;
|
|
79
|
+
/**
|
|
80
|
+
* Append an agent session entry to a WU's agent_sessions[] array
|
|
81
|
+
*
|
|
82
|
+
* @param {string} wuId - WU ID (e.g., "WU-1234")
|
|
83
|
+
* @param {object} sessionData - Session summary from endSession()
|
|
84
|
+
* @throws {Error} if WU file not found
|
|
85
|
+
*/
|
|
86
|
+
export declare function appendAgentSession(wuId: any, sessionData: any): void;
|
package/dist/wu-yaml.js
ADDED
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
import { existsSync, readFileSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { parse, stringify } from 'yaml';
|
|
3
|
+
import { createError, ErrorCodes } from './error-handler.js';
|
|
4
|
+
import { STRING_LITERALS } from './wu-constants.js';
|
|
5
|
+
/**
|
|
6
|
+
* Unified WU YAML I/O module.
|
|
7
|
+
*
|
|
8
|
+
* Replaces 4 duplicate read/write functions scattered across wu-claim, wu-done,
|
|
9
|
+
* wu-block, and wu-unblock. Single source of truth for WU YAML operations.
|
|
10
|
+
*
|
|
11
|
+
* WU-1352: Standardized on yaml v2.8.1 (eemeli) with consistent stringify options.
|
|
12
|
+
*
|
|
13
|
+
* Functions:
|
|
14
|
+
* - readWU(path, id): Read and validate WU YAML
|
|
15
|
+
* - readWURaw(path): Read YAML without ID validation
|
|
16
|
+
* - parseYAML(text): Parse YAML string to object
|
|
17
|
+
* - writeWU(path, doc): Write WU YAML with consistent formatting
|
|
18
|
+
* - stringifyYAML(doc): Stringify object to YAML with consistent formatting
|
|
19
|
+
* - appendNote(doc, note): Append note to doc.notes field
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* import { readWU, writeWU, appendNote } from './lib/wu-yaml.js';
|
|
23
|
+
*
|
|
24
|
+
* // Read WU
|
|
25
|
+
* const doc = readWU('docs/04-operations/tasks/wu/WU-123.yaml', 'WU-123');
|
|
26
|
+
*
|
|
27
|
+
* // Modify doc
|
|
28
|
+
* doc.status = 'in_progress';
|
|
29
|
+
* appendNote(doc, 'Started work on this WU');
|
|
30
|
+
*
|
|
31
|
+
* // Write back
|
|
32
|
+
* writeWU('docs/04-operations/tasks/wu/WU-123.yaml', doc);
|
|
33
|
+
*/
|
|
34
|
+
/**
|
|
35
|
+
* WU-1352: YAML scalar type constants (from yaml library).
|
|
36
|
+
* These match the string values expected by the yaml package.
|
|
37
|
+
* @see https://github.com/eemeli/yaml/blob/main/docs/03_options.md
|
|
38
|
+
*/
|
|
39
|
+
const YAML_SCALAR_TYPES = Object.freeze({
|
|
40
|
+
/** Unquoted string (when safe) */
|
|
41
|
+
PLAIN: 'PLAIN',
|
|
42
|
+
/** Single-quoted string */
|
|
43
|
+
QUOTE_SINGLE: 'QUOTE_SINGLE',
|
|
44
|
+
/** Double-quoted string */
|
|
45
|
+
QUOTE_DOUBLE: 'QUOTE_DOUBLE',
|
|
46
|
+
/** Block literal (|) */
|
|
47
|
+
BLOCK_LITERAL: 'BLOCK_LITERAL',
|
|
48
|
+
/** Block folded (>) */
|
|
49
|
+
BLOCK_FOLDED: 'BLOCK_FOLDED',
|
|
50
|
+
});
|
|
51
|
+
/** Standard line width for WU YAML files */
|
|
52
|
+
const YAML_LINE_WIDTH = 100;
|
|
53
|
+
/**
|
|
54
|
+
* WU-1352: Standardized YAML stringify options.
|
|
55
|
+
*
|
|
56
|
+
* Ensures consistent output across all WU tools:
|
|
57
|
+
* - lineWidth: 100 (wrap long lines)
|
|
58
|
+
* - singleQuote: true (prefer 'single' over "double")
|
|
59
|
+
* - defaultKeyType: PLAIN (unquoted keys when safe)
|
|
60
|
+
*
|
|
61
|
+
* @type {import('yaml').ToStringOptions}
|
|
62
|
+
*/
|
|
63
|
+
export const YAML_STRINGIFY_OPTIONS = Object.freeze({
|
|
64
|
+
lineWidth: YAML_LINE_WIDTH,
|
|
65
|
+
singleQuote: true,
|
|
66
|
+
defaultKeyType: YAML_SCALAR_TYPES.PLAIN,
|
|
67
|
+
});
|
|
68
|
+
/**
|
|
69
|
+
* Read and parse WU YAML file.
|
|
70
|
+
*
|
|
71
|
+
* Validates:
|
|
72
|
+
* - File exists
|
|
73
|
+
* - YAML is valid
|
|
74
|
+
* - WU ID matches expected ID
|
|
75
|
+
*
|
|
76
|
+
* @param {string} wuPath - Path to WU YAML file
|
|
77
|
+
* @param {string} expectedId - Expected WU ID (e.g., 'WU-123')
|
|
78
|
+
* @returns {object} Parsed YAML document
|
|
79
|
+
* @throws {Error} If file not found, YAML invalid, or ID mismatch
|
|
80
|
+
*/
|
|
81
|
+
export function readWU(wuPath, expectedId) {
|
|
82
|
+
if (!existsSync(wuPath)) {
|
|
83
|
+
throw createError(ErrorCodes.FILE_NOT_FOUND, `WU file not found: ${wuPath}`, {
|
|
84
|
+
path: wuPath,
|
|
85
|
+
expectedId,
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
const text = readFileSync(wuPath, { encoding: 'utf-8' });
|
|
89
|
+
let doc;
|
|
90
|
+
try {
|
|
91
|
+
doc = parse(text);
|
|
92
|
+
}
|
|
93
|
+
catch (e) {
|
|
94
|
+
throw createError(ErrorCodes.YAML_PARSE_ERROR, `Failed to parse YAML ${wuPath}: ${e.message}`, {
|
|
95
|
+
path: wuPath,
|
|
96
|
+
originalError: e.message,
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
// Validate ID matches
|
|
100
|
+
if (!doc || doc.id !== expectedId) {
|
|
101
|
+
throw createError(ErrorCodes.WU_NOT_FOUND, `WU YAML id mismatch. Expected ${expectedId}, found ${doc && doc.id}`, { path: wuPath, expectedId, foundId: doc && doc.id });
|
|
102
|
+
}
|
|
103
|
+
return doc;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Parse YAML string to object.
|
|
107
|
+
* WU-1352: Centralized YAML parsing for consistency.
|
|
108
|
+
*
|
|
109
|
+
* @param {string} text - YAML string to parse
|
|
110
|
+
* @returns {object} Parsed object
|
|
111
|
+
* @throws {Error} If YAML is invalid
|
|
112
|
+
*/
|
|
113
|
+
export function parseYAML(text) {
|
|
114
|
+
return parse(text);
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Stringify object to YAML with standardized options.
|
|
118
|
+
* WU-1352: Centralized YAML serialization for consistency.
|
|
119
|
+
*
|
|
120
|
+
* @param {object} doc - Object to stringify
|
|
121
|
+
* @param {object} [options] - Additional stringify options (merged with YAML_STRINGIFY_OPTIONS)
|
|
122
|
+
* @returns {string} YAML string
|
|
123
|
+
*/
|
|
124
|
+
export function stringifyYAML(doc, options = {}) {
|
|
125
|
+
return stringify(doc, { ...YAML_STRINGIFY_OPTIONS, ...options });
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Read and parse YAML file without ID validation.
|
|
129
|
+
* WU-1352: For cases where you don't know/need to validate the WU ID.
|
|
130
|
+
*
|
|
131
|
+
* @param {string} yamlPath - Path to YAML file
|
|
132
|
+
* @returns {object} Parsed YAML document
|
|
133
|
+
* @throws {Error} If file not found or YAML invalid
|
|
134
|
+
*/
|
|
135
|
+
export function readWURaw(yamlPath) {
|
|
136
|
+
if (!existsSync(yamlPath)) {
|
|
137
|
+
throw createError(ErrorCodes.FILE_NOT_FOUND, `YAML file not found: ${yamlPath}`, {
|
|
138
|
+
path: yamlPath,
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
const text = readFileSync(yamlPath, { encoding: 'utf-8' });
|
|
142
|
+
try {
|
|
143
|
+
return parse(text);
|
|
144
|
+
}
|
|
145
|
+
catch (e) {
|
|
146
|
+
throw createError(ErrorCodes.YAML_PARSE_ERROR, `Failed to parse YAML ${yamlPath}: ${e.message}`, {
|
|
147
|
+
path: yamlPath,
|
|
148
|
+
originalError: e.message,
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Write WU YAML file with consistent formatting.
|
|
154
|
+
* WU-1352: Uses YAML_STRINGIFY_OPTIONS for consistent output.
|
|
155
|
+
*
|
|
156
|
+
* @param {string} wuPath - Path to WU YAML file
|
|
157
|
+
* @param {object} doc - YAML document to write
|
|
158
|
+
*/
|
|
159
|
+
export function writeWU(wuPath, doc) {
|
|
160
|
+
const out = stringify(doc, YAML_STRINGIFY_OPTIONS);
|
|
161
|
+
writeFileSync(wuPath, out, { encoding: 'utf-8' });
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Append note to WU document's notes field.
|
|
165
|
+
*
|
|
166
|
+
* Always outputs a string (Zod schema requires string type).
|
|
167
|
+
* Handles various input formats:
|
|
168
|
+
* - undefined/null/empty: Set note directly as string
|
|
169
|
+
* - string: Append with newline separator
|
|
170
|
+
* - array: Convert to newline-separated string, then append
|
|
171
|
+
* - other: Replace with note
|
|
172
|
+
*
|
|
173
|
+
* @param {object} doc - WU document
|
|
174
|
+
* @param {string} note - Note to append
|
|
175
|
+
*/
|
|
176
|
+
export function appendNote(doc, note) {
|
|
177
|
+
// Do nothing if note is falsy
|
|
178
|
+
if (!note)
|
|
179
|
+
return;
|
|
180
|
+
const existing = doc.notes;
|
|
181
|
+
if (existing === undefined || existing === null || existing === '') {
|
|
182
|
+
// No existing notes: set directly as string
|
|
183
|
+
doc.notes = note;
|
|
184
|
+
}
|
|
185
|
+
else if (Array.isArray(existing)) {
|
|
186
|
+
// Array notes: convert to string first (schema requires string), then append
|
|
187
|
+
const joined = existing.filter(Boolean).join(STRING_LITERALS.NEWLINE).trimEnd();
|
|
188
|
+
doc.notes = joined ? `${joined}${STRING_LITERALS.NEWLINE}${note}` : note;
|
|
189
|
+
}
|
|
190
|
+
else if (typeof existing === 'string') {
|
|
191
|
+
// String notes: append with newline
|
|
192
|
+
const trimmed = existing.trimEnd();
|
|
193
|
+
doc.notes = trimmed ? `${trimmed}${STRING_LITERALS.NEWLINE}${note}` : note;
|
|
194
|
+
}
|
|
195
|
+
else {
|
|
196
|
+
// Invalid type: replace with note
|
|
197
|
+
doc.notes = note;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Append an agent session entry to a WU's agent_sessions[] array
|
|
202
|
+
*
|
|
203
|
+
* @param {string} wuId - WU ID (e.g., "WU-1234")
|
|
204
|
+
* @param {object} sessionData - Session summary from endSession()
|
|
205
|
+
* @throws {Error} if WU file not found
|
|
206
|
+
*/
|
|
207
|
+
export function appendAgentSession(wuId, sessionData) {
|
|
208
|
+
const wuPath = `docs/04-operations/tasks/wu/${wuId}.yaml`;
|
|
209
|
+
if (!existsSync(wuPath)) {
|
|
210
|
+
throw new Error(`WU file not found: ${wuPath}`);
|
|
211
|
+
}
|
|
212
|
+
// Parse WU YAML
|
|
213
|
+
const doc = readWU(wuPath, wuId);
|
|
214
|
+
// Initialize agent_sessions array if needed
|
|
215
|
+
if (!doc.agent_sessions) {
|
|
216
|
+
doc.agent_sessions = [];
|
|
217
|
+
}
|
|
218
|
+
// Append session
|
|
219
|
+
doc.agent_sessions.push(sessionData);
|
|
220
|
+
// Write back
|
|
221
|
+
writeWU(wuPath, doc);
|
|
222
|
+
}
|