@lumenflow/cli 2.6.0 → 2.8.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/README.md +120 -105
- package/dist/__tests__/agent-spawn-coordination.test.js +451 -0
- package/dist/__tests__/commands/integrate.test.js +165 -0
- package/dist/__tests__/gates-config.test.js +0 -1
- package/dist/__tests__/hooks/enforcement.test.js +279 -0
- package/dist/__tests__/init-greenfield.test.js +247 -0
- package/dist/__tests__/init-quick-ref.test.js +0 -1
- package/dist/__tests__/init-template-portability.test.js +0 -1
- package/dist/__tests__/init.test.js +27 -0
- package/dist/__tests__/initiative-e2e.test.js +442 -0
- package/dist/__tests__/initiative-plan-replacement.test.js +0 -1
- package/dist/__tests__/memory-integration.test.js +333 -0
- package/dist/__tests__/release.test.js +1 -1
- package/dist/__tests__/safe-git.test.js +0 -1
- package/dist/__tests__/state-doctor.test.js +54 -0
- package/dist/__tests__/sync-templates.test.js +255 -0
- package/dist/__tests__/wu-create-required-fields.test.js +121 -0
- package/dist/__tests__/wu-done-auto-cleanup.test.js +135 -0
- package/dist/__tests__/wu-lifecycle-integration.test.js +388 -0
- package/dist/__tests__/wu-proto.test.js +97 -0
- package/dist/backlog-prune.js +0 -1
- package/dist/cli-entry-point.js +0 -1
- package/dist/commands/integrate.js +229 -0
- package/dist/docs-sync.js +46 -0
- package/dist/doctor.js +0 -2
- package/dist/gates.js +0 -7
- package/dist/hooks/enforcement-checks.js +209 -0
- package/dist/hooks/enforcement-generator.js +365 -0
- package/dist/hooks/enforcement-sync.js +243 -0
- package/dist/hooks/index.js +7 -0
- package/dist/init.js +266 -11
- package/dist/initiative-add-wu.js +0 -2
- package/dist/initiative-create.js +0 -3
- package/dist/initiative-edit.js +0 -5
- package/dist/initiative-plan.js +0 -1
- package/dist/initiative-remove-wu.js +0 -2
- package/dist/lane-health.js +0 -2
- package/dist/lane-suggest.js +0 -1
- package/dist/mem-checkpoint.js +0 -2
- package/dist/mem-cleanup.js +0 -2
- package/dist/mem-context.js +0 -3
- package/dist/mem-create.js +0 -2
- package/dist/mem-delete.js +0 -3
- package/dist/mem-inbox.js +0 -2
- package/dist/mem-index.js +0 -1
- package/dist/mem-init.js +0 -2
- package/dist/mem-profile.js +0 -1
- package/dist/mem-promote.js +0 -1
- package/dist/mem-ready.js +0 -2
- package/dist/mem-signal.js +0 -2
- package/dist/mem-start.js +0 -2
- package/dist/mem-summarize.js +0 -2
- package/dist/metrics-cli.js +1 -1
- package/dist/metrics-snapshot.js +1 -1
- package/dist/onboarding-smoke-test.js +0 -5
- package/dist/orchestrate-init-status.js +0 -1
- package/dist/orchestrate-initiative.js +0 -1
- package/dist/orchestrate-monitor.js +0 -1
- package/dist/plan-create.js +0 -2
- package/dist/plan-edit.js +0 -2
- package/dist/plan-link.js +0 -2
- package/dist/plan-promote.js +0 -2
- package/dist/signal-cleanup.js +0 -4
- package/dist/state-bootstrap.js +0 -1
- package/dist/state-cleanup.js +0 -4
- package/dist/state-doctor-fix.js +5 -8
- package/dist/state-doctor.js +0 -11
- package/dist/sync-templates.js +188 -34
- package/dist/wu-block.js +100 -48
- package/dist/wu-claim.js +1 -22
- package/dist/wu-cleanup.js +0 -1
- package/dist/wu-create.js +0 -2
- package/dist/wu-done-auto-cleanup.js +139 -0
- package/dist/wu-done.js +11 -4
- package/dist/wu-edit.js +0 -12
- package/dist/wu-preflight.js +0 -1
- package/dist/wu-prep.js +0 -1
- package/dist/wu-proto.js +329 -0
- package/dist/wu-spawn.js +0 -3
- package/dist/wu-unblock.js +0 -2
- package/dist/wu-validate.js +0 -1
- package/package.json +9 -7
- package/templates/core/.husky/pre-commit.template +93 -0
- package/templates/core/ai/onboarding/quick-ref-commands.md.template +27 -0
- package/templates/core/ai/onboarding/rapid-prototyping.md +143 -0
- package/templates/core/ai/onboarding/starting-prompt.md.template +3 -3
- package/templates/vendors/claude/.claude/CLAUDE.md.template +25 -0
- package/templates/vendors/claude/.claude/hooks/enforce-worktree.sh +135 -0
package/dist/init.js
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
* WU-1028: Vendor-agnostic core + vendor overlays
|
|
6
6
|
* WU-1085: Added createWUParser for proper --help support
|
|
7
7
|
* WU-1171: Added --merge mode, --client flag, AGENTS.md, updated vendor paths
|
|
8
|
+
* WU-1362: Added branch guard to check branch before writing tracked files
|
|
8
9
|
*/
|
|
9
10
|
import * as fs from 'node:fs';
|
|
10
11
|
import * as path from 'node:path';
|
|
@@ -16,6 +17,8 @@ import { getDefaultConfig, createWUParser, WU_OPTIONS } from '@lumenflow/core';
|
|
|
16
17
|
import { GATE_PRESETS } from '@lumenflow/core/dist/gates-config.js';
|
|
17
18
|
// WU-1171: Import merge block utilities
|
|
18
19
|
import { updateMergeBlock } from './merge-block.js';
|
|
20
|
+
// WU-1362: Import worktree guard utilities for branch checking
|
|
21
|
+
import { isMainBranch, isInWorktree } from '@lumenflow/core/dist/core/worktree-guard.js';
|
|
19
22
|
/**
|
|
20
23
|
* WU-1085: CLI option definitions for init command
|
|
21
24
|
* WU-1171: Added --merge and --client options
|
|
@@ -131,6 +134,44 @@ const LUMENFLOW_DIR = '.lumenflow';
|
|
|
131
134
|
const LUMENFLOW_AGENTS_DIR = `${LUMENFLOW_DIR}/agents`;
|
|
132
135
|
const CLAUDE_DIR = '.claude';
|
|
133
136
|
const CLAUDE_AGENTS_DIR = path.join(CLAUDE_DIR, 'agents');
|
|
137
|
+
/**
|
|
138
|
+
* WU-1362: Check branch guard before writing tracked files
|
|
139
|
+
*
|
|
140
|
+
* Warns (but does not block) if:
|
|
141
|
+
* - On main branch AND
|
|
142
|
+
* - Not in a worktree directory AND
|
|
143
|
+
* - Git repository exists (has .git)
|
|
144
|
+
*
|
|
145
|
+
* This prevents accidental main branch pollution during init operations.
|
|
146
|
+
* Uses warning instead of error to allow initial project setup.
|
|
147
|
+
*
|
|
148
|
+
* @param targetDir - Directory where files will be written
|
|
149
|
+
* @param result - ScaffoldResult to add warnings to
|
|
150
|
+
*/
|
|
151
|
+
async function checkBranchGuard(targetDir, result) {
|
|
152
|
+
result.warnings = result.warnings ?? [];
|
|
153
|
+
// Only check if target is a git repository
|
|
154
|
+
const gitDir = path.join(targetDir, '.git');
|
|
155
|
+
if (!fs.existsSync(gitDir)) {
|
|
156
|
+
// Not a git repo - allow scaffold (initial setup)
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
// Check if we're in a worktree (always allow)
|
|
160
|
+
if (isInWorktree({ cwd: targetDir })) {
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
// Check if on main branch
|
|
164
|
+
try {
|
|
165
|
+
const onMain = await isMainBranch();
|
|
166
|
+
if (onMain) {
|
|
167
|
+
result.warnings.push('Running init on main branch in main checkout. ' +
|
|
168
|
+
'Consider using a worktree for changes to tracked files.');
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
catch {
|
|
172
|
+
// Git error (e.g., not initialized) - silently allow
|
|
173
|
+
}
|
|
174
|
+
}
|
|
134
175
|
/**
|
|
135
176
|
* WU-1177: Detect IDE environment from environment variables
|
|
136
177
|
* Auto-detects which AI coding assistant is running
|
|
@@ -279,8 +320,9 @@ const DEFAULT_LANE_DEFINITIONS = [
|
|
|
279
320
|
* Generate YAML configuration with header comment
|
|
280
321
|
* WU-1067: Supports --preset option for config-driven gates
|
|
281
322
|
* WU-1307: Includes default lane definitions for onboarding
|
|
323
|
+
* WU-1364: Supports git config overrides (requireRemote)
|
|
282
324
|
*/
|
|
283
|
-
function generateLumenflowConfigYaml(gatePreset) {
|
|
325
|
+
function generateLumenflowConfigYaml(gatePreset, gitConfigOverride) {
|
|
284
326
|
const header = `# LumenFlow Configuration\n# Generated by: lumenflow init\n# Customize paths based on your project structure\n\n`;
|
|
285
327
|
const config = getDefaultConfig();
|
|
286
328
|
config.directories.agentsDir = LUMENFLOW_AGENTS_DIR;
|
|
@@ -296,6 +338,12 @@ function generateLumenflowConfigYaml(gatePreset) {
|
|
|
296
338
|
config.lanes = {
|
|
297
339
|
definitions: DEFAULT_LANE_DEFINITIONS,
|
|
298
340
|
};
|
|
341
|
+
// WU-1364: Add git config overrides (e.g., requireRemote: false for local-only)
|
|
342
|
+
if (gitConfigOverride) {
|
|
343
|
+
config.git = {
|
|
344
|
+
requireRemote: gitConfigOverride.requireRemote,
|
|
345
|
+
};
|
|
346
|
+
}
|
|
299
347
|
return header + yaml.stringify(config);
|
|
300
348
|
}
|
|
301
349
|
/**
|
|
@@ -314,7 +362,6 @@ function normalizeFrameworkName(framework) {
|
|
|
314
362
|
.replace(/[^a-z0-9_-]+/g, '-')
|
|
315
363
|
// Remove leading dashes and trailing dashes separately (explicit precedence)
|
|
316
364
|
.replace(/^-+/, '')
|
|
317
|
-
// eslint-disable-next-line sonarjs/slow-regex -- Simple pattern, no catastrophic backtracking risk
|
|
318
365
|
.replace(/-+$/, '');
|
|
319
366
|
if (!slug) {
|
|
320
367
|
throw new Error(`Invalid framework name: "${framework}"`);
|
|
@@ -400,7 +447,8 @@ This file provides universal guidance for all AI agents. Additional vendor-speci
|
|
|
400
447
|
`;
|
|
401
448
|
// Template for LUMENFLOW.md (main entry point)
|
|
402
449
|
// WU-1309: Use <project-root> placeholder for portability
|
|
403
|
-
|
|
450
|
+
// WU-1364: Added initiative workflow section
|
|
451
|
+
const LUMENFLOW_MD_TEMPLATE = `# LumenFlow Workflow Guide\n\n**Last updated:** {{DATE}}\n\nLumenFlow is a vendor-agnostic workflow framework for AI-native software development.\n\n---\n\n## Critical Rule: ALWAYS Run wu:done\n\n**After completing work on a WU, you MUST run \`pnpm wu:done --id WU-XXXX\` from the main checkout.**\n\nThis is the single most forgotten step. Do NOT:\n- Write "To Complete: pnpm wu:done" and stop\n- Ask if you should run wu:done\n- Forget to run wu:done\n\n**DO**: Run \`pnpm wu:done --id WU-XXXX\` immediately after gates pass.\n\n---\n\n## When to Use Initiatives\n\nUse **Initiatives** for multi-phase work spanning multiple WUs:\n\n- **Product visions**: "Build a task management app"\n- **Larger features**: Work requiring multiple WUs across lanes\n- **Complex projects**: Anything that needs phased delivery\n\n\`\`\`bash\n# Create an initiative for multi-phase work\npnpm initiative:create --id INIT-001 --title "Feature Name" \\\\\n --description "..." --phase "Phase 1: MVP" --phase "Phase 2: Polish"\n\n# Add WUs to the initiative\npnpm initiative:add-wu --initiative INIT-001 --wu WU-XXX --phase 1\n\n# Track progress\npnpm initiative:status --id INIT-001\n\`\`\`\n\n**Skip initiatives** for: single-file bug fixes, small docs updates, isolated refactoring.\n\n---\n\n## Quick Start\n\n\`\`\`bash\n# 1. Create a WU\npnpm wu:create --id WU-XXXX --lane <Lane> --title "Title"\n\n# 2. Edit WU spec with acceptance criteria, then claim:\npnpm wu:claim --id WU-XXXX --lane <Lane>\ncd worktrees/<lane>-wu-xxxx\n\n# 3. Implement in worktree\n\n# 4. Run gates\npnpm gates --docs-only # for docs changes\npnpm gates # for code changes\n\n# 5. Complete (from main checkout)\ncd <project-root>\npnpm wu:done --id WU-XXXX\n\`\`\`\n\n---\n\n## Core Principles\n\n1. **TDD**: Failing test -> implementation -> passing test (>=90% coverage on new code)\n2. **Library-First**: Search existing libraries before custom code\n3. **DRY/SOLID/KISS/YAGNI**: No magic numbers, no hardcoded strings\n4. **Worktree Discipline**: After \`wu:claim\`, work ONLY in the worktree\n5. **Gates Before Done**: All gates must pass before \`wu:done\`\n6. **Do Not Bypass Hooks**: No \`--no-verify\`, fix issues properly\n7. **Always wu:done**: Complete every WU by running \`pnpm wu:done\`\n\n---\n\n## Documentation Structure\n\n### Core (Vendor-Agnostic)\n\n- **LUMENFLOW.md** - This file, main entry point\n- **.lumenflow/constraints.md** - Non-negotiable workflow constraints\n- **.lumenflow/agents/** - Agent instructions (vendor-agnostic)\n- **.lumenflow.config.yaml** - Workflow configuration\n\n### Optional Overlays\n\n- **CLAUDE.md + .claude/agents/** - Claude Code overlay (auto if Claude Code detected)\n- **{{DOCS_TASKS_PATH}}** - Task boards and WU storage (\`lumenflow init --full\`)\n- **{{DOCS_ONBOARDING_PATH}}** - Agent onboarding docs\n- **.lumenflow.framework.yaml** - Framework hint file (created with \`--framework\`)\n\n---\n\n## Worktree Discipline (IMMUTABLE LAW)\n\nAfter claiming a WU, you MUST work in its worktree:\n\n\`\`\`bash\n# 1. Claim creates worktree\npnpm wu:claim --id WU-XXX --lane <lane>\n\n# 2. IMMEDIATELY cd to worktree\ncd worktrees/<lane>-wu-xxx\n\n# 3. ALL work happens here\n\n# 4. Return to main ONLY to complete\ncd <project-root>\npnpm wu:done --id WU-XXX\n\`\`\`\n\n---\n\n## Definition of Done\n\n- Acceptance criteria satisfied\n- Gates green (\`pnpm gates\` or \`pnpm gates --docs-only\`)\n- WU YAML status = \`done\`\n- \`wu:done\` has been run\n\n---\n\n## Commands Reference\n\n| Command | Description |\n| ----------------- | ----------------------------------- |\n| \`pnpm wu:create\` | Create new WU spec |\n| \`pnpm wu:claim\` | Claim WU and create worktree |\n| \`pnpm wu:done\` | Complete WU (merge, stamp, cleanup) |\n| \`pnpm gates\` | Run quality gates |\n| \`pnpm initiative:create\` | Create multi-phase initiative |\n| \`pnpm initiative:status\` | View initiative progress |\n\n---\n\n## Constraints\n\nSee [.lumenflow/constraints.md](.lumenflow/constraints.md) for the 6 non-negotiable rules.\n\n---\n\n## Agent Onboarding\n\n- Start with **CLAUDE.md** if present (Claude Code overlay).\n- Add vendor-agnostic guidance in **.lumenflow/agents/**.\n- Check the onboarding docs in **{{DOCS_ONBOARDING_PATH}}** for detailed guidance.\n`;
|
|
404
452
|
// Template for .lumenflow/constraints.md
|
|
405
453
|
const CONSTRAINTS_MD_TEMPLATE = `# LumenFlow Constraints Capsule\n\n**Version:** 1.0\n**Last updated:** {{DATE}}\n\n## The 6 Non-Negotiable Constraints\n\n### 1. Worktree Discipline and Git Safety\nWork only in worktrees, treat main as read-only, never run destructive git commands on main.\n\n### 2. WUs Are Specs, Not Code\nRespect code_paths boundaries, no feature creep, no code blocks in WU YAML files.\n\n### 3. Docs-Only vs Code WUs\nDocumentation WUs use \`--docs-only\` gates, code WUs run full gates.\n\n### 4. LLM-First, Zero-Fallback Inference\nUse LLMs for semantic tasks, fall back to safe defaults (never regex/keywords).\n\n### 5. Gates and Skip-Gates\nComplete via \`pnpm wu:done\`; skip-gates only for pre-existing failures with \`--reason\` and \`--fix-wu\`.\n\n### 6. Safety and Governance\nRespect privacy rules, approved sources, security policies; when uncertain, choose safer path.\n\n---\n\n## Mini Audit Checklist\n\nBefore running \`wu:done\`, verify:\n\n- [ ] Working in worktree (not main)\n- [ ] Only modified files in \`code_paths\`\n- [ ] Gates pass\n- [ ] No forbidden git commands used\n- [ ] Acceptance criteria satisfied\n\n---\n\n## Escalation Triggers\n\nStop and ask a human when:\n- Same error repeats 3 times\n- Auth or permissions changes required\n- PII/PHI/safety issues discovered\n- Cloud spend or secrets involved\n`;
|
|
406
454
|
// Template for root CLAUDE.md
|
|
@@ -1046,17 +1094,74 @@ Choose the safer path:
|
|
|
1046
1094
|
- Ask rather than assume
|
|
1047
1095
|
`;
|
|
1048
1096
|
// WU-1307: Lane inference configuration template (hierarchical Parent→Sublane format)
|
|
1097
|
+
// WU-1364: Added Core and Feature as parent lanes for intuitive naming
|
|
1049
1098
|
// This format is required by lane-inference.ts and lane-checker.ts
|
|
1050
1099
|
const LANE_INFERENCE_TEMPLATE = `# Lane Inference Configuration
|
|
1051
1100
|
# Generated by: lumenflow init
|
|
1052
1101
|
#
|
|
1053
1102
|
# Hierarchical format: Parent -> Sublane -> { code_paths, keywords }
|
|
1054
1103
|
# This format is required by lane-inference.ts for proper sub-lane suggestion.
|
|
1104
|
+
#
|
|
1105
|
+
# Common parent lanes: Core, Feature, Framework, Experience, Operations, Content
|
|
1106
|
+
|
|
1107
|
+
# Core Lane: Platform foundations, shared libraries, base infrastructure
|
|
1108
|
+
Core:
|
|
1109
|
+
Platform:
|
|
1110
|
+
description: 'Core platform: shared utilities, base infrastructure, common libraries'
|
|
1111
|
+
code_paths:
|
|
1112
|
+
- 'packages/**/core/**'
|
|
1113
|
+
- 'src/core/**'
|
|
1114
|
+
- 'src/lib/**'
|
|
1115
|
+
- 'lib/**'
|
|
1116
|
+
keywords:
|
|
1117
|
+
- 'platform'
|
|
1118
|
+
- 'core'
|
|
1119
|
+
- 'infrastructure'
|
|
1120
|
+
- 'foundation'
|
|
1121
|
+
|
|
1122
|
+
Library:
|
|
1123
|
+
description: 'Shared libraries and utilities'
|
|
1124
|
+
code_paths:
|
|
1125
|
+
- 'packages/**/lib/**'
|
|
1126
|
+
- 'src/utils/**'
|
|
1127
|
+
- 'src/helpers/**'
|
|
1128
|
+
keywords:
|
|
1129
|
+
- 'library'
|
|
1130
|
+
- 'utility'
|
|
1131
|
+
- 'helper'
|
|
1132
|
+
- 'shared'
|
|
1055
1133
|
|
|
1056
|
-
#
|
|
1134
|
+
# Feature Lane: Product features and user-facing functionality
|
|
1135
|
+
Feature:
|
|
1136
|
+
Backend:
|
|
1137
|
+
description: 'Backend features: APIs, services, business logic'
|
|
1138
|
+
code_paths:
|
|
1139
|
+
- 'src/api/**'
|
|
1140
|
+
- 'src/services/**'
|
|
1141
|
+
- 'packages/**/api/**'
|
|
1142
|
+
keywords:
|
|
1143
|
+
- 'api'
|
|
1144
|
+
- 'service'
|
|
1145
|
+
- 'backend'
|
|
1146
|
+
- 'business logic'
|
|
1147
|
+
|
|
1148
|
+
Frontend:
|
|
1149
|
+
description: 'Frontend features: UI, components, pages'
|
|
1150
|
+
code_paths:
|
|
1151
|
+
- 'src/components/**'
|
|
1152
|
+
- 'src/pages/**'
|
|
1153
|
+
- 'src/app/**'
|
|
1154
|
+
- 'apps/web/**'
|
|
1155
|
+
keywords:
|
|
1156
|
+
- 'frontend'
|
|
1157
|
+
- 'ui'
|
|
1158
|
+
- 'component'
|
|
1159
|
+
- 'page'
|
|
1160
|
+
|
|
1161
|
+
# Framework Lane: Framework-specific code and tooling
|
|
1057
1162
|
Framework:
|
|
1058
1163
|
Core:
|
|
1059
|
-
description: 'Core
|
|
1164
|
+
description: 'Core framework: business logic, domain models, utilities'
|
|
1060
1165
|
code_paths:
|
|
1061
1166
|
- 'packages/**/core/**'
|
|
1062
1167
|
- 'src/core/**'
|
|
@@ -1151,6 +1256,7 @@ Content:
|
|
|
1151
1256
|
{{FRAMEWORK_LANES}}
|
|
1152
1257
|
`;
|
|
1153
1258
|
// WU-1300: Starting prompt template for agent onboarding
|
|
1259
|
+
// WU-1364: Added "When Starting From Product Vision" section for initiative-first workflow
|
|
1154
1260
|
const STARTING_PROMPT_TEMPLATE = `# Starting Prompt for LumenFlow Agents
|
|
1155
1261
|
|
|
1156
1262
|
**Last updated:** {{DATE}}
|
|
@@ -1159,6 +1265,48 @@ This document provides the initial context for AI agents working on this project
|
|
|
1159
1265
|
|
|
1160
1266
|
---
|
|
1161
1267
|
|
|
1268
|
+
## When Starting From Product Vision
|
|
1269
|
+
|
|
1270
|
+
If you are starting a new project or feature from a product vision (e.g., "Build a task management app"), **do NOT create standalone WUs immediately**. Instead, follow the initiative-first workflow:
|
|
1271
|
+
|
|
1272
|
+
### 4-Step Initiative Workflow
|
|
1273
|
+
|
|
1274
|
+
1. **Create an Initiative**: Capture the vision as an initiative
|
|
1275
|
+
\`\`\`bash
|
|
1276
|
+
pnpm initiative:create --id INIT-001 --title "Task Management App" \\
|
|
1277
|
+
--description "Build a task management application with..." \\
|
|
1278
|
+
--phase "Phase 1: Core MVP" --phase "Phase 2: Collaboration"
|
|
1279
|
+
\`\`\`
|
|
1280
|
+
|
|
1281
|
+
2. **Define Phases**: Break the vision into logical phases (MVP, iteration, polish)
|
|
1282
|
+
|
|
1283
|
+
3. **Create WUs under the Initiative**: Each WU belongs to a phase
|
|
1284
|
+
\`\`\`bash
|
|
1285
|
+
pnpm wu:create --lane "Core: Platform" --title "Add task model" \\
|
|
1286
|
+
--description "..." --acceptance "..." --code-paths "..." \\
|
|
1287
|
+
&& pnpm initiative:add-wu --initiative INIT-001 --wu WU-XXX --phase 1
|
|
1288
|
+
\`\`\`
|
|
1289
|
+
|
|
1290
|
+
4. **Track Progress**: Use \`pnpm initiative:status --id INIT-001\` to see overall progress
|
|
1291
|
+
|
|
1292
|
+
### Why Initiatives Matter
|
|
1293
|
+
|
|
1294
|
+
- **Avoid orphan WUs**: Without initiative structure, agents create disconnected WUs that lack coherent scope
|
|
1295
|
+
- **Better coordination**: Phases enable parallel work across lanes
|
|
1296
|
+
- **Clear completion criteria**: The initiative tracks when all phases are done
|
|
1297
|
+
- **Visibility**: Stakeholders can see multi-phase progress
|
|
1298
|
+
|
|
1299
|
+
### When to Skip Initiatives
|
|
1300
|
+
|
|
1301
|
+
Only skip initiatives for:
|
|
1302
|
+
- Single-file bug fixes
|
|
1303
|
+
- Small documentation updates
|
|
1304
|
+
- Isolated refactoring tasks
|
|
1305
|
+
|
|
1306
|
+
If work spans multiple WUs or multiple days, create an initiative first.
|
|
1307
|
+
|
|
1308
|
+
---
|
|
1309
|
+
|
|
1162
1310
|
## Step 1: Read Core Documentation
|
|
1163
1311
|
|
|
1164
1312
|
Before starting any work, read these documents in order:
|
|
@@ -1197,6 +1345,8 @@ LumenFlow uses Work Units (WUs) to track all changes:
|
|
|
1197
1345
|
| \`pnpm gates\` | Run quality gates |
|
|
1198
1346
|
| \`pnpm wu:done --id WU-XXX\` | Complete WU |
|
|
1199
1347
|
| \`pnpm wu:status --id WU-XXX\` | Check WU status |
|
|
1348
|
+
| \`pnpm initiative:create ...\` | Create a new initiative |
|
|
1349
|
+
| \`pnpm initiative:status --id INIT-XXX\` | Check initiative progress |
|
|
1200
1350
|
|
|
1201
1351
|
---
|
|
1202
1352
|
|
|
@@ -1835,6 +1985,85 @@ function getFileMode(options) {
|
|
|
1835
1985
|
}
|
|
1836
1986
|
return 'skip';
|
|
1837
1987
|
}
|
|
1988
|
+
/**
|
|
1989
|
+
* WU-1364: Check if directory is a git repository
|
|
1990
|
+
*/
|
|
1991
|
+
function isGitRepo(targetDir) {
|
|
1992
|
+
try {
|
|
1993
|
+
execFileSync('git', ['rev-parse', '--git-dir'], {
|
|
1994
|
+
cwd: targetDir,
|
|
1995
|
+
stdio: 'pipe',
|
|
1996
|
+
});
|
|
1997
|
+
return true;
|
|
1998
|
+
}
|
|
1999
|
+
catch {
|
|
2000
|
+
return false;
|
|
2001
|
+
}
|
|
2002
|
+
}
|
|
2003
|
+
/**
|
|
2004
|
+
* WU-1364: Check if git repo has any commits
|
|
2005
|
+
*/
|
|
2006
|
+
function hasGitCommits(targetDir) {
|
|
2007
|
+
try {
|
|
2008
|
+
execFileSync('git', ['rev-parse', 'HEAD'], {
|
|
2009
|
+
cwd: targetDir,
|
|
2010
|
+
stdio: 'pipe',
|
|
2011
|
+
});
|
|
2012
|
+
return true;
|
|
2013
|
+
}
|
|
2014
|
+
catch {
|
|
2015
|
+
return false;
|
|
2016
|
+
}
|
|
2017
|
+
}
|
|
2018
|
+
/**
|
|
2019
|
+
* WU-1364: Check if git repo has an origin remote
|
|
2020
|
+
*/
|
|
2021
|
+
function hasOriginRemote(targetDir) {
|
|
2022
|
+
try {
|
|
2023
|
+
const result = execFileSync('git', ['remote', 'get-url', 'origin'], {
|
|
2024
|
+
cwd: targetDir,
|
|
2025
|
+
encoding: 'utf-8',
|
|
2026
|
+
stdio: 'pipe',
|
|
2027
|
+
});
|
|
2028
|
+
return result.trim().length > 0;
|
|
2029
|
+
}
|
|
2030
|
+
catch {
|
|
2031
|
+
return false;
|
|
2032
|
+
}
|
|
2033
|
+
}
|
|
2034
|
+
/**
|
|
2035
|
+
* WU-1364: Create initial commit if git repo has no commits
|
|
2036
|
+
*/
|
|
2037
|
+
function createInitialCommitIfNeeded(targetDir) {
|
|
2038
|
+
if (!isGitRepo(targetDir) || hasGitCommits(targetDir)) {
|
|
2039
|
+
return false;
|
|
2040
|
+
}
|
|
2041
|
+
try {
|
|
2042
|
+
// Stage all files
|
|
2043
|
+
execFileSync('git', ['add', '.'], { cwd: targetDir, stdio: 'pipe' });
|
|
2044
|
+
// Create initial commit
|
|
2045
|
+
execFileSync('git', ['commit', '-m', 'chore: initialize LumenFlow project'], {
|
|
2046
|
+
cwd: targetDir,
|
|
2047
|
+
stdio: 'pipe',
|
|
2048
|
+
});
|
|
2049
|
+
return true;
|
|
2050
|
+
}
|
|
2051
|
+
catch {
|
|
2052
|
+
return false;
|
|
2053
|
+
}
|
|
2054
|
+
}
|
|
2055
|
+
function detectGitStateConfig(targetDir) {
|
|
2056
|
+
// If not a git repo, default to local-only mode for safety
|
|
2057
|
+
if (!isGitRepo(targetDir)) {
|
|
2058
|
+
return { requireRemote: false };
|
|
2059
|
+
}
|
|
2060
|
+
// If git repo but no origin remote, set requireRemote: false
|
|
2061
|
+
if (!hasOriginRemote(targetDir)) {
|
|
2062
|
+
return { requireRemote: false };
|
|
2063
|
+
}
|
|
2064
|
+
// Has origin remote - use default (requireRemote: true)
|
|
2065
|
+
return null;
|
|
2066
|
+
}
|
|
1838
2067
|
/**
|
|
1839
2068
|
* WU-1171: Get templates directory path
|
|
1840
2069
|
*/
|
|
@@ -1862,6 +2091,7 @@ function loadTemplate(templatePath) {
|
|
|
1862
2091
|
/**
|
|
1863
2092
|
* Scaffold a new LumenFlow project
|
|
1864
2093
|
* WU-1171: Added AGENTS.md, --merge mode, updated vendor/client handling
|
|
2094
|
+
* WU-1362: Added branch guard to prevent main branch pollution
|
|
1865
2095
|
*/
|
|
1866
2096
|
export async function scaffoldProject(targetDir, options) {
|
|
1867
2097
|
const result = {
|
|
@@ -1870,9 +2100,12 @@ export async function scaffoldProject(targetDir, options) {
|
|
|
1870
2100
|
merged: [],
|
|
1871
2101
|
warnings: [],
|
|
1872
2102
|
};
|
|
2103
|
+
// WU-1362: Check branch before writing tracked files
|
|
2104
|
+
// Only block if we're on main branch AND not in a worktree
|
|
2105
|
+
// This allows scaffold to run in worktrees and during initial setup
|
|
2106
|
+
await checkBranchGuard(targetDir, result);
|
|
1873
2107
|
const defaultClient = options.defaultClient ?? detectDefaultClient();
|
|
1874
2108
|
// WU-1171: Use resolveClientType with both client and vendor (vendor is deprecated but kept for backwards compat)
|
|
1875
|
-
// eslint-disable-next-line sonarjs/deprecation -- Intentional backwards compatibility
|
|
1876
2109
|
const client = resolveClientType(options.client, options.vendor, defaultClient);
|
|
1877
2110
|
const fileMode = getFileMode(options);
|
|
1878
2111
|
// Ensure target directory exists
|
|
@@ -1882,6 +2115,8 @@ export async function scaffoldProject(targetDir, options) {
|
|
|
1882
2115
|
// WU-1309: Detect or use specified docs structure
|
|
1883
2116
|
const docsStructure = options.docsStructure ?? detectDocsStructure(targetDir);
|
|
1884
2117
|
const docsPaths = getDocsPath(docsStructure);
|
|
2118
|
+
// WU-1364: Detect git state for config generation
|
|
2119
|
+
const gitConfigOverride = detectGitStateConfig(targetDir);
|
|
1885
2120
|
const tokenDefaults = {
|
|
1886
2121
|
DATE: getCurrentDate(),
|
|
1887
2122
|
PROJECT_ROOT: '<project-root>', // WU-1309: Use portable placeholder
|
|
@@ -1891,8 +2126,9 @@ export async function scaffoldProject(targetDir, options) {
|
|
|
1891
2126
|
DOCS_ONBOARDING_PATH: docsPaths.onboarding,
|
|
1892
2127
|
};
|
|
1893
2128
|
// Create .lumenflow.config.yaml (WU-1067: includes gate preset if specified)
|
|
2129
|
+
// WU-1364: Includes git config overrides (e.g., requireRemote: false for local-only)
|
|
1894
2130
|
// Note: Config files don't use merge mode (always skip or force)
|
|
1895
|
-
await createFile(path.join(targetDir, CONFIG_FILE_NAME), generateLumenflowConfigYaml(options.gatePreset), options.force ? 'force' : 'skip', result, targetDir);
|
|
2131
|
+
await createFile(path.join(targetDir, CONFIG_FILE_NAME), generateLumenflowConfigYaml(options.gatePreset, gitConfigOverride), options.force ? 'force' : 'skip', result, targetDir);
|
|
1896
2132
|
// WU-1171: Create AGENTS.md (universal entry point for all agents)
|
|
1897
2133
|
try {
|
|
1898
2134
|
const agentsTemplate = loadTemplate('core/AGENTS.md.template');
|
|
@@ -1925,6 +2161,12 @@ export async function scaffoldProject(targetDir, options) {
|
|
|
1925
2161
|
if (options.full) {
|
|
1926
2162
|
await injectPackageJsonScripts(targetDir, options, result);
|
|
1927
2163
|
}
|
|
2164
|
+
// WU-1364: Create initial commit if git repo has no commits
|
|
2165
|
+
// This must be done after all files are created
|
|
2166
|
+
const createdInitialCommit = createInitialCommitIfNeeded(targetDir);
|
|
2167
|
+
if (createdInitialCommit) {
|
|
2168
|
+
result.created.push('Initial git commit');
|
|
2169
|
+
}
|
|
1928
2170
|
return result;
|
|
1929
2171
|
}
|
|
1930
2172
|
/**
|
|
@@ -2011,6 +2253,7 @@ const LUMENFLOW_SCRIPTS = {
|
|
|
2011
2253
|
'wu:claim': 'wu-claim',
|
|
2012
2254
|
'wu:done': 'wu-done',
|
|
2013
2255
|
'wu:create': 'wu-create',
|
|
2256
|
+
'wu:proto': 'wu-proto', // WU-1359: Prototype WU with relaxed validation
|
|
2014
2257
|
'wu:status': 'wu-status',
|
|
2015
2258
|
'wu:block': 'wu-block',
|
|
2016
2259
|
'wu:unblock': 'wu-unblock',
|
|
@@ -2318,7 +2561,6 @@ function writeNewFile(filePath, content, result, relativePath) {
|
|
|
2318
2561
|
* WU-1171: Added --merge and --client support
|
|
2319
2562
|
*/
|
|
2320
2563
|
export async function main() {
|
|
2321
|
-
/* eslint-disable no-console -- CLI tool requires console output for user feedback */
|
|
2322
2564
|
const opts = parseInitOptions();
|
|
2323
2565
|
const targetDir = process.cwd();
|
|
2324
2566
|
console.log('[lumenflow init] Scaffolding LumenFlow project...');
|
|
@@ -2361,17 +2603,30 @@ export async function main() {
|
|
|
2361
2603
|
console.log('\nWarnings:');
|
|
2362
2604
|
result.warnings.forEach((w) => console.log(` ⚠ ${w}`));
|
|
2363
2605
|
}
|
|
2606
|
+
// WU-1359: Show complete lifecycle with auto-ID (no --id flag required)
|
|
2607
|
+
// WU-1364: Added initiative-first guidance for product visions
|
|
2364
2608
|
console.log('\n[lumenflow init] Done! Next steps:');
|
|
2365
2609
|
console.log(' 1. Review AGENTS.md and LUMENFLOW.md for workflow documentation');
|
|
2366
2610
|
console.log(` 2. Edit ${CONFIG_FILE_NAME} to match your project structure`);
|
|
2367
|
-
console.log('
|
|
2368
|
-
|
|
2611
|
+
console.log('');
|
|
2612
|
+
console.log(' For a product vision (multi-phase work):');
|
|
2613
|
+
console.log(' pnpm initiative:create --id INIT-001 --title "Project Name" \\');
|
|
2614
|
+
console.log(' --phase "Phase 1: MVP" --phase "Phase 2: Polish"');
|
|
2615
|
+
console.log('');
|
|
2616
|
+
console.log(' For a single WU:');
|
|
2617
|
+
console.log(' pnpm wu:create --lane <lane> --title "First WU" \\');
|
|
2618
|
+
console.log(' --description "Context: ... Problem: ... Solution: ..." \\');
|
|
2619
|
+
console.log(' --acceptance "Criterion 1" --code-paths "src/..." --exposure backend-only');
|
|
2620
|
+
console.log('');
|
|
2621
|
+
console.log(' # Or for rapid prototyping (minimal validation):');
|
|
2622
|
+
console.log(' pnpm wu:proto --lane <lane> --title "Quick experiment"');
|
|
2623
|
+
console.log('');
|
|
2624
|
+
console.log(' Full lifecycle: wu:create -> wu:claim -> wu:prep -> wu:done');
|
|
2369
2625
|
}
|
|
2370
2626
|
// WU-1297: Use import.meta.main instead of exporting main() without calling it
|
|
2371
2627
|
// This ensures main() runs when the script is executed as a CLI entry point
|
|
2372
2628
|
if (import.meta.main) {
|
|
2373
2629
|
main().catch((err) => {
|
|
2374
|
-
// eslint-disable-next-line no-console -- CLI error output
|
|
2375
2630
|
console.error('[lumenflow init] Error:', err instanceof Error ? err.message : String(err));
|
|
2376
2631
|
process.exit(1);
|
|
2377
2632
|
});
|
|
@@ -1,7 +1,4 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
/* eslint-disable security/detect-non-literal-fs-filename */
|
|
3
|
-
/* eslint-disable no-console -- CLI tool requires console output */
|
|
4
|
-
/* eslint-disable @typescript-eslint/explicit-function-return-type -- CLI tool uses dynamic YAML types */
|
|
5
2
|
/**
|
|
6
3
|
* Initiative Create Helper (WU-1247, WU-1439)
|
|
7
4
|
*
|
package/dist/initiative-edit.js
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
/* eslint-disable no-console -- CLI tool requires console output */
|
|
3
|
-
/* eslint-disable @typescript-eslint/explicit-function-return-type -- CLI tool uses dynamic YAML types */
|
|
4
2
|
/**
|
|
5
3
|
* Initiative Edit Helper
|
|
6
4
|
*
|
|
@@ -225,12 +223,10 @@ function validateCreatedDate(date) {
|
|
|
225
223
|
*/
|
|
226
224
|
function loadInitiative(id) {
|
|
227
225
|
const initPath = INIT_PATHS.INITIATIVE(id);
|
|
228
|
-
// eslint-disable-next-line security/detect-non-literal-fs-filename -- CLI tool validates init files
|
|
229
226
|
if (!existsSync(initPath)) {
|
|
230
227
|
die(`Initiative ${id} not found at ${initPath}\n\n` +
|
|
231
228
|
`Ensure the Initiative exists and you're in the repo root.`);
|
|
232
229
|
}
|
|
233
|
-
// eslint-disable-next-line security/detect-non-literal-fs-filename -- CLI tool validates init files
|
|
234
230
|
const content = readFileSync(initPath, { encoding: FILE_SYSTEM.ENCODING });
|
|
235
231
|
return parseYAML(content);
|
|
236
232
|
}
|
|
@@ -428,7 +424,6 @@ async function main() {
|
|
|
428
424
|
// Write updated Initiative to micro-worktree
|
|
429
425
|
const initPath = join(worktreePath, INIT_PATHS.INITIATIVE(id));
|
|
430
426
|
const yamlContent = stringifyYAML(updatedInit);
|
|
431
|
-
// eslint-disable-next-line security/detect-non-literal-fs-filename -- CLI tool writes init files
|
|
432
427
|
writeFileSync(initPath, yamlContent, { encoding: FILE_SYSTEM.ENCODING });
|
|
433
428
|
console.log(`${PREFIX} Updated ${id}.yaml in micro-worktree`);
|
|
434
429
|
return {
|
package/dist/initiative-plan.js
CHANGED
package/dist/lane-health.js
CHANGED
|
@@ -301,9 +301,7 @@ export function formatLaneHealthReport(report) {
|
|
|
301
301
|
// CLI Entry Point
|
|
302
302
|
// ============================================================================
|
|
303
303
|
/** Logger for CLI output */
|
|
304
|
-
// eslint-disable-next-line no-console
|
|
305
304
|
const log = console.log.bind(console);
|
|
306
|
-
// eslint-disable-next-line no-console
|
|
307
305
|
const warn = console.warn.bind(console);
|
|
308
306
|
/**
|
|
309
307
|
* Run lane health check
|
package/dist/lane-suggest.js
CHANGED
package/dist/mem-checkpoint.js
CHANGED
|
@@ -76,10 +76,8 @@ async function writeAuditLog(baseDir, entry) {
|
|
|
76
76
|
try {
|
|
77
77
|
const logPath = path.join(baseDir, LUMENFLOW_PATHS.AUDIT_LOG);
|
|
78
78
|
const logDir = path.dirname(logPath);
|
|
79
|
-
// eslint-disable-next-line security/detect-non-literal-fs-filename -- CLI tool creates known directory
|
|
80
79
|
await fs.mkdir(logDir, { recursive: true });
|
|
81
80
|
const line = `${JSON.stringify(entry)}\n`;
|
|
82
|
-
// eslint-disable-next-line security/detect-non-literal-fs-filename -- CLI tool writes audit log
|
|
83
81
|
await fs.appendFile(logPath, line, 'utf-8');
|
|
84
82
|
}
|
|
85
83
|
catch {
|
package/dist/mem-cleanup.js
CHANGED
|
@@ -88,10 +88,8 @@ async function writeAuditLog(baseDir, entry) {
|
|
|
88
88
|
try {
|
|
89
89
|
const logPath = path.join(baseDir, LUMENFLOW_PATHS.AUDIT_LOG);
|
|
90
90
|
const logDir = path.dirname(logPath);
|
|
91
|
-
// eslint-disable-next-line security/detect-non-literal-fs-filename -- CLI tool creates known directory
|
|
92
91
|
await fs.mkdir(logDir, { recursive: true });
|
|
93
92
|
const line = `${JSON.stringify(entry)}\n`;
|
|
94
|
-
// eslint-disable-next-line security/detect-non-literal-fs-filename -- CLI tool writes audit log
|
|
95
93
|
await fs.appendFile(logPath, line, 'utf-8');
|
|
96
94
|
}
|
|
97
95
|
catch {
|
package/dist/mem-context.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
/* eslint-disable no-console -- CLI tool requires console output */
|
|
3
2
|
/**
|
|
4
3
|
* Memory Context CLI (WU-1234, WU-1292)
|
|
5
4
|
*
|
|
@@ -96,10 +95,8 @@ async function writeAuditLog(baseDir, entry) {
|
|
|
96
95
|
try {
|
|
97
96
|
const logPath = path.join(baseDir, LUMENFLOW_PATHS.AUDIT_LOG);
|
|
98
97
|
const logDir = path.dirname(logPath);
|
|
99
|
-
// eslint-disable-next-line security/detect-non-literal-fs-filename -- CLI tool creates known directory
|
|
100
98
|
await fs.mkdir(logDir, { recursive: true });
|
|
101
99
|
const line = `${JSON.stringify(entry)}\n`;
|
|
102
|
-
// eslint-disable-next-line security/detect-non-literal-fs-filename -- CLI tool writes audit log
|
|
103
100
|
await fs.appendFile(logPath, line, 'utf-8');
|
|
104
101
|
}
|
|
105
102
|
catch {
|
package/dist/mem-create.js
CHANGED
|
@@ -83,10 +83,8 @@ async function writeAuditLog(baseDir, entry) {
|
|
|
83
83
|
try {
|
|
84
84
|
const logPath = path.join(baseDir, LUMENFLOW_PATHS.AUDIT_LOG);
|
|
85
85
|
const logDir = path.dirname(logPath);
|
|
86
|
-
// eslint-disable-next-line security/detect-non-literal-fs-filename -- CLI tool creates known directory
|
|
87
86
|
await fs.mkdir(logDir, { recursive: true });
|
|
88
87
|
const line = `${JSON.stringify(entry)}\n`;
|
|
89
|
-
// eslint-disable-next-line security/detect-non-literal-fs-filename -- CLI tool writes audit log
|
|
90
88
|
await fs.appendFile(logPath, line, 'utf-8');
|
|
91
89
|
}
|
|
92
90
|
catch {
|
package/dist/mem-delete.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
/* eslint-disable no-console -- CLI command uses console for status output */
|
|
3
2
|
/**
|
|
4
3
|
* Memory Delete CLI (WU-1284)
|
|
5
4
|
*
|
|
@@ -81,10 +80,8 @@ async function writeAuditLog(baseDir, entry) {
|
|
|
81
80
|
try {
|
|
82
81
|
const logPath = path.join(baseDir, LUMENFLOW_PATHS.AUDIT_LOG);
|
|
83
82
|
const logDir = path.dirname(logPath);
|
|
84
|
-
// eslint-disable-next-line security/detect-non-literal-fs-filename -- CLI tool creates known directory
|
|
85
83
|
await fs.mkdir(logDir, { recursive: true });
|
|
86
84
|
const line = `${JSON.stringify(entry)}\n`;
|
|
87
|
-
// eslint-disable-next-line security/detect-non-literal-fs-filename -- CLI tool writes audit log
|
|
88
85
|
await fs.appendFile(logPath, line, 'utf-8');
|
|
89
86
|
}
|
|
90
87
|
catch {
|
package/dist/mem-inbox.js
CHANGED
|
@@ -77,10 +77,8 @@ async function writeAuditLog(baseDir, entry) {
|
|
|
77
77
|
try {
|
|
78
78
|
const logPath = path.join(baseDir, LUMENFLOW_PATHS.AUDIT_LOG);
|
|
79
79
|
const logDir = path.dirname(logPath);
|
|
80
|
-
// eslint-disable-next-line security/detect-non-literal-fs-filename -- CLI tool creates known directory
|
|
81
80
|
await fs.mkdir(logDir, { recursive: true });
|
|
82
81
|
const line = `${JSON.stringify(entry)}\n`;
|
|
83
|
-
// eslint-disable-next-line security/detect-non-literal-fs-filename -- CLI tool writes audit log
|
|
84
82
|
await fs.appendFile(logPath, line, 'utf-8');
|
|
85
83
|
}
|
|
86
84
|
catch {
|
package/dist/mem-index.js
CHANGED
package/dist/mem-init.js
CHANGED
|
@@ -36,11 +36,9 @@ async function writeAuditLog(baseDir, entry) {
|
|
|
36
36
|
const logPath = path.join(baseDir, LUMENFLOW_PATHS.AUDIT_LOG);
|
|
37
37
|
const logDir = path.dirname(logPath);
|
|
38
38
|
// Ensure telemetry directory exists
|
|
39
|
-
// eslint-disable-next-line security/detect-non-literal-fs-filename -- CLI tool creates known directory
|
|
40
39
|
await fs.mkdir(logDir, { recursive: true });
|
|
41
40
|
// Append NDJSON entry
|
|
42
41
|
const line = `${JSON.stringify(entry)}\n`;
|
|
43
|
-
// eslint-disable-next-line security/detect-non-literal-fs-filename -- CLI tool writes audit log
|
|
44
42
|
await fs.appendFile(logPath, line, 'utf-8');
|
|
45
43
|
}
|
|
46
44
|
catch {
|
package/dist/mem-profile.js
CHANGED
package/dist/mem-promote.js
CHANGED
package/dist/mem-ready.js
CHANGED
|
@@ -62,10 +62,8 @@ async function writeAuditLog(baseDir, entry) {
|
|
|
62
62
|
try {
|
|
63
63
|
const logPath = path.join(baseDir, LUMENFLOW_PATHS.AUDIT_LOG);
|
|
64
64
|
const logDir = path.dirname(logPath);
|
|
65
|
-
// eslint-disable-next-line security/detect-non-literal-fs-filename -- CLI tool creates known directory
|
|
66
65
|
await fs.mkdir(logDir, { recursive: true });
|
|
67
66
|
const line = `${JSON.stringify(entry)}\n`;
|
|
68
|
-
// eslint-disable-next-line security/detect-non-literal-fs-filename -- CLI tool writes audit log
|
|
69
67
|
await fs.appendFile(logPath, line, 'utf-8');
|
|
70
68
|
}
|
|
71
69
|
catch {
|