@ranger-testing/ranger-cli 1.0.10 → 1.0.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +111 -0
- package/build/cli.js +290 -8
- package/build/cli.js.map +1 -1
- package/build/commands/addEnv.js +1 -1
- package/build/commands/addEnv.js.map +1 -1
- package/build/commands/clean.js +1 -1
- package/build/commands/clean.js.map +1 -1
- package/build/commands/dataMcpServer.js +1 -1
- package/build/commands/dataMcpServer.js.map +1 -1
- package/build/commands/env.js +46 -0
- package/build/commands/env.js.map +1 -0
- package/build/commands/feature.js +494 -0
- package/build/commands/feature.js.map +1 -0
- package/build/commands/index.js +1 -0
- package/build/commands/index.js.map +1 -1
- package/build/commands/skillup.js +68 -16
- package/build/commands/skillup.js.map +1 -1
- package/build/commands/start.js +1 -1
- package/build/commands/start.js.map +1 -1
- package/build/commands/status.js +42 -11
- package/build/commands/status.js.map +1 -1
- package/build/commands/update.js +29 -16
- package/build/commands/update.js.map +1 -1
- package/build/commands/updateEnv.js +1 -1
- package/build/commands/updateEnv.js.map +1 -1
- package/build/commands/useEnv.js +1 -1
- package/build/commands/useEnv.js.map +1 -1
- package/build/commands/utils/browserSessionsApi.js +1 -1
- package/build/commands/utils/browserSessionsApi.js.map +1 -1
- package/build/commands/utils/claudeConfig.js +73 -0
- package/build/commands/utils/claudeConfig.js.map +1 -0
- package/build/commands/utils/cliSecret.js +1 -1
- package/build/commands/utils/environment.js +69 -0
- package/build/commands/utils/environment.js.map +1 -0
- package/build/commands/utils/featureApi.js +190 -0
- package/build/commands/utils/featureApi.js.map +1 -0
- package/build/commands/utils/featureReportGenerator.js +170 -0
- package/build/commands/utils/featureReportGenerator.js.map +1 -0
- package/build/commands/utils/keychain.js +1 -1
- package/build/commands/utils/localAgentInstallationsApi.js +1 -1
- package/build/commands/utils/localAgentInstallationsApi.js.map +1 -1
- package/build/commands/utils/mcpConfig.js +1 -1
- package/build/commands/utils/settings.js +2 -2
- package/build/commands/utils/settings.js.map +1 -1
- package/build/commands/utils/skills.js +1 -1
- package/build/commands/utils/skills.js.map +1 -1
- package/build/commands/verifyFeature.js +451 -0
- package/build/commands/verifyFeature.js.map +1 -0
- package/build/commands/verifyInBrowser.js +1 -1
- package/build/commands/verifyInBrowser.js.map +1 -1
- package/build/skills/feature-tracker/SKILL.md +185 -0
- package/build/skills/feature-tracker/create.md +105 -0
- package/build/skills/feature-tracker/manage.md +145 -0
- package/build/skills/feature-tracker/report.md +159 -0
- package/build/skills/feature-tracker/start.md +93 -0
- package/build/skills/feature-tracker/verify.md +143 -0
- package/package.json +23 -20
- package/build/agents/bug-basher.md +0 -259
- package/build/agents/e2e-test-recommender.md +0 -164
- package/build/agents/quality-advocate.md +0 -164
- package/build/agents/ui-verifier.md +0 -100
- package/build/commands/addApp.js +0 -21
- package/build/commands/initAgents.js +0 -84
- package/build/commands/utils/agents.js +0 -45
- package/build/index.js +0 -436
- package/build/test-auth.js +0 -13
|
@@ -1 +1 @@
|
|
|
1
|
-
function
|
|
1
|
+
function _0x2874(_0x38de0c,_0x32555c){_0x38de0c=_0x38de0c-0x110;const _0x14d0a6=_0x3186();let _0x1ac93b=_0x14d0a6[_0x38de0c];if(_0x2874['OPceef']===undefined){var _0x2afa2e=function(_0x138ee1){const _0xda2819='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/=';let _0x294d69='',_0x33fb9c='',_0x4450d8=_0x294d69+_0x2afa2e;for(let _0x63f5a7=0x0,_0x5e9113,_0x54dd7d,_0x4297fe=0x0;_0x54dd7d=_0x138ee1['charAt'](_0x4297fe++);~_0x54dd7d&&(_0x5e9113=_0x63f5a7%0x4?_0x5e9113*0x40+_0x54dd7d:_0x54dd7d,_0x63f5a7++%0x4)?_0x294d69+=_0x4450d8['charCodeAt'](_0x4297fe+0xa)-0xa!==0x0?String['fromCharCode'](0xff&_0x5e9113>>(-0x2*_0x63f5a7&0x6)):_0x63f5a7:0x0){_0x54dd7d=_0xda2819['indexOf'](_0x54dd7d);}for(let _0x1d1ab8=0x0,_0xe3750=_0x294d69['length'];_0x1d1ab8<_0xe3750;_0x1d1ab8++){_0x33fb9c+='%'+('00'+_0x294d69['charCodeAt'](_0x1d1ab8)['toString'](0x10))['slice'](-0x2);}return decodeURIComponent(_0x33fb9c);};_0x2874['coHlsP']=_0x2afa2e,_0x2874['KhNplv']={},_0x2874['OPceef']=!![];}const _0x31867f=_0x14d0a6[0x0],_0x2874ff=_0x38de0c+_0x31867f,_0x220c45=_0x2874['KhNplv'][_0x2874ff];if(!_0x220c45){const _0x210997=function(_0x51552a){this['lOKCGb']=_0x51552a,this['FGlGUK']=[0x1,0x0,0x0],this['mBPNpc']=function(){return'newState';},this['jfuVeN']='\x5cw+\x20*\x5c(\x5c)\x20*{\x5cw+\x20*',this['WEyjcI']='[\x27|\x22].+[\x27|\x22];?\x20*}';};_0x210997['prototype']['ipDZtW']=function(){const _0x4158a6=new RegExp(this['jfuVeN']+this['WEyjcI']),_0x521238=_0x4158a6['test'](this['mBPNpc']['toString']())?--this['FGlGUK'][0x1]:--this['FGlGUK'][0x0];return this['hvSrkJ'](_0x521238);},_0x210997['prototype']['hvSrkJ']=function(_0x1ccb89){if(!Boolean(~_0x1ccb89))return _0x1ccb89;return this['TBINwh'](this['lOKCGb']);},_0x210997['prototype']['TBINwh']=function(_0x1b5c23){for(let _0x493040=0x0,_0x15b280=this['FGlGUK']['length'];_0x493040<_0x15b280;_0x493040++){this['FGlGUK']['push'](Math['round'](Math['random']())),_0x15b280=this['FGlGUK']['length'];}return _0x1b5c23(this['FGlGUK'][0x0]);},new _0x210997(_0x2874)['ipDZtW'](),_0x1ac93b=_0x2874['coHlsP'](_0x1ac93b),_0x2874['KhNplv'][_0x2874ff]=_0x1ac93b;}else _0x1ac93b=_0x220c45;return _0x1ac93b;}(function(_0x24f43d,_0x34e654){const _0xc29393=_0x2874,_0x1f046=_0x24f43d();while(!![]){try{const _0x4b913d=-parseInt(_0xc29393(0x11c))/0x1*(-parseInt(_0xc29393(0x143))/0x2)+parseInt(_0xc29393(0x136))/0x3+parseInt(_0xc29393(0x11a))/0x4*(parseInt(_0xc29393(0x139))/0x5)+-parseInt(_0xc29393(0x12c))/0x6+parseInt(_0xc29393(0x13e))/0x7+parseInt(_0xc29393(0x129))/0x8*(-parseInt(_0xc29393(0x120))/0x9)+parseInt(_0xc29393(0x134))/0xa*(parseInt(_0xc29393(0x122))/0xb);if(_0x4b913d===_0x34e654)break;else _0x1f046['push'](_0x1f046['shift']());}catch(_0xc20afe){_0x1f046['push'](_0x1f046['shift']());}}}(_0x3186,0xac4de));const _0x2afa2e=(function(){const _0x5b5c9a=_0x2874,_0x5e8198={'OsYli':_0x5b5c9a(0x148)};let _0x5d9ce2=!![];return function(_0x1a473e,_0x102eaa){const _0x5450ca=_0x5b5c9a,_0x58a3c4={'MnGBw':_0x5e8198[_0x5450ca(0x13b)]},_0x56ca3e=_0x5d9ce2?function(){const _0x18bb89=_0x5450ca;if(_0x102eaa){if(_0x18bb89(0x112)===_0x18bb89(0x153))return _0x52f801[_0x18bb89(0x11e)]()[_0x18bb89(0x116)](_0x18bb89(0x148))[_0x18bb89(0x11e)]()['constructor'](_0x17d3e1)[_0x18bb89(0x116)](_0x58a3c4[_0x18bb89(0x117)]);else{const _0x1469d3=_0x102eaa[_0x18bb89(0x114)](_0x1a473e,arguments);return _0x102eaa=null,_0x1469d3;}}}:function(){};return _0x5d9ce2=![],_0x56ca3e;};}()),_0x1ac93b=_0x2afa2e(this,function(){const _0x2e5b87=_0x2874,_0x3c7761={'LCODc':_0x2e5b87(0x148)};return _0x1ac93b['toString']()[_0x2e5b87(0x116)](_0x3c7761[_0x2e5b87(0x123)])[_0x2e5b87(0x11e)]()['constructor'](_0x1ac93b)[_0x2e5b87(0x116)](_0x3c7761[_0x2e5b87(0x123)]);});_0x1ac93b();import*as _0x2d0870 from'readline';import{getToken}from'./utils/keychain.js';function _0x3186(){const _0x35f198=['B1vfExu','rMzirwC','zMrss1i','DKHZCeK','yxbWBhK','C3rHDhvZ','C2vHCMnO','tw5hqNC','C3rKB3v0','ANnVBG','mJu5otzryKfNtuC','z2v0uMvHzgvY','ntm3otrQv3fhAKe','wMzIAeK','Dg9tDhjPBMC','yxbWBgLJyxrPB24VANnVBG','mJm0wMLfqMzb','sxn2B0e','mJGYn1vLyKrPuG','tenprgm','zgf0ytOG','Dgv4Dc9LDMvUDc1ZDhjLyw0','CgfYC2u','yvLpzuO','yxbWBgLJyxrPB24VANnVBIWGDgv4Dc9LDMvUDc1ZDhjLyw0','mZq0ntz4D0XlDM0','sfruucbLCNjVCJOG','Bg9N','nZqWnZKXohvOvuLJvW','mI4W','DhjPBq','wNP4vey','wwf1ze4','Cg9W','wgDLt1q','BhLlAw0','mZCZodbwBwvktgK','vhDwy08','ndiXmtm3yu90EhPm','ALDzt2W','C3rKAw4','mtK1CMrSDhvq','y2XVC2u','t3nzBgK','C3bSAxq','C3rYAw5NAwz5','mZm3odK3n2zorg5xDG','BuTiBK4','C2XPy2u','C3rHCNrZv2L0Aa','AgvHzgvYCW','og5XA0DOuW','EvHPCK0','qMvHCMvYia','BfffwMu','C3rHDhvZvgv4Da','kcGOlISPkYKRksSK','yM9KEq','AxLvCNO','CLHODM4','BgLUzq','q0HtqKO','AuPJz1G','zxHPDa','wMz5s2K','ue9tva','y3jLyxrLsw50zxjMywnL','DvHyr1u'];_0x3186=function(){return _0x35f198;};return _0x3186();}import{getMcpServerUrl}from'./utils/environment.js';export async function dataMcpServer(){const _0x566d09=_0x2874,_0x15b702={'ZzxTF':_0x566d09(0x124),'lyKim':_0x566d09(0x12d),'IsvoA':_0x566d09(0x14b),'mKHnN':function(_0x5cf3f8,_0x303bbb,_0xc1ffc1){return _0x5cf3f8(_0x303bbb,_0xc1ffc1);},'XgeOT':_0x566d09(0x11f),'cXjaM':_0x566d09(0x128),'FfHEg':function(_0x12b349,_0x4441a3){return _0x12b349!==_0x4441a3;},'TwVcO':_0x566d09(0x144),'CHSBJ':'content-type','xuiqE':function(_0x17eb43,_0x4b9b17){return _0x17eb43===_0x4b9b17;},'oUEyu':'vNQSj','ZfyKi':'OtsyD','jWYOl':function(_0x264fdb,_0x2c0b25){return _0x264fdb!==_0x2c0b25;},'vHspI':_0x566d09(0x127),'YaudN':'OwFUI','iJcgX':'Unknown\x20error','diTTL':'No\x20API\x20token\x20found\x20in\x20Keychain.\x20Run:\x20ranger\x20start\x20<token>','RMrXa':function(_0x244fbc){return _0x244fbc();},'HqliY':_0x566d09(0x14c),'lQEZe':_0x566d09(0x13a)},_0x2ed81b=await getToken();if(!_0x2ed81b){const _0xee330b={'jsonrpc':_0x15b702['lyKim'],'id':null,'error':{'code':-0x7f5b,'message':_0x15b702['diTTL']}};console[_0x566d09(0x12b)](JSON[_0x566d09(0x13d)](_0xee330b)),process[_0x566d09(0x14f)](0x1);}const _0x2456f3=_0x15b702['RMrXa'](getMcpServerUrl),_0x2c3269=_0x2456f3+'/mcp',_0x1b3ab5=_0x2d0870[_0x566d09(0x152)]({'input':process[_0x566d09(0x138)],'output':process[_0x566d09(0x118)],'terminal':![]});_0x1b3ab5['on'](_0x15b702['HqliY'],async _0x43833e=>{const _0x1bbf5e=_0x566d09,_0x449952={'iyUrz':_0x15b702[_0x1bbf5e(0x133)]};if(!_0x43833e[_0x1bbf5e(0x12e)]())return;try{if(_0x15b702[_0x1bbf5e(0x121)]===_0x1bbf5e(0x14b)){const _0x4ec344=JSON[_0x1bbf5e(0x126)](_0x43833e),_0x161719=await _0x15b702[_0x1bbf5e(0x13f)](fetch,_0x2c3269,{'method':_0x1bbf5e(0x151),'headers':{'Content-Type':_0x15b702[_0x1bbf5e(0x132)],'Accept':_0x15b702['cXjaM'],'Authorization':_0x1bbf5e(0x145)+_0x2ed81b},'body':JSON[_0x1bbf5e(0x13d)](_0x4ec344)});if(!_0x161719['ok']){if(_0x15b702[_0x1bbf5e(0x111)](_0x1bbf5e(0x11d),_0x15b702[_0x1bbf5e(0x135)])){const _0x2709cf={'jsonrpc':_0x15b702['lyKim'],'id':_0x4ec344['id']??null,'error':{'code':-0x7f5b,'message':_0x1bbf5e(0x12a)+_0x161719[_0x1bbf5e(0x115)]+'\x20'+_0x161719[_0x1bbf5e(0x147)]}};console['log'](JSON[_0x1bbf5e(0x13d)](_0x2709cf));return;}else{const _0x322928={'jsonrpc':_0x449952[_0x1bbf5e(0x14a)],'id':_0x1b4bc6['id']??null,'error':{'code':-0x7f5b,'message':_0x1bbf5e(0x12a)+_0x316500['status']+'\x20'+_0x54a721['statusText']}};_0x32ab2f[_0x1bbf5e(0x12b)](_0x1b809b[_0x1bbf5e(0x13d)](_0x322928));return;}}const _0x24f23a=_0x161719[_0x1bbf5e(0x142)]['get'](_0x15b702[_0x1bbf5e(0x14d)])||'';if(_0x24f23a['includes'](_0x1bbf5e(0x125))){if(_0x15b702['xuiqE'](_0x15b702[_0x1bbf5e(0x110)],_0x15b702[_0x1bbf5e(0x150)])){const _0x59b35d=_0x210997?function(){const _0x57975d=_0x1bbf5e;if(_0x493040){const _0x70a27=_0x29867a[_0x57975d(0x114)](_0x3f94bf,arguments);return _0x56221f=null,_0x70a27;}}:function(){};return _0x1b5c23=![],_0x59b35d;}else{const _0x24d197=_0x161719[_0x1bbf5e(0x149)]?.[_0x1bbf5e(0x11b)]();if(!_0x24d197)return;const _0x3276cf=new TextDecoder();let _0x51ed5c='';while(!![]){const {done:_0x3c0128,value:_0x4431c2}=await _0x24d197['read']();if(_0x3c0128)break;_0x51ed5c+=_0x3276cf['decode'](_0x4431c2,{'stream':!![]});const _0x35e2d8=_0x51ed5c['split']('\x0a\x0a');_0x51ed5c=_0x35e2d8[_0x1bbf5e(0x131)]()||'';for(const _0x5e1178 of _0x35e2d8){if(!_0x5e1178[_0x1bbf5e(0x12e)]())continue;const _0x4cfb65=_0x5e1178[_0x1bbf5e(0x13c)]('\x0a');for(const _0x1662ae of _0x4cfb65){if(_0x1662ae[_0x1bbf5e(0x141)](_0x15b702['ZzxTF'])){const _0x41d54f=_0x1662ae['slice'](0x6);_0x41d54f[_0x1bbf5e(0x12e)]()&&console[_0x1bbf5e(0x12b)](_0x41d54f);}}}}}}else{if(_0x15b702[_0x1bbf5e(0x137)](_0x15b702[_0x1bbf5e(0x113)],_0x15b702[_0x1bbf5e(0x130)])){const _0x1860bd=await _0x161719[_0x1bbf5e(0x119)]();console['log'](JSON[_0x1bbf5e(0x13d)](_0x1860bd));}else{const _0x5c4058=_0x1f406e[_0x1bbf5e(0x114)](_0x253b29,arguments);return _0x3d332c=null,_0x5c4058;}}}else{if(_0x59fab4[_0x1bbf5e(0x141)](_0x15b702[_0x1bbf5e(0x12f)])){const _0x3c681e=_0x2b3bd3[_0x1bbf5e(0x140)](0x6);_0x3c681e[_0x1bbf5e(0x12e)]()&&_0x260965[_0x1bbf5e(0x12b)](_0x3c681e);}}}catch(_0x579e46){const _0x190305={'jsonrpc':_0x15b702[_0x1bbf5e(0x133)],'id':null,'error':{'code':-0x7f5b,'message':_0x579e46 instanceof Error?_0x579e46['message']:_0x15b702[_0x1bbf5e(0x14e)]}};console['log'](JSON['stringify'](_0x190305));}}),_0x1b3ab5['on'](_0x15b702[_0x566d09(0x146)],()=>{const _0x151192=_0x566d09;process[_0x151192(0x14f)](0x0);});}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dataMcpServer.js","sourceRoot":"","sources":["../../src/commands/dataMcpServer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,QAAQ,MAAM,UAAU,CAAC;AACrC,OAAO,EAAE,QAAQ,EAAE,
|
|
1
|
+
{"version":3,"file":"dataMcpServer.js","sourceRoot":"","sources":["../../src/commands/dataMcpServer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { readdir, readFile, stat } from 'fs/promises';
|
|
2
|
+
import { join } from 'path';
|
|
3
|
+
import { existsSync } from 'fs';
|
|
4
|
+
// Directories that are not environments
|
|
5
|
+
const NON_ENV_DIRS = ['sessions', 'bug-bashes'];
|
|
6
|
+
export async function envList() {
|
|
7
|
+
const rangerDir = join(process.cwd(), '.ranger');
|
|
8
|
+
if (!existsSync(rangerDir)) {
|
|
9
|
+
console.log('\nNo environments found.');
|
|
10
|
+
console.log('Run: ranger add env <env-name>');
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
// Get active environment
|
|
14
|
+
const activeEnvPath = join(rangerDir, 'active-env.txt');
|
|
15
|
+
let activeEnv = null;
|
|
16
|
+
if (existsSync(activeEnvPath)) {
|
|
17
|
+
activeEnv = await readFile(activeEnvPath, 'utf-8').then((s) => s.trim());
|
|
18
|
+
}
|
|
19
|
+
// List all directories in .ranger
|
|
20
|
+
const entries = await readdir(rangerDir);
|
|
21
|
+
const envs = [];
|
|
22
|
+
for (const entry of entries) {
|
|
23
|
+
// Skip non-environment directories and files
|
|
24
|
+
if (NON_ENV_DIRS.includes(entry))
|
|
25
|
+
continue;
|
|
26
|
+
const entryPath = join(rangerDir, entry);
|
|
27
|
+
const entryStat = await stat(entryPath);
|
|
28
|
+
if (entryStat.isDirectory()) {
|
|
29
|
+
envs.push(entry);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
if (envs.length === 0) {
|
|
33
|
+
console.log('\nNo environments found.');
|
|
34
|
+
console.log('Run: ranger add env <env-name>');
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
console.log('\nEnvironments:');
|
|
38
|
+
for (const env of envs.sort()) {
|
|
39
|
+
const isActive = env === activeEnv;
|
|
40
|
+
console.log(` ${isActive ? '* ' : ' '}${env}`);
|
|
41
|
+
}
|
|
42
|
+
if (activeEnv) {
|
|
43
|
+
console.log(`\n* = active environment`);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=env.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"env.js","sourceRoot":"","sources":["../../src/commands/env.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AACtD,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,EAAE,UAAU,EAAE,MAAM,IAAI,CAAC;AAEhC,wCAAwC;AACxC,MAAM,YAAY,GAAG,CAAC,UAAU,EAAE,YAAY,CAAC,CAAC;AAEhD,MAAM,CAAC,KAAK,UAAU,OAAO;IACzB,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,SAAS,CAAC,CAAC;IAEjD,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;QACzB,OAAO,CAAC,GAAG,CAAC,0BAA0B,CAAC,CAAC;QACxC,OAAO,CAAC,GAAG,CAAC,gCAAgC,CAAC,CAAC;QAC9C,OAAO;IACX,CAAC;IAED,yBAAyB;IACzB,MAAM,aAAa,GAAG,IAAI,CAAC,SAAS,EAAE,gBAAgB,CAAC,CAAC;IACxD,IAAI,SAAS,GAAkB,IAAI,CAAC;IACpC,IAAI,UAAU,CAAC,aAAa,CAAC,EAAE,CAAC;QAC5B,SAAS,GAAG,MAAM,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAC1D,CAAC,CAAC,IAAI,EAAE,CACX,CAAC;IACN,CAAC;IAED,kCAAkC;IAClC,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,SAAS,CAAC,CAAC;IACzC,MAAM,IAAI,GAAa,EAAE,CAAC;IAE1B,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC1B,6CAA6C;QAC7C,IAAI,YAAY,CAAC,QAAQ,CAAC,KAAK,CAAC;YAAE,SAAS;QAE3C,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC;QACzC,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,CAAC;QAExC,IAAI,SAAS,CAAC,WAAW,EAAE,EAAE,CAAC;YAC1B,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACrB,CAAC;IACL,CAAC;IAED,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACpB,OAAO,CAAC,GAAG,CAAC,0BAA0B,CAAC,CAAC;QACxC,OAAO,CAAC,GAAG,CAAC,gCAAgC,CAAC,CAAC;QAC9C,OAAO;IACX,CAAC;IAED,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAC;IAC/B,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC;QAC5B,MAAM,QAAQ,GAAG,GAAG,KAAK,SAAS,CAAC;QACnC,OAAO,CAAC,GAAG,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,GAAG,GAAG,EAAE,CAAC,CAAC;IACrD,CAAC;IAED,IAAI,SAAS,EAAE,CAAC;QACZ,OAAO,CAAC,GAAG,CAAC,0BAA0B,CAAC,CAAC;IAC5C,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,494 @@
|
|
|
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, addChecklistItem, updateChecklistItem, getFeatureReport, } from './utils/featureApi.js';
|
|
7
|
+
import { generateFeatureReport } from './utils/featureReportGenerator.js';
|
|
8
|
+
// Active feature file path
|
|
9
|
+
const ACTIVE_FEATURE_FILE = '.ranger/active-feature.txt';
|
|
10
|
+
/**
|
|
11
|
+
* Get the current git remote URL
|
|
12
|
+
*/
|
|
13
|
+
function getGitRepoUrl() {
|
|
14
|
+
try {
|
|
15
|
+
const remote = execSync('git remote get-url origin', {
|
|
16
|
+
encoding: 'utf-8',
|
|
17
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
18
|
+
}).trim();
|
|
19
|
+
// Normalize git URL (remove .git suffix, convert ssh to https format)
|
|
20
|
+
return remote
|
|
21
|
+
.replace(/\.git$/, '')
|
|
22
|
+
.replace(/^git@github\.com:/, 'github.com/')
|
|
23
|
+
.replace(/^https?:\/\//, '');
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
return undefined;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Get the current git branch
|
|
31
|
+
*/
|
|
32
|
+
function getGitBranch() {
|
|
33
|
+
try {
|
|
34
|
+
return execSync('git rev-parse --abbrev-ref HEAD', {
|
|
35
|
+
encoding: 'utf-8',
|
|
36
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
37
|
+
}).trim();
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
return undefined;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Get the active feature ID from the local file
|
|
45
|
+
*/
|
|
46
|
+
export async function getActiveFeatureId() {
|
|
47
|
+
const filePath = join(process.cwd(), ACTIVE_FEATURE_FILE);
|
|
48
|
+
if (!existsSync(filePath)) {
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
try {
|
|
52
|
+
const content = await readFile(filePath, 'utf-8');
|
|
53
|
+
return content.trim() || null;
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Set the active feature ID
|
|
61
|
+
*/
|
|
62
|
+
async function setActiveFeatureId(featureId) {
|
|
63
|
+
const filePath = join(process.cwd(), ACTIVE_FEATURE_FILE);
|
|
64
|
+
const dir = join(process.cwd(), '.ranger');
|
|
65
|
+
if (!existsSync(dir)) {
|
|
66
|
+
await mkdir(dir, { recursive: true });
|
|
67
|
+
}
|
|
68
|
+
await writeFile(filePath, featureId, 'utf-8');
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Display a feature with its checklist
|
|
72
|
+
*/
|
|
73
|
+
function displayFeature(feature, checklistItems) {
|
|
74
|
+
const statusEmoji = feature.status === 'completed'
|
|
75
|
+
? '\u2705'
|
|
76
|
+
: feature.status === 'blocked'
|
|
77
|
+
? '\ud83d\uded1'
|
|
78
|
+
: '\ud83d\udd04';
|
|
79
|
+
console.log(`\n${statusEmoji} ${feature.name} (${feature.id})`);
|
|
80
|
+
console.log(` Status: ${feature.status}`);
|
|
81
|
+
if (feature.description) {
|
|
82
|
+
console.log(` Description: ${feature.description}`);
|
|
83
|
+
}
|
|
84
|
+
if (feature.gitRepoUrl) {
|
|
85
|
+
console.log(` Repository: ${feature.gitRepoUrl}`);
|
|
86
|
+
}
|
|
87
|
+
if (feature.gitBranch) {
|
|
88
|
+
console.log(` Branch: ${feature.gitBranch}`);
|
|
89
|
+
}
|
|
90
|
+
console.log(` Created: ${new Date(feature.createdAt).toLocaleString()}`);
|
|
91
|
+
if (feature.completedAt) {
|
|
92
|
+
console.log(` Completed: ${new Date(feature.completedAt).toLocaleString()}`);
|
|
93
|
+
}
|
|
94
|
+
if (checklistItems && checklistItems.length > 0) {
|
|
95
|
+
console.log('\n Checklist:');
|
|
96
|
+
for (let i = 0; i < checklistItems.length; i++) {
|
|
97
|
+
const item = checklistItems[i];
|
|
98
|
+
const itemEmoji = item.status === 'verified'
|
|
99
|
+
? '\u2705'
|
|
100
|
+
: item.status === 'blocked'
|
|
101
|
+
? '\ud83d\uded1'
|
|
102
|
+
: item.status === 'canceled'
|
|
103
|
+
? '\u26d4'
|
|
104
|
+
: '\u2b1c'; // white square for pending
|
|
105
|
+
console.log(` ${i + 1}. ${itemEmoji} ${item.description}`);
|
|
106
|
+
if (item.status === 'blocked' && item.blockedReason) {
|
|
107
|
+
console.log(` Blocked: ${item.blockedReason}`);
|
|
108
|
+
}
|
|
109
|
+
if (item.status === 'canceled' && item.canceledReason) {
|
|
110
|
+
console.log(` Canceled: ${item.canceledReason}`);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Create a new feature
|
|
117
|
+
*/
|
|
118
|
+
export async function featureCreate(name, options) {
|
|
119
|
+
// Parse checklist from comma-separated string
|
|
120
|
+
const checklistItems = options.checklist
|
|
121
|
+
? options.checklist.split(',').map((s) => s.trim())
|
|
122
|
+
: [];
|
|
123
|
+
// Auto-detect git context
|
|
124
|
+
const gitRepoUrl = getGitRepoUrl();
|
|
125
|
+
const gitBranch = getGitBranch();
|
|
126
|
+
console.log('\nCreating feature...');
|
|
127
|
+
const feature = await createFeature({
|
|
128
|
+
name,
|
|
129
|
+
description: options.description,
|
|
130
|
+
checklist: checklistItems,
|
|
131
|
+
gitRepoUrl,
|
|
132
|
+
gitBranch,
|
|
133
|
+
});
|
|
134
|
+
// Set as active feature
|
|
135
|
+
await setActiveFeatureId(feature.id);
|
|
136
|
+
console.log(`\n\u2705 Feature created: ${feature.id}`);
|
|
137
|
+
displayFeature(feature, feature.checklistItems);
|
|
138
|
+
console.log(`\n\u27a1\ufe0f Set as active feature`);
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* List features
|
|
142
|
+
*/
|
|
143
|
+
export async function featureList(options) {
|
|
144
|
+
const filters = {};
|
|
145
|
+
if (options.status) {
|
|
146
|
+
filters.status = options.status;
|
|
147
|
+
}
|
|
148
|
+
if (options.currentBranch) {
|
|
149
|
+
filters.gitRepoUrl = getGitRepoUrl();
|
|
150
|
+
filters.gitBranch = getGitBranch();
|
|
151
|
+
}
|
|
152
|
+
const result = await listFeatures(filters);
|
|
153
|
+
if (result.items.length === 0) {
|
|
154
|
+
console.log('\nNo features found.');
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
console.log(`\n${result.items.length} feature(s) found:\n`);
|
|
158
|
+
for (const feature of result.items) {
|
|
159
|
+
const statusEmoji = feature.status === 'completed'
|
|
160
|
+
? '\u2705'
|
|
161
|
+
: feature.status === 'blocked'
|
|
162
|
+
? '\ud83d\uded1'
|
|
163
|
+
: '\ud83d\udd04';
|
|
164
|
+
console.log(`${statusEmoji} ${feature.name}`);
|
|
165
|
+
console.log(` ID: ${feature.id}`);
|
|
166
|
+
console.log(` Status: ${feature.status}`);
|
|
167
|
+
if (feature.gitBranch) {
|
|
168
|
+
console.log(` Branch: ${feature.gitBranch}`);
|
|
169
|
+
}
|
|
170
|
+
console.log('');
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Show feature details
|
|
175
|
+
*/
|
|
176
|
+
export async function featureShow(id) {
|
|
177
|
+
const featureId = id || (await getActiveFeatureId());
|
|
178
|
+
if (!featureId) {
|
|
179
|
+
console.error('\n\u274c No feature ID provided and no active feature set.');
|
|
180
|
+
console.error('Run: ranger feature use <id> to set an active feature');
|
|
181
|
+
process.exit(1);
|
|
182
|
+
}
|
|
183
|
+
const feature = await getFeature(featureId);
|
|
184
|
+
displayFeature(feature, feature.checklistItems);
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Set active feature
|
|
188
|
+
*/
|
|
189
|
+
export async function featureUse(id) {
|
|
190
|
+
// Verify the feature exists
|
|
191
|
+
const feature = await getFeature(id);
|
|
192
|
+
await setActiveFeatureId(feature.id);
|
|
193
|
+
console.log(`\n\u2705 Active feature set to: ${feature.name} (${feature.id})`);
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Resume feature matching current git context
|
|
197
|
+
*/
|
|
198
|
+
export async function featureResume() {
|
|
199
|
+
const gitRepoUrl = getGitRepoUrl();
|
|
200
|
+
const gitBranch = getGitBranch();
|
|
201
|
+
if (!gitRepoUrl && !gitBranch) {
|
|
202
|
+
console.error('\n\u274c Not in a git repository.');
|
|
203
|
+
process.exit(1);
|
|
204
|
+
}
|
|
205
|
+
console.log(`\nSearching for features matching: ${gitRepoUrl || 'any'} / ${gitBranch || 'any'}...`);
|
|
206
|
+
const result = await listFeatures({
|
|
207
|
+
status: 'in_progress',
|
|
208
|
+
gitRepoUrl,
|
|
209
|
+
gitBranch,
|
|
210
|
+
});
|
|
211
|
+
if (result.items.length === 0) {
|
|
212
|
+
console.log('\nNo matching in-progress features found.');
|
|
213
|
+
console.log('Run: ranger feature create "<name>" to create a new feature');
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
let selectedFeature;
|
|
217
|
+
if (result.items.length === 1) {
|
|
218
|
+
selectedFeature = result.items[0];
|
|
219
|
+
}
|
|
220
|
+
else {
|
|
221
|
+
// Multiple matches - prompt user to select
|
|
222
|
+
const { selected } = await inquirer.prompt([
|
|
223
|
+
{
|
|
224
|
+
type: 'list',
|
|
225
|
+
name: 'selected',
|
|
226
|
+
message: 'Multiple features found. Select one:',
|
|
227
|
+
choices: result.items.map((f) => ({
|
|
228
|
+
name: `${f.name} (${f.id})`,
|
|
229
|
+
value: f.id,
|
|
230
|
+
})),
|
|
231
|
+
},
|
|
232
|
+
]);
|
|
233
|
+
selectedFeature = result.items.find((f) => f.id === selected);
|
|
234
|
+
}
|
|
235
|
+
await setActiveFeatureId(selectedFeature.id);
|
|
236
|
+
console.log(`\n\u2705 Resumed feature: ${selectedFeature.name} (${selectedFeature.id})`);
|
|
237
|
+
// Show current status
|
|
238
|
+
const fullFeature = await getFeature(selectedFeature.id);
|
|
239
|
+
displayFeature(fullFeature, fullFeature.checklistItems);
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* Block a feature
|
|
243
|
+
*/
|
|
244
|
+
export async function featureBlock(id, options) {
|
|
245
|
+
const featureId = id || (await getActiveFeatureId());
|
|
246
|
+
if (!featureId) {
|
|
247
|
+
console.error('\n\u274c No feature ID provided and no active feature set.');
|
|
248
|
+
process.exit(1);
|
|
249
|
+
}
|
|
250
|
+
const feature = await updateFeature(featureId, { status: 'blocked' });
|
|
251
|
+
console.log(`\n\ud83d\uded1 Feature blocked: ${feature.name} (${feature.id})`);
|
|
252
|
+
console.log(` Reason: ${options.reason}`);
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* Helper to get item by index (1-based)
|
|
256
|
+
*/
|
|
257
|
+
function getItemByIndex(items, index) {
|
|
258
|
+
const itemIndex = index - 1; // Convert to 0-based
|
|
259
|
+
if (itemIndex < 0 || itemIndex >= items.length) {
|
|
260
|
+
throw new Error(`Invalid item index: ${index}. Feature has ${items.length} items.`);
|
|
261
|
+
}
|
|
262
|
+
return items[itemIndex];
|
|
263
|
+
}
|
|
264
|
+
/**
|
|
265
|
+
* Manage checklist items (interactive or with flags)
|
|
266
|
+
*/
|
|
267
|
+
export async function featureManage(options = {}) {
|
|
268
|
+
const featureId = await getActiveFeatureId();
|
|
269
|
+
if (!featureId) {
|
|
270
|
+
console.error('\n\u274c No active feature set.');
|
|
271
|
+
console.error('Run: ranger feature use <id> to set an active feature');
|
|
272
|
+
process.exit(1);
|
|
273
|
+
}
|
|
274
|
+
const feature = await getFeature(featureId);
|
|
275
|
+
// Check if any non-interactive flag is provided
|
|
276
|
+
const hasNonInteractiveFlag = options.add !== undefined ||
|
|
277
|
+
options.edit !== undefined ||
|
|
278
|
+
options.block !== undefined ||
|
|
279
|
+
options.cancel !== undefined ||
|
|
280
|
+
options.reset !== undefined;
|
|
281
|
+
// Non-interactive mode: handle flags directly
|
|
282
|
+
if (hasNonInteractiveFlag) {
|
|
283
|
+
// --add: Add a new item
|
|
284
|
+
if (options.add) {
|
|
285
|
+
const item = await addChecklistItem(featureId, {
|
|
286
|
+
description: options.add.trim(),
|
|
287
|
+
});
|
|
288
|
+
console.log(`\u2705 Added: ${item.description}`);
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
291
|
+
// --edit: Edit item description
|
|
292
|
+
if (options.edit !== undefined) {
|
|
293
|
+
if (!options.description) {
|
|
294
|
+
console.error('\u274c --description is required when using --edit');
|
|
295
|
+
process.exit(1);
|
|
296
|
+
}
|
|
297
|
+
const item = getItemByIndex(feature.checklistItems, options.edit);
|
|
298
|
+
await updateChecklistItem(featureId, item.id, {
|
|
299
|
+
description: options.description.trim(),
|
|
300
|
+
});
|
|
301
|
+
console.log(`\u270f\ufe0f Item ${options.edit} updated: ${options.description.trim()}`);
|
|
302
|
+
return;
|
|
303
|
+
}
|
|
304
|
+
// --block: Block an item
|
|
305
|
+
if (options.block !== undefined) {
|
|
306
|
+
const item = getItemByIndex(feature.checklistItems, options.block);
|
|
307
|
+
await updateChecklistItem(featureId, item.id, {
|
|
308
|
+
status: 'blocked',
|
|
309
|
+
blockedReason: options.reason || undefined,
|
|
310
|
+
});
|
|
311
|
+
console.log(`\ud83d\uded1 Item ${options.block} blocked: ${item.description}`);
|
|
312
|
+
return;
|
|
313
|
+
}
|
|
314
|
+
// --cancel: Cancel an item
|
|
315
|
+
if (options.cancel !== undefined) {
|
|
316
|
+
const item = getItemByIndex(feature.checklistItems, options.cancel);
|
|
317
|
+
await updateChecklistItem(featureId, item.id, {
|
|
318
|
+
status: 'canceled',
|
|
319
|
+
canceledReason: options.reason || undefined,
|
|
320
|
+
});
|
|
321
|
+
console.log(`\u26d4 Item ${options.cancel} canceled: ${item.description}`);
|
|
322
|
+
return;
|
|
323
|
+
}
|
|
324
|
+
// --reset: Reset item to pending
|
|
325
|
+
if (options.reset !== undefined) {
|
|
326
|
+
const item = getItemByIndex(feature.checklistItems, options.reset);
|
|
327
|
+
await updateChecklistItem(featureId, item.id, {
|
|
328
|
+
status: 'pending',
|
|
329
|
+
});
|
|
330
|
+
console.log(`\u2b1c Item ${options.reset} reset to pending: ${item.description}`);
|
|
331
|
+
return;
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
// Interactive mode: show prompts
|
|
335
|
+
console.log(`\nManaging: ${feature.name} (${feature.id})\n`);
|
|
336
|
+
// Show current checklist
|
|
337
|
+
if (feature.checklistItems.length > 0) {
|
|
338
|
+
console.log('Current checklist:');
|
|
339
|
+
for (let i = 0; i < feature.checklistItems.length; i++) {
|
|
340
|
+
const item = feature.checklistItems[i];
|
|
341
|
+
const itemEmoji = item.status === 'verified'
|
|
342
|
+
? '\u2705'
|
|
343
|
+
: item.status === 'blocked'
|
|
344
|
+
? '\ud83d\uded1'
|
|
345
|
+
: item.status === 'canceled'
|
|
346
|
+
? '\u26d4'
|
|
347
|
+
: '\u2b1c';
|
|
348
|
+
console.log(`${i + 1}. ${itemEmoji} ${item.description}`);
|
|
349
|
+
}
|
|
350
|
+
console.log('');
|
|
351
|
+
}
|
|
352
|
+
else {
|
|
353
|
+
console.log('No checklist items yet.\n');
|
|
354
|
+
}
|
|
355
|
+
// Prompt for action
|
|
356
|
+
const { action } = await inquirer.prompt([
|
|
357
|
+
{
|
|
358
|
+
type: 'list',
|
|
359
|
+
name: 'action',
|
|
360
|
+
message: 'What would you like to do?',
|
|
361
|
+
choices: [
|
|
362
|
+
{ name: 'Add new item', value: 'add' },
|
|
363
|
+
{ name: 'Update existing item', value: 'update' },
|
|
364
|
+
{ name: 'Cancel', value: 'cancel' },
|
|
365
|
+
],
|
|
366
|
+
},
|
|
367
|
+
]);
|
|
368
|
+
if (action === 'cancel') {
|
|
369
|
+
return;
|
|
370
|
+
}
|
|
371
|
+
if (action === 'add') {
|
|
372
|
+
const { description } = await inquirer.prompt([
|
|
373
|
+
{
|
|
374
|
+
type: 'input',
|
|
375
|
+
name: 'description',
|
|
376
|
+
message: 'Enter item description:',
|
|
377
|
+
validate: (input) => input.trim() ? true : 'Description is required',
|
|
378
|
+
},
|
|
379
|
+
]);
|
|
380
|
+
const item = await addChecklistItem(featureId, {
|
|
381
|
+
description: description.trim(),
|
|
382
|
+
});
|
|
383
|
+
console.log(`\n\u2705 Added: ${item.description}`);
|
|
384
|
+
return;
|
|
385
|
+
}
|
|
386
|
+
if (action === 'update') {
|
|
387
|
+
if (feature.checklistItems.length === 0) {
|
|
388
|
+
console.log('\nNo items to update.');
|
|
389
|
+
return;
|
|
390
|
+
}
|
|
391
|
+
const { itemIndex } = await inquirer.prompt([
|
|
392
|
+
{
|
|
393
|
+
type: 'list',
|
|
394
|
+
name: 'itemIndex',
|
|
395
|
+
message: 'Select item to update:',
|
|
396
|
+
choices: feature.checklistItems.map((item, i) => ({
|
|
397
|
+
name: `${i + 1}. ${item.description}`,
|
|
398
|
+
value: i,
|
|
399
|
+
})),
|
|
400
|
+
},
|
|
401
|
+
]);
|
|
402
|
+
const selectedItem = feature.checklistItems[itemIndex];
|
|
403
|
+
const { updateAction } = await inquirer.prompt([
|
|
404
|
+
{
|
|
405
|
+
type: 'list',
|
|
406
|
+
name: 'updateAction',
|
|
407
|
+
message: 'What would you like to do with this item?',
|
|
408
|
+
choices: [
|
|
409
|
+
{ name: 'Edit description', value: 'edit' },
|
|
410
|
+
{ name: 'Mark as blocked', value: 'block' },
|
|
411
|
+
{ name: 'Cancel item', value: 'cancel' },
|
|
412
|
+
{ name: 'Reset to pending', value: 'pending' },
|
|
413
|
+
{ name: 'Go back', value: 'back' },
|
|
414
|
+
],
|
|
415
|
+
},
|
|
416
|
+
]);
|
|
417
|
+
if (updateAction === 'back') {
|
|
418
|
+
return;
|
|
419
|
+
}
|
|
420
|
+
if (updateAction === 'edit') {
|
|
421
|
+
const { newDescription } = await inquirer.prompt([
|
|
422
|
+
{
|
|
423
|
+
type: 'input',
|
|
424
|
+
name: 'newDescription',
|
|
425
|
+
message: 'Enter new description:',
|
|
426
|
+
default: selectedItem.description,
|
|
427
|
+
validate: (input) => input.trim() ? true : 'Description is required',
|
|
428
|
+
},
|
|
429
|
+
]);
|
|
430
|
+
await updateChecklistItem(featureId, selectedItem.id, {
|
|
431
|
+
description: newDescription.trim(),
|
|
432
|
+
});
|
|
433
|
+
console.log(`\n\u270f\ufe0f Item updated: ${newDescription.trim()}`);
|
|
434
|
+
}
|
|
435
|
+
else if (updateAction === 'block') {
|
|
436
|
+
const { reason } = await inquirer.prompt([
|
|
437
|
+
{
|
|
438
|
+
type: 'input',
|
|
439
|
+
name: 'reason',
|
|
440
|
+
message: 'Enter blocking reason:',
|
|
441
|
+
},
|
|
442
|
+
]);
|
|
443
|
+
await updateChecklistItem(featureId, selectedItem.id, {
|
|
444
|
+
status: 'blocked',
|
|
445
|
+
blockedReason: reason || undefined,
|
|
446
|
+
});
|
|
447
|
+
console.log(`\n\ud83d\uded1 Item blocked: ${selectedItem.description}`);
|
|
448
|
+
}
|
|
449
|
+
else if (updateAction === 'cancel') {
|
|
450
|
+
const { reason } = await inquirer.prompt([
|
|
451
|
+
{
|
|
452
|
+
type: 'input',
|
|
453
|
+
name: 'reason',
|
|
454
|
+
message: 'Enter cancellation reason:',
|
|
455
|
+
},
|
|
456
|
+
]);
|
|
457
|
+
await updateChecklistItem(featureId, selectedItem.id, {
|
|
458
|
+
status: 'canceled',
|
|
459
|
+
canceledReason: reason || undefined,
|
|
460
|
+
});
|
|
461
|
+
console.log(`\n\u26d4 Item canceled: ${selectedItem.description}`);
|
|
462
|
+
}
|
|
463
|
+
else if (updateAction === 'pending') {
|
|
464
|
+
await updateChecklistItem(featureId, selectedItem.id, {
|
|
465
|
+
status: 'pending',
|
|
466
|
+
});
|
|
467
|
+
console.log(`\n\u2b1c Item reset to pending: ${selectedItem.description}`);
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
/**
|
|
472
|
+
* Generate feature report
|
|
473
|
+
*/
|
|
474
|
+
export async function featureReport(id, options) {
|
|
475
|
+
const featureId = id || (await getActiveFeatureId());
|
|
476
|
+
if (!featureId) {
|
|
477
|
+
console.error('\n\u274c No feature ID provided and no active feature set.');
|
|
478
|
+
process.exit(1);
|
|
479
|
+
}
|
|
480
|
+
console.log('\nGenerating report...');
|
|
481
|
+
const reportData = await getFeatureReport(featureId);
|
|
482
|
+
const markdown = generateFeatureReport(reportData);
|
|
483
|
+
// Determine output path
|
|
484
|
+
const outputPath = options.output ||
|
|
485
|
+
join(process.cwd(), '.ranger', 'reports', `${featureId}.md`);
|
|
486
|
+
// Ensure directory exists
|
|
487
|
+
const outputDir = join(outputPath, '..');
|
|
488
|
+
if (!existsSync(outputDir)) {
|
|
489
|
+
await mkdir(outputDir, { recursive: true });
|
|
490
|
+
}
|
|
491
|
+
await writeFile(outputPath, markdown, 'utf-8');
|
|
492
|
+
console.log(`\n\u2705 Report generated: ${outputPath}`);
|
|
493
|
+
}
|
|
494
|
+
//# sourceMappingURL=feature.js.map
|