@laitszkin/apollo-toolkit 3.13.2 → 3.14.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +7 -7
- package/CHANGELOG.md +36 -0
- package/CLAUDE.md +8 -8
- package/analyse-app-logs/SKILL.md +3 -3
- package/bin/apollo-toolkit.ts +7 -0
- package/codex/codex-memory-manager/SKILL.md +2 -2
- package/codex/learn-skill-from-conversations/SKILL.md +3 -3
- package/dist/bin/apollo-toolkit.d.ts +2 -0
- package/dist/bin/apollo-toolkit.js +7 -0
- package/dist/lib/cli.d.ts +41 -0
- package/dist/lib/cli.js +655 -0
- package/dist/lib/installer.d.ts +59 -0
- package/dist/lib/installer.js +404 -0
- package/dist/lib/tool-runner.d.ts +19 -0
- package/dist/lib/tool-runner.js +536 -0
- package/dist/lib/tools/architecture.d.ts +2 -0
- package/dist/lib/tools/architecture.js +23 -0
- package/dist/lib/tools/create-specs.d.ts +2 -0
- package/dist/lib/tools/create-specs.js +175 -0
- package/dist/lib/tools/docs-to-voice.d.ts +2 -0
- package/dist/lib/tools/docs-to-voice.js +705 -0
- package/dist/lib/tools/enforce-video-aspect-ratio.d.ts +2 -0
- package/dist/lib/tools/enforce-video-aspect-ratio.js +312 -0
- package/dist/lib/tools/extract-conversations.d.ts +2 -0
- package/dist/lib/tools/extract-conversations.js +105 -0
- package/dist/lib/tools/extract-pdf-text.d.ts +2 -0
- package/dist/lib/tools/extract-pdf-text.js +92 -0
- package/dist/lib/tools/filter-logs.d.ts +2 -0
- package/dist/lib/tools/filter-logs.js +94 -0
- package/dist/lib/tools/find-github-issues.d.ts +2 -0
- package/dist/lib/tools/find-github-issues.js +176 -0
- package/dist/lib/tools/generate-storyboard-images.d.ts +2 -0
- package/dist/lib/tools/generate-storyboard-images.js +419 -0
- package/dist/lib/tools/log-cli-utils.d.ts +35 -0
- package/dist/lib/tools/log-cli-utils.js +233 -0
- package/dist/lib/tools/open-github-issue.d.ts +2 -0
- package/dist/lib/tools/open-github-issue.js +750 -0
- package/dist/lib/tools/read-github-issue.d.ts +2 -0
- package/dist/lib/tools/read-github-issue.js +134 -0
- package/dist/lib/tools/render-error-book.d.ts +2 -0
- package/dist/lib/tools/render-error-book.js +265 -0
- package/dist/lib/tools/render-katex.d.ts +2 -0
- package/dist/lib/tools/render-katex.js +294 -0
- package/dist/lib/tools/review-threads.d.ts +2 -0
- package/dist/lib/tools/review-threads.js +491 -0
- package/dist/lib/tools/search-logs.d.ts +2 -0
- package/dist/lib/tools/search-logs.js +164 -0
- package/dist/lib/tools/sync-memory-index.d.ts +2 -0
- package/dist/lib/tools/sync-memory-index.js +113 -0
- package/dist/lib/tools/validate-openai-agent-config.d.ts +2 -0
- package/dist/lib/tools/validate-openai-agent-config.js +190 -0
- package/dist/lib/tools/validate-skill-frontmatter.d.ts +2 -0
- package/dist/lib/tools/validate-skill-frontmatter.js +118 -0
- package/dist/lib/types.d.ts +82 -0
- package/dist/lib/types.js +2 -0
- package/dist/lib/updater.d.ts +34 -0
- package/dist/lib/updater.js +112 -0
- package/dist/lib/utils/format.d.ts +2 -0
- package/dist/lib/utils/format.js +6 -0
- package/dist/lib/utils/terminal.d.ts +12 -0
- package/dist/lib/utils/terminal.js +26 -0
- package/docs-to-voice/SKILL.md +0 -1
- package/generate-spec/SKILL.md +1 -1
- package/katex/SKILL.md +1 -2
- package/lib/cli.ts +780 -0
- package/lib/installer.ts +466 -0
- package/lib/tool-runner.ts +561 -0
- package/lib/tools/architecture.ts +20 -0
- package/lib/tools/create-specs.ts +204 -0
- package/lib/tools/docs-to-voice.ts +799 -0
- package/lib/tools/enforce-video-aspect-ratio.ts +368 -0
- package/lib/tools/extract-conversations.ts +114 -0
- package/lib/tools/extract-pdf-text.ts +99 -0
- package/lib/tools/filter-logs.ts +118 -0
- package/lib/tools/find-github-issues.ts +211 -0
- package/lib/tools/generate-storyboard-images.ts +455 -0
- package/lib/tools/log-cli-utils.ts +262 -0
- package/lib/tools/open-github-issue.ts +930 -0
- package/lib/tools/read-github-issue.ts +179 -0
- package/lib/tools/render-error-book.ts +300 -0
- package/lib/tools/render-katex.ts +325 -0
- package/lib/tools/review-threads.ts +590 -0
- package/lib/tools/search-logs.ts +200 -0
- package/lib/tools/sync-memory-index.ts +114 -0
- package/lib/tools/validate-openai-agent-config.ts +213 -0
- package/lib/tools/validate-skill-frontmatter.ts +124 -0
- package/lib/types.ts +90 -0
- package/lib/updater.ts +165 -0
- package/lib/utils/format.ts +7 -0
- package/lib/utils/terminal.ts +22 -0
- package/open-github-issue/SKILL.md +2 -2
- package/optimise-skill/SKILL.md +1 -1
- package/package.json +13 -4
- package/resources/project-architecture/assets/architecture.css +764 -0
- package/resources/project-architecture/assets/viewer.client.js +144 -0
- package/resources/project-architecture/index.html +42 -0
- package/review-spec-related-changes/SKILL.md +1 -1
- package/solve-issues-found-during-review/SKILL.md +2 -1
- package/tsconfig.json +28 -0
- package/analyse-app-logs/scripts/__pycache__/filter_logs_by_time.cpython-312.pyc +0 -0
- package/analyse-app-logs/scripts/__pycache__/log_cli_utils.cpython-312.pyc +0 -0
- package/analyse-app-logs/scripts/__pycache__/search_logs.cpython-312.pyc +0 -0
- package/analyse-app-logs/scripts/filter_logs_by_time.py +0 -64
- package/analyse-app-logs/scripts/log_cli_utils.py +0 -112
- package/analyse-app-logs/scripts/search_logs.py +0 -137
- package/analyse-app-logs/tests/test_filter_logs_by_time.py +0 -95
- package/analyse-app-logs/tests/test_search_logs.py +0 -100
- package/codex/codex-memory-manager/scripts/extract_recent_conversations.py +0 -369
- package/codex/codex-memory-manager/scripts/sync_memory_index.py +0 -130
- package/codex/codex-memory-manager/tests/test_extract_recent_conversations.py +0 -177
- package/codex/codex-memory-manager/tests/test_memory_template.py +0 -37
- package/codex/codex-memory-manager/tests/test_sync_memory_index.py +0 -84
- package/codex/learn-skill-from-conversations/scripts/extract_recent_conversations.py +0 -369
- package/codex/learn-skill-from-conversations/tests/test_extract_recent_conversations.py +0 -177
- package/docs-to-voice/scripts/__pycache__/docs_to_voice.cpython-312.pyc +0 -0
- package/docs-to-voice/scripts/docs_to_voice.py +0 -1385
- package/docs-to-voice/scripts/docs_to_voice.sh +0 -11
- package/docs-to-voice/tests/test_docs_to_voice_api_max_chars.py +0 -210
- package/docs-to-voice/tests/test_docs_to_voice_sentence_timeline.py +0 -115
- package/docs-to-voice/tests/test_docs_to_voice_settings.py +0 -43
- package/docs-to-voice/tests/test_docs_to_voice_shell_wrapper.py +0 -51
- package/docs-to-voice/tests/test_docs_to_voice_speech_rate.py +0 -57
- package/generate-spec/scripts/__pycache__/create-specscpython-312.pyc +0 -0
- package/generate-spec/scripts/create-specs +0 -215
- package/generate-spec/tests/test_create_specs.py +0 -200
- package/init-project-html/scripts/architecture-bootstrap-render.js +0 -16
- package/init-project-html/scripts/architecture.js +0 -296
- package/katex/scripts/__pycache__/render_katex.cpython-312.pyc +0 -0
- package/katex/scripts/render_katex.py +0 -247
- package/katex/scripts/render_katex.sh +0 -11
- package/katex/tests/test_render_katex.py +0 -174
- package/learning-error-book/scripts/render_error_book_json_to_pdf.py +0 -590
- package/learning-error-book/tests/test_render_error_book_json_to_pdf.py +0 -134
- package/open-github-issue/scripts/__pycache__/open_github_issue.cpython-312.pyc +0 -0
- package/open-github-issue/scripts/open_github_issue.py +0 -705
- package/open-github-issue/tests/test_open_github_issue.py +0 -381
- package/openai-text-to-image-storyboard/scripts/generate_storyboard_images.py +0 -763
- package/openai-text-to-image-storyboard/tests/test_generate_storyboard_images.py +0 -177
- package/read-github-issue/scripts/__pycache__/find_issues.cpython-312.pyc +0 -0
- package/read-github-issue/scripts/__pycache__/read_issue.cpython-312.pyc +0 -0
- package/read-github-issue/scripts/find_issues.py +0 -148
- package/read-github-issue/scripts/read_issue.py +0 -108
- package/read-github-issue/tests/test_find_issues.py +0 -127
- package/read-github-issue/tests/test_read_issue.py +0 -109
- package/resolve-review-comments/scripts/__pycache__/review_threads.cpython-312.pyc +0 -0
- package/resolve-review-comments/scripts/review_threads.py +0 -425
- package/resolve-review-comments/tests/test_review_threads.py +0 -74
- package/scripts/validate_openai_agent_config.py +0 -209
- package/scripts/validate_skill_frontmatter.py +0 -131
- package/text-to-short-video/scripts/__pycache__/enforce_video_aspect_ratio.cpython-312.pyc +0 -0
- package/text-to-short-video/scripts/enforce_video_aspect_ratio.py +0 -350
- package/text-to-short-video/tests/test_enforce_video_aspect_ratio.py +0 -194
- package/weekly-financial-event-report/scripts/extract_pdf_text_pdfkit.swift +0 -99
- package/weekly-financial-event-report/tests/test_extract_pdf_text_pdfkit.py +0 -64
|
@@ -0,0 +1,750 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.openGitHubIssueHandler = openGitHubIssueHandler;
|
|
37
|
+
// @ts-nocheck
|
|
38
|
+
const node_child_process_1 = require("node:child_process");
|
|
39
|
+
const node_fs_1 = require("node:fs");
|
|
40
|
+
const node_https_1 = require("node:https");
|
|
41
|
+
const node_os_1 = require("node:os");
|
|
42
|
+
const node_path_1 = require("node:path");
|
|
43
|
+
const GITHUB_API_BASE = 'https://api.github.com';
|
|
44
|
+
const README_ACCEPT = 'application/vnd.github.raw+json';
|
|
45
|
+
const JSON_ACCEPT = 'application/vnd.github+json';
|
|
46
|
+
const DEFAULT_REPRO_ZH = '尚未穩定重現;需補充更多執行期資料。';
|
|
47
|
+
const DEFAULT_REPRO_EN = 'Not yet reliably reproducible; more runtime evidence is required.';
|
|
48
|
+
const ISSUE_TYPE_PROBLEM = 'problem';
|
|
49
|
+
const ISSUE_TYPE_FEATURE = 'feature';
|
|
50
|
+
const ISSUE_TYPE_PERFORMANCE = 'performance';
|
|
51
|
+
const ISSUE_TYPE_SECURITY = 'security';
|
|
52
|
+
const ISSUE_TYPE_DOCS = 'docs';
|
|
53
|
+
const ISSUE_TYPE_OBSERVABILITY = 'observability';
|
|
54
|
+
const ISSUE_TYPES = [
|
|
55
|
+
ISSUE_TYPE_PROBLEM,
|
|
56
|
+
ISSUE_TYPE_FEATURE,
|
|
57
|
+
ISSUE_TYPE_PERFORMANCE,
|
|
58
|
+
ISSUE_TYPE_SECURITY,
|
|
59
|
+
ISSUE_TYPE_DOCS,
|
|
60
|
+
ISSUE_TYPE_OBSERVABILITY,
|
|
61
|
+
];
|
|
62
|
+
const PROBLEM_BDD_MARKER_GROUPS = [
|
|
63
|
+
[
|
|
64
|
+
/Expected Behavior\s*\(BDD\)/i,
|
|
65
|
+
/Current Behavior\s*\(BDD\)/i,
|
|
66
|
+
/Behavior Gap/i,
|
|
67
|
+
],
|
|
68
|
+
[
|
|
69
|
+
/預期行為\s*[((]BDD[))]/i,
|
|
70
|
+
/(?:目前|當前)行為\s*[((]BDD[))]/i,
|
|
71
|
+
/行為(?:落差|差異)/i,
|
|
72
|
+
],
|
|
73
|
+
];
|
|
74
|
+
const TEXT_FIELDS = [
|
|
75
|
+
'title',
|
|
76
|
+
'problem_description',
|
|
77
|
+
'suspected_cause',
|
|
78
|
+
'reproduction',
|
|
79
|
+
'proposal',
|
|
80
|
+
'reason',
|
|
81
|
+
'suggested_architecture',
|
|
82
|
+
'impact',
|
|
83
|
+
'evidence',
|
|
84
|
+
'suggested_action',
|
|
85
|
+
'affected_scope',
|
|
86
|
+
];
|
|
87
|
+
const PAYLOAD_FIELDS = new Set([
|
|
88
|
+
'title',
|
|
89
|
+
'issue_type',
|
|
90
|
+
'problem_description',
|
|
91
|
+
'suspected_cause',
|
|
92
|
+
'reproduction',
|
|
93
|
+
'proposal',
|
|
94
|
+
'reason',
|
|
95
|
+
'suggested_architecture',
|
|
96
|
+
'impact',
|
|
97
|
+
'evidence',
|
|
98
|
+
'suggested_action',
|
|
99
|
+
'severity',
|
|
100
|
+
'affected_scope',
|
|
101
|
+
'repo',
|
|
102
|
+
'dry_run',
|
|
103
|
+
]);
|
|
104
|
+
function parseArgs(argv) {
|
|
105
|
+
const args = {
|
|
106
|
+
payloadFile: null,
|
|
107
|
+
title: null,
|
|
108
|
+
issueType: null,
|
|
109
|
+
problemDescription: null,
|
|
110
|
+
suspectedCause: null,
|
|
111
|
+
reproduction: null,
|
|
112
|
+
proposal: null,
|
|
113
|
+
reason: null,
|
|
114
|
+
suggestedArchitecture: null,
|
|
115
|
+
impact: null,
|
|
116
|
+
evidence: null,
|
|
117
|
+
suggestedAction: null,
|
|
118
|
+
severity: null,
|
|
119
|
+
affectedScope: null,
|
|
120
|
+
repo: null,
|
|
121
|
+
dryRun: false,
|
|
122
|
+
};
|
|
123
|
+
let i = 0;
|
|
124
|
+
while (i < argv.length) {
|
|
125
|
+
const arg = argv[i];
|
|
126
|
+
switch (arg) {
|
|
127
|
+
case '--payload-file':
|
|
128
|
+
if (i + 1 < argv.length)
|
|
129
|
+
args.payloadFile = argv[++i];
|
|
130
|
+
break;
|
|
131
|
+
case '--title':
|
|
132
|
+
if (i + 1 < argv.length)
|
|
133
|
+
args.title = argv[++i];
|
|
134
|
+
break;
|
|
135
|
+
case '--issue-type':
|
|
136
|
+
if (i + 1 < argv.length)
|
|
137
|
+
args.issueType = argv[++i];
|
|
138
|
+
break;
|
|
139
|
+
case '--problem-description':
|
|
140
|
+
if (i + 1 < argv.length)
|
|
141
|
+
args.problemDescription = argv[++i];
|
|
142
|
+
break;
|
|
143
|
+
case '--suspected-cause':
|
|
144
|
+
if (i + 1 < argv.length)
|
|
145
|
+
args.suspectedCause = argv[++i];
|
|
146
|
+
break;
|
|
147
|
+
case '--reproduction':
|
|
148
|
+
if (i + 1 < argv.length)
|
|
149
|
+
args.reproduction = argv[++i];
|
|
150
|
+
break;
|
|
151
|
+
case '--proposal':
|
|
152
|
+
if (i + 1 < argv.length)
|
|
153
|
+
args.proposal = argv[++i];
|
|
154
|
+
break;
|
|
155
|
+
case '--reason':
|
|
156
|
+
if (i + 1 < argv.length)
|
|
157
|
+
args.reason = argv[++i];
|
|
158
|
+
break;
|
|
159
|
+
case '--suggested-architecture':
|
|
160
|
+
if (i + 1 < argv.length)
|
|
161
|
+
args.suggestedArchitecture = argv[++i];
|
|
162
|
+
break;
|
|
163
|
+
case '--impact':
|
|
164
|
+
if (i + 1 < argv.length)
|
|
165
|
+
args.impact = argv[++i];
|
|
166
|
+
break;
|
|
167
|
+
case '--evidence':
|
|
168
|
+
if (i + 1 < argv.length)
|
|
169
|
+
args.evidence = argv[++i];
|
|
170
|
+
break;
|
|
171
|
+
case '--suggested-action':
|
|
172
|
+
if (i + 1 < argv.length)
|
|
173
|
+
args.suggestedAction = argv[++i];
|
|
174
|
+
break;
|
|
175
|
+
case '--severity':
|
|
176
|
+
if (i + 1 < argv.length)
|
|
177
|
+
args.severity = argv[++i];
|
|
178
|
+
break;
|
|
179
|
+
case '--affected-scope':
|
|
180
|
+
if (i + 1 < argv.length)
|
|
181
|
+
args.affectedScope = argv[++i];
|
|
182
|
+
break;
|
|
183
|
+
case '--repo':
|
|
184
|
+
if (i + 1 < argv.length)
|
|
185
|
+
args.repo = argv[++i];
|
|
186
|
+
break;
|
|
187
|
+
case '--dry-run':
|
|
188
|
+
args.dryRun = true;
|
|
189
|
+
break;
|
|
190
|
+
default:
|
|
191
|
+
if (!arg.startsWith('-')) {
|
|
192
|
+
// unsupported positional — ignore per Python behavior
|
|
193
|
+
}
|
|
194
|
+
break;
|
|
195
|
+
}
|
|
196
|
+
i++;
|
|
197
|
+
}
|
|
198
|
+
return args;
|
|
199
|
+
}
|
|
200
|
+
function runCommand(cmd, cmdArgs) {
|
|
201
|
+
return new Promise((resolve) => {
|
|
202
|
+
(0, node_child_process_1.execFile)(cmd, cmdArgs, { maxBuffer: 10 * 1024 * 1024 }, (error, stdout, stderr) => {
|
|
203
|
+
if (error) {
|
|
204
|
+
resolve({
|
|
205
|
+
stdout: stdout || '',
|
|
206
|
+
stderr: stderr || '',
|
|
207
|
+
exitCode: error.status ?? 1,
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
else {
|
|
211
|
+
resolve({ stdout: stdout || '', stderr: stderr || '', exitCode: 0 });
|
|
212
|
+
}
|
|
213
|
+
});
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
function normalizeKey(key) {
|
|
217
|
+
return key.replace(/-/g, '_');
|
|
218
|
+
}
|
|
219
|
+
function readPayloadFile(rawPath) {
|
|
220
|
+
let rawContent;
|
|
221
|
+
let context;
|
|
222
|
+
if (rawPath === '-') {
|
|
223
|
+
// We cannot read stdin here easily; throw clear error
|
|
224
|
+
throw new Error('stdin payload (-) is not supported in handler mode; use a file path');
|
|
225
|
+
}
|
|
226
|
+
else {
|
|
227
|
+
rawContent = (0, node_fs_1.readFileSync)(rawPath, 'utf-8');
|
|
228
|
+
context = rawPath;
|
|
229
|
+
}
|
|
230
|
+
let payload;
|
|
231
|
+
try {
|
|
232
|
+
payload = JSON.parse(rawContent);
|
|
233
|
+
}
|
|
234
|
+
catch (exc) {
|
|
235
|
+
throw new Error(`Invalid JSON payload in ${context}: ${exc.message}`);
|
|
236
|
+
}
|
|
237
|
+
if (typeof payload !== 'object' || payload === null || Array.isArray(payload)) {
|
|
238
|
+
throw new Error(`Invalid JSON payload in ${context}: top-level value must be an object.`);
|
|
239
|
+
}
|
|
240
|
+
const normalized = {};
|
|
241
|
+
for (const [rawKey, value] of Object.entries(payload)) {
|
|
242
|
+
const key = normalizeKey(rawKey);
|
|
243
|
+
if (!PAYLOAD_FIELDS.has(key)) {
|
|
244
|
+
throw new Error(`Unsupported payload key: ${rawKey}`);
|
|
245
|
+
}
|
|
246
|
+
normalized[key] = value;
|
|
247
|
+
}
|
|
248
|
+
return normalized;
|
|
249
|
+
}
|
|
250
|
+
function readAtFileValue(fieldName, value) {
|
|
251
|
+
if (value === null)
|
|
252
|
+
return null;
|
|
253
|
+
if (value.startsWith('@@'))
|
|
254
|
+
return value.slice(1);
|
|
255
|
+
if (value === '@-') {
|
|
256
|
+
throw new Error('stdin reading (@-) is not supported in handler mode');
|
|
257
|
+
}
|
|
258
|
+
if (value.startsWith('@') && value.length > 1) {
|
|
259
|
+
const filePath = value.slice(1);
|
|
260
|
+
try {
|
|
261
|
+
return (0, node_fs_1.readFileSync)(filePath, 'utf-8');
|
|
262
|
+
}
|
|
263
|
+
catch (exc) {
|
|
264
|
+
throw new Error(`Unable to read @${fieldName} file ${filePath}: ${exc.message}`);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
return value;
|
|
268
|
+
}
|
|
269
|
+
function requireNonEmpty(value, message) {
|
|
270
|
+
if (!(value || '').trim()) {
|
|
271
|
+
throw new Error(message);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
function hasRequiredProblemBddSections(problemDescription) {
|
|
275
|
+
const normalized = problemDescription.trim();
|
|
276
|
+
return PROBLEM_BDD_MARKER_GROUPS.some((group) => group.every((pattern) => pattern.test(normalized)));
|
|
277
|
+
}
|
|
278
|
+
function hasGhAuth() {
|
|
279
|
+
return runCommand('gh', ['auth', 'status']).then((r) => r.exitCode === 0);
|
|
280
|
+
}
|
|
281
|
+
function getToken(env) {
|
|
282
|
+
return env.GITHUB_TOKEN || env.GH_TOKEN || null;
|
|
283
|
+
}
|
|
284
|
+
function resolveRepo(explicitRepo) {
|
|
285
|
+
if (explicitRepo) {
|
|
286
|
+
return validateRepo(explicitRepo);
|
|
287
|
+
}
|
|
288
|
+
// Try to resolve from git remote
|
|
289
|
+
const result = runCommand('git', [
|
|
290
|
+
'remote', 'get-url', 'origin',
|
|
291
|
+
]);
|
|
292
|
+
// This is sync because it's called in a non-async context flow
|
|
293
|
+
// Actually, let me use the promisified version — but we need to make the caller async
|
|
294
|
+
// For now, throw a helpful error
|
|
295
|
+
throw new Error('--repo is required in handler mode. Unable to auto-detect from git remote in-process.');
|
|
296
|
+
}
|
|
297
|
+
function validateRepo(repo) {
|
|
298
|
+
const candidate = repo.trim();
|
|
299
|
+
if (!/^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/.test(candidate)) {
|
|
300
|
+
throw new Error('Invalid repo format. Use owner/repo.');
|
|
301
|
+
}
|
|
302
|
+
return candidate;
|
|
303
|
+
}
|
|
304
|
+
// ---- HTTP helpers ----
|
|
305
|
+
function githubRequest(method, path, token, accept, payload) {
|
|
306
|
+
return new Promise((resolve, reject) => {
|
|
307
|
+
const headers = {
|
|
308
|
+
Accept: accept,
|
|
309
|
+
'User-Agent': 'open-github-issue-skill',
|
|
310
|
+
'X-GitHub-Api-Version': '2022-11-28',
|
|
311
|
+
};
|
|
312
|
+
if (token) {
|
|
313
|
+
headers['Authorization'] = `Bearer ${token}`;
|
|
314
|
+
}
|
|
315
|
+
let body;
|
|
316
|
+
if (payload !== undefined) {
|
|
317
|
+
body = JSON.stringify(payload);
|
|
318
|
+
headers['Content-Type'] = 'application/json';
|
|
319
|
+
}
|
|
320
|
+
const url = new URL(`${GITHUB_API_BASE}${path}`);
|
|
321
|
+
const req = (0, node_https_1.request)(url, {
|
|
322
|
+
method,
|
|
323
|
+
headers,
|
|
324
|
+
}, (res) => {
|
|
325
|
+
let data = '';
|
|
326
|
+
res.on('data', (chunk) => {
|
|
327
|
+
data += chunk.toString('utf-8');
|
|
328
|
+
});
|
|
329
|
+
res.on('end', () => {
|
|
330
|
+
if (res.statusCode && res.statusCode >= 200 && res.statusCode < 300) {
|
|
331
|
+
resolve(data);
|
|
332
|
+
}
|
|
333
|
+
else {
|
|
334
|
+
const detail = data || 'unknown error';
|
|
335
|
+
reject(new Error(`GitHub API ${res.statusCode} ${path}: ${detail}`));
|
|
336
|
+
}
|
|
337
|
+
});
|
|
338
|
+
});
|
|
339
|
+
req.on('error', (err) => {
|
|
340
|
+
reject(new Error(`GitHub API request failed for ${path}: ${err.message}`));
|
|
341
|
+
});
|
|
342
|
+
if (body !== undefined) {
|
|
343
|
+
req.write(body);
|
|
344
|
+
}
|
|
345
|
+
req.end();
|
|
346
|
+
});
|
|
347
|
+
}
|
|
348
|
+
async function fetchRemoteReadme(repo, ghAuthenticated, token) {
|
|
349
|
+
if (ghAuthenticated) {
|
|
350
|
+
const result = await runCommand('gh', [
|
|
351
|
+
'api',
|
|
352
|
+
'-H',
|
|
353
|
+
`Accept: ${README_ACCEPT}`,
|
|
354
|
+
`repos/${repo}/readme`,
|
|
355
|
+
]);
|
|
356
|
+
if (result.exitCode === 0) {
|
|
357
|
+
return result.stdout;
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
try {
|
|
361
|
+
return await githubRequest('GET', `/repos/${repo}/readme`, token, README_ACCEPT);
|
|
362
|
+
}
|
|
363
|
+
catch {
|
|
364
|
+
return '';
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
function detectIssueLanguage(readmeContent) {
|
|
368
|
+
if (!readmeContent.trim())
|
|
369
|
+
return 'en';
|
|
370
|
+
const chineseChars = (readmeContent.match(/[一-鿿]/g) || []).length;
|
|
371
|
+
const languageChars = (readmeContent.match(/[A-Za-z一-鿿]/g) || []).length;
|
|
372
|
+
if (chineseChars >= 20 && languageChars > 0 && chineseChars / languageChars >= 0.08) {
|
|
373
|
+
return 'zh';
|
|
374
|
+
}
|
|
375
|
+
return 'en';
|
|
376
|
+
}
|
|
377
|
+
// ---- Issue body builder ----
|
|
378
|
+
function buildIssueBody(params) {
|
|
379
|
+
const { issueType, language, title, problemDescription, suspectedCause, reproduction, proposal, reason, suggestedArchitecture, impact, evidence, suggestedAction, severity, affectedScope, } = params;
|
|
380
|
+
if (issueType === ISSUE_TYPE_FEATURE) {
|
|
381
|
+
const proposalText = (proposal || title).trim();
|
|
382
|
+
const reasonText = (reason || '').trim();
|
|
383
|
+
const architectureText = (suggestedArchitecture || '').trim();
|
|
384
|
+
if (language === 'zh') {
|
|
385
|
+
return ('### 功能提案\n' +
|
|
386
|
+
`${proposalText}\n\n` +
|
|
387
|
+
'### 原因\n' +
|
|
388
|
+
`${reasonText}\n\n` +
|
|
389
|
+
'### 建議架構\n' +
|
|
390
|
+
`${architectureText}\n`);
|
|
391
|
+
}
|
|
392
|
+
return ('### Feature Proposal\n' +
|
|
393
|
+
`${proposalText}\n\n` +
|
|
394
|
+
'### Why This Is Needed\n' +
|
|
395
|
+
`${reasonText}\n\n` +
|
|
396
|
+
'### Suggested Architecture\n' +
|
|
397
|
+
`${architectureText}\n`);
|
|
398
|
+
}
|
|
399
|
+
if (issueType === ISSUE_TYPE_PERFORMANCE) {
|
|
400
|
+
if (language === 'zh') {
|
|
401
|
+
return ('### 效能問題\n' +
|
|
402
|
+
`${(problemDescription || '').trim()}\n\n` +
|
|
403
|
+
'### 影響\n' +
|
|
404
|
+
`${(impact || '').trim()}\n\n` +
|
|
405
|
+
'### 證據\n' +
|
|
406
|
+
`${(evidence || '').trim()}\n\n` +
|
|
407
|
+
'### 建議行動\n' +
|
|
408
|
+
`${(suggestedAction || '').trim()}\n`);
|
|
409
|
+
}
|
|
410
|
+
return ('### Performance Problem\n' +
|
|
411
|
+
`${(problemDescription || '').trim()}\n\n` +
|
|
412
|
+
'### Impact\n' +
|
|
413
|
+
`${(impact || '').trim()}\n\n` +
|
|
414
|
+
'### Evidence\n' +
|
|
415
|
+
`${(evidence || '').trim()}\n\n` +
|
|
416
|
+
'### Suggested Action\n' +
|
|
417
|
+
`${(suggestedAction || '').trim()}\n`);
|
|
418
|
+
}
|
|
419
|
+
if (issueType === ISSUE_TYPE_SECURITY) {
|
|
420
|
+
if (language === 'zh') {
|
|
421
|
+
return ('### 安全風險\n' +
|
|
422
|
+
`${(problemDescription || '').trim()}\n\n` +
|
|
423
|
+
'### 嚴重程度\n' +
|
|
424
|
+
`${(severity || '').trim()}\n\n` +
|
|
425
|
+
'### 受影響範圍\n' +
|
|
426
|
+
`${(affectedScope || '').trim()}\n\n` +
|
|
427
|
+
'### 影響\n' +
|
|
428
|
+
`${(impact || '').trim()}\n\n` +
|
|
429
|
+
'### 證據\n' +
|
|
430
|
+
`${(evidence || '').trim()}\n\n` +
|
|
431
|
+
'### 建議緩解\n' +
|
|
432
|
+
`${(suggestedAction || '').trim()}\n`);
|
|
433
|
+
}
|
|
434
|
+
return ('### Security Risk\n' +
|
|
435
|
+
`${(problemDescription || '').trim()}\n\n` +
|
|
436
|
+
'### Severity\n' +
|
|
437
|
+
`${(severity || '').trim()}\n\n` +
|
|
438
|
+
'### Affected Scope\n' +
|
|
439
|
+
`${(affectedScope || '').trim()}\n\n` +
|
|
440
|
+
'### Impact\n' +
|
|
441
|
+
`${(impact || '').trim()}\n\n` +
|
|
442
|
+
'### Evidence\n' +
|
|
443
|
+
`${(evidence || '').trim()}\n\n` +
|
|
444
|
+
'### Suggested Mitigation\n' +
|
|
445
|
+
`${(suggestedAction || '').trim()}\n`);
|
|
446
|
+
}
|
|
447
|
+
if (issueType === ISSUE_TYPE_DOCS) {
|
|
448
|
+
if (language === 'zh') {
|
|
449
|
+
return ('### 文件缺口\n' +
|
|
450
|
+
`${(problemDescription || '').trim()}\n\n` +
|
|
451
|
+
'### 證據\n' +
|
|
452
|
+
`${(evidence || '').trim()}\n\n` +
|
|
453
|
+
'### 建議更新\n' +
|
|
454
|
+
`${(suggestedAction || '').trim()}\n`);
|
|
455
|
+
}
|
|
456
|
+
return ('### Documentation Gap\n' +
|
|
457
|
+
`${(problemDescription || '').trim()}\n\n` +
|
|
458
|
+
'### Evidence\n' +
|
|
459
|
+
`${(evidence || '').trim()}\n\n` +
|
|
460
|
+
'### Suggested Update\n' +
|
|
461
|
+
`${(suggestedAction || '').trim()}\n`);
|
|
462
|
+
}
|
|
463
|
+
if (issueType === ISSUE_TYPE_OBSERVABILITY) {
|
|
464
|
+
if (language === 'zh') {
|
|
465
|
+
return ('### 可觀測性缺口\n' +
|
|
466
|
+
`${(problemDescription || '').trim()}\n\n` +
|
|
467
|
+
'### 影響\n' +
|
|
468
|
+
`${(impact || '').trim()}\n\n` +
|
|
469
|
+
'### 證據\n' +
|
|
470
|
+
`${(evidence || '').trim()}\n\n` +
|
|
471
|
+
'### 建議儀表化\n' +
|
|
472
|
+
`${(suggestedAction || '').trim()}\n`);
|
|
473
|
+
}
|
|
474
|
+
return ('### Observability Gap\n' +
|
|
475
|
+
`${(problemDescription || '').trim()}\n\n` +
|
|
476
|
+
'### Impact\n' +
|
|
477
|
+
`${(impact || '').trim()}\n\n` +
|
|
478
|
+
'### Evidence\n' +
|
|
479
|
+
`${(evidence || '').trim()}\n\n` +
|
|
480
|
+
'### Suggested Instrumentation\n' +
|
|
481
|
+
`${(suggestedAction || '').trim()}\n`);
|
|
482
|
+
}
|
|
483
|
+
// Default: problem
|
|
484
|
+
if (language === 'zh') {
|
|
485
|
+
const reproText = (reproduction || DEFAULT_REPRO_ZH).trim();
|
|
486
|
+
return ('### 問題描述\n' +
|
|
487
|
+
`${(problemDescription || '').trim()}\n\n` +
|
|
488
|
+
'### 推測原因\n' +
|
|
489
|
+
`${(suspectedCause || '').trim()}\n\n` +
|
|
490
|
+
'### 重現條件(如有)\n' +
|
|
491
|
+
`${reproText}\n`);
|
|
492
|
+
}
|
|
493
|
+
const reproText = (reproduction || DEFAULT_REPRO_EN).trim();
|
|
494
|
+
return ('### Problem Description\n' +
|
|
495
|
+
`${(problemDescription || '').trim()}\n\n` +
|
|
496
|
+
'### Suspected Cause\n' +
|
|
497
|
+
`${(suspectedCause || '').trim()}\n\n` +
|
|
498
|
+
'### Reproduction Conditions (if available)\n' +
|
|
499
|
+
`${reproText}\n`);
|
|
500
|
+
}
|
|
501
|
+
// ---- Issue creation ----
|
|
502
|
+
async function createIssueWithGh(repo, title, body) {
|
|
503
|
+
// Write body to a temp file
|
|
504
|
+
const tmpFile = (0, node_path_1.join)((0, node_os_1.tmpdir)(), `issue-${Date.now()}.md`);
|
|
505
|
+
const { writeFileSync, unlinkSync } = await Promise.resolve().then(() => __importStar(require('node:fs')));
|
|
506
|
+
writeFileSync(tmpFile, body, 'utf-8');
|
|
507
|
+
try {
|
|
508
|
+
const result = await runCommand('gh', [
|
|
509
|
+
'issue',
|
|
510
|
+
'create',
|
|
511
|
+
'--repo',
|
|
512
|
+
repo,
|
|
513
|
+
'--title',
|
|
514
|
+
title,
|
|
515
|
+
'--body-file',
|
|
516
|
+
tmpFile,
|
|
517
|
+
]);
|
|
518
|
+
if (result.exitCode !== 0) {
|
|
519
|
+
throw new Error(result.stderr.trim() || 'gh issue create failed');
|
|
520
|
+
}
|
|
521
|
+
const urlMatch = result.stdout.match(/https:\/\/github\.com\/[^\s]+\/issues\/\d+/);
|
|
522
|
+
return urlMatch ? urlMatch[0] : result.stdout.trim();
|
|
523
|
+
}
|
|
524
|
+
finally {
|
|
525
|
+
try {
|
|
526
|
+
unlinkSync(tmpFile);
|
|
527
|
+
}
|
|
528
|
+
catch {
|
|
529
|
+
// ignore cleanup errors
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
async function createIssueWithToken(repo, title, body, token) {
|
|
534
|
+
const response = await githubRequest('POST', `/repos/${repo}/issues`, token, JSON_ACCEPT, {
|
|
535
|
+
title,
|
|
536
|
+
body,
|
|
537
|
+
});
|
|
538
|
+
const parsed = JSON.parse(response);
|
|
539
|
+
const issueUrl = parsed.html_url;
|
|
540
|
+
if (!issueUrl) {
|
|
541
|
+
throw new Error('Issue created but response did not include html_url');
|
|
542
|
+
}
|
|
543
|
+
return issueUrl;
|
|
544
|
+
}
|
|
545
|
+
// ---- Validation ----
|
|
546
|
+
function validateIssueContent(args) {
|
|
547
|
+
const issueType = args.issueType || ISSUE_TYPE_PROBLEM;
|
|
548
|
+
if (issueType === ISSUE_TYPE_FEATURE) {
|
|
549
|
+
requireNonEmpty(args.reason, 'Feature issues require --reason.');
|
|
550
|
+
requireNonEmpty(args.suggestedArchitecture, 'Feature issues require --suggested-architecture.');
|
|
551
|
+
return;
|
|
552
|
+
}
|
|
553
|
+
if (issueType === ISSUE_TYPE_PERFORMANCE) {
|
|
554
|
+
requireNonEmpty(args.problemDescription, 'Performance issues require --problem-description.');
|
|
555
|
+
requireNonEmpty(args.impact, 'Performance issues require --impact.');
|
|
556
|
+
requireNonEmpty(args.evidence, 'Performance issues require --evidence.');
|
|
557
|
+
requireNonEmpty(args.suggestedAction, 'Performance issues require --suggested-action.');
|
|
558
|
+
return;
|
|
559
|
+
}
|
|
560
|
+
if (issueType === ISSUE_TYPE_SECURITY) {
|
|
561
|
+
requireNonEmpty(args.problemDescription, 'Security issues require --problem-description.');
|
|
562
|
+
requireNonEmpty(args.affectedScope, 'Security issues require --affected-scope.');
|
|
563
|
+
requireNonEmpty(args.impact, 'Security issues require --impact.');
|
|
564
|
+
requireNonEmpty(args.evidence, 'Security issues require --evidence.');
|
|
565
|
+
requireNonEmpty(args.suggestedAction, 'Security issues require --suggested-action.');
|
|
566
|
+
requireNonEmpty(args.severity, 'Security issues require --severity.');
|
|
567
|
+
return;
|
|
568
|
+
}
|
|
569
|
+
if (issueType === ISSUE_TYPE_DOCS) {
|
|
570
|
+
requireNonEmpty(args.problemDescription, 'Docs issues require --problem-description.');
|
|
571
|
+
requireNonEmpty(args.evidence, 'Docs issues require --evidence.');
|
|
572
|
+
requireNonEmpty(args.suggestedAction, 'Docs issues require --suggested-action.');
|
|
573
|
+
return;
|
|
574
|
+
}
|
|
575
|
+
if (issueType === ISSUE_TYPE_OBSERVABILITY) {
|
|
576
|
+
requireNonEmpty(args.problemDescription, 'Observability issues require --problem-description.');
|
|
577
|
+
requireNonEmpty(args.impact, 'Observability issues require --impact.');
|
|
578
|
+
requireNonEmpty(args.evidence, 'Observability issues require --evidence.');
|
|
579
|
+
requireNonEmpty(args.suggestedAction, 'Observability issues require --suggested-action.');
|
|
580
|
+
return;
|
|
581
|
+
}
|
|
582
|
+
// Problem issue
|
|
583
|
+
requireNonEmpty(args.problemDescription, 'Problem issues require --problem-description.');
|
|
584
|
+
requireNonEmpty(args.suspectedCause, 'Problem issues require --suspected-cause.');
|
|
585
|
+
if (!hasRequiredProblemBddSections(args.problemDescription || '')) {
|
|
586
|
+
throw new Error('Problem issues require --problem-description to include ' +
|
|
587
|
+
'Expected Behavior (BDD), Current Behavior (BDD), and Behavior Gap sections.');
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
function hydrateArgs(args) {
|
|
591
|
+
const result = { ...args };
|
|
592
|
+
// Load from payload file if provided
|
|
593
|
+
if (result.payloadFile) {
|
|
594
|
+
const payload = readPayloadFile(result.payloadFile);
|
|
595
|
+
for (const [key, value] of Object.entries(payload)) {
|
|
596
|
+
if (key === 'dry_run') {
|
|
597
|
+
if (typeof value !== 'boolean') {
|
|
598
|
+
throw new Error("Payload field 'dry_run' must be a boolean.");
|
|
599
|
+
}
|
|
600
|
+
if (!result.dryRun) {
|
|
601
|
+
result.dryRun = value;
|
|
602
|
+
}
|
|
603
|
+
continue;
|
|
604
|
+
}
|
|
605
|
+
// String fields
|
|
606
|
+
if (TEXT_FIELDS.includes(key)) {
|
|
607
|
+
if (value !== null && typeof value !== 'string') {
|
|
608
|
+
throw new Error(`Payload field '${key}' must be a string or null.`);
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
else if (typeof value !== 'string') {
|
|
612
|
+
throw new Error(`Payload field '${key}' must be a string.`);
|
|
613
|
+
}
|
|
614
|
+
const currentVal = result[key];
|
|
615
|
+
if (currentVal === null || currentVal === '') {
|
|
616
|
+
result[key] = value;
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
// Set default issue type
|
|
621
|
+
if (!result.issueType) {
|
|
622
|
+
result.issueType = ISSUE_TYPE_PROBLEM;
|
|
623
|
+
}
|
|
624
|
+
if (!ISSUE_TYPES.includes(result.issueType)) {
|
|
625
|
+
throw new Error(`Invalid issue_type: ${result.issueType}`);
|
|
626
|
+
}
|
|
627
|
+
// Resolve @-prefixed file values
|
|
628
|
+
for (const fieldName of TEXT_FIELDS) {
|
|
629
|
+
const resultObj = result;
|
|
630
|
+
const val = resultObj[fieldName];
|
|
631
|
+
resultObj[fieldName] = readAtFileValue(fieldName, val);
|
|
632
|
+
}
|
|
633
|
+
// Title is required
|
|
634
|
+
if (!(result.title || '').trim()) {
|
|
635
|
+
throw new Error('Issue title is required. Pass --title or include title in --payload-file.');
|
|
636
|
+
}
|
|
637
|
+
return result;
|
|
638
|
+
}
|
|
639
|
+
async function resolveRepoAsync(explicitRepo, context) {
|
|
640
|
+
if (explicitRepo)
|
|
641
|
+
return validateRepo(explicitRepo);
|
|
642
|
+
// Try to resolve from git remote
|
|
643
|
+
const result = await runCommand('git', ['remote', 'get-url', 'origin']);
|
|
644
|
+
if (result.exitCode !== 0) {
|
|
645
|
+
context.stderr.write('Unable to resolve origin remote. Pass --repo owner/repo.\n');
|
|
646
|
+
throw new Error('--repo resolution failed');
|
|
647
|
+
}
|
|
648
|
+
const remote = result.stdout.trim();
|
|
649
|
+
const match = remote.match(/github\.com[:/](?<owner>[A-Za-z0-9_.-]+)\/(?<repo>[A-Za-z0-9_.-]+?)(?:\.git)?$/);
|
|
650
|
+
if (!match?.groups) {
|
|
651
|
+
context.stderr.write('Origin remote is not a GitHub repository. Pass --repo owner/repo.\n');
|
|
652
|
+
throw new Error('--repo resolution failed');
|
|
653
|
+
}
|
|
654
|
+
return `${match.groups.owner}/${match.groups.repo}`;
|
|
655
|
+
}
|
|
656
|
+
async function openGitHubIssueHandler(argv, context) {
|
|
657
|
+
const { stdout, stderr, env } = context;
|
|
658
|
+
let args;
|
|
659
|
+
try {
|
|
660
|
+
args = hydrateArgs(parseArgs(argv));
|
|
661
|
+
validateIssueContent(args);
|
|
662
|
+
}
|
|
663
|
+
catch (err) {
|
|
664
|
+
stderr.write(`Error: ${err.message}\n`);
|
|
665
|
+
return 1;
|
|
666
|
+
}
|
|
667
|
+
const ghAuthenticated = await hasGhAuth();
|
|
668
|
+
const token = getToken(env);
|
|
669
|
+
let repo;
|
|
670
|
+
try {
|
|
671
|
+
repo = await resolveRepoAsync(args.repo, context);
|
|
672
|
+
}
|
|
673
|
+
catch {
|
|
674
|
+
return 1;
|
|
675
|
+
}
|
|
676
|
+
const readmeContent = await fetchRemoteReadme(repo, ghAuthenticated, token);
|
|
677
|
+
const language = detectIssueLanguage(readmeContent);
|
|
678
|
+
const issueBody = buildIssueBody({
|
|
679
|
+
issueType: args.issueType || ISSUE_TYPE_PROBLEM,
|
|
680
|
+
language,
|
|
681
|
+
title: args.title || '',
|
|
682
|
+
problemDescription: args.problemDescription,
|
|
683
|
+
suspectedCause: args.suspectedCause,
|
|
684
|
+
reproduction: args.reproduction,
|
|
685
|
+
proposal: args.proposal,
|
|
686
|
+
reason: args.reason,
|
|
687
|
+
suggestedArchitecture: args.suggestedArchitecture,
|
|
688
|
+
impact: args.impact,
|
|
689
|
+
evidence: args.evidence,
|
|
690
|
+
suggestedAction: args.suggestedAction,
|
|
691
|
+
severity: args.severity,
|
|
692
|
+
affectedScope: args.affectedScope,
|
|
693
|
+
});
|
|
694
|
+
let mode = 'draft-only';
|
|
695
|
+
let issueUrl = '';
|
|
696
|
+
let publishError = '';
|
|
697
|
+
if (args.dryRun) {
|
|
698
|
+
mode = 'dry-run';
|
|
699
|
+
}
|
|
700
|
+
else if (ghAuthenticated) {
|
|
701
|
+
try {
|
|
702
|
+
issueUrl = await createIssueWithGh(repo, args.title || '', issueBody);
|
|
703
|
+
mode = 'gh-cli';
|
|
704
|
+
}
|
|
705
|
+
catch (exc) {
|
|
706
|
+
if (token) {
|
|
707
|
+
try {
|
|
708
|
+
issueUrl = await createIssueWithToken(repo, args.title || '', issueBody, token);
|
|
709
|
+
mode = 'github-token';
|
|
710
|
+
}
|
|
711
|
+
catch (tokenExc) {
|
|
712
|
+
publishError = tokenExc.message;
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
else {
|
|
716
|
+
publishError = exc.message;
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
else if (token) {
|
|
721
|
+
try {
|
|
722
|
+
issueUrl = await createIssueWithToken(repo, args.title || '', issueBody, token);
|
|
723
|
+
mode = 'github-token';
|
|
724
|
+
}
|
|
725
|
+
catch (exc) {
|
|
726
|
+
publishError = exc.message;
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
const output = {
|
|
730
|
+
repo,
|
|
731
|
+
issue_type: args.issueType || ISSUE_TYPE_PROBLEM,
|
|
732
|
+
language: language === 'zh' ? 'zh' : 'en',
|
|
733
|
+
mode,
|
|
734
|
+
issue_url: issueUrl,
|
|
735
|
+
issue_title: args.title || '',
|
|
736
|
+
issue_body: issueBody,
|
|
737
|
+
publish_error: publishError,
|
|
738
|
+
};
|
|
739
|
+
stdout.write(JSON.stringify(output, null, 2) + '\n');
|
|
740
|
+
if (mode === 'draft-only') {
|
|
741
|
+
if (publishError) {
|
|
742
|
+
stderr.write(`Issue publish failed. Return draft only: ${publishError}\n`);
|
|
743
|
+
}
|
|
744
|
+
else {
|
|
745
|
+
stderr.write('No authenticated gh CLI session and no GitHub token found. ' +
|
|
746
|
+
'Return draft issue body only.\n');
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
return 0;
|
|
750
|
+
}
|