@meltstudio/meltctl 4.16.0 → 4.17.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/README.md +3 -0
- package/dist/commands/standup.d.ts +7 -0
- package/dist/commands/standup.js +71 -0
- package/dist/index.js +11 -1
- package/dist/utils/auth.d.ts +1 -1
- package/dist/utils/auth.js +3 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import { input, editor } from '@inquirer/prompts';
|
|
3
|
+
import { isAuthenticated, authenticatedFetch } from '../utils/auth.js';
|
|
4
|
+
const EDITOR_HINT = chalk.dim('(type \\e to open your editor)');
|
|
5
|
+
async function promptField(message, required) {
|
|
6
|
+
const value = await input({ message: `${message} ${EDITOR_HINT}` });
|
|
7
|
+
if (value === '\\e') {
|
|
8
|
+
return editor({ message });
|
|
9
|
+
}
|
|
10
|
+
if (required && !value.trim()) {
|
|
11
|
+
console.log(chalk.yellow('This field is required.'));
|
|
12
|
+
return promptField(message, required);
|
|
13
|
+
}
|
|
14
|
+
return value;
|
|
15
|
+
}
|
|
16
|
+
export async function standupCommand(options) {
|
|
17
|
+
if (!(await isAuthenticated())) {
|
|
18
|
+
console.error(chalk.red('Not authenticated. Run `npx @meltstudio/meltctl@latest login` first.'));
|
|
19
|
+
process.exit(1);
|
|
20
|
+
}
|
|
21
|
+
// Check if already submitted today
|
|
22
|
+
try {
|
|
23
|
+
const statusRes = await authenticatedFetch('/standups/status');
|
|
24
|
+
if (statusRes.ok) {
|
|
25
|
+
const existing = (await statusRes.json());
|
|
26
|
+
console.log(chalk.yellow('\nYou already submitted a standup today:\n'));
|
|
27
|
+
console.log(chalk.bold('Yesterday:'), existing.yesterday);
|
|
28
|
+
console.log(chalk.bold('Today:'), existing.today);
|
|
29
|
+
if (existing.blockers) {
|
|
30
|
+
console.log(chalk.bold('Blockers:'), existing.blockers);
|
|
31
|
+
}
|
|
32
|
+
console.log(chalk.dim('\nTo edit your standup, use the Melt Connect frontend.'));
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
// Status check failed — continue to submission
|
|
38
|
+
}
|
|
39
|
+
let yesterday;
|
|
40
|
+
let today;
|
|
41
|
+
let blockers;
|
|
42
|
+
if (options.yesterday && options.today) {
|
|
43
|
+
// Hidden flags for E2E testing
|
|
44
|
+
yesterday = options.yesterday;
|
|
45
|
+
today = options.today;
|
|
46
|
+
blockers = options.blockers ?? '';
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
console.log(chalk.bold.cyan('\n Daily Standup Report\n'));
|
|
50
|
+
yesterday = await promptField('What did you work on yesterday?', true);
|
|
51
|
+
today = await promptField('What are you going to work on today?', true);
|
|
52
|
+
blockers = await promptField('Any blockers? (leave empty if none)', false);
|
|
53
|
+
}
|
|
54
|
+
const res = await authenticatedFetch('/standups', {
|
|
55
|
+
method: 'POST',
|
|
56
|
+
headers: { 'Content-Type': 'application/json' },
|
|
57
|
+
body: JSON.stringify({
|
|
58
|
+
yesterday,
|
|
59
|
+
today,
|
|
60
|
+
blockers: blockers || undefined,
|
|
61
|
+
}),
|
|
62
|
+
});
|
|
63
|
+
if (res.ok) {
|
|
64
|
+
console.log(chalk.green('\n ✓ Standup submitted!\n'));
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
const body = (await res.json());
|
|
68
|
+
console.error(chalk.red(`\nFailed to submit standup: ${body.error ?? res.statusText}`));
|
|
69
|
+
process.exit(1);
|
|
70
|
+
}
|
|
71
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import chalk from 'chalk';
|
|
3
|
-
import { Command } from '@commander-js/extra-typings';
|
|
3
|
+
import { Command, Option } from '@commander-js/extra-typings';
|
|
4
4
|
import { readFileSync } from 'fs';
|
|
5
5
|
import { join, dirname } from 'path';
|
|
6
6
|
import { fileURLToPath } from 'url';
|
|
@@ -11,6 +11,7 @@ import { logoutCommand } from './commands/logout.js';
|
|
|
11
11
|
import { printBanner } from './utils/banner.js';
|
|
12
12
|
import { checkAndEnforceUpdate } from './utils/version-check.js';
|
|
13
13
|
import { versionCheckCommand } from './commands/version.js';
|
|
14
|
+
import { standupCommand } from './commands/standup.js';
|
|
14
15
|
// Read version from package.json
|
|
15
16
|
const __filename = fileURLToPath(import.meta.url);
|
|
16
17
|
const __dirname = dirname(__filename);
|
|
@@ -55,6 +56,15 @@ project
|
|
|
55
56
|
.action(async () => {
|
|
56
57
|
await templatesCommand();
|
|
57
58
|
});
|
|
59
|
+
program
|
|
60
|
+
.command('standup')
|
|
61
|
+
.description('submit your daily standup report')
|
|
62
|
+
.addOption(new Option('--yesterday <text>').hideHelp())
|
|
63
|
+
.addOption(new Option('--today <text>').hideHelp())
|
|
64
|
+
.addOption(new Option('--blockers <text>').hideHelp())
|
|
65
|
+
.action(async (options) => {
|
|
66
|
+
await standupCommand(options);
|
|
67
|
+
});
|
|
58
68
|
program
|
|
59
69
|
.command('version')
|
|
60
70
|
.description('show current version')
|
package/dist/utils/auth.d.ts
CHANGED
|
@@ -8,5 +8,5 @@ export declare function getStoredAuth(): Promise<StoredAuth | undefined>;
|
|
|
8
8
|
export declare function storeAuth(auth: StoredAuth): Promise<void>;
|
|
9
9
|
export declare function clearAuth(): Promise<void>;
|
|
10
10
|
export declare function isAuthenticated(): Promise<boolean>;
|
|
11
|
-
export declare function authenticatedFetch(urlPath: string): Promise<Response>;
|
|
11
|
+
export declare function authenticatedFetch(urlPath: string, options?: RequestInit): Promise<Response>;
|
|
12
12
|
export {};
|
package/dist/utils/auth.js
CHANGED
|
@@ -32,7 +32,7 @@ export async function isAuthenticated() {
|
|
|
32
32
|
}
|
|
33
33
|
return new Date(auth.expiresAt) > new Date();
|
|
34
34
|
}
|
|
35
|
-
export async function authenticatedFetch(urlPath) {
|
|
35
|
+
export async function authenticatedFetch(urlPath, options = {}) {
|
|
36
36
|
const auth = await getStoredAuth();
|
|
37
37
|
if (!auth) {
|
|
38
38
|
throw new Error('Not authenticated. Run `npx @meltstudio/meltctl@latest login` first.');
|
|
@@ -41,8 +41,10 @@ export async function authenticatedFetch(urlPath) {
|
|
|
41
41
|
throw new Error('Session expired. Run `npx @meltstudio/meltctl@latest login` to re-authenticate.');
|
|
42
42
|
}
|
|
43
43
|
const response = await fetch(`${API_BASE}${urlPath}`, {
|
|
44
|
+
...options,
|
|
44
45
|
headers: {
|
|
45
46
|
Authorization: `Bearer ${auth.token}`,
|
|
47
|
+
...options.headers,
|
|
46
48
|
},
|
|
47
49
|
});
|
|
48
50
|
if (response.status === 401) {
|
package/package.json
CHANGED