taskmonkey-cli 0.5.2 → 0.7.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/bin/tm.js CHANGED
@@ -9,6 +9,7 @@ import { watch } from '../src/commands/watch.js';
9
9
  import { logs } from '../src/commands/logs.js';
10
10
  import { chat } from '../src/commands/chat.js';
11
11
  import { tasks } from '../src/commands/tasks.js';
12
+ import { history } from '../src/commands/history.js';
12
13
  import { testChat } from '../src/commands/test-chat.js';
13
14
  import { testConversations } from '../src/commands/test-conversations.js';
14
15
  import { optimizePrompt } from '../src/commands/optimize-prompt.js';
@@ -41,6 +42,7 @@ program
41
42
  program
42
43
  .command('sync')
43
44
  .description('Upload local config files to server')
45
+ .option('-d, --delete', 'Delete remote files that no longer exist locally')
44
46
  .action(sync);
45
47
 
46
48
  program
@@ -53,6 +55,13 @@ program
53
55
  .description('Watch for file changes and auto-sync')
54
56
  .action(watch);
55
57
 
58
+ program
59
+ .command('history')
60
+ .description('Show recent chat messages for a task (for debugging)')
61
+ .option('-t, --task <slug>', 'Monkey task slug (default: unified)')
62
+ .option('-n, --last <number>', 'Number of messages', '20')
63
+ .action(history);
64
+
56
65
  program
57
66
  .command('logs')
58
67
  .description('Stream server logs')
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "taskmonkey-cli",
3
- "version": "0.5.2",
3
+ "version": "0.7.0",
4
4
  "description": "TaskMonkey CLI — Remote dev tools for tenant config editing and tool testing",
5
5
  "bin": {
6
6
  "tm": "./bin/tm.js",
@@ -0,0 +1,59 @@
1
+ import chalk from 'chalk';
2
+ import ora from 'ora';
3
+ import { createClient } from '../lib/api.js';
4
+
5
+ export async function history(options) {
6
+ const client = createClient();
7
+
8
+ const spinner = ora('Loading chat history...').start();
9
+
10
+ try {
11
+ const result = await client.get('/api/test/history', {
12
+ task: options.task || '',
13
+ last: options.last || '20',
14
+ });
15
+
16
+ spinner.stop();
17
+
18
+ if (result.count === 0) {
19
+ console.log(chalk.yellow('Keine Nachrichten gefunden.'));
20
+ console.log(chalk.gray(`Chat-ID: ${result.chat_id}`));
21
+ return;
22
+ }
23
+
24
+ console.log(chalk.gray(`${result.chat_id} — ${result.count} Nachrichten\n`));
25
+
26
+ for (const msg of result.messages) {
27
+ const time = chalk.gray(msg.created);
28
+ const content = (msg.content || '').replace(/\n{3,}/g, '\n').trim();
29
+
30
+ switch (msg.role) {
31
+ case 'user':
32
+ console.log(`${time} ${chalk.green('USER')} ${content}`);
33
+ break;
34
+ case 'assistant':
35
+ // Check for tool_use
36
+ if (content.includes('[tool_use:')) {
37
+ const tools = content.match(/\[tool_use: ([^\]]+)\]/g) || [];
38
+ for (const t of tools) {
39
+ console.log(`${time} ${chalk.yellow('TOOL')} ${t}`);
40
+ }
41
+ } else {
42
+ console.log(`${time} ${chalk.cyan('AI')} ${content.substring(0, 200)}${content.length > 200 ? '...' : ''}`);
43
+ }
44
+ break;
45
+ case 'tool':
46
+ console.log(`${time} ${chalk.gray('RESULT')} ${content.substring(0, 150)}${content.length > 150 ? '...' : ''}`);
47
+ break;
48
+ case 'system':
49
+ console.log(`${time} ${chalk.magenta('SYS')} ${content.substring(0, 150)}`);
50
+ break;
51
+ default:
52
+ console.log(`${time} ${chalk.gray(msg.role.toUpperCase())} ${content.substring(0, 150)}`);
53
+ }
54
+ }
55
+ } catch (err) {
56
+ spinner.fail(err.message);
57
+ process.exit(1);
58
+ }
59
+ }
@@ -1,4 +1,4 @@
1
- import { readdirSync, readFileSync, statSync } from 'fs';
1
+ import { readdirSync, readFileSync } from 'fs';
2
2
  import { join, relative } from 'path';
3
3
  import chalk from 'chalk';
4
4
  import ora from 'ora';
@@ -22,7 +22,7 @@ function collectFiles(dir, base = dir) {
22
22
  return files;
23
23
  }
24
24
 
