@lumenflow/cli 2.2.2 → 2.3.1
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 +147 -57
- package/dist/__tests__/agent-log-issue.test.js +56 -0
- package/dist/__tests__/cli-entry-point.test.js +66 -17
- package/dist/__tests__/cli-subprocess.test.js +25 -0
- package/dist/__tests__/init.test.js +298 -0
- package/dist/__tests__/initiative-plan.test.js +340 -0
- package/dist/__tests__/mem-cleanup-execution.test.js +19 -0
- package/dist/__tests__/merge-block.test.js +220 -0
- package/dist/__tests__/safe-git.test.js +191 -0
- package/dist/__tests__/state-doctor.test.js +274 -0
- package/dist/__tests__/wu-done.test.js +36 -0
- package/dist/__tests__/wu-edit.test.js +119 -0
- package/dist/__tests__/wu-prep.test.js +108 -0
- package/dist/agent-issues-query.js +4 -3
- package/dist/agent-log-issue.js +25 -4
- package/dist/backlog-prune.js +5 -4
- package/dist/cli-entry-point.js +11 -1
- package/dist/doctor.js +368 -0
- package/dist/flow-bottlenecks.js +6 -5
- package/dist/flow-report.js +4 -3
- package/dist/gates.js +356 -101
- package/dist/guard-locked.js +4 -3
- package/dist/guard-worktree-commit.js +4 -3
- package/dist/init.js +508 -86
- package/dist/initiative-add-wu.js +4 -3
- package/dist/initiative-bulk-assign-wus.js +8 -5
- package/dist/initiative-create.js +73 -37
- package/dist/initiative-edit.js +37 -21
- package/dist/initiative-list.js +4 -3
- package/dist/initiative-plan.js +337 -0
- package/dist/initiative-status.js +4 -3
- package/dist/lane-health.js +377 -0
- package/dist/lane-suggest.js +382 -0
- package/dist/mem-checkpoint.js +2 -2
- package/dist/mem-cleanup.js +2 -2
- package/dist/mem-context.js +306 -0
- package/dist/mem-create.js +2 -2
- package/dist/mem-delete.js +293 -0
- package/dist/mem-inbox.js +2 -2
- package/dist/mem-index.js +211 -0
- package/dist/mem-init.js +1 -1
- package/dist/mem-profile.js +207 -0
- package/dist/mem-promote.js +254 -0
- package/dist/mem-ready.js +2 -2
- package/dist/mem-signal.js +2 -2
- package/dist/mem-start.js +2 -2
- package/dist/mem-summarize.js +2 -2
- package/dist/mem-triage.js +2 -2
- package/dist/merge-block.js +222 -0
- package/dist/metrics-cli.js +7 -4
- package/dist/metrics-snapshot.js +4 -3
- package/dist/orchestrate-initiative.js +10 -4
- package/dist/orchestrate-monitor.js +379 -31
- package/dist/signal-cleanup.js +296 -0
- package/dist/spawn-list.js +6 -5
- package/dist/state-bootstrap.js +5 -4
- package/dist/state-cleanup.js +360 -0
- package/dist/state-doctor-fix.js +196 -0
- package/dist/state-doctor.js +501 -0
- package/dist/validate-agent-skills.js +4 -3
- package/dist/validate-agent-sync.js +4 -3
- package/dist/validate-backlog-sync.js +4 -3
- package/dist/validate-skills-spec.js +4 -3
- package/dist/validate.js +4 -3
- package/dist/wu-block.js +3 -3
- package/dist/wu-claim.js +208 -98
- package/dist/wu-cleanup.js +5 -4
- package/dist/wu-create.js +71 -46
- package/dist/wu-delete.js +88 -60
- package/dist/wu-deps.js +6 -5
- package/dist/wu-done-check.js +34 -0
- package/dist/wu-done.js +39 -12
- package/dist/wu-edit.js +63 -28
- package/dist/wu-infer-lane.js +7 -6
- package/dist/wu-preflight.js +23 -81
- package/dist/wu-prep.js +125 -0
- package/dist/wu-prune.js +4 -3
- package/dist/wu-recover.js +88 -22
- package/dist/wu-repair.js +7 -6
- package/dist/wu-spawn.js +226 -270
- package/dist/wu-status.js +4 -3
- package/dist/wu-unblock.js +5 -5
- package/dist/wu-unlock-lane.js +4 -3
- package/dist/wu-validate.js +5 -4
- package/package.json +16 -7
- package/templates/core/.lumenflow/constraints.md.template +192 -0
- package/templates/core/.lumenflow/rules/git-safety.md.template +27 -0
- package/templates/core/.lumenflow/rules/wu-workflow.md.template +48 -0
- package/templates/core/AGENTS.md.template +60 -0
- package/templates/core/LUMENFLOW.md.template +255 -0
- package/templates/core/UPGRADING.md.template +121 -0
- package/templates/core/ai/onboarding/agent-safety-card.md.template +106 -0
- package/templates/core/ai/onboarding/first-wu-mistakes.md.template +198 -0
- package/templates/core/ai/onboarding/quick-ref-commands.md.template +186 -0
- package/templates/core/ai/onboarding/release-process.md.template +362 -0
- package/templates/core/ai/onboarding/troubleshooting-wu-done.md.template +159 -0
- package/templates/core/ai/onboarding/wu-create-checklist.md.template +117 -0
- package/templates/vendors/aider/.aider.conf.yml.template +27 -0
- package/templates/vendors/claude/.claude/CLAUDE.md.template +52 -0
- package/templates/vendors/claude/.claude/settings.json.template +49 -0
- package/templates/vendors/claude/.claude/skills/bug-classification/SKILL.md.template +192 -0
- package/templates/vendors/claude/.claude/skills/code-quality/SKILL.md.template +152 -0
- package/templates/vendors/claude/.claude/skills/context-management/SKILL.md.template +155 -0
- package/templates/vendors/claude/.claude/skills/execution-memory/SKILL.md.template +304 -0
- package/templates/vendors/claude/.claude/skills/frontend-design/SKILL.md.template +131 -0
- package/templates/vendors/claude/.claude/skills/initiative-management/SKILL.md.template +164 -0
- package/templates/vendors/claude/.claude/skills/library-first/SKILL.md.template +98 -0
- package/templates/vendors/claude/.claude/skills/lumenflow-gates/SKILL.md.template +87 -0
- package/templates/vendors/claude/.claude/skills/multi-agent-coordination/SKILL.md.template +84 -0
- package/templates/vendors/claude/.claude/skills/ops-maintenance/SKILL.md.template +254 -0
- package/templates/vendors/claude/.claude/skills/orchestration/SKILL.md.template +189 -0
- package/templates/vendors/claude/.claude/skills/tdd-workflow/SKILL.md.template +139 -0
- package/templates/vendors/claude/.claude/skills/worktree-discipline/SKILL.md.template +138 -0
- package/templates/vendors/claude/.claude/skills/wu-lifecycle/SKILL.md.template +106 -0
- package/templates/vendors/cline/.clinerules.template +53 -0
- package/templates/vendors/cursor/.cursor/rules/lumenflow.md.template +34 -0
- package/templates/vendors/cursor/.cursor/rules.md.template +28 -0
- package/templates/vendors/windsurf/.windsurf/rules/lumenflow.md.template +34 -0
|
@@ -0,0 +1,382 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/* eslint-disable no-console */
|
|
3
|
+
/**
|
|
4
|
+
* lane:suggest CLI Command (WU-1189, WU-1190)
|
|
5
|
+
*
|
|
6
|
+
* LLM-driven lane generation based on codebase context.
|
|
7
|
+
* Analyzes project structure and uses LLM to suggest appropriate lane definitions.
|
|
8
|
+
*
|
|
9
|
+
* Usage:
|
|
10
|
+
* pnpm lane:suggest # Generate suggestions
|
|
11
|
+
* pnpm lane:suggest --dry-run # Preview without LLM call
|
|
12
|
+
* pnpm lane:suggest --interactive # Accept/skip/edit each suggestion
|
|
13
|
+
* pnpm lane:suggest --output lanes.yaml # Write to file
|
|
14
|
+
* pnpm lane:suggest --include-git # Add git history context (WU-1190)
|
|
15
|
+
*/
|
|
16
|
+
import { existsSync, writeFileSync, mkdirSync } from 'node:fs';
|
|
17
|
+
import * as readline from 'node:readline';
|
|
18
|
+
import path from 'node:path';
|
|
19
|
+
import YAML from 'yaml';
|
|
20
|
+
import chalk from 'chalk';
|
|
21
|
+
import { findProjectRoot, createWUParser } from '@lumenflow/core';
|
|
22
|
+
import { gatherProjectContext, generateSystemPrompt, generateUserPrompt, getDefaultSuggestions, isValidLaneFormat, } from '@lumenflow/core/dist/lane-suggest-prompt.js';
|
|
23
|
+
import { extractGitContext, summarizeGitContext, } from '@lumenflow/core/dist/git-context-extractor.js';
|
|
24
|
+
import { runCLI } from './cli-entry-point.js';
|
|
25
|
+
/**
|
|
26
|
+
* CLI option definitions
|
|
27
|
+
*/
|
|
28
|
+
const LANE_SUGGEST_OPTIONS = {
|
|
29
|
+
dryRun: {
|
|
30
|
+
name: 'dryRun',
|
|
31
|
+
flags: '--dry-run',
|
|
32
|
+
description: 'Show what would be suggested without making LLM call',
|
|
33
|
+
},
|
|
34
|
+
interactive: {
|
|
35
|
+
name: 'interactive',
|
|
36
|
+
flags: '--interactive, -i',
|
|
37
|
+
description: 'Interactively accept/skip/edit each suggestion',
|
|
38
|
+
},
|
|
39
|
+
output: {
|
|
40
|
+
name: 'output',
|
|
41
|
+
flags: '--output, -o <file>',
|
|
42
|
+
description: 'Write suggestions to YAML file',
|
|
43
|
+
},
|
|
44
|
+
json: {
|
|
45
|
+
name: 'json',
|
|
46
|
+
flags: '--json',
|
|
47
|
+
description: 'Output suggestions as JSON',
|
|
48
|
+
},
|
|
49
|
+
noLlm: {
|
|
50
|
+
name: 'noLlm',
|
|
51
|
+
flags: '--no-llm',
|
|
52
|
+
description: 'Use heuristic-based suggestions (no LLM)',
|
|
53
|
+
},
|
|
54
|
+
includeGit: {
|
|
55
|
+
name: 'includeGit',
|
|
56
|
+
flags: '--include-git',
|
|
57
|
+
description: 'Add git history context (co-occurrence, ownership, churn) to LLM prompt',
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
/**
|
|
61
|
+
* Parse CLI options
|
|
62
|
+
*/
|
|
63
|
+
function parseOptions() {
|
|
64
|
+
const opts = createWUParser({
|
|
65
|
+
name: 'lane-suggest',
|
|
66
|
+
description: 'Suggest lane definitions based on codebase context',
|
|
67
|
+
options: Object.values(LANE_SUGGEST_OPTIONS),
|
|
68
|
+
});
|
|
69
|
+
return {
|
|
70
|
+
dryRun: opts.dryRun ?? false,
|
|
71
|
+
interactive: opts.interactive ?? false,
|
|
72
|
+
output: opts.output,
|
|
73
|
+
json: opts.json ?? false,
|
|
74
|
+
noLlm: opts.noLlm ?? false,
|
|
75
|
+
includeGit: opts.includeGit ?? false,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Format a lane suggestion for terminal output
|
|
80
|
+
*/
|
|
81
|
+
function formatSuggestion(suggestion, index) {
|
|
82
|
+
const lines = [];
|
|
83
|
+
lines.push(chalk.bold.cyan(`\n[${index + 1}] ${suggestion.lane}`));
|
|
84
|
+
lines.push(chalk.gray(` Description: ${suggestion.description}`));
|
|
85
|
+
lines.push(chalk.gray(` Rationale: ${suggestion.rationale}`));
|
|
86
|
+
lines.push(chalk.gray(` Code Paths:`));
|
|
87
|
+
for (const cp of suggestion.code_paths) {
|
|
88
|
+
lines.push(chalk.gray(` - ${cp}`));
|
|
89
|
+
}
|
|
90
|
+
lines.push(chalk.gray(` Keywords: ${suggestion.keywords.join(', ')}`));
|
|
91
|
+
return lines.join('\n');
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Interactive mode: prompt user for each suggestion
|
|
95
|
+
*/
|
|
96
|
+
async function interactiveMode(suggestions) {
|
|
97
|
+
const accepted = [];
|
|
98
|
+
const rl = readline.createInterface({
|
|
99
|
+
input: process.stdin,
|
|
100
|
+
output: process.stdout,
|
|
101
|
+
});
|
|
102
|
+
const question = (prompt) => {
|
|
103
|
+
return new Promise((resolve) => {
|
|
104
|
+
rl.question(prompt, resolve);
|
|
105
|
+
});
|
|
106
|
+
};
|
|
107
|
+
console.log(chalk.bold('\nInteractive Mode'));
|
|
108
|
+
console.log(chalk.gray('For each suggestion, choose: (a)ccept, (s)kip, (e)dit, (q)uit\n'));
|
|
109
|
+
for (let i = 0; i < suggestions.length; i++) {
|
|
110
|
+
const suggestion = suggestions[i];
|
|
111
|
+
console.log(formatSuggestion(suggestion, i));
|
|
112
|
+
const answer = await question(chalk.yellow('\n Action [a/s/e/q]: '));
|
|
113
|
+
switch (answer.toLowerCase().trim()) {
|
|
114
|
+
case 'a':
|
|
115
|
+
case 'accept':
|
|
116
|
+
case '': // Default to accept
|
|
117
|
+
accepted.push(suggestion);
|
|
118
|
+
console.log(chalk.green(' Accepted'));
|
|
119
|
+
break;
|
|
120
|
+
case 's':
|
|
121
|
+
case 'skip':
|
|
122
|
+
console.log(chalk.gray(' Skipped'));
|
|
123
|
+
break;
|
|
124
|
+
case 'e':
|
|
125
|
+
case 'edit': {
|
|
126
|
+
const newLane = await question(chalk.yellow(` New lane name [${suggestion.lane}]: `));
|
|
127
|
+
const newDesc = await question(chalk.yellow(` New description [${suggestion.description}]: `));
|
|
128
|
+
const edited = {
|
|
129
|
+
...suggestion,
|
|
130
|
+
lane: newLane.trim() || suggestion.lane,
|
|
131
|
+
description: newDesc.trim() || suggestion.description,
|
|
132
|
+
};
|
|
133
|
+
// Validate lane format
|
|
134
|
+
if (!isValidLaneFormat(edited.lane)) {
|
|
135
|
+
console.log(chalk.red(' Invalid lane format. Expected "Parent: Sublane"'));
|
|
136
|
+
console.log(chalk.gray(' Using original lane name'));
|
|
137
|
+
edited.lane = suggestion.lane;
|
|
138
|
+
}
|
|
139
|
+
accepted.push(edited);
|
|
140
|
+
console.log(chalk.green(' Edited and accepted'));
|
|
141
|
+
break;
|
|
142
|
+
}
|
|
143
|
+
case 'q':
|
|
144
|
+
case 'quit':
|
|
145
|
+
console.log(chalk.gray(' Quitting interactive mode'));
|
|
146
|
+
rl.close();
|
|
147
|
+
return accepted;
|
|
148
|
+
default:
|
|
149
|
+
console.log(chalk.gray(' Unknown action, skipping'));
|
|
150
|
+
break;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
rl.close();
|
|
154
|
+
return accepted;
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Generate lane inference YAML from suggestions
|
|
158
|
+
*/
|
|
159
|
+
function generateLaneInferenceYAML(suggestions) {
|
|
160
|
+
// Group suggestions by parent lane
|
|
161
|
+
const grouped = {};
|
|
162
|
+
for (const suggestion of suggestions) {
|
|
163
|
+
const [parent, sublane] = suggestion.lane.split(': ');
|
|
164
|
+
if (!parent || !sublane)
|
|
165
|
+
continue;
|
|
166
|
+
if (!grouped[parent]) {
|
|
167
|
+
grouped[parent] = {};
|
|
168
|
+
}
|
|
169
|
+
grouped[parent][sublane] = {
|
|
170
|
+
description: suggestion.description,
|
|
171
|
+
code_paths: suggestion.code_paths,
|
|
172
|
+
keywords: suggestion.keywords,
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
const header = `# Lane Inference Configuration
|
|
176
|
+
# Generated by: pnpm lane:suggest
|
|
177
|
+
# Customize this file to match your project structure
|
|
178
|
+
|
|
179
|
+
`;
|
|
180
|
+
return header + YAML.stringify(grouped);
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Display dry-run information
|
|
184
|
+
*/
|
|
185
|
+
function displayDryRun(context, systemPrompt, userPrompt, gitContext, gitSummary) {
|
|
186
|
+
console.log(chalk.bold.cyan('\n=== DRY RUN MODE ===\n'));
|
|
187
|
+
console.log(chalk.gray('This shows what would be sent to the LLM.\n'));
|
|
188
|
+
console.log(chalk.bold('Project Context:'));
|
|
189
|
+
console.log(chalk.gray(` - Monorepo: ${context.isMonorepo}`));
|
|
190
|
+
console.log(chalk.gray(` - Packages: ${context.packageNames.length}`));
|
|
191
|
+
console.log(chalk.gray(` - Has docs/: ${context.hasDocsDir}`));
|
|
192
|
+
console.log(chalk.gray(` - Has apps/: ${context.hasAppsDir}`));
|
|
193
|
+
console.log(chalk.gray(` - Existing lanes: ${context.existingLanes.length}`));
|
|
194
|
+
if (context.packageNames.length > 0) {
|
|
195
|
+
console.log(chalk.bold('\nPackages Found:'));
|
|
196
|
+
for (const pkg of context.packageNames) {
|
|
197
|
+
console.log(chalk.gray(` - ${pkg}`));
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
if (context.directoryStructure.length > 0) {
|
|
201
|
+
console.log(chalk.bold('\nDirectory Structure:'));
|
|
202
|
+
for (const dir of context.directoryStructure) {
|
|
203
|
+
console.log(chalk.gray(` - ${dir}/`));
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
// Display git context if available (WU-1190)
|
|
207
|
+
if (gitContext && gitSummary) {
|
|
208
|
+
console.log(chalk.bold('\nGit History Context (--include-git):'));
|
|
209
|
+
if (gitContext.hasLimitedHistory) {
|
|
210
|
+
console.log(chalk.yellow(` Limited history: ${gitContext.error ?? 'sparse commit history'}`));
|
|
211
|
+
}
|
|
212
|
+
else {
|
|
213
|
+
console.log(chalk.gray(` - Co-occurrences: ${gitContext.coOccurrences.length} pairs found`));
|
|
214
|
+
console.log(chalk.gray(` - Ownership signals: ${gitContext.ownership.length} paths analyzed`));
|
|
215
|
+
console.log(chalk.gray(` - Churn hotspots: ${gitContext.churn.length} files identified`));
|
|
216
|
+
}
|
|
217
|
+
console.log(chalk.bold('\nGit Summary (for LLM):'));
|
|
218
|
+
console.log(chalk.gray(gitSummary.slice(0, 500) + (gitSummary.length > 500 ? '...' : '')));
|
|
219
|
+
}
|
|
220
|
+
console.log(chalk.bold('\nSystem Prompt Preview:'));
|
|
221
|
+
console.log(chalk.gray(systemPrompt.slice(0, 500) + '...'));
|
|
222
|
+
console.log(chalk.bold('\nUser Prompt Preview:'));
|
|
223
|
+
console.log(chalk.gray(userPrompt.slice(0, 1000) + '...'));
|
|
224
|
+
console.log(chalk.bold('\nDefault Suggestions (without LLM):'));
|
|
225
|
+
const defaults = getDefaultSuggestions(context);
|
|
226
|
+
for (let i = 0; i < defaults.length; i++) {
|
|
227
|
+
console.log(formatSuggestion(defaults[i], i));
|
|
228
|
+
}
|
|
229
|
+
console.log(chalk.cyan('\n=== END DRY RUN ==='));
|
|
230
|
+
console.log(chalk.gray('\nTo generate actual suggestions, run without --dry-run'));
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* Get suggestions using heuristics (LLM deferred per WU-1189)
|
|
234
|
+
*/
|
|
235
|
+
function getSuggestions(context, noLlm) {
|
|
236
|
+
if (noLlm) {
|
|
237
|
+
console.log(chalk.gray(' Using heuristic-based suggestions (--no-llm)'));
|
|
238
|
+
}
|
|
239
|
+
else {
|
|
240
|
+
console.log(chalk.yellow(' Note: LLM integration requires API configuration'));
|
|
241
|
+
console.log(chalk.gray(' Using heuristic-based suggestions as fallback'));
|
|
242
|
+
console.log(chalk.gray(' Set OPENAI_API_KEY or use --no-llm for explicit heuristics\n'));
|
|
243
|
+
}
|
|
244
|
+
// NOTE: LLM call implementation deferred per WU-1189 scope
|
|
245
|
+
return getDefaultSuggestions(context);
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* Output suggestions as JSON
|
|
249
|
+
*/
|
|
250
|
+
function outputAsJson(suggestions, context) {
|
|
251
|
+
const result = {
|
|
252
|
+
suggestions,
|
|
253
|
+
context: {
|
|
254
|
+
packageCount: context.packageNames.length,
|
|
255
|
+
docsFound: context.hasDocsDir,
|
|
256
|
+
existingConfig: context.existingLanes.length > 0,
|
|
257
|
+
},
|
|
258
|
+
};
|
|
259
|
+
console.log(chalk.bold('\nJSON Output:'));
|
|
260
|
+
console.log(JSON.stringify(result, null, 2));
|
|
261
|
+
}
|
|
262
|
+
/**
|
|
263
|
+
* Write suggestions to YAML file
|
|
264
|
+
*/
|
|
265
|
+
function writeToFile(suggestions, outputFile, projectRoot) {
|
|
266
|
+
const outputPath = path.isAbsolute(outputFile) ? outputFile : path.join(projectRoot, outputFile);
|
|
267
|
+
const yamlContent = generateLaneInferenceYAML(suggestions);
|
|
268
|
+
const outputDir = path.dirname(outputPath);
|
|
269
|
+
if (!existsSync(outputDir)) {
|
|
270
|
+
mkdirSync(outputDir, { recursive: true });
|
|
271
|
+
}
|
|
272
|
+
writeFileSync(outputPath, yamlContent);
|
|
273
|
+
console.log(chalk.green(`\nLane configuration written to: ${outputPath}`));
|
|
274
|
+
}
|
|
275
|
+
/**
|
|
276
|
+
* Display suggestions in terminal
|
|
277
|
+
*/
|
|
278
|
+
function displaySuggestions(suggestions) {
|
|
279
|
+
suggestions.forEach((s, i) => console.log(formatSuggestion(s, i)));
|
|
280
|
+
}
|
|
281
|
+
/**
|
|
282
|
+
* Show save instructions
|
|
283
|
+
*/
|
|
284
|
+
function showSaveInstructions() {
|
|
285
|
+
console.log(chalk.bold('\nTo save these suggestions:'));
|
|
286
|
+
console.log(chalk.gray(' pnpm lane:suggest --output .lumenflow.lane-inference.yaml'));
|
|
287
|
+
console.log(chalk.gray(' pnpm lane:suggest --interactive --output lanes.yaml'));
|
|
288
|
+
console.log(chalk.gray(' pnpm lane:suggest --json > lanes.json'));
|
|
289
|
+
}
|
|
290
|
+
/**
|
|
291
|
+
* Generate user prompt with optional git context (WU-1190)
|
|
292
|
+
*/
|
|
293
|
+
function generateEnrichedUserPrompt(context, gitSummary) {
|
|
294
|
+
let prompt = generateUserPrompt(context);
|
|
295
|
+
if (gitSummary) {
|
|
296
|
+
// Insert git context before the instructions section
|
|
297
|
+
const instructionsMarker = '## Instructions';
|
|
298
|
+
const insertionPoint = prompt.indexOf(instructionsMarker);
|
|
299
|
+
if (insertionPoint !== -1) {
|
|
300
|
+
const gitSection = `## Git History Analysis
|
|
301
|
+
|
|
302
|
+
The following insights were extracted from the repository's git history.
|
|
303
|
+
Use these to understand which files are often changed together (suggesting shared ownership),
|
|
304
|
+
who the primary contributors are for different areas, and which files have high churn (potential complexity).
|
|
305
|
+
|
|
306
|
+
${gitSummary}
|
|
307
|
+
|
|
308
|
+
`;
|
|
309
|
+
prompt = prompt.slice(0, insertionPoint) + gitSection + prompt.slice(insertionPoint);
|
|
310
|
+
}
|
|
311
|
+
else {
|
|
312
|
+
// Fallback: append at the end
|
|
313
|
+
prompt += `\n\n## Git History Analysis\n\n${gitSummary}`;
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
return prompt;
|
|
317
|
+
}
|
|
318
|
+
/**
|
|
319
|
+
* Main entry point
|
|
320
|
+
*/
|
|
321
|
+
async function main() {
|
|
322
|
+
const opts = parseOptions();
|
|
323
|
+
const projectRoot = findProjectRoot();
|
|
324
|
+
console.log(chalk.bold('[lane:suggest] Analyzing project structure...'));
|
|
325
|
+
console.log(chalk.gray(` Project root: ${projectRoot}`));
|
|
326
|
+
const context = gatherProjectContext(projectRoot);
|
|
327
|
+
// Extract git context if requested (WU-1190)
|
|
328
|
+
let gitContext;
|
|
329
|
+
let gitSummary;
|
|
330
|
+
if (opts.includeGit) {
|
|
331
|
+
console.log(chalk.gray(' Extracting git history context...'));
|
|
332
|
+
gitContext = extractGitContext(projectRoot);
|
|
333
|
+
if (gitContext.hasLimitedHistory) {
|
|
334
|
+
console.log(chalk.yellow(` Git history limited: ${gitContext.error ?? 'sparse history'}`));
|
|
335
|
+
}
|
|
336
|
+
else {
|
|
337
|
+
console.log(chalk.gray(` - ${gitContext.coOccurrences.length} co-occurrence pairs`));
|
|
338
|
+
console.log(chalk.gray(` - ${gitContext.ownership.length} ownership signals`));
|
|
339
|
+
console.log(chalk.gray(` - ${gitContext.churn.length} churn hotspots`));
|
|
340
|
+
}
|
|
341
|
+
// Summarize for LLM prompt (respecting token limits)
|
|
342
|
+
gitSummary = summarizeGitContext(gitContext, { maxTokens: 500 });
|
|
343
|
+
}
|
|
344
|
+
if (opts.dryRun) {
|
|
345
|
+
const userPrompt = generateEnrichedUserPrompt(context, gitSummary);
|
|
346
|
+
displayDryRun(context, generateSystemPrompt(), userPrompt, gitContext, gitSummary);
|
|
347
|
+
return;
|
|
348
|
+
}
|
|
349
|
+
let suggestions = getSuggestions(context, opts.noLlm ?? false);
|
|
350
|
+
if (suggestions.length === 0) {
|
|
351
|
+
console.log(chalk.yellow('\nNo lane suggestions generated.'));
|
|
352
|
+
console.log(chalk.gray('Try providing more project structure (packages/, docs/, etc.)'));
|
|
353
|
+
return;
|
|
354
|
+
}
|
|
355
|
+
console.log(chalk.bold(`\nGenerated ${suggestions.length} lane suggestion(s):`));
|
|
356
|
+
if (opts.interactive) {
|
|
357
|
+
suggestions = await interactiveMode(suggestions);
|
|
358
|
+
if (suggestions.length === 0) {
|
|
359
|
+
console.log(chalk.yellow('\nNo suggestions accepted.'));
|
|
360
|
+
return;
|
|
361
|
+
}
|
|
362
|
+
console.log(chalk.bold(`\nAccepted ${suggestions.length} suggestion(s)`));
|
|
363
|
+
}
|
|
364
|
+
else {
|
|
365
|
+
displaySuggestions(suggestions);
|
|
366
|
+
}
|
|
367
|
+
if (opts.json) {
|
|
368
|
+
outputAsJson(suggestions, context);
|
|
369
|
+
return;
|
|
370
|
+
}
|
|
371
|
+
if (opts.output) {
|
|
372
|
+
writeToFile(suggestions, opts.output, projectRoot);
|
|
373
|
+
return;
|
|
374
|
+
}
|
|
375
|
+
showSaveInstructions();
|
|
376
|
+
}
|
|
377
|
+
// Entry point
|
|
378
|
+
if (import.meta.main) {
|
|
379
|
+
void runCLI(main);
|
|
380
|
+
}
|
|
381
|
+
// Export for testing
|
|
382
|
+
export { main, parseOptions, formatSuggestion, interactiveMode, generateLaneInferenceYAML };
|
package/dist/mem-checkpoint.js
CHANGED
|
@@ -10,8 +10,8 @@
|
|
|
10
10
|
* Usage:
|
|
11
11
|
* pnpm mem:checkpoint 'note' [--session <id>] [--wu <id>] [--progress <text>] [--next-steps <text>] [--trigger <type>] [--quiet]
|
|
12
12
|
*
|
|
13
|
-
* @see {@link
|
|
14
|
-
* @see {@link
|
|
13
|
+
* @see {@link packages/@lumenflow/cli/src/lib/mem-checkpoint-core.ts} - Core logic
|
|
14
|
+
* @see {@link packages/@lumenflow/cli/src/__tests__/mem-checkpoint.test.ts} - Tests
|
|
15
15
|
*/
|
|
16
16
|
import fs from 'node:fs/promises';
|
|
17
17
|
import path from 'node:path';
|
package/dist/mem-cleanup.js
CHANGED
|
@@ -23,8 +23,8 @@
|
|
|
23
23
|
* pnpm mem:cleanup --session-id <uuid> # Close specific session
|
|
24
24
|
* pnpm mem:cleanup --json # Output as JSON
|
|
25
25
|
*
|
|
26
|
-
* @see {@link
|
|
27
|
-
* @see {@link
|
|
26
|
+
* @see {@link packages/@lumenflow/cli/src/lib/mem-cleanup-core.ts} - Core logic
|
|
27
|
+
* @see {@link packages/@lumenflow/cli/src/__tests__/mem-cleanup.test.ts} - Tests
|
|
28
28
|
*/
|
|
29
29
|
import fs from 'node:fs/promises';
|
|
30
30
|
import path from 'node:path';
|