@ranger-testing/ranger-cli 2.0.5 → 2.0.6

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.
Files changed (115) hide show
  1. package/build/cli.js +1 -914
  2. package/build/commands/addEnv.js +1 -1
  3. package/build/commands/authEncrypt.js +1 -36
  4. package/build/commands/clean.js +1 -1
  5. package/build/commands/config.js +1 -93
  6. package/build/commands/env.js +1 -98
  7. package/build/commands/feature.js +1 -653
  8. package/build/commands/hook.js +1 -33
  9. package/build/commands/hooks/autoPrompt.js +1 -32
  10. package/build/commands/hooks/disable.js +1 -33
  11. package/build/commands/hooks/enable.js +1 -58
  12. package/build/commands/hooks/exitPlanMode.js +1 -35
  13. package/build/commands/hooks/index.js +1 -12
  14. package/build/commands/hooks/output.js +1 -71
  15. package/build/commands/hooks/planReminder.js +1 -46
  16. package/build/commands/hooks/planStart.js +1 -30
  17. package/build/commands/hooks/postEdit.js +1 -43
  18. package/build/commands/hooks/preCompact.js +1 -30
  19. package/build/commands/hooks/sessionEnd.js +1 -25
  20. package/build/commands/hooks/sessionStart.js +1 -93
  21. package/build/commands/hooks/stopHook.js +1 -155
  22. package/build/commands/index.js +1 -12
  23. package/build/commands/login.js +1 -26
  24. package/build/commands/setupCi.js +1 -189
  25. package/build/commands/skillup.js +1 -80
  26. package/build/commands/start.js +1 -1
  27. package/build/commands/status.js +1 -198
  28. package/build/commands/update.js +1 -182
  29. package/build/commands/updateEnv.js +1 -1
  30. package/build/commands/useEnv.js +1 -1
  31. package/build/commands/utils/activeProfile.js +1 -76
  32. package/build/commands/utils/browserSessionsApi.js +1 -1
  33. package/build/commands/utils/claudeConfig.js +1 -73
  34. package/build/commands/utils/claudePlugin.js +1 -85
  35. package/build/commands/utils/crypto.js +1 -42
  36. package/build/commands/utils/desirePathLog.js +1 -139
  37. package/build/commands/utils/deviceAuth.js +1 -232
  38. package/build/commands/utils/environment.js +1 -65
  39. package/build/commands/utils/featureApi.js +1 -371
  40. package/build/commands/utils/featureReportGenerator.js +1 -204
  41. package/build/commands/utils/git.js +1 -44
  42. package/build/commands/utils/keychain.js +1 -1
  43. package/build/commands/utils/localAgentInstallationsApi.js +1 -1
  44. package/build/commands/utils/profileMessages.js +1 -8
  45. package/build/commands/utils/profileSetupBanner.js +1 -167
  46. package/build/commands/utils/rangerRoot.js +1 -60
  47. package/build/commands/utils/reportGenerator.js +1 -130
  48. package/build/commands/utils/retry.js +1 -25
  49. package/build/commands/utils/sessionCache.js +1 -299
  50. package/build/commands/utils/settings.js +1 -313
  51. package/build/commands/utils/skillContent.js +1 -28
  52. package/build/commands/utils/skills.js +1 -1
  53. package/build/commands/utils/telemetry.js +1 -254
  54. package/build/commands/utils/userApi.js +1 -32
  55. package/build/commands/utils/version.js +1 -62
  56. package/build/commands/verifyFeature.js +1 -1343
  57. package/build/commands/verifyInBrowser.js +1 -1
  58. package/package.json +1 -1
  59. package/build/cli.js.map +0 -1
  60. package/build/commands/addEnv.js.map +0 -1
  61. package/build/commands/authEncrypt.js.map +0 -1
  62. package/build/commands/clean.js.map +0 -1
  63. package/build/commands/config.js.map +0 -1
  64. package/build/commands/env.js.map +0 -1
  65. package/build/commands/feature.js.map +0 -1
  66. package/build/commands/hook.js.map +0 -1
  67. package/build/commands/hooks/autoPrompt.js.map +0 -1
  68. package/build/commands/hooks/disable.js.map +0 -1
  69. package/build/commands/hooks/enable.js.map +0 -1
  70. package/build/commands/hooks/exitPlanMode.js.map +0 -1
  71. package/build/commands/hooks/index.js.map +0 -1
  72. package/build/commands/hooks/output.js.map +0 -1
  73. package/build/commands/hooks/planReminder.js.map +0 -1
  74. package/build/commands/hooks/planStart.js.map +0 -1
  75. package/build/commands/hooks/postEdit.js.map +0 -1
  76. package/build/commands/hooks/preCompact.js.map +0 -1
  77. package/build/commands/hooks/sessionEnd.js.map +0 -1
  78. package/build/commands/hooks/sessionStart.js.map +0 -1
  79. package/build/commands/hooks/stopHook.js.map +0 -1
  80. package/build/commands/index.js.map +0 -1
  81. package/build/commands/login.js.map +0 -1
  82. package/build/commands/setupCi.js.map +0 -1
  83. package/build/commands/skillup.js.map +0 -1
  84. package/build/commands/start.js.map +0 -1
  85. package/build/commands/status.js.map +0 -1
  86. package/build/commands/update.js.map +0 -1
  87. package/build/commands/updateEnv.js.map +0 -1
  88. package/build/commands/useEnv.js.map +0 -1
  89. package/build/commands/utils/activeProfile.js.map +0 -1
  90. package/build/commands/utils/browserSessionsApi.js.map +0 -1
  91. package/build/commands/utils/claudeConfig.js.map +0 -1
  92. package/build/commands/utils/claudePlugin.js.map +0 -1
  93. package/build/commands/utils/crypto.js.map +0 -1
  94. package/build/commands/utils/desirePathLog.js.map +0 -1
  95. package/build/commands/utils/deviceAuth.js.map +0 -1
  96. package/build/commands/utils/environment.js.map +0 -1
  97. package/build/commands/utils/featureApi.js.map +0 -1
  98. package/build/commands/utils/featureReportGenerator.js.map +0 -1
  99. package/build/commands/utils/git.js.map +0 -1
  100. package/build/commands/utils/keychain.js.map +0 -1
  101. package/build/commands/utils/localAgentInstallationsApi.js.map +0 -1
  102. package/build/commands/utils/profileMessages.js.map +0 -1
  103. package/build/commands/utils/profileSetupBanner.js.map +0 -1
  104. package/build/commands/utils/rangerRoot.js.map +0 -1
  105. package/build/commands/utils/reportGenerator.js.map +0 -1
  106. package/build/commands/utils/retry.js.map +0 -1
  107. package/build/commands/utils/sessionCache.js.map +0 -1
  108. package/build/commands/utils/settings.js.map +0 -1
  109. package/build/commands/utils/skillContent.js.map +0 -1
  110. package/build/commands/utils/skills.js.map +0 -1
  111. package/build/commands/utils/telemetry.js.map +0 -1
  112. package/build/commands/utils/userApi.js.map +0 -1
  113. package/build/commands/utils/version.js.map +0 -1
  114. package/build/commands/verifyFeature.js.map +0 -1
  115. package/build/commands/verifyInBrowser.js.map +0 -1
