wogiflow 1.0.11 → 1.0.13
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/.workflow/specs/architecture.md.template +24 -0
- package/.workflow/specs/stack.md.template +33 -0
- package/.workflow/specs/testing.md.template +36 -0
- package/README.md +90 -1
- package/lib/unified-wizard.js +569 -30
- package/package.json +1 -1
- package/scripts/MEMORY-ARCHITECTURE.md +150 -0
- package/scripts/flow +20 -19
- package/scripts/flow-auto-context.js +97 -3
- package/scripts/flow-conflict-resolver.js +735 -0
- package/scripts/flow-context-gatherer.js +520 -0
- package/scripts/flow-context-monitor.js +148 -19
- package/scripts/flow-damage-control.js +5 -1
- package/scripts/flow-export-profile +168 -1
- package/scripts/flow-import-profile +257 -6
- package/scripts/flow-instruction-richness.js +182 -18
- package/scripts/flow-knowledge-router.js +2 -0
- package/scripts/flow-knowledge-sync.js +2 -0
- package/scripts/{flow-transcript-chunking.js → flow-long-input-chunking.js} +4 -2
- package/scripts/{flow-transcript-parsing.js → flow-long-input-parsing.js} +35 -0
- package/scripts/{flow-transcript-stories.js → flow-long-input-stories.js} +86 -38
- package/scripts/{flow-transcript-digest.js → flow-long-input.js} +231 -15
- package/scripts/flow-memory-db.js +386 -1
- package/scripts/flow-memory-sync.js +2 -0
- package/scripts/flow-model-adapter.js +53 -29
- package/scripts/flow-model-router.js +246 -1
- package/scripts/flow-morning.js +94 -0
- package/scripts/flow-onboard +223 -10
- package/scripts/flow-orchestrate-validation.js +539 -0
- package/scripts/flow-orchestrate.js +16 -507
- package/scripts/flow-pattern-extractor.js +1265 -0
- package/scripts/flow-prompt-composer.js +222 -2
- package/scripts/flow-quality-guard.js +594 -0
- package/scripts/flow-section-index.js +713 -0
- package/scripts/flow-section-resolver.js +484 -0
- package/scripts/flow-session-end.js +188 -2
- package/scripts/flow-skill-create.js +19 -3
- package/scripts/flow-skill-matcher.js +122 -7
- package/scripts/flow-statusline-setup.js +218 -0
- package/scripts/flow-step-review.js +19 -0
- package/scripts/flow-tech-debt.js +734 -0
- package/scripts/flow-utils.js +2 -0
- package/scripts/hooks/core/long-input-gate.js +293 -0
- package/scripts/flow-parallel-detector.js +0 -399
- package/scripts/flow-parallel-dispatch.js +0 -987
- /package/scripts/{flow-transcript-language.js → flow-long-input-language.js} +0 -0
|
@@ -5,6 +5,10 @@
|
|
|
5
5
|
*
|
|
6
6
|
* Creates new skills from templates with interactive prompts.
|
|
7
7
|
*
|
|
8
|
+
* TODO: Consider consolidating with flow-skill-creator.js and
|
|
9
|
+
* flow-skill-generator.js into a single flow-skill.js with subcommands.
|
|
10
|
+
* See audit plan from 2026-01-12.
|
|
11
|
+
*
|
|
8
12
|
* Usage:
|
|
9
13
|
* flow skill-create # Interactive mode
|
|
10
14
|
* flow skill-create <name> # Create with name
|
|
@@ -76,14 +80,26 @@ function copyDirectory(src, dest, replacements = {}) {
|
|
|
76
80
|
}
|
|
77
81
|
|
|
78
82
|
async function createSkill(name, options = {}) {
|
|
79
|
-
|
|
83
|
+
// Support nested paths like "frontend/react" or "backend/nestjs"
|
|
84
|
+
const pathParts = name.split('/').filter(Boolean);
|
|
85
|
+
const skillPath = path.join(SKILLS_DIR, ...pathParts);
|
|
86
|
+
const displayName = pathParts[pathParts.length - 1]; // Base name for display
|
|
80
87
|
|
|
81
88
|
if (fs.existsSync(skillPath)) {
|
|
82
89
|
log('red', `Skill '${name}' already exists at ${skillPath}`);
|
|
83
90
|
return false;
|
|
84
91
|
}
|
|
85
92
|
|
|
93
|
+
// Create parent directories if nested
|
|
94
|
+
if (pathParts.length > 1) {
|
|
95
|
+
const parentDir = path.join(SKILLS_DIR, ...pathParts.slice(0, -1));
|
|
96
|
+
fs.mkdirSync(parentDir, { recursive: true });
|
|
97
|
+
}
|
|
98
|
+
|
|
86
99
|
log('cyan', `\n📦 Creating skill: ${name}\n`);
|
|
100
|
+
if (pathParts.length > 1) {
|
|
101
|
+
log('dim', ` (nested skill at ${skillPath})\n`);
|
|
102
|
+
}
|
|
87
103
|
|
|
88
104
|
// Gather information
|
|
89
105
|
const description = options.description || await prompt('Short description');
|
|
@@ -323,7 +339,7 @@ async function main() {
|
|
|
323
339
|
await createSkill(name);
|
|
324
340
|
}
|
|
325
341
|
|
|
326
|
-
main().catch(
|
|
327
|
-
log('red', `Error: ${
|
|
342
|
+
main().catch(err => {
|
|
343
|
+
log('red', `Error: ${err.message}`);
|
|
328
344
|
process.exit(1);
|
|
329
345
|
});
|
|
@@ -23,6 +23,70 @@ const { getProjectRoot, getConfig, PATHS, colors } = require('./flow-utils');
|
|
|
23
23
|
const PROJECT_ROOT = getProjectRoot();
|
|
24
24
|
const SKILLS_DIR = path.join(PROJECT_ROOT, '.claude', 'skills');
|
|
25
25
|
|
|
26
|
+
// Maximum nesting depth for skill directories (prevents runaway recursion)
|
|
27
|
+
const MAX_SKILL_NESTING_DEPTH = 3;
|
|
28
|
+
|
|
29
|
+
// ============================================================
|
|
30
|
+
// Nested Skill Discovery
|
|
31
|
+
// ============================================================
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Recursively discover all skills in the skills directory
|
|
35
|
+
* Looks for directories containing skill.md files
|
|
36
|
+
*
|
|
37
|
+
* @param {string} baseDir - Base directory to search
|
|
38
|
+
* @param {string} prefix - Path prefix for nested skills
|
|
39
|
+
* @param {number} depth - Current recursion depth
|
|
40
|
+
* @returns {string[]} Array of skill paths (e.g., ["nestjs", "frontend/react"])
|
|
41
|
+
*/
|
|
42
|
+
function discoverNestedSkills(baseDir = SKILLS_DIR, prefix = '', depth = 0) {
|
|
43
|
+
if (depth > MAX_SKILL_NESTING_DEPTH) {
|
|
44
|
+
return [];
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const skills = [];
|
|
48
|
+
|
|
49
|
+
try {
|
|
50
|
+
if (!fs.existsSync(baseDir)) {
|
|
51
|
+
return [];
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const entries = fs.readdirSync(baseDir, { withFileTypes: true });
|
|
55
|
+
|
|
56
|
+
for (const entry of entries) {
|
|
57
|
+
// Skip hidden directories, _template, and non-directories
|
|
58
|
+
if (!entry.isDirectory() || entry.name.startsWith('.') || entry.name.startsWith('_')) {
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const entryPath = path.join(baseDir, entry.name);
|
|
63
|
+
const skillPath = prefix ? `${prefix}/${entry.name}` : entry.name;
|
|
64
|
+
const skillMdPath = path.join(entryPath, 'skill.md');
|
|
65
|
+
|
|
66
|
+
// If this directory has a skill.md, it's a skill
|
|
67
|
+
if (fs.existsSync(skillMdPath)) {
|
|
68
|
+
skills.push(skillPath);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Recursively check subdirectories for more skills
|
|
72
|
+
const nestedSkills = discoverNestedSkills(entryPath, skillPath, depth + 1);
|
|
73
|
+
skills.push(...nestedSkills);
|
|
74
|
+
}
|
|
75
|
+
} catch (err) {
|
|
76
|
+
// Silently ignore permission errors, etc.
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return skills;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Get the absolute path to a skill directory
|
|
84
|
+
* Handles both flat ("nestjs") and nested ("frontend/react") paths
|
|
85
|
+
*/
|
|
86
|
+
function getSkillDir(skillName) {
|
|
87
|
+
return path.join(SKILLS_DIR, ...skillName.split('/'));
|
|
88
|
+
}
|
|
89
|
+
|
|
26
90
|
// ============================================================
|
|
27
91
|
// Skill Trigger Definitions
|
|
28
92
|
// ============================================================
|
|
@@ -71,6 +135,7 @@ const DEFAULT_TRIGGERS = {
|
|
|
71
135
|
/**
|
|
72
136
|
* Load skill metadata from skill.md
|
|
73
137
|
* Parses YAML frontmatter and extracts trigger configuration
|
|
138
|
+
* Supports both flat ("nestjs") and nested ("frontend/react") skill paths
|
|
74
139
|
*/
|
|
75
140
|
function loadSkillMetadata(skillName) {
|
|
76
141
|
// Skip template skills
|
|
@@ -78,7 +143,14 @@ function loadSkillMetadata(skillName) {
|
|
|
78
143
|
return null;
|
|
79
144
|
}
|
|
80
145
|
|
|
81
|
-
|
|
146
|
+
// Handle nested paths - get the base name for template checks
|
|
147
|
+
const baseName = skillName.split('/').pop();
|
|
148
|
+
if (baseName.startsWith('_')) {
|
|
149
|
+
return null;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const skillDir = getSkillDir(skillName);
|
|
153
|
+
const skillPath = path.join(skillDir, 'skill.md');
|
|
82
154
|
|
|
83
155
|
if (!fs.existsSync(skillPath)) {
|
|
84
156
|
return null;
|
|
@@ -206,15 +278,24 @@ function parseListSection(section) {
|
|
|
206
278
|
|
|
207
279
|
/**
|
|
208
280
|
* Get all installed skills with their triggers
|
|
281
|
+
* Combines skills from config.json with auto-discovered nested skills
|
|
209
282
|
*/
|
|
210
283
|
function getAllSkills() {
|
|
211
284
|
const config = getConfig();
|
|
212
|
-
const
|
|
285
|
+
const configuredSkills = config.skills?.installed || [];
|
|
286
|
+
const autoDiscover = config.skills?.autoDiscoverNested !== false; // Default: true
|
|
213
287
|
const skills = [];
|
|
288
|
+
const seenSkills = new Set();
|
|
289
|
+
|
|
290
|
+
// First, load configured skills (these take priority)
|
|
291
|
+
for (const skillName of configuredSkills) {
|
|
292
|
+
if (seenSkills.has(skillName)) continue;
|
|
293
|
+
seenSkills.add(skillName);
|
|
214
294
|
|
|
215
|
-
for (const skillName of installedSkills) {
|
|
216
295
|
const metadata = loadSkillMetadata(skillName);
|
|
217
|
-
|
|
296
|
+
// Get default triggers - check both full path and base name
|
|
297
|
+
const baseName = skillName.split('/').pop();
|
|
298
|
+
const defaultTriggers = DEFAULT_TRIGGERS[skillName] || DEFAULT_TRIGGERS[baseName] || {
|
|
218
299
|
keywords: [],
|
|
219
300
|
filePatterns: [],
|
|
220
301
|
taskTypes: ['feature', 'bugfix', 'refactor'],
|
|
@@ -229,6 +310,35 @@ function getAllSkills() {
|
|
|
229
310
|
});
|
|
230
311
|
}
|
|
231
312
|
|
|
313
|
+
// Then, auto-discover nested skills if enabled
|
|
314
|
+
if (autoDiscover) {
|
|
315
|
+
const discoveredSkills = discoverNestedSkills();
|
|
316
|
+
|
|
317
|
+
for (const skillName of discoveredSkills) {
|
|
318
|
+
if (seenSkills.has(skillName)) continue;
|
|
319
|
+
seenSkills.add(skillName);
|
|
320
|
+
|
|
321
|
+
const metadata = loadSkillMetadata(skillName);
|
|
322
|
+
if (!metadata) continue; // Skip if can't load metadata
|
|
323
|
+
|
|
324
|
+
// Get default triggers - check both full path and base name
|
|
325
|
+
const baseName = skillName.split('/').pop();
|
|
326
|
+
const defaultTriggers = DEFAULT_TRIGGERS[skillName] || DEFAULT_TRIGGERS[baseName] || {
|
|
327
|
+
keywords: [],
|
|
328
|
+
filePatterns: [],
|
|
329
|
+
taskTypes: ['feature', 'bugfix', 'refactor'],
|
|
330
|
+
categories: []
|
|
331
|
+
};
|
|
332
|
+
|
|
333
|
+
skills.push({
|
|
334
|
+
name: skillName,
|
|
335
|
+
metadata: metadata || {},
|
|
336
|
+
triggers: metadata?.triggers || defaultTriggers,
|
|
337
|
+
filePatterns: metadata?.filePatterns || defaultTriggers.filePatterns
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
|
|
232
342
|
return skills;
|
|
233
343
|
}
|
|
234
344
|
|
|
@@ -323,11 +433,12 @@ function matchSkills(taskDescription, options = {}) {
|
|
|
323
433
|
|
|
324
434
|
/**
|
|
325
435
|
* Convert glob pattern to regex
|
|
436
|
+
* Uses [^/]* instead of .* to prevent matching directory separators (security)
|
|
326
437
|
*/
|
|
327
438
|
function patternToRegex(pattern) {
|
|
328
439
|
const escaped = pattern
|
|
329
440
|
.replace(/[.+^${}()|[\]\\]/g, '\\$&')
|
|
330
|
-
.replace(/\*/g, '
|
|
441
|
+
.replace(/\*/g, '[^/]*') // Security: don't match path separators
|
|
331
442
|
.replace(/\?/g, '.');
|
|
332
443
|
return new RegExp(escaped, 'i');
|
|
333
444
|
}
|
|
@@ -360,7 +471,8 @@ async function loadSkillContext(matchedSkills, options = {}) {
|
|
|
360
471
|
};
|
|
361
472
|
|
|
362
473
|
for (const skill of skillsToLoad) {
|
|
363
|
-
|
|
474
|
+
// Use getSkillDir to handle nested paths
|
|
475
|
+
const skillDir = getSkillDir(skill.name);
|
|
364
476
|
const skillContext = {
|
|
365
477
|
name: skill.name,
|
|
366
478
|
score: skill.score,
|
|
@@ -567,7 +679,10 @@ module.exports = {
|
|
|
567
679
|
loadSkillContext,
|
|
568
680
|
formatSkillContext,
|
|
569
681
|
getSkillSummary,
|
|
570
|
-
|
|
682
|
+
discoverNestedSkills,
|
|
683
|
+
getSkillDir,
|
|
684
|
+
DEFAULT_TRIGGERS,
|
|
685
|
+
MAX_SKILL_NESTING_DEPTH
|
|
571
686
|
};
|
|
572
687
|
|
|
573
688
|
if (require.main === module) {
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Wogi Flow - Status Line Setup
|
|
5
|
+
*
|
|
6
|
+
* Configures Claude Code's status line to show WogiFlow task information.
|
|
7
|
+
* Uses the new context_window.used_percentage field from Claude Code v1.0.52+.
|
|
8
|
+
*
|
|
9
|
+
* Usage:
|
|
10
|
+
* flow statusline-setup # Interactive setup
|
|
11
|
+
* flow statusline-setup --format compact
|
|
12
|
+
* flow statusline-setup --format detailed
|
|
13
|
+
* flow statusline-setup --show # Show current config
|
|
14
|
+
* flow statusline-setup --disable # Disable status line
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
const fs = require('fs');
|
|
18
|
+
const path = require('path');
|
|
19
|
+
const os = require('os');
|
|
20
|
+
const readline = require('readline');
|
|
21
|
+
const { colors, printHeader, safeJsonParse } = require('./flow-utils');
|
|
22
|
+
|
|
23
|
+
// Status line format presets
|
|
24
|
+
const FORMATS = {
|
|
25
|
+
minimal: {
|
|
26
|
+
name: 'Minimal',
|
|
27
|
+
description: 'Just model and context percentage',
|
|
28
|
+
format: '{{model}} | {{context_window.used_percentage}}%'
|
|
29
|
+
},
|
|
30
|
+
compact: {
|
|
31
|
+
name: 'Compact',
|
|
32
|
+
description: 'Task ID + model + context',
|
|
33
|
+
format: '{{#if task}}[{{task.id}}] {{/if}}{{model}} | {{context_window.used_percentage}}%'
|
|
34
|
+
},
|
|
35
|
+
standard: {
|
|
36
|
+
name: 'Standard (Recommended)',
|
|
37
|
+
description: 'Task + model + labeled context',
|
|
38
|
+
format: '{{#if task}}[{{task.id}}] {{/if}}{{model}} | Ctx: {{context_window.used_percentage}}%'
|
|
39
|
+
},
|
|
40
|
+
detailed: {
|
|
41
|
+
name: 'Detailed',
|
|
42
|
+
description: 'Full info including skill',
|
|
43
|
+
format: '{{#if task}}[{{task.id}}] {{task.title}} | {{/if}}{{model}} | {{context_window.used_percentage}}% used{{#if skill}} | {{skill}}{{/if}}'
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
// Claude settings file location
|
|
48
|
+
const CLAUDE_SETTINGS_PATH = path.join(os.homedir(), '.claude', 'settings.json');
|
|
49
|
+
|
|
50
|
+
function loadClaudeSettings() {
|
|
51
|
+
if (!fs.existsSync(CLAUDE_SETTINGS_PATH)) {
|
|
52
|
+
return {};
|
|
53
|
+
}
|
|
54
|
+
// Use safeJsonParse for prototype pollution protection
|
|
55
|
+
return safeJsonParse(CLAUDE_SETTINGS_PATH, {});
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function saveClaudeSettings(settings) {
|
|
59
|
+
try {
|
|
60
|
+
// Ensure directory exists
|
|
61
|
+
const dir = path.dirname(CLAUDE_SETTINGS_PATH);
|
|
62
|
+
if (!fs.existsSync(dir)) {
|
|
63
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
64
|
+
}
|
|
65
|
+
fs.writeFileSync(CLAUDE_SETTINGS_PATH, JSON.stringify(settings, null, 2));
|
|
66
|
+
return true;
|
|
67
|
+
} catch (err) {
|
|
68
|
+
console.error(`${colors.red}Error: Could not save Claude settings: ${err.message}${colors.reset}`);
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function showCurrentConfig() {
|
|
74
|
+
const settings = loadClaudeSettings();
|
|
75
|
+
const statusLine = settings.statusLine || {};
|
|
76
|
+
|
|
77
|
+
printHeader('Current Status Line Configuration');
|
|
78
|
+
|
|
79
|
+
if (!statusLine.enabled && statusLine.enabled !== undefined) {
|
|
80
|
+
console.log(`${colors.dim}Status: ${colors.yellow}Disabled${colors.reset}`);
|
|
81
|
+
} else if (statusLine.format) {
|
|
82
|
+
console.log(`${colors.dim}Status: ${colors.green}Enabled${colors.reset}`);
|
|
83
|
+
console.log(`${colors.dim}Format:${colors.reset} ${statusLine.format}`);
|
|
84
|
+
} else {
|
|
85
|
+
console.log(`${colors.dim}Status: ${colors.yellow}Not configured${colors.reset}`);
|
|
86
|
+
}
|
|
87
|
+
console.log('');
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function showFormats() {
|
|
91
|
+
console.log(`${colors.bold}Available Formats:${colors.reset}\n`);
|
|
92
|
+
|
|
93
|
+
for (const [key, preset] of Object.entries(FORMATS)) {
|
|
94
|
+
console.log(` ${colors.cyan}${key}${colors.reset} - ${preset.name}`);
|
|
95
|
+
console.log(` ${colors.dim}${preset.description}${colors.reset}`);
|
|
96
|
+
console.log(` ${colors.dim}Preview: ${preset.format}${colors.reset}`);
|
|
97
|
+
console.log('');
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
async function interactiveSetup() {
|
|
102
|
+
const rl = readline.createInterface({
|
|
103
|
+
input: process.stdin,
|
|
104
|
+
output: process.stdout
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
const question = (q) => new Promise(resolve => rl.question(q, resolve));
|
|
108
|
+
|
|
109
|
+
printHeader('Status Line Setup');
|
|
110
|
+
showCurrentConfig();
|
|
111
|
+
showFormats();
|
|
112
|
+
|
|
113
|
+
const format = await question(`\nChoose format (minimal/compact/standard/detailed) [standard]: `);
|
|
114
|
+
const selectedFormat = format.trim() || 'standard';
|
|
115
|
+
|
|
116
|
+
if (!FORMATS[selectedFormat]) {
|
|
117
|
+
console.log(`${colors.red}Invalid format: ${selectedFormat}${colors.reset}`);
|
|
118
|
+
rl.close();
|
|
119
|
+
process.exit(1);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const settings = loadClaudeSettings();
|
|
123
|
+
settings.statusLine = {
|
|
124
|
+
enabled: true,
|
|
125
|
+
format: FORMATS[selectedFormat].format
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
const confirm = await question(`\nApply "${FORMATS[selectedFormat].name}" format? (y/N): `);
|
|
129
|
+
|
|
130
|
+
if (confirm.toLowerCase() === 'y') {
|
|
131
|
+
if (saveClaudeSettings(settings)) {
|
|
132
|
+
console.log(`\n${colors.green}✓ Status line configured successfully!${colors.reset}`);
|
|
133
|
+
console.log(`${colors.dim}Restart Claude Code to see changes.${colors.reset}`);
|
|
134
|
+
}
|
|
135
|
+
} else {
|
|
136
|
+
console.log(`${colors.dim}Setup cancelled.${colors.reset}`);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
rl.close();
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
async function main() {
|
|
143
|
+
const args = process.argv.slice(2);
|
|
144
|
+
|
|
145
|
+
if (args.includes('--help') || args.includes('-h')) {
|
|
146
|
+
console.log(`
|
|
147
|
+
Wogi Flow - Status Line Setup
|
|
148
|
+
|
|
149
|
+
Configure Claude Code's status line to show task and context info.
|
|
150
|
+
|
|
151
|
+
Usage:
|
|
152
|
+
flow statusline-setup Interactive setup
|
|
153
|
+
flow statusline-setup --format X Set format directly
|
|
154
|
+
flow statusline-setup --show Show current config
|
|
155
|
+
flow statusline-setup --disable Disable status line
|
|
156
|
+
flow statusline-setup --formats List available formats
|
|
157
|
+
|
|
158
|
+
Formats:
|
|
159
|
+
minimal - Model + context %
|
|
160
|
+
compact - Task ID + model + context %
|
|
161
|
+
standard - Task ID + model + labeled context (recommended)
|
|
162
|
+
detailed - Full info including skill name
|
|
163
|
+
|
|
164
|
+
Examples:
|
|
165
|
+
flow statusline-setup --format standard
|
|
166
|
+
flow statusline-setup --format detailed
|
|
167
|
+
`);
|
|
168
|
+
process.exit(0);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
if (args.includes('--show')) {
|
|
172
|
+
showCurrentConfig();
|
|
173
|
+
process.exit(0);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
if (args.includes('--formats')) {
|
|
177
|
+
showFormats();
|
|
178
|
+
process.exit(0);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
if (args.includes('--disable')) {
|
|
182
|
+
const settings = loadClaudeSettings();
|
|
183
|
+
settings.statusLine = { enabled: false };
|
|
184
|
+
if (saveClaudeSettings(settings)) {
|
|
185
|
+
console.log(`${colors.green}✓ Status line disabled.${colors.reset}`);
|
|
186
|
+
}
|
|
187
|
+
process.exit(0);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const formatIndex = args.indexOf('--format');
|
|
191
|
+
if (formatIndex >= 0) {
|
|
192
|
+
const format = args[formatIndex + 1];
|
|
193
|
+
if (!format || !FORMATS[format]) {
|
|
194
|
+
console.log(`${colors.red}Invalid format. Use: minimal, compact, standard, or detailed${colors.reset}`);
|
|
195
|
+
process.exit(1);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
const settings = loadClaudeSettings();
|
|
199
|
+
settings.statusLine = {
|
|
200
|
+
enabled: true,
|
|
201
|
+
format: FORMATS[format].format
|
|
202
|
+
};
|
|
203
|
+
|
|
204
|
+
if (saveClaudeSettings(settings)) {
|
|
205
|
+
console.log(`${colors.green}✓ Status line configured with "${format}" format.${colors.reset}`);
|
|
206
|
+
console.log(`${colors.dim}Restart Claude Code to see changes.${colors.reset}`);
|
|
207
|
+
}
|
|
208
|
+
process.exit(0);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// Default: interactive mode
|
|
212
|
+
await interactiveSetup();
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
main().catch(err => {
|
|
216
|
+
console.error(`${colors.red}Error: ${err.message}${colors.reset}`);
|
|
217
|
+
process.exit(1);
|
|
218
|
+
});
|
|
@@ -116,6 +116,25 @@ async function run(options = {}) {
|
|
|
116
116
|
} catch (err) {
|
|
117
117
|
// Auto-learn not available or failed - continue silently
|
|
118
118
|
}
|
|
119
|
+
|
|
120
|
+
// Also capture to tech debt ledger for persistent tracking
|
|
121
|
+
try {
|
|
122
|
+
const { TechDebtManager } = require('./flow-tech-debt');
|
|
123
|
+
const debtManager = new TechDebtManager();
|
|
124
|
+
const { added, updated } = debtManager.addIssues(reportableIssues.map(issue => ({
|
|
125
|
+
file: issue.file,
|
|
126
|
+
line: issue.line,
|
|
127
|
+
category: issue.perspective || 'code',
|
|
128
|
+
severity: issue.severity === 'critical' ? 'critical' : (issue.severity === 'important' ? 'high' : 'medium'),
|
|
129
|
+
description: issue.description,
|
|
130
|
+
fix: issue.fix || ''
|
|
131
|
+
})));
|
|
132
|
+
if (added > 0 || updated > 0) {
|
|
133
|
+
console.log(colors.dim + ` Tech debt: ${added} new, ${updated} updated` + colors.reset);
|
|
134
|
+
}
|
|
135
|
+
} catch (err) {
|
|
136
|
+
// Tech debt manager not available or failed - continue silently
|
|
137
|
+
}
|
|
119
138
|
}
|
|
120
139
|
|
|
121
140
|
return {
|