rbin-task-flow 1.19.4 → 1.23.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/.claude/skills/rbin-coding-standards/SKILL.md +29 -0
- package/.claude/skills/rbin-coding-standards/reference.md +42 -0
- package/.claude/skills/rbin-git/SKILL.md +39 -0
- package/.claude/skills/task-flow-audit/SKILL.md +15 -0
- package/.claude/skills/task-flow-check/SKILL.md +15 -0
- package/.claude/skills/task-flow-estimate/SKILL.md +15 -0
- package/.claude/skills/task-flow-generate-flow/SKILL.md +15 -0
- package/.claude/skills/task-flow-improve-changes/SKILL.md +15 -0
- package/.claude/skills/task-flow-refactor/SKILL.md +14 -0
- package/.claude/skills/task-flow-report/SKILL.md +15 -0
- package/.claude/skills/task-flow-review/SKILL.md +14 -0
- package/.claude/skills/task-flow-run/SKILL.md +28 -0
- package/.claude/skills/task-flow-run/workflow.md +59 -0
- package/.claude/skills/task-flow-status/SKILL.md +13 -0
- package/.claude/skills/task-flow-sync/SKILL.md +30 -0
- package/.claude/skills/task-flow-sync/workflow.md +57 -0
- package/.claude/skills/task-flow-think/SKILL.md +17 -0
- package/.codex/config.toml +10 -0
- package/.cursor/rules/code_comments.mdc +4 -4
- package/.cursor/rules/coding_standards.mdc +57 -810
- package/.cursor/rules/commit_practices.mdc +5 -138
- package/.cursor/rules/cursor_rules.mdc +4 -3
- package/.cursor/rules/git_control.mdc +5 -86
- package/.cursor/rules/graphify-task-flow.mdc +31 -0
- package/.cursor/rules/rbin-git-policy.mdc +47 -0
- package/.cursor/rules/self_improve.mdc +3 -3
- package/.cursor/rules/task-flow-cursor.mdc +51 -0
- package/.cursor/rules/task-flow-sync.mdc +46 -0
- package/.cursor/rules/task_analysis.mdc +31 -179
- package/.cursor/rules/task_audit.mdc +6 -5
- package/.cursor/rules/task_check.mdc +2 -3
- package/.cursor/rules/task_estimate.mdc +3 -4
- package/.cursor/rules/task_execution.mdc +26 -138
- package/.cursor/rules/task_generate_flow.mdc +2 -3
- package/.cursor/rules/task_generation.mdc +22 -140
- package/.cursor/rules/task_improve_changes.mdc +3 -4
- package/.cursor/rules/task_refactor.mdc +4 -5
- package/.cursor/rules/task_report.mdc +3 -4
- package/.cursor/rules/task_review.mdc +4 -5
- package/.cursor/rules/task_status.mdc +4 -4
- package/.cursor/rules/task_work.mdc +23 -210
- package/.task-flow/AI-PLATFORMS.md +104 -0
- package/.task-flow/CODEX.md +141 -0
- package/.task-flow/CURSOR.md +94 -0
- package/.task-flow/GRAPHIFY.md +112 -0
- package/.task-flow/OPTIMIZATION-IMPLEMENTATION-TASKS.md +365 -0
- package/.task-flow/OPTIMIZATION-PLAN.md +264 -0
- package/.task-flow/README.md +19 -4
- package/.task-flow/docs/coding-standards-full.md +851 -0
- package/.task-flow/platforms/claude-code.md +352 -0
- package/.task-flow/platforms/codex.md +379 -0
- package/.task-flow/platforms/cursor.md +333 -0
- package/AGENTS.md +69 -31
- package/CLAUDE.md +56 -48
- package/README.md +86 -10
- package/bin/cli.js +41 -16
- package/lib/codex.js +45 -0
- package/lib/cursor.js +41 -0
- package/lib/gitignore.js +101 -0
- package/lib/graphify.js +118 -0
- package/lib/install.js +106 -52
- package/lib/profiles.js +110 -0
- package/lib/skills.js +34 -0
- package/lib/utils.js +38 -2
- package/package.json +6 -2
package/lib/install.js
CHANGED
|
@@ -3,21 +3,66 @@ const path = require('path');
|
|
|
3
3
|
const chalk = require('chalk');
|
|
4
4
|
const ora = require('ora');
|
|
5
5
|
const { showHeader, showSuccess, showError, showWarning, showInfo, showNextSteps } = require('./utils');
|
|
6
|
+
const { setupGraphifyIntegration } = require('./graphify');
|
|
7
|
+
const { copySkillsToProject } = require('./skills');
|
|
8
|
+
const { setupCodexIntegration } = require('./codex');
|
|
9
|
+
const { setupCursorIntegration } = require('./cursor');
|
|
10
|
+
const {
|
|
11
|
+
PROFILE_MINIMAL,
|
|
12
|
+
PROFILE_STANDARD,
|
|
13
|
+
resolveProfile,
|
|
14
|
+
resolveShareAiConfig,
|
|
15
|
+
writeInstallMeta,
|
|
16
|
+
copyCursorRules,
|
|
17
|
+
} = require('./profiles');
|
|
18
|
+
const { updateGitignore } = require('./gitignore');
|
|
19
|
+
|
|
20
|
+
const PROFILES = { STANDARD: PROFILE_STANDARD, MINIMAL: PROFILE_MINIMAL };
|
|
6
21
|
|
|
7
22
|
const TEMPLATE_DIR = path.join(__dirname, '..');
|
|
23
|
+
const PACKAGE_VERSION = require('../package.json').version;
|
|
8
24
|
|
|
9
25
|
async function installInProject(targetPath, options = {}) {
|
|
10
26
|
const isUpdate = options.update || false;
|
|
27
|
+
const isReset = options.reset || false;
|
|
28
|
+
|
|
29
|
+
let profile;
|
|
30
|
+
let shareAiConfig;
|
|
31
|
+
try {
|
|
32
|
+
profile = await resolveProfile(targetPath, { profile: options.profile });
|
|
33
|
+
shareAiConfig = await resolveShareAiConfig(targetPath, {
|
|
34
|
+
shareAiConfig: options.shareAiConfig,
|
|
35
|
+
});
|
|
36
|
+
} catch (error) {
|
|
37
|
+
showError(error.message);
|
|
38
|
+
process.exit(1);
|
|
39
|
+
}
|
|
11
40
|
|
|
12
41
|
showHeader();
|
|
13
42
|
|
|
14
|
-
if (
|
|
43
|
+
if (isReset) {
|
|
44
|
+
console.log(chalk.blue('♻️ Resetting RBIN Task Flow...'));
|
|
45
|
+
} else if (isUpdate) {
|
|
15
46
|
console.log(chalk.blue('🔄 Updating RBIN Task Flow...'));
|
|
16
47
|
} else {
|
|
17
48
|
console.log(chalk.blue('🚀 Installing RBIN Task Flow...'));
|
|
18
49
|
}
|
|
19
50
|
|
|
20
|
-
console.log(chalk.blue('📁 Target:'), targetPath
|
|
51
|
+
console.log(chalk.blue('📁 Target:'), targetPath);
|
|
52
|
+
console.log(
|
|
53
|
+
chalk.blue('📦 Profile:'),
|
|
54
|
+
profile === PROFILES.MINIMAL ? chalk.yellow('minimal') : chalk.green('standard'),
|
|
55
|
+
profile === PROFILES.MINIMAL
|
|
56
|
+
? chalk.gray('(2 always-on rules + skills; workflows via @task-flow-*)')
|
|
57
|
+
: chalk.gray('(all Cursor rules + skills)')
|
|
58
|
+
);
|
|
59
|
+
console.log(
|
|
60
|
+
chalk.blue('🔗 Git:'),
|
|
61
|
+
shareAiConfig
|
|
62
|
+
? chalk.yellow('share-ai-config') + chalk.gray(' (.cursor/skills + .cursor/rules versionáveis)')
|
|
63
|
+
: chalk.gray('local .cursor/ gitignored (default)')
|
|
64
|
+
);
|
|
65
|
+
console.log('');
|
|
21
66
|
|
|
22
67
|
const spinner = ora('Processing...').start();
|
|
23
68
|
|
|
@@ -36,10 +81,17 @@ async function installInProject(targetPath, options = {}) {
|
|
|
36
81
|
|
|
37
82
|
spinner.text = 'Creating directories...';
|
|
38
83
|
|
|
84
|
+
if (isReset) {
|
|
85
|
+
await fs.remove(path.join(targetPath, '.task-flow'));
|
|
86
|
+
}
|
|
87
|
+
|
|
39
88
|
const dirs = [
|
|
40
89
|
'.cursor/rules',
|
|
90
|
+
'.cursor/skills',
|
|
41
91
|
'.claude',
|
|
42
|
-
'.
|
|
92
|
+
'.claude/skills',
|
|
93
|
+
'.task-flow',
|
|
94
|
+
'.codex'
|
|
43
95
|
];
|
|
44
96
|
|
|
45
97
|
for (const dir of dirs) {
|
|
@@ -48,11 +100,33 @@ async function installInProject(targetPath, options = {}) {
|
|
|
48
100
|
|
|
49
101
|
spinner.text = 'Copying configuration files...';
|
|
50
102
|
|
|
51
|
-
await copyConfigs(targetPath, isUpdate);
|
|
103
|
+
await copyConfigs(targetPath, { update: isUpdate, reset: isReset, profile });
|
|
52
104
|
|
|
53
105
|
spinner.text = 'Updating .gitignore...';
|
|
54
106
|
|
|
55
|
-
await updateGitignore(targetPath);
|
|
107
|
+
await updateGitignore(targetPath, { shareAiConfig });
|
|
108
|
+
showSuccess('.gitignore updated');
|
|
109
|
+
if (shareAiConfig) {
|
|
110
|
+
showInfo('Git: .cursor/skills/ and .cursor/rules/ can be committed (see .gitignore comment)');
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
spinner.text = 'Installing agent skills...';
|
|
114
|
+
await copySkillsToProject(targetPath);
|
|
115
|
+
|
|
116
|
+
spinner.text = 'Configuring Cursor (rules + skills)...';
|
|
117
|
+
await setupCursorIntegration(targetPath, { profile });
|
|
118
|
+
|
|
119
|
+
await writeInstallMeta(targetPath, {
|
|
120
|
+
profile,
|
|
121
|
+
packageVersion: PACKAGE_VERSION,
|
|
122
|
+
shareAiConfig,
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
spinner.text = 'Configuring Codex (AGENTS.md + .codex/config.toml)...';
|
|
126
|
+
await setupCodexIntegration(targetPath);
|
|
127
|
+
|
|
128
|
+
spinner.text = 'Configuring Graphify coexistence...';
|
|
129
|
+
await setupGraphifyIntegration(targetPath, { extract: options.graphify === true });
|
|
56
130
|
|
|
57
131
|
spinner.succeed(chalk.green('Installation completed!'));
|
|
58
132
|
|
|
@@ -69,15 +143,21 @@ async function installInProject(targetPath, options = {}) {
|
|
|
69
143
|
}
|
|
70
144
|
}
|
|
71
145
|
|
|
72
|
-
async function copyConfigs(targetPath,
|
|
73
|
-
const
|
|
74
|
-
const
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
146
|
+
async function copyConfigs(targetPath, options = {}) {
|
|
147
|
+
const isUpdate = options.update || false;
|
|
148
|
+
const isReset = options.reset || false;
|
|
149
|
+
const profile = options.profile;
|
|
150
|
+
|
|
151
|
+
const rulesResult = await copyCursorRules(targetPath, TEMPLATE_DIR, {
|
|
152
|
+
profile,
|
|
153
|
+
update: isUpdate,
|
|
154
|
+
reset: isReset,
|
|
155
|
+
});
|
|
156
|
+
if (rulesResult?.mode === PROFILE_MINIMAL) {
|
|
157
|
+
showSuccess(`Cursor rules (minimal: ${rulesResult.count} files, always-on only)`);
|
|
158
|
+
showInfo('Use @task-flow-* skills for sync, run, audit — omit heavy .mdc rules to save tokens');
|
|
159
|
+
} else if (rulesResult) {
|
|
160
|
+
showSuccess(`Cursor rules (${rulesResult.count} files)`);
|
|
81
161
|
}
|
|
82
162
|
|
|
83
163
|
const cursorSettingsPath = path.join(TEMPLATE_DIR, '.cursor/settings.json');
|
|
@@ -120,10 +200,12 @@ async function copyConfigs(targetPath, isUpdate = false) {
|
|
|
120
200
|
showSuccess('Codex instructions (AGENTS.md)');
|
|
121
201
|
}
|
|
122
202
|
|
|
123
|
-
await copyTaskFlow(targetPath, isUpdate);
|
|
203
|
+
await copyTaskFlow(targetPath, { update: isUpdate, reset: isReset });
|
|
124
204
|
}
|
|
125
205
|
|
|
126
|
-
async function copyTaskFlow(targetPath,
|
|
206
|
+
async function copyTaskFlow(targetPath, options = {}) {
|
|
207
|
+
const isUpdate = options.update || false;
|
|
208
|
+
const isReset = options.reset || false;
|
|
127
209
|
const taskFlowSrc = path.join(TEMPLATE_DIR, '.task-flow');
|
|
128
210
|
const taskFlowDest = path.join(targetPath, '.task-flow');
|
|
129
211
|
|
|
@@ -136,11 +218,16 @@ async function copyTaskFlow(targetPath, isUpdate = false) {
|
|
|
136
218
|
path.join(taskFlowDest, 'tasks.input.txt'),
|
|
137
219
|
path.join(taskFlowDest, 'tasks.status.md'),
|
|
138
220
|
path.join(taskFlowDest, 'tasks.flow.md'),
|
|
221
|
+
path.join(taskFlowDest, 'install-meta.json'),
|
|
139
222
|
];
|
|
140
223
|
|
|
141
224
|
await fs.copy(taskFlowSrc, taskFlowDest, {
|
|
142
225
|
overwrite: true,
|
|
143
226
|
filter: (src, dest) => {
|
|
227
|
+
if (isReset) {
|
|
228
|
+
return true;
|
|
229
|
+
}
|
|
230
|
+
|
|
144
231
|
if (PROTECTED.some((p) => src.startsWith(p) || dest.startsWith(p))) {
|
|
145
232
|
return false;
|
|
146
233
|
}
|
|
@@ -168,48 +255,15 @@ async function copyTaskFlow(targetPath, isUpdate = false) {
|
|
|
168
255
|
}
|
|
169
256
|
|
|
170
257
|
showSuccess('Task Flow directory');
|
|
171
|
-
if (
|
|
258
|
+
if (isReset) {
|
|
259
|
+
showWarning('Reset completed: .task-flow was recreated from scratch');
|
|
260
|
+
} else if (isUpdate) {
|
|
172
261
|
showInfo('Protected: .internal/ (your task data is safe)');
|
|
173
262
|
} else {
|
|
174
263
|
showInfo('Protected on init: .internal/, tasks.input.txt, tasks.status.md, tasks.flow.md');
|
|
175
264
|
}
|
|
176
265
|
}
|
|
177
266
|
|
|
178
|
-
async function updateGitignore(targetPath) {
|
|
179
|
-
const gitignorePath = path.join(targetPath, '.gitignore');
|
|
180
|
-
|
|
181
|
-
if (!fs.existsSync(gitignorePath)) {
|
|
182
|
-
await fs.writeFile(gitignorePath, '');
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
let content = await fs.readFile(gitignorePath, 'utf8');
|
|
186
|
-
|
|
187
|
-
const entries = [
|
|
188
|
-
'.claude/',
|
|
189
|
-
'.cursor/',
|
|
190
|
-
'.task-flow/',
|
|
191
|
-
'CLAUDE.md',
|
|
192
|
-
'AGENTS.md'
|
|
193
|
-
];
|
|
194
|
-
|
|
195
|
-
for (const entry of entries) {
|
|
196
|
-
const regex = new RegExp(`^${entry.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}$`, 'gm');
|
|
197
|
-
content = content.replace(regex, '');
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
content = content.replace(/\n{3,}/g, '\n\n');
|
|
201
|
-
|
|
202
|
-
if (!content.endsWith('\n')) {
|
|
203
|
-
content += '\n';
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
content += '\n' + entries.join('\n') + '\n';
|
|
207
|
-
|
|
208
|
-
await fs.writeFile(gitignorePath, content);
|
|
209
|
-
|
|
210
|
-
showSuccess('.gitignore updated');
|
|
211
|
-
}
|
|
212
|
-
|
|
213
267
|
async function showModelVersions(targetPath) {
|
|
214
268
|
console.log(chalk.cyan('═'.repeat(60)));
|
|
215
269
|
console.log(chalk.magenta('📋 Model Versions Configured:'));
|
package/lib/profiles.js
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
const fs = require('fs-extra');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
|
|
4
|
+
const PROFILE_STANDARD = 'standard';
|
|
5
|
+
const PROFILE_MINIMAL = 'minimal';
|
|
6
|
+
|
|
7
|
+
const MINIMAL_RULE_FILES = ['task-flow-cursor.mdc', 'rbin-git-policy.mdc'];
|
|
8
|
+
|
|
9
|
+
function normalizeProfile(profile) {
|
|
10
|
+
const value = String(profile || PROFILE_STANDARD).trim().toLowerCase();
|
|
11
|
+
if (value !== PROFILE_STANDARD && value !== PROFILE_MINIMAL) {
|
|
12
|
+
throw new Error(`Invalid profile "${profile}". Use "minimal" or "standard".`);
|
|
13
|
+
}
|
|
14
|
+
return value;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function parseProfileOption(value) {
|
|
18
|
+
return normalizeProfile(value || PROFILE_STANDARD);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
async function readInstallMeta(targetPath) {
|
|
22
|
+
const metaPath = path.join(targetPath, '.task-flow', 'install-meta.json');
|
|
23
|
+
if (!fs.existsSync(metaPath)) {
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
try {
|
|
27
|
+
return await fs.readJson(metaPath);
|
|
28
|
+
} catch {
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
async function writeInstallMeta(targetPath, { profile, packageVersion, shareAiConfig }) {
|
|
34
|
+
const metaPath = path.join(targetPath, '.task-flow', 'install-meta.json');
|
|
35
|
+
await fs.ensureDir(path.dirname(metaPath));
|
|
36
|
+
const existing = (await readInstallMeta(targetPath)) || {};
|
|
37
|
+
const meta = {
|
|
38
|
+
...existing,
|
|
39
|
+
profile: normalizeProfile(profile),
|
|
40
|
+
packageVersion,
|
|
41
|
+
updatedAt: new Date().toISOString(),
|
|
42
|
+
};
|
|
43
|
+
if (shareAiConfig !== undefined) {
|
|
44
|
+
meta.shareAiConfig = Boolean(shareAiConfig);
|
|
45
|
+
}
|
|
46
|
+
await fs.writeJson(metaPath, meta, { spaces: 2 });
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
async function resolveShareAiConfig(targetPath, options = {}) {
|
|
50
|
+
if (options.shareAiConfig !== undefined) {
|
|
51
|
+
return Boolean(options.shareAiConfig);
|
|
52
|
+
}
|
|
53
|
+
const meta = await readInstallMeta(targetPath);
|
|
54
|
+
if (meta && typeof meta.shareAiConfig === 'boolean') {
|
|
55
|
+
return meta.shareAiConfig;
|
|
56
|
+
}
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
async function resolveProfile(targetPath, options = {}) {
|
|
61
|
+
if (options.profile) {
|
|
62
|
+
return normalizeProfile(options.profile);
|
|
63
|
+
}
|
|
64
|
+
const meta = await readInstallMeta(targetPath);
|
|
65
|
+
if (meta?.profile) {
|
|
66
|
+
return normalizeProfile(meta.profile);
|
|
67
|
+
}
|
|
68
|
+
return PROFILE_STANDARD;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
async function copyCursorRules(targetPath, templateDir, { profile, update, reset }) {
|
|
72
|
+
const src = path.join(templateDir, '.cursor', 'rules');
|
|
73
|
+
const dest = path.join(targetPath, '.cursor', 'rules');
|
|
74
|
+
|
|
75
|
+
if (!fs.existsSync(src)) {
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if ((update || reset) && fs.existsSync(dest)) {
|
|
80
|
+
await fs.emptyDir(dest);
|
|
81
|
+
}
|
|
82
|
+
await fs.ensureDir(dest);
|
|
83
|
+
|
|
84
|
+
if (profile === PROFILE_MINIMAL) {
|
|
85
|
+
for (const file of MINIMAL_RULE_FILES) {
|
|
86
|
+
const from = path.join(src, file);
|
|
87
|
+
if (fs.existsSync(from)) {
|
|
88
|
+
await fs.copy(from, path.join(dest, file), { overwrite: true });
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return { mode: PROFILE_MINIMAL, count: MINIMAL_RULE_FILES.length };
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
await fs.copy(src, dest, { overwrite: true });
|
|
95
|
+
const count = (await fs.readdir(dest)).filter((name) => name.endsWith('.mdc')).length;
|
|
96
|
+
return { mode: PROFILE_STANDARD, count };
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
module.exports = {
|
|
100
|
+
PROFILE_STANDARD,
|
|
101
|
+
PROFILE_MINIMAL,
|
|
102
|
+
MINIMAL_RULE_FILES,
|
|
103
|
+
normalizeProfile,
|
|
104
|
+
parseProfileOption,
|
|
105
|
+
readInstallMeta,
|
|
106
|
+
writeInstallMeta,
|
|
107
|
+
resolveProfile,
|
|
108
|
+
resolveShareAiConfig,
|
|
109
|
+
copyCursorRules,
|
|
110
|
+
};
|
package/lib/skills.js
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
const fs = require('fs-extra');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const { showSuccess, showInfo } = require('./utils');
|
|
4
|
+
|
|
5
|
+
const TEMPLATE_DIR = path.join(__dirname, '..');
|
|
6
|
+
const SKILLS_SRC = path.join(TEMPLATE_DIR, '.claude', 'skills');
|
|
7
|
+
|
|
8
|
+
async function copySkillsToProject(targetPath) {
|
|
9
|
+
if (!fs.existsSync(SKILLS_SRC)) {
|
|
10
|
+
return false;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const destinations = [
|
|
14
|
+
path.join(targetPath, '.claude', 'skills'),
|
|
15
|
+
path.join(targetPath, '.cursor', 'skills'),
|
|
16
|
+
];
|
|
17
|
+
|
|
18
|
+
for (const dest of destinations) {
|
|
19
|
+
await fs.ensureDir(dest);
|
|
20
|
+
await fs.copy(SKILLS_SRC, dest, { overwrite: true });
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const count = (await fs.readdir(SKILLS_SRC)).filter((name) =>
|
|
24
|
+
fs.statSync(path.join(SKILLS_SRC, name)).isDirectory()
|
|
25
|
+
).length;
|
|
26
|
+
|
|
27
|
+
showSuccess(`Agent skills (${count} skills → .claude/skills/ and .cursor/skills/)`);
|
|
28
|
+
showInfo('Claude Code: restart session if .claude/skills/ was created for the first time');
|
|
29
|
+
showInfo('Claude: /task-flow-run · Cursor: @task-flow-run in Agent (or task-flow: run next X)');
|
|
30
|
+
|
|
31
|
+
return true;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
module.exports = { copySkillsToProject, SKILLS_SRC };
|
package/lib/utils.js
CHANGED
|
@@ -36,14 +36,49 @@ function showInfo(message) {
|
|
|
36
36
|
console.log(chalk.blue('ℹ️ ' + message));
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
+
function measureAlwaysOnRules(targetPath) {
|
|
40
|
+
const rulesDir = path.join(targetPath, '.cursor', 'rules');
|
|
41
|
+
if (!fs.existsSync(rulesDir)) return null;
|
|
42
|
+
|
|
43
|
+
let totalBytes = 0;
|
|
44
|
+
const files = fs.readdirSync(rulesDir).filter((name) => name.endsWith('.mdc'));
|
|
45
|
+
|
|
46
|
+
for (const name of files) {
|
|
47
|
+
const filePath = path.join(rulesDir, name);
|
|
48
|
+
const content = fs.readFileSync(filePath, 'utf8');
|
|
49
|
+
if (/alwaysApply:\s*true\b/.test(content)) {
|
|
50
|
+
totalBytes += Buffer.byteLength(content, 'utf8');
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (totalBytes === 0) return null;
|
|
55
|
+
return {
|
|
56
|
+
bytes: totalBytes,
|
|
57
|
+
kb: totalBytes / 1024,
|
|
58
|
+
approxTokens: Math.round(totalBytes / 4),
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
39
62
|
function showNextSteps(targetPath) {
|
|
63
|
+
const alwaysOn = measureAlwaysOnRules(targetPath);
|
|
64
|
+
|
|
40
65
|
console.log('\n' + chalk.cyan('═'.repeat(60)));
|
|
41
66
|
console.log(chalk.magenta.bold(' Next Steps:'));
|
|
42
67
|
console.log(chalk.cyan('═'.repeat(60)));
|
|
68
|
+
if (alwaysOn) {
|
|
69
|
+
const kbLabel = alwaysOn.kb < 10 ? alwaysOn.kb.toFixed(1) : String(Math.round(alwaysOn.kb));
|
|
70
|
+
console.log(
|
|
71
|
+
chalk.gray(' Always-on rules:'),
|
|
72
|
+
chalk.yellow(`~${kbLabel} KB`),
|
|
73
|
+
chalk.gray(`(~${alwaysOn.approxTokens.toLocaleString('en-US')} tokens est.)`)
|
|
74
|
+
);
|
|
75
|
+
}
|
|
43
76
|
console.log(chalk.blue(' 1.'), 'Edit', chalk.yellow('.task-flow/tasks.input.txt'));
|
|
44
|
-
console.log(chalk.blue(' 2.'), '
|
|
45
|
-
console.log(chalk.blue(' 3.'), '
|
|
77
|
+
console.log(chalk.blue(' 2.'), 'Sync:', chalk.cyan('task-flow: sync'), chalk.gray('· Cursor/Claude: @task-flow-sync or /task-flow-sync'));
|
|
78
|
+
console.log(chalk.blue(' 3.'), 'Run:', chalk.cyan('task-flow: run next X'), chalk.gray('· @task-flow-run'));
|
|
46
79
|
console.log(chalk.blue(' 4.'), 'Check status:', chalk.cyan('task-flow: status'));
|
|
80
|
+
console.log(chalk.blue(' 5.'), 'Codex:', chalk.gray('AGENTS.md + .task-flow/CODEX.md on demand'));
|
|
81
|
+
console.log(chalk.blue(' 6.'), 'Optional Graphify:', chalk.yellow('graphify extract .'), chalk.gray('— .task-flow/GRAPHIFY.md'));
|
|
47
82
|
console.log(chalk.cyan('═'.repeat(60)));
|
|
48
83
|
console.log(chalk.blue('\n See'), chalk.yellow('.task-flow/README.md'), chalk.blue('for all available commands\n'));
|
|
49
84
|
}
|
|
@@ -69,5 +104,6 @@ module.exports = {
|
|
|
69
104
|
showWarning,
|
|
70
105
|
showInfo,
|
|
71
106
|
showNextSteps,
|
|
107
|
+
measureAlwaysOnRules,
|
|
72
108
|
parseTaskIds
|
|
73
109
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rbin-task-flow",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.23.0",
|
|
4
4
|
"description": "AI-powered task management for Claude and Cursor",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"bin": {
|
|
@@ -9,7 +9,8 @@
|
|
|
9
9
|
},
|
|
10
10
|
"scripts": {
|
|
11
11
|
"test": "echo \"No tests yet\"",
|
|
12
|
-
"
|
|
12
|
+
"measure:rules": "node scripts/measure-rule-bytes.js",
|
|
13
|
+
"prepublishOnly": "npm run measure:rules && chmod +x bin/cli.js scripts/measure-rule-bytes.js"
|
|
13
14
|
},
|
|
14
15
|
"keywords": [
|
|
15
16
|
"task-flow",
|
|
@@ -32,10 +33,13 @@
|
|
|
32
33
|
"bin/",
|
|
33
34
|
".cursor/",
|
|
34
35
|
".claude/",
|
|
36
|
+
".claude/skills/",
|
|
37
|
+
".cursor/skills/",
|
|
35
38
|
".task-flow/",
|
|
36
39
|
".model-versions.json",
|
|
37
40
|
"CLAUDE.md",
|
|
38
41
|
"AGENTS.md",
|
|
42
|
+
".codex/",
|
|
39
43
|
"lib/"
|
|
40
44
|
],
|
|
41
45
|
"preferGlobal": true,
|