@ginkoai/cli 2.0.0-beta.2 → 2.0.0-beta.4
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/dist/commands/invite/index.d.ts +22 -0
- package/dist/commands/invite/index.d.ts.map +1 -0
- package/dist/commands/invite/index.js +192 -0
- package/dist/commands/invite/index.js.map +1 -0
- package/dist/commands/join/index.d.ts +22 -0
- package/dist/commands/join/index.d.ts.map +1 -0
- package/dist/commands/join/index.js +177 -0
- package/dist/commands/join/index.js.map +1 -0
- package/dist/commands/sync/index.d.ts +2 -1
- package/dist/commands/sync/index.d.ts.map +1 -1
- package/dist/commands/sync/index.js +6 -1
- package/dist/commands/sync/index.js.map +1 -1
- package/dist/commands/sync/sync-command.d.ts +2 -2
- package/dist/commands/sync/sync-command.d.ts.map +1 -1
- package/dist/commands/sync/sync-command.js +30 -1
- package/dist/commands/sync/sync-command.js.map +1 -1
- package/dist/commands/sync/team-sync.d.ts +32 -0
- package/dist/commands/sync/team-sync.d.ts.map +1 -0
- package/dist/commands/sync/team-sync.js +194 -0
- package/dist/commands/sync/team-sync.js.map +1 -0
- package/dist/commands/sync/types.d.ts +34 -0
- package/dist/commands/sync/types.d.ts.map +1 -1
- package/dist/commands/team/members.d.ts +3 -3
- package/dist/commands/team/members.d.ts.map +1 -1
- package/dist/commands/team/members.js +75 -12
- package/dist/commands/team/members.js.map +1 -1
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -1
- package/dist/utils/api-client.d.ts +1 -1
- package/dist/utils/api-client.d.ts.map +1 -1
- package/dist/utils/api-client.js +2 -2
- package/dist/utils/api-client.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileType: command
|
|
3
|
+
* @status: current
|
|
4
|
+
* @updated: 2026-01-03
|
|
5
|
+
* @tags: [invite, team, collaboration, epic-008]
|
|
6
|
+
* @related: [../join/index.ts, ../team/index.ts]
|
|
7
|
+
* @priority: high
|
|
8
|
+
* @complexity: medium
|
|
9
|
+
* @dependencies: [commander, chalk, ora]
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* Invite Command (EPIC-008 Sprint 1)
|
|
13
|
+
*
|
|
14
|
+
* CLI command for team owners/admins to invite members via email
|
|
15
|
+
*/
|
|
16
|
+
import { Command } from 'commander';
|
|
17
|
+
/**
|
|
18
|
+
* Main invite command with subcommands
|
|
19
|
+
*/
|
|
20
|
+
export declare function inviteCommand(): Command;
|
|
21
|
+
export default inviteCommand;
|
|
22
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/commands/invite/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH;;;;GAIG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA8LpC;;GAEG;AACH,wBAAgB,aAAa,YA2C5B;AAED,eAAe,aAAa,CAAC"}
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileType: command
|
|
3
|
+
* @status: current
|
|
4
|
+
* @updated: 2026-01-03
|
|
5
|
+
* @tags: [invite, team, collaboration, epic-008]
|
|
6
|
+
* @related: [../join/index.ts, ../team/index.ts]
|
|
7
|
+
* @priority: high
|
|
8
|
+
* @complexity: medium
|
|
9
|
+
* @dependencies: [commander, chalk, ora]
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* Invite Command (EPIC-008 Sprint 1)
|
|
13
|
+
*
|
|
14
|
+
* CLI command for team owners/admins to invite members via email
|
|
15
|
+
*/
|
|
16
|
+
import { Command } from 'commander';
|
|
17
|
+
import chalk from 'chalk';
|
|
18
|
+
import ora from 'ora';
|
|
19
|
+
import Table from 'cli-table3';
|
|
20
|
+
import { api } from '../../utils/api-client.js';
|
|
21
|
+
/**
|
|
22
|
+
* Send invitation to join a team
|
|
23
|
+
*/
|
|
24
|
+
async function sendInvite(email, options) {
|
|
25
|
+
const spinner = ora('Sending invitation...').start();
|
|
26
|
+
try {
|
|
27
|
+
// Get team ID - if not provided, use current project's default team
|
|
28
|
+
let teamId = options.team;
|
|
29
|
+
if (!teamId) {
|
|
30
|
+
// Try to get team from current project context
|
|
31
|
+
const teamsResponse = await api.get('/api/v1/teams?limit=1');
|
|
32
|
+
if (teamsResponse.error || !teamsResponse.data?.teams?.length) {
|
|
33
|
+
spinner.fail(chalk.red('No team specified and no default team found'));
|
|
34
|
+
console.log('');
|
|
35
|
+
console.log(chalk.dim('Usage: ginko invite <email> --team <team-id>'));
|
|
36
|
+
console.log(chalk.dim(' ginko teams list # to see available teams'));
|
|
37
|
+
process.exit(1);
|
|
38
|
+
}
|
|
39
|
+
teamId = teamsResponse.data.teams[0].id;
|
|
40
|
+
}
|
|
41
|
+
const response = await api.post('/api/v1/team/invite', {
|
|
42
|
+
team_id: teamId,
|
|
43
|
+
email: email.toLowerCase(),
|
|
44
|
+
role: options.role || 'member',
|
|
45
|
+
});
|
|
46
|
+
if (response.error) {
|
|
47
|
+
spinner.fail(chalk.red('Failed to send invitation'));
|
|
48
|
+
console.error(chalk.red(` ${response.error}`));
|
|
49
|
+
process.exit(1);
|
|
50
|
+
}
|
|
51
|
+
const invitation = response.data.invitation;
|
|
52
|
+
spinner.succeed(chalk.green('Invitation sent'));
|
|
53
|
+
console.log('');
|
|
54
|
+
console.log(chalk.bold(` Invited: ${invitation.email}`));
|
|
55
|
+
console.log(chalk.dim(` Team: ${invitation.team_name}`));
|
|
56
|
+
console.log(chalk.dim(` Role: ${invitation.role}`));
|
|
57
|
+
console.log(chalk.dim(` Expires: ${new Date(invitation.expires_at).toLocaleDateString()}`));
|
|
58
|
+
console.log('');
|
|
59
|
+
console.log(chalk.cyan(` Invite code: ${invitation.code}`));
|
|
60
|
+
console.log('');
|
|
61
|
+
console.log(chalk.dim('Share this code with the invitee to join:'));
|
|
62
|
+
console.log(chalk.green(` ginko join ${invitation.code}`));
|
|
63
|
+
}
|
|
64
|
+
catch (error) {
|
|
65
|
+
spinner.fail(chalk.red('Failed to send invitation'));
|
|
66
|
+
console.error(chalk.red(` ${error.message}`));
|
|
67
|
+
process.exit(1);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* List pending invitations for a team
|
|
72
|
+
*/
|
|
73
|
+
async function listInvites(options) {
|
|
74
|
+
const spinner = ora('Loading invitations...').start();
|
|
75
|
+
try {
|
|
76
|
+
let teamId = options.team;
|
|
77
|
+
if (!teamId) {
|
|
78
|
+
// Get first team
|
|
79
|
+
const teamsResponse = await api.get('/api/v1/teams?limit=1');
|
|
80
|
+
if (teamsResponse.error || !teamsResponse.data?.teams?.length) {
|
|
81
|
+
spinner.fail(chalk.red('No team specified and no default team found'));
|
|
82
|
+
process.exit(1);
|
|
83
|
+
}
|
|
84
|
+
teamId = teamsResponse.data.teams[0].id;
|
|
85
|
+
}
|
|
86
|
+
const response = await api.get(`/api/v1/team/invite?team_id=${teamId}`);
|
|
87
|
+
if (response.error) {
|
|
88
|
+
spinner.fail(chalk.red('Failed to load invitations'));
|
|
89
|
+
console.error(chalk.red(` ${response.error}`));
|
|
90
|
+
process.exit(1);
|
|
91
|
+
}
|
|
92
|
+
const invitations = response.data.invitations;
|
|
93
|
+
if (invitations.length === 0) {
|
|
94
|
+
spinner.succeed(chalk.yellow('No pending invitations'));
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
spinner.succeed(chalk.green(`Found ${invitations.length} pending invitation(s)`));
|
|
98
|
+
console.log('');
|
|
99
|
+
const table = new Table({
|
|
100
|
+
head: [
|
|
101
|
+
chalk.cyan('Email'),
|
|
102
|
+
chalk.cyan('Role'),
|
|
103
|
+
chalk.cyan('Code'),
|
|
104
|
+
chalk.cyan('Expires'),
|
|
105
|
+
],
|
|
106
|
+
colWidths: [30, 10, 15, 15],
|
|
107
|
+
wordWrap: true,
|
|
108
|
+
});
|
|
109
|
+
invitations.forEach((inv) => {
|
|
110
|
+
table.push([
|
|
111
|
+
inv.email,
|
|
112
|
+
inv.role,
|
|
113
|
+
inv.code,
|
|
114
|
+
new Date(inv.expires_at).toLocaleDateString(),
|
|
115
|
+
]);
|
|
116
|
+
});
|
|
117
|
+
console.log(table.toString());
|
|
118
|
+
}
|
|
119
|
+
catch (error) {
|
|
120
|
+
spinner.fail(chalk.red('Failed to load invitations'));
|
|
121
|
+
console.error(chalk.red(` ${error.message}`));
|
|
122
|
+
process.exit(1);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Revoke a pending invitation
|
|
127
|
+
*/
|
|
128
|
+
async function revokeInvite(code) {
|
|
129
|
+
const spinner = ora('Revoking invitation...').start();
|
|
130
|
+
try {
|
|
131
|
+
const response = await api.delete('/api/v1/team/invite', { code });
|
|
132
|
+
if (response.error) {
|
|
133
|
+
spinner.fail(chalk.red('Failed to revoke invitation'));
|
|
134
|
+
console.error(chalk.red(` ${response.error}`));
|
|
135
|
+
process.exit(1);
|
|
136
|
+
}
|
|
137
|
+
spinner.succeed(chalk.green('Invitation revoked'));
|
|
138
|
+
}
|
|
139
|
+
catch (error) {
|
|
140
|
+
spinner.fail(chalk.red('Failed to revoke invitation'));
|
|
141
|
+
console.error(chalk.red(` ${error.message}`));
|
|
142
|
+
process.exit(1);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Main invite command with subcommands
|
|
147
|
+
*/
|
|
148
|
+
export function inviteCommand() {
|
|
149
|
+
const invite = new Command('invite')
|
|
150
|
+
.description('Invite collaborators to join your team')
|
|
151
|
+
.showHelpAfterError('(use --help for additional information)')
|
|
152
|
+
.addHelpText('after', `
|
|
153
|
+
${chalk.gray('Quick Start:')}
|
|
154
|
+
${chalk.green('ginko invite')} user@example.com ${chalk.gray('# Invite as member')}
|
|
155
|
+
${chalk.green('ginko invite')} user@example.com --role owner ${chalk.gray('# Invite as owner')}
|
|
156
|
+
|
|
157
|
+
${chalk.gray('Management:')}
|
|
158
|
+
${chalk.green('ginko invite --list')} ${chalk.gray('# List pending invitations')}
|
|
159
|
+
${chalk.green('ginko invite --revoke')} <code> ${chalk.gray('# Cancel invitation')}
|
|
160
|
+
|
|
161
|
+
${chalk.gray('Options:')}
|
|
162
|
+
--team <id> ${chalk.dim('Team to invite to (defaults to your first team)')}
|
|
163
|
+
--role <role> ${chalk.dim('Role to assign: owner, admin, member (default: member)')}
|
|
164
|
+
|
|
165
|
+
${chalk.gray('Workflow:')}
|
|
166
|
+
1. ${chalk.green('ginko invite')} collaborator@example.com
|
|
167
|
+
2. Share the invite code with them
|
|
168
|
+
3. They run: ${chalk.green('ginko join')} <code>
|
|
169
|
+
`)
|
|
170
|
+
.argument('[email]', 'Email address to invite')
|
|
171
|
+
.option('-t, --team <id>', 'Team ID to invite to')
|
|
172
|
+
.option('-r, --role <role>', 'Role to assign (owner|admin|member)', 'member')
|
|
173
|
+
.option('-l, --list', 'List pending invitations')
|
|
174
|
+
.option('--revoke <code>', 'Revoke an invitation by code')
|
|
175
|
+
.action(async (email, options) => {
|
|
176
|
+
if (options.list) {
|
|
177
|
+
await listInvites({ team: options.team });
|
|
178
|
+
}
|
|
179
|
+
else if (options.revoke) {
|
|
180
|
+
await revokeInvite(options.revoke);
|
|
181
|
+
}
|
|
182
|
+
else if (email) {
|
|
183
|
+
await sendInvite(email, { team: options.team, role: options.role });
|
|
184
|
+
}
|
|
185
|
+
else {
|
|
186
|
+
invite.help({ error: false });
|
|
187
|
+
}
|
|
188
|
+
});
|
|
189
|
+
return invite;
|
|
190
|
+
}
|
|
191
|
+
export default inviteCommand;
|
|
192
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/commands/invite/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileType: command
|
|
3
|
+
* @status: current
|
|
4
|
+
* @updated: 2026-01-03
|
|
5
|
+
* @tags: [join, team, collaboration, epic-008]
|
|
6
|
+
* @related: [../invite/index.ts, ../team/index.ts]
|
|
7
|
+
* @priority: high
|
|
8
|
+
* @complexity: medium
|
|
9
|
+
* @dependencies: [commander, chalk, ora, prompts]
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* Join Command (EPIC-008 Sprint 1)
|
|
13
|
+
*
|
|
14
|
+
* CLI command for users to join a team via invitation code
|
|
15
|
+
*/
|
|
16
|
+
import { Command } from 'commander';
|
|
17
|
+
/**
|
|
18
|
+
* Main join command
|
|
19
|
+
*/
|
|
20
|
+
export declare function joinCommand(): Command;
|
|
21
|
+
export default joinCommand;
|
|
22
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/commands/join/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH;;;;GAIG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAgLpC;;GAEG;AACH,wBAAgB,WAAW,YAqC1B;AAED,eAAe,WAAW,CAAC"}
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileType: command
|
|
3
|
+
* @status: current
|
|
4
|
+
* @updated: 2026-01-03
|
|
5
|
+
* @tags: [join, team, collaboration, epic-008]
|
|
6
|
+
* @related: [../invite/index.ts, ../team/index.ts]
|
|
7
|
+
* @priority: high
|
|
8
|
+
* @complexity: medium
|
|
9
|
+
* @dependencies: [commander, chalk, ora, prompts]
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* Join Command (EPIC-008 Sprint 1)
|
|
13
|
+
*
|
|
14
|
+
* CLI command for users to join a team via invitation code
|
|
15
|
+
*/
|
|
16
|
+
import { Command } from 'commander';
|
|
17
|
+
import chalk from 'chalk';
|
|
18
|
+
import ora from 'ora';
|
|
19
|
+
import prompts from 'prompts';
|
|
20
|
+
import { api } from '../../utils/api-client.js';
|
|
21
|
+
/**
|
|
22
|
+
* Validate and preview invitation
|
|
23
|
+
*/
|
|
24
|
+
async function previewInvitation(code) {
|
|
25
|
+
const spinner = ora('Validating invitation code...').start();
|
|
26
|
+
try {
|
|
27
|
+
const response = await api.get(`/api/v1/team/join?code=${code}`);
|
|
28
|
+
if (response.error) {
|
|
29
|
+
spinner.fail(chalk.red('Invalid invitation code'));
|
|
30
|
+
console.error(chalk.red(` ${response.error}`));
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
spinner.succeed(chalk.green('Invitation valid'));
|
|
34
|
+
return response.data;
|
|
35
|
+
}
|
|
36
|
+
catch (error) {
|
|
37
|
+
spinner.fail(chalk.red('Failed to validate invitation'));
|
|
38
|
+
console.error(chalk.red(` ${error.message}`));
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Accept invitation and join team
|
|
44
|
+
*/
|
|
45
|
+
async function acceptInvitation(code) {
|
|
46
|
+
const spinner = ora('Joining team...').start();
|
|
47
|
+
try {
|
|
48
|
+
const response = await api.post('/api/v1/team/join', { code });
|
|
49
|
+
if (response.error) {
|
|
50
|
+
spinner.fail(chalk.red('Failed to join team'));
|
|
51
|
+
console.error(chalk.red(` ${response.error}`));
|
|
52
|
+
process.exit(1);
|
|
53
|
+
}
|
|
54
|
+
const result = response.data;
|
|
55
|
+
spinner.succeed(chalk.green(result.message));
|
|
56
|
+
console.log('');
|
|
57
|
+
console.log(chalk.bold(` Team: ${result.membership.team_name}`));
|
|
58
|
+
console.log(chalk.dim(` Role: ${result.membership.role}`));
|
|
59
|
+
console.log(chalk.dim(` Joined: ${new Date(result.membership.joined_at).toLocaleString()}`));
|
|
60
|
+
if (!result.email_matched) {
|
|
61
|
+
console.log('');
|
|
62
|
+
console.log(chalk.yellow(' Note: Invitation was sent to a different email.'));
|
|
63
|
+
}
|
|
64
|
+
console.log('');
|
|
65
|
+
console.log(chalk.dim('Next steps:'));
|
|
66
|
+
console.log(chalk.green(` ginko sync ${chalk.dim('# Pull team context')}`));
|
|
67
|
+
console.log(chalk.green(` ginko teams list-members ${chalk.dim('# See your teammates')}`));
|
|
68
|
+
}
|
|
69
|
+
catch (error) {
|
|
70
|
+
spinner.fail(chalk.red('Failed to join team'));
|
|
71
|
+
console.error(chalk.red(` ${error.message}`));
|
|
72
|
+
process.exit(1);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Interactive join flow (prompts for code)
|
|
77
|
+
*/
|
|
78
|
+
async function interactiveJoin() {
|
|
79
|
+
console.log('');
|
|
80
|
+
console.log(chalk.cyan('Join a Team'));
|
|
81
|
+
console.log(chalk.dim('Enter the invitation code you received'));
|
|
82
|
+
console.log('');
|
|
83
|
+
const response = await prompts({
|
|
84
|
+
type: 'text',
|
|
85
|
+
name: 'code',
|
|
86
|
+
message: 'Invitation code:',
|
|
87
|
+
validate: (value) => value.length >= 8 || 'Code should be at least 8 characters',
|
|
88
|
+
});
|
|
89
|
+
if (!response.code) {
|
|
90
|
+
console.log(chalk.yellow('Cancelled'));
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
await joinTeam(response.code);
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Main join flow
|
|
97
|
+
*/
|
|
98
|
+
async function joinTeam(code) {
|
|
99
|
+
// First validate and preview
|
|
100
|
+
const preview = await previewInvitation(code);
|
|
101
|
+
if (!preview) {
|
|
102
|
+
process.exit(1);
|
|
103
|
+
}
|
|
104
|
+
// Check if already a member
|
|
105
|
+
if (preview.already_member) {
|
|
106
|
+
console.log('');
|
|
107
|
+
console.log(chalk.yellow(`You are already a member of "${preview.team.name}"`));
|
|
108
|
+
console.log(chalk.dim(` Current role: ${preview.current_role}`));
|
|
109
|
+
process.exit(0);
|
|
110
|
+
}
|
|
111
|
+
// Show preview
|
|
112
|
+
console.log('');
|
|
113
|
+
console.log(chalk.bold('Invitation Details:'));
|
|
114
|
+
console.log(` Team: ${chalk.cyan(preview.team.name)}`);
|
|
115
|
+
console.log(` Role: ${chalk.cyan(preview.invitation.role)}`);
|
|
116
|
+
console.log(` Expires: ${new Date(preview.invitation.expires_at).toLocaleDateString()}`);
|
|
117
|
+
if (!preview.invitation.is_for_current_user) {
|
|
118
|
+
console.log('');
|
|
119
|
+
console.log(chalk.yellow(` Note: This invitation was sent to ${preview.invitation.email}`));
|
|
120
|
+
console.log(chalk.dim(' You can still accept it with your current account.'));
|
|
121
|
+
}
|
|
122
|
+
console.log('');
|
|
123
|
+
// Confirm
|
|
124
|
+
const confirm = await prompts({
|
|
125
|
+
type: 'confirm',
|
|
126
|
+
name: 'accept',
|
|
127
|
+
message: `Join team "${preview.team.name}" as ${preview.invitation.role}?`,
|
|
128
|
+
initial: true,
|
|
129
|
+
});
|
|
130
|
+
if (!confirm.accept) {
|
|
131
|
+
console.log(chalk.yellow('Cancelled'));
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
// Accept
|
|
135
|
+
await acceptInvitation(code);
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Main join command
|
|
139
|
+
*/
|
|
140
|
+
export function joinCommand() {
|
|
141
|
+
const join = new Command('join')
|
|
142
|
+
.description('Join a team using an invitation code')
|
|
143
|
+
.showHelpAfterError('(use --help for additional information)')
|
|
144
|
+
.addHelpText('after', `
|
|
145
|
+
${chalk.gray('Usage:')}
|
|
146
|
+
${chalk.green('ginko join')} <invite-code> ${chalk.gray('# Join via invitation code')}
|
|
147
|
+
${chalk.green('ginko join')} ${chalk.gray('# Interactive: prompts for code')}
|
|
148
|
+
|
|
149
|
+
${chalk.gray('Workflow:')}
|
|
150
|
+
1. Receive invitation code from a team owner
|
|
151
|
+
2. Run: ${chalk.green('ginko join')} <code>
|
|
152
|
+
3. Confirm to accept invitation
|
|
153
|
+
4. Run: ${chalk.green('ginko sync')} to pull team context
|
|
154
|
+
|
|
155
|
+
${chalk.gray('Example:')}
|
|
156
|
+
${chalk.green('ginko join')} a1b2c3d4e5f6
|
|
157
|
+
`)
|
|
158
|
+
.argument('[code]', 'Invitation code')
|
|
159
|
+
.option('-y, --yes', 'Skip confirmation prompt')
|
|
160
|
+
.action(async (code, options) => {
|
|
161
|
+
if (code) {
|
|
162
|
+
if (options.yes) {
|
|
163
|
+
// Skip preview confirmation
|
|
164
|
+
await acceptInvitation(code);
|
|
165
|
+
}
|
|
166
|
+
else {
|
|
167
|
+
await joinTeam(code);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
else {
|
|
171
|
+
await interactiveJoin();
|
|
172
|
+
}
|
|
173
|
+
});
|
|
174
|
+
return join;
|
|
175
|
+
}
|
|
176
|
+
export default joinCommand;
|
|
177
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/commands/join/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -17,5 +17,6 @@ import { Command } from 'commander';
|
|
|
17
17
|
export declare function createSyncCommand(): Command;
|
|
18
18
|
export { syncCommand } from './sync-command.js';
|
|
19
19
|
export { findSprintFiles, syncSprintFile } from './sprint-syncer.js';
|
|
20
|
-
export
|
|
20
|
+
export { getTeamSyncStatus, updateLastSyncTimestamp, displayStalenessWarning, } from './team-sync.js';
|
|
21
|
+
export type { SyncOptions, SyncResult, UnsyncedNode, SprintSyncResult, TeamSyncOptions, TeamSyncStatus, } from './types.js';
|
|
21
22
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/commands/sync/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH;;;;GAIG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAIpC,wBAAgB,iBAAiB,IAAI,OAAO,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/commands/sync/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH;;;;GAIG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAIpC,wBAAgB,iBAAiB,IAAI,OAAO,CAwB3C;AAED,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACrE,OAAO,EACL,iBAAiB,EACjB,uBAAuB,EACvB,uBAAuB,GACxB,MAAM,gBAAgB,CAAC;AACxB,YAAY,EACV,WAAW,EACX,UAAU,EACV,YAAY,EACZ,gBAAgB,EAChB,eAAe,EACf,cAAc,GACf,MAAM,YAAY,CAAC"}
|
|
@@ -21,13 +21,17 @@ export function createSyncCommand() {
|
|
|
21
21
|
.option('--dry-run', 'Preview changes without applying')
|
|
22
22
|
.option('--force', 'Overwrite local files with graph versions')
|
|
23
23
|
.option('--type <type>', 'Sync only specific node type (ADR, PRD, Pattern, Gotcha, Charter, Sprint)')
|
|
24
|
-
.option('--no-commit', 'Sync files but do not commit')
|
|
24
|
+
.option('--no-commit', 'Sync files but do not commit')
|
|
25
|
+
.option('--staleness-days <days>', 'Days threshold for staleness warning (default: 3)', '3')
|
|
26
|
+
.option('--skip-team-check', 'Skip team membership verification');
|
|
25
27
|
sync.action(async (options) => {
|
|
26
28
|
const syncOptions = {
|
|
27
29
|
dryRun: options.dryRun === true,
|
|
28
30
|
force: options.force === true,
|
|
29
31
|
type: options.type,
|
|
30
32
|
interactive: true,
|
|
33
|
+
stalenessThresholdDays: parseInt(options.stalenessDays, 10) || 3,
|
|
34
|
+
skipMembershipCheck: options.skipTeamCheck === true,
|
|
31
35
|
};
|
|
32
36
|
await syncCommand(syncOptions);
|
|
33
37
|
});
|
|
@@ -35,4 +39,5 @@ export function createSyncCommand() {
|
|
|
35
39
|
}
|
|
36
40
|
export { syncCommand } from './sync-command.js';
|
|
37
41
|
export { findSprintFiles, syncSprintFile } from './sprint-syncer.js';
|
|
42
|
+
export { getTeamSyncStatus, updateLastSyncTimestamp, displayStalenessWarning, } from './team-sync.js';
|
|
38
43
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/commands/sync/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH;;;;GAIG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAGhD,MAAM,UAAU,iBAAiB;IAC/B,MAAM,IAAI,GAAG,IAAI,OAAO,CAAC,MAAM,CAAC;SAC7B,WAAW,CAAC,uDAAuD,CAAC;SACpE,MAAM,CAAC,WAAW,EAAE,kCAAkC,CAAC;SACvD,MAAM,CAAC,SAAS,EAAE,2CAA2C,CAAC;SAC9D,MAAM,CAAC,eAAe,EAAE,2EAA2E,CAAC;SACpG,MAAM,CAAC,aAAa,EAAE,8BAA8B,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/commands/sync/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH;;;;GAIG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAGhD,MAAM,UAAU,iBAAiB;IAC/B,MAAM,IAAI,GAAG,IAAI,OAAO,CAAC,MAAM,CAAC;SAC7B,WAAW,CAAC,uDAAuD,CAAC;SACpE,MAAM,CAAC,WAAW,EAAE,kCAAkC,CAAC;SACvD,MAAM,CAAC,SAAS,EAAE,2CAA2C,CAAC;SAC9D,MAAM,CAAC,eAAe,EAAE,2EAA2E,CAAC;SACpG,MAAM,CAAC,aAAa,EAAE,8BAA8B,CAAC;SACrD,MAAM,CAAC,yBAAyB,EAAE,mDAAmD,EAAE,GAAG,CAAC;SAC3F,MAAM,CAAC,mBAAmB,EAAE,mCAAmC,CAAC,CAAC;IAEpE,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,OAAgC,EAAE,EAAE;QACrD,MAAM,WAAW,GAAoB;YACnC,MAAM,EAAE,OAAO,CAAC,MAAM,KAAK,IAAI;YAC/B,KAAK,EAAE,OAAO,CAAC,KAAK,KAAK,IAAI;YAC7B,IAAI,EAAE,OAAO,CAAC,IAA4B;YAC1C,WAAW,EAAE,IAAI;YACjB,sBAAsB,EAAE,QAAQ,CAAC,OAAO,CAAC,aAAuB,EAAE,EAAE,CAAC,IAAI,CAAC;YAC1E,mBAAmB,EAAE,OAAO,CAAC,aAAa,KAAK,IAAI;SACpD,CAAC;QAEF,MAAM,WAAW,CAAC,WAAW,CAAC,CAAC;IACjC,CAAC,CAAC,CAAC;IAEH,OAAO,IAAI,CAAC;AACd,CAAC;AAED,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACrE,OAAO,EACL,iBAAiB,EACjB,uBAAuB,EACvB,uBAAuB,GACxB,MAAM,gBAAgB,CAAC"}
|
|
@@ -8,9 +8,9 @@
|
|
|
8
8
|
* @complexity: high
|
|
9
9
|
* @dependencies: [chalk, prompts, simple-git]
|
|
10
10
|
*/
|
|
11
|
-
import type {
|
|
11
|
+
import type { SyncResult, TeamSyncOptions } from './types.js';
|
|
12
12
|
/**
|
|
13
13
|
* Main sync command implementation
|
|
14
14
|
*/
|
|
15
|
-
export declare function syncCommand(options:
|
|
15
|
+
export declare function syncCommand(options: TeamSyncOptions): Promise<SyncResult>;
|
|
16
16
|
//# sourceMappingURL=sync-command.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sync-command.d.ts","sourceRoot":"","sources":["../../../src/commands/sync/sync-command.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;
|
|
1
|
+
{"version":3,"file":"sync-command.d.ts","sourceRoot":"","sources":["../../../src/commands/sync/sync-command.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAmCH,OAAO,KAAK,EAGV,UAAU,EAKV,eAAe,EAChB,MAAM,YAAY,CAAC;AA8PpB;;GAEG;AACH,wBAAsB,WAAW,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,UAAU,CAAC,CAwP/E"}
|
|
@@ -24,6 +24,7 @@ import prompts from 'prompts';
|
|
|
24
24
|
import { getConfig, getApiToken } from '../graph/config.js';
|
|
25
25
|
import { syncNode, getFilePath, computeHash, readFileContent, applyResolution, } from './node-syncer.js';
|
|
26
26
|
import { findSprintFiles, syncSprintFile, updateCurrentSprintFile, } from './sprint-syncer.js';
|
|
27
|
+
import { getTeamSyncStatus, updateLastSyncTimestamp, displayStalenessWarning, displayTeamInfo, } from './team-sync.js';
|
|
27
28
|
const API_BASE = process.env.GINKO_API_URL || 'https://app.ginkoai.com';
|
|
28
29
|
/**
|
|
29
30
|
* Transform raw API node to UnsyncedNode
|
|
@@ -236,6 +237,20 @@ export async function syncCommand(options) {
|
|
|
236
237
|
console.error(chalk.dim(' Run `ginko login` to authenticate.'));
|
|
237
238
|
return result;
|
|
238
239
|
}
|
|
240
|
+
// Check team membership and staleness (EPIC-008)
|
|
241
|
+
const stalenessThreshold = options.stalenessThresholdDays ?? 3;
|
|
242
|
+
let teamStatus = null;
|
|
243
|
+
if (!options.skipMembershipCheck) {
|
|
244
|
+
teamStatus = await getTeamSyncStatus(graphId, token, stalenessThreshold);
|
|
245
|
+
// Display team info if member
|
|
246
|
+
if (teamStatus.isMember) {
|
|
247
|
+
displayTeamInfo(teamStatus);
|
|
248
|
+
}
|
|
249
|
+
// Show staleness warning if applicable
|
|
250
|
+
if (teamStatus.isMember && teamStatus.staleness.isStale) {
|
|
251
|
+
displayStalenessWarning(teamStatus);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
239
254
|
// Get git root directory (not cwd, which might be a subdirectory)
|
|
240
255
|
let projectRoot = process.cwd();
|
|
241
256
|
try {
|
|
@@ -264,8 +279,15 @@ export async function syncCommand(options) {
|
|
|
264
279
|
console.log(chalk.green(`\n✓ Synced ${updatedSprints.length} sprint(s) and committed to git.`));
|
|
265
280
|
}
|
|
266
281
|
}
|
|
267
|
-
//
|
|
282
|
+
// Update team sync timestamp if we synced anything (EPIC-008)
|
|
268
283
|
const totalUpdated = sprintResults.reduce((sum, r) => sum + r.tasksUpdated, 0);
|
|
284
|
+
if (totalUpdated > 0 && teamStatus?.isMember) {
|
|
285
|
+
const updated = await updateLastSyncTimestamp(graphId, token);
|
|
286
|
+
if (updated) {
|
|
287
|
+
console.log(chalk.dim('✓ Team sync timestamp updated'));
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
// Summary
|
|
269
291
|
console.log(chalk.bold('\n📊 Summary:'));
|
|
270
292
|
console.log(` Sprints processed: ${sprintResults.length}`);
|
|
271
293
|
console.log(` Tasks updated: ${totalUpdated}`);
|
|
@@ -395,6 +417,13 @@ export async function syncCommand(options) {
|
|
|
395
417
|
console.log(chalk.dim(' You can commit manually with: git add . && git commit'));
|
|
396
418
|
}
|
|
397
419
|
}
|
|
420
|
+
// Update team sync timestamp if we synced anything (EPIC-008)
|
|
421
|
+
if (result.synced.length > 0 && teamStatus?.isMember) {
|
|
422
|
+
const updated = await updateLastSyncTimestamp(graphId, token);
|
|
423
|
+
if (updated) {
|
|
424
|
+
console.log(chalk.dim('✓ Team sync timestamp updated'));
|
|
425
|
+
}
|
|
426
|
+
}
|
|
398
427
|
// Summary
|
|
399
428
|
console.log(chalk.bold('\n📊 Summary:'));
|
|
400
429
|
console.log(` Synced: ${result.synced.length}`);
|