@@ -1,653 +1 @@
1
- import { readFile, writeFile, mkdir } from 'fs/promises';
2
- import { join } from 'path';
3
- import { existsSync } from 'fs';
4
- import { execSync } from 'child_process';
5
- import inquirer from 'inquirer';
6
- import { createFeature, listFeatures, getFeature, updateFeature, getFeatureReportMarkdown, listFeatureSessions, startSession, concludeSession, createChecklistItem, getActionItems, getItemFeedback, softDeleteFeature, restoreFeature, updateChecklistItem, } from './utils/featureApi.js';
7
- import { getRangerDir } from './utils/rangerRoot.js';
8
- import { registerSession } from './utils/sessionCache.js';
9
- import { getEnvNames } from './env.js';
10
- import { formatProfileRequiredMessage } from './utils/profileMessages.js';
11
- // Active feature file name (relative to .ranger dir)
12
- const ACTIVE_FEATURE_FILE = 'active-feature.txt';
13
- const bold = (text) => `\x1b[1m${text}\x1b[0m`;
14
- /**
15
- * Get the current git remote URL
16
- */
17
- function getGitRepoUrl() {
18
- try {
19
- const remote = execSync('git remote get-url origin', {
20
- encoding: 'utf-8',
21
- stdio: ['pipe', 'pipe', 'pipe'],
22
- }).trim();
23
- // Normalize git URL (remove .git suffix, convert ssh to https format)
24
- return remote
25
- .replace(/\.git$/, '')
26
- .replace(/^git@github\.com:/, 'github.com/')
27
- .replace(/^https?:\/\//, '');
28
- }
29
- catch {
30
- return undefined;
31
- }
32
- }
33
- /**
34
- * Get the current git branch
35
- */
36
- function getGitBranch() {
37
- try {
38
- return execSync('git rev-parse --abbrev-ref HEAD', {
39
- encoding: 'utf-8',
40
- stdio: ['pipe', 'pipe', 'pipe'],
41
- }).trim();
42
- }
43
- catch {
44
- return undefined;
45
- }
46
- }
47
- /**
48
- * Get the active feature ID from the local file
49
- */
50
- export async function getActiveFeatureId() {
51
- const filePath = join(getRangerDir(), ACTIVE_FEATURE_FILE);
52
- if (!existsSync(filePath)) {
53
- return null;
54
- }
55
- try {
56
- const content = await readFile(filePath, 'utf-8');
57
- return content.trim() || null;
58
- }
59
- catch {
60
- return null;
61
- }
62
- }
63
- /**
64
- * Set the active feature ID
65
- */
66
- async function setActiveFeatureId(featureId) {
67
- const dir = getRangerDir();
68
- const filePath = join(dir, ACTIVE_FEATURE_FILE);
69
- if (!existsSync(dir)) {
70
- await mkdir(dir, { recursive: true });
71
- }
72
- await writeFile(filePath, featureId, 'utf-8');
73
- }
74
- /**
75
- * Get a text-based progress display for a feature's stats
76
- */
77
- function getProgressDisplay(stats) {
78
- const verified = stats.verifiedItems || 0;
79
- const inProgress = stats.verificationInProgressItems || 0;
80
- const blocked = stats.blockedItems || 0;
81
- const incomplete = stats.incompleteItems || 0;
82
- const pending = stats.pendingItems || 0;
83
- const nonCancelled = verified + inProgress + blocked + incomplete + pending;
84
- if (nonCancelled === 0) {
85
- return '-';
86
- }
87
- // Build a 10-char progress bar
88
- const barWidth = 10;
89
- const filledCount = Math.round((verified / nonCancelled) * barWidth);
90
- const bar = '\u2588'.repeat(filledCount) + '\u2591'.repeat(barWidth - filledCount);
91
- // Build breakdown of non-verified issues
92
- const parts = [];
93
- if (blocked > 0)
94
- parts.push(`${blocked} blocked`);
95
- if (incomplete > 0)
96
- parts.push(`${incomplete} incomplete`);
97
- if (inProgress > 0)
98
- parts.push(`${inProgress} in progress`);
99
- const suffix = parts.length > 0 ? ` (${parts.join(', ')})` : '';
100
- return `${bar} ${verified}/${nonCancelled} verified${suffix}`;
101
- }
102
- /**
103
- * Display a feature with its scenarios (current session items only)
104
- */
105
- function displayFeature(feature, checklistItems) {
106
- console.log(`\n${feature.name} (${feature.dashboardUrl})`);
107
- if (feature.stats) {
108
- console.log(` Progress: ${getProgressDisplay(feature.stats)}`);
109
- }
110
- if (feature.description) {
111
- console.log(` Description: ${feature.description}`);
112
- }
113
- if (feature.gitRepoUrl) {
114
- console.log(` Repository: ${feature.gitRepoUrl}`);
115
- }
116
- if (feature.gitBranch) {
117
- console.log(` Branch: ${feature.gitBranch}`);
118
- }
119
- console.log(` Created: ${new Date(feature.createdAt).toLocaleString()}`);
120
- if (feature.completedAt) {
121
- console.log(` Completed: ${new Date(feature.completedAt).toLocaleString()}`);
122
- }
123
- if (checklistItems && checklistItems.length > 0) {
124
- console.log('\n Scenarios:');
125
- for (let i = 0; i < checklistItems.length; i++) {
126
- const item = checklistItems[i];
127
- let itemEmoji;
128
- if (item.status === 'closed' &&
129
- item.terminalReason === 'approved') {
130
- itemEmoji = '\u2705'; // green checkmark for approved
131
- }
132
- else if (item.status === 'verified') {
133
- itemEmoji = '\ud83d\udfe2'; // green circle for verified (awaiting review)
134
- }
135
- else if (item.status === 'incomplete') {
136
- itemEmoji = '\ud83d\udfe0'; // orange circle
137
- }
138
- else if (item.status === 'blocked') {
139
- itemEmoji = '\ud83d\uded1'; // stop sign
140
- }
141
- else if (item.status === 'closed') {
142
- itemEmoji = '\u26d4'; // no entry
143
- }
144
- else if (item.status === 'verification_in_progress') {
145
- itemEmoji = '\u23f3'; // hourglass
146
- }
147
- else {
148
- itemEmoji = '\u2b1c'; // white square for pending
149
- }
150
- const versionTag = item.version > 1 ? ` (v${item.version})` : '';
151
- const feedbackHint = item.version > 1 &&
152
- item.parentItemId &&
153
- item.status !== 'closed' &&
154
- item.status !== 'verified'
155
- ? ' [has reviewer feedback]'
156
- : '';
157
- console.log(` ${i + 1}. ${itemEmoji} ${item.description}${versionTag}${feedbackHint}`);
158
- if (item.status === 'blocked' && item.blockedReason) {
159
- console.log(` Blocked: ${item.blockedReason}`);
160
- }
161
- if (item.status === 'incomplete' && item.incompleteReason) {
162
- console.log(` Incomplete: ${item.incompleteReason}`);
163
- }
164
- if (item.status === 'closed' &&
165
- item.terminalReason === 'approved') {
166
- console.log(` Approved`);
167
- }
168
- else if (item.status === 'closed' && item.canceledReason) {
169
- console.log(` Cancelled: ${item.canceledReason}`);
170
- }
171
- }
172
- }
173
- }
174
- /**
175
- * Create a new feature
176
- */
177
- export async function featureCreate(name, options) {
178
- const { envNames } = await getEnvNames();
179
- if (envNames.length === 0) {
180
- console.warn(`\n${formatProfileRequiredMessage((text) => bold(text))}`);
181
- }
182
- // Get scenarios from array (each -c/--scenario flag is one scenario)
183
- const checklistItems = options.scenarios
184
- ? options.scenarios.map((s) => s.trim())
185
- : [];
186
- // Auto-detect git context
187
- const gitRepoUrl = getGitRepoUrl();
188
- const gitBranch = getGitBranch();
189
- console.log('\nCreating feature review...');
190
- const feature = await createFeature({
191
- name,
192
- description: options.description,
193
- checklist: checklistItems,
194
- gitRepoUrl,
195
- gitBranch,
196
- });
197
- // Set as active feature
198
- await setActiveFeatureId(feature.id);
199
- // Start the session immediately so it's in_progress
200
- await tryStartCurrentSession(feature.id);
201
- // Register Claude session if provided via env var
202
- const claudeSessionId = process.env.CLAUDE_SESSION_ID;
203
- if (claudeSessionId) {
204
- registerSession(claudeSessionId, feature.id);
205
- }
206
- console.log(`\n\u2705 Feature review created: ${feature.id}`);
207
- displayFeature(feature, feature.checklistItems);
208
- console.log(`\n\u27a1\ufe0f Set as active feature review`);
209
- }
210
- /**
211
- * List features
212
- */
213
- export async function featureList(options) {
214
- const filters = {
215
- limit: options.limit ?? 10,
216
- offset: options.offset ?? 0,
217
- includeDeleted: options.includeDeleted,
218
- };
219
- if (options.currentBranch) {
220
- filters.gitRepoUrl = getGitRepoUrl();
221
- filters.gitBranch = getGitBranch();
222
- }
223
- const result = await listFeatures(filters);
224
- if (result.items.length === 0) {
225
- console.log('\nNo feature reviews found.');
226
- return;
227
- }
228
- const showing = result.totalCount > result.items.length
229
- ? `Showing ${result.items.length} of ${result.totalCount}`
230
- : `${result.totalCount} feature review(s) found`;
231
- console.log(`\n${showing}:\n`);
232
- for (const feature of result.items) {
233
- console.log(`${feature.name}`);
234
- console.log(` ID: ${feature.id}`);
235
- if (feature.stats) {
236
- console.log(` Progress: ${getProgressDisplay(feature.stats)}`);
237
- }
238
- if (feature.gitBranch) {
239
- console.log(` Branch: ${feature.gitBranch}`);
240
- }
241
- console.log(` Created: ${new Date(feature.createdAt).toLocaleDateString()}`);
242
- console.log('');
243
- }
244
- // Show next page command if there are more items
245
- const currentLimit = filters.limit ?? 10;
246
- const nextOffset = (filters.offset ?? 0) + result.items.length;
247
- if (nextOffset < result.totalCount) {
248
- const limitFlag = currentLimit !== 10 ? ` --limit ${currentLimit}` : '';
249
- const branchFlag = options.currentBranch ? ' --current-branch' : '';
250
- const deletedFlag = options.includeDeleted ? ' --include-deleted' : '';
251
- console.log(`Next page: ranger list --offset ${nextOffset}${limitFlag}${branchFlag}${deletedFlag}`);
252
- }
253
- }
254
- /**
255
- * Show feature details
256
- */
257
- export async function featureShow(id) {
258
- const featureId = id || (await getActiveFeatureId());
259
- if (!featureId) {
260
- throw new Error('No feature review ID provided and no active feature review set. Run: ranger resume <id> to set an active feature review');
261
- }
262
- const feature = await getFeature(featureId);
263
- displayFeature(feature, feature.checklistItems);
264
- }
265
- /**
266
- * Resume feature matching current git context
267
- * @param id Optional feature ID to resume directly (bypasses search/prompt)
268
- */
269
- export async function featureResume(id) {
270
- // If ID provided, set it as the active feature directly
271
- if (id) {
272
- const feature = await getFeature(id);
273
- await setActiveFeatureId(feature.id);
274
- // Update the feature's gitBranch to the current branch
275
- const currentBranch = getGitBranch();
276
- if (currentBranch && currentBranch !== feature.gitBranch) {
277
- await updateFeature(feature.id, { gitBranch: currentBranch });
278
- console.log(` Updated branch to: ${currentBranch}`);
279
- }
280
- console.log(`\n\u2705 Resumed feature review: ${feature.name} (${feature.id})`);
281
- // Start the current session if it's ready
282
- await tryStartCurrentSession(feature.id);
283
- // Register Claude session if provided via env var
284
- const claudeSessionId = process.env.CLAUDE_SESSION_ID;
285
- if (claudeSessionId) {
286
- registerSession(claudeSessionId, feature.id);
287
- }
288
- displayFeature(feature, feature.checklistItems);
289
- await printFeedbackNudge(feature.id);
290
- return;
291
- }
292
- const gitRepoUrl = getGitRepoUrl();
293
- const gitBranch = getGitBranch();
294
- if (!gitRepoUrl && !gitBranch) {
295
- throw new Error('Not in a git repository.');
296
- }
297
- console.log(`\nSearching for feature reviews matching: ${gitRepoUrl || 'any'} / ${gitBranch || 'any'}...`);
298
- const result = await listFeatures({
299
- gitRepoUrl,
300
- gitBranch,
301
- });
302
- // Filter to non-completed features
303
- const activeFeatures = result.items.filter((f) => !f.completedAt);
304
- if (activeFeatures.length === 0) {
305
- console.log('\nNo matching active feature reviews found.');
306
- console.log('Run: ranger create "<name>" to create a new feature review');
307
- return;
308
- }
309
- let selectedFeature;
310
- if (activeFeatures.length === 1) {
311
- selectedFeature = activeFeatures[0];
312
- }
313
- else {
314
- // Multiple matches - check if we're in interactive mode
315
- const isInteractive = process.stdin.isTTY && process.stdout.isTTY;
316
- if (!isInteractive) {
317
- // Non-interactive mode: list features and require explicit ID
318
- console.log('\nNon-interactive mode detected. Multiple feature reviews found:');
319
- for (const f of activeFeatures) {
320
- console.log(` ${f.name}`);
321
- console.log(` ID: ${f.id}`);
322
- if (f.stats) {
323
- console.log(` Progress: ${getProgressDisplay(f.stats)}`);
324
- }
325
- if (f.gitBranch) {
326
- console.log(` Branch: ${f.gitBranch}`);
327
- }
328
- }
329
- throw new Error('Please specify the feature review ID: ranger resume <id>');
330
- }
331
- // Interactive mode: prompt user to select
332
- const { selected } = await inquirer.prompt([
333
- {
334
- type: 'list',
335
- name: 'selected',
336
- message: 'Multiple feature reviews found. Select one:',
337
- choices: activeFeatures.map((f) => ({
338
- name: `${f.name} (${f.id})`,
339
- value: f.id,
340
- })),
341
- },
342
- ]);
343
- selectedFeature = activeFeatures.find((f) => f.id === selected);
344
- }
345
- await setActiveFeatureId(selectedFeature.id);
346
- // Update the feature's gitBranch to the current branch
347
- if (gitBranch && gitBranch !== selectedFeature.gitBranch) {
348
- await updateFeature(selectedFeature.id, { gitBranch });
349
- console.log(` Updated branch to: ${gitBranch}`);
350
- }
351
- console.log(`\n\u2705 Resumed feature review: ${selectedFeature.name} (${selectedFeature.id})`);
352
- // Start the current session if it's ready
353
- await tryStartCurrentSession(selectedFeature.id);
354
- // Register Claude session if provided via env var
355
- const fullFeature = await getFeature(selectedFeature.id);
356
- const claudeSessionId = process.env.CLAUDE_SESSION_ID;
357
- if (claudeSessionId) {
358
- registerSession(claudeSessionId, fullFeature.id);
359
- }
360
- // Show current status
361
- displayFeature(fullFeature, fullFeature.checklistItems);
362
- await printFeedbackNudge(fullFeature.id);
363
- }
364
- /**
365
- * Print a nudge about unaddressed reviewer comments if any scenarios have them
366
- */
367
- async function printFeedbackNudge(featureId) {
368
- try {
369
- const { items } = await getActionItems(featureId);
370
- let totalComments = 0;
371
- let itemsWithComments = 0;
372
- for (const item of items) {
373
- const feedback = await getItemFeedback(featureId, item.id);
374
- if (feedback.unaddressedComments.length > 0) {
375
- totalComments += feedback.unaddressedComments.length;
376
- itemsWithComments++;
377
- }
378
- }
379
- if (totalComments > 0) {
380
- console.log(`\n\u26a0\ufe0f ${totalComments} unaddressed reviewer comment(s) across ${itemsWithComments} scenario(s).`);
381
- console.log(' Run `ranger get-review` to see what needs to be fixed.');
382
- }
383
- }
384
- catch {
385
- // Silently ignore - feedback check is best-effort
386
- }
387
- }
388
- /**
389
- * Try to start the current session if it's in ready status
390
- */
391
- async function tryStartCurrentSession(featureId) {
392
- try {
393
- const { sessions } = await listFeatureSessions(featureId);
394
- // Find the current session (most recent ready one)
395
- const readySession = sessions.find((s) => s.status === 'ready');
396
- if (readySession) {
397
- await startSession(featureId, readySession.id);
398
- console.log(` \u25b6\ufe0f Started session ${readySession.iteration}`);
399
- }
400
- }
401
- catch {
402
- // Silently ignore - session may already be started or in another state
403
- }
404
- }
405
- /**
406
- * Generate feature report
407
- */
408
- export async function featureReport(id, options) {
409
- const featureId = id || (await getActiveFeatureId());
410
- if (!featureId) {
411
- throw new Error('No feature review ID provided and no active feature review set.');
412
- }
413
- console.log('\nGenerating report...');
414
- // Fetch markdown report from API (includes embedded screenshots)
415
- const markdown = await getFeatureReportMarkdown(featureId, {
416
- style: options.style,
417
- });
418
- // Determine output path
419
- const outputPath = options.output || join(getRangerDir(), 'reports', `${featureId}.md`);
420
- // Ensure directory exists
421
- const outputDir = join(outputPath, '..');
422
- if (!existsSync(outputDir)) {
423
- await mkdir(outputDir, { recursive: true });
424
- }
425
- await writeFile(outputPath, markdown, 'utf-8');
426
- console.log(`\n\u2705 Report generated: ${outputPath}`);
427
- }
428
- /**
429
- * Conclude the current session (even with incomplete items)
430
- */
431
- export async function featureConcludeSession(id) {
432
- const featureId = id || (await getActiveFeatureId());
433
- if (!featureId) {
434
- throw new Error('No feature review ID provided and no active feature review set. Run: ranger resume <id> to set an active feature review');
435
- }
436
- const feature = await getFeature(featureId);
437
- if (!feature.currentSessionId) {
438
- throw new Error('No active session for this feature review.');
439
- }
440
- const { sessions } = await listFeatureSessions(featureId);
441
- const currentSession = sessions.find((s) => s.id === feature.currentSessionId);
442
- if (!currentSession) {
443
- throw new Error('Current session not found.');
444
- }
445
- if (currentSession.status === 'completed') {
446
- console.log('\n\u2705 Session is already completed.');
447
- return;
448
- }
449
- if (currentSession.status !== 'in_progress') {
450
- throw new Error(`Cannot conclude session with status: ${currentSession.status}`);
451
- }
452
- console.log('\nConcluding session...');
453
- await concludeSession(featureId, feature.currentSessionId);
454
- console.log(`\n\u2705 Session ${currentSession.iteration} concluded.`);
455
- console.log(` The feature review is now ready for human review on the Ranger dashboard.`);
456
- }
457
- /**
458
- * List sessions for a feature
459
- */
460
- export async function featureSessions(id) {
461
- const featureId = id || (await getActiveFeatureId());
462
- if (!featureId) {
463
- throw new Error('No feature review ID provided and no active feature review set.');
464
- }
465
- const feature = await getFeature(featureId);
466
- console.log(`\n\ud83d\udcc1 Sessions for feature review: ${feature.name}\n`);
467
- const { sessions } = await listFeatureSessions(featureId);
468
- if (sessions.length === 0) {
469
- console.log('No sessions found.');
470
- return;
471
- }
472
- for (const session of sessions) {
473
- const statusEmoji = session.status === 'completed'
474
- ? '\u2705'
475
- : session.status === 'in_progress'
476
- ? '\ud83d\udd04'
477
- : '\u25b6\ufe0f'; // ready
478
- console.log(`${statusEmoji} Session ${session.iteration} (${session.id})`);
479
- console.log(` Status: ${session.status}`);
480
- console.log(` Created: ${new Date(session.createdAt).toLocaleString()}`);
481
- if (session.completedAt) {
482
- console.log(` Completed: ${new Date(session.completedAt).toLocaleString()}`);
483
- }
484
- console.log('');
485
- }
486
- }
487
- /**
488
- * Add a scenario to the active feature
489
- */
490
- export async function featureAddScenario(description, id) {
491
- const featureId = id || (await getActiveFeatureId());
492
- if (!featureId) {
493
- throw new Error('No feature review ID provided and no active feature review set. Run: ranger resume <id> to set an active feature review');
494
- }
495
- const feature = await getFeature(featureId);
496
- // Check if there's an active (non-submitted) review
497
- if (feature.latestReview && !feature.latestReview.submittedAt) {
498
- throw new Error(`Cannot add scenario: feature review has an active review. Please finish your review first: ${feature.dashboardUrl}`);
499
- }
500
- // Create the scenario
501
- await createChecklistItem(featureId, description);
502
- // Refetch and display the updated feature
503
- const updatedFeature = await getFeature(featureId);
504
- console.log(`\n\u2705 Scenario added`);
505
- displayFeature(updatedFeature, updatedFeature.checklistItems);
506
- }
507
- /**
508
- * Edit a scenario description on the active feature
509
- */
510
- export async function featureEditScenario(description, options) {
511
- const featureId = options.id || (await getActiveFeatureId());
512
- if (!featureId) {
513
- console.error('\n\u274c No feature review ID provided and no active feature review set.');
514
- console.error('Run: ranger resume <id> to set an active feature review');
515
- process.exit(1);
516
- }
517
- const feature = await getFeature(featureId);
518
- // Check if there's an active (non-submitted) review
519
- if (feature.latestReview && !feature.latestReview.submittedAt) {
520
- console.error('\n\u274c Cannot edit scenario: feature review has an active review.');
521
- console.error('Please finish your review first:');
522
- console.error(` ${feature.dashboardUrl}`);
523
- process.exit(1);
524
- }
525
- const checklistItems = feature.checklistItems || [];
526
- if (checklistItems.length === 0) {
527
- console.error('\n\u274c No scenarios found to edit.');
528
- process.exit(1);
529
- }
530
- if (options.scenario === undefined) {
531
- console.error('\n\u274c No scenario number provided. Use --scenario <number>.');
532
- console.log('\nAvailable scenarios:');
533
- for (let i = 0; i < checklistItems.length; i++) {
534
- console.log(` ${i + 1}. ${checklistItems[i].description}`);
535
- }
536
- process.exit(1);
537
- }
538
- const itemIndex = options.scenario - 1;
539
- if (itemIndex < 0 || itemIndex >= checklistItems.length) {
540
- console.error(`\n\u274c Invalid scenario index: ${options.scenario}. Feature review has ${checklistItems.length} scenarios.`);
541
- console.log('\nAvailable scenarios:');
542
- for (let i = 0; i < checklistItems.length; i++) {
543
- console.log(` ${i + 1}. ${checklistItems[i].description}`);
544
- }
545
- process.exit(1);
546
- }
547
- const checklistItem = checklistItems[itemIndex];
548
- await updateChecklistItem(featureId, checklistItem.id, { description });
549
- const updatedFeature = await getFeature(featureId);
550
- console.log(`\n\u2705 Scenario updated`);
551
- displayFeature(updatedFeature, updatedFeature.checklistItems);
552
- }
553
- /**
554
- * Get review comments for all scenarios in a feature
555
- */
556
- /**
557
- * Download an image from a signed URL to a local file path.
558
- * Returns the local file path on success, or null on failure.
559
- */
560
- async function downloadFeedbackImage(assetUrl, commentId) {
561
- try {
562
- const imagesDir = join(getRangerDir(), 'feedback-images');
563
- await mkdir(imagesDir, { recursive: true });
564
- const filePath = join(imagesDir, `comment_${commentId}.png`);
565
- const response = await fetch(assetUrl);
566
- if (!response.ok) {
567
- return null;
568
- }
569
- const buffer = Buffer.from(await response.arrayBuffer());
570
- await writeFile(filePath, buffer);
571
- return filePath;
572
- }
573
- catch {
574
- return null;
575
- }
576
- }
577
- export async function featureGetReview(id) {
578
- const featureId = id || (await getActiveFeatureId());
579
- if (!featureId) {
580
- throw new Error('No feature review ID provided and no active feature review set. Run: ranger resume <id> to set an active feature review');
581
- }
582
- const feature = await getFeature(featureId);
583
- const { items: actionItems } = await getActionItems(featureId);
584
- console.log(`\n\ud83d\udccb Feedback for: ${feature.name} (${feature.id})\n`);
585
- if (actionItems.length === 0) {
586
- console.log('No scenarios found.');
587
- return;
588
- }
589
- for (let i = 0; i < actionItems.length; i++) {
590
- const item = actionItems[i];
591
- const versionTag = item.version > 1 ? ` (v${item.version})` : '';
592
- const feedback = await getItemFeedback(featureId, item.id);
593
- if (feedback.unaddressedComments.length > 0) {
594
- console.log(`Scenario ${i + 1}: "${item.description}"${versionTag} \u2014 ${feedback.unaddressedComments.length} unaddressed comment(s)`);
595
- for (const comment of feedback.unaddressedComments) {
596
- const date = new Date(comment.createdAt).toLocaleDateString('en-US', { month: 'short', day: 'numeric' });
597
- const author = comment.authorName || comment.authorEmail || 'Unknown';
598
- console.log(` \ud83d\udcac ${author} (${date}): "${comment.content}"`);
599
- // Download and display annotated screenshot if available
600
- if (comment.assetUrl) {
601
- const localPath = await downloadFeedbackImage(comment.assetUrl, comment.id);
602
- if (localPath) {
603
- console.log(` \ud83d\udcf7 Screenshot: ${localPath}`);
604
- }
605
- }
606
- if (comment.spatial) {
607
- const coords = comment.spatial.coordinates;
608
- if (comment.spatial.type === 'point' &&
609
- coords.length >= 2) {
610
- console.log(` \ud83d\udccd Annotation: point at (${coords[0].toFixed(2)}, ${coords[1].toFixed(2)})`);
611
- }
612
- else {
613
- console.log(` \ud83d\udccd Annotation: ${comment.spatial.type} at [${coords.map((c) => c.toFixed(2)).join(', ')}]`);
614
- }
615
- }
616
- }
617
- if (feedback.canonicalFlow) {
618
- console.log(` Expected flow:`);
619
- for (const line of feedback.canonicalFlow.split('\n')) {
620
- console.log(` ${line}`);
621
- }
622
- }
623
- }
624
- else {
625
- console.log(`Scenario ${i + 1}: "${item.description}"${versionTag} \u2014 no comments`);
626
- console.log(` \u2705 No feedback to address`);
627
- }
628
- console.log('');
629
- }
630
- }
631
- /**
632
- * Soft delete a feature
633
- */
634
- export async function featureDelete(id) {
635
- const featureId = id || (await getActiveFeatureId());
636
- if (!featureId) {
637
- throw new Error('No feature review ID provided and no active feature review set. Run: ranger resume <id> to set an active feature review');
638
- }
639
- const feature = await getFeature(featureId);
640
- await softDeleteFeature(featureId);
641
- console.log(`\n\ud83d\uddd1\ufe0f Deleted feature review: ${feature.name} (${featureId})`);
642
- console.log(' To restore it, run: ranger restore ' + featureId);
643
- }
644
- /**
645
- * Restore a soft-deleted feature
646
- */
647
- export async function featureRestore(id) {
648
- await restoreFeature(id);
649
- const feature = await getFeature(id);
650
- console.log(`\n\u2705 Restored feature review: ${feature.name} (${id})`);
651
- displayFeature(feature, feature.checklistItems);
652
- }
653
- //# sourceMappingURL=feature.js.map
1
+ const _0x2f2801=_0x5a31;(function(_0x3b6808,_0x580e6b){const _0x4395da=_0x5a31,_0x488104=_0x3b6808();while(!![]){try{const _0x3fc8cb=parseInt(_0x4395da(0x1ec))/0x1+-parseInt(_0x4395da(0x25b))/0x2+parseInt(_0x4395da(0x257))/0x3+parseInt(_0x4395da(0x273))/0x4+-parseInt(_0x4395da(0x219))/0x5*(-parseInt(_0x4395da(0x244))/0x6)+parseInt(_0x4395da(0x25d))/0x7+-parseInt(_0x4395da(0x233))/0x8;if(_0x3fc8cb===_0x580e6b)break;else _0x488104['push'](_0x488104['shift']());}catch(_0x541277){_0x488104['push'](_0x488104['shift']());}}}(_0x293e,0xe833f));import{readFile,writeFile,mkdir}from'fs/promises';import{join}from'path';import{existsSync}from'fs';import{execSync}from'child_process';import _0x59d90c from'inquirer';function _0x5a31(_0x1b0e62,_0x2e2ae2){_0x1b0e62=_0x1b0e62-0x1e9;const _0x293e13=_0x293e();let _0x5a314a=_0x293e13[_0x1b0e62];return _0x5a314a;}import{createFeature,listFeatures,getFeature,updateFeature,getFeatureReportMarkdown,listFeatureSessions,startSession,concludeSession,createChecklistItem,getActionItems,getItemFeedback,softDeleteFeature,restoreFeature,updateChecklistItem}from'./utils/featureApi.js';import{getRangerDir}from'./utils/rangerRoot.js';function _0x293e(){const _0x115ec4=['dashboardUrl','\x0aSearching\x20for\x20feature\x20reviews\x20matching:\x20','canonicalFlow','jNVCR','\x20\x20\x20Branch:\x20','utf-8','PQqWI','\x0a❌\x20Cannot\x20edit\x20scenario:\x20feature\x20review\x20has\x20an\x20active\x20review.','\x0a✅\x20Resumed\x20feature\x20review:\x20','No\x20scenarios\x20found.','dFKZh','git\x20remote\x20get-url\x20origin','KYemf','No\x20feature\x20review\x20ID\x20provided\x20and\x20no\x20active\x20feature\x20review\x20set.','github.com/','\x20\x20\x20To\x20restore\x20it,\x20run:\x20ranger\x20restore\x20','\x20at\x20[','currentSessionId','\x20\x20\x20Run\x20`ranger\x20get-review`\x20to\x20see\x20what\x20needs\x20to\x20be\x20fixed.','\x1b[0m','verificationInProgressItems','\x20\x20\x20ID:\x20','KQFYJ','myuEK','\x0aNo\x20matching\x20active\x20feature\x20reviews\x20found.','\x20unaddressed\x20reviewer\x20comment(s)\x20across\x20','1728388SvZgNE','\x20\x20\x20\x20\x20📍\x20Annotation:\x20point\x20at\x20(','trim','drakK','Unknown','kspdU','\x20\x20\x20Updated\x20branch\x20to:\x20','\x0a⚠️\x20\x20','toFixed','\x20scenario(s).','blocked','completedAt','vhGjW','CJlzo','Not\x20in\x20a\x20git\x20repository.','includeDeleted','gitBranch','pipe','KDQnN','bIjgb','offset','\x0aCreating\x20feature\x20review...','map','totalCount','createdAt','SMsnr','style','CLAUDE_SESSION_ID','.\x20Feature\x20review\x20has\x20','\x0aConcluding\x20session...','\x20\x20\x20\x20\x20\x20Approved','isTTY','ewlNm','gitRepoUrl','\x20\x20\x20\x20\x20📍\x20Annotation:\x20','\x0aAvailable\x20scenarios:','No\x20feature\x20review\x20ID\x20provided\x20and\x20no\x20active\x20feature\x20review\x20set.\x20Run:\x20ranger\x20resume\x20<id>\x20to\x20set\x20an\x20active\x20feature\x20review','DFpDT','ueedx','round','scenario','completed','nYNPC','NEVpQ','\x20\x20\x20Completed:\x20','18895ZrXDbe','eZbmI','Current\x20session\x20not\x20found.','incompleteItems','GYtTF','parentItemId','status','eEpkH','\x20verified','\x20in\x20progress','filter','in_progress','checklistItems','TytjO','split',':\x20\x22','\x20\x20\x20Repository:\x20','\x0a📋\x20Feedback\x20for:\x20','lwxVn','verified','oonyU','short','en-US','toLocaleDateString','mNbsp','\x20—\x20','22517112bZEYdH','\x20\x20\x20Created:\x20','IFOyK','UjuXo','coordinates','env','unaddressedComments','\x20\x20✅\x20No\x20feedback\x20to\x20address','VmknY','latestReview','\x0a❌\x20Invalid\x20scenario\x20index:\x20','numeric','WDsgg','\x0a➡️\x20\x20Set\x20as\x20active\x20feature\x20review','\x20\x20\x20The\x20feature\x20review\x20is\x20now\x20ready\x20for\x20human\x20review\x20on\x20the\x20Ranger\x20dashboard.','Please\x20specify\x20the\x20feature\x20review\x20ID:\x20ranger\x20resume\x20<id>','length','1338tJzYcM','\x20\x20\x20\x20\x20Progress:\x20','lyeVf','\x0a🗑️\x20\x20Deleted\x20feature\x20review:\x20','version','find','zZXoF','idDxA','push','description','fZmAc','assetUrl','Run:\x20ranger\x20create\x20\x22<name>\x22\x20to\x20create\x20a\x20new\x20feature\x20review','TzUAB','WLzUv','items','Please\x20finish\x20your\x20review\x20first:','\x0aNon-interactive\x20mode\x20detected.\x20Multiple\x20feature\x20reviews\x20found:','lPFLw','2554377JMqEvS','from','JvDVd','stdin','3348208nrRlJX','qStax','7834050UwNabj','\x1b[1m','currentBranch','\x0a✅\x20Session\x20is\x20already\x20completed.','ZqmKP','\x20\x20\x20Progress:\x20','CeJuR','selected','aSKRC','rBPlI','spatial','hMoBf','PDFmM','limit','eeQiC','turEb','vQkvD','iteration','\x20Session\x20','kSFdB','\x20(v','sTplq','3592528bIrCfd','exit','\x0a❌\x20No\x20scenario\x20number\x20provided.\x20Use\x20--scenario\x20<number>.','ZAwNZ','incompleteReason','git\x20rev-parse\x20--abbrev-ref\x20HEAD','any','log','cIIdg','Scenario\x20','type','verifiedItems','toLocaleString','VHeUR','error','terminalReason','content','jfUZr','stats','plezX','agNFm','\x20\x20\x20\x20','comment_','name','warn','closed','\x20\x20\x20Description:\x20'];_0x293e=function(){return _0x115ec4;};return _0x293e();}import{registerSession}from'./utils/sessionCache.js';import{getEnvNames}from'./env.js';import{formatProfileRequiredMessage}from'./utils/profileMessages.js';const ACTIVE_FEATURE_FILE='active-feature.txt',bold=_0x549476=>_0x2f2801(0x25e)+_0x549476+_0x2f2801(0x2a1);function getGitRepoUrl(){const _0x252760=_0x2f2801,_0x3c542b={'wMjof':function(_0x111683,_0x4dd82c,_0x5d2b42){return _0x111683(_0x4dd82c,_0x5d2b42);},'duVHY':_0x252760(0x299),'NEVpQ':_0x252760(0x293),'fZmAc':_0x252760(0x1fd)};try{const _0x4b1a6c=_0x3c542b['wMjof'](execSync,_0x3c542b['duVHY'],{'encoding':_0x3c542b[_0x252760(0x217)],'stdio':[_0x252760(0x1fd),_0x252760(0x1fd),_0x3c542b[_0x252760(0x24e)]]})['trim']();return _0x4b1a6c['replace'](/\.git$/,'')['replace'](/^git@github\.com:/,_0x252760(0x29c))['replace'](/^https?:\/\//,'');}catch{return undefined;}}function getGitBranch(){const _0x566562=_0x2f2801,_0x44bd48={'TNeod':'pipe'};try{return execSync(_0x566562(0x278),{'encoding':'utf-8','stdio':['pipe',_0x44bd48['TNeod'],_0x44bd48['TNeod']]})['trim']();}catch{return undefined;}}export async function getActiveFeatureId(){const _0x6f7cb9=_0x2f2801,_0x236ae1={'qStax':function(_0x54a53a,_0x38a66c,_0x57c3dd){return _0x54a53a(_0x38a66c,_0x57c3dd);},'lwxVn':function(_0x4a63f6,_0x34622a){return _0x4a63f6(_0x34622a);},'JJbfe':function(_0x4d6b22,_0x2e4c82,_0x2f9994){return _0x4d6b22(_0x2e4c82,_0x2f9994);}},_0x4855e0=_0x236ae1[_0x6f7cb9(0x25c)](join,getRangerDir(),ACTIVE_FEATURE_FILE);if(!_0x236ae1[_0x6f7cb9(0x22b)](existsSync,_0x4855e0))return null;try{const _0x24f1db=await _0x236ae1['JJbfe'](readFile,_0x4855e0,'utf-8');return _0x24f1db[_0x6f7cb9(0x1ee)]()||null;}catch{return null;}}async function setActiveFeatureId(_0x3ef7c1){const _0x45023e=_0x2f2801,_0x2d2be3={'aSKRC':function(_0x3b1cb5,_0xe5d5c7,_0x2c21be){return _0x3b1cb5(_0xe5d5c7,_0x2c21be);},'qPADl':function(_0x520ad8,_0x1926cd){return _0x520ad8(_0x1926cd);},'TaQYL':function(_0x1aa495,_0x14122c,_0x52b428,_0x48ba27){return _0x1aa495(_0x14122c,_0x52b428,_0x48ba27);},'GPtPt':_0x45023e(0x293)},_0x766f55=getRangerDir(),_0x41ac24=_0x2d2be3[_0x45023e(0x265)](join,_0x766f55,ACTIVE_FEATURE_FILE);!_0x2d2be3['qPADl'](existsSync,_0x766f55)&&await mkdir(_0x766f55,{'recursive':!![]}),await _0x2d2be3['TaQYL'](writeFile,_0x41ac24,_0x3ef7c1,_0x2d2be3['GPtPt']);}function getProgressDisplay(_0x2a1f21){const _0x12b145=_0x2f2801,_0x2b2826={'xjVXw':function(_0x285fec,_0x22deb5){return _0x285fec+_0x22deb5;},'EjTVm':function(_0x8f212a,_0x3fb152){return _0x8f212a+_0x3fb152;},'eEpkH':function(_0xd62a21,_0x4f9745){return _0xd62a21+_0x4f9745;},'AUiqQ':function(_0x345383,_0x18f6ad){return _0x345383+_0x18f6ad;},'CJlzo':function(_0x269a03,_0xcf5e61){return _0x269a03*_0xcf5e61;},'ghuoj':function(_0x4ed414,_0x438b8f){return _0x4ed414/_0x438b8f;},'IQpOG':function(_0x422d3f,_0x355ba9){return _0x422d3f-_0x355ba9;},'HPAMc':function(_0x2676ca,_0x5eb130){return _0x2676ca>_0x5eb130;},'yNOFe':function(_0x2bb4e8,_0x28971e){return _0x2bb4e8>_0x28971e;}},_0x289a79=_0x2a1f21[_0x12b145(0x27e)]||0x0,_0x428656=_0x2a1f21[_0x12b145(0x2a2)]||0x0,_0x3c681a=_0x2a1f21['blockedItems']||0x0,_0x133f51=_0x2a1f21[_0x12b145(0x21c)]||0x0,_0x421444=_0x2a1f21['pendingItems']||0x0,_0x5cca5f=_0x2b2826['xjVXw'](_0x2b2826['EjTVm'](_0x2b2826[_0x12b145(0x220)](_0x2b2826['AUiqQ'](_0x289a79,_0x428656),_0x3c681a),_0x133f51),_0x421444);if(_0x5cca5f===0x0)return'-';const _0x31a27c=0xa,_0x2a3fba=Math[_0x12b145(0x213)](_0x2b2826[_0x12b145(0x1f9)](_0x2b2826['ghuoj'](_0x289a79,_0x5cca5f),_0x31a27c)),_0x5d8e18='█'['repeat'](_0x2a3fba)+'░'['repeat'](_0x2b2826['IQpOG'](_0x31a27c,_0x2a3fba)),_0x4470e1=[];if(_0x2b2826['HPAMc'](_0x3c681a,0x0))_0x4470e1[_0x12b145(0x24c)](_0x3c681a+'\x20blocked');if(_0x2b2826['yNOFe'](_0x133f51,0x0))_0x4470e1[_0x12b145(0x24c)](_0x133f51+'\x20incomplete');if(_0x428656>0x0)_0x4470e1[_0x12b145(0x24c)](_0x428656+_0x12b145(0x222));const _0x4cfc3f=_0x4470e1['length']>0x0?'\x20('+_0x4470e1['join'](',\x20')+')':'';return _0x5d8e18+'\x20'+_0x289a79+'/'+_0x5cca5f+_0x12b145(0x221)+_0x4cfc3f;}function displayFeature(_0xb735e9,_0x517e9b){const _0xd05916=_0x2f2801,_0x5615ea={'uPzFk':function(_0xc53f3b,_0x8c75ab){return _0xc53f3b>_0x8c75ab;},'idDxA':'\x0a\x20\x20\x20Scenarios:','OVXya':function(_0xfea8f6,_0x378e1e){return _0xfea8f6<_0x378e1e;},'WLzUv':_0xd05916(0x28c),'vhGjW':function(_0x82ae16,_0x55dbea){return _0x82ae16===_0x55dbea;},'GYtTF':'approved','gznoR':function(_0x1f33aa,_0xc6301e){return _0x1f33aa===_0xc6301e;},'ewlNm':'verification_in_progress','XwEcb':function(_0x37d4f4,_0x38dfdf){return _0x37d4f4>_0x38dfdf;},'WGTcD':function(_0x495ee4,_0x65d411){return _0x495ee4!==_0x65d411;},'agNFm':'\x20[has\x20reviewer\x20feedback]','JvDVd':_0xd05916(0x1f6),'VHeUR':function(_0x8f3328,_0x3b238e){return _0x8f3328===_0x3b238e;}};console['log']('\x0a'+_0xb735e9[_0xd05916(0x28a)]+'\x20('+_0xb735e9[_0xd05916(0x28e)]+')');_0xb735e9[_0xd05916(0x285)]&&console['log'](_0xd05916(0x262)+getProgressDisplay(_0xb735e9[_0xd05916(0x285)]));_0xb735e9[_0xd05916(0x24d)]&&console[_0xd05916(0x27a)](_0xd05916(0x28d)+_0xb735e9['description']);_0xb735e9[_0xd05916(0x20d)]&&console[_0xd05916(0x27a)](_0xd05916(0x229)+_0xb735e9[_0xd05916(0x20d)]);_0xb735e9[_0xd05916(0x1fc)]&&console[_0xd05916(0x27a)]('\x20\x20\x20Branch:\x20'+_0xb735e9['gitBranch']);console[_0xd05916(0x27a)]('\x20\x20\x20Created:\x20'+new Date(_0xb735e9['createdAt'])['toLocaleString']());_0xb735e9[_0xd05916(0x1f7)]&&console[_0xd05916(0x27a)]('\x20\x20\x20Completed:\x20'+new Date(_0xb735e9['completedAt'])['toLocaleString']());if(_0x517e9b&&_0x5615ea['uPzFk'](_0x517e9b['length'],0x0)){console[_0xd05916(0x27a)](_0x5615ea[_0xd05916(0x24b)]);for(let _0x2926f5=0x0;_0x5615ea['OVXya'](_0x2926f5,_0x517e9b[_0xd05916(0x243)]);_0x2926f5++){const _0x1fd883=_0x517e9b[_0x2926f5];let _0x5133e2;if(_0x1fd883[_0xd05916(0x21f)]===_0x5615ea[_0xd05916(0x252)]&&_0x5615ea[_0xd05916(0x1f8)](_0x1fd883[_0xd05916(0x282)],_0x5615ea[_0xd05916(0x21d)]))_0x5133e2='✅';else{if(_0x1fd883[_0xd05916(0x21f)]===_0xd05916(0x22c))_0x5133e2='🟢';else{if(_0x5615ea['vhGjW'](_0x1fd883['status'],'incomplete'))_0x5133e2='🟠';else{if(_0x5615ea[_0xd05916(0x1f8)](_0x1fd883[_0xd05916(0x21f)],_0xd05916(0x1f6)))_0x5133e2='🛑';else{if(_0x5615ea['gznoR'](_0x1fd883[_0xd05916(0x21f)],_0x5615ea['WLzUv']))_0x5133e2='⛔';else _0x1fd883[_0xd05916(0x21f)]===_0x5615ea[_0xd05916(0x20c)]?_0x5133e2='⏳':_0x5133e2='⬜';}}}}const _0x48d6b0=_0x5615ea['XwEcb'](_0x1fd883['version'],0x1)?_0xd05916(0x271)+_0x1fd883['version']+')':'',_0x7666fb=_0x1fd883[_0xd05916(0x248)]>0x1&&_0x1fd883[_0xd05916(0x21e)]&&_0x1fd883[_0xd05916(0x21f)]!=='closed'&&_0x5615ea['WGTcD'](_0x1fd883['status'],_0xd05916(0x22c))?_0x5615ea[_0xd05916(0x287)]:'';console['log']('\x20\x20\x20'+(_0x2926f5+0x1)+'.\x20'+_0x5133e2+'\x20'+_0x1fd883['description']+_0x48d6b0+_0x7666fb);_0x5615ea[_0xd05916(0x1f8)](_0x1fd883['status'],_0x5615ea[_0xd05916(0x259)])&&_0x1fd883['blockedReason']&&console[_0xd05916(0x27a)]('\x20\x20\x20\x20\x20\x20Blocked:\x20'+_0x1fd883['blockedReason']);_0x1fd883['status']==='incomplete'&&_0x1fd883[_0xd05916(0x277)]&&console['log']('\x20\x20\x20\x20\x20\x20Incomplete:\x20'+_0x1fd883['incompleteReason']);if(_0x5615ea['VHeUR'](_0x1fd883['status'],_0x5615ea['WLzUv'])&&_0x5615ea['gznoR'](_0x1fd883[_0xd05916(0x282)],'approved'))console[_0xd05916(0x27a)](_0xd05916(0x20a));else _0x5615ea[_0xd05916(0x280)](_0x1fd883['status'],_0xd05916(0x28c))&&_0x1fd883['canceledReason']&&console['log']('\x20\x20\x20\x20\x20\x20Cancelled:\x20'+_0x1fd883['canceledReason']);}}}export async function featureCreate(_0x4eacfc,_0x37be56){const _0x5dc3d6=_0x2f2801,_0x117df5={'KQFYJ':function(_0x53eebd){return _0x53eebd();},'rlEPJ':function(_0x43d38d,_0x3d84f8){return _0x43d38d===_0x3d84f8;},'vQkvD':function(_0x788716,_0x3e7c79){return _0x788716(_0x3e7c79);},'lyeVf':function(_0x1a1a6c,_0x1a3347){return _0x1a1a6c(_0x1a3347);},'zZXoF':function(_0xcef2bb,_0x26ef17){return _0xcef2bb(_0x26ef17);},'oonyU':function(_0x2b2cc1,_0x3a74d9,_0x266168){return _0x2b2cc1(_0x3a74d9,_0x266168);}},{envNames:_0x3337cd}=await _0x117df5[_0x5dc3d6(0x2a4)](getEnvNames);_0x117df5['rlEPJ'](_0x3337cd[_0x5dc3d6(0x243)],0x0)&&console[_0x5dc3d6(0x28b)]('\x0a'+_0x117df5[_0x5dc3d6(0x26d)](formatProfileRequiredMessage,_0xe9a622=>bold(_0xe9a622)));const _0x2ac725=_0x37be56['scenarios']?_0x37be56['scenarios'][_0x5dc3d6(0x202)](_0x150a4a=>_0x150a4a[_0x5dc3d6(0x1ee)]()):[],_0x3920a3=_0x117df5['KQFYJ'](getGitRepoUrl),_0x35b376=_0x117df5['KQFYJ'](getGitBranch);console['log'](_0x5dc3d6(0x201));const _0x514a83=await _0x117df5[_0x5dc3d6(0x26d)](createFeature,{'name':_0x4eacfc,'description':_0x37be56[_0x5dc3d6(0x24d)],'checklist':_0x2ac725,'gitRepoUrl':_0x3920a3,'gitBranch':_0x35b376});await _0x117df5[_0x5dc3d6(0x246)](setActiveFeatureId,_0x514a83['id']),await _0x117df5[_0x5dc3d6(0x24a)](tryStartCurrentSession,_0x514a83['id']);const _0x3888b8=process[_0x5dc3d6(0x238)]['CLAUDE_SESSION_ID'];_0x3888b8&&registerSession(_0x3888b8,_0x514a83['id']),console[_0x5dc3d6(0x27a)]('\x0a✅\x20Feature\x20review\x20created:\x20'+_0x514a83['id']),_0x117df5[_0x5dc3d6(0x22d)](displayFeature,_0x514a83,_0x514a83[_0x5dc3d6(0x225)]),console['log'](_0x5dc3d6(0x240));}export async function featureList(_0x4866e1){const _0x56e5b2=_0x2f2801,_0x2d2a7d={'hMoBf':function(_0x5b01ad){return _0x5b01ad();},'mlVHW':function(_0x1fa5e7,_0x36eb4d){return _0x1fa5e7(_0x36eb4d);},'nYNPC':function(_0x379c15,_0x4ddc9e){return _0x379c15===_0x4ddc9e;},'ZWLGy':'1|5|4|3|2|0','sXDzA':function(_0x34bebe,_0x247582){return _0x34bebe<_0x247582;},'AbuQI':function(_0x4b5460,_0x4b538d){return _0x4b5460!==_0x4b538d;},'SzVHN':'\x20--current-branch','IwLMh':'\x20--include-deleted'},_0x422472={'limit':_0x4866e1[_0x56e5b2(0x26a)]??0xa,'offset':_0x4866e1[_0x56e5b2(0x200)]??0x0,'includeDeleted':_0x4866e1['includeDeleted']};_0x4866e1['currentBranch']&&(_0x422472[_0x56e5b2(0x20d)]=getGitRepoUrl(),_0x422472['gitBranch']=_0x2d2a7d[_0x56e5b2(0x268)](getGitBranch));const _0x18e246=await _0x2d2a7d['mlVHW'](listFeatures,_0x422472);if(_0x2d2a7d[_0x56e5b2(0x216)](_0x18e246['items'][_0x56e5b2(0x243)],0x0)){console[_0x56e5b2(0x27a)]('\x0aNo\x20feature\x20reviews\x20found.');return;}const _0x507e3d=_0x18e246['totalCount']>_0x18e246[_0x56e5b2(0x253)][_0x56e5b2(0x243)]?'Showing\x20'+_0x18e246[_0x56e5b2(0x253)]['length']+'\x20of\x20'+_0x18e246[_0x56e5b2(0x203)]:_0x18e246['totalCount']+'\x20feature\x20review(s)\x20found';console['log']('\x0a'+_0x507e3d+':\x0a');for(const _0x3c18ad of _0x18e246['items']){const _0x213f50=_0x2d2a7d['ZWLGy'][_0x56e5b2(0x227)]('|');let _0x3c6188=0x0;while(!![]){switch(_0x213f50[_0x3c6188++]){case'0':console['log']('');continue;case'1':console['log'](''+_0x3c18ad[_0x56e5b2(0x28a)]);continue;case'2':console['log'](_0x56e5b2(0x234)+new Date(_0x3c18ad[_0x56e5b2(0x204)])[_0x56e5b2(0x230)]());continue;case'3':_0x3c18ad[_0x56e5b2(0x1fc)]&&console['log'](_0x56e5b2(0x292)+_0x3c18ad[_0x56e5b2(0x1fc)]);continue;case'4':_0x3c18ad[_0x56e5b2(0x285)]&&console['log']('\x20\x20\x20Progress:\x20'+_0x2d2a7d['mlVHW'](getProgressDisplay,_0x3c18ad['stats']));continue;case'5':console['log'](_0x56e5b2(0x2a3)+_0x3c18ad['id']);continue;}break;}}const _0x113fbf=_0x422472[_0x56e5b2(0x26a)]??0xa,_0x47d55c=(_0x422472[_0x56e5b2(0x200)]??0x0)+_0x18e246[_0x56e5b2(0x253)]['length'];if(_0x2d2a7d['sXDzA'](_0x47d55c,_0x18e246['totalCount'])){const _0x500fae=_0x2d2a7d['AbuQI'](_0x113fbf,0xa)?'\x20--limit\x20'+_0x113fbf:'',_0x44926c=_0x4866e1[_0x56e5b2(0x25f)]?_0x2d2a7d['SzVHN']:'',_0x491073=_0x4866e1[_0x56e5b2(0x1fb)]?_0x2d2a7d['IwLMh']:'';console[_0x56e5b2(0x27a)]('Next\x20page:\x20ranger\x20list\x20--offset\x20'+_0x47d55c+_0x500fae+_0x44926c+_0x491073);}}export async function featureShow(_0x4482c5){const _0x14ab05=_0x2f2801,_0x42484e={'ZqmKP':function(_0x2060cf,_0x389ca7){return _0x2060cf(_0x389ca7);},'ZAwNZ':function(_0x1921dd,_0x234aed,_0x56efda){return _0x1921dd(_0x234aed,_0x56efda);}},_0xac58ca=_0x4482c5||await getActiveFeatureId();if(!_0xac58ca)throw new Error(_0x14ab05(0x210));const _0x2f778f=await _0x42484e[_0x14ab05(0x261)](getFeature,_0xac58ca);_0x42484e[_0x14ab05(0x276)](displayFeature,_0x2f778f,_0x2f778f[_0x14ab05(0x225)]);}export async function featureResume(_0x4dbf9a){const _0x4f8043=_0x2f2801,_0x42bef3={'OrNup':function(_0x40dc2d,_0x237ee1){return _0x40dc2d(_0x237ee1);},'SZHEg':function(_0x49f6c2){return _0x49f6c2();},'Vzcci':function(_0x4eb625,_0xce3692,_0xdc142c){return _0x4eb625(_0xce3692,_0xdc142c);},'sTplq':function(_0x3c1ba2,_0x544158,_0x59e539){return _0x3c1ba2(_0x544158,_0x59e539);},'kSFdB':function(_0x42a829,_0x126e04){return _0x42a829(_0x126e04);},'WDsgg':_0x4f8043(0x1fa),'qEvaB':function(_0x18e7fd,_0x18f64b){return _0x18e7fd||_0x18f64b;},'drakK':function(_0x1501e8,_0x212364){return _0x1501e8===_0x212364;},'TffHb':_0x4f8043(0x255),'cIIdg':'list','turEb':'Multiple\x20feature\x20reviews\x20found.\x20Select\x20one:','qVDCH':function(_0x258dd9,_0x5c89e8){return _0x258dd9(_0x5c89e8);},'lPFLw':function(_0x1d007c,_0xb09b67){return _0x1d007c!==_0xb09b67;}};if(_0x4dbf9a){const _0x50dcb7=await getFeature(_0x4dbf9a);await _0x42bef3['OrNup'](setActiveFeatureId,_0x50dcb7['id']);const _0x173525=_0x42bef3['SZHEg'](getGitBranch);_0x173525&&_0x173525!==_0x50dcb7['gitBranch']&&(await _0x42bef3['Vzcci'](updateFeature,_0x50dcb7['id'],{'gitBranch':_0x173525}),console[_0x4f8043(0x27a)]('\x20\x20\x20Updated\x20branch\x20to:\x20'+_0x173525));console[_0x4f8043(0x27a)](_0x4f8043(0x296)+_0x50dcb7['name']+'\x20('+_0x50dcb7['id']+')'),await tryStartCurrentSession(_0x50dcb7['id']);const _0x3a9b32=process[_0x4f8043(0x238)]['CLAUDE_SESSION_ID'];_0x3a9b32&&registerSession(_0x3a9b32,_0x50dcb7['id']);_0x42bef3[_0x4f8043(0x272)](displayFeature,_0x50dcb7,_0x50dcb7['checklistItems']),await _0x42bef3[_0x4f8043(0x270)](printFeedbackNudge,_0x50dcb7['id']);return;}const _0x4c2f6e=getGitRepoUrl(),_0x2d4fd5=getGitBranch();if(!_0x4c2f6e&&!_0x2d4fd5)throw new Error(_0x42bef3[_0x4f8043(0x23f)]);console[_0x4f8043(0x27a)](_0x4f8043(0x28f)+(_0x4c2f6e||'any')+'\x20/\x20'+_0x42bef3['qEvaB'](_0x2d4fd5,_0x4f8043(0x279))+'...');const _0x430f04=await listFeatures({'gitRepoUrl':_0x4c2f6e,'gitBranch':_0x2d4fd5}),_0x23811b=_0x430f04['items'][_0x4f8043(0x223)](_0x79b58c=>!_0x79b58c['completedAt']);if(_0x42bef3[_0x4f8043(0x1ef)](_0x23811b['length'],0x0)){console['log'](_0x4f8043(0x1ea)),console[_0x4f8043(0x27a)](_0x4f8043(0x250));return;}let _0x436ac6;if(_0x23811b[_0x4f8043(0x243)]===0x1)_0x436ac6=_0x23811b[0x0];else{const _0x5b72ab=process[_0x4f8043(0x25a)][_0x4f8043(0x20b)]&&process['stdout'][_0x4f8043(0x20b)];if(!_0x5b72ab){console[_0x4f8043(0x27a)](_0x42bef3['TffHb']);for(const _0x3a4e01 of _0x23811b){console['log']('\x20\x20'+_0x3a4e01['name']),console[_0x4f8043(0x27a)]('\x20\x20\x20\x20\x20ID:\x20'+_0x3a4e01['id']),_0x3a4e01[_0x4f8043(0x285)]&&console['log'](_0x4f8043(0x245)+_0x42bef3['OrNup'](getProgressDisplay,_0x3a4e01['stats'])),_0x3a4e01['gitBranch']&&console[_0x4f8043(0x27a)]('\x20\x20\x20\x20\x20Branch:\x20'+_0x3a4e01[_0x4f8043(0x1fc)]);}throw new Error(_0x4f8043(0x242));}const {selected:_0x155f9a}=await _0x59d90c['prompt']([{'type':_0x42bef3[_0x4f8043(0x27b)],'name':_0x4f8043(0x264),'message':_0x42bef3[_0x4f8043(0x26c)],'choices':_0x23811b['map'](_0x278d0a=>({'name':_0x278d0a['name']+'\x20('+_0x278d0a['id']+')','value':_0x278d0a['id']}))}]);_0x436ac6=_0x23811b['find'](_0x488604=>_0x488604['id']===_0x155f9a);}await _0x42bef3['qVDCH'](setActiveFeatureId,_0x436ac6['id']);_0x2d4fd5&&_0x42bef3[_0x4f8043(0x256)](_0x2d4fd5,_0x436ac6[_0x4f8043(0x1fc)])&&(await _0x42bef3['Vzcci'](updateFeature,_0x436ac6['id'],{'gitBranch':_0x2d4fd5}),console['log'](_0x4f8043(0x1f2)+_0x2d4fd5));console[_0x4f8043(0x27a)]('\x0a✅\x20Resumed\x20feature\x20review:\x20'+_0x436ac6[_0x4f8043(0x28a)]+'\x20('+_0x436ac6['id']+')'),await tryStartCurrentSession(_0x436ac6['id']);const _0x1b8b3a=await _0x42bef3['qVDCH'](getFeature,_0x436ac6['id']),_0x541373=process[_0x4f8043(0x238)][_0x4f8043(0x207)];_0x541373&&registerSession(_0x541373,_0x1b8b3a['id']),displayFeature(_0x1b8b3a,_0x1b8b3a['checklistItems']),await _0x42bef3[_0x4f8043(0x270)](printFeedbackNudge,_0x1b8b3a['id']);}async function printFeedbackNudge(_0x5a8465){const _0x174365=_0x2f2801,_0x339be7={'pIyNr':function(_0x5001bb,_0x380c49){return _0x5001bb>_0x380c49;}};try{const {items:_0x824b5a}=await getActionItems(_0x5a8465);let _0x3151c6=0x0,_0x17a145=0x0;for(const _0xc3164a of _0x824b5a){const _0xe0b086=await getItemFeedback(_0x5a8465,_0xc3164a['id']);_0xe0b086[_0x174365(0x239)]['length']>0x0&&(_0x3151c6+=_0xe0b086['unaddressedComments']['length'],_0x17a145++);}_0x339be7['pIyNr'](_0x3151c6,0x0)&&(console[_0x174365(0x27a)](_0x174365(0x1f3)+_0x3151c6+_0x174365(0x1eb)+_0x17a145+_0x174365(0x1f5)),console['log'](_0x174365(0x2a0)));}catch{}}async function tryStartCurrentSession(_0x118f5d){const _0x20afa9=_0x2f2801,_0xc65464={'cnCYX':function(_0x423e39,_0x538119,_0x4cfe91){return _0x423e39(_0x538119,_0x4cfe91);}};try{const {sessions:_0x547ac5}=await listFeatureSessions(_0x118f5d),_0x23ec0b=_0x547ac5['find'](_0x3eccf6=>_0x3eccf6[_0x20afa9(0x21f)]==='ready');_0x23ec0b&&(await _0xc65464['cnCYX'](startSession,_0x118f5d,_0x23ec0b['id']),console['log']('\x20\x20\x20▶️\x20Started\x20session\x20'+_0x23ec0b['iteration']));}catch{}}export async function featureReport(_0x26b7f7,_0x413025){const _0x3bc972=_0x2f2801,_0x531224={'jNVCR':function(_0x308d94){return _0x308d94();},'JqZld':'No\x20feature\x20review\x20ID\x20provided\x20and\x20no\x20active\x20feature\x20review\x20set.','CyPlE':'\x0aGenerating\x20report...','ikCGB':function(_0x31eeda){return _0x31eeda();},'TytjO':'reports','IFOyK':function(_0x1a944d,_0x320994,_0x5718b8){return _0x1a944d(_0x320994,_0x5718b8);},'Rqdse':function(_0x2a7071,_0x48299f,_0x4221fc){return _0x2a7071(_0x48299f,_0x4221fc);},'UjuXo':function(_0x28c3f2,_0x566ff2,_0xccdfde,_0x5a0d9f){return _0x28c3f2(_0x566ff2,_0xccdfde,_0x5a0d9f);}},_0x3a3a06=_0x26b7f7||await _0x531224[_0x3bc972(0x291)](getActiveFeatureId);if(!_0x3a3a06)throw new Error(_0x531224['JqZld']);console['log'](_0x531224['CyPlE']);const _0x346635=await getFeatureReportMarkdown(_0x3a3a06,{'style':_0x413025[_0x3bc972(0x206)]}),_0x46644a=_0x413025['output']||join(_0x531224['ikCGB'](getRangerDir),_0x531224[_0x3bc972(0x226)],_0x3a3a06+'.md'),_0x129496=_0x531224[_0x3bc972(0x235)](join,_0x46644a,'..');!existsSync(_0x129496)&&await _0x531224['Rqdse'](mkdir,_0x129496,{'recursive':!![]}),await _0x531224[_0x3bc972(0x236)](writeFile,_0x46644a,_0x346635,'utf-8'),console['log']('\x0a✅\x20Report\x20generated:\x20'+_0x46644a);}export async function featureConcludeSession(_0x59b9f6){const _0x8375a3=_0x2f2801,_0x43072b={'jLDWY':'No\x20feature\x20review\x20ID\x20provided\x20and\x20no\x20active\x20feature\x20review\x20set.\x20Run:\x20ranger\x20resume\x20<id>\x20to\x20set\x20an\x20active\x20feature\x20review','YKJyB':function(_0x326a2d,_0x498555){return _0x326a2d(_0x498555);},'KYemf':'No\x20active\x20session\x20for\x20this\x20feature\x20review.','IGsZv':_0x8375a3(0x21b),'kspdU':_0x8375a3(0x215),'wXejI':_0x8375a3(0x224),'eZbmI':_0x8375a3(0x209)},_0x20551b=_0x59b9f6||await getActiveFeatureId();if(!_0x20551b)throw new Error(_0x43072b['jLDWY']);const _0x2e7c09=await _0x43072b['YKJyB'](getFeature,_0x20551b);if(!_0x2e7c09['currentSessionId'])throw new Error(_0x43072b[_0x8375a3(0x29a)]);const {sessions:_0x72f083}=await listFeatureSessions(_0x20551b),_0x142f94=_0x72f083[_0x8375a3(0x249)](_0x329ae5=>_0x329ae5['id']===_0x2e7c09[_0x8375a3(0x29f)]);if(!_0x142f94)throw new Error(_0x43072b['IGsZv']);if(_0x142f94['status']===_0x43072b[_0x8375a3(0x1f1)]){console['log'](_0x8375a3(0x260));return;}if(_0x142f94[_0x8375a3(0x21f)]!==_0x43072b['wXejI'])throw new Error('Cannot\x20conclude\x20session\x20with\x20status:\x20'+_0x142f94[_0x8375a3(0x21f)]);console['log'](_0x43072b[_0x8375a3(0x21a)]),await concludeSession(_0x20551b,_0x2e7c09[_0x8375a3(0x29f)]),console[_0x8375a3(0x27a)]('\x0a✅\x20Session\x20'+_0x142f94[_0x8375a3(0x26e)]+'\x20concluded.'),console['log'](_0x8375a3(0x241));}export async function featureSessions(_0x43f486){const _0x4a7f2c=_0x2f2801,_0x36e4ed={'mNbsp':function(_0x467980){return _0x467980();},'FtrQQ':_0x4a7f2c(0x29b),'JvfjK':function(_0xa2babe,_0x12f940){return _0xa2babe(_0x12f940);},'ueedx':function(_0x2b45ae,_0x592aa9){return _0x2b45ae===_0x592aa9;},'KDQnN':_0x4a7f2c(0x215),'DFpDT':function(_0x20370e,_0x10fe91){return _0x20370e===_0x10fe91;}},_0x3b0bbd=_0x43f486||await _0x36e4ed[_0x4a7f2c(0x231)](getActiveFeatureId);if(!_0x3b0bbd)throw new Error(_0x36e4ed['FtrQQ']);const _0x100682=await _0x36e4ed['JvfjK'](getFeature,_0x3b0bbd);console[_0x4a7f2c(0x27a)]('\x0a📁\x20Sessions\x20for\x20feature\x20review:\x20'+_0x100682[_0x4a7f2c(0x28a)]+'\x0a');const {sessions:_0x4b3c57}=await listFeatureSessions(_0x3b0bbd);if(_0x36e4ed[_0x4a7f2c(0x212)](_0x4b3c57[_0x4a7f2c(0x243)],0x0)){console['log']('No\x20sessions\x20found.');return;}for(const _0x2ca96b of _0x4b3c57){const _0x13d27c=_0x2ca96b['status']===_0x36e4ed[_0x4a7f2c(0x1fe)]?'✅':_0x36e4ed[_0x4a7f2c(0x211)](_0x2ca96b['status'],_0x4a7f2c(0x224))?'🔄':'▶️';console[_0x4a7f2c(0x27a)](_0x13d27c+_0x4a7f2c(0x26f)+_0x2ca96b['iteration']+'\x20('+_0x2ca96b['id']+')'),console['log']('\x20\x20\x20Status:\x20'+_0x2ca96b[_0x4a7f2c(0x21f)]),console['log'](_0x4a7f2c(0x234)+new Date(_0x2ca96b[_0x4a7f2c(0x204)])[_0x4a7f2c(0x27f)]()),_0x2ca96b['completedAt']&&console[_0x4a7f2c(0x27a)](_0x4a7f2c(0x218)+new Date(_0x2ca96b[_0x4a7f2c(0x1f7)])['toLocaleString']()),console['log']('');}}export async function featureAddScenario(_0x3cf380,_0x39c4ea){const _0x4a0ea5=_0x2f2801,_0x5b4951={'qeKjx':function(_0xab295a,_0x3f4f77,_0x29e8ea){return _0xab295a(_0x3f4f77,_0x29e8ea);}},_0x25f58b=_0x39c4ea||await getActiveFeatureId();if(!_0x25f58b)throw new Error('No\x20feature\x20review\x20ID\x20provided\x20and\x20no\x20active\x20feature\x20review\x20set.\x20Run:\x20ranger\x20resume\x20<id>\x20to\x20set\x20an\x20active\x20feature\x20review');const _0x365f93=await getFeature(_0x25f58b);if(_0x365f93[_0x4a0ea5(0x23c)]&&!_0x365f93[_0x4a0ea5(0x23c)]['submittedAt'])throw new Error('Cannot\x20add\x20scenario:\x20feature\x20review\x20has\x20an\x20active\x20review.\x20Please\x20finish\x20your\x20review\x20first:\x20'+_0x365f93['dashboardUrl']);await _0x5b4951['qeKjx'](createChecklistItem,_0x25f58b,_0x3cf380);const _0x33ca83=await getFeature(_0x25f58b);console[_0x4a0ea5(0x27a)]('\x0a✅\x20Scenario\x20added'),displayFeature(_0x33ca83,_0x33ca83['checklistItems']);}export async function featureEditScenario(_0x277c9a,_0x1766f7){const _0x37ff8d=_0x2f2801,_0x33e183={'CeJuR':'\x0a❌\x20No\x20feature\x20review\x20ID\x20provided\x20and\x20no\x20active\x20feature\x20review\x20set.','bIjgb':function(_0x198504,_0x40ccf4){return _0x198504===_0x40ccf4;},'SMsnr':function(_0x443a7c,_0x2638b0){return _0x443a7c-_0x2638b0;},'dEfkY':function(_0xe12847,_0x34bba7){return _0xe12847>=_0x34bba7;},'mBEWT':_0x37ff8d(0x20f),'VmknY':function(_0x5837b1,_0x4612b3){return _0x5837b1<_0x4612b3;},'yZjdK':function(_0x5aa823,_0x7eb494){return _0x5aa823+_0x7eb494;},'PDFmM':function(_0x5594a3,_0x26e839,_0xdb9cad,_0x235210){return _0x5594a3(_0x26e839,_0xdb9cad,_0x235210);},'rBPlI':function(_0x2cd433,_0x3e5617){return _0x2cd433(_0x3e5617);},'OUCzD':function(_0x462c2f,_0x5f05af,_0x14ca25){return _0x462c2f(_0x5f05af,_0x14ca25);}},_0x55ca98=_0x1766f7['id']||await getActiveFeatureId();!_0x55ca98&&(console['error'](_0x33e183[_0x37ff8d(0x263)]),console['error']('Run:\x20ranger\x20resume\x20<id>\x20to\x20set\x20an\x20active\x20feature\x20review'),process['exit'](0x1));const _0x4984ca=await getFeature(_0x55ca98);_0x4984ca['latestReview']&&!_0x4984ca[_0x37ff8d(0x23c)]['submittedAt']&&(console[_0x37ff8d(0x281)](_0x37ff8d(0x295)),console[_0x37ff8d(0x281)](_0x37ff8d(0x254)),console[_0x37ff8d(0x281)]('\x20\x20'+_0x4984ca[_0x37ff8d(0x28e)]),process['exit'](0x1));const _0x3551ed=_0x4984ca['checklistItems']||[];_0x33e183['bIjgb'](_0x3551ed[_0x37ff8d(0x243)],0x0)&&(console[_0x37ff8d(0x281)]('\x0a❌\x20No\x20scenarios\x20found\x20to\x20edit.'),process['exit'](0x1));if(_0x33e183[_0x37ff8d(0x1ff)](_0x1766f7[_0x37ff8d(0x214)],undefined)){console['error'](_0x37ff8d(0x275)),console[_0x37ff8d(0x27a)]('\x0aAvailable\x20scenarios:');for(let _0x20710c=0x0;_0x20710c<_0x3551ed['length'];_0x20710c++){console[_0x37ff8d(0x27a)]('\x20\x20'+(_0x20710c+0x1)+'.\x20'+_0x3551ed[_0x20710c]['description']);}process['exit'](0x1);}const _0xd6c453=_0x33e183[_0x37ff8d(0x205)](_0x1766f7[_0x37ff8d(0x214)],0x1);if(_0xd6c453<0x0||_0x33e183['dEfkY'](_0xd6c453,_0x3551ed[_0x37ff8d(0x243)])){console[_0x37ff8d(0x281)](_0x37ff8d(0x23d)+_0x1766f7['scenario']+_0x37ff8d(0x208)+_0x3551ed['length']+'\x20scenarios.'),console['log'](_0x33e183['mBEWT']);for(let _0x9d438c=0x0;_0x33e183[_0x37ff8d(0x23b)](_0x9d438c,_0x3551ed['length']);_0x9d438c++){console['log']('\x20\x20'+_0x33e183['yZjdK'](_0x9d438c,0x1)+'.\x20'+_0x3551ed[_0x9d438c]['description']);}process[_0x37ff8d(0x274)](0x1);}const _0x571b22=_0x3551ed[_0xd6c453];await _0x33e183[_0x37ff8d(0x269)](updateChecklistItem,_0x55ca98,_0x571b22['id'],{'description':_0x277c9a});const _0x3b886b=await _0x33e183[_0x37ff8d(0x266)](getFeature,_0x55ca98);console['log']('\x0a✅\x20Scenario\x20updated'),_0x33e183['OUCzD'](displayFeature,_0x3b886b,_0x3b886b[_0x37ff8d(0x225)]);}async function downloadFeedbackImage(_0x1fbfdb,_0x2c5b40){const _0x499690=_0x2f2801,_0x4b5f07={'yFswC':function(_0x26b447,_0x3a53cb,_0x35700c){return _0x26b447(_0x3a53cb,_0x35700c);},'rCPCM':function(_0x5bf701){return _0x5bf701();},'PQqWI':'feedback-images','JQZII':function(_0x2cc0c8,_0xa81450){return _0x2cc0c8(_0xa81450);}};try{const _0x27ecb8=_0x4b5f07['yFswC'](join,_0x4b5f07['rCPCM'](getRangerDir),_0x4b5f07[_0x499690(0x294)]);await _0x4b5f07['yFswC'](mkdir,_0x27ecb8,{'recursive':!![]});const _0x175938=_0x4b5f07['yFswC'](join,_0x27ecb8,_0x499690(0x289)+_0x2c5b40+'.png'),_0x5eae54=await _0x4b5f07['JQZII'](fetch,_0x1fbfdb);if(!_0x5eae54['ok'])return null;const _0x26a3ce=Buffer[_0x499690(0x258)](await _0x5eae54['arrayBuffer']());return await writeFile(_0x175938,_0x26a3ce),_0x175938;}catch{return null;}}export async function featureGetReview(_0x12142b){const _0x1508b1=_0x2f2801,_0x741c8a={'eeQiC':function(_0x14d6a3){return _0x14d6a3();},'Avfdt':_0x1508b1(0x210),'SLxGm':function(_0x2561ae,_0x1179f3){return _0x2561ae(_0x1179f3);},'XPkfU':function(_0x10ba98,_0x568c11){return _0x10ba98(_0x568c11);},'qmdDq':function(_0x2ca6cd,_0x33a0ac){return _0x2ca6cd<_0x33a0ac;},'KLorw':function(_0x2f3e45,_0x461861){return _0x2f3e45>_0x461861;},'jfUZr':function(_0x5ae2a9,_0x5def43){return _0x5ae2a9+_0x5def43;},'TzUAB':function(_0x20b920,_0x286a2c,_0x23d317){return _0x20b920(_0x286a2c,_0x23d317);},'dFKZh':'point','myuEK':function(_0xb0354d,_0x4bacfe){return _0xb0354d>=_0x4bacfe;}},_0x22f9a1=_0x12142b||await _0x741c8a[_0x1508b1(0x26b)](getActiveFeatureId);if(!_0x22f9a1)throw new Error(_0x741c8a['Avfdt']);const _0x984506=await _0x741c8a['SLxGm'](getFeature,_0x22f9a1),{items:_0x5c447c}=await _0x741c8a['XPkfU'](getActionItems,_0x22f9a1);console[_0x1508b1(0x27a)](_0x1508b1(0x22a)+_0x984506['name']+'\x20('+_0x984506['id']+')\x0a');if(_0x5c447c['length']===0x0){console[_0x1508b1(0x27a)](_0x1508b1(0x297));return;}for(let _0x30f471=0x0;_0x741c8a['qmdDq'](_0x30f471,_0x5c447c['length']);_0x30f471++){const _0x16f6a7=_0x5c447c[_0x30f471],_0x10774f=_0x741c8a['KLorw'](_0x16f6a7[_0x1508b1(0x248)],0x1)?_0x1508b1(0x271)+_0x16f6a7[_0x1508b1(0x248)]+')':'',_0x547629=await getItemFeedback(_0x22f9a1,_0x16f6a7['id']);if(_0x547629[_0x1508b1(0x239)][_0x1508b1(0x243)]>0x0){console[_0x1508b1(0x27a)](_0x1508b1(0x27c)+_0x741c8a[_0x1508b1(0x284)](_0x30f471,0x1)+':\x20\x22'+_0x16f6a7['description']+'\x22'+_0x10774f+_0x1508b1(0x232)+_0x547629['unaddressedComments'][_0x1508b1(0x243)]+'\x20unaddressed\x20comment(s)');for(const _0x369c0a of _0x547629['unaddressedComments']){const _0x35a6c4=new Date(_0x369c0a[_0x1508b1(0x204)])['toLocaleDateString'](_0x1508b1(0x22f),{'month':_0x1508b1(0x22e),'day':_0x1508b1(0x23e)}),_0x279be2=_0x369c0a['authorName']||_0x369c0a['authorEmail']||_0x1508b1(0x1f0);console['log']('\x20\x20💬\x20'+_0x279be2+'\x20('+_0x35a6c4+'):\x20\x22'+_0x369c0a[_0x1508b1(0x283)]+'\x22');if(_0x369c0a[_0x1508b1(0x24f)]){const _0x3fc038=await _0x741c8a[_0x1508b1(0x251)](downloadFeedbackImage,_0x369c0a[_0x1508b1(0x24f)],_0x369c0a['id']);_0x3fc038&&console['log']('\x20\x20\x20\x20\x20📷\x20Screenshot:\x20'+_0x3fc038);}if(_0x369c0a['spatial']){const _0x4e5743=_0x369c0a[_0x1508b1(0x267)][_0x1508b1(0x237)];_0x369c0a[_0x1508b1(0x267)][_0x1508b1(0x27d)]===_0x741c8a[_0x1508b1(0x298)]&&_0x741c8a[_0x1508b1(0x1e9)](_0x4e5743['length'],0x2)?console[_0x1508b1(0x27a)](_0x1508b1(0x1ed)+_0x4e5743[0x0]['toFixed'](0x2)+',\x20'+_0x4e5743[0x1]['toFixed'](0x2)+')'):console['log'](_0x1508b1(0x20e)+_0x369c0a['spatial']['type']+_0x1508b1(0x29e)+_0x4e5743['map'](_0x18a18f=>_0x18a18f[_0x1508b1(0x1f4)](0x2))['join'](',\x20')+']');}}if(_0x547629['canonicalFlow']){console['log']('\x20\x20Expected\x20flow:');for(const _0x5539bc of _0x547629[_0x1508b1(0x290)][_0x1508b1(0x227)]('\x0a')){console[_0x1508b1(0x27a)](_0x1508b1(0x288)+_0x5539bc);}}}else console['log'](_0x1508b1(0x27c)+(_0x30f471+0x1)+_0x1508b1(0x228)+_0x16f6a7['description']+'\x22'+_0x10774f+'\x20—\x20no\x20comments'),console[_0x1508b1(0x27a)](_0x1508b1(0x23a));console[_0x1508b1(0x27a)]('');}}export async function featureDelete(_0x2929fc){const _0x27950d=_0x2f2801,_0x43d6f8={'uLhSu':function(_0x24ca7b){return _0x24ca7b();},'plezX':function(_0x3524a7,_0x1a05df){return _0x3524a7(_0x1a05df);}},_0x11c968=_0x2929fc||await _0x43d6f8['uLhSu'](getActiveFeatureId);if(!_0x11c968)throw new Error(_0x27950d(0x210));const _0x2f3f66=await getFeature(_0x11c968);await _0x43d6f8[_0x27950d(0x286)](softDeleteFeature,_0x11c968),console['log'](_0x27950d(0x247)+_0x2f3f66['name']+'\x20('+_0x11c968+')'),console['log'](_0x27950d(0x29d)+_0x11c968);}export async function featureRestore(_0x2f03e0){const _0x3bbbc6=_0x2f2801,_0x413ed4={'CfRgI':function(_0x153d36,_0x44a542){return _0x153d36(_0x44a542);}};await restoreFeature(_0x2f03e0);const _0x4291aa=await _0x413ed4['CfRgI'](getFeature,_0x2f03e0);console[_0x3bbbc6(0x27a)]('\x0a✅\x20Restored\x20feature\x20review:\x20'+_0x4291aa[_0x3bbbc6(0x28a)]+'\x20('+_0x2f03e0+')'),displayFeature(_0x4291aa,_0x4291aa[_0x3bbbc6(0x225)]);}