edsger 0.31.0 โ†’ 0.31.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.
@@ -50,3 +50,16 @@ export declare function getEdsgerBaseUrl(): string;
50
50
  * Get the auth file path (for display purposes)
51
51
  */
52
52
  export declare function getAuthFilePath(): string;
53
+ /**
54
+ * Watch the auth file for external changes (e.g., from Edsger Desktop app).
55
+ * When the file changes, the in-memory cache is invalidated so the CLI
56
+ * picks up new auth credentials automatically.
57
+ *
58
+ * @param onChange - Optional callback invoked when auth config changes
59
+ * @returns A function to stop watching
60
+ */
61
+ export declare function watchAuthFile(onChange?: (config: AuthConfig | null) => void): () => void;
62
+ /**
63
+ * Stop watching the auth file
64
+ */
65
+ export declare function stopWatchingAuthFile(): void;
@@ -4,7 +4,7 @@
4
4
  * Stores auth credentials in ~/.edsger/auth.json
5
5
  * Provides read/write/clear operations for auth tokens
6
6
  */
7
- import { readFileSync, writeFileSync, mkdirSync, existsSync, unlinkSync, chmodSync } from 'fs';
7
+ import { readFileSync, writeFileSync, mkdirSync, existsSync, unlinkSync, chmodSync, watch } from 'fs';
8
8
  import { join } from 'path';
9
9
  import { homedir } from 'os';
10
10
  const EDSGER_DIR = join(homedir(), '.edsger');
