@ranger-testing/ranger-cli 2.0.5 → 2.0.7

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 (116) 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/fixWebmDuration.js +1 -0
  42. package/build/commands/utils/git.js +1 -44
  43. package/build/commands/utils/keychain.js +1 -1
  44. package/build/commands/utils/localAgentInstallationsApi.js +1 -1
  45. package/build/commands/utils/profileMessages.js +1 -8
  46. package/build/commands/utils/profileSetupBanner.js +1 -167
  47. package/build/commands/utils/rangerRoot.js +1 -60
  48. package/build/commands/utils/reportGenerator.js +1 -130
  49. package/build/commands/utils/retry.js +1 -25
  50. package/build/commands/utils/sessionCache.js +1 -299
  51. package/build/commands/utils/settings.js +1 -313
  52. package/build/commands/utils/skillContent.js +1 -28
  53. package/build/commands/utils/skills.js +1 -1
  54. package/build/commands/utils/telemetry.js +1 -254
  55. package/build/commands/utils/userApi.js +1 -32
  56. package/build/commands/utils/version.js +1 -62
  57. package/build/commands/verifyFeature.js +1 -1343
  58. package/build/commands/verifyInBrowser.js +1 -1
  59. package/package.json +4 -1
  60. package/build/cli.js.map +0 -1
  61. package/build/commands/addEnv.js.map +0 -1
  62. package/build/commands/authEncrypt.js.map +0 -1
  63. package/build/commands/clean.js.map +0 -1
  64. package/build/commands/config.js.map +0 -1
  65. package/build/commands/env.js.map +0 -1
  66. package/build/commands/feature.js.map +0 -1
  67. package/build/commands/hook.js.map +0 -1
  68. package/build/commands/hooks/autoPrompt.js.map +0 -1
  69. package/build/commands/hooks/disable.js.map +0 -1
  70. package/build/commands/hooks/enable.js.map +0 -1
  71. package/build/commands/hooks/exitPlanMode.js.map +0 -1
  72. package/build/commands/hooks/index.js.map +0 -1
  73. package/build/commands/hooks/output.js.map +0 -1
  74. package/build/commands/hooks/planReminder.js.map +0 -1
  75. package/build/commands/hooks/planStart.js.map +0 -1
  76. package/build/commands/hooks/postEdit.js.map +0 -1
  77. package/build/commands/hooks/preCompact.js.map +0 -1
  78. package/build/commands/hooks/sessionEnd.js.map +0 -1
  79. package/build/commands/hooks/sessionStart.js.map +0 -1
  80. package/build/commands/hooks/stopHook.js.map +0 -1
  81. package/build/commands/index.js.map +0 -1
  82. package/build/commands/login.js.map +0 -1
  83. package/build/commands/setupCi.js.map +0 -1
  84. package/build/commands/skillup.js.map +0 -1
  85. package/build/commands/start.js.map +0 -1
  86. package/build/commands/status.js.map +0 -1
  87. package/build/commands/update.js.map +0 -1
  88. package/build/commands/updateEnv.js.map +0 -1
  89. package/build/commands/useEnv.js.map +0 -1
  90. package/build/commands/utils/activeProfile.js.map +0 -1
  91. package/build/commands/utils/browserSessionsApi.js.map +0 -1
  92. package/build/commands/utils/claudeConfig.js.map +0 -1
  93. package/build/commands/utils/claudePlugin.js.map +0 -1
  94. package/build/commands/utils/crypto.js.map +0 -1
  95. package/build/commands/utils/desirePathLog.js.map +0 -1
  96. package/build/commands/utils/deviceAuth.js.map +0 -1
  97. package/build/commands/utils/environment.js.map +0 -1
  98. package/build/commands/utils/featureApi.js.map +0 -1
  99. package/build/commands/utils/featureReportGenerator.js.map +0 -1
  100. package/build/commands/utils/git.js.map +0 -1
  101. package/build/commands/utils/keychain.js.map +0 -1
  102. package/build/commands/utils/localAgentInstallationsApi.js.map +0 -1
  103. package/build/commands/utils/profileMessages.js.map +0 -1
  104. package/build/commands/utils/profileSetupBanner.js.map +0 -1
  105. package/build/commands/utils/rangerRoot.js.map +0 -1
  106. package/build/commands/utils/reportGenerator.js.map +0 -1
  107. package/build/commands/utils/retry.js.map +0 -1
  108. package/build/commands/utils/sessionCache.js.map +0 -1
  109. package/build/commands/utils/settings.js.map +0 -1
  110. package/build/commands/utils/skillContent.js.map +0 -1
  111. package/build/commands/utils/skills.js.map +0 -1
  112. package/build/commands/utils/telemetry.js.map +0 -1
  113. package/build/commands/utils/userApi.js.map +0 -1
  114. package/build/commands/utils/version.js.map +0 -1
  115. package/build/commands/verifyFeature.js.map +0 -1
  116. 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 _0x41fdff=_0x4908;(function(_0x9ac79,_0x2e7652){const _0x41aeee=_0x4908,_0x48b040=_0x9ac79();while(!![]){try{const _0x4993db=parseInt(_0x41aeee(0x19c))/0x1*(-parseInt(_0x41aeee(0x1b0))/0x2)+parseInt(_0x41aeee(0x168))/0x3*(-parseInt(_0x41aeee(0x1a9))/0x4)+parseInt(_0x41aeee(0x10a))/0x5+parseInt(_0x41aeee(0x15d))/0x6*(-parseInt(_0x41aeee(0x16c))/0x7)+-parseInt(_0x41aeee(0x1a1))/0x8+-parseInt(_0x41aeee(0x128))/0x9+parseInt(_0x41aeee(0x113))/0xa;if(_0x4993db===_0x2e7652)break;else _0x48b040['push'](_0x48b040['shift']());}catch(_0x12307c){_0x48b040['push'](_0x48b040['shift']());}}}(_0x46a4,0x6bc0e));import{readFile,writeFile,mkdir}from'fs/promises';import{join}from'path';import{existsSync}from'fs';import{execSync}from'child_process';import _0x5c0d7c from'inquirer';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';import{registerSession}from'./utils/sessionCache.js';import{getEnvNames}from'./env.js';import{formatProfileRequiredMessage}from'./utils/profileMessages.js';const ACTIVE_FEATURE_FILE=_0x41fdff(0x157),bold=_0x4d54c4=>_0x41fdff(0x11b)+_0x4d54c4+'\x1b[0m';function getGitRepoUrl(){const _0x5acf20=_0x41fdff,_0x182533={'uDONv':_0x5acf20(0x15a),'UAmIv':_0x5acf20(0x125),'jBkmO':_0x5acf20(0x1bb)};try{const _0x2d7360=execSync(_0x182533[_0x5acf20(0x13c)],{'encoding':_0x182533[_0x5acf20(0x15b)],'stdio':['pipe',_0x182533['jBkmO'],_0x182533[_0x5acf20(0x1a6)]]})['trim']();return _0x2d7360['replace'](/\.git$/,'')['replace'](/^git@github\.com:/,'github.com/')['replace'](/^https?:\/\//,'');}catch{return undefined;}}function _0x46a4(){const _0x3e5365=['UAmIv','fqZMv','8238cwmnaV','Please\x20specify\x20the\x20feature\x20review\x20ID:\x20ranger\x20resume\x20<id>','tPPxs','IguNy','oDBgU','\x0a✅\x20Resumed\x20feature\x20review:\x20','version','dashboardUrl','zAnVG','\x20blocked','any','36cvNvIU','\x0aGenerating\x20report...','approved','QBCId','1449UbfIqm','UYnRH','No\x20sessions\x20found.','\x20\x20\x20\x20\x20ID:\x20','stdout','dtGEO','YLmII','CLAUDE_SESSION_ID','KTbgB','lPMCk','join','latestReview','\x20—\x20','IEWov','cBney','NArcM','incompleteItems','\x20--current-branch','selected','HVQAU','mHivn','authorName','split','\x20\x20\x20\x20\x20\x20Approved','stdin','completedAt','find','incompleteReason','\x0a✅\x20Feature\x20review\x20created:\x20','gitBranch','):\x20\x22','mOgag','.md','completed','stats','gitRepoUrl','USqDb','length','pxONe','Kohxc','mvFFT','TKeDK','\x0aCreating\x20feature\x20review...','error','\x20scenario(s).','ZybQW','\x0a📁\x20Sessions\x20for\x20feature\x20review:\x20','\x20\x20\x20\x20\x20\x20Cancelled:\x20','1NxqovP','\x20\x20\x20\x20','\x20at\x20[','PVlOw','DfBwV','5306888miRyyD','\x20unaddressed\x20comment(s)','hMOJT','status','\x20\x20\x20Created:\x20','jBkmO','jxfTU','type','112076XcZZlh','blockedReason','xwmgZ','\x20incomplete','coordinates','content','XLTWi','1733254qGgsKu','\x20unaddressed\x20reviewer\x20comment(s)\x20across\x20','\x20\x20\x20Status:\x20','push','\x20\x20\x20\x20\x20📍\x20Annotation:\x20point\x20at\x20(','\x20\x20\x20Branch:\x20','TZDLx','scenario','spatial','verifiedItems','isTTY','pipe','ZVWcn','name','\x0a➡️\x20\x20Set\x20as\x20active\x20feature\x20review','currentBranch','\x20\x20\x20To\x20restore\x20it,\x20run:\x20ranger\x20restore\x20','GNNpD','blocked','WhrEp','hIbSh','offset','canceledReason','items','2534720OBrNux','\x20\x20\x20Description:\x20','RFZAk','NYTYb','repeat','No\x20scenarios\x20found.','\x20feature\x20review(s)\x20found','sLmvu','LyYTj','22885580gtxXFQ','eXBWi','feedback-images','UMsdC','exit','toFixed','\x0aNo\x20feature\x20reviews\x20found.','\x20(v','\x1b[1m','YUCpo','unaddressedComments','iBkEm','Scenario\x20','\x20--limit\x20','\x20\x20\x20\x20\x20\x20Incomplete:\x20','mnRqy','closed','limit','utf-8','GyqCa','\x0a✅\x20Scenario\x20updated','1833453yXbsxU','wpSMF','description','trim','TIyym','\x20scenarios.','\x20\x20\x20Progress:\x20','incomplete','terminalReason','\x20\x20\x20Repository:\x20','toLocaleString','fCZKw','XpdWW','Not\x20in\x20a\x20git\x20repository.','createdAt','log','prompt','Cannot\x20conclude\x20session\x20with\x20status:\x20','\x20\x20\x20The\x20feature\x20review\x20is\x20now\x20ready\x20for\x20human\x20review\x20on\x20the\x20Ranger\x20dashboard.','...','uDONv','iteration','JbHLJ','toLocaleDateString','blockedItems','warn','yiACl','ryLea','Current\x20session\x20not\x20found.','\x20\x20✅\x20No\x20feedback\x20to\x20address','map','checklistItems','gQJbg','totalCount',':\x20\x22','\x20\x20\x20\x20\x20Progress:\x20','assetUrl','No\x20feature\x20review\x20ID\x20provided\x20and\x20no\x20active\x20feature\x20review\x20set.\x20Run:\x20ranger\x20resume\x20<id>\x20to\x20set\x20an\x20active\x20feature\x20review','parentItemId','round','QSqII','\x20\x20\x20\x20\x20Branch:\x20','\x20\x20\x20','verificationInProgressItems','style','qnxHV','\x0a✅\x20Session\x20','active-feature.txt','VqoTc','\x20--include-deleted','git\x20remote\x20get-url\x20origin'];_0x46a4=function(){return _0x3e5365;};return _0x46a4();}function getGitBranch(){const _0x3fef09=_0x41fdff,_0xfa3b6b={'akUwz':'git\x20rev-parse\x20--abbrev-ref\x20HEAD','oDBgU':'pipe'};try{return execSync(_0xfa3b6b['akUwz'],{'encoding':_0x3fef09(0x125),'stdio':[_0xfa3b6b['oDBgU'],_0xfa3b6b[_0x3fef09(0x161)],_0x3fef09(0x1bb)]})['trim']();}catch{return undefined;}}export async function getActiveFeatureId(){const _0x311ce5=_0x41fdff,_0x47a5b2={'WhrEp':function(_0x1937d6){return _0x1937d6();},'SGqTh':function(_0x39194c,_0x47aa70){return _0x39194c(_0x47aa70);}},_0x34b5b0=join(_0x47a5b2[_0x311ce5(0x105)](getRangerDir),ACTIVE_FEATURE_FILE);if(!_0x47a5b2['SGqTh'](existsSync,_0x34b5b0))return null;try{const _0x2a8e02=await readFile(_0x34b5b0,_0x311ce5(0x125));return _0x2a8e02['trim']()||null;}catch{return null;}}function _0x4908(_0x11906b,_0xba534e){_0x11906b=_0x11906b-0x104;const _0x46a433=_0x46a4();let _0x490843=_0x46a433[_0x11906b];return _0x490843;}async function setActiveFeatureId(_0x1e3f99){const _0x16ff01=_0x41fdff,_0x212fd7={'MQTlN':function(_0x3030ff){return _0x3030ff();},'JbHLJ':function(_0x2b9ec6,_0x871307,_0x54bb75){return _0x2b9ec6(_0x871307,_0x54bb75);},'TKeDK':_0x16ff01(0x125)},_0x3717d7=_0x212fd7['MQTlN'](getRangerDir),_0x2f33c6=_0x212fd7[_0x16ff01(0x13e)](join,_0x3717d7,ACTIVE_FEATURE_FILE);!existsSync(_0x3717d7)&&await mkdir(_0x3717d7,{'recursive':!![]}),await writeFile(_0x2f33c6,_0x1e3f99,_0x212fd7[_0x16ff01(0x195)]);}function getProgressDisplay(_0x27748a){const _0x43316f=_0x41fdff,_0x4558dd={'LyYTj':function(_0x225dde,_0x2fdd87){return _0x225dde+_0x2fdd87;},'PVlOw':function(_0x4e1fee,_0x5da440){return _0x4e1fee/_0x5da440;},'mnDIP':function(_0x13725e,_0x42b491){return _0x13725e>_0x42b491;}},_0x7d3d71=_0x27748a[_0x43316f(0x1b9)]||0x0,_0x2bd4f6=_0x27748a[_0x43316f(0x153)]||0x0,_0x540e27=_0x27748a[_0x43316f(0x140)]||0x0,_0x4efefb=_0x27748a[_0x43316f(0x17c)]||0x0,_0x53062e=_0x27748a['pendingItems']||0x0,_0x12b1d5=_0x4558dd['LyYTj'](_0x7d3d71+_0x2bd4f6,_0x540e27)+_0x4efefb+_0x53062e;if(_0x12b1d5===0x0)return'-';const _0x365b34=0xa,_0x92b073=Math[_0x43316f(0x14f)](_0x4558dd[_0x43316f(0x19f)](_0x7d3d71,_0x12b1d5)*_0x365b34),_0x4a2305=_0x4558dd[_0x43316f(0x112)]('█'[_0x43316f(0x10e)](_0x92b073),'░'[_0x43316f(0x10e)](_0x365b34-_0x92b073)),_0x1af48f=[];if(_0x540e27>0x0)_0x1af48f[_0x43316f(0x1b3)](_0x540e27+_0x43316f(0x166));if(_0x4558dd['mnDIP'](_0x4efefb,0x0))_0x1af48f['push'](_0x4efefb+_0x43316f(0x1ac));if(_0x2bd4f6>0x0)_0x1af48f['push'](_0x2bd4f6+'\x20in\x20progress');const _0x1dea1b=_0x1af48f['length']>0x0?'\x20('+_0x1af48f[_0x43316f(0x176)](',\x20')+')':'';return _0x4a2305+'\x20'+_0x7d3d71+'/'+_0x12b1d5+'\x20verified'+_0x1dea1b;}function displayFeature(_0x246900,_0x4483e9){const _0x4277af=_0x41fdff,_0x1a5db3={'TZDLx':function(_0x869781,_0x934000){return _0x869781<_0x934000;},'SieSS':function(_0x152d17,_0x74224c){return _0x152d17===_0x74224c;},'NArcM':_0x4277af(0x123),'RFcOw':function(_0x55310f,_0x3dd38e){return _0x55310f===_0x3dd38e;},'SFjho':_0x4277af(0x16a),'DCQYj':'incomplete','xgbQj':function(_0x5b9573,_0x39821d){return _0x5b9573===_0x39821d;},'VqoTc':function(_0x7bc28,_0x506927){return _0x7bc28>_0x506927;},'oirMd':function(_0x465be2,_0xdd7e92){return _0x465be2!==_0xdd7e92;},'bKlyw':'verified','RFZAk':'\x20[has\x20reviewer\x20feedback]','xwmgZ':function(_0x255a1,_0x55db4a){return _0x255a1+_0x55db4a;},'IguNy':_0x4277af(0x104),'hqCqH':function(_0x1c1234,_0x306e35){return _0x1c1234===_0x306e35;}};console['log']('\x0a'+_0x246900['name']+'\x20('+_0x246900[_0x4277af(0x164)]+')');_0x246900['stats']&&console['log']('\x20\x20\x20Progress:\x20'+getProgressDisplay(_0x246900['stats']));_0x246900[_0x4277af(0x12a)]&&console['log'](_0x4277af(0x10b)+_0x246900['description']);_0x246900['gitRepoUrl']&&console[_0x4277af(0x137)](_0x4277af(0x131)+_0x246900[_0x4277af(0x18f)]);_0x246900['gitBranch']&&console['log']('\x20\x20\x20Branch:\x20'+_0x246900['gitBranch']);console[_0x4277af(0x137)]('\x20\x20\x20Created:\x20'+new Date(_0x246900[_0x4277af(0x136)])[_0x4277af(0x132)]());_0x246900[_0x4277af(0x185)]&&console['log']('\x20\x20\x20Completed:\x20'+new Date(_0x246900['completedAt'])['toLocaleString']());if(_0x4483e9&&_0x4483e9[_0x4277af(0x191)]>0x0){console['log']('\x0a\x20\x20\x20Scenarios:');for(let _0x43c39f=0x0;_0x1a5db3[_0x4277af(0x1b6)](_0x43c39f,_0x4483e9['length']);_0x43c39f++){const _0x54cde7=_0x4483e9[_0x43c39f];let _0x529cfe;if(_0x1a5db3['SieSS'](_0x54cde7[_0x4277af(0x1a4)],_0x1a5db3[_0x4277af(0x17b)])&&_0x1a5db3['RFcOw'](_0x54cde7['terminalReason'],_0x1a5db3['SFjho']))_0x529cfe='✅';else{if(_0x54cde7['status']==='verified')_0x529cfe='🟢';else{if(_0x1a5db3['SieSS'](_0x54cde7[_0x4277af(0x1a4)],_0x1a5db3['DCQYj']))_0x529cfe='🟠';else{if(_0x1a5db3['xgbQj'](_0x54cde7['status'],'blocked'))_0x529cfe='🛑';else{if(_0x54cde7['status']==='closed')_0x529cfe='⛔';else _0x1a5db3['RFcOw'](_0x54cde7[_0x4277af(0x1a4)],'verification_in_progress')?_0x529cfe='⏳':_0x529cfe='⬜';}}}}const _0x32bfc7=_0x1a5db3[_0x4277af(0x158)](_0x54cde7['version'],0x1)?'\x20(v'+_0x54cde7['version']+')':'',_0x1aa889=_0x54cde7['version']>0x1&&_0x54cde7[_0x4277af(0x14e)]&&_0x54cde7['status']!==_0x1a5db3['NArcM']&&_0x1a5db3['oirMd'](_0x54cde7[_0x4277af(0x1a4)],_0x1a5db3['bKlyw'])?_0x1a5db3[_0x4277af(0x10c)]:'';console[_0x4277af(0x137)](_0x4277af(0x152)+_0x1a5db3[_0x4277af(0x1ab)](_0x43c39f,0x1)+'.\x20'+_0x529cfe+'\x20'+_0x54cde7[_0x4277af(0x12a)]+_0x32bfc7+_0x1aa889);_0x54cde7[_0x4277af(0x1a4)]===_0x1a5db3[_0x4277af(0x160)]&&_0x54cde7[_0x4277af(0x1aa)]&&console['log']('\x20\x20\x20\x20\x20\x20Blocked:\x20'+_0x54cde7[_0x4277af(0x1aa)]);_0x1a5db3['hqCqH'](_0x54cde7['status'],_0x4277af(0x12f))&&_0x54cde7[_0x4277af(0x187)]&&console['log'](_0x4277af(0x121)+_0x54cde7['incompleteReason']);if(_0x1a5db3['RFcOw'](_0x54cde7['status'],_0x4277af(0x123))&&_0x54cde7[_0x4277af(0x130)]==='approved')console[_0x4277af(0x137)](_0x4277af(0x183));else _0x1a5db3['SieSS'](_0x54cde7[_0x4277af(0x1a4)],_0x4277af(0x123))&&_0x54cde7['canceledReason']&&console['log'](_0x4277af(0x19b)+_0x54cde7[_0x4277af(0x108)]);}}}export async function featureCreate(_0x463da7,_0x339d8d){const _0x2a627f=_0x41fdff,_0x147a0c={'rgSxw':function(_0x53f3b3){return _0x53f3b3();},'GNNpD':function(_0x2d8a47){return _0x2d8a47();},'iBkEm':_0x2a627f(0x196),'ZybQW':function(_0x539a10,_0x18b14e){return _0x539a10(_0x18b14e);},'GlkSr':function(_0x596d9a,_0x531188,_0x1a12d1){return _0x596d9a(_0x531188,_0x1a12d1);}},{envNames:_0x841b21}=await getEnvNames();_0x841b21['length']===0x0&&console[_0x2a627f(0x141)]('\x0a'+formatProfileRequiredMessage(_0x31bcb8=>bold(_0x31bcb8)));const _0x2a1ea6=_0x339d8d['scenarios']?_0x339d8d['scenarios'][_0x2a627f(0x146)](_0x10f0c4=>_0x10f0c4[_0x2a627f(0x12b)]()):[],_0x190d5d=_0x147a0c['rgSxw'](getGitRepoUrl),_0x293bb9=_0x147a0c[_0x2a627f(0x1c1)](getGitBranch);console[_0x2a627f(0x137)](_0x147a0c[_0x2a627f(0x11e)]);const _0x1164af=await _0x147a0c[_0x2a627f(0x199)](createFeature,{'name':_0x463da7,'description':_0x339d8d[_0x2a627f(0x12a)],'checklist':_0x2a1ea6,'gitRepoUrl':_0x190d5d,'gitBranch':_0x293bb9});await setActiveFeatureId(_0x1164af['id']),await _0x147a0c[_0x2a627f(0x199)](tryStartCurrentSession,_0x1164af['id']);const _0x20402b=process['env'][_0x2a627f(0x173)];_0x20402b&&registerSession(_0x20402b,_0x1164af['id']),console['log'](_0x2a627f(0x188)+_0x1164af['id']),_0x147a0c['GlkSr'](displayFeature,_0x1164af,_0x1164af[_0x2a627f(0x147)]),console['log'](_0x2a627f(0x1be));}export async function featureList(_0x3c5a4c){const _0x4aba6e=_0x41fdff,_0x155ede={'akUaO':function(_0x57e7c7){return _0x57e7c7();},'GyqCa':function(_0x129c3f){return _0x129c3f();},'sLmvu':function(_0xa4a27d,_0x56f053){return _0xa4a27d(_0x56f053);},'YUCpo':function(_0x54e138,_0x86110b){return _0x54e138>_0x86110b;},'ryLea':function(_0x9d4530,_0xb36c3){return _0x9d4530<_0xb36c3;}},_0x10dbdb={'limit':_0x3c5a4c[_0x4aba6e(0x124)]??0xa,'offset':_0x3c5a4c[_0x4aba6e(0x107)]??0x0,'includeDeleted':_0x3c5a4c['includeDeleted']};_0x3c5a4c[_0x4aba6e(0x1bf)]&&(_0x10dbdb['gitRepoUrl']=_0x155ede['akUaO'](getGitRepoUrl),_0x10dbdb[_0x4aba6e(0x189)]=_0x155ede[_0x4aba6e(0x126)](getGitBranch));const _0x477660=await _0x155ede['sLmvu'](listFeatures,_0x10dbdb);if(_0x477660[_0x4aba6e(0x109)]['length']===0x0){console['log'](_0x4aba6e(0x119));return;}const _0x227c11=_0x155ede[_0x4aba6e(0x11c)](_0x477660['totalCount'],_0x477660['items'][_0x4aba6e(0x191)])?'Showing\x20'+_0x477660['items'][_0x4aba6e(0x191)]+'\x20of\x20'+_0x477660['totalCount']:_0x477660[_0x4aba6e(0x149)]+_0x4aba6e(0x110);console['log']('\x0a'+_0x227c11+':\x0a');for(const _0x57975d of _0x477660['items']){const _0x3d68ba='3|0|4|5|2|1'['split']('|');let _0x4a49ac=0x0;while(!![]){switch(_0x3d68ba[_0x4a49ac++]){case'0':console[_0x4aba6e(0x137)]('\x20\x20\x20ID:\x20'+_0x57975d['id']);continue;case'1':console['log']('');continue;case'2':console['log'](_0x4aba6e(0x1a5)+new Date(_0x57975d[_0x4aba6e(0x136)])['toLocaleDateString']());continue;case'3':console['log'](''+_0x57975d['name']);continue;case'4':_0x57975d['stats']&&console['log'](_0x4aba6e(0x12e)+_0x155ede[_0x4aba6e(0x111)](getProgressDisplay,_0x57975d[_0x4aba6e(0x18e)]));continue;case'5':_0x57975d['gitBranch']&&console[_0x4aba6e(0x137)](_0x4aba6e(0x1b5)+_0x57975d[_0x4aba6e(0x189)]);continue;}break;}}const _0xd66a3=_0x10dbdb[_0x4aba6e(0x124)]??0xa,_0x4b555f=(_0x10dbdb[_0x4aba6e(0x107)]??0x0)+_0x477660['items']['length'];if(_0x155ede[_0x4aba6e(0x143)](_0x4b555f,_0x477660['totalCount'])){const _0x3926c0=_0xd66a3!==0xa?_0x4aba6e(0x120)+_0xd66a3:'',_0x3678c2=_0x3c5a4c['currentBranch']?_0x4aba6e(0x17d):'',_0x3918ec=_0x3c5a4c['includeDeleted']?_0x4aba6e(0x159):'';console[_0x4aba6e(0x137)]('Next\x20page:\x20ranger\x20list\x20--offset\x20'+_0x4b555f+_0x3926c0+_0x3678c2+_0x3918ec);}}export async function featureShow(_0x40ae8b){const _0x4e0fb3=_0x41fdff,_0x223bb5={'fqZMv':_0x4e0fb3(0x14d),'QqObA':function(_0x4c7fd9,_0x5effaf){return _0x4c7fd9(_0x5effaf);},'bqjZf':function(_0x4d0043,_0x491c4d,_0xdc3855){return _0x4d0043(_0x491c4d,_0xdc3855);}},_0x108ce4=_0x40ae8b||await getActiveFeatureId();if(!_0x108ce4)throw new Error(_0x223bb5[_0x4e0fb3(0x15c)]);const _0x4f935b=await _0x223bb5['QqObA'](getFeature,_0x108ce4);_0x223bb5['bqjZf'](displayFeature,_0x4f935b,_0x4f935b[_0x4e0fb3(0x147)]);}export async function featureResume(_0x20dde5){const _0x2f0c4d=_0x41fdff,_0x12fd7a={'zjfci':function(_0x1ed6c9,_0x4390c0){return _0x1ed6c9!==_0x4390c0;},'dtGEO':function(_0x1455a4,_0x33d57e){return _0x1455a4(_0x33d57e);},'tPPxs':function(_0x42c00e,_0x4e94d5,_0x4c8276){return _0x42c00e(_0x4e94d5,_0x4c8276);},'npLPv':function(_0x272ef1){return _0x272ef1();},'IEWov':function(_0x1390ee,_0x2a7cdc){return _0x1390ee&&_0x2a7cdc;},'pIkID':_0x2f0c4d(0x135),'zAnVG':'\x0aNo\x20matching\x20active\x20feature\x20reviews\x20found.','QBCId':function(_0x1822ab,_0x38901f){return _0x1822ab===_0x38901f;},'mvFFT':'\x0aNon-interactive\x20mode\x20detected.\x20Multiple\x20feature\x20reviews\x20found:','TIyym':_0x2f0c4d(0x15e),'XsYSg':'list','Kohxc':_0x2f0c4d(0x17e),'UYnRH':'Multiple\x20feature\x20reviews\x20found.\x20Select\x20one:','KTbgB':function(_0x2e0c4d,_0x3bf485){return _0x2e0c4d(_0x3bf485);},'BcZyd':function(_0x155e59,_0x38f293,_0x4554e2){return _0x155e59(_0x38f293,_0x4554e2);}};if(_0x20dde5){const _0x5abca5=await getFeature(_0x20dde5);await setActiveFeatureId(_0x5abca5['id']);const _0x23936d=getGitBranch();_0x23936d&&_0x12fd7a['zjfci'](_0x23936d,_0x5abca5[_0x2f0c4d(0x189)])&&(await updateFeature(_0x5abca5['id'],{'gitBranch':_0x23936d}),console[_0x2f0c4d(0x137)]('\x20\x20\x20Updated\x20branch\x20to:\x20'+_0x23936d));console[_0x2f0c4d(0x137)]('\x0a✅\x20Resumed\x20feature\x20review:\x20'+_0x5abca5['name']+'\x20('+_0x5abca5['id']+')'),await _0x12fd7a[_0x2f0c4d(0x171)](tryStartCurrentSession,_0x5abca5['id']);const _0x222c7a=process['env'][_0x2f0c4d(0x173)];_0x222c7a&&_0x12fd7a[_0x2f0c4d(0x15f)](registerSession,_0x222c7a,_0x5abca5['id']);displayFeature(_0x5abca5,_0x5abca5[_0x2f0c4d(0x147)]),await printFeedbackNudge(_0x5abca5['id']);return;}const _0x173492=getGitRepoUrl(),_0x165e92=_0x12fd7a['npLPv'](getGitBranch);if(_0x12fd7a[_0x2f0c4d(0x179)](!_0x173492,!_0x165e92))throw new Error(_0x12fd7a['pIkID']);console[_0x2f0c4d(0x137)]('\x0aSearching\x20for\x20feature\x20reviews\x20matching:\x20'+(_0x173492||_0x2f0c4d(0x167))+'\x20/\x20'+(_0x165e92||_0x2f0c4d(0x167))+_0x2f0c4d(0x13b));const _0x33b750=await listFeatures({'gitRepoUrl':_0x173492,'gitBranch':_0x165e92}),_0x2b2fb0=_0x33b750[_0x2f0c4d(0x109)]['filter'](_0x52acd2=>!_0x52acd2['completedAt']);if(_0x2b2fb0[_0x2f0c4d(0x191)]===0x0){console[_0x2f0c4d(0x137)](_0x12fd7a[_0x2f0c4d(0x165)]),console['log']('Run:\x20ranger\x20create\x20\x22<name>\x22\x20to\x20create\x20a\x20new\x20feature\x20review');return;}let _0x5c6c8c;if(_0x12fd7a[_0x2f0c4d(0x16b)](_0x2b2fb0['length'],0x1))_0x5c6c8c=_0x2b2fb0[0x0];else{const _0x18a674=process[_0x2f0c4d(0x184)]['isTTY']&&process[_0x2f0c4d(0x170)][_0x2f0c4d(0x1ba)];if(!_0x18a674){console['log'](_0x12fd7a[_0x2f0c4d(0x194)]);for(const _0xbe0e3b of _0x2b2fb0){console['log']('\x20\x20'+_0xbe0e3b['name']),console[_0x2f0c4d(0x137)](_0x2f0c4d(0x16f)+_0xbe0e3b['id']),_0xbe0e3b[_0x2f0c4d(0x18e)]&&console[_0x2f0c4d(0x137)](_0x2f0c4d(0x14b)+getProgressDisplay(_0xbe0e3b['stats'])),_0xbe0e3b[_0x2f0c4d(0x189)]&&console['log'](_0x2f0c4d(0x151)+_0xbe0e3b[_0x2f0c4d(0x189)]);}throw new Error(_0x12fd7a[_0x2f0c4d(0x12c)]);}const {selected:_0x496d5e}=await _0x5c0d7c[_0x2f0c4d(0x138)]([{'type':_0x12fd7a['XsYSg'],'name':_0x12fd7a[_0x2f0c4d(0x193)],'message':_0x12fd7a[_0x2f0c4d(0x16d)],'choices':_0x2b2fb0[_0x2f0c4d(0x146)](_0x2538f8=>({'name':_0x2538f8['name']+'\x20('+_0x2538f8['id']+')','value':_0x2538f8['id']}))}]);_0x5c6c8c=_0x2b2fb0['find'](_0x5b37e0=>_0x5b37e0['id']===_0x496d5e);}await setActiveFeatureId(_0x5c6c8c['id']);_0x165e92&&_0x165e92!==_0x5c6c8c[_0x2f0c4d(0x189)]&&(await _0x12fd7a['tPPxs'](updateFeature,_0x5c6c8c['id'],{'gitBranch':_0x165e92}),console['log']('\x20\x20\x20Updated\x20branch\x20to:\x20'+_0x165e92));console[_0x2f0c4d(0x137)](_0x2f0c4d(0x162)+_0x5c6c8c['name']+'\x20('+_0x5c6c8c['id']+')'),await tryStartCurrentSession(_0x5c6c8c['id']);const _0x5780a7=await _0x12fd7a[_0x2f0c4d(0x174)](getFeature,_0x5c6c8c['id']),_0x3ff65c=process['env']['CLAUDE_SESSION_ID'];_0x3ff65c&&registerSession(_0x3ff65c,_0x5780a7['id']),_0x12fd7a['BcZyd'](displayFeature,_0x5780a7,_0x5780a7['checklistItems']),await printFeedbackNudge(_0x5780a7['id']);}async function printFeedbackNudge(_0x35d994){const _0x540a9f=_0x41fdff,_0x3eb21b={'ZvGsz':function(_0x1f3112,_0x253ac1){return _0x1f3112(_0x253ac1);},'itvFN':function(_0x4a3f69,_0x2670cc){return _0x4a3f69>_0x2670cc;},'XLTWi':'\x20\x20\x20Run\x20`ranger\x20get-review`\x20to\x20see\x20what\x20needs\x20to\x20be\x20fixed.'};try{const {items:_0xe8b4ac}=await _0x3eb21b['ZvGsz'](getActionItems,_0x35d994);let _0xe96d38=0x0,_0x2184e1=0x0;for(const _0x291c79 of _0xe8b4ac){const _0x28fe08=await getItemFeedback(_0x35d994,_0x291c79['id']);_0x3eb21b['itvFN'](_0x28fe08[_0x540a9f(0x11d)][_0x540a9f(0x191)],0x0)&&(_0xe96d38+=_0x28fe08['unaddressedComments'][_0x540a9f(0x191)],_0x2184e1++);}_0xe96d38>0x0&&(console['log']('\x0a⚠️\x20\x20'+_0xe96d38+_0x540a9f(0x1b1)+_0x2184e1+_0x540a9f(0x198)),console['log'](_0x3eb21b[_0x540a9f(0x1af)]));}catch{}}async function tryStartCurrentSession(_0x3a2d27){const _0x19434b=_0x41fdff,_0x3d8848={'HsKTP':function(_0x5e404f,_0x1a0d19){return _0x5e404f(_0x1a0d19);},'qnxHV':function(_0x4b8e6a,_0x40ecc3,_0x53043f){return _0x4b8e6a(_0x40ecc3,_0x53043f);}};try{const {sessions:_0x4ee109}=await _0x3d8848['HsKTP'](listFeatureSessions,_0x3a2d27),_0x1009d8=_0x4ee109[_0x19434b(0x186)](_0x5b3c2c=>_0x5b3c2c[_0x19434b(0x1a4)]==='ready');_0x1009d8&&(await _0x3d8848[_0x19434b(0x155)](startSession,_0x3a2d27,_0x1009d8['id']),console['log']('\x20\x20\x20▶️\x20Started\x20session\x20'+_0x1009d8[_0x19434b(0x13d)]));}catch{}}export async function featureReport(_0x1eff91,_0x2fd680){const _0x589367=_0x41fdff,_0x3fcf01={'yiACl':function(_0x4be988){return _0x4be988();},'pxONe':'No\x20feature\x20review\x20ID\x20provided\x20and\x20no\x20active\x20feature\x20review\x20set.','MCbJe':function(_0x36e153,_0x35114c,_0x1c8394,_0x6b1ba2){return _0x36e153(_0x35114c,_0x1c8394,_0x6b1ba2);},'USqDb':function(_0x4f1611,_0x2b9d5f,_0x1cccb0){return _0x4f1611(_0x2b9d5f,_0x1cccb0);},'snCMp':function(_0x1c1160,_0x250ac8){return _0x1c1160(_0x250ac8);},'chQHb':'utf-8'},_0x2a5914=_0x1eff91||await _0x3fcf01[_0x589367(0x142)](getActiveFeatureId);if(!_0x2a5914)throw new Error(_0x3fcf01[_0x589367(0x192)]);console['log'](_0x589367(0x169));const _0x2d8cce=await getFeatureReportMarkdown(_0x2a5914,{'style':_0x2fd680[_0x589367(0x154)]}),_0x101e1b=_0x2fd680['output']||_0x3fcf01['MCbJe'](join,getRangerDir(),'reports',_0x2a5914+_0x589367(0x18c)),_0x5ba232=_0x3fcf01[_0x589367(0x190)](join,_0x101e1b,'..');!_0x3fcf01['snCMp'](existsSync,_0x5ba232)&&await _0x3fcf01['USqDb'](mkdir,_0x5ba232,{'recursive':!![]}),await writeFile(_0x101e1b,_0x2d8cce,_0x3fcf01['chQHb']),console['log']('\x0a✅\x20Report\x20generated:\x20'+_0x101e1b);}export async function featureConcludeSession(_0x8e209c){const _0x1bf4a3=_0x41fdff,_0x24699d={'kIQLK':_0x1bf4a3(0x14d),'XClmH':function(_0x21a617,_0x93d2ed){return _0x21a617(_0x93d2ed);},'mOgag':'No\x20active\x20session\x20for\x20this\x20feature\x20review.','dTREt':_0x1bf4a3(0x144),'RyGqV':'\x0aConcluding\x20session...','cBney':function(_0x476bc8,_0x2749cc,_0xbbb093){return _0x476bc8(_0x2749cc,_0xbbb093);}},_0x2ac3d7=_0x8e209c||await getActiveFeatureId();if(!_0x2ac3d7)throw new Error(_0x24699d['kIQLK']);const _0x2752e5=await _0x24699d['XClmH'](getFeature,_0x2ac3d7);if(!_0x2752e5['currentSessionId'])throw new Error(_0x24699d[_0x1bf4a3(0x18b)]);const {sessions:_0x383fa4}=await listFeatureSessions(_0x2ac3d7),_0x1de903=_0x383fa4['find'](_0x5b46a7=>_0x5b46a7['id']===_0x2752e5['currentSessionId']);if(!_0x1de903)throw new Error(_0x24699d['dTREt']);if(_0x1de903['status']===_0x1bf4a3(0x18d)){console['log']('\x0a✅\x20Session\x20is\x20already\x20completed.');return;}if(_0x1de903[_0x1bf4a3(0x1a4)]!=='in_progress')throw new Error(_0x1bf4a3(0x139)+_0x1de903['status']);console[_0x1bf4a3(0x137)](_0x24699d['RyGqV']),await _0x24699d[_0x1bf4a3(0x17a)](concludeSession,_0x2ac3d7,_0x2752e5['currentSessionId']),console['log'](_0x1bf4a3(0x156)+_0x1de903['iteration']+'\x20concluded.'),console[_0x1bf4a3(0x137)](_0x1bf4a3(0x13a));}export async function featureSessions(_0x4ee4b1){const _0x2ca95e=_0x41fdff,_0x3af663={'sLUMQ':function(_0x328761){return _0x328761();},'kMkyY':function(_0x4d3c08,_0x53cd66){return _0x4d3c08(_0x53cd66);},'mifur':_0x2ca95e(0x18d)},_0x25948f=_0x4ee4b1||await _0x3af663['sLUMQ'](getActiveFeatureId);if(!_0x25948f)throw new Error('No\x20feature\x20review\x20ID\x20provided\x20and\x20no\x20active\x20feature\x20review\x20set.');const _0x7089f1=await getFeature(_0x25948f);console['log'](_0x2ca95e(0x19a)+_0x7089f1[_0x2ca95e(0x1bd)]+'\x0a');const {sessions:_0x581f6e}=await _0x3af663['kMkyY'](listFeatureSessions,_0x25948f);if(_0x581f6e['length']===0x0){console[_0x2ca95e(0x137)](_0x2ca95e(0x16e));return;}for(const _0x2be6cd of _0x581f6e){const _0x1d80a2=_0x2be6cd[_0x2ca95e(0x1a4)]===_0x3af663['mifur']?'✅':_0x2be6cd['status']==='in_progress'?'🔄':'▶️';console['log'](_0x1d80a2+'\x20Session\x20'+_0x2be6cd[_0x2ca95e(0x13d)]+'\x20('+_0x2be6cd['id']+')'),console[_0x2ca95e(0x137)](_0x2ca95e(0x1b2)+_0x2be6cd[_0x2ca95e(0x1a4)]),console[_0x2ca95e(0x137)](_0x2ca95e(0x1a5)+new Date(_0x2be6cd['createdAt'])['toLocaleString']()),_0x2be6cd[_0x2ca95e(0x185)]&&console['log']('\x20\x20\x20Completed:\x20'+new Date(_0x2be6cd['completedAt'])[_0x2ca95e(0x132)]()),console[_0x2ca95e(0x137)]('');}}export async function featureAddScenario(_0x271f22,_0x3fa27f){const _0x590b37=_0x41fdff,_0x2b0cc1={'ZVWcn':function(_0x170338,_0x3479e5,_0x3840e8){return _0x170338(_0x3479e5,_0x3840e8);},'XpdWW':function(_0x1363ed,_0x41c415){return _0x1363ed(_0x41c415);},'YLmII':function(_0x942f66,_0xbf5655,_0x597def){return _0x942f66(_0xbf5655,_0x597def);}},_0x241cc9=_0x3fa27f||await getActiveFeatureId();if(!_0x241cc9)throw new Error(_0x590b37(0x14d));const _0x27f729=await getFeature(_0x241cc9);if(_0x27f729[_0x590b37(0x177)]&&!_0x27f729['latestReview']['submittedAt'])throw new Error('Cannot\x20add\x20scenario:\x20feature\x20review\x20has\x20an\x20active\x20review.\x20Please\x20finish\x20your\x20review\x20first:\x20'+_0x27f729['dashboardUrl']);await _0x2b0cc1[_0x590b37(0x1bc)](createChecklistItem,_0x241cc9,_0x271f22);const _0x1fb556=await _0x2b0cc1[_0x590b37(0x134)](getFeature,_0x241cc9);console['log']('\x0a✅\x20Scenario\x20added'),_0x2b0cc1[_0x590b37(0x172)](displayFeature,_0x1fb556,_0x1fb556['checklistItems']);}export async function featureEditScenario(_0x5d7888,_0x92d18d){const _0x1e571a=_0x41fdff,_0x1d413d={'QSqII':'Run:\x20ranger\x20resume\x20<id>\x20to\x20set\x20an\x20active\x20feature\x20review','LONsj':'\x0a❌\x20Cannot\x20edit\x20scenario:\x20feature\x20review\x20has\x20an\x20active\x20review.','SSCDh':'Please\x20finish\x20your\x20review\x20first:','wpSMF':'\x0a❌\x20No\x20scenarios\x20found\x20to\x20edit.','DfBwV':'\x0aAvailable\x20scenarios:','fCZKw':function(_0x43e5d3,_0x14afa6){return _0x43e5d3-_0x14afa6;},'lPMCk':function(_0x5282aa,_0x4dcc38){return _0x5282aa<_0x4dcc38;},'gQJbg':function(_0x1e4a76,_0x556ce8){return _0x1e4a76>=_0x556ce8;},'NYTYb':function(_0x273722,_0x5a5ca5,_0x8bd3f6){return _0x273722(_0x5a5ca5,_0x8bd3f6);}},_0x222398=_0x92d18d['id']||await getActiveFeatureId();!_0x222398&&(console['error']('\x0a❌\x20No\x20feature\x20review\x20ID\x20provided\x20and\x20no\x20active\x20feature\x20review\x20set.'),console[_0x1e571a(0x197)](_0x1d413d[_0x1e571a(0x150)]),process['exit'](0x1));const _0x43374b=await getFeature(_0x222398);_0x43374b[_0x1e571a(0x177)]&&!_0x43374b[_0x1e571a(0x177)]['submittedAt']&&(console['error'](_0x1d413d['LONsj']),console[_0x1e571a(0x197)](_0x1d413d['SSCDh']),console[_0x1e571a(0x197)]('\x20\x20'+_0x43374b['dashboardUrl']),process['exit'](0x1));const _0x125b62=_0x43374b[_0x1e571a(0x147)]||[];_0x125b62['length']===0x0&&(console['error'](_0x1d413d[_0x1e571a(0x129)]),process[_0x1e571a(0x117)](0x1));if(_0x92d18d[_0x1e571a(0x1b7)]===undefined){console[_0x1e571a(0x197)]('\x0a❌\x20No\x20scenario\x20number\x20provided.\x20Use\x20--scenario\x20<number>.'),console['log'](_0x1d413d['DfBwV']);for(let _0x151be2=0x0;_0x151be2<_0x125b62[_0x1e571a(0x191)];_0x151be2++){console[_0x1e571a(0x137)]('\x20\x20'+(_0x151be2+0x1)+'.\x20'+_0x125b62[_0x151be2][_0x1e571a(0x12a)]);}process['exit'](0x1);}const _0x3b76be=_0x1d413d[_0x1e571a(0x133)](_0x92d18d['scenario'],0x1);if(_0x1d413d['lPMCk'](_0x3b76be,0x0)||_0x1d413d[_0x1e571a(0x148)](_0x3b76be,_0x125b62[_0x1e571a(0x191)])){console['error']('\x0a❌\x20Invalid\x20scenario\x20index:\x20'+_0x92d18d[_0x1e571a(0x1b7)]+'.\x20Feature\x20review\x20has\x20'+_0x125b62[_0x1e571a(0x191)]+_0x1e571a(0x12d)),console[_0x1e571a(0x137)](_0x1d413d[_0x1e571a(0x1a0)]);for(let _0xfa4876=0x0;_0x1d413d[_0x1e571a(0x175)](_0xfa4876,_0x125b62[_0x1e571a(0x191)]);_0xfa4876++){console[_0x1e571a(0x137)]('\x20\x20'+(_0xfa4876+0x1)+'.\x20'+_0x125b62[_0xfa4876][_0x1e571a(0x12a)]);}process['exit'](0x1);}const _0x59f2c8=_0x125b62[_0x3b76be];await updateChecklistItem(_0x222398,_0x59f2c8['id'],{'description':_0x5d7888});const _0x348249=await getFeature(_0x222398);console[_0x1e571a(0x137)](_0x1e571a(0x127)),_0x1d413d[_0x1e571a(0x10d)](displayFeature,_0x348249,_0x348249[_0x1e571a(0x147)]);}async function downloadFeedbackImage(_0x69ec19,_0x320468){const _0x5d692a=_0x41fdff,_0x77f7c7={'RNdrr':function(_0x414c02,_0x3be0fb,_0x59997d){return _0x414c02(_0x3be0fb,_0x59997d);},'cLgJz':_0x5d692a(0x115),'hMOJT':function(_0x1e0d6e,_0x2773e1,_0xe542ab){return _0x1e0d6e(_0x2773e1,_0xe542ab);}};try{const _0x2347de=_0x77f7c7['RNdrr'](join,getRangerDir(),_0x77f7c7['cLgJz']);await _0x77f7c7[_0x5d692a(0x1a3)](mkdir,_0x2347de,{'recursive':!![]});const _0x200089=join(_0x2347de,'comment_'+_0x320468+'.png'),_0x17e87d=await fetch(_0x69ec19);if(!_0x17e87d['ok'])return null;const _0x344594=Buffer['from'](await _0x17e87d['arrayBuffer']());return await writeFile(_0x200089,_0x344594),_0x200089;}catch{return null;}}export async function featureGetReview(_0x249273){const _0x2a4ff0=_0x41fdff,_0x3926c4={'OFrCB':function(_0x339ecd){return _0x339ecd();},'DQWah':'No\x20feature\x20review\x20ID\x20provided\x20and\x20no\x20active\x20feature\x20review\x20set.\x20Run:\x20ranger\x20resume\x20<id>\x20to\x20set\x20an\x20active\x20feature\x20review','SncOq':function(_0x5a3fc7,_0x156ac7){return _0x5a3fc7(_0x156ac7);},'jxfTU':_0x2a4ff0(0x10f),'YQtgB':function(_0x411983,_0x2a5338){return _0x411983<_0x2a5338;},'UMsdC':function(_0x134aa8,_0xef37bc){return _0x134aa8>_0xef37bc;},'mQkpY':function(_0x5f0722,_0x2e5065,_0x151726){return _0x5f0722(_0x2e5065,_0x151726);},'dGMiE':function(_0x3a2da4,_0x59af47){return _0x3a2da4+_0x59af47;},'NpNkG':'en-US','IaumI':'short','HVQAU':function(_0xdca3a9,_0x5aea36,_0x4d9578){return _0xdca3a9(_0x5aea36,_0x4d9578);},'uhrey':function(_0x25176c,_0x4e6d89){return _0x25176c===_0x4e6d89;},'UImwA':'point','hIbSh':function(_0x4b69f6,_0x158e62){return _0x4b69f6+_0x158e62;}},_0x7af38f=_0x249273||await _0x3926c4['OFrCB'](getActiveFeatureId);if(!_0x7af38f)throw new Error(_0x3926c4['DQWah']);const _0x482a8b=await _0x3926c4['SncOq'](getFeature,_0x7af38f),{items:_0x4ed4d0}=await getActionItems(_0x7af38f);console[_0x2a4ff0(0x137)]('\x0a📋\x20Feedback\x20for:\x20'+_0x482a8b[_0x2a4ff0(0x1bd)]+'\x20('+_0x482a8b['id']+')\x0a');if(_0x4ed4d0['length']===0x0){console[_0x2a4ff0(0x137)](_0x3926c4[_0x2a4ff0(0x1a7)]);return;}for(let _0x5d48e6=0x0;_0x3926c4['YQtgB'](_0x5d48e6,_0x4ed4d0['length']);_0x5d48e6++){const _0x5cbe87=_0x4ed4d0[_0x5d48e6],_0x4c1741=_0x3926c4[_0x2a4ff0(0x116)](_0x5cbe87[_0x2a4ff0(0x163)],0x1)?_0x2a4ff0(0x11a)+_0x5cbe87[_0x2a4ff0(0x163)]+')':'',_0x845b4e=await _0x3926c4['mQkpY'](getItemFeedback,_0x7af38f,_0x5cbe87['id']);if(_0x3926c4['UMsdC'](_0x845b4e['unaddressedComments']['length'],0x0)){console[_0x2a4ff0(0x137)](_0x2a4ff0(0x11f)+_0x3926c4['dGMiE'](_0x5d48e6,0x1)+_0x2a4ff0(0x14a)+_0x5cbe87[_0x2a4ff0(0x12a)]+'\x22'+_0x4c1741+_0x2a4ff0(0x178)+_0x845b4e['unaddressedComments']['length']+_0x2a4ff0(0x1a2));for(const _0x3cf252 of _0x845b4e['unaddressedComments']){const _0x18fa39=new Date(_0x3cf252['createdAt'])[_0x2a4ff0(0x13f)](_0x3926c4['NpNkG'],{'month':_0x3926c4['IaumI'],'day':'numeric'}),_0x4b85df=_0x3cf252[_0x2a4ff0(0x181)]||_0x3cf252['authorEmail']||'Unknown';console['log']('\x20\x20💬\x20'+_0x4b85df+'\x20('+_0x18fa39+_0x2a4ff0(0x18a)+_0x3cf252[_0x2a4ff0(0x1ae)]+'\x22');if(_0x3cf252[_0x2a4ff0(0x14c)]){const _0x18a448=await _0x3926c4[_0x2a4ff0(0x17f)](downloadFeedbackImage,_0x3cf252['assetUrl'],_0x3cf252['id']);_0x18a448&&console[_0x2a4ff0(0x137)]('\x20\x20\x20\x20\x20📷\x20Screenshot:\x20'+_0x18a448);}if(_0x3cf252['spatial']){const _0x55066f=_0x3cf252[_0x2a4ff0(0x1b8)][_0x2a4ff0(0x1ad)];_0x3926c4['uhrey'](_0x3cf252[_0x2a4ff0(0x1b8)]['type'],_0x3926c4['UImwA'])&&_0x55066f['length']>=0x2?console['log'](_0x2a4ff0(0x1b4)+_0x55066f[0x0]['toFixed'](0x2)+',\x20'+_0x55066f[0x1]['toFixed'](0x2)+')'):console[_0x2a4ff0(0x137)]('\x20\x20\x20\x20\x20📍\x20Annotation:\x20'+_0x3cf252[_0x2a4ff0(0x1b8)][_0x2a4ff0(0x1a8)]+_0x2a4ff0(0x19e)+_0x55066f[_0x2a4ff0(0x146)](_0x3282ea=>_0x3282ea[_0x2a4ff0(0x118)](0x2))[_0x2a4ff0(0x176)](',\x20')+']');}}if(_0x845b4e['canonicalFlow']){console['log']('\x20\x20Expected\x20flow:');for(const _0x2bda3a of _0x845b4e['canonicalFlow'][_0x2a4ff0(0x182)]('\x0a')){console['log'](_0x2a4ff0(0x19d)+_0x2bda3a);}}}else console[_0x2a4ff0(0x137)](_0x2a4ff0(0x11f)+_0x3926c4[_0x2a4ff0(0x106)](_0x5d48e6,0x1)+_0x2a4ff0(0x14a)+_0x5cbe87[_0x2a4ff0(0x12a)]+'\x22'+_0x4c1741+'\x20—\x20no\x20comments'),console['log'](_0x2a4ff0(0x145));console[_0x2a4ff0(0x137)]('');}}export async function featureDelete(_0x4bf0f5){const _0x528995=_0x41fdff,_0x204341={'mnRqy':function(_0x4675d6){return _0x4675d6();},'YQFyP':'No\x20feature\x20review\x20ID\x20provided\x20and\x20no\x20active\x20feature\x20review\x20set.\x20Run:\x20ranger\x20resume\x20<id>\x20to\x20set\x20an\x20active\x20feature\x20review','eXBWi':function(_0x59f907,_0x4d4bbd){return _0x59f907(_0x4d4bbd);},'YocTg':function(_0x5aba11,_0x1ee6ba){return _0x5aba11+_0x1ee6ba;}},_0x4abfc2=_0x4bf0f5||await _0x204341[_0x528995(0x122)](getActiveFeatureId);if(!_0x4abfc2)throw new Error(_0x204341['YQFyP']);const _0x1327f2=await getFeature(_0x4abfc2);await _0x204341[_0x528995(0x114)](softDeleteFeature,_0x4abfc2),console[_0x528995(0x137)]('\x0a🗑️\x20\x20Deleted\x20feature\x20review:\x20'+_0x1327f2['name']+'\x20('+_0x4abfc2+')'),console['log'](_0x204341['YocTg'](_0x528995(0x1c0),_0x4abfc2));}export async function featureRestore(_0x397b74){const _0x1dcfb4=_0x41fdff,_0xa25e7d={'WjQrj':function(_0x42229c,_0x4ef6c9){return _0x42229c(_0x4ef6c9);},'mHivn':function(_0x33de22,_0x201d48,_0x569e95){return _0x33de22(_0x201d48,_0x569e95);}};await _0xa25e7d['WjQrj'](restoreFeature,_0x397b74);const _0x352cc7=await getFeature(_0x397b74);console[_0x1dcfb4(0x137)]('\x0a✅\x20Restored\x20feature\x20review:\x20'+_0x352cc7[_0x1dcfb4(0x1bd)]+'\x20('+_0x397b74+')'),_0xa25e7d[_0x1dcfb4(0x180)](displayFeature,_0x352cc7,_0x352cc7['checklistItems']);}