deepflow 0.1.0 → 0.1.2
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/VERSION +1 -1
- package/bin/install.js +84 -41
- package/hooks/df-statusline.js +24 -26
- package/package.json +1 -1
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
0.1.
|
|
1
|
+
0.1.2
|
package/bin/install.js
CHANGED
|
@@ -19,23 +19,35 @@ const c = {
|
|
|
19
19
|
};
|
|
20
20
|
|
|
21
21
|
// Paths
|
|
22
|
-
const
|
|
22
|
+
const GLOBAL_DIR = path.join(os.homedir(), '.claude');
|
|
23
|
+
const PROJECT_DIR = path.join(process.cwd(), '.claude');
|
|
23
24
|
const PACKAGE_DIR = path.resolve(__dirname, '..');
|
|
24
25
|
|
|
25
26
|
async function main() {
|
|
26
27
|
console.log('');
|
|
27
|
-
console.log(`${c.cyan}
|
|
28
|
+
console.log(`${c.cyan}deepflow installer${c.reset}`);
|
|
29
|
+
console.log('');
|
|
30
|
+
|
|
31
|
+
// Ask for installation level
|
|
32
|
+
const level = await askInstallLevel();
|
|
33
|
+
const CLAUDE_DIR = level === 'global' ? GLOBAL_DIR : PROJECT_DIR;
|
|
34
|
+
const levelLabel = level === 'global' ? 'globally' : 'in this project';
|
|
35
|
+
|
|
36
|
+
console.log('');
|
|
37
|
+
console.log(`Installing ${levelLabel}...`);
|
|
28
38
|
console.log('');
|
|
29
39
|
|
|
30
40
|
// Create directories
|
|
31
41
|
const dirs = [
|
|
32
42
|
'commands/df',
|
|
33
43
|
'skills',
|
|
34
|
-
'agents'
|
|
35
|
-
'hooks',
|
|
36
|
-
'deepflow'
|
|
44
|
+
'agents'
|
|
37
45
|
];
|
|
38
46
|
|
|
47
|
+
if (level === 'global') {
|
|
48
|
+
dirs.push('hooks', 'deepflow');
|
|
49
|
+
}
|
|
50
|
+
|
|
39
51
|
for (const dir of dirs) {
|
|
40
52
|
fs.mkdirSync(path.join(CLAUDE_DIR, dir), { recursive: true });
|
|
41
53
|
}
|
|
@@ -61,55 +73,71 @@ async function main() {
|
|
|
61
73
|
);
|
|
62
74
|
log('Agents installed');
|
|
63
75
|
|
|
64
|
-
// Copy hooks
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
76
|
+
// Copy hooks (global only - statusline requires global settings)
|
|
77
|
+
if (level === 'global') {
|
|
78
|
+
const hooksDir = path.join(PACKAGE_DIR, 'hooks');
|
|
79
|
+
if (fs.existsSync(hooksDir)) {
|
|
80
|
+
for (const file of fs.readdirSync(hooksDir)) {
|
|
81
|
+
if (file.endsWith('.js')) {
|
|
82
|
+
fs.copyFileSync(
|
|
83
|
+
path.join(hooksDir, file),
|
|
84
|
+
path.join(CLAUDE_DIR, 'hooks', file)
|
|
85
|
+
);
|
|
86
|
+
}
|
|
73
87
|
}
|
|
88
|
+
log('Hooks installed');
|
|
74
89
|
}
|
|
75
|
-
log('Hooks installed');
|
|
76
90
|
}
|
|
77
91
|
|
|
78
|
-
// Copy VERSION
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
fs.
|
|
82
|
-
|
|
92
|
+
// Copy VERSION (global only - for update checking)
|
|
93
|
+
if (level === 'global') {
|
|
94
|
+
const versionFile = path.join(PACKAGE_DIR, 'VERSION');
|
|
95
|
+
if (fs.existsSync(versionFile)) {
|
|
96
|
+
fs.copyFileSync(versionFile, path.join(CLAUDE_DIR, 'deepflow', 'VERSION'));
|
|
97
|
+
log('Version file installed');
|
|
98
|
+
}
|
|
83
99
|
}
|
|
84
100
|
|
|
85
|
-
// Configure statusline
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
101
|
+
// Configure statusline (global only)
|
|
102
|
+
if (level === 'global') {
|
|
103
|
+
await configureStatusline(CLAUDE_DIR);
|
|
104
|
+
|
|
105
|
+
// Trigger background update check
|
|
106
|
+
const updateChecker = path.join(CLAUDE_DIR, 'hooks', 'df-check-update.js');
|
|
107
|
+
if (fs.existsSync(updateChecker)) {
|
|
108
|
+
const { spawn } = require('child_process');
|
|
109
|
+
const child = spawn(process.execPath, [updateChecker], {
|
|
110
|
+
detached: true,
|
|
111
|
+
stdio: 'ignore'
|
|
112
|
+
});
|
|
113
|
+
child.unref();
|
|
114
|
+
}
|
|
97
115
|
}
|
|
98
116
|
|
|
99
117
|
console.log('');
|
|
100
118
|
console.log(`${c.green}Installation complete!${c.reset}`);
|
|
101
119
|
console.log('');
|
|
102
|
-
console.log(`Installed to ${CLAUDE_DIR}:`);
|
|
120
|
+
console.log(`Installed to ${c.cyan}${CLAUDE_DIR}${c.reset}:`);
|
|
103
121
|
console.log(' commands/df/ — /df:spec, /df:plan, /df:execute, /df:verify');
|
|
104
122
|
console.log(' skills/ — gap-discovery, atomic-commits, code-completeness');
|
|
105
123
|
console.log(' agents/ — reasoner');
|
|
106
|
-
|
|
124
|
+
if (level === 'global') {
|
|
125
|
+
console.log(' hooks/ — statusline, update checker');
|
|
126
|
+
}
|
|
107
127
|
console.log('');
|
|
128
|
+
if (level === 'project') {
|
|
129
|
+
console.log(`${c.dim}Note: Statusline is only available with global install.${c.reset}`);
|
|
130
|
+
console.log('');
|
|
131
|
+
}
|
|
108
132
|
console.log('Quick start:');
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
133
|
+
if (level === 'global') {
|
|
134
|
+
console.log(' 1. cd your-project');
|
|
135
|
+
console.log(' 2. claude');
|
|
136
|
+
} else {
|
|
137
|
+
console.log(' 1. claude');
|
|
138
|
+
}
|
|
139
|
+
console.log(' 2. Describe what you want to build');
|
|
140
|
+
console.log(' 3. /df:spec feature-name');
|
|
113
141
|
console.log('');
|
|
114
142
|
}
|
|
115
143
|
|
|
@@ -130,9 +158,9 @@ function copyDir(src, dest) {
|
|
|
130
158
|
}
|
|
131
159
|
}
|
|
132
160
|
|
|
133
|
-
async function configureStatusline() {
|
|
134
|
-
const settingsPath = path.join(
|
|
135
|
-
const hookCmd = `node "${path.join(
|
|
161
|
+
async function configureStatusline(claudeDir) {
|
|
162
|
+
const settingsPath = path.join(claudeDir, 'settings.json');
|
|
163
|
+
const hookCmd = `node "${path.join(claudeDir, 'hooks', 'df-statusline.js')}"`;
|
|
136
164
|
|
|
137
165
|
let settings = {};
|
|
138
166
|
|
|
@@ -177,6 +205,21 @@ function ask(question) {
|
|
|
177
205
|
});
|
|
178
206
|
}
|
|
179
207
|
|
|
208
|
+
async function askInstallLevel() {
|
|
209
|
+
console.log('Where do you want to install deepflow?');
|
|
210
|
+
console.log('');
|
|
211
|
+
console.log(` ${c.cyan}1${c.reset}) Global ${c.dim}(~/.claude/ - available in all projects)${c.reset}`);
|
|
212
|
+
console.log(` ${c.cyan}2${c.reset}) Project ${c.dim}(./.claude/ - only this project)${c.reset}`);
|
|
213
|
+
console.log('');
|
|
214
|
+
|
|
215
|
+
const answer = await ask('Choose [1/2]: ');
|
|
216
|
+
|
|
217
|
+
if (answer === '2') {
|
|
218
|
+
return 'project';
|
|
219
|
+
}
|
|
220
|
+
return 'global';
|
|
221
|
+
}
|
|
222
|
+
|
|
180
223
|
function log(msg) {
|
|
181
224
|
console.log(` ${c.green}✓${c.reset} ${msg}`);
|
|
182
225
|
}
|
package/hooks/df-statusline.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
3
|
* deepflow statusline for Claude Code
|
|
4
|
-
* Displays: model | project | context usage
|
|
4
|
+
* Displays: update | model | project | context usage
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
const fs = require('fs');
|
|
@@ -43,38 +43,36 @@ function buildStatusLine(data) {
|
|
|
43
43
|
parts.push(`${colors.yellow}⬆ /df:update${colors.reset}`);
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
-
// Model name
|
|
47
|
-
const model =
|
|
46
|
+
// Model name (Claude Code format: data.model.display_name)
|
|
47
|
+
const model = data.model?.display_name || data.model?.id || 'unknown';
|
|
48
48
|
parts.push(model);
|
|
49
49
|
|
|
50
|
-
// Project name (
|
|
51
|
-
const
|
|
50
|
+
// Project name (Claude Code format: data.workspace.current_dir)
|
|
51
|
+
const currentDir = data.workspace?.current_dir || data.cwd || process.cwd();
|
|
52
|
+
const project = path.basename(currentDir);
|
|
52
53
|
parts.push(`${colors.cyan}${project}${colors.reset}`);
|
|
53
54
|
|
|
54
|
-
// Context window meter
|
|
55
|
-
const contextMeter = buildContextMeter(data.
|
|
55
|
+
// Context window meter (Claude Code format: data.context_window)
|
|
56
|
+
const contextMeter = buildContextMeter(data.context_window || {});
|
|
56
57
|
parts.push(contextMeter);
|
|
57
58
|
|
|
58
59
|
return parts.join(` ${colors.dim}│${colors.reset} `);
|
|
59
60
|
}
|
|
60
61
|
|
|
61
|
-
function
|
|
62
|
-
//
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
const limit = tokenUsage.limit || 200000;
|
|
62
|
+
function buildContextMeter(contextWindow) {
|
|
63
|
+
// Use pre-calculated percentage if available
|
|
64
|
+
let percentage = contextWindow.used_percentage || 0;
|
|
65
|
+
|
|
66
|
+
// Fallback: calculate from current_usage if available
|
|
67
|
+
if (!percentage && contextWindow.current_usage && contextWindow.context_window_size) {
|
|
68
|
+
const usage = contextWindow.current_usage;
|
|
69
|
+
const totalTokens = (usage.input_tokens || 0) +
|
|
70
|
+
(usage.cache_creation_input_tokens || 0) +
|
|
71
|
+
(usage.cache_read_input_tokens || 0);
|
|
72
|
+
percentage = (totalTokens / contextWindow.context_window_size) * 100;
|
|
73
|
+
}
|
|
74
74
|
|
|
75
|
-
|
|
76
|
-
const effectiveLimit = limit * 0.8;
|
|
77
|
-
const percentage = Math.min(100, Math.round((used / effectiveLimit) * 100));
|
|
75
|
+
percentage = Math.min(100, Math.round(percentage));
|
|
78
76
|
|
|
79
77
|
// Build 10-segment bar
|
|
80
78
|
const segments = 10;
|
|
@@ -83,11 +81,11 @@ function buildContextMeter(tokenUsage) {
|
|
|
83
81
|
|
|
84
82
|
// Color based on usage
|
|
85
83
|
let color;
|
|
86
|
-
if (percentage <
|
|
84
|
+
if (percentage < 50) {
|
|
87
85
|
color = colors.green;
|
|
88
|
-
} else if (percentage <
|
|
86
|
+
} else if (percentage < 70) {
|
|
89
87
|
color = colors.yellow;
|
|
90
|
-
} else if (percentage <
|
|
88
|
+
} else if (percentage < 90) {
|
|
91
89
|
color = colors.orange;
|
|
92
90
|
} else {
|
|
93
91
|
color = colors.blink + colors.red;
|