25
- export async function sync() {
25
+ export async function sync(options = {}) {
26
26
  const config = loadConfig();
27
27
  if (!config) {
28
28
  console.error(chalk.red('Not logged in. Run `tm login` first.'));
@@ -30,22 +30,46 @@ export async function sync() {
30
30
  }
31
31
 
32
32
  const tenantDir = join(config._configDir, config.tenant_path || '.');
33
+ const deleteRemote = options.delete || false;
33
34
 
34
35
  const spinner = ora('Collecting files...').start();
35
36
 
36
- const files = collectFiles(tenantDir);
37
- const fileCount = Object.keys(files).length;
37
+ const localFiles = collectFiles(tenantDir);
38
+ const localPaths = Object.keys(localFiles);
38
39
 
39
- if (fileCount === 0) {
40
+ if (localPaths.length === 0) {
40
41
  spinner.warn('No .php files found');
41
42
  return;
42
43
  }
43
44
 
44
- spinner.text = `Syncing ${fileCount} files...`;
45
+ const client = createClient();
46
+
47
+ // Delete remote files that don't exist locally
48
+ if (deleteRemote) {
49
+ spinner.text = 'Comparing with server...';
50
+ try {
51
+ const remoteList = await client.get('/api/tenant/files');
52
+ const remotePaths = (remoteList.files || []).map(f => f.path);
53
+ const toDelete = remotePaths.filter(p => !localPaths.includes(p));
54
+
55
+ if (toDelete.length > 0) {
56
+ const result = await client.post('/api/tenant/delete-files', { files: toDelete });
57
+ spinner.stop();
58
+ console.log(chalk.red(`✗ ${result.deleted || 0} files deleted on server`));
59
+ for (const file of result.files || []) {
60
+ console.log(chalk.red(` - ${file}`));
61
+ }
62
+ }
63
+ } catch (err) {
64
+ spinner.stop();
65
+ console.error(chalk.yellow(` Delete check failed: ${err.message}`));
66
+ }
67
+ }
68
+
69
+ spinner.text = `Syncing ${localPaths.length} files...`;
45
70
 
46
71
  try {
47
- const client = createClient();
48
- const result = await client.post('/api/tenant/sync', { files });
72
+ const result = await client.post('/api/tenant/sync', { files: localFiles });
49
73
 
50
74
  spinner.stop();
51
75
 
@@ -1,8 +1,9 @@
1
1
  import chokidar from 'chokidar';
2
2
  import chalk from 'chalk';
3
3
  import { loadConfig } from '../config.js';
4
+ import { createClient } from '../lib/api.js';
4
5
  import { sync } from './sync.js';
5
- import { join } from 'path';
6
+ import { join, relative } from 'path';
6
7
 
7
8
  export async function watch() {
8
9
  const config = loadConfig();
@@ -16,28 +17,57 @@ export async function watch() {
16
17
  console.log(chalk.cyan('👀 Watching'), tenantDir);
17
18
  console.log(chalk.gray(' Ctrl+C to stop\n'));
18
19
 
19
- let debounceTimer = null;
20
+ let syncTimer = null;
21
+ const pendingDeletes = [];
20
22
 
21
23
  const watcher = chokidar.watch(join(tenantDir, '**/*.php'), {
22
24
  ignoreInitial: true,
23
25
  awaitWriteFinish: { stabilityThreshold: 300 },
24
26
  });
25
27
 
26
- watcher.on('all', (event, path) => {
27
- console.log(chalk.gray(` ${event}: ${path.replace(tenantDir + '/', '')}`));
28
+ watcher.on('add', (path) => {
29
+ console.log(chalk.green(` + ${relative(tenantDir, path)}`));
30
+ debouncedSync();
31
+ });
32
+
33
+ watcher.on('change', (path) => {
34
+ console.log(chalk.yellow(` ~ ${relative(tenantDir, path)}`));
35
+ debouncedSync();
36
+ });
28
37
 
29
- // Debounce: wait 500ms after last change
30
- if (debounceTimer) clearTimeout(debounceTimer);
31
- debounceTimer = setTimeout(async () => {
38
+ watcher.on('unlink', (path) => {
39
+ const relPath = relative(tenantDir, path);
40
+ console.log(chalk.red(` - ${relPath}`));
41
+ pendingDeletes.push(relPath);
42
+ debouncedSync();
43
+ });
44
+
45
+ function debouncedSync() {
46
+ if (syncTimer) clearTimeout(syncTimer);
47
+ syncTimer = setTimeout(async () => {
32
48
  try {
49
+ // Delete files first
50
+ if (pendingDeletes.length > 0) {
51
+ const toDelete = [...pendingDeletes];
52
+ pendingDeletes.length = 0;
53
+ try {
54
+ const client = createClient();
55
+ const result = await client.post('/api/tenant/delete-files', { files: toDelete });
56
+ if (result.deleted > 0) {
57
+ console.log(chalk.red(` ✓ ${result.deleted} deleted on server`));
58
+ }
59
+ } catch (err) {
60
+ console.error(chalk.red(` Delete error: ${err.message}`));
61
+ }
62
+ }
63
+ // Then sync remaining files
33
64
  await sync();
34
65
  } catch (err) {
35
66
  console.error(chalk.red(` Sync error: ${err.message}`));
36
67
  }
37
68
  }, 500);
38
- });
69
+ }
39
70
 
40
- // Keep process alive
41
71
  process.on('SIGINT', () => {
42
72
  watcher.close();
43
73
  console.log(chalk.gray('\nStopped.'));