rbin-task-flow 1.26.1 → 1.30.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/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
  }
@@ -62,6 +60,12 @@ async function installInProject(targetPath, options = {}) {
62
60
  ? chalk.yellow('share-ai-config') + chalk.gray(' (.cursor/skills + .cursor/rules versionáveis)')
63
61
  : chalk.gray('local .cursor/ gitignored (default)')
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,7 +110,12 @@ 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
 
@@ -144,13 +159,12 @@ async function installInProject(targetPath, options = {}) {
144
159
  }
145
160
 
146
161
  async function copyConfigs(targetPath, options = {}) {
147
- const isUpdate = options.update || false;
148
162
  const isReset = options.reset || false;
149
163
  const profile = options.profile;
164
+ const taskBackup = options.taskBackup || null;
150
165
 
151
166
  const rulesResult = await copyCursorRules(targetPath, TEMPLATE_DIR, {
152
167
  profile,
153
- update: isUpdate,
154
168
  reset: isReset,
155
169
  });
156
170
  if (rulesResult?.mode === PROFILE_MINIMAL) {
@@ -200,7 +214,11 @@ async function copyConfigs(targetPath, options = {}) {
200
214
  showSuccess('Codex instructions (AGENTS.md)');
201
215
  }
202
216
 
203
- await copyTaskFlow(targetPath, { update: isUpdate, reset: isReset });
217
+ await copyTaskFlow(targetPath, {
218
+ reset: isReset,
219
+ keepTasks: options.keepTasks === true,
220
+ taskBackup,
221
+ });
204
222
  }
205
223
 
206
224
  const LEGACY_TASK_FLOW_ROOT_FILES = [
@@ -255,9 +273,55 @@ async function migrateLegacyTaskFlowLayout(taskFlowDest) {
255
273
  }
256
274
  }
257
275
 
276
+ async function backupTaskFiles(taskFlowDest) {
277
+ const backup = {};
278
+ const files = ['tasks.input.txt', 'tasks.status.md'];
279
+ for (const name of files) {
280
+ const filePath = path.join(taskFlowDest, name);
281
+ if (await fs.pathExists(filePath)) {
282
+ backup[name] = await fs.readFile(filePath, 'utf8');
283
+ }
284
+ }
285
+ const internalPath = path.join(taskFlowDest, '.internal');
286
+ if (await fs.pathExists(internalPath)) {
287
+ backup['.internal'] = await fs.mkdtemp(path.join(os.tmpdir(), 'rbin-task-flow-internal-'));
288
+ await fs.copy(internalPath, path.join(backup['.internal'], '.internal'));
289
+ }
290
+ const devLogsPath = path.join(taskFlowDest, 'dev-logs');
291
+ if (await fs.pathExists(devLogsPath)) {
292
+ backup['dev-logs'] = await fs.mkdtemp(path.join(os.tmpdir(), 'rbin-task-flow-dev-logs-'));
293
+ await fs.copy(devLogsPath, path.join(backup['dev-logs'], 'dev-logs'));
294
+ }
295
+ return backup;
296
+ }
297
+
298
+ async function restoreTaskFiles(taskFlowDest, backup) {
299
+ if (!backup) return;
300
+ for (const name of ['tasks.input.txt', 'tasks.status.md']) {
301
+ if (backup[name] !== undefined) {
302
+ await fs.writeFile(path.join(taskFlowDest, name), backup[name], 'utf8');
303
+ }
304
+ }
305
+ if (backup['.internal']) {
306
+ const src = path.join(backup['.internal'], '.internal');
307
+ if (await fs.pathExists(src)) {
308
+ await fs.copy(src, path.join(taskFlowDest, '.internal'), { overwrite: true });
309
+ }
310
+ await fs.remove(backup['.internal']);
311
+ }
312
+ if (backup['dev-logs']) {
313
+ const src = path.join(backup['dev-logs'], 'dev-logs');
314
+ if (await fs.pathExists(src)) {
315
+ await fs.copy(src, path.join(taskFlowDest, 'dev-logs'), { overwrite: true });
316
+ }
317
+ await fs.remove(backup['dev-logs']);
318
+ }
319
+ }
320
+
258
321
  async function copyTaskFlow(targetPath, options = {}) {
259
- const isUpdate = options.update || false;
260
322
  const isReset = options.reset || false;
323
+ const keepTasks = options.keepTasks === true;
324
+ const taskBackup = options.taskBackup || null;
261
325
  const taskFlowSrc = path.join(TEMPLATE_DIR, '.task-flow');
262
326
  const taskFlowDest = path.join(targetPath, '.task-flow');
263
327
 
@@ -271,11 +335,15 @@ async function copyTaskFlow(targetPath, options = {}) {
271
335
  path.join(taskFlowDest, 'tasks.status.md'),
272
336
  path.join(taskFlowDest, 'install-meta.json'),
273
337
  ];
338
+ const PRESERVED_KEEP_TASKS = [
339
+ path.join(taskFlowDest, 'tasks.input.txt'),
340
+ path.join(taskFlowDest, 'tasks.status.md'),
341
+ ];
274
342
 
275
343
  await fs.copy(taskFlowSrc, taskFlowDest, {
276
344
  overwrite: true,
277
345
  filter: (src, dest) => {
278
- if (isReset) {
346
+ if (isReset && !keepTasks) {
279
347
  return true;
280
348
  }
281
349
 
@@ -283,7 +351,11 @@ async function copyTaskFlow(targetPath, options = {}) {
283
351
  return false;
284
352
  }
285
353
 
286
- if (!isUpdate && PRESERVED_ON_INIT.includes(dest) && fs.existsSync(dest)) {
354
+ if (keepTasks && PRESERVED_KEEP_TASKS.includes(dest) && fs.existsSync(dest)) {
355
+ return false;
356
+ }
357
+
358
+ if (!isReset && !keepTasks && PRESERVED_ON_INIT.includes(dest) && fs.existsSync(dest)) {
287
359
  return false;
288
360
  }
289
361
 
@@ -291,15 +363,20 @@ async function copyTaskFlow(targetPath, options = {}) {
291
363
  },
292
364
  });
293
365
 
366
+ if (taskBackup) {
367
+ await restoreTaskFiles(taskFlowDest, taskBackup);
368
+ }
369
+
294
370
  await fs.ensureDir(path.join(taskFlowDest, 'contexts'));
371
+ await fs.ensureDir(path.join(taskFlowDest, 'dev-logs'));
295
372
  await fs.ensureDir(path.join(taskFlowDest, 'guides', 'reports'));
296
373
  await migrateLegacyTaskFlowLayout(taskFlowDest);
297
374
 
298
375
  showSuccess('Task Flow directory');
299
- if (isReset) {
376
+ if (keepTasks) {
377
+ showInfo('Kept: tasks.input.txt, tasks.status.md, .internal/, dev-logs/');
378
+ } else if (isReset) {
300
379
  showWarning('Reset completed: .task-flow was recreated from scratch');
301
- } else if (isUpdate) {
302
- showInfo('Protected: .internal/ (your task data is safe)');
303
380
  } else {
304
381
  showInfo('Protected on init: .internal/, tasks.input.txt, tasks.status.md');
305
382
  }
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.26.1",
3
+ "version": "1.30.1",
4
4
  "description": "AI-powered task management for Claude and Cursor",
5
5
  "main": "index.js",
6
6
  "bin": {