@@ -115,3 +115,40 @@ export function getEdsgerBaseUrl() {
115
115
  export function getAuthFilePath() {
116
116
  return AUTH_FILE;
117
117
  }
118
+ /** Active file watcher for auth file changes */
119
+ let _authWatcher = null;
120
+ /**
121
+ * Watch the auth file for external changes (e.g., from Edsger Desktop app).
122
+ * When the file changes, the in-memory cache is invalidated so the CLI
123
+ * picks up new auth credentials automatically.
124
+ *
125
+ * @param onChange - Optional callback invoked when auth config changes
126
+ * @returns A function to stop watching
127
+ */
128
+ export function watchAuthFile(onChange) {
129
+ // Stop any existing watcher
130
+ stopWatchingAuthFile();
131
+ ensureEdsgerDir();
132
+ _authWatcher = watch(EDSGER_DIR, (eventType, filename) => {
133
+ if (filename !== 'auth.json')
134
+ return;
135
+ // Invalidate cache so next loadAuth() reads from disk
136
+ _authCache = undefined;
137
+ const config = loadAuth();
138
+ if (onChange) {
139
+ onChange(config);
140
+ }
141
+ });
142
+ // Don't keep the process alive just for the watcher
143
+ _authWatcher.unref();
144
+ return () => stopWatchingAuthFile();
145
+ }
146
+ /**
147
+ * Stop watching the auth file
148
+ */
149
+ export function stopWatchingAuthFile() {
150
+ if (_authWatcher) {
151
+ _authWatcher.close();
152
+ _authWatcher = null;
153
+ }
154
+ }
@@ -5,6 +5,7 @@
5
5
  import { Octokit } from '@octokit/rest';
6
6
  import { getFeature } from '../../api/features/get-feature.js';
7
7
  import { downloadImagesForClaudeCode } from '../../utils/image-downloader.js';
8
+ import { getMcpServerUrl, getMcpToken } from '../../auth/auth-store.js';
8
9
  /**
9
10
  * Extract owner, repo, and PR number from GitHub PR URL
10
11
  */
@@ -183,8 +184,8 @@ export async function fetchUserStories(featureId, verbose) {
183
184
  if (verbose) {
184
185
  console.log(`๐Ÿ“– Fetching user stories for ${featureId}...`);
185
186
  }
186
- const mcpServerUrl = process.env.EDSGER_MCP_SERVER_URL;
187
- const mcpToken = process.env.EDSGER_MCP_TOKEN;
187
+ const mcpServerUrl = getMcpServerUrl();
188
+ const mcpToken = getMcpToken();
188
189
  const response = await fetch(`${mcpServerUrl}/mcp`, {
189
190
  method: 'POST',
190
191
  headers: {
@@ -230,8 +231,8 @@ export async function fetchTestCases(featureId, verbose) {
230
231
  if (verbose) {
231
232
  console.log(`๐Ÿงช Fetching test cases for ${featureId}...`);
232
233
  }
233
- const mcpServerUrl = process.env.EDSGER_MCP_SERVER_URL;
234
- const mcpToken = process.env.EDSGER_MCP_TOKEN;
234
+ const mcpServerUrl = getMcpServerUrl();
235
+ const mcpToken = getMcpToken();
235
236
  const response = await fetch(`${mcpServerUrl}/mcp`, {
236
237
  method: 'POST',
237
238
  headers: {
@@ -4,6 +4,7 @@
4
4
  */
5
5
  import { Octokit } from '@octokit/rest';
6
6
  import { getFeature } from '../../api/features/get-feature.js';
7
+ import { getMcpServerUrl, getMcpToken } from '../../auth/auth-store.js';
7
8
  /**
8
9
  * Extract owner, repo, and PR number from GitHub PR URL
9
10
  */
@@ -79,8 +80,8 @@ export async function fetchUserStories(featureId, verbose) {
79
80
  if (verbose) {
80
81
  console.log(`๐Ÿ“– Fetching user stories for ${featureId}...`);
81
82
  }
82
- const mcpServerUrl = process.env.EDSGER_MCP_SERVER_URL;
83
- const mcpToken = process.env.EDSGER_MCP_TOKEN;
83
+ const mcpServerUrl = getMcpServerUrl();
84
+ const mcpToken = getMcpToken();
84
85
  const response = await fetch(`${mcpServerUrl}/mcp`, {
85
86
  method: 'POST',
86
87
  headers: {
@@ -126,8 +127,8 @@ export async function fetchTestCases(featureId, verbose) {
126
127
  if (verbose) {
127
128
  console.log(`๐Ÿงช Fetching test cases for ${featureId}...`);
128
129
  }
129
- const mcpServerUrl = process.env.EDSGER_MCP_SERVER_URL;
130
- const mcpToken = process.env.EDSGER_MCP_TOKEN;
130
+ const mcpServerUrl = getMcpServerUrl();
131
+ const mcpToken = getMcpToken();
131
132
  const response = await fetch(`${mcpServerUrl}/mcp`, {
132
133
  method: 'POST',
133
134
  headers: {
@@ -8,6 +8,7 @@ import { fetchFunctionalTestingContext, formatContextForPrompt, } from './contex
8
8
  import { updateFeatureStatus } from '../../api/features/index.js';
9
9
  import { createTestReport, } from './test-report-creator.js';
10
10
  import { preparePhaseGitEnvironment, prepareCustomBranchGitEnvironment, } from '../../utils/git-branch-manager.js';
11
+ import { getMcpServerUrl, getMcpToken } from '../../auth/auth-store.js';
11
12
  import { getReadyForReviewBranch, } from '../../services/branches.js';
12
13
  function userMessage(content) {
13
14
  return {
@@ -299,8 +300,8 @@ export const runFunctionalTesting = async (options, config, checklistContext) =>
299
300
  structuredTestResult.test_cases.length > 0) {
300
301
  // Fetch test cases for this feature to get proper test_case_ids
301
302
  try {
302
- const mcpServerUrl = process.env.EDSGER_MCP_SERVER_URL;
303
- const mcpToken = process.env.EDSGER_MCP_TOKEN;
303
+ const mcpServerUrl = getMcpServerUrl();
304
+ const mcpToken = getMcpToken();
304
305
  const testCasesResponse = await fetch(`${mcpServerUrl}/mcp`, {
305
306
  method: 'POST',
306
307
  headers: {
@@ -1,12 +1,13 @@
1
1
  import { logInfo, logError } from '../../utils/logger.js';
2
+ import { getMcpServerUrl, getMcpToken } from '../../auth/auth-store.js';
2
3
  export async function saveFunctionalTestResultsViaHttp(options) {
3
4
  const { featureId, testStatus, testResults: _testResults, verbose } = options;
4
5
  try {
5
6
  if (verbose) {
6
7
  logInfo('๐Ÿ”„ Attempting to save functional test results via HTTP fallback...');
7
8
  }
8
- const mcpServerUrl = process.env.EDSGER_MCP_SERVER_URL;
9
- const mcpToken = process.env.EDSGER_MCP_TOKEN;
9
+ const mcpServerUrl = getMcpServerUrl();
10
+ const mcpToken = getMcpToken();
10
11
  const response = await fetch(`${mcpServerUrl}/mcp`, {
11
12
  method: 'POST',
12
13
  headers: {
@@ -47,8 +48,8 @@ export async function verifyTestStatusSaved(featureId, verbose, expectedStatus)
47
48
  if (verbose) {
48
49
  logInfo('๐Ÿ” Verifying test status was saved...');
49
50
  }
50
- const mcpServerUrl = process.env.EDSGER_MCP_SERVER_URL;
51
- const mcpToken = process.env.EDSGER_MCP_TOKEN;
51
+ const mcpServerUrl = getMcpServerUrl();
52
+ const mcpToken = getMcpToken();
52
53
  const response = await fetch(`${mcpServerUrl}/mcp`, {
53
54
  method: 'POST',
54
55
  headers: {
@@ -1,4 +1,5 @@
1
1
  import { logInfo, logError } from '../../utils/logger.js';
2
+ import { getMcpServerUrl, getMcpToken } from '../../auth/auth-store.js';
2
3
  /**
3
4
  * Create a test report via MCP endpoint
4
5
  * Uses structured data generated by Claude Code
@@ -23,8 +24,8 @@ export async function createTestReport(options) {
23
24
  timestamp: new Date().toISOString(),
24
25
  };
25
26
  try {
26
- const mcpServerUrl = process.env.EDSGER_MCP_SERVER_URL;
27
- const mcpToken = process.env.EDSGER_MCP_TOKEN;
27
+ const mcpServerUrl = getMcpServerUrl();
28
+ const mcpToken = getMcpToken();
28
29
  // Try to create test report via MCP endpoint
29
30
  // Claude Code should call this endpoint directly with the structured data
30
31
  const response = await fetch(`${mcpServerUrl}/mcp`, {
@@ -103,8 +104,8 @@ export async function createTestReportResults(options) {
103
104
  logInfo(`Creating ${testResults.length} test report results for report: ${testReportId}`);
104
105
  }
105
106
  try {
106
- const mcpServerUrl = process.env.EDSGER_MCP_SERVER_URL;
107
- const mcpToken = process.env.EDSGER_MCP_TOKEN;
107
+ const mcpServerUrl = getMcpServerUrl();
108
+ const mcpToken = getMcpToken();
108
109
  const response = await fetch(`${mcpServerUrl}/mcp`, {
109
110
  method: 'POST',
110
111
  headers: {
@@ -157,8 +158,8 @@ async function manualMcpTestReportCreation(featureId, structuredReport, testResu
157
158
  logInfo('Manually calling MCP test_reports/create endpoint with structured data');
158
159
  }
159
160
  try {
160
- const mcpServerUrl = process.env.EDSGER_MCP_SERVER_URL;
161
- const mcpToken = process.env.EDSGER_MCP_TOKEN;
161
+ const mcpServerUrl = getMcpServerUrl();
162
+ const mcpToken = getMcpToken();
162
163
  // Manually construct and send the MCP request
163
164
  const response = await fetch(`${mcpServerUrl}/mcp`, {
164
165
  method: 'POST',
@@ -2,6 +2,7 @@
2
2
  * Feedbacks service for pipeline integration
3
3
  * Provides human-guided feedbacks to influence Claude Code behavior in each phase
4
4
  */
5
+ import { getMcpServerUrl, getMcpToken } from '../auth/auth-store.js';
5
6
  import { downloadImagesForClaudeCode } from '../utils/image-downloader.js';
6
7
  /**
7
8
  * Fetch feedbacks for a specific phase
@@ -10,8 +11,8 @@ import { downloadImagesForClaudeCode } from '../utils/image-downloader.js';
10
11
  */
11
12
  export async function getFeedbacksForPhase(options, phase, branchId) {
12
13
  const { featureId, verbose } = options;
13
- const mcpServerUrl = process.env.EDSGER_MCP_SERVER_URL;
14
- const mcpToken = process.env.EDSGER_MCP_TOKEN;
14
+ const mcpServerUrl = getMcpServerUrl();
15
+ const mcpToken = getMcpToken();
15
16
  // Convert phase name from hyphen to underscore format for database compatibility
16
17
  // e.g., 'feature-analysis' -> 'feature_analysis'
17
18
  const dbPhase = phase.replace(/-/g, '_');
@@ -207,8 +208,8 @@ function getPriorityBadge(priority) {
207
208
  * Used after successful verification to track which feedbacks have been addressed
208
209
  */
209
210
  export async function resolveFeedbacks(options, feedbackIds, verbose) {
210
- const mcpServerUrl = process.env.EDSGER_MCP_SERVER_URL;
211
- const mcpToken = process.env.EDSGER_MCP_TOKEN;
211
+ const mcpServerUrl = getMcpServerUrl();
212
+ const mcpToken = getMcpToken();
212
213
  if (!feedbackIds || feedbackIds.length === 0) {
213
214
  return;
214
215
  }
@@ -1,3 +1,4 @@
1
+ import { getMcpServerUrl, getMcpToken } from '../auth/auth-store.js';
1
2
  import { loadConfig, validateConfig } from '../config.js';
2
3
  import { logInfo } from './logger.js';
3
4
  /**
@@ -18,13 +19,13 @@ export const validateConfiguration = (options) => {
18
19
  * Validate MCP environment variables and URL format
19
20
  */
20
21
  export const validateMCPEnvironment = () => {
21
- const mcpServerUrl = process.env.EDSGER_MCP_SERVER_URL;
22
- const mcpToken = process.env.EDSGER_MCP_TOKEN;
22
+ const mcpServerUrl = getMcpServerUrl();
23
+ const mcpToken = getMcpToken();
23
24
  if (!mcpServerUrl) {
24
- throw new Error('MCP server URL is required. Set EDSGER_MCP_SERVER_URL environment variable.');
25
+ throw new Error('Not authenticated. Run `edsger login` or set EDSGER_MCP_SERVER_URL environment variable.');
25
26
  }
26
27
  if (!mcpToken) {
27
- throw new Error('MCP token is required. Set EDSGER_MCP_TOKEN environment variable.');
28
+ throw new Error('Not authenticated. Run `edsger login` or set EDSGER_MCP_TOKEN environment variable.');
28
29
  }
29
30
  // Validate MCP server URL format
30
31
  try {
@@ -1,12 +1,13 @@
1
1
  import { validateConfiguration } from './validation.js';
2
2
  import { logInfo } from './logger.js';
3
+ import { getMcpServerUrl, getMcpToken } from '../auth/auth-store.js';
3
4
  /**
4
5
  * Validate workflow-specific environment variables
5
6
  */
6
7
  export const validateWorkflowEnvironment = () => {
7
8
  const productId = process.env.EDSGER_PRODUCT_ID;
8
- const mcpServerUrl = process.env.EDSGER_MCP_SERVER_URL;
9
- const mcpToken = process.env.EDSGER_MCP_TOKEN;
9
+ const mcpServerUrl = getMcpServerUrl();
10
+ const mcpToken = getMcpToken();
10
11
  if (!productId) {
11
12
  throw new Error('Product ID is required. Set EDSGER_PRODUCT_ID environment variable.');
12
13
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "edsger",
3
- "version": "0.31.0",
3
+ "version": "0.31.1",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "edsger": "dist/index.js"