@ginkoai/cli 2.0.0-beta.3 → 2.0.0-beta.5
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/start/start-reflection.d.ts +7 -0
- package/dist/commands/start/start-reflection.d.ts.map +1 -1
- package/dist/commands/start/start-reflection.js +32 -0
- package/dist/commands/start/start-reflection.js.map +1 -1
- package/dist/commands/sync/index.d.ts +7 -4
- package/dist/commands/sync/index.d.ts.map +1 -1
- package/dist/commands/sync/index.js +10 -6
- package/dist/commands/sync/index.js.map +1 -1
- package/dist/commands/sync/sync-command.d.ts +3 -3
- package/dist/commands/sync/sync-command.d.ts.map +1 -1
- package/dist/commands/sync/sync-command.js +27 -4
- package/dist/commands/sync/sync-command.js.map +1 -1
- package/dist/commands/sync/types.d.ts +5 -3
- package/dist/commands/sync/types.d.ts.map +1 -1
- package/dist/commands/sync/types.js +3 -3
- 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/lib/staleness-detector.d.ts +88 -0
- package/dist/lib/staleness-detector.d.ts.map +1 -0
- package/dist/lib/staleness-detector.js +290 -0
- package/dist/lib/staleness-detector.js.map +1 -0
- package/dist/lib/team-sync-reporter.d.ts +110 -0
- package/dist/lib/team-sync-reporter.d.ts.map +1 -0
- package/dist/lib/team-sync-reporter.js +316 -0
- package/dist/lib/team-sync-reporter.js.map +1 -0
- package/package.json +1 -1
|
@@ -14,14 +14,14 @@ interface AddMemberOptions {
|
|
|
14
14
|
/**
|
|
15
15
|
* Add a member to a team
|
|
16
16
|
*/
|
|
17
|
-
export declare function addTeamMemberCommand(
|
|
17
|
+
export declare function addTeamMemberCommand(teamIdOrName: string, githubUsername: string, options: AddMemberOptions): Promise<void>;
|
|
18
18
|
/**
|
|
19
19
|
* Remove a member from a team
|
|
20
20
|
*/
|
|
21
|
-
export declare function removeTeamMemberCommand(
|
|
21
|
+
export declare function removeTeamMemberCommand(teamIdOrName: string, githubUsernameOrUserId: string): Promise<void>;
|
|
22
22
|
/**
|
|
23
23
|
* List team members
|
|
24
24
|
*/
|
|
25
|
-
export declare function listTeamMembersCommand(
|
|
25
|
+
export declare function listTeamMembersCommand(teamIdOrName: string): Promise<void>;
|
|
26
26
|
export {};
|
|
27
27
|
//# sourceMappingURL=members.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"members.d.ts","sourceRoot":"","sources":["../../../src/commands/team/members.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAaH,UAAU,gBAAgB;IACxB,IAAI,CAAC,EAAE,OAAO,GAAG,QAAQ,CAAC;CAC3B;
|
|
1
|
+
{"version":3,"file":"members.d.ts","sourceRoot":"","sources":["../../../src/commands/team/members.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAaH,UAAU,gBAAgB;IACxB,IAAI,CAAC,EAAE,OAAO,GAAG,QAAQ,CAAC;CAC3B;AAoCD;;GAEG;AACH,wBAAsB,oBAAoB,CACxC,YAAY,EAAE,MAAM,EACpB,cAAc,EAAE,MAAM,EACtB,OAAO,EAAE,gBAAgB,GACxB,OAAO,CAAC,IAAI,CAAC,CA2Cf;AAED;;GAEG;AACH,wBAAsB,uBAAuB,CAC3C,YAAY,EAAE,MAAM,EACpB,sBAAsB,EAAE,MAAM,GAC7B,OAAO,CAAC,IAAI,CAAC,CA2Df;AAED;;GAEG;AACH,wBAAsB,sBAAsB,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CA6DhF"}
|
|
@@ -17,12 +17,50 @@ import chalk from 'chalk';
|
|
|
17
17
|
import ora from 'ora';
|
|
18
18
|
import Table from 'cli-table3';
|
|
19
19
|
import { TeamsClient } from '../../lib/api/teams-client.js';
|
|
20
|
+
/**
|
|
21
|
+
* Resolve team name or ID to team ID
|
|
22
|
+
* Accepts either UUID or team name
|
|
23
|
+
*/
|
|
24
|
+
async function resolveTeamId(teamIdOrName) {
|
|
25
|
+
// If it looks like a UUID, return as-is
|
|
26
|
+
const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
27
|
+
if (uuidRegex.test(teamIdOrName)) {
|
|
28
|
+
// Verify team exists and get name
|
|
29
|
+
const response = await TeamsClient.get(teamIdOrName);
|
|
30
|
+
if (response.data) {
|
|
31
|
+
return { teamId: teamIdOrName, teamName: response.data.name };
|
|
32
|
+
}
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
// Otherwise, look up by name
|
|
36
|
+
const response = await TeamsClient.list();
|
|
37
|
+
if (response.error || !response.data?.teams) {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
// Find team by name (case-insensitive)
|
|
41
|
+
const team = response.data.teams.find((t) => t.name.toLowerCase() === teamIdOrName.toLowerCase());
|
|
42
|
+
if (team) {
|
|
43
|
+
return { teamId: team.id, teamName: team.name };
|
|
44
|
+
}
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
20
47
|
/**
|
|
21
48
|
* Add a member to a team
|
|
22
49
|
*/
|
|
23
|
-
export async function addTeamMemberCommand(
|
|
50
|
+
export async function addTeamMemberCommand(teamIdOrName, githubUsername, options) {
|
|
24
51
|
const spinner = ora(`Adding ${githubUsername} to team...`).start();
|
|
25
52
|
try {
|
|
53
|
+
// Resolve team name to ID
|
|
54
|
+
const resolved = await resolveTeamId(teamIdOrName);
|
|
55
|
+
if (!resolved) {
|
|
56
|
+
spinner.fail(chalk.red('Team not found'));
|
|
57
|
+
console.error(chalk.red(` No team found with name or ID: ${teamIdOrName}`));
|
|
58
|
+
console.log('');
|
|
59
|
+
console.log(chalk.dim('💡 List available teams:'));
|
|
60
|
+
console.log(chalk.dim(' ginko teams list'));
|
|
61
|
+
process.exit(1);
|
|
62
|
+
}
|
|
63
|
+
const { teamId, teamName } = resolved;
|
|
26
64
|
const response = await TeamsClient.addMember(teamId, {
|
|
27
65
|
github_username: githubUsername,
|
|
28
66
|
role: options.role || 'member',
|
|
@@ -38,10 +76,10 @@ export async function addTeamMemberCommand(teamId, githubUsername, options) {
|
|
|
38
76
|
console.log('');
|
|
39
77
|
console.log(chalk.bold(` ${githubUsername}`));
|
|
40
78
|
console.log(chalk.dim(` Role: ${role}`));
|
|
41
|
-
console.log(chalk.dim(`
|
|
79
|
+
console.log(chalk.dim(` Team: ${teamName}`));
|
|
42
80
|
console.log('');
|
|
43
81
|
console.log(chalk.dim('💡 View all members:'));
|
|
44
|
-
console.log(chalk.dim(` ginko
|
|
82
|
+
console.log(chalk.dim(` ginko teams list-members ${teamName}`));
|
|
45
83
|
}
|
|
46
84
|
catch (error) {
|
|
47
85
|
spinner.fail(chalk.red('Failed to add member'));
|
|
@@ -52,10 +90,21 @@ export async function addTeamMemberCommand(teamId, githubUsername, options) {
|
|
|
52
90
|
/**
|
|
53
91
|
* Remove a member from a team
|
|
54
92
|
*/
|
|
55
|
-
export async function removeTeamMemberCommand(
|
|
93
|
+
export async function removeTeamMemberCommand(teamIdOrName, githubUsernameOrUserId) {
|
|
56
94
|
const spinner = ora(`Removing ${githubUsernameOrUserId} from team...`).start();
|
|
57
95
|
try {
|
|
58
|
-
//
|
|
96
|
+
// Resolve team name to ID
|
|
97
|
+
const resolved = await resolveTeamId(teamIdOrName);
|
|
98
|
+
if (!resolved) {
|
|
99
|
+
spinner.fail(chalk.red('Team not found'));
|
|
100
|
+
console.error(chalk.red(` No team found with name or ID: ${teamIdOrName}`));
|
|
101
|
+
console.log('');
|
|
102
|
+
console.log(chalk.dim('💡 List available teams:'));
|
|
103
|
+
console.log(chalk.dim(' ginko teams list'));
|
|
104
|
+
process.exit(1);
|
|
105
|
+
}
|
|
106
|
+
const { teamId, teamName } = resolved;
|
|
107
|
+
// List members to find the user ID if username was provided
|
|
59
108
|
const membersResponse = await TeamsClient.listMembers(teamId);
|
|
60
109
|
if (membersResponse.error) {
|
|
61
110
|
spinner.fail(chalk.red('Failed to load team members'));
|
|
@@ -64,7 +113,9 @@ export async function removeTeamMemberCommand(teamId, githubUsernameOrUserId) {
|
|
|
64
113
|
}
|
|
65
114
|
const members = membersResponse.data.members;
|
|
66
115
|
const member = members.find((m) => m.user_id === githubUsernameOrUserId ||
|
|
116
|
+
m.user?.github_username === githubUsernameOrUserId ||
|
|
67
117
|
m.github_username === githubUsernameOrUserId ||
|
|
118
|
+
m.user?.email === githubUsernameOrUserId ||
|
|
68
119
|
m.email === githubUsernameOrUserId);
|
|
69
120
|
if (!member) {
|
|
70
121
|
spinner.fail(chalk.red('Member not found'));
|
|
@@ -80,7 +131,7 @@ export async function removeTeamMemberCommand(teamId, githubUsernameOrUserId) {
|
|
|
80
131
|
}
|
|
81
132
|
spinner.succeed(chalk.green('Member removed'));
|
|
82
133
|
console.log('');
|
|
83
|
-
console.log(chalk.dim(` ${githubUsernameOrUserId} has been removed from
|
|
134
|
+
console.log(chalk.dim(` ${githubUsernameOrUserId} has been removed from ${teamName}`));
|
|
84
135
|
}
|
|
85
136
|
catch (error) {
|
|
86
137
|
spinner.fail(chalk.red('Failed to remove member'));
|
|
@@ -91,9 +142,20 @@ export async function removeTeamMemberCommand(teamId, githubUsernameOrUserId) {
|
|
|
91
142
|
/**
|
|
92
143
|
* List team members
|
|
93
144
|
*/
|
|
94
|
-
export async function listTeamMembersCommand(
|
|
145
|
+
export async function listTeamMembersCommand(teamIdOrName) {
|
|
95
146
|
const spinner = ora('Loading team members...').start();
|
|
96
147
|
try {
|
|
148
|
+
// Resolve team name to ID
|
|
149
|
+
const resolved = await resolveTeamId(teamIdOrName);
|
|
150
|
+
if (!resolved) {
|
|
151
|
+
spinner.fail(chalk.red('Team not found'));
|
|
152
|
+
console.error(chalk.red(` No team found with name or ID: ${teamIdOrName}`));
|
|
153
|
+
console.log('');
|
|
154
|
+
console.log(chalk.dim('💡 List available teams:'));
|
|
155
|
+
console.log(chalk.dim(' ginko teams list'));
|
|
156
|
+
process.exit(1);
|
|
157
|
+
}
|
|
158
|
+
const { teamId, teamName } = resolved;
|
|
97
159
|
const response = await TeamsClient.listMembers(teamId);
|
|
98
160
|
if (response.error) {
|
|
99
161
|
spinner.fail(chalk.red('Failed to load members'));
|
|
@@ -101,7 +163,7 @@ export async function listTeamMembersCommand(teamId) {
|
|
|
101
163
|
process.exit(1);
|
|
102
164
|
}
|
|
103
165
|
const members = response.data.members;
|
|
104
|
-
spinner.succeed(chalk.green(
|
|
166
|
+
spinner.succeed(chalk.green(`${teamName}: ${members.length} member${members.length !== 1 ? 's' : ''}`));
|
|
105
167
|
console.log('');
|
|
106
168
|
if (members.length === 0) {
|
|
107
169
|
console.log(chalk.yellow('No members found'));
|
|
@@ -114,8 +176,9 @@ export async function listTeamMembersCommand(teamId) {
|
|
|
114
176
|
wordWrap: true,
|
|
115
177
|
});
|
|
116
178
|
members.forEach((member) => {
|
|
117
|
-
|
|
118
|
-
const
|
|
179
|
+
// Profile data is in nested 'user' object from API
|
|
180
|
+
const username = member.user?.github_username || member.github_username || '-';
|
|
181
|
+
const email = member.user?.email || member.email || '-';
|
|
119
182
|
const role = member.role === 'owner' ? chalk.yellow('owner') : 'member';
|
|
120
183
|
const joined = new Date(member.joined_at).toLocaleDateString();
|
|
121
184
|
table.push([username, email, role, joined]);
|
|
@@ -123,8 +186,8 @@ export async function listTeamMembersCommand(teamId) {
|
|
|
123
186
|
console.log(table.toString());
|
|
124
187
|
console.log('');
|
|
125
188
|
console.log(chalk.dim('💡 Manage members:'));
|
|
126
|
-
console.log(chalk.dim(` ginko
|
|
127
|
-
console.log(chalk.dim(` ginko
|
|
189
|
+
console.log(chalk.dim(` ginko teams add-member ${teamName} <github-username>`));
|
|
190
|
+
console.log(chalk.dim(` ginko teams remove-member ${teamName} <github-username>`));
|
|
128
191
|
}
|
|
129
192
|
catch (error) {
|
|
130
193
|
spinner.fail(chalk.red('Failed to load members'));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"members.js","sourceRoot":"","sources":["../../../src/commands/team/members.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH;;;;GAIG;AAEH,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,GAAG,MAAM,KAAK,CAAC;AACtB,OAAO,KAAK,MAAM,YAAY,CAAC;AAC/B,OAAO,EAAE,WAAW,EAAE,MAAM,+BAA+B,CAAC;AAM5D;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,
|
|
1
|
+
{"version":3,"file":"members.js","sourceRoot":"","sources":["../../../src/commands/team/members.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileType: utility
|
|
3
|
+
* @status: current
|
|
4
|
+
* @updated: 2026-01-03
|
|
5
|
+
* @tags: [staleness, detection, team, sync, EPIC-008]
|
|
6
|
+
* @related: [../commands/sync/team-sync.ts, ../commands/start/start-reflection.ts]
|
|
7
|
+
* @priority: high
|
|
8
|
+
* @complexity: medium
|
|
9
|
+
* @dependencies: [chalk]
|
|
10
|
+
*/
|
|
11
|
+
export interface StalenessConfig {
|
|
12
|
+
/** Days before showing warning (default: 1) */
|
|
13
|
+
warningThresholdDays: number;
|
|
14
|
+
/** Days before showing critical warning (default: 7) */
|
|
15
|
+
criticalThresholdDays: number;
|
|
16
|
+
}
|
|
17
|
+
export interface ChangedSinceSync {
|
|
18
|
+
adrs: number;
|
|
19
|
+
patterns: number;
|
|
20
|
+
sprints: number;
|
|
21
|
+
total: number;
|
|
22
|
+
}
|
|
23
|
+
export interface StalenessResult {
|
|
24
|
+
/** Whether context is stale (exceeds warning threshold) */
|
|
25
|
+
isStale: boolean;
|
|
26
|
+
/** Severity level based on days since sync */
|
|
27
|
+
severity: 'none' | 'warning' | 'critical';
|
|
28
|
+
/** Number of days since last sync (Infinity if never synced) */
|
|
29
|
+
daysSinceSync: number;
|
|
30
|
+
/** ISO timestamp of last sync, null if never synced */
|
|
31
|
+
lastSyncAt: string | null;
|
|
32
|
+
/** Count of changes by category since last sync */
|
|
33
|
+
changedSinceSync: ChangedSinceSync;
|
|
34
|
+
/** Human-readable message describing staleness state */
|
|
35
|
+
message: string;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Check staleness of team context for current user
|
|
39
|
+
*
|
|
40
|
+
* @param graphId - The graph ID to check
|
|
41
|
+
* @param token - Bearer token for authentication
|
|
42
|
+
* @param config - Optional staleness configuration
|
|
43
|
+
* @returns StalenessResult with severity, days since sync, and changes
|
|
44
|
+
*
|
|
45
|
+
* @example
|
|
46
|
+
* ```typescript
|
|
47
|
+
* const result = await checkStaleness(graphId, token, {
|
|
48
|
+
* warningThresholdDays: 1,
|
|
49
|
+
* criticalThresholdDays: 7,
|
|
50
|
+
* });
|
|
51
|
+
*
|
|
52
|
+
* if (result.severity === 'critical') {
|
|
53
|
+
* console.log(chalk.red(result.message));
|
|
54
|
+
* }
|
|
55
|
+
* ```
|
|
56
|
+
*/
|
|
57
|
+
export declare function checkStaleness(graphId: string, token: string, config?: Partial<StalenessConfig>): Promise<StalenessResult>;
|
|
58
|
+
/**
|
|
59
|
+
* Display staleness warning in CLI with appropriate styling
|
|
60
|
+
*
|
|
61
|
+
* @param result - The staleness check result
|
|
62
|
+
*/
|
|
63
|
+
export declare function displayStalenessWarning(result: StalenessResult): void;
|
|
64
|
+
/**
|
|
65
|
+
* Calculate number of days since last sync
|
|
66
|
+
*/
|
|
67
|
+
declare function calculateDaysSinceSync(lastSyncAt: string | null): number;
|
|
68
|
+
/**
|
|
69
|
+
* Determine severity based on days since sync and config thresholds
|
|
70
|
+
*/
|
|
71
|
+
declare function determineSeverity(daysSinceSync: number, config: StalenessConfig): 'none' | 'warning' | 'critical';
|
|
72
|
+
/**
|
|
73
|
+
* Format human-readable message based on staleness state
|
|
74
|
+
*/
|
|
75
|
+
declare function formatMessage(severity: 'none' | 'warning' | 'critical', daysSinceSync: number, lastSyncAt: string | null, changes: ChangedSinceSync): string;
|
|
76
|
+
/**
|
|
77
|
+
* Format relative time for display
|
|
78
|
+
*/
|
|
79
|
+
declare function formatRelativeTime(dateStr: string): string;
|
|
80
|
+
export declare const _internal: {
|
|
81
|
+
calculateDaysSinceSync: typeof calculateDaysSinceSync;
|
|
82
|
+
determineSeverity: typeof determineSeverity;
|
|
83
|
+
formatMessage: typeof formatMessage;
|
|
84
|
+
formatRelativeTime: typeof formatRelativeTime;
|
|
85
|
+
DEFAULT_CONFIG: StalenessConfig;
|
|
86
|
+
};
|
|
87
|
+
export {};
|
|
88
|
+
//# sourceMappingURL=staleness-detector.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"staleness-detector.d.ts","sourceRoot":"","sources":["../../src/lib/staleness-detector.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAoBH,MAAM,WAAW,eAAe;IAC9B,+CAA+C;IAC/C,oBAAoB,EAAE,MAAM,CAAC;IAC7B,wDAAwD;IACxD,qBAAqB,EAAE,MAAM,CAAC;CAC/B;AAED,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,eAAe;IAC9B,2DAA2D;IAC3D,OAAO,EAAE,OAAO,CAAC;IACjB,8CAA8C;IAC9C,QAAQ,EAAE,MAAM,GAAG,SAAS,GAAG,UAAU,CAAC;IAC1C,gEAAgE;IAChE,aAAa,EAAE,MAAM,CAAC;IACtB,uDAAuD;IACvD,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,mDAAmD;IACnD,gBAAgB,EAAE,gBAAgB,CAAC;IACnC,wDAAwD;IACxD,OAAO,EAAE,MAAM,CAAC;CACjB;AAiBD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAsB,cAAc,CAClC,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,MAAM,EACb,MAAM,CAAC,EAAE,OAAO,CAAC,eAAe,CAAC,GAChC,OAAO,CAAC,eAAe,CAAC,CAwF1B;AAMD;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,eAAe,GAAG,IAAI,CAiCrE;AAMD;;GAEG;AACH,iBAAS,sBAAsB,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAWjE;AAED;;GAEG;AACH,iBAAS,iBAAiB,CACxB,aAAa,EAAE,MAAM,EACrB,MAAM,EAAE,eAAe,GACtB,MAAM,GAAG,SAAS,GAAG,UAAU,CAQjC;AA8CD;;GAEG;AACH,iBAAS,aAAa,CACpB,QAAQ,EAAE,MAAM,GAAG,SAAS,GAAG,UAAU,EACzC,aAAa,EAAE,MAAM,EACrB,UAAU,EAAE,MAAM,GAAG,IAAI,EACzB,OAAO,EAAE,gBAAgB,GACxB,MAAM,CAqBR;AAED;;GAEG;AACH,iBAAS,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAanD;AAaD,eAAO,MAAM,SAAS;;;;;;CAMrB,CAAC"}
|
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileType: utility
|
|
3
|
+
* @status: current
|
|
4
|
+
* @updated: 2026-01-03
|
|
5
|
+
* @tags: [staleness, detection, team, sync, EPIC-008]
|
|
6
|
+
* @related: [../commands/sync/team-sync.ts, ../commands/start/start-reflection.ts]
|
|
7
|
+
* @priority: high
|
|
8
|
+
* @complexity: medium
|
|
9
|
+
* @dependencies: [chalk]
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* Staleness Detection System (EPIC-008 Sprint 2)
|
|
13
|
+
*
|
|
14
|
+
* Provides comprehensive staleness detection for team context synchronization.
|
|
15
|
+
* Used by both CLI (ginko start) and Dashboard (StalenessWarning component).
|
|
16
|
+
*
|
|
17
|
+
* Key features:
|
|
18
|
+
* - Configurable warning/critical thresholds
|
|
19
|
+
* - Tracks changes since last sync (ADRs, Patterns, Sprints)
|
|
20
|
+
* - Human-readable messages for different severity levels
|
|
21
|
+
*/
|
|
22
|
+
import chalk from 'chalk';
|
|
23
|
+
// =============================================================================
|
|
24
|
+
// Constants
|
|
25
|
+
// =============================================================================
|
|
26
|
+
const API_BASE = process.env.GINKO_API_URL || 'https://app.ginkoai.com';
|
|
27
|
+
const DEFAULT_CONFIG = {
|
|
28
|
+
warningThresholdDays: 1,
|
|
29
|
+
criticalThresholdDays: 7,
|
|
30
|
+
};
|
|
31
|
+
// =============================================================================
|
|
32
|
+
// Main API
|
|
33
|
+
// =============================================================================
|
|
34
|
+
/**
|
|
35
|
+
* Check staleness of team context for current user
|
|
36
|
+
*
|
|
37
|
+
* @param graphId - The graph ID to check
|
|
38
|
+
* @param token - Bearer token for authentication
|
|
39
|
+
* @param config - Optional staleness configuration
|
|
40
|
+
* @returns StalenessResult with severity, days since sync, and changes
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* ```typescript
|
|
44
|
+
* const result = await checkStaleness(graphId, token, {
|
|
45
|
+
* warningThresholdDays: 1,
|
|
46
|
+
* criticalThresholdDays: 7,
|
|
47
|
+
* });
|
|
48
|
+
*
|
|
49
|
+
* if (result.severity === 'critical') {
|
|
50
|
+
* console.log(chalk.red(result.message));
|
|
51
|
+
* }
|
|
52
|
+
* ```
|
|
53
|
+
*/
|
|
54
|
+
export async function checkStaleness(graphId, token, config) {
|
|
55
|
+
const mergedConfig = {
|
|
56
|
+
...DEFAULT_CONFIG,
|
|
57
|
+
...config,
|
|
58
|
+
};
|
|
59
|
+
try {
|
|
60
|
+
// Fetch membership status (includes last_sync_at)
|
|
61
|
+
const membershipUrl = new URL(`${API_BASE}/api/v1/graph/membership`);
|
|
62
|
+
membershipUrl.searchParams.set('graphId', graphId);
|
|
63
|
+
const membershipResponse = await fetch(membershipUrl.toString(), {
|
|
64
|
+
headers: {
|
|
65
|
+
Authorization: `Bearer ${token}`,
|
|
66
|
+
'Content-Type': 'application/json',
|
|
67
|
+
},
|
|
68
|
+
});
|
|
69
|
+
if (membershipResponse.status === 404) {
|
|
70
|
+
// User is not a team member - return non-stale (no team context to sync)
|
|
71
|
+
return createResult({
|
|
72
|
+
isStale: false,
|
|
73
|
+
severity: 'none',
|
|
74
|
+
daysSinceSync: 0,
|
|
75
|
+
lastSyncAt: null,
|
|
76
|
+
changedSinceSync: { adrs: 0, patterns: 0, sprints: 0, total: 0 },
|
|
77
|
+
message: 'Not a team member',
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
if (!membershipResponse.ok) {
|
|
81
|
+
// API error - return safe default (non-stale to avoid blocking)
|
|
82
|
+
return createResult({
|
|
83
|
+
isStale: false,
|
|
84
|
+
severity: 'none',
|
|
85
|
+
daysSinceSync: 0,
|
|
86
|
+
lastSyncAt: null,
|
|
87
|
+
changedSinceSync: { adrs: 0, patterns: 0, sprints: 0, total: 0 },
|
|
88
|
+
message: 'Could not verify membership status',
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
const membershipData = await membershipResponse.json();
|
|
92
|
+
const lastSyncAt = membershipData.membership?.last_sync_at || null;
|
|
93
|
+
// Calculate days since sync
|
|
94
|
+
const daysSinceSync = calculateDaysSinceSync(lastSyncAt);
|
|
95
|
+
// Determine severity based on thresholds
|
|
96
|
+
const severity = determineSeverity(daysSinceSync, mergedConfig);
|
|
97
|
+
// If not stale, return early (skip expensive changes query)
|
|
98
|
+
if (severity === 'none') {
|
|
99
|
+
return createResult({
|
|
100
|
+
isStale: false,
|
|
101
|
+
severity: 'none',
|
|
102
|
+
daysSinceSync,
|
|
103
|
+
lastSyncAt,
|
|
104
|
+
changedSinceSync: { adrs: 0, patterns: 0, sprints: 0, total: 0 },
|
|
105
|
+
message: formatMessage(severity, daysSinceSync, lastSyncAt, { adrs: 0, patterns: 0, sprints: 0, total: 0 }),
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
// Fetch changes since last sync
|
|
109
|
+
const changedSinceSync = await fetchChangesSinceSync(graphId, token, lastSyncAt);
|
|
110
|
+
// Generate human-readable message
|
|
111
|
+
const message = formatMessage(severity, daysSinceSync, lastSyncAt, changedSinceSync);
|
|
112
|
+
return createResult({
|
|
113
|
+
isStale: true,
|
|
114
|
+
severity,
|
|
115
|
+
daysSinceSync,
|
|
116
|
+
lastSyncAt,
|
|
117
|
+
changedSinceSync,
|
|
118
|
+
message,
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
catch (error) {
|
|
122
|
+
// Network error - return safe default
|
|
123
|
+
return createResult({
|
|
124
|
+
isStale: false,
|
|
125
|
+
severity: 'none',
|
|
126
|
+
daysSinceSync: 0,
|
|
127
|
+
lastSyncAt: null,
|
|
128
|
+
changedSinceSync: { adrs: 0, patterns: 0, sprints: 0, total: 0 },
|
|
129
|
+
message: 'Could not check staleness (offline?)',
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
// =============================================================================
|
|
134
|
+
// CLI Display Helper
|
|
135
|
+
// =============================================================================
|
|
136
|
+
/**
|
|
137
|
+
* Display staleness warning in CLI with appropriate styling
|
|
138
|
+
*
|
|
139
|
+
* @param result - The staleness check result
|
|
140
|
+
*/
|
|
141
|
+
export function displayStalenessWarning(result) {
|
|
142
|
+
if (!result.isStale) {
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
console.log('');
|
|
146
|
+
if (result.severity === 'critical') {
|
|
147
|
+
console.log(chalk.red.bold('🚨 Team context is critically stale'));
|
|
148
|
+
console.log(chalk.red(` ${result.message}`));
|
|
149
|
+
}
|
|
150
|
+
else {
|
|
151
|
+
console.log(chalk.yellow('⚠️ Team context may be stale'));
|
|
152
|
+
console.log(chalk.yellow(` ${result.message}`));
|
|
153
|
+
}
|
|
154
|
+
// Show changes breakdown if any
|
|
155
|
+
if (result.changedSinceSync.total > 0) {
|
|
156
|
+
console.log('');
|
|
157
|
+
console.log(chalk.dim(' Changes since last sync:'));
|
|
158
|
+
if (result.changedSinceSync.adrs > 0) {
|
|
159
|
+
console.log(chalk.dim(` - ${result.changedSinceSync.adrs} ADR${result.changedSinceSync.adrs === 1 ? '' : 's'}`));
|
|
160
|
+
}
|
|
161
|
+
if (result.changedSinceSync.patterns > 0) {
|
|
162
|
+
console.log(chalk.dim(` - ${result.changedSinceSync.patterns} Pattern${result.changedSinceSync.patterns === 1 ? '' : 's'}`));
|
|
163
|
+
}
|
|
164
|
+
if (result.changedSinceSync.sprints > 0) {
|
|
165
|
+
console.log(chalk.dim(` - ${result.changedSinceSync.sprints} Sprint${result.changedSinceSync.sprints === 1 ? '' : 's'}`));
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
console.log('');
|
|
169
|
+
console.log(chalk.dim(' Run `ginko sync` to pull team updates.'));
|
|
170
|
+
console.log('');
|
|
171
|
+
}
|
|
172
|
+
// =============================================================================
|
|
173
|
+
// Helper Functions
|
|
174
|
+
// =============================================================================
|
|
175
|
+
/**
|
|
176
|
+
* Calculate number of days since last sync
|
|
177
|
+
*/
|
|
178
|
+
function calculateDaysSinceSync(lastSyncAt) {
|
|
179
|
+
if (!lastSyncAt) {
|
|
180
|
+
return Infinity;
|
|
181
|
+
}
|
|
182
|
+
const lastSync = new Date(lastSyncAt);
|
|
183
|
+
const now = new Date();
|
|
184
|
+
const diffMs = now.getTime() - lastSync.getTime();
|
|
185
|
+
const daysSinceSync = diffMs / (1000 * 60 * 60 * 24);
|
|
186
|
+
return Math.floor(daysSinceSync);
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Determine severity based on days since sync and config thresholds
|
|
190
|
+
*/
|
|
191
|
+
function determineSeverity(daysSinceSync, config) {
|
|
192
|
+
if (daysSinceSync >= config.criticalThresholdDays) {
|
|
193
|
+
return 'critical';
|
|
194
|
+
}
|
|
195
|
+
if (daysSinceSync >= config.warningThresholdDays) {
|
|
196
|
+
return 'warning';
|
|
197
|
+
}
|
|
198
|
+
return 'none';
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Fetch count of changes since last sync from API
|
|
202
|
+
*/
|
|
203
|
+
async function fetchChangesSinceSync(graphId, token, lastSyncAt) {
|
|
204
|
+
try {
|
|
205
|
+
const url = new URL(`${API_BASE}/api/v1/graph/changes`);
|
|
206
|
+
url.searchParams.set('graphId', graphId);
|
|
207
|
+
if (lastSyncAt) {
|
|
208
|
+
url.searchParams.set('since', lastSyncAt);
|
|
209
|
+
}
|
|
210
|
+
const response = await fetch(url.toString(), {
|
|
211
|
+
headers: {
|
|
212
|
+
Authorization: `Bearer ${token}`,
|
|
213
|
+
'Content-Type': 'application/json',
|
|
214
|
+
},
|
|
215
|
+
});
|
|
216
|
+
if (!response.ok) {
|
|
217
|
+
// Fallback if endpoint not available
|
|
218
|
+
return { adrs: 0, patterns: 0, sprints: 0, total: 0 };
|
|
219
|
+
}
|
|
220
|
+
const data = await response.json();
|
|
221
|
+
// API returns { changes: { ADR: n, Pattern: n, Sprint: n, ... }, total: n }
|
|
222
|
+
return {
|
|
223
|
+
adrs: data.changes?.ADR || 0,
|
|
224
|
+
patterns: data.changes?.Pattern || 0,
|
|
225
|
+
sprints: data.changes?.Sprint || 0,
|
|
226
|
+
total: data.total || 0,
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
catch {
|
|
230
|
+
return { adrs: 0, patterns: 0, sprints: 0, total: 0 };
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Format human-readable message based on staleness state
|
|
235
|
+
*/
|
|
236
|
+
function formatMessage(severity, daysSinceSync, lastSyncAt, changes) {
|
|
237
|
+
if (severity === 'none') {
|
|
238
|
+
if (!lastSyncAt) {
|
|
239
|
+
return 'No sync history';
|
|
240
|
+
}
|
|
241
|
+
return `Last synced ${formatRelativeTime(lastSyncAt)}`;
|
|
242
|
+
}
|
|
243
|
+
if (!lastSyncAt || daysSinceSync === Infinity) {
|
|
244
|
+
return 'Never synced - team context not loaded';
|
|
245
|
+
}
|
|
246
|
+
const changeInfo = changes.total > 0
|
|
247
|
+
? ` (${changes.total} change${changes.total === 1 ? '' : 's'})`
|
|
248
|
+
: '';
|
|
249
|
+
if (severity === 'critical') {
|
|
250
|
+
return `${daysSinceSync} days since last sync${changeInfo}. Team patterns and ADRs may be outdated.`;
|
|
251
|
+
}
|
|
252
|
+
return `${daysSinceSync} day${daysSinceSync === 1 ? '' : 's'} since last sync${changeInfo}.`;
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* Format relative time for display
|
|
256
|
+
*/
|
|
257
|
+
function formatRelativeTime(dateStr) {
|
|
258
|
+
const date = new Date(dateStr);
|
|
259
|
+
const now = new Date();
|
|
260
|
+
const diffMs = now.getTime() - date.getTime();
|
|
261
|
+
const diffMins = Math.floor(diffMs / (1000 * 60));
|
|
262
|
+
const diffHours = Math.floor(diffMs / (1000 * 60 * 60));
|
|
263
|
+
const diffDays = Math.floor(diffMs / (1000 * 60 * 60 * 24));
|
|
264
|
+
if (diffMins < 1)
|
|
265
|
+
return 'just now';
|
|
266
|
+
if (diffMins < 60)
|
|
267
|
+
return `${diffMins} minute${diffMins === 1 ? '' : 's'} ago`;
|
|
268
|
+
if (diffHours < 24)
|
|
269
|
+
return `${diffHours} hour${diffHours === 1 ? '' : 's'} ago`;
|
|
270
|
+
if (diffDays < 7)
|
|
271
|
+
return `${diffDays} day${diffDays === 1 ? '' : 's'} ago`;
|
|
272
|
+
return date.toLocaleDateString();
|
|
273
|
+
}
|
|
274
|
+
/**
|
|
275
|
+
* Helper to create a StalenessResult with proper types
|
|
276
|
+
*/
|
|
277
|
+
function createResult(partial) {
|
|
278
|
+
return partial;
|
|
279
|
+
}
|
|
280
|
+
// =============================================================================
|
|
281
|
+
// Exports for Testing
|
|
282
|
+
// =============================================================================
|
|
283
|
+
export const _internal = {
|
|
284
|
+
calculateDaysSinceSync,
|
|
285
|
+
determineSeverity,
|
|
286
|
+
formatMessage,
|
|
287
|
+
formatRelativeTime,
|
|
288
|
+
DEFAULT_CONFIG,
|
|
289
|
+
};
|
|
290
|
+
//# sourceMappingURL=staleness-detector.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"staleness-detector.js","sourceRoot":"","sources":["../../src/lib/staleness-detector.ts"],"names":[],"mappings":"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"}
|