@meltstudio/meltctl 4.16.0 → 4.18.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 CHANGED
@@ -28,6 +28,9 @@ meltctl project init --force
28
28
  # Fetch latest templates (used by /melt-update)
29
29
  meltctl project templates
30
30
 
31
+ # Submit daily standup report
32
+ meltctl standup
33
+
31
34
  # Other commands
32
35
  meltctl logout
33
36
  meltctl version --check
@@ -0,0 +1,7 @@
1
+ interface StandupOptions {
2
+ yesterday?: string;
3
+ today?: string;
4
+ blockers?: string;
5
+ }
6
+ export declare function standupCommand(options: StandupOptions): Promise<void>;
7
+ export {};
@@ -0,0 +1,78 @@
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
+ try {
9
+ return await editor({ message });
10
+ }
11
+ catch {
12
+ console.log(chalk.yellow('Editor failed to open. Falling back to inline input.'));
13
+ return promptField(message, required);
14
+ }
15
+ }
16
+ if (required && !value.trim()) {
17
+ console.log(chalk.yellow('This field is required.'));
18
+ return promptField(message, required);
19
+ }
20
+ return value;
21
+ }
22
+ export async function standupCommand(options) {
23
+ if (!(await isAuthenticated())) {
24
+ console.error(chalk.red('Not authenticated. Run `npx @meltstudio/meltctl@latest login` first.'));
25
+ process.exit(1);
26
+ }
27
+ // Check if already submitted today
28
+ try {
29
+ const statusRes = await authenticatedFetch('/standups/status');
30
+ if (statusRes.ok) {
31
+ const existing = (await statusRes.json());
32
+ console.log(chalk.yellow('\nYou already submitted a standup today:\n'));
33
+ console.log(chalk.bold('Yesterday:'), existing.yesterday);
34
+ console.log(chalk.bold('Today:'), existing.today);
35
+ if (existing.blockers) {
36
+ console.log(chalk.bold('Blockers:'), existing.blockers);
37
+ }
38
+ console.log(chalk.dim('\nTo edit your standup, use the Melt Connect frontend.'));
39
+ return;
40
+ }
41
+ }
42
+ catch {
43
+ // Status check failed — continue to submission
44
+ }
45
+ let yesterday;
46
+ let today;
47
+ let blockers;
48
+ if (options.yesterday && options.today) {
49
+ // Hidden flags for E2E testing
50
+ yesterday = options.yesterday;
51
+ today = options.today;
52
+ blockers = options.blockers ?? '';
53
+ }
54
+ else {
55
+ console.log(chalk.bold.cyan('\n Daily Standup Report\n'));
56
+ console.log(chalk.dim(' Tip: Mention tickets, PRs, or features by name.\n'));
57
+ yesterday = await promptField('What did you work on yesterday?', true);
58
+ today = await promptField('What are you going to work on today?', true);
59
+ blockers = await promptField('Any blockers? (leave empty if none)', false);
60
+ }
61
+ const res = await authenticatedFetch('/standups', {
62
+ method: 'POST',
63
+ headers: { 'Content-Type': 'application/json' },
64
+ body: JSON.stringify({
65
+ yesterday,
66
+ today,
67
+ blockers: blockers || undefined,
68
+ }),
69
+ });
70
+ if (res.ok) {
71
+ console.log(chalk.green('\n ✓ Standup submitted!\n'));
72
+ }
73
+ else {
74
+ const body = (await res.json());
75
+ console.error(chalk.red(`\nFailed to submit standup: ${body.error ?? res.statusText}`));
76
+ process.exit(1);
77
+ }
78
+ }
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')
@@ -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 {};
@@ -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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@meltstudio/meltctl",
3
- "version": "4.16.0",
3
+ "version": "4.18.0",
4
4
  "description": "AI-first development tools for teams - set up AGENTS.md, Claude Code, Cursor, and Copilot standards",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",