rbin-task-flow 1.27.1 → 1.30.3

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/lib/install.js CHANGED
@@ -1,4 +1,5 @@
1
1
  const fs = require('fs-extra');
2
+ const os = require('os');
2
3
  const path = require('path');
3
4
  const chalk = require('chalk');
4
5
  const ora = require('ora');
@@ -23,7 +24,6 @@ const TEMPLATE_DIR = path.join(__dirname, '..');
23
24
  const PACKAGE_VERSION = require('../package.json').version;
24
25
 
25
26
  async function installInProject(targetPath, options = {}) {
26
- const isUpdate = options.update || false;
27
27
  const isReset = options.reset || false;
28
28
 
29
29
  let profile;
@@ -42,8 +42,6 @@ async function installInProject(targetPath, options = {}) {
42
42
 
43
43
  if (isReset) {
44
44
  console.log(chalk.blue('♻️ Resetting RBIN Task Flow...'));
45
- } else if (isUpdate) {
46
- console.log(chalk.blue('🔄 Updating RBIN Task Flow...'));
47
45
  } else {
48
46
  console.log(chalk.blue('🚀 Installing RBIN Task Flow...'));
49
47
  }
@@ -59,9 +57,15 @@ async function installInProject(targetPath, options = {}) {
59
57
  console.log(
60
58
  chalk.blue('🔗 Git:'),
61
59
  shareAiConfig
62
- ? chalk.yellow('share-ai-config') + chalk.gray(' (.cursor/skills + .cursor/rules versionáveis)')
63
- : chalk.gray('local .cursor/ gitignored (default)')
60
+ ? chalk.yellow('share-ai-config') + chalk.gray(' (flag saved in install-meta.json)')
61
+ : chalk.gray('appends .task-flow to .gitignore')
64
62
  );
63
+ if (options.keepTasks) {
64
+ console.log(
65
+ chalk.blue('📋 Tasks:'),
66
+ chalk.yellow('keep-tasks') + chalk.gray(' (tasks.input.txt, tasks.status.md, .internal/, dev-logs/)')
67
+ );
68
+ }
65
69
  console.log('');
66
70
 
67
71
  const spinner = ora('Processing...').start();
@@ -81,8 +85,14 @@ async function installInProject(targetPath, options = {}) {
81
85
 
82
86
  spinner.text = 'Creating directories...';
83
87
 
88
+ let taskBackup = null;
89
+ const taskFlowPath = path.join(targetPath, '.task-flow');
90
+ if (isReset && options.keepTasks && (await fs.pathExists(taskFlowPath))) {
91
+ taskBackup = await backupTaskFiles(taskFlowPath);
92
+ }
93
+
84
94
  if (isReset) {
85
- await fs.remove(path.join(targetPath, '.task-flow'));
95
+ await fs.remove(taskFlowPath);
86
96
  }
87
97
 
88
98
  const dirs = [
@@ -100,15 +110,18 @@ async function installInProject(targetPath, options = {}) {
100
110
 
101
111
  spinner.text = 'Copying configuration files...';
102
112
 
103
- await copyConfigs(targetPath, { update: isUpdate, reset: isReset, profile });
113
+ await copyConfigs(targetPath, {
114
+ reset: isReset,
115
+ profile,
116
+ keepTasks: options.keepTasks === true,
117
+ taskBackup,
118
+ });
104
119
 
105
120
  spinner.text = 'Updating .gitignore...';
106
121
 
107
- await updateGitignore(targetPath, { shareAiConfig });
122
+ await updateGitignore(targetPath);
108
123
  showSuccess('.gitignore updated');
109
- if (shareAiConfig) {
110
- showInfo('Git: .cursor/skills/ and .cursor/rules/ can be committed (see .gitignore comment)');
111
- }
124
+ showInfo('Git: appended .task-flow to .gitignore');
112
125
 
113
126
  spinner.text = 'Installing agent skills...';
114
127
  await copySkillsToProject(targetPath);
@@ -144,13 +157,12 @@ async function installInProject(targetPath, options = {}) {
144
157
  }
145
158
 
146
159
  async function copyConfigs(targetPath, options = {}) {
147
- const isUpdate = options.update || false;
148
160
  const isReset = options.reset || false;
149
161
  const profile = options.profile;
162
+ const taskBackup = options.taskBackup || null;
150
163
 
151
164
  const rulesResult = await copyCursorRules(targetPath, TEMPLATE_DIR, {
152
165
  profile,
153
- update: isUpdate,
154
166
  reset: isReset,
155
167
  });
156
168
  if (rulesResult?.mode === PROFILE_MINIMAL) {
@@ -200,7 +212,11 @@ async function copyConfigs(targetPath, options = {}) {
200
212
  showSuccess('Codex instructions (AGENTS.md)');
201
213
  }
202
214
 
203
- await copyTaskFlow(targetPath, { update: isUpdate, reset: isReset });
215
+ await copyTaskFlow(targetPath, {
216
+ reset: isReset,
217
+ keepTasks: options.keepTasks === true,
218
+ taskBackup,
219
+ });
204
220
  }
205
221
 
206
222
  const LEGACY_TASK_FLOW_ROOT_FILES = [
@@ -255,9 +271,55 @@ async function migrateLegacyTaskFlowLayout(taskFlowDest) {
255
271
  }
256
272
  }
257
273
 
274
+ async function backupTaskFiles(taskFlowDest) {
275
+ const backup = {};
276
+ const files = ['tasks.input.txt', 'tasks.status.md'];
277
+ for (const name of files) {
278
+ const filePath = path.join(taskFlowDest, name);
279
+ if (await fs.pathExists(filePath)) {
280
+ backup[name] = await fs.readFile(filePath, 'utf8');
281
+ }
282
+ }
283
+ const internalPath = path.join(taskFlowDest, '.internal');
284
+ if (await fs.pathExists(internalPath)) {
285
+ backup['.internal'] = await fs.mkdtemp(path.join(os.tmpdir(), 'rbin-task-flow-internal-'));
286
+ await fs.copy(internalPath, path.join(backup['.internal'], '.internal'));
287
+ }
288
+ const devLogsPath = path.join(taskFlowDest, 'dev-logs');
289
+ if (await fs.pathExists(devLogsPath)) {
290
+ backup['dev-logs'] = await fs.mkdtemp(path.join(os.tmpdir(), 'rbin-task-flow-dev-logs-'));
291
+ await fs.copy(devLogsPath, path.join(backup['dev-logs'], 'dev-logs'));
292
+ }
293
+ return backup;
294
+ }
295
+
296
+ async function restoreTaskFiles(taskFlowDest, backup) {
297
+ if (!backup) return;
298
+ for (const name of ['tasks.input.txt', 'tasks.status.md']) {
299
+ if (backup[name] !== undefined) {
300
+ await fs.writeFile(path.join(taskFlowDest, name), backup[name], 'utf8');
301
+ }
302
+ }
303
+ if (backup['.internal']) {
304
+ const src = path.join(backup['.internal'], '.internal');
305
+ if (await fs.pathExists(src)) {
306
+ await fs.copy(src, path.join(taskFlowDest, '.internal'), { overwrite: true });
307
+ }
308
+ await fs.remove(backup['.internal']);
309
+ }
310
+ if (backup['dev-logs']) {
311
+ const src = path.join(backup['dev-logs'], 'dev-logs');
312
+ if (await fs.pathExists(src)) {
313
+ await fs.copy(src, path.join(taskFlowDest, 'dev-logs'), { overwrite: true });
314
+ }
315
+ await fs.remove(backup['dev-logs']);
316
+ }
317
+ }
318
+
258
319
  async function copyTaskFlow(targetPath, options = {}) {
259
- const isUpdate = options.update || false;
260
320
  const isReset = options.reset || false;
321
+ const keepTasks = options.keepTasks === true;
322
+ const taskBackup = options.taskBackup || null;
261
323
  const taskFlowSrc = path.join(TEMPLATE_DIR, '.task-flow');
262
324
  const taskFlowDest = path.join(targetPath, '.task-flow');
263
325
 
@@ -271,11 +333,15 @@ async function copyTaskFlow(targetPath, options = {}) {
271
333
  path.join(taskFlowDest, 'tasks.status.md'),
272
334
  path.join(taskFlowDest, 'install-meta.json'),
273
335
  ];
336
+ const PRESERVED_KEEP_TASKS = [
337
+ path.join(taskFlowDest, 'tasks.input.txt'),
338
+ path.join(taskFlowDest, 'tasks.status.md'),
339
+ ];
274
340
 
275
341
  await fs.copy(taskFlowSrc, taskFlowDest, {
276
342
  overwrite: true,
277
343
  filter: (src, dest) => {
278
- if (isReset) {
344
+ if (isReset && !keepTasks) {
279
345
  return true;
280
346
  }
281
347
 
@@ -283,7 +349,11 @@ async function copyTaskFlow(targetPath, options = {}) {
283
349
  return false;
284
350
  }
285
351
 
286
- if (!isUpdate && PRESERVED_ON_INIT.includes(dest) && fs.existsSync(dest)) {
352
+ if (keepTasks && PRESERVED_KEEP_TASKS.includes(dest) && fs.existsSync(dest)) {
353
+ return false;
354
+ }
355
+
356
+ if (!isReset && !keepTasks && PRESERVED_ON_INIT.includes(dest) && fs.existsSync(dest)) {
287
357
  return false;
288
358
  }
289
359
 
@@ -291,15 +361,20 @@ async function copyTaskFlow(targetPath, options = {}) {
291
361
  },
292
362
  });
293
363
 
364
+ if (taskBackup) {
365
+ await restoreTaskFiles(taskFlowDest, taskBackup);
366
+ }
367
+
294
368
  await fs.ensureDir(path.join(taskFlowDest, 'contexts'));
369
+ await fs.ensureDir(path.join(taskFlowDest, 'dev-logs'));
295
370
  await fs.ensureDir(path.join(taskFlowDest, 'guides', 'reports'));
296
371
  await migrateLegacyTaskFlowLayout(taskFlowDest);
297
372
 
298
373
  showSuccess('Task Flow directory');
299
- if (isReset) {
374
+ if (keepTasks) {
375
+ showInfo('Kept: tasks.input.txt, tasks.status.md, .internal/, dev-logs/');
376
+ } else if (isReset) {
300
377
  showWarning('Reset completed: .task-flow was recreated from scratch');
301
- } else if (isUpdate) {
302
- showInfo('Protected: .internal/ (your task data is safe)');
303
378
  } else {
304
379
  showInfo('Protected on init: .internal/, tasks.input.txt, tasks.status.md');
305
380
  }
package/lib/profiles.js CHANGED
@@ -68,7 +68,7 @@ async function resolveProfile(targetPath, options = {}) {
68
68
  return PROFILE_STANDARD;
69
69
  }
70
70
 
71
- async function copyCursorRules(targetPath, templateDir, { profile, update, reset }) {
71
+ async function copyCursorRules(targetPath, templateDir, { profile, reset }) {
72
72
  const src = path.join(templateDir, '.cursor', 'rules');
73
73
  const dest = path.join(targetPath, '.cursor', 'rules');
74
74
 
@@ -76,7 +76,7 @@ async function copyCursorRules(targetPath, templateDir, { profile, update, reset
76
76
  return;
77
77
  }
78
78
 
79
- if ((update || reset) && fs.existsSync(dest)) {
79
+ if (reset && fs.existsSync(dest)) {
80
80
  await fs.emptyDir(dest);
81
81
  }
82
82
  await fs.ensureDir(dest);
package/lib/version.js CHANGED
@@ -97,7 +97,7 @@ async function checkModelVersion(modelName, versionInfo, settingsPath, rl) {
97
97
  }
98
98
  await fs.writeJSON(fullSettingsPath, settings, { spaces: 2 });
99
99
  console.log(chalk.green(` ✅ ${modelName} updated to ${versionInfo.latest}`));
100
- console.log(chalk.cyan(' (Repository template updated - run init/update on projects to apply)'));
100
+ console.log(chalk.cyan(' (Repository template updated - run init/reset on projects to apply)'));
101
101
  } else {
102
102
  console.log(chalk.cyan(' Skipped update'));
103
103
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rbin-task-flow",
3
- "version": "1.27.1",
3
+ "version": "1.30.3",
4
4
  "description": "AI-powered task management for Claude and Cursor",
5
5
  "main": "index.js",
6
6
  "bin": {