@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,94 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { existsSync, unlinkSync, symlinkSync } from 'node:fs';
|
|
3
|
+
/**
|
|
4
|
+
* Determine whether gates should run in low-noise "agent mode".
|
|
5
|
+
*
|
|
6
|
+
* Agent mode is intended for Claude Code sessions, where tool output is injected into the
|
|
7
|
+
* conversation context and can trigger "prompt too long".
|
|
8
|
+
*
|
|
9
|
+
* Detection strategy (WU-1827):
|
|
10
|
+
* 1. --verbose flag always forces full output (returns false)
|
|
11
|
+
* 2. CLAUDE_PROJECT_DIR env var is a strong hint (returns true if set)
|
|
12
|
+
* 3. TTY check: non-TTY + non-CI = likely agent mode (returns true)
|
|
13
|
+
* 4. Interactive TTY = human user (returns false)
|
|
14
|
+
*
|
|
15
|
+
* @param {GatesAgentModeOptions} options
|
|
16
|
+
* @returns {boolean} True if gates should run in agent mode
|
|
17
|
+
*/
|
|
18
|
+
export function shouldUseGatesAgentMode({ argv, env, stdout } = {}) {
|
|
19
|
+
// --verbose flag always forces full output
|
|
20
|
+
const isVerbose = Array.isArray(argv) && argv.includes('--verbose');
|
|
21
|
+
if (isVerbose) {
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
// CLAUDE_PROJECT_DIR is a strong hint that we're in Claude Code
|
|
25
|
+
const hasClaudeProjectDir = Boolean(env?.CLAUDE_PROJECT_DIR);
|
|
26
|
+
if (hasClaudeProjectDir) {
|
|
27
|
+
return true;
|
|
28
|
+
}
|
|
29
|
+
// CI environments should get full output for debugging
|
|
30
|
+
const isCI = Boolean(env?.CI);
|
|
31
|
+
if (isCI) {
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
// Use provided stdout or fall back to process.stdout
|
|
35
|
+
const stdoutStream = stdout ?? process.stdout;
|
|
36
|
+
// TTY check: non-TTY = likely agent mode (Claude Code Bash tool doesn't have TTY)
|
|
37
|
+
// If stdout is undefined or isTTY is falsy, assume agent mode (safer default)
|
|
38
|
+
const isTTY = stdoutStream?.isTTY ?? false;
|
|
39
|
+
// Non-TTY + non-CI = likely agent mode
|
|
40
|
+
return !isTTY;
|
|
41
|
+
}
|
|
42
|
+
export function getGatesLogDir({ cwd, env }) {
|
|
43
|
+
const configured = env?.LUMENFLOW_LOG_DIR;
|
|
44
|
+
return path.resolve(cwd, configured || '.logs');
|
|
45
|
+
}
|
|
46
|
+
export function buildGatesLogPath({ cwd, env, wuId, lane, now = new Date(), }) {
|
|
47
|
+
const logDir = getGatesLogDir({ cwd, env });
|
|
48
|
+
const safeLane = (lane || 'unknown').toLowerCase().replace(/[^a-z0-9]+/g, '-');
|
|
49
|
+
const safeWu = (wuId || 'unknown').toLowerCase().replace(/[^a-z0-9]+/g, '-');
|
|
50
|
+
const stamp = now.toISOString().replace(/[:.]/g, '-');
|
|
51
|
+
return path.join(logDir, `gates-${safeLane}-${safeWu}-${stamp}.log`);
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Get the path to the gates-latest.log symlink (WU-2064)
|
|
55
|
+
*
|
|
56
|
+
* @param {Object} options
|
|
57
|
+
* @param {string} options.cwd - Working directory
|
|
58
|
+
* @param {Object} [options.env] - Environment variables
|
|
59
|
+
* @returns {string} Path to the symlink
|
|
60
|
+
*/
|
|
61
|
+
export function getGatesLatestSymlinkPath({ cwd, env }) {
|
|
62
|
+
const logDir = getGatesLogDir({ cwd, env });
|
|
63
|
+
return path.join(logDir, 'gates-latest.log');
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Create or update the gates-latest.log symlink to point to the most recent gate run (WU-2064)
|
|
67
|
+
*
|
|
68
|
+
* This provides a stable path for agents to access the most recent gate log
|
|
69
|
+
* without needing to know the timestamp-based filename.
|
|
70
|
+
*
|
|
71
|
+
* @param {Object} options
|
|
72
|
+
* @param {string} options.logPath - Path to the actual gate log file
|
|
73
|
+
* @param {string} options.cwd - Working directory
|
|
74
|
+
* @param {Object} [options.env] - Environment variables
|
|
75
|
+
* @returns {boolean} True if symlink was created/updated successfully
|
|
76
|
+
*/
|
|
77
|
+
export function updateGatesLatestSymlink({ logPath, cwd, env }) {
|
|
78
|
+
const symlinkPath = getGatesLatestSymlinkPath({ cwd, env });
|
|
79
|
+
try {
|
|
80
|
+
// Remove existing symlink if present
|
|
81
|
+
if (existsSync(symlinkPath)) {
|
|
82
|
+
unlinkSync(symlinkPath);
|
|
83
|
+
}
|
|
84
|
+
// Create relative symlink (so it works regardless of absolute path)
|
|
85
|
+
const logDir = path.dirname(symlinkPath);
|
|
86
|
+
const relativePath = path.relative(logDir, logPath);
|
|
87
|
+
symlinkSync(relativePath, symlinkPath);
|
|
88
|
+
return true;
|
|
89
|
+
}
|
|
90
|
+
catch {
|
|
91
|
+
// Symlink creation is best-effort, don't fail gates
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generate Traceability Library
|
|
3
|
+
*
|
|
4
|
+
* Core functions for generating compliance traceability documentation:
|
|
5
|
+
* - Hazard log parsing
|
|
6
|
+
* - Prompt version extraction
|
|
7
|
+
* - Cross-reference building
|
|
8
|
+
* - Markdown document generation
|
|
9
|
+
*
|
|
10
|
+
* @module tools/lib/generate-traceability
|
|
11
|
+
* @see WU-2035 - INIT-034
|
|
12
|
+
*/
|
|
13
|
+
/**
|
|
14
|
+
* @typedef {Object} HazardDefinition
|
|
15
|
+
* @property {string} id
|
|
16
|
+
* @property {string} title
|
|
17
|
+
* @property {number} severity
|
|
18
|
+
* @property {number} likelihood
|
|
19
|
+
* @property {number} riskScore
|
|
20
|
+
* @property {ControlDefinition[]} controls
|
|
21
|
+
* @property {number} residualRiskScore
|
|
22
|
+
* @property {string} acceptability
|
|
23
|
+
*/
|
|
24
|
+
/**
|
|
25
|
+
* @typedef {Object} ControlDefinition
|
|
26
|
+
* @property {string} id
|
|
27
|
+
* @property {string} description
|
|
28
|
+
* @property {string} type
|
|
29
|
+
* @property {string} status
|
|
30
|
+
*/
|
|
31
|
+
/**
|
|
32
|
+
* @typedef {Object} PromptVersion
|
|
33
|
+
* @property {string} filePath
|
|
34
|
+
* @property {string} fileName
|
|
35
|
+
* @property {string} version
|
|
36
|
+
* @property {string} relativePath
|
|
37
|
+
*/
|
|
38
|
+
/**
|
|
39
|
+
* @typedef {Object} GitCommit
|
|
40
|
+
* @property {string} hash
|
|
41
|
+
* @property {string} date
|
|
42
|
+
* @property {string} author
|
|
43
|
+
* @property {string} message
|
|
44
|
+
* @property {string[]} files
|
|
45
|
+
*/
|
|
46
|
+
/**
|
|
47
|
+
* @typedef {Object} CrossReference
|
|
48
|
+
* @property {string} source
|
|
49
|
+
* @property {string} target
|
|
50
|
+
* @property {'hazard_to_control' | 'control_to_prompt' | 'control_to_test'} type
|
|
51
|
+
* @property {boolean} valid
|
|
52
|
+
* @property {string} [error]
|
|
53
|
+
*/
|
|
54
|
+
/**
|
|
55
|
+
* Parse hazard definitions from hazard-log.md content
|
|
56
|
+
* @param {string} content - Markdown content of hazard log
|
|
57
|
+
* @returns {HazardDefinition[]}
|
|
58
|
+
*/
|
|
59
|
+
export declare function parseHazardLog(content: any): any[];
|
|
60
|
+
/**
|
|
61
|
+
* Extract versions from prompt YAML files
|
|
62
|
+
* @param {Array<{path: string, content: string}>} prompts
|
|
63
|
+
* @returns {PromptVersion[]}
|
|
64
|
+
*/
|
|
65
|
+
export declare function extractPromptVersions(prompts: any): any[];
|
|
66
|
+
/**
|
|
67
|
+
* Build cross-references between hazards, controls, and tests
|
|
68
|
+
* @param {Array<{id: string, controls: Array<{id: string}>}>} hazards
|
|
69
|
+
* @param {PromptVersion[]} prompts
|
|
70
|
+
* @param {Array<{file: string, mentionsControls: string[]}>} goldenTests
|
|
71
|
+
* @returns {CrossReference[]}
|
|
72
|
+
*/
|
|
73
|
+
export declare function buildCrossReferences(hazards: any, prompts: any, goldenTests: any): any[];
|
|
74
|
+
/**
|
|
75
|
+
* Generate version history markdown from git commits
|
|
76
|
+
* @param {GitCommit[]} commits
|
|
77
|
+
* @returns {string}
|
|
78
|
+
*/
|
|
79
|
+
export declare function generateVersionHistory(commits: any): string;
|
|
80
|
+
/**
|
|
81
|
+
* Generate prompt inventory markdown
|
|
82
|
+
* @param {PromptVersion[]} prompts
|
|
83
|
+
* @returns {string}
|
|
84
|
+
*/
|
|
85
|
+
export declare function generatePromptInventory(prompts: any): string;
|
|
86
|
+
/**
|
|
87
|
+
* Generate hazard matrix markdown
|
|
88
|
+
* @param {HazardDefinition[]} hazards
|
|
89
|
+
* @returns {string}
|
|
90
|
+
*/
|
|
91
|
+
export declare function generateHazardMatrix(hazards: any): string;
|
|
92
|
+
/**
|
|
93
|
+
* Generate validation report markdown
|
|
94
|
+
* @param {CrossReference[]} crossRefs
|
|
95
|
+
* @returns {string}
|
|
96
|
+
*/
|
|
97
|
+
export declare function generateValidationReport(crossRefs: any): string;
|
|
98
|
+
/**
|
|
99
|
+
* Validate cross-references and return summary
|
|
100
|
+
* @param {CrossReference[]} refs
|
|
101
|
+
* @returns {{valid: boolean, invalidCount: number, errors: string[]}}
|
|
102
|
+
*/
|
|
103
|
+
export declare function validateCrossReferences(refs: any): {
|
|
104
|
+
valid: boolean;
|
|
105
|
+
invalidCount: any;
|
|
106
|
+
errors: any;
|
|
107
|
+
};
|
|
@@ -0,0 +1,411 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generate Traceability Library
|
|
3
|
+
*
|
|
4
|
+
* Core functions for generating compliance traceability documentation:
|
|
5
|
+
* - Hazard log parsing
|
|
6
|
+
* - Prompt version extraction
|
|
7
|
+
* - Cross-reference building
|
|
8
|
+
* - Markdown document generation
|
|
9
|
+
*
|
|
10
|
+
* @module tools/lib/generate-traceability
|
|
11
|
+
* @see WU-2035 - INIT-034
|
|
12
|
+
*/
|
|
13
|
+
import path from 'node:path';
|
|
14
|
+
import yaml from 'yaml';
|
|
15
|
+
/**
|
|
16
|
+
* @typedef {Object} HazardDefinition
|
|
17
|
+
* @property {string} id
|
|
18
|
+
* @property {string} title
|
|
19
|
+
* @property {number} severity
|
|
20
|
+
* @property {number} likelihood
|
|
21
|
+
* @property {number} riskScore
|
|
22
|
+
* @property {ControlDefinition[]} controls
|
|
23
|
+
* @property {number} residualRiskScore
|
|
24
|
+
* @property {string} acceptability
|
|
25
|
+
*/
|
|
26
|
+
/**
|
|
27
|
+
* @typedef {Object} ControlDefinition
|
|
28
|
+
* @property {string} id
|
|
29
|
+
* @property {string} description
|
|
30
|
+
* @property {string} type
|
|
31
|
+
* @property {string} status
|
|
32
|
+
*/
|
|
33
|
+
/**
|
|
34
|
+
* @typedef {Object} PromptVersion
|
|
35
|
+
* @property {string} filePath
|
|
36
|
+
* @property {string} fileName
|
|
37
|
+
* @property {string} version
|
|
38
|
+
* @property {string} relativePath
|
|
39
|
+
*/
|
|
40
|
+
/**
|
|
41
|
+
* @typedef {Object} GitCommit
|
|
42
|
+
* @property {string} hash
|
|
43
|
+
* @property {string} date
|
|
44
|
+
* @property {string} author
|
|
45
|
+
* @property {string} message
|
|
46
|
+
* @property {string[]} files
|
|
47
|
+
*/
|
|
48
|
+
/**
|
|
49
|
+
* @typedef {Object} CrossReference
|
|
50
|
+
* @property {string} source
|
|
51
|
+
* @property {string} target
|
|
52
|
+
* @property {'hazard_to_control' | 'control_to_prompt' | 'control_to_test'} type
|
|
53
|
+
* @property {boolean} valid
|
|
54
|
+
* @property {string} [error]
|
|
55
|
+
*/
|
|
56
|
+
/**
|
|
57
|
+
* Parse hazard definitions from hazard-log.md content
|
|
58
|
+
* @param {string} content - Markdown content of hazard log
|
|
59
|
+
* @returns {HazardDefinition[]}
|
|
60
|
+
*/
|
|
61
|
+
export function parseHazardLog(content) {
|
|
62
|
+
const hazards = [];
|
|
63
|
+
const hazardSections = content.split(/### HAZ-\d{3}:/);
|
|
64
|
+
for (let i = 1; i < hazardSections.length; i++) {
|
|
65
|
+
const section = hazardSections[i];
|
|
66
|
+
const hazard = parseHazardSection(section);
|
|
67
|
+
if (hazard) {
|
|
68
|
+
hazards.push(hazard);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return hazards;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Parse a single hazard section
|
|
75
|
+
* @param {string} section
|
|
76
|
+
* @returns {HazardDefinition | null}
|
|
77
|
+
*/
|
|
78
|
+
function parseHazardSection(section) {
|
|
79
|
+
const idMatch = section.match(/\*\*Hazard ID\*\*\s*\|\s*(HAZ-\d{3})/);
|
|
80
|
+
if (!idMatch)
|
|
81
|
+
return null;
|
|
82
|
+
const titleLine = section.split('\n')[0].trim();
|
|
83
|
+
// Parse initial risk values
|
|
84
|
+
const severityMatch = section.match(/\*\*Initial Severity\*\*\s*\|\s*(\d)/);
|
|
85
|
+
const likelihoodMatch = section.match(/\*\*Initial Likelihood\*\*\s*\|\s*(\d)/);
|
|
86
|
+
const riskScoreMatch = section.match(/\*\*Initial Risk Score\*\*\s*\|\s*(\d+)/);
|
|
87
|
+
// Parse residual assessment
|
|
88
|
+
const residualRiskMatch = section.match(/Residual Risk Score\s*\|\s*(\d+)/);
|
|
89
|
+
const acceptabilityMatch = section.match(/Acceptability\s*\|\s*([^\n|]+)/);
|
|
90
|
+
// Parse controls
|
|
91
|
+
const controls = parseControls(section);
|
|
92
|
+
return {
|
|
93
|
+
id: idMatch[1],
|
|
94
|
+
title: titleLine,
|
|
95
|
+
severity: severityMatch ? parseInt(severityMatch[1], 10) : 0,
|
|
96
|
+
likelihood: likelihoodMatch ? parseInt(likelihoodMatch[1], 10) : 0,
|
|
97
|
+
riskScore: riskScoreMatch ? parseInt(riskScoreMatch[1], 10) : 0,
|
|
98
|
+
controls,
|
|
99
|
+
residualRiskScore: residualRiskMatch ? parseInt(residualRiskMatch[1], 10) : 0,
|
|
100
|
+
acceptability: acceptabilityMatch ? acceptabilityMatch[1].trim() : 'Unknown',
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Parse controls from a hazard section
|
|
105
|
+
* @param {string} section
|
|
106
|
+
* @returns {ControlDefinition[]}
|
|
107
|
+
*/
|
|
108
|
+
function parseControls(section) {
|
|
109
|
+
const controls = [];
|
|
110
|
+
const controlMatches = section.matchAll(/\|\s*(C-\d{3}[a-z]?)\s*\|\s*([^|]+)\s*\|\s*([^|]+)\s*\|\s*([^|]+)\s*\|/g);
|
|
111
|
+
for (const match of controlMatches) {
|
|
112
|
+
controls.push({
|
|
113
|
+
id: match[1].trim(),
|
|
114
|
+
description: match[2].trim(),
|
|
115
|
+
type: match[3].trim(),
|
|
116
|
+
status: match[4].trim(),
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
return controls;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Extract versions from prompt YAML files
|
|
123
|
+
* @param {Array<{path: string, content: string}>} prompts
|
|
124
|
+
* @returns {PromptVersion[]}
|
|
125
|
+
*/
|
|
126
|
+
export function extractPromptVersions(prompts) {
|
|
127
|
+
const versions = [];
|
|
128
|
+
for (const prompt of prompts) {
|
|
129
|
+
try {
|
|
130
|
+
const parsed = yaml.parse(prompt.content);
|
|
131
|
+
if (parsed && parsed.version) {
|
|
132
|
+
versions.push({
|
|
133
|
+
filePath: prompt.path,
|
|
134
|
+
fileName: path.basename(prompt.path),
|
|
135
|
+
version: parsed.version,
|
|
136
|
+
relativePath: prompt.path,
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
catch {
|
|
141
|
+
// Skip files that can't be parsed
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
return versions.sort((a, b) => a.fileName.localeCompare(b.fileName));
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Build cross-references between hazards, controls, and tests
|
|
148
|
+
* @param {Array<{id: string, controls: Array<{id: string}>}>} hazards
|
|
149
|
+
* @param {PromptVersion[]} prompts
|
|
150
|
+
* @param {Array<{file: string, mentionsControls: string[]}>} goldenTests
|
|
151
|
+
* @returns {CrossReference[]}
|
|
152
|
+
*/
|
|
153
|
+
export function buildCrossReferences(hazards, prompts, goldenTests) {
|
|
154
|
+
const refs = [];
|
|
155
|
+
// Build set of controls covered by tests
|
|
156
|
+
const coveredControls = new Set();
|
|
157
|
+
for (const test of goldenTests) {
|
|
158
|
+
for (const controlId of test.mentionsControls) {
|
|
159
|
+
coveredControls.add(controlId);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
// Create hazard to control references
|
|
163
|
+
for (const hazard of hazards) {
|
|
164
|
+
for (const control of hazard.controls) {
|
|
165
|
+
refs.push({
|
|
166
|
+
source: hazard.id,
|
|
167
|
+
target: control.id,
|
|
168
|
+
type: 'hazard_to_control',
|
|
169
|
+
valid: true,
|
|
170
|
+
});
|
|
171
|
+
// Create control to test references
|
|
172
|
+
const hasCoverage = coveredControls.has(control.id);
|
|
173
|
+
refs.push({
|
|
174
|
+
source: control.id,
|
|
175
|
+
target: 'golden-tests',
|
|
176
|
+
type: 'control_to_test',
|
|
177
|
+
valid: hasCoverage,
|
|
178
|
+
error: hasCoverage ? undefined : `No golden test coverage for ${control.id}`,
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
return refs;
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Generate version history markdown from git commits
|
|
186
|
+
* @param {GitCommit[]} commits
|
|
187
|
+
* @returns {string}
|
|
188
|
+
*/
|
|
189
|
+
export function generateVersionHistory(commits) {
|
|
190
|
+
const lines = [
|
|
191
|
+
'# Prompt Version History',
|
|
192
|
+
'',
|
|
193
|
+
'> Auto-generated by `tools/generate-traceability.ts`',
|
|
194
|
+
`> Last generated: ${new Date().toISOString()}`,
|
|
195
|
+
'',
|
|
196
|
+
'This document tracks all changes to prompt files for compliance traceability.',
|
|
197
|
+
'',
|
|
198
|
+
'---',
|
|
199
|
+
'',
|
|
200
|
+
'## Recent Changes',
|
|
201
|
+
'',
|
|
202
|
+
'| Date | Commit | Author | Message | Files Changed |',
|
|
203
|
+
'|------|--------|--------|---------|---------------|',
|
|
204
|
+
];
|
|
205
|
+
for (const commit of commits.slice(0, 50)) {
|
|
206
|
+
const filesStr = commit.files.length > 3
|
|
207
|
+
? `${commit.files
|
|
208
|
+
.slice(0, 3)
|
|
209
|
+
.map((f) => path.basename(f))
|
|
210
|
+
.join(', ')}... (+${commit.files.length - 3})`
|
|
211
|
+
: commit.files.map((f) => path.basename(f)).join(', ');
|
|
212
|
+
lines.push(`| ${commit.date} | \`${commit.hash.slice(0, 7)}\` | ${commit.author} | ${commit.message.slice(0, 60)}${commit.message.length > 60 ? '...' : ''} | ${filesStr} |`);
|
|
213
|
+
}
|
|
214
|
+
lines.push('');
|
|
215
|
+
lines.push('---');
|
|
216
|
+
lines.push('');
|
|
217
|
+
lines.push('## Change Categories');
|
|
218
|
+
lines.push('');
|
|
219
|
+
// Group by month
|
|
220
|
+
const byMonth = new Map();
|
|
221
|
+
for (const commit of commits) {
|
|
222
|
+
const month = commit.date.slice(0, 7); // YYYY-MM
|
|
223
|
+
if (!byMonth.has(month)) {
|
|
224
|
+
byMonth.set(month, []);
|
|
225
|
+
}
|
|
226
|
+
byMonth.get(month).push(commit);
|
|
227
|
+
}
|
|
228
|
+
lines.push('| Month | Total Changes | Safety-Related | Feature |');
|
|
229
|
+
lines.push('|-------|---------------|----------------|---------|');
|
|
230
|
+
for (const [month, monthCommits] of [...byMonth.entries()].sort().reverse()) {
|
|
231
|
+
const safetyCount = monthCommits.filter((c) => c.message.toLowerCase().includes('safety') ||
|
|
232
|
+
c.message.toLowerCase().includes('red-flag') ||
|
|
233
|
+
c.message.toLowerCase().includes('emergency')).length;
|
|
234
|
+
const featureCount = monthCommits.length - safetyCount;
|
|
235
|
+
lines.push(`| ${month} | ${monthCommits.length} | ${safetyCount} | ${featureCount} |`);
|
|
236
|
+
}
|
|
237
|
+
return lines.join('\n');
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Generate prompt inventory markdown
|
|
241
|
+
* @param {PromptVersion[]} prompts
|
|
242
|
+
* @returns {string}
|
|
243
|
+
*/
|
|
244
|
+
export function generatePromptInventory(prompts) {
|
|
245
|
+
const lines = [
|
|
246
|
+
'# Prompt Inventory',
|
|
247
|
+
'',
|
|
248
|
+
'> Auto-generated by `tools/generate-traceability.ts`',
|
|
249
|
+
`> Last generated: ${new Date().toISOString()}`,
|
|
250
|
+
'',
|
|
251
|
+
'Complete inventory of all versioned prompt files in the system.',
|
|
252
|
+
'',
|
|
253
|
+
'---',
|
|
254
|
+
'',
|
|
255
|
+
'## Prompt Files',
|
|
256
|
+
'',
|
|
257
|
+
'| Prompt | Version | Path |',
|
|
258
|
+
'|--------|---------|------|',
|
|
259
|
+
];
|
|
260
|
+
for (const prompt of prompts) {
|
|
261
|
+
lines.push(`| ${prompt.fileName} | \`${prompt.version}\` | \`${prompt.relativePath}\` |`);
|
|
262
|
+
}
|
|
263
|
+
lines.push('');
|
|
264
|
+
lines.push('---');
|
|
265
|
+
lines.push('');
|
|
266
|
+
lines.push('## Version Summary');
|
|
267
|
+
lines.push('');
|
|
268
|
+
lines.push(`- **Total Prompts**: ${prompts.length}`);
|
|
269
|
+
const majorVersions = new Map();
|
|
270
|
+
for (const prompt of prompts) {
|
|
271
|
+
const major = prompt.version.split('.')[0];
|
|
272
|
+
majorVersions.set(major, (majorVersions.get(major) || 0) + 1);
|
|
273
|
+
}
|
|
274
|
+
lines.push('- **By Major Version**:');
|
|
275
|
+
for (const [major, count] of [...majorVersions.entries()].sort()) {
|
|
276
|
+
lines.push(` - v${major}.x: ${count} prompts`);
|
|
277
|
+
}
|
|
278
|
+
return lines.join('\n');
|
|
279
|
+
}
|
|
280
|
+
/**
|
|
281
|
+
* Generate hazard matrix markdown
|
|
282
|
+
* @param {HazardDefinition[]} hazards
|
|
283
|
+
* @returns {string}
|
|
284
|
+
*/
|
|
285
|
+
export function generateHazardMatrix(hazards) {
|
|
286
|
+
const lines = [
|
|
287
|
+
'# Hazard Traceability Matrix',
|
|
288
|
+
'',
|
|
289
|
+
'> Auto-generated by `tools/generate-traceability.ts`',
|
|
290
|
+
`> Last generated: ${new Date().toISOString()}`,
|
|
291
|
+
'',
|
|
292
|
+
'Maps hazards to controls for DCB0129 compliance.',
|
|
293
|
+
'',
|
|
294
|
+
'---',
|
|
295
|
+
'',
|
|
296
|
+
'## Hazard Summary',
|
|
297
|
+
'',
|
|
298
|
+
'| Hazard ID | Title | Initial Risk | Residual Risk | Acceptability | Controls |',
|
|
299
|
+
'|-----------|-------|--------------|---------------|---------------|----------|',
|
|
300
|
+
];
|
|
301
|
+
for (const hazard of hazards) {
|
|
302
|
+
const controlList = hazard.controls.map((c) => c.id).join(', ');
|
|
303
|
+
const title = hazard.title || '';
|
|
304
|
+
lines.push(`| ${hazard.id} | ${title.slice(0, 50)}${title.length > 50 ? '...' : ''} | ${hazard.riskScore} | ${hazard.residualRiskScore} | ${hazard.acceptability} | ${controlList} |`);
|
|
305
|
+
}
|
|
306
|
+
lines.push('');
|
|
307
|
+
lines.push('---');
|
|
308
|
+
lines.push('');
|
|
309
|
+
lines.push('## Control Implementation Status');
|
|
310
|
+
lines.push('');
|
|
311
|
+
lines.push('| Control ID | Hazard | Description | Type | Status |');
|
|
312
|
+
lines.push('|------------|--------|-------------|------|--------|');
|
|
313
|
+
for (const hazard of hazards) {
|
|
314
|
+
for (const control of hazard.controls) {
|
|
315
|
+
const desc = control.description || '';
|
|
316
|
+
lines.push(`| ${control.id} | ${hazard.id} | ${desc.slice(0, 60)}${desc.length > 60 ? '...' : ''} | ${control.type} | ${control.status} |`);
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
lines.push('');
|
|
320
|
+
lines.push('---');
|
|
321
|
+
lines.push('');
|
|
322
|
+
lines.push('## Risk Distribution');
|
|
323
|
+
lines.push('');
|
|
324
|
+
const riskLevels = {
|
|
325
|
+
Acceptable: 0,
|
|
326
|
+
Low: 0,
|
|
327
|
+
Medium: 0,
|
|
328
|
+
High: 0,
|
|
329
|
+
Unacceptable: 0,
|
|
330
|
+
Tolerable: 0,
|
|
331
|
+
};
|
|
332
|
+
for (const hazard of hazards) {
|
|
333
|
+
const level = hazard.acceptability.replace(/\s*\(.*\)/, '');
|
|
334
|
+
if (level in riskLevels) {
|
|
335
|
+
riskLevels[level]++;
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
lines.push('| Risk Level | Count |');
|
|
339
|
+
lines.push('|------------|-------|');
|
|
340
|
+
for (const [level, count] of Object.entries(riskLevels)) {
|
|
341
|
+
if (count > 0) {
|
|
342
|
+
lines.push(`| ${level} | ${count} |`);
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
return lines.join('\n');
|
|
346
|
+
}
|
|
347
|
+
/**
|
|
348
|
+
* Generate validation report markdown
|
|
349
|
+
* @param {CrossReference[]} crossRefs
|
|
350
|
+
* @returns {string}
|
|
351
|
+
*/
|
|
352
|
+
export function generateValidationReport(crossRefs) {
|
|
353
|
+
const lines = [
|
|
354
|
+
'# Traceability Validation Report',
|
|
355
|
+
'',
|
|
356
|
+
'> Auto-generated by `tools/generate-traceability.ts`',
|
|
357
|
+
`> Last generated: ${new Date().toISOString()}`,
|
|
358
|
+
'',
|
|
359
|
+
'Validates cross-references between hazards, controls, and test coverage.',
|
|
360
|
+
'',
|
|
361
|
+
'---',
|
|
362
|
+
'',
|
|
363
|
+
'## Summary',
|
|
364
|
+
'',
|
|
365
|
+
];
|
|
366
|
+
const valid = crossRefs.filter((r) => r.valid).length;
|
|
367
|
+
const invalid = crossRefs.filter((r) => !r.valid).length;
|
|
368
|
+
const total = crossRefs.length;
|
|
369
|
+
const coverage = total > 0 ? ((valid / total) * 100).toFixed(1) : '0.0';
|
|
370
|
+
lines.push(`- **Total References**: ${total}`);
|
|
371
|
+
lines.push(`- **Valid**: ${valid}`);
|
|
372
|
+
lines.push(`- **Invalid/Missing**: ${invalid}`);
|
|
373
|
+
lines.push(`- **Coverage**: ${coverage}%`);
|
|
374
|
+
lines.push('');
|
|
375
|
+
if (invalid > 0) {
|
|
376
|
+
lines.push('---');
|
|
377
|
+
lines.push('');
|
|
378
|
+
lines.push('## Issues Found');
|
|
379
|
+
lines.push('');
|
|
380
|
+
lines.push('| Source | Target | Type | Issue |');
|
|
381
|
+
lines.push('|--------|--------|------|-------|');
|
|
382
|
+
for (const ref of crossRefs.filter((r) => !r.valid)) {
|
|
383
|
+
lines.push(`| ${ref.source} | ${ref.target} | ${ref.type} | ${ref.error || 'Unknown'} |`);
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
lines.push('');
|
|
387
|
+
lines.push('---');
|
|
388
|
+
lines.push('');
|
|
389
|
+
lines.push('## All References');
|
|
390
|
+
lines.push('');
|
|
391
|
+
lines.push('| Source | Target | Type | Valid |');
|
|
392
|
+
lines.push('|--------|--------|------|-------|');
|
|
393
|
+
for (const ref of crossRefs) {
|
|
394
|
+
lines.push(`| ${ref.source} | ${ref.target} | ${ref.type} | ${ref.valid ? 'Yes' : 'No'} |`);
|
|
395
|
+
}
|
|
396
|
+
return lines.join('\n');
|
|
397
|
+
}
|
|
398
|
+
/**
|
|
399
|
+
* Validate cross-references and return summary
|
|
400
|
+
* @param {CrossReference[]} refs
|
|
401
|
+
* @returns {{valid: boolean, invalidCount: number, errors: string[]}}
|
|
402
|
+
*/
|
|
403
|
+
export function validateCrossReferences(refs) {
|
|
404
|
+
const invalid = refs.filter((r) => !r.valid);
|
|
405
|
+
const errors = invalid.map((r) => r.error).filter(Boolean);
|
|
406
|
+
return {
|
|
407
|
+
valid: invalid.length === 0,
|
|
408
|
+
invalidCount: invalid.length,
|
|
409
|
+
errors,
|
|
410
|
+
};
|
|
411
|
+
}
|