thinkncollab-shell 0.0.20 → 2.0.0-beta.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/LICENSE +82 -0
- package/Readme.md +246 -502
- package/dist/bin/thinknsh.js +8 -0
- package/dist/src/commands/auth/login.js +7 -0
- package/dist/src/commands/auth/logout.js +7 -0
- package/dist/src/commands/auth/register.js +7 -0
- package/dist/src/commands/auth/whoami.js +7 -0
- package/dist/src/commands/dock/bench.js +7 -0
- package/dist/src/commands/dock/ci.js +7 -0
- package/dist/src/commands/dock/cluster.js +7 -0
- package/dist/src/commands/dock/containers.js +7 -0
- package/dist/src/commands/dock/dock.js +7 -0
- package/dist/src/commands/github/commits.js +7 -0
- package/dist/src/commands/github/issue-create.js +7 -0
- package/dist/src/commands/github/issues.js +7 -0
- package/dist/src/commands/github/pulls.js +7 -0
- package/dist/src/commands/github/statusgh.js +7 -0
- package/dist/src/commands/index.js +7 -0
- package/dist/src/commands/message/say.js +7 -0
- package/dist/src/commands/room/join.js +7 -0
- package/dist/src/commands/room/leave.js +7 -0
- package/dist/src/commands/room/rooms.js +7 -0
- package/dist/src/commands/room/sync.js +7 -0
- package/dist/src/commands/sandbox/console.js +7 -0
- package/dist/src/commands/sandbox/dep.js +7 -0
- package/dist/src/commands/sandbox/deploy.js +7 -0
- package/dist/src/commands/sandbox/dev.js +7 -0
- package/dist/src/commands/sandbox/exec.js +7 -0
- package/dist/src/commands/sandbox/freeze.js +7 -0
- package/dist/src/commands/sandbox/push.js +7 -0
- package/dist/src/commands/sandbox/resume.js +7 -0
- package/dist/src/commands/sandbox/sandbox.js +7 -0
- package/dist/src/commands/sandbox/share.js +7 -0
- package/dist/src/commands/sandbox/snapshot.js +7 -0
- package/dist/src/commands/sandbox/sync-deps.js +7 -0
- package/dist/src/commands/shell/doctor.js +7 -0
- package/dist/src/commands/shell/notifications.js +7 -0
- package/dist/src/commands/shell/notify.js +7 -0
- package/dist/src/commands/shell/status.js +7 -0
- package/dist/src/commands/shell/version.js +7 -0
- package/dist/src/commands/tasks/build.js +7 -0
- package/dist/src/commands/tasks/complete.js +7 -0
- package/dist/src/commands/tasks/create-task.js +7 -0
- package/dist/src/commands/tasks/loadtest.js +7 -0
- package/dist/src/commands/tasks/task-status.js +7 -0
- package/dist/src/commands/tasks/task.js +7 -0
- package/dist/src/commands/tasks/tasks.js +7 -0
- package/dist/src/commands/team/invite.js +7 -0
- package/dist/src/commands/team/myteam.js +7 -0
- package/dist/src/commands/team/teammates.js +7 -0
- package/dist/src/commands/terminal/share.js +7 -0
- package/dist/src/core/api-client.js +7 -0
- package/dist/src/core/crypto/e2ee-vault.js +7 -0
- package/dist/src/core/dep-store.js +7 -0
- package/dist/src/core/dependency-tracker.js +7 -0
- package/dist/src/core/geekload.js +7 -0
- package/dist/src/core/local-runner.js +7 -0
- package/dist/src/core/multi-lang-scanner.js +7 -0
- package/dist/src/core/notification-window.js +8 -0
- package/dist/src/core/notify-auth.js +7 -0
- package/dist/src/core/project-config.js +7 -0
- package/dist/src/core/sandbox/arch-shim.js +7 -0
- package/dist/src/core/sandbox/capsule-builder.js +7 -0
- package/dist/src/core/sandbox/cgroups.js +7 -0
- package/dist/src/core/sandbox/container-monitor.js +7 -0
- package/dist/src/core/sandbox/crun-manager.js +7 -0
- package/dist/src/core/sandbox/engine.js +7 -0
- package/dist/src/core/sandbox/image-manager.js +7 -0
- package/dist/src/core/sandbox/image-store.js +7 -0
- package/dist/src/core/sandbox/index.js +7 -0
- package/dist/src/core/sandbox/macos-sandbox.js +7 -0
- package/dist/src/core/sandbox/microvm-manager.js +7 -0
- package/dist/src/core/sandbox/native-service-manager.js +7 -0
- package/dist/src/core/sandbox/network.js +7 -0
- package/dist/src/core/sandbox/oci-exporter.js +7 -0
- package/dist/src/core/sandbox/oci-puller.js +7 -0
- package/dist/src/core/sandbox/oci-runtime-spec.js +7 -0
- package/dist/src/core/sandbox/policy-engine.js +7 -0
- package/dist/src/core/sandbox/port-proxy-daemon.js +7 -0
- package/dist/src/core/sandbox/room-sync-manager.js +7 -0
- package/dist/src/core/sandbox/runtime-manager.js +7 -0
- package/dist/src/core/sandbox/snapshot-manager.js +7 -0
- package/dist/src/core/sandbox/storage.js +7 -0
- package/dist/src/core/sandbox/supervisor.js +7 -0
- package/dist/src/core/sandbox/tunnel-daemon.js +7 -0
- package/dist/src/core/sandbox/tunnel.js +7 -0
- package/dist/src/core/sandbox/window-launcher.js +7 -0
- package/dist/src/core/sandbox/windows-wsl2.js +7 -0
- package/dist/src/core/sandbox-window.js +8 -0
- package/dist/src/core/shell.js +7 -0
- package/dist/src/core/websocket.js +7 -0
- package/dist/src/dock/api/routes.js +7 -0
- package/dist/src/dock/api/server.js +7 -0
- package/dist/src/dock/api/wire.js +7 -0
- package/dist/src/dock/bench/memory.js +7 -0
- package/dist/src/dock/bench/runner.js +7 -0
- package/dist/src/dock/build/builder.js +7 -0
- package/dist/src/dock/build/buildfile.js +7 -0
- package/dist/src/dock/build/buildkit.js +7 -0
- package/dist/src/dock/build/changes.js +7 -0
- package/dist/src/dock/ci/actions.js +7 -0
- package/dist/src/dock/ci/runner.js +7 -0
- package/dist/src/dock/ci/workflow.js +7 -0
- package/dist/src/dock/cli/api.js +7 -0
- package/dist/src/dock/cli/args.js +7 -0
- package/dist/src/dock/cli/commands.js +7 -0
- package/dist/src/dock/cli/compose.js +7 -0
- package/dist/src/dock/cli/context.js +7 -0
- package/dist/src/dock/cli/files.js +7 -0
- package/dist/src/dock/cli/format.js +7 -0
- package/dist/src/dock/cli/manage.js +7 -0
- package/dist/src/dock/cli/moved.js +7 -0
- package/dist/src/dock/cli/services.js +7 -0
- package/dist/src/dock/cli/session.js +7 -0
- package/dist/src/dock/cli/workspace.js +7 -0
- package/dist/src/dock/client.js +7 -0
- package/dist/src/dock/compat/cases.js +7 -0
- package/dist/src/dock/compat/runner.js +7 -0
- package/dist/src/dock/config.js +7 -0
- package/dist/src/dock/daemon-main.js +7 -0
- package/dist/src/dock/daemon.js +7 -0
- package/dist/src/dock/deploy/compose.js +7 -0
- package/dist/src/dock/engine.js +7 -0
- package/dist/src/dock/image/archive.js +7 -0
- package/dist/src/dock/image/auth.js +7 -0
- package/dist/src/dock/image/reference.js +7 -0
- package/dist/src/dock/image/registry.js +7 -0
- package/dist/src/dock/image/tar.js +7 -0
- package/dist/src/dock/index.js +7 -0
- package/dist/src/dock/linux/host.js +7 -0
- package/dist/src/dock/paths.js +7 -0
- package/dist/src/dock/runtime/emulation.js +7 -0
- package/dist/src/dock/runtime/fsevents.js +7 -0
- package/dist/src/dock/runtime/fsops.js +7 -0
- package/dist/src/dock/runtime/hostfwd.js +7 -0
- package/dist/src/dock/runtime/logs.js +7 -0
- package/dist/src/dock/runtime/network.js +7 -0
- package/dist/src/dock/runtime/ports.js +7 -0
- package/dist/src/dock/runtime/rootfs.js +7 -0
- package/dist/src/dock/runtime/seccomp-profile.LICENSE +202 -0
- package/dist/src/dock/runtime/seccomp-profile.json +1227 -0
- package/dist/src/dock/runtime/seccomp.js +7 -0
- package/dist/src/dock/runtime/spec.js +7 -0
- package/dist/src/dock/runtime/stats.js +7 -0
- package/dist/src/dock/runtime/volumes.js +7 -0
- package/dist/src/dock/selftest.js +7 -0
- package/dist/src/dock/setup/app.js +7 -0
- package/dist/src/dock/setup/autostart.js +7 -0
- package/dist/src/dock/setup/index.js +7 -0
- package/dist/src/dock/setup/integrate.js +7 -0
- package/dist/src/dock/setup/migrate.js +7 -0
- package/dist/src/dock/state/store.js +7 -0
- package/dist/src/dock/ui/page.js +385 -0
- package/dist/src/dock/ui/server.js +7 -0
- package/dist/src/dock/version.js +7 -0
- package/dist/src/dock/workspace/apps.js +7 -0
- package/dist/src/dock/workspace/workspace.js +7 -0
- package/dist/src/events/notifications.js +7 -0
- package/dist/src/utils/logger.js +7 -0
- package/dist/src/utils/terminalCrypto.js +7 -0
- package/package.json +41 -16
- package/bin/thinknsh.js +0 -71
- package/src/commands/auth/login.js +0 -84
- package/src/commands/auth/logout.js +0 -32
- package/src/commands/auth/register.js +0 -52
- package/src/commands/auth/whoami.js +0 -52
- package/src/commands/github/commits.js +0 -58
- package/src/commands/github/issue-create.js +0 -59
- package/src/commands/github/issues.js +0 -57
- package/src/commands/github/pulls.js +0 -58
- package/src/commands/github/statusgh.js +0 -50
- package/src/commands/index.js +0 -136
- package/src/commands/message/say.js +0 -49
- package/src/commands/room/join.js +0 -142
- package/src/commands/room/leave.js +0 -24
- package/src/commands/room/rooms.js +0 -66
- package/src/commands/shell/notify.js +0 -88
- package/src/commands/shell/status.js +0 -73
- package/src/commands/shell/version.js +0 -18
- package/src/commands/tasks/build.js +0 -190
- package/src/commands/tasks/complete.js +0 -940
- package/src/commands/tasks/create-task.js +0 -101
- package/src/commands/tasks/loadtest.js +0 -101
- package/src/commands/tasks/suggestTask.js +0 -0
- package/src/commands/tasks/task-status.js +0 -72
- package/src/commands/tasks/task.js +0 -182
- package/src/commands/tasks/tasks.js +0 -184
- package/src/commands/team/invite.js +0 -46
- package/src/commands/team/myteam.js +0 -78
- package/src/commands/team/teammates.js +0 -52
- package/src/commands/terminal/share.js +0 -261
- package/src/core/api-client.js +0 -246
- package/src/core/geekload.js +0 -524
- package/src/core/local-runner.js +0 -861
- package/src/core/notification-window.js +0 -165
- package/src/core/project-config.js +0 -100
- package/src/core/shell.js +0 -734
- package/src/core/websocket.js +0 -657
- package/src/events/notifications.js +0 -184
- package/src/utils/logger.js +0 -159
- package/src/utils/terminalCrypto.js +0 -47
|
@@ -1,101 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
src/commands/tasks/create-task.js — Create a new task interactively
|
|
3
|
-
*/
|
|
4
|
-
const chalk = require('chalk');
|
|
5
|
-
|
|
6
|
-
module.exports = {
|
|
7
|
-
name: 'create-task',
|
|
8
|
-
description: 'Create a new task in current room',
|
|
9
|
-
aliases: ['newtask', 'addtask'],
|
|
10
|
-
requiresAuth: true,
|
|
11
|
-
requiresRoom: true,
|
|
12
|
-
|
|
13
|
-
async execute(args, shell) {
|
|
14
|
-
const { input, select, confirm } = require('@inquirer/prompts');
|
|
15
|
-
const roomId = shell.ws.getCurrentRoom();
|
|
16
|
-
|
|
17
|
-
try {
|
|
18
|
-
// Fetch columns of current room's board
|
|
19
|
-
const { columns } = await shell.api._request('GET', `/thinknsh/${roomId}/cli/columns`);
|
|
20
|
-
if (!columns?.length) {
|
|
21
|
-
console.log(chalk.red('❌ No columns found in this room\'s board'));
|
|
22
|
-
return;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
console.log(chalk.cyan('\n // create-task\n'));
|
|
26
|
-
|
|
27
|
-
const title = await input({
|
|
28
|
-
message: 'Task title:',
|
|
29
|
-
validate: v => v.trim().length >= 3 || 'Min 3 characters'
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
const description = await input({ message: 'Description (optional):' });
|
|
33
|
-
|
|
34
|
-
const columnId = await select({
|
|
35
|
-
message: 'Column:',
|
|
36
|
-
choices: columns.map(c => ({ name: c.title, value: c._id }))
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
const priority = await select({
|
|
40
|
-
message: 'Priority:',
|
|
41
|
-
choices: [
|
|
42
|
-
{ name: 'low', value: 'low' },
|
|
43
|
-
{ name: 'medium', value: 'medium' },
|
|
44
|
-
{ name: 'high', value: 'high' },
|
|
45
|
-
],
|
|
46
|
-
default: 'medium'
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
const categories = [
|
|
50
|
-
'Bugs', 'Feature Requests', 'Improvements / Enhancements',
|
|
51
|
-
'Production Critical Issues', 'Security Issues', 'Performance Issues',
|
|
52
|
-
'UI/UX Issues', 'Testing & QA', 'DevOps & Infrastructure',
|
|
53
|
-
'Refactoring & Code Quality', 'Documentation', 'Other'
|
|
54
|
-
];
|
|
55
|
-
const category = await select({
|
|
56
|
-
message: 'Category:',
|
|
57
|
-
choices: categories.map(c => ({ name: c, value: c }))
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
const dueDateStr = await input({
|
|
61
|
-
message: 'Due date (YYYY-MM-DD, optional):',
|
|
62
|
-
validate: v => {
|
|
63
|
-
if (!v) return true;
|
|
64
|
-
return !isNaN(Date.parse(v)) || 'Invalid date format';
|
|
65
|
-
}
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
const confirmed = await confirm({ message: 'Create task?', default: true });
|
|
69
|
-
if (!confirmed) { console.log(chalk.dim(' Cancelled.')); return; }
|
|
70
|
-
|
|
71
|
-
const payload = {
|
|
72
|
-
title: title.trim(),
|
|
73
|
-
description: description.trim() || undefined,
|
|
74
|
-
columnId,
|
|
75
|
-
priority,
|
|
76
|
-
category: category === 'Other' ? undefined : category,
|
|
77
|
-
dueDate: dueDateStr ? new Date(dueDateStr).toISOString() : undefined,
|
|
78
|
-
source: 'cli',
|
|
79
|
-
};
|
|
80
|
-
|
|
81
|
-
const result = await shell.api._request('POST', `/thinknsh/${roomId}/cli/tasks`, payload);
|
|
82
|
-
|
|
83
|
-
console.log(chalk.green(`\n✅ Task created: "${result.task.title}"`));
|
|
84
|
-
console.log(chalk.dim(` ID: ${result.task._id}`));
|
|
85
|
-
console.log(chalk.dim(` Column: ${result.task.columnId?.title || columnId}\n`));
|
|
86
|
-
|
|
87
|
-
shell.pushNotification({
|
|
88
|
-
type: 'notification',
|
|
89
|
-
level: 'success',
|
|
90
|
-
title: 'Task Created',
|
|
91
|
-
message: result.task.title,
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
} catch (err) {
|
|
95
|
-
if (err.message?.includes('403'))
|
|
96
|
-
console.log(chalk.red('❌ You are not a member of this room'));
|
|
97
|
-
else
|
|
98
|
-
console.log(chalk.red(`❌ ${err.message}`));
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
};
|
|
@@ -1,101 +0,0 @@
|
|
|
1
|
-
const chalk = require('chalk');
|
|
2
|
-
const path = require('path');
|
|
3
|
-
const fs = require('fs-extra');
|
|
4
|
-
const { runLoadTest } = require('../../core/geekload');
|
|
5
|
-
const Table = require('cli-table3');
|
|
6
|
-
|
|
7
|
-
module.exports = {
|
|
8
|
-
name: 'loadtest',
|
|
9
|
-
description: 'Run a local load test script using GeekLoad and sync stats to TNC',
|
|
10
|
-
aliases: ['geekload'],
|
|
11
|
-
requiresAuth: true,
|
|
12
|
-
requiresRoom: true,
|
|
13
|
-
|
|
14
|
-
async execute(args, shell) {
|
|
15
|
-
if (!args[0]) {
|
|
16
|
-
console.log(chalk.red('Usage: loadtest <script-path>'));
|
|
17
|
-
return;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
const scriptPath = path.resolve(process.cwd(), args[0]);
|
|
21
|
-
if (!fs.existsSync(scriptPath)) {
|
|
22
|
-
console.log(chalk.red(`❌ Script not found: ${args[0]}`));
|
|
23
|
-
return;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
console.log(chalk.cyan(`\n📋 Loading test script: ${args[0]}`));
|
|
27
|
-
|
|
28
|
-
try {
|
|
29
|
-
const results = await runLoadTest(scriptPath);
|
|
30
|
-
|
|
31
|
-
const table = new Table({
|
|
32
|
-
head: [chalk.green('Metric'), chalk.green('Value')],
|
|
33
|
-
colWidths: [25, 20]
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
table.push(
|
|
37
|
-
['Virtual Users', results.virtual_users],
|
|
38
|
-
['Duration (sec)', results.duration_sec],
|
|
39
|
-
['Total Requests', results.total_requests],
|
|
40
|
-
['Failed Requests', results.failed_requests],
|
|
41
|
-
['Requests/sec (RPS)', results.requests_per_sec],
|
|
42
|
-
['Failure Rate', `${(results.failure_rate * 100).toFixed(2)}%`],
|
|
43
|
-
['Average Latency (ms)', results.avg_latency_ms],
|
|
44
|
-
['p95 Latency (ms)', results.p95_latency_ms],
|
|
45
|
-
['p99 Latency (ms)', results.p99_latency_ms]
|
|
46
|
-
);
|
|
47
|
-
|
|
48
|
-
console.log(table.toString());
|
|
49
|
-
|
|
50
|
-
// Sync stats back to ThinkNCollab backend
|
|
51
|
-
const roomId = shell.ws.getCurrentRoom();
|
|
52
|
-
if (roomId) {
|
|
53
|
-
process.stdout.write(chalk.dim(' Syncing stats to ThinkNCollab server...\r'));
|
|
54
|
-
try {
|
|
55
|
-
const stats = {
|
|
56
|
-
targetUrl: args[0],
|
|
57
|
-
duration: results.duration_sec,
|
|
58
|
-
connections: results.virtual_users,
|
|
59
|
-
requestsTotal: results.total_requests,
|
|
60
|
-
throughputTotalBytes: results.total_requests * 512,
|
|
61
|
-
latency: {
|
|
62
|
-
p50: results.avg_latency_ms,
|
|
63
|
-
p90: results.p95_latency_ms,
|
|
64
|
-
p99: results.p99_latency_ms,
|
|
65
|
-
average: results.avg_latency_ms,
|
|
66
|
-
min: results.avg_latency_ms * 0.5,
|
|
67
|
-
max: results.avg_latency_ms * 1.5
|
|
68
|
-
},
|
|
69
|
-
errors: results.failed_requests
|
|
70
|
-
};
|
|
71
|
-
|
|
72
|
-
await shell.api._request('POST', `/tncloadtest/api/save-run/${roomId}`, {
|
|
73
|
-
targetUrl: args[0],
|
|
74
|
-
duration: results.duration_sec,
|
|
75
|
-
connections: results.virtual_users,
|
|
76
|
-
stats
|
|
77
|
-
});
|
|
78
|
-
console.log(chalk.green(' ✅ Load test stats successfully synced to workspace!'));
|
|
79
|
-
} catch (syncErr) {
|
|
80
|
-
console.log(chalk.red(` ⚠️ Failed to sync stats to server: ${syncErr.message}`));
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
if (results.failures.length > 0) {
|
|
85
|
-
console.log(chalk.yellow(`\n⚠️ Recorded Failures (${results.failures.length}):`));
|
|
86
|
-
results.failures.slice(0, 5).forEach((f, idx) => {
|
|
87
|
-
console.log(chalk.dim(` ${idx + 1}. [${f.method}] ${f.url}`));
|
|
88
|
-
f.failures.forEach(msg => {
|
|
89
|
-
console.log(chalk.red(` - ${msg}`));
|
|
90
|
-
});
|
|
91
|
-
});
|
|
92
|
-
if (results.failures.length > 5) {
|
|
93
|
-
console.log(chalk.dim(` ... and ${results.failures.length - 5} more failures`));
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
} catch (err) {
|
|
98
|
-
console.log(chalk.red(`❌ Load test execution failed: ${err.message}`));
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
};
|
|
File without changes
|
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
// src/commands/tasks/task-status.js
|
|
2
|
-
const chalk = require('chalk');
|
|
3
|
-
|
|
4
|
-
const STATUS_MAP = {
|
|
5
|
-
accept: 'accepted',
|
|
6
|
-
start: 'inprogress',
|
|
7
|
-
complete: 'completed',
|
|
8
|
-
reject: 'rejected',
|
|
9
|
-
done: 'completed',
|
|
10
|
-
'task-status': null, // requires explicit arg
|
|
11
|
-
};
|
|
12
|
-
|
|
13
|
-
module.exports = {
|
|
14
|
-
name: 'task-status',
|
|
15
|
-
description: 'Update your task status',
|
|
16
|
-
aliases: ['accept', 'start', 'complete', 'reject', 'done'],
|
|
17
|
-
requiresAuth: true,
|
|
18
|
-
requiresRoom: true,
|
|
19
|
-
|
|
20
|
-
async execute(args, shell, invokedAs) {
|
|
21
|
-
const roomId = shell.ws.getCurrentRoom(); // ← top pe, ek baar
|
|
22
|
-
const taskId = args[0];
|
|
23
|
-
const verb = invokedAs || shell._lastCommand || 'task-status';
|
|
24
|
-
const status = STATUS_MAP[verb] ?? args[1];
|
|
25
|
-
|
|
26
|
-
if (!taskId) {
|
|
27
|
-
console.log(chalk.yellow(' Usage: accept <taskId> | start <taskId> | complete <taskId>'));
|
|
28
|
-
return;
|
|
29
|
-
}
|
|
30
|
-
if (!status) {
|
|
31
|
-
console.log(chalk.yellow(' Usage: task-status <taskId> <accepted|inprogress|completed|rejected>'));
|
|
32
|
-
return;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
try {
|
|
36
|
-
const result = await shell.api._request(
|
|
37
|
-
'POST',
|
|
38
|
-
`/thinknsh/${roomId}/tasks/${taskId}/status`,
|
|
39
|
-
{ status }
|
|
40
|
-
);
|
|
41
|
-
|
|
42
|
-
const statusColors = {
|
|
43
|
-
accepted: chalk.green,
|
|
44
|
-
inprogress: chalk.blue,
|
|
45
|
-
completed: chalk.green,
|
|
46
|
-
rejected: chalk.red,
|
|
47
|
-
};
|
|
48
|
-
|
|
49
|
-
const col = statusColors[status] || chalk.white;
|
|
50
|
-
console.log(col(`\n ✓ Task "${result.taskTitle}" → ${status}`));
|
|
51
|
-
|
|
52
|
-
if (result.allCompleted) {
|
|
53
|
-
console.log(chalk.green(' 🎉 All assignees done — task fully completed!\n'));
|
|
54
|
-
} else {
|
|
55
|
-
console.log('');
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
// Socket emit
|
|
59
|
-
if (shell.ws.isConnected?.() && shell.ws.socket) {
|
|
60
|
-
shell.ws.socket.emit('task:' + (status === 'inprogress' ? 'started' : status), {
|
|
61
|
-
roomId,
|
|
62
|
-
taskId,
|
|
63
|
-
taskTitle: result.taskTitle,
|
|
64
|
-
userId: shell.api.getUser()?._id,
|
|
65
|
-
});
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
} catch (err) {
|
|
69
|
-
console.log(chalk.red(`\n ❌ ${err.message}\n`));
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
};
|
|
@@ -1,182 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
src/commands/tasks/task-info.js — View task details + testConfig + webhook URL
|
|
3
|
-
Usage: task-info <taskId> OR info <taskId> load
|
|
4
|
-
*/
|
|
5
|
-
const chalk = require('chalk');
|
|
6
|
-
|
|
7
|
-
module.exports = {
|
|
8
|
-
name: 'task-info',
|
|
9
|
-
description: 'View full task details including test config and webhook URL',
|
|
10
|
-
aliases: ['task', 'info', 'taskInfo', '--t' ],
|
|
11
|
-
requiresAuth: true,
|
|
12
|
-
|
|
13
|
-
async execute(args, shell) {
|
|
14
|
-
if (!args[0]) {
|
|
15
|
-
console.log(chalk.red('Usage: task-info <task-id>'));
|
|
16
|
-
console.log(chalk.dim(' Alias: info <task-id>'));
|
|
17
|
-
return;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
const taskId = args[0];
|
|
21
|
-
|
|
22
|
-
let task;
|
|
23
|
-
try {
|
|
24
|
-
task = await shell.api._request('GET', `/thinknsh/${taskId}/cli`);
|
|
25
|
-
} catch (err) {
|
|
26
|
-
if (err.message?.includes('404')) console.log(chalk.red('❌ Task not found'));
|
|
27
|
-
else if (err.message?.includes('403')) console.log(chalk.red('❌ Access denied'));
|
|
28
|
-
else console.log(chalk.red(`❌ ${err.message}`));
|
|
29
|
-
return;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
const stColors = {
|
|
33
|
-
pending: chalk.yellow,
|
|
34
|
-
inprogress: chalk.blue,
|
|
35
|
-
review: chalk.magenta,
|
|
36
|
-
completed: chalk.green,
|
|
37
|
-
reopened: chalk.cyan,
|
|
38
|
-
};
|
|
39
|
-
const prColors = { high: chalk.red, medium: chalk.yellow, low: chalk.green };
|
|
40
|
-
const stColor = stColors[task.status] || chalk.white;
|
|
41
|
-
const prColor = prColors[task.priority] || chalk.white;
|
|
42
|
-
|
|
43
|
-
// ── Basic Info ────────────────────────────────────────────────────────────
|
|
44
|
-
console.log(chalk.cyan('\n ─── Task Info ───────────────────────────────────'));
|
|
45
|
-
console.log(` ${chalk.dim('id ')} ${chalk.dim(task._id)}${task.taskID ? chalk.cyan(' #' + task.taskID) : ''}`);
|
|
46
|
-
console.log(` ${chalk.dim('title ')} ${chalk.bold(task.title)}`);
|
|
47
|
-
console.log(` ${chalk.dim('status ')} ${stColor(task.status)}`);
|
|
48
|
-
console.log(` ${chalk.dim('priority')} ${prColor(task.priority)}`);
|
|
49
|
-
if (task.category)
|
|
50
|
-
console.log(` ${chalk.dim('category')} ${chalk.dim(task.category)}`);
|
|
51
|
-
if (task.dueDate)
|
|
52
|
-
console.log(` ${chalk.dim('due ')} ${chalk.yellow(new Date(task.dueDate).toLocaleDateString())}`);
|
|
53
|
-
if (task.description) {
|
|
54
|
-
console.log(chalk.dim('\n description:'));
|
|
55
|
-
task.description.split('\n').forEach(l => console.log(` ${chalk.white(l)}`));
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
if (task.referenceLinks && task.referenceLinks.length > 0) {
|
|
59
|
-
console.log(chalk.cyan('\n ─── Supported Links ─────────────────────────────'));
|
|
60
|
-
task.referenceLinks.forEach(link => {
|
|
61
|
-
console.log(` ${chalk.dim(link.title.padEnd(12))} ${chalk.blue(link.url)}`);
|
|
62
|
-
});
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
// ── Assignees ─────────────────────────────────────────────────────────────
|
|
66
|
-
if (task.assignedTo?.length) {
|
|
67
|
-
console.log(chalk.dim('\n assignees:'));
|
|
68
|
-
task.assignedTo.forEach(a => {
|
|
69
|
-
const name = a.userId?.name || 'Unknown';
|
|
70
|
-
const stBadge = a.status === 'completed' ? chalk.green('✓') :
|
|
71
|
-
a.status === 'accepted' ? chalk.blue('●') :
|
|
72
|
-
a.status === 'rejected' ? chalk.red('✗') : chalk.dim('○');
|
|
73
|
-
console.log(` ${stBadge} ${name} ${chalk.dim(`[${a.status}]`)}`);
|
|
74
|
-
});
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
// ── Test Config ───────────────────────────────────────────────────────────
|
|
78
|
-
if (task.testConfig?.type) {
|
|
79
|
-
const tc = task.testConfig;
|
|
80
|
-
|
|
81
|
-
console.log(chalk.cyan('\n ─── Test Config ─────────────────────────────────'));
|
|
82
|
-
console.log(` ${chalk.dim('type ')} ${chalk.bold(tc.type)}`);
|
|
83
|
-
|
|
84
|
-
if (tc.type === 'http') {
|
|
85
|
-
console.log(` ${chalk.dim('method ')} ${chalk.green(tc.method || 'GET')}`);
|
|
86
|
-
console.log(` ${chalk.dim('target ')} ${chalk.white((tc.baseUrl || '') + (tc.endpoint || ''))}`);
|
|
87
|
-
if (tc.body && Object.keys(tc.body).length) {
|
|
88
|
-
console.log(` ${chalk.dim('body ')} ${chalk.dim(JSON.stringify(tc.body))}`);
|
|
89
|
-
}
|
|
90
|
-
if (tc.expect) {
|
|
91
|
-
console.log(chalk.dim('\n expect:'));
|
|
92
|
-
if (tc.expect.status !== undefined)
|
|
93
|
-
console.log(` ${chalk.dim('status')} ${chalk.green(tc.expect.status)}`);
|
|
94
|
-
if (tc.expect.body) {
|
|
95
|
-
Object.entries(tc.expect.body).forEach(([key, condition]) => {
|
|
96
|
-
const condStr = typeof condition === 'object'
|
|
97
|
-
? Object.entries(condition).map(([op, val]) => `${op}: ${val}`).join(', ')
|
|
98
|
-
: String(condition);
|
|
99
|
-
console.log(` ${chalk.dim('body.' + key)} ${chalk.cyan(condStr)}`);
|
|
100
|
-
});
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
if (tc.type === 'browser') {
|
|
106
|
-
console.log(` ${chalk.dim('url ')} ${chalk.white(tc.url || '')}`);
|
|
107
|
-
if (tc.flow?.length) {
|
|
108
|
-
console.log(chalk.dim('\n flow:'));
|
|
109
|
-
tc.flow.forEach((step, i) => {
|
|
110
|
-
console.log(` ${chalk.dim(i + 1 + '.')} ${chalk.green(step.action)} ${chalk.dim(step.selector || '')} ${step.value ? chalk.white(`"${step.value}"`) : ''}`);
|
|
111
|
-
});
|
|
112
|
-
}
|
|
113
|
-
if (tc.expect) {
|
|
114
|
-
console.log(chalk.dim('\n expect:'));
|
|
115
|
-
if (tc.expect.element)
|
|
116
|
-
console.log(` ${chalk.dim('element ')} ${chalk.cyan(tc.expect.element)}`);
|
|
117
|
-
if (tc.expect.text)
|
|
118
|
-
console.log(` ${chalk.dim('text ')} ${chalk.cyan(`contains "${tc.expect.text}"`)}`);
|
|
119
|
-
if (tc.expect.redirectUrl)
|
|
120
|
-
console.log(` ${chalk.dim('redirectUrl')} ${chalk.cyan(tc.expect.redirectUrl)}`);
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
if (tc.type === 'json') {
|
|
125
|
-
if (tc.expect?.condition) {
|
|
126
|
-
console.log(chalk.dim('\n conditions:'));
|
|
127
|
-
Object.entries(tc.expect.condition).forEach(([key, ops]) => {
|
|
128
|
-
const condStr = Object.entries(ops).map(([op, val]) => `${op} ${val}`).join(', ');
|
|
129
|
-
console.log(` ${chalk.dim(key)} ${chalk.cyan(condStr)}`);
|
|
130
|
-
});
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
if (tc.type === 'exitCode')
|
|
135
|
-
console.log(` ${chalk.dim('expect ')} exitCode ${chalk.green(tc.expect?.exitCode ?? 0)}`);
|
|
136
|
-
|
|
137
|
-
if (tc.type === 'stdout')
|
|
138
|
-
console.log(` ${chalk.dim('expect ')} stdout "${chalk.green(tc.expect?.stdout || '')}"`);
|
|
139
|
-
|
|
140
|
-
if (tc.type === 'regex')
|
|
141
|
-
console.log(` ${chalk.dim('expect ')} regex ${chalk.green(tc.expect?.regex || '')}`);
|
|
142
|
-
|
|
143
|
-
if (tc.type === 'tnc-build')
|
|
144
|
-
console.log(` ${chalk.dim('runner ')} ${chalk.green('Local tnc-ci.yml workflow execution')}`);
|
|
145
|
-
|
|
146
|
-
} else {
|
|
147
|
-
console.log(chalk.yellow('\n ⚠️ No test config — task will need manual completion'));
|
|
148
|
-
console.log(chalk.dim(' Use: complete <task-id>'));
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
// ── Webhook ───────────────────────────────────────────────────────────────
|
|
152
|
-
if (task.webhookSecret) {
|
|
153
|
-
const apiUrl = shell.api.apiUrl || 'https://thinkncollab.com';
|
|
154
|
-
const webhookUrl = `${apiUrl}/webhooks/tasks/${taskId}/judge`;
|
|
155
|
-
|
|
156
|
-
console.log(chalk.cyan('\n ─── Webhook ─────────────────────────────────────'));
|
|
157
|
-
console.log(` ${chalk.dim('url ')} ${chalk.white(webhookUrl)}`);
|
|
158
|
-
console.log(` ${chalk.dim('secret ')} ${chalk.dim(task.webhookSecret)}`);
|
|
159
|
-
console.log(` ${chalk.dim('header ')} ${chalk.dim('x-tnc-signature: sha256=<HMAC of body>')}`);
|
|
160
|
-
console.log(chalk.dim('\n To submit from your project:'));
|
|
161
|
-
console.log(chalk.cyan(' submit ' + taskId));
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
// ── Last Verdict ──────────────────────────────────────────────────────────
|
|
165
|
-
if (task.lastVerdict?.at) {
|
|
166
|
-
const v = task.lastVerdict;
|
|
167
|
-
const passed = v.passed;
|
|
168
|
-
const badge = passed ? chalk.bgGreen.black(' PASS ') : chalk.bgRed.white(' FAIL ');
|
|
169
|
-
const ts = new Date(v.at).toLocaleString();
|
|
170
|
-
|
|
171
|
-
console.log(chalk.cyan('\n ─── Last Verdict ────────────────────────────────'));
|
|
172
|
-
console.log(` ${badge} ${passed ? chalk.green(v.reason) : chalk.red(v.reason)}`);
|
|
173
|
-
console.log(` ${chalk.dim('at')} ${chalk.dim(ts)}`);
|
|
174
|
-
if (v.diff) {
|
|
175
|
-
console.log(` ${chalk.dim('expected')} ${chalk.cyan(JSON.stringify(v.diff.expected ?? v.diff.conditions))}`);
|
|
176
|
-
console.log(` ${chalk.dim('actual ')} ${chalk.yellow(JSON.stringify(v.diff.actual))}`);
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
console.log(chalk.cyan(' ─────────────────────────────────────────────────\n'));
|
|
181
|
-
}
|
|
182
|
-
};
|
|
@@ -1,184 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
src/commands/tasks/tasks.js — List my tasks + suggestions in current room
|
|
3
|
-
*/
|
|
4
|
-
const chalk = require('chalk');
|
|
5
|
-
|
|
6
|
-
module.exports = {
|
|
7
|
-
name: 'tasks',
|
|
8
|
-
description: 'List your tasks + suggested tasks in the current room',
|
|
9
|
-
aliases: ['mytasks', 'my-tasks'],
|
|
10
|
-
requiresAuth: true,
|
|
11
|
-
requiresRoom: true,
|
|
12
|
-
|
|
13
|
-
async execute(args, shell) {
|
|
14
|
-
const user = shell.api.getUser();
|
|
15
|
-
const roomId = shell.ws.getCurrentRoom();
|
|
16
|
-
|
|
17
|
-
if (!user) { console.log(chalk.red('❌ Please login first')); return; }
|
|
18
|
-
if (!roomId) { console.log(chalk.yellow('⚠️ Join a room first: join <room-id>')); return; }
|
|
19
|
-
|
|
20
|
-
// ── Flags ────────────────────────────────────────────────────────────
|
|
21
|
-
const showAll = args.includes('--all') || args.includes('-a');
|
|
22
|
-
const noSuggestions = args.includes('--no-suggest') || args.includes('-ns');
|
|
23
|
-
const filterStatus = args.find(a => ['pending','inprogress','review','completed','reopened'].includes(a));
|
|
24
|
-
const topN = args.find(a => /^\d+$/.test(a)) || 5;
|
|
25
|
-
|
|
26
|
-
// ── Color maps ───────────────────────────────────────────────────────
|
|
27
|
-
const stColors = {
|
|
28
|
-
pending: chalk.yellow,
|
|
29
|
-
inprogress: chalk.blue,
|
|
30
|
-
review: chalk.magenta,
|
|
31
|
-
completed: chalk.green,
|
|
32
|
-
reopened: chalk.cyan,
|
|
33
|
-
};
|
|
34
|
-
const prColors = {
|
|
35
|
-
critical: chalk.red,
|
|
36
|
-
high: chalk.hex('#FF6B35'),
|
|
37
|
-
medium: chalk.yellow,
|
|
38
|
-
low: chalk.green,
|
|
39
|
-
};
|
|
40
|
-
const scoreColor = s => s >= 80 ? chalk.red(s) : s >= 60 ? chalk.yellow(s) : chalk.green(s);
|
|
41
|
-
const plColor = pl => ({
|
|
42
|
-
CRITICAL: chalk.red,
|
|
43
|
-
HIGH: chalk.hex('#FF6B35'),
|
|
44
|
-
MEDIUM: chalk.yellow,
|
|
45
|
-
LOW: chalk.green,
|
|
46
|
-
'VERY LOW': chalk.dim,
|
|
47
|
-
}[pl] || chalk.white);
|
|
48
|
-
|
|
49
|
-
// ── Helpers ──────────────────────────────────────────────────────────
|
|
50
|
-
const fmtDue = dueDate => {
|
|
51
|
-
if (!dueDate) return '';
|
|
52
|
-
const d = new Date(dueDate);
|
|
53
|
-
const days = Math.ceil((d - new Date().setHours(0,0,0,0)) / 86400000);
|
|
54
|
-
const str = d.toLocaleDateString('en-IN', { day:'2-digit', month:'short' });
|
|
55
|
-
if (days < 0) return chalk.red(` due:${str}(!)`);
|
|
56
|
-
if (days === 0) return chalk.yellow(` due:TODAY`);
|
|
57
|
-
if (days === 1) return chalk.yellow(` due:${str}`);
|
|
58
|
-
return chalk.dim(` due:${str}`);
|
|
59
|
-
};
|
|
60
|
-
|
|
61
|
-
const divider = (color = chalk.cyan) => color(' ' + '─'.repeat(58));
|
|
62
|
-
const subline = (label, val) =>
|
|
63
|
-
` ${chalk.dim(label+':')} ${chalk.dim(val)}`;
|
|
64
|
-
|
|
65
|
-
// ── Fetch ────────────────────────────────────────────────────────────
|
|
66
|
-
process.stdout.write(chalk.dim(' Fetching tasks...\r'));
|
|
67
|
-
|
|
68
|
-
let result;
|
|
69
|
-
try {
|
|
70
|
-
result = await shell.api._request('GET', `/thinknsh/${roomId}/tasks?n=${topN}`);
|
|
71
|
-
} catch (err) {
|
|
72
|
-
console.log(chalk.red(`\n ❌ ${err.message}\n`));
|
|
73
|
-
return;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
process.stdout.write(' '.repeat(30) + '\r'); // clear spinner line
|
|
77
|
-
|
|
78
|
-
// ════════════════════════════════════════════════════════════════════
|
|
79
|
-
// MY TASKS
|
|
80
|
-
// ════════════════════════════════════════════════════════════════════
|
|
81
|
-
let tasks = result.tasks || [];
|
|
82
|
-
|
|
83
|
-
if (filterStatus) tasks = tasks.filter(t => t.status === filterStatus);
|
|
84
|
-
if (!showAll) tasks = tasks.filter(t => !['completed','done'].includes(t.status));
|
|
85
|
-
|
|
86
|
-
console.log(divider());
|
|
87
|
-
console.log(chalk.cyan(` MY TASKS`) + chalk.dim(` (${tasks.length} active${result.total > tasks.length ? ` of ${result.total}` : ''})`));
|
|
88
|
-
console.log(divider());
|
|
89
|
-
|
|
90
|
-
if (!tasks.length) {
|
|
91
|
-
console.log(chalk.dim('\n No assigned tasks.\n'));
|
|
92
|
-
} else {
|
|
93
|
-
tasks.forEach((task, i) => {
|
|
94
|
-
const sc = stColors[task.status] || chalk.white;
|
|
95
|
-
const pc = prColors[task.priority] || chalk.white;
|
|
96
|
-
|
|
97
|
-
const num = chalk.dim(` ${String(i+1).padStart(2)}.`);
|
|
98
|
-
const status = sc(`[${(task.status || 'unknown').padEnd(10)}]`);
|
|
99
|
-
const title = task.status === 'completed'
|
|
100
|
-
? chalk.dim(task.title)
|
|
101
|
-
: chalk.white(task.title);
|
|
102
|
-
const due = fmtDue(task.dueDate);
|
|
103
|
-
const myStatus = task.myStatus && task.myStatus !== task.status
|
|
104
|
-
? chalk.dim(` (${task.myStatus})`)
|
|
105
|
-
: '';
|
|
106
|
-
const col = task.column ? chalk.dim(` [${task.column}]`) : '';
|
|
107
|
-
|
|
108
|
-
console.log(`\n${num} ${status} ${title}`);
|
|
109
|
-
|
|
110
|
-
console.log(subline('id', `${task.taskID ? '#' + task.taskID : task._id}`) +
|
|
111
|
-
` ${chalk.dim('priority:')} ${pc(task.priority)}` +
|
|
112
|
-
due + myStatus + col);
|
|
113
|
-
|
|
114
|
-
if (task.description) {
|
|
115
|
-
const desc = task.description.length > 90
|
|
116
|
-
? task.description.slice(0, 87) + '...'
|
|
117
|
-
: task.description;
|
|
118
|
-
console.log(` ${chalk.dim(desc)}`);
|
|
119
|
-
}
|
|
120
|
-
});
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
// ════════════════════════════════════════════════════════════════════
|
|
124
|
-
// SUGGESTIONS
|
|
125
|
-
// ════════════════════════════════════════════════════════════════════
|
|
126
|
-
if (!noSuggestions) {
|
|
127
|
-
const suggestions = result.suggestions?.tasks || [];
|
|
128
|
-
const insights = result.suggestions?.insights || {};
|
|
129
|
-
const meta = result.suggestions?.metadata || {};
|
|
130
|
-
|
|
131
|
-
console.log('\n' + divider(chalk.magenta));
|
|
132
|
-
console.log(chalk.magenta(` SUGGESTED FOR YOU`) +
|
|
133
|
-
chalk.dim(` (top ${suggestions.length}${meta.totalScored ? ` of ${meta.totalScored} available` : ''})`));
|
|
134
|
-
console.log(divider(chalk.magenta));
|
|
135
|
-
|
|
136
|
-
if (!suggestions.length) {
|
|
137
|
-
console.log(chalk.dim('\n No suggestions — all tasks are assigned.\n'));
|
|
138
|
-
} else {
|
|
139
|
-
suggestions.forEach((task, i) => {
|
|
140
|
-
const pc = prColors[task.priority] || chalk.white;
|
|
141
|
-
const plc = plColor(task.priorityLevel);
|
|
142
|
-
const due = fmtDue(task.dueDate);
|
|
143
|
-
const col = task.column ? chalk.dim(` [${task.column}]`) : '';
|
|
144
|
-
const assignees = task.assigneeCount
|
|
145
|
-
? chalk.dim(` · ${task.assigneeCount} assigned`)
|
|
146
|
-
: chalk.dim(' · unassigned');
|
|
147
|
-
const urgentTag = task.isOverdue
|
|
148
|
-
? chalk.red(' ⚠ OVERDUE')
|
|
149
|
-
: task.isUrgent ? chalk.yellow(' ⏰ URGENT') : '';
|
|
150
|
-
|
|
151
|
-
const num = chalk.dim(` ${String(i+1).padStart(2)}.`);
|
|
152
|
-
const score = `[score:${scoreColor(task.score)}]`;
|
|
153
|
-
const level = plc(`[${task.priorityLevel}]`);
|
|
154
|
-
|
|
155
|
-
console.log(`\n${num} ${score} ${level} ${chalk.white(task.title)}${urgentTag}`);
|
|
156
|
-
console.log(subline('id', task._id) +
|
|
157
|
-
` ${chalk.dim('priority:')} ${pc(task.priority)}` +
|
|
158
|
-
due + col + assignees);
|
|
159
|
-
|
|
160
|
-
if (task.priorityReason) {
|
|
161
|
-
console.log(` ${chalk.dim('↳ ' + task.priorityReason)}`);
|
|
162
|
-
}
|
|
163
|
-
});
|
|
164
|
-
|
|
165
|
-
// ── Insights bar ─────────────────────────────────────────────
|
|
166
|
-
console.log('\n' + divider(chalk.dim));
|
|
167
|
-
if (insights.focusRecommendation) {
|
|
168
|
-
console.log(` ${chalk.magenta('💡')} ${chalk.white(insights.focusRecommendation)}`);
|
|
169
|
-
}
|
|
170
|
-
if (insights.estimatedTime) {
|
|
171
|
-
console.log(` ${chalk.dim('⏱ Est. time:')} ${chalk.white(insights.estimatedTime)}`);
|
|
172
|
-
}
|
|
173
|
-
if (meta.scoreRange) {
|
|
174
|
-
console.log(` ${chalk.dim('📊 Score range:')} ${chalk.white(`${meta.scoreRange.min}–${meta.scoreRange.max}`)} ${chalk.dim('avg:')} ${chalk.white(meta.scoreRange.avg)}`);
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
// ── Footer ───────────────────────────────────────────────────────────
|
|
180
|
-
console.log('\n' + divider());
|
|
181
|
-
console.log(chalk.dim(' task <id> · complete <id> · tasks --all · tasks 10 · tasks --no-suggest'));
|
|
182
|
-
console.log(divider() + '\n');
|
|
183
|
-
}
|
|
184
|
-
};
|