1+ #!/usr/bin/env node
2+
3+ /**
4+ * GitHub Issue Management for Todo System
5+ * Replaces ACTIVE_WORK.md with GitHub Issues
6+ */
7+
8+ const { execSync } = require ( 'child_process' ) ;
9+
10+ const LABELS = {
11+ priority : [ 'high' , 'medium' , 'low' ] ,
12+ type : [ 'bug' , 'feature' , 'docs' , 'refactor' , 'test' ] ,
13+ status : [ 'blocked' , 'in-progress' ] ,
14+ area : [ 'commands' , 'scripts' , 'docs' , 'ci' , 'setup' ]
15+ } ;
16+
17+ function exec ( command ) {
18+ try {
19+ return execSync ( command , { encoding : 'utf8' , stdio : 'pipe' } ) . trim ( ) ;
20+ } catch ( error ) {
21+ console . error ( `Error: ${ error . message } ` ) ;
22+ process . exit ( 1 ) ;
23+ }
24+ }
25+
26+ function formatIssue ( issue ) {
27+ const parts = issue . split ( '\t' ) ;
28+ const number = parts [ 0 ] ;
29+ const state = parts [ 1 ] ;
30+ const title = parts [ 2 ] ;
31+ const labels = parts [ 3 ] || '' ;
32+ const updated = parts [ 4 ] || '' ;
33+
34+ const stateIcon = state === 'OPEN' ? '🔵' : '✅' ;
35+ const labelStr = labels ? ` [${ labels } ]` : '' ;
36+
37+ return `${ stateIcon } #${ number } : ${ title } ${ labelStr } ` ;
38+ }
39+
40+ function listIssues ( filter = 'open' ) {
41+ console . log ( `\n📋 GitHub Issues (${ filter } ):` ) ;
42+ console . log ( '─' . repeat ( 50 ) ) ;
43+
44+ const stateFlag = filter === 'all' ? '' : `--state ${ filter } ` ;
45+ const command = `gh issue list ${ stateFlag } --limit 30 --json number,state,title,labels,updatedAt --template '{{range .}}{{.number}}\t{{.state}}\t{{.title}}\t{{range .labels}}{{.name}} {{end}}\t{{.updatedAt}}{{println}}{{end}}'` ;
46+
47+ const output = exec ( command ) ;
48+
49+ if ( ! output ) {
50+ console . log ( 'No issues found' ) ;
51+ return ;
52+ }
53+
54+ const issues = output . split ( '\n' ) . filter ( Boolean ) ;
55+ issues . forEach ( issue => {
56+ console . log ( formatIssue ( issue ) ) ;
57+ } ) ;
58+
59+ console . log ( '─' . repeat ( 50 ) ) ;
60+ console . log ( `Total: ${ issues . length } issues` ) ;
61+ }
62+
63+ function createIssue ( title , body = '' , labels = [ ] ) {
64+ console . log ( `\n➕ Creating issue: "${ title } "` ) ;
65+
66+ const labelFlag = labels . length > 0 ? `--label "${ labels . join ( ',' ) } "` : '' ;
67+ const bodyFlag = body ? `--body "${ body } "` : '' ;
68+
69+ const command = `gh issue create --title "${ title } " ${ bodyFlag } ${ labelFlag } ` ;
70+ const result = exec ( command ) ;
71+
72+ console . log ( `✅ Issue created: ${ result } ` ) ;
73+ return result ;
74+ }
75+
76+ function closeIssue ( number , comment = '' ) {
77+ console . log ( `\n✅ Closing issue #${ number } ` ) ;
78+
79+ if ( comment ) {
80+ exec ( `gh issue comment ${ number } --body "${ comment } "` ) ;
81+ }
82+
83+ exec ( `gh issue close ${ number } ` ) ;
84+ console . log ( `Issue #${ number } closed` ) ;
85+ }
86+
87+ function reopenIssue ( number , comment = '' ) {
88+ console . log ( `\n🔄 Reopening issue #${ number } ` ) ;
89+
90+ if ( comment ) {
91+ exec ( `gh issue comment ${ number } --body "${ comment } "` ) ;
92+ }
93+
94+ exec ( `gh issue reopen ${ number } ` ) ;
95+ console . log ( `Issue #${ number } reopened` ) ;
96+ }
97+
98+ function addComment ( number , comment ) {
99+ console . log ( `\n💬 Adding comment to issue #${ number } ` ) ;
100+ exec ( `gh issue comment ${ number } --body "${ comment } "` ) ;
101+ console . log ( `Comment added to issue #${ number } ` ) ;
102+ }
103+
104+ function showIssue ( number ) {
105+ console . log ( `\n📄 Issue #${ number } Details:` ) ;
106+ console . log ( '─' . repeat ( 50 ) ) ;
107+
108+ const issue = exec ( `gh issue view ${ number } ` ) ;
109+ console . log ( issue ) ;
110+ }
111+
112+ function showStats ( ) {
113+ console . log ( '\n📊 Issue Statistics:' ) ;
114+ console . log ( '─' . repeat ( 50 ) ) ;
115+
116+ const openCount = exec ( 'gh issue list --state open --limit 999 --json number --jq "length"' ) ;
117+ const closedCount = exec ( 'gh issue list --state closed --limit 999 --json number --jq "length"' ) ;
118+
119+ console . log ( `🔵 Open: ${ openCount } ` ) ;
120+ console . log ( `✅ Closed: ${ closedCount } ` ) ;
121+ console . log ( `📈 Total: ${ parseInt ( openCount ) + parseInt ( closedCount ) } ` ) ;
122+
123+ // Get label distribution
124+ console . log ( '\n🏷️ Label Distribution:' ) ;
125+ const labels = exec ( 'gh issue list --state open --limit 999 --json labels --jq "[.[].labels[].name] | group_by(.) | map({label: .[0], count: length})"' ) ;
126+
127+ if ( labels && labels !== '[]' ) {
128+ const labelData = JSON . parse ( labels ) ;
129+ labelData . forEach ( item => {
130+ console . log ( ` ${ item . label } : ${ item . count } ` ) ;
131+ } ) ;
132+ }
133+ }
134+
135+ function showHelp ( ) {
136+ console . log ( `
137+ 📋 GitHub Issue Todo Management
138+
139+ Usage: node scripts/todo-github.js <command> [options]
140+
141+ Commands:
142+ list [filter] List issues (open/closed/all, default: open)
143+ add <title> [body] Create new issue
144+ done <number> Close an issue
145+ reopen <number> Reopen an issue
146+ comment <number> Add comment to issue
147+ show <number> Show issue details
148+ stats Show issue statistics
149+ help Show this help
150+
151+ Examples:
152+ node scripts/todo-github.js list
153+ node scripts/todo-github.js add "Fix login bug"
154+ node scripts/todo-github.js done 42
155+ node scripts/todo-github.js comment 42 "Working on this"
156+
157+ Labels:
158+ Priority: ${ LABELS . priority . join ( ', ' ) }
159+ Type: ${ LABELS . type . join ( ', ' ) }
160+ Status: ${ LABELS . status . join ( ', ' ) }
161+ Area: ${ LABELS . area . join ( ', ' ) }
162+ ` ) ;
163+ }
164+
165+ // Main execution
166+ const command = process . argv [ 2 ] || 'list' ;
167+ const args = process . argv . slice ( 3 ) ;
168+
169+ switch ( command ) {
170+ case 'list' :
171+ listIssues ( args [ 0 ] || 'open' ) ;
172+ break ;
173+
174+ case 'add' :
175+ case 'create' :
176+ if ( ! args [ 0 ] ) {
177+ console . error ( 'Error: Title required' ) ;
178+ process . exit ( 1 ) ;
179+ }
180+ createIssue ( args [ 0 ] , args [ 1 ] || '' ) ;
181+ break ;
182+
183+ case 'done' :
184+ case 'close' :
185+ if ( ! args [ 0 ] ) {
186+ console . error ( 'Error: Issue number required' ) ;
187+ process . exit ( 1 ) ;
188+ }
189+ closeIssue ( args [ 0 ] , args [ 1 ] ) ;
190+ break ;
191+
192+ case 'reopen' :
193+ if ( ! args [ 0 ] ) {
194+ console . error ( 'Error: Issue number required' ) ;
195+ process . exit ( 1 ) ;
196+ }
197+ reopenIssue ( args [ 0 ] , args [ 1 ] ) ;
198+ break ;
199+
200+ case 'comment' :
201+ if ( ! args [ 0 ] || ! args [ 1 ] ) {
202+ console . error ( 'Error: Issue number and comment required' ) ;
203+ process . exit ( 1 ) ;
204+ }
205+ addComment ( args [ 0 ] , args . slice ( 1 ) . join ( ' ' ) ) ;
206+ break ;
207+
208+ case 'show' :
209+ case 'view' :
210+ if ( ! args [ 0 ] ) {
211+ console . error ( 'Error: Issue number required' ) ;
212+ process . exit ( 1 ) ;
213+ }
214+ showIssue ( args [ 0 ] ) ;
215+ break ;
216+
217+ case 'stats' :
218+ case 'status' :
219+ showStats ( ) ;
220+ break ;
221+
222+ case 'help' :
223+ case '--help' :
224+ case '-h' :
225+ showHelp ( ) ;
226+ break ;
227+
228+ default :
229+ console . error ( `Unknown command: ${ command } ` ) ;
230+ showHelp ( ) ;
231+ process . exit ( 1 ) ;
232+ }
0 commit comments