@axis-ds/cli 1.1.1 → 1.1.3
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.
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// src/template/registry-client.ts
|
|
2
2
|
import fetch from "node-fetch";
|
|
3
|
-
var TEMPLATE_REGISTRY_URL = process.env.AXIS_TEMPLATE_URL || "https://
|
|
3
|
+
var TEMPLATE_REGISTRY_URL = process.env.AXIS_TEMPLATE_URL || "https://axis.minu.best/templates";
|
|
4
4
|
async function fetchTemplateIndex() {
|
|
5
5
|
const url = `${TEMPLATE_REGISTRY_URL}/index.json`;
|
|
6
6
|
const res = await fetch(url);
|
package/dist/index.js
CHANGED
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
import {
|
|
12
12
|
fetchTemplateDetail,
|
|
13
13
|
fetchTemplateIndex
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-3WGMHDZL.js";
|
|
15
15
|
|
|
16
16
|
// src/index.ts
|
|
17
17
|
import { Command } from "commander";
|
|
@@ -1080,7 +1080,8 @@ var AxisCollector = class extends BaseCollector {
|
|
|
1080
1080
|
*/
|
|
1081
1081
|
getDocsUrl(slug, category) {
|
|
1082
1082
|
const baseUrl = "https://axis.minu.best";
|
|
1083
|
-
|
|
1083
|
+
const route = category === "agentic" ? "agentic" : "components";
|
|
1084
|
+
return `${baseUrl}/${route}/${slug}`;
|
|
1084
1085
|
}
|
|
1085
1086
|
};
|
|
1086
1087
|
|
|
@@ -1654,7 +1655,7 @@ async function applyTemplate(slug, options) {
|
|
|
1654
1655
|
async function initTemplate(slug, options) {
|
|
1655
1656
|
let selectedSlug = slug;
|
|
1656
1657
|
if (!selectedSlug) {
|
|
1657
|
-
const { fetchTemplateIndex: fetchTemplateIndex2 } = await import("./registry-client-
|
|
1658
|
+
const { fetchTemplateIndex: fetchTemplateIndex2 } = await import("./registry-client-RMES4CL5.js");
|
|
1658
1659
|
const index = await fetchTemplateIndex2();
|
|
1659
1660
|
const { choice } = await prompts({
|
|
1660
1661
|
type: "select",
|
|
@@ -1705,7 +1706,7 @@ async function initTemplate(slug, options) {
|
|
|
1705
1706
|
const configPath = path6.join(resolved, "axis.config.json");
|
|
1706
1707
|
if (!await fs6.pathExists(configPath)) {
|
|
1707
1708
|
const axisConfig = {
|
|
1708
|
-
$schema: "https://
|
|
1709
|
+
$schema: "https://axis.minu.best/schema.json",
|
|
1709
1710
|
template: selectedSlug,
|
|
1710
1711
|
componentsDir: "./components/ui",
|
|
1711
1712
|
tailwindConfig: "./tailwind.config.ts",
|
|
@@ -2102,7 +2103,7 @@ function registerCheckCommand(program2) {
|
|
|
2102
2103
|
}
|
|
2103
2104
|
|
|
2104
2105
|
// src/index.ts
|
|
2105
|
-
var REGISTRY_URL = process.env.AXIS_REGISTRY_URL || "https://
|
|
2106
|
+
var REGISTRY_URL = process.env.AXIS_REGISTRY_URL || "https://axis.minu.best/r";
|
|
2106
2107
|
var program = new Command();
|
|
2107
2108
|
program.name("axis").description("AXIS Design System CLI - shadcn \uD638\uD658 \uCEF4\uD3EC\uB10C\uD2B8 \uC124\uCE58 \uB3C4\uAD6C").version("0.1.0");
|
|
2108
2109
|
program.command("init").description("\uD504\uB85C\uC81D\uD2B8\uC5D0 AXIS Design System \uCD08\uAE30\uD654").option("-y, --yes", "\uAE30\uBCF8\uAC12\uC73C\uB85C \uC790\uB3D9 \uC124\uC815").action(async (options) => {
|
|
@@ -2132,7 +2133,7 @@ program.command("init").description("\uD504\uB85C\uC81D\uD2B8\uC5D0 AXIS Design
|
|
|
2132
2133
|
}
|
|
2133
2134
|
]);
|
|
2134
2135
|
const axisConfig = {
|
|
2135
|
-
$schema: "https://
|
|
2136
|
+
$schema: "https://axis.minu.best/schema.json",
|
|
2136
2137
|
componentsDir: config.componentsDir,
|
|
2137
2138
|
tailwindConfig: config.tailwindConfig,
|
|
2138
2139
|
globalCss: config.globalCss,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@axis-ds/cli",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.3",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "AXIS Design System - CLI for adding components (shadcn compatible)",
|
|
6
6
|
"publishConfig": {
|
|
@@ -26,6 +26,7 @@
|
|
|
26
26
|
"@types/fs-extra": "^11.0.0",
|
|
27
27
|
"@types/node": "^22.0.0",
|
|
28
28
|
"@types/prompts": "^2.4.0",
|
|
29
|
+
"@vitest/coverage-v8": "^2.1.0",
|
|
29
30
|
"tsup": "^8.0.0",
|
|
30
31
|
"typescript": "^5.7.0",
|
|
31
32
|
"vitest": "^2.1.0"
|
|
@@ -49,6 +50,7 @@
|
|
|
49
50
|
"dev": "tsup src/index.ts src/monet.ts src/v0.ts src/sync/index.ts --format esm --watch",
|
|
50
51
|
"clean": "rm -rf dist",
|
|
51
52
|
"test": "vitest run",
|
|
53
|
+
"test:coverage": "vitest run --coverage",
|
|
52
54
|
"test:watch": "vitest"
|
|
53
55
|
}
|
|
54
56
|
}
|
package/dist/chunk-DYIVW3T5.js
DELETED
|
@@ -1,873 +0,0 @@
|
|
|
1
|
-
// src/sync/index.ts
|
|
2
|
-
import chalk from "chalk";
|
|
3
|
-
import ora from "ora";
|
|
4
|
-
import path2 from "path";
|
|
5
|
-
import fs3 from "fs-extra";
|
|
6
|
-
|
|
7
|
-
// src/sync/parser.ts
|
|
8
|
-
import fs from "fs-extra";
|
|
9
|
-
var IN_PROGRESS_ROW_REGEX = /\|\s*(\d+)\s*\|\s*([^|]+)\|\s*\[?(WI-\d{4})\]?(?:\([^)]*\))?\s*\|\s*([^|]*)\|\s*([^|]*)\|\s*(✅|🔄|⏳)\s*\|/;
|
|
10
|
-
var COMPLETED_ROW_REGEX = /\|\s*(\d+)\s*\|\s*([^|]+)\|\s*\[?(WI-\d{4})\]?(?:\([^)]*\))?\s*\|\s*([^|]*)\|\s*([^|]*)\|/;
|
|
11
|
-
var SECTION_HEADER_REGEX = /^###\s+(진행 중|완료)/;
|
|
12
|
-
async function parseProjectTodo(filePath) {
|
|
13
|
-
const content = await fs.readFile(filePath, "utf-8");
|
|
14
|
-
return parseContent(content);
|
|
15
|
-
}
|
|
16
|
-
function parseContent(content) {
|
|
17
|
-
const lines = content.split("\n");
|
|
18
|
-
const inProgress = [];
|
|
19
|
-
const completed = [];
|
|
20
|
-
const errors = [];
|
|
21
|
-
let currentSection = "none";
|
|
22
|
-
let inTable = false;
|
|
23
|
-
for (let i = 0; i < lines.length; i++) {
|
|
24
|
-
const line = lines[i];
|
|
25
|
-
const lineNumber = i + 1;
|
|
26
|
-
const sectionMatch = line.match(SECTION_HEADER_REGEX);
|
|
27
|
-
if (sectionMatch) {
|
|
28
|
-
currentSection = sectionMatch[1] === "\uC9C4\uD589 \uC911" ? "inProgress" : "completed";
|
|
29
|
-
inTable = false;
|
|
30
|
-
continue;
|
|
31
|
-
}
|
|
32
|
-
if (line.trim().startsWith("| #") || line.trim().startsWith("|---")) {
|
|
33
|
-
inTable = true;
|
|
34
|
-
continue;
|
|
35
|
-
}
|
|
36
|
-
if (line.trim() === "" || !line.trim().startsWith("|")) {
|
|
37
|
-
inTable = false;
|
|
38
|
-
continue;
|
|
39
|
-
}
|
|
40
|
-
if (inTable && currentSection !== "none") {
|
|
41
|
-
try {
|
|
42
|
-
const workItem = parseTableRow(line, currentSection);
|
|
43
|
-
if (workItem) {
|
|
44
|
-
if (currentSection === "inProgress") {
|
|
45
|
-
inProgress.push(workItem);
|
|
46
|
-
} else {
|
|
47
|
-
completed.push(workItem);
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
} catch (error) {
|
|
51
|
-
if (line.includes("WI-")) {
|
|
52
|
-
errors.push({
|
|
53
|
-
line: lineNumber,
|
|
54
|
-
content: line,
|
|
55
|
-
message: error.message
|
|
56
|
-
});
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
return { inProgress, completed, errors };
|
|
62
|
-
}
|
|
63
|
-
function parseTableRow(line, section) {
|
|
64
|
-
if (section === "inProgress") {
|
|
65
|
-
const match = line.match(IN_PROGRESS_ROW_REGEX);
|
|
66
|
-
if (!match) {
|
|
67
|
-
if (!line.includes("WI-")) {
|
|
68
|
-
return null;
|
|
69
|
-
}
|
|
70
|
-
throw new Error("\uC9C4\uD589 \uC911 \uD14C\uC774\uBE14 \uD589 \uD30C\uC2F1 \uC2E4\uD328");
|
|
71
|
-
}
|
|
72
|
-
const [, index, title, wiId, phase, priority, status] = match;
|
|
73
|
-
const linkMatch = line.match(/\[WI-\d{4}\]\(([^)]+)\)/);
|
|
74
|
-
return {
|
|
75
|
-
index: parseInt(index, 10),
|
|
76
|
-
title: title.trim(),
|
|
77
|
-
wiId: wiId.trim(),
|
|
78
|
-
phase: phase.trim(),
|
|
79
|
-
priority: priority.trim(),
|
|
80
|
-
status: status.trim(),
|
|
81
|
-
link: linkMatch ? linkMatch[1] : void 0
|
|
82
|
-
};
|
|
83
|
-
} else {
|
|
84
|
-
const match = line.match(COMPLETED_ROW_REGEX);
|
|
85
|
-
if (!match) {
|
|
86
|
-
if (!line.includes("WI-")) {
|
|
87
|
-
return null;
|
|
88
|
-
}
|
|
89
|
-
throw new Error("\uC644\uB8CC \uD14C\uC774\uBE14 \uD589 \uD30C\uC2F1 \uC2E4\uD328");
|
|
90
|
-
}
|
|
91
|
-
const [, index, title, wiId, phase] = match;
|
|
92
|
-
const linkMatch = line.match(/\[WI-\d{4}\]\(([^)]+)\)/);
|
|
93
|
-
return {
|
|
94
|
-
index: parseInt(index, 10),
|
|
95
|
-
title: title.trim(),
|
|
96
|
-
wiId: wiId.trim(),
|
|
97
|
-
phase: phase.trim(),
|
|
98
|
-
priority: "",
|
|
99
|
-
status: "\u2705",
|
|
100
|
-
link: linkMatch ? linkMatch[1] : void 0
|
|
101
|
-
};
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
function extractAllWorkItems(result) {
|
|
105
|
-
const allItems = [...result.inProgress, ...result.completed];
|
|
106
|
-
const seen = /* @__PURE__ */ new Set();
|
|
107
|
-
return allItems.filter((item) => {
|
|
108
|
-
if (seen.has(item.wiId)) {
|
|
109
|
-
return false;
|
|
110
|
-
}
|
|
111
|
-
seen.add(item.wiId);
|
|
112
|
-
return true;
|
|
113
|
-
});
|
|
114
|
-
}
|
|
115
|
-
function filterByWiIds(items, wiIds) {
|
|
116
|
-
const targetSet = new Set(wiIds.map((id) => id.toUpperCase()));
|
|
117
|
-
return items.filter((item) => targetSet.has(item.wiId.toUpperCase()));
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
// src/sync/github-client.ts
|
|
121
|
-
import { execSync } from "child_process";
|
|
122
|
-
var GITHUB_GRAPHQL_URL = "https://api.github.com/graphql";
|
|
123
|
-
var GitHubClient = class {
|
|
124
|
-
token;
|
|
125
|
-
owner;
|
|
126
|
-
repo;
|
|
127
|
-
constructor(owner, repo, token) {
|
|
128
|
-
this.owner = owner;
|
|
129
|
-
this.repo = repo;
|
|
130
|
-
this.token = token || this.getTokenFromGhCli();
|
|
131
|
-
}
|
|
132
|
-
/**
|
|
133
|
-
* gh CLI에서 토큰 가져오기
|
|
134
|
-
*/
|
|
135
|
-
getTokenFromGhCli() {
|
|
136
|
-
try {
|
|
137
|
-
const token = execSync("gh auth token", {
|
|
138
|
-
encoding: "utf-8",
|
|
139
|
-
stdio: ["pipe", "pipe", "ignore"]
|
|
140
|
-
}).trim();
|
|
141
|
-
if (!token) {
|
|
142
|
-
throw new Error("\uD1A0\uD070\uC774 \uBE44\uC5B4\uC788\uC2B5\uB2C8\uB2E4.");
|
|
143
|
-
}
|
|
144
|
-
return token;
|
|
145
|
-
} catch {
|
|
146
|
-
throw new Error(
|
|
147
|
-
"GitHub \uD1A0\uD070\uC744 \uAC00\uC838\uC62C \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. gh CLI \uC124\uCE58 \uBC0F \uC778\uC99D\uC774 \uD544\uC694\uD569\uB2C8\uB2E4.\n \uC124\uCE58: https://cli.github.com\n \uC778\uC99D: gh auth login"
|
|
148
|
-
);
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
/**
|
|
152
|
-
* GraphQL 요청 실행
|
|
153
|
-
*/
|
|
154
|
-
async graphql(query, variables) {
|
|
155
|
-
const response = await fetch(GITHUB_GRAPHQL_URL, {
|
|
156
|
-
method: "POST",
|
|
157
|
-
headers: {
|
|
158
|
-
"Authorization": `Bearer ${this.token}`,
|
|
159
|
-
"Content-Type": "application/json",
|
|
160
|
-
"Accept": "application/json"
|
|
161
|
-
},
|
|
162
|
-
body: JSON.stringify({ query, variables })
|
|
163
|
-
});
|
|
164
|
-
if (!response.ok) {
|
|
165
|
-
throw new Error(`GitHub API \uC624\uB958: ${response.status} ${response.statusText}`);
|
|
166
|
-
}
|
|
167
|
-
const result = await response.json();
|
|
168
|
-
if (result.errors && result.errors.length > 0) {
|
|
169
|
-
throw new Error(`GraphQL \uC624\uB958: ${result.errors.map((e) => e.message).join(", ")}`);
|
|
170
|
-
}
|
|
171
|
-
return result.data;
|
|
172
|
-
}
|
|
173
|
-
/**
|
|
174
|
-
* Project V2 정보 조회
|
|
175
|
-
*/
|
|
176
|
-
async getProjectInfo(projectNumber) {
|
|
177
|
-
const query = `
|
|
178
|
-
query GetProject($owner: String!, $repo: String!, $number: Int!) {
|
|
179
|
-
repository(owner: $owner, name: $repo) {
|
|
180
|
-
projectV2(number: $number) {
|
|
181
|
-
id
|
|
182
|
-
title
|
|
183
|
-
url
|
|
184
|
-
field(name: "Status") {
|
|
185
|
-
... on ProjectV2SingleSelectField {
|
|
186
|
-
id
|
|
187
|
-
options {
|
|
188
|
-
id
|
|
189
|
-
name
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
`;
|
|
197
|
-
const data = await this.graphql(query, {
|
|
198
|
-
owner: this.owner,
|
|
199
|
-
repo: this.repo,
|
|
200
|
-
number: projectNumber
|
|
201
|
-
});
|
|
202
|
-
const project = data.repository.projectV2;
|
|
203
|
-
const statusField = project.field;
|
|
204
|
-
const options = {
|
|
205
|
-
todo: "",
|
|
206
|
-
inProgress: "",
|
|
207
|
-
done: ""
|
|
208
|
-
};
|
|
209
|
-
for (const opt of statusField.options) {
|
|
210
|
-
const nameLower = opt.name.toLowerCase();
|
|
211
|
-
if (nameLower === "todo" || nameLower === "to do") {
|
|
212
|
-
options.todo = opt.id;
|
|
213
|
-
} else if (nameLower === "in progress" || nameLower === "in-progress") {
|
|
214
|
-
options.inProgress = opt.id;
|
|
215
|
-
} else if (nameLower === "done" || nameLower === "completed") {
|
|
216
|
-
options.done = opt.id;
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
return {
|
|
220
|
-
id: project.id,
|
|
221
|
-
title: project.title,
|
|
222
|
-
url: project.url,
|
|
223
|
-
statusField: {
|
|
224
|
-
id: statusField.id,
|
|
225
|
-
options
|
|
226
|
-
}
|
|
227
|
-
};
|
|
228
|
-
}
|
|
229
|
-
/**
|
|
230
|
-
* 프로젝트의 모든 아이템 조회
|
|
231
|
-
*/
|
|
232
|
-
async getProjectItems(projectId) {
|
|
233
|
-
const query = `
|
|
234
|
-
query GetProjectItems($projectId: ID!, $cursor: String) {
|
|
235
|
-
node(id: $projectId) {
|
|
236
|
-
... on ProjectV2 {
|
|
237
|
-
items(first: 100, after: $cursor) {
|
|
238
|
-
pageInfo {
|
|
239
|
-
hasNextPage
|
|
240
|
-
endCursor
|
|
241
|
-
}
|
|
242
|
-
nodes {
|
|
243
|
-
id
|
|
244
|
-
content {
|
|
245
|
-
... on Issue {
|
|
246
|
-
id
|
|
247
|
-
number
|
|
248
|
-
title
|
|
249
|
-
url
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
fieldValueByName(name: "Status") {
|
|
253
|
-
... on ProjectV2ItemFieldSingleSelectValue {
|
|
254
|
-
name
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
`;
|
|
263
|
-
const items = [];
|
|
264
|
-
let cursor = null;
|
|
265
|
-
let hasNextPage = true;
|
|
266
|
-
while (hasNextPage) {
|
|
267
|
-
const data = await this.graphql(query, {
|
|
268
|
-
projectId,
|
|
269
|
-
cursor
|
|
270
|
-
});
|
|
271
|
-
const itemsData = data.node.items;
|
|
272
|
-
for (const node of itemsData.nodes) {
|
|
273
|
-
items.push({
|
|
274
|
-
id: node.id,
|
|
275
|
-
content: node.content,
|
|
276
|
-
fieldValues: {
|
|
277
|
-
status: node.fieldValueByName?.name
|
|
278
|
-
}
|
|
279
|
-
});
|
|
280
|
-
}
|
|
281
|
-
hasNextPage = itemsData.pageInfo.hasNextPage;
|
|
282
|
-
cursor = itemsData.pageInfo.endCursor;
|
|
283
|
-
}
|
|
284
|
-
return items;
|
|
285
|
-
}
|
|
286
|
-
/**
|
|
287
|
-
* Issue 생성
|
|
288
|
-
*/
|
|
289
|
-
async createIssue(title, body, labels) {
|
|
290
|
-
const query = `
|
|
291
|
-
mutation CreateIssue($repositoryId: ID!, $title: String!, $body: String, $labelIds: [ID!]) {
|
|
292
|
-
createIssue(input: {
|
|
293
|
-
repositoryId: $repositoryId
|
|
294
|
-
title: $title
|
|
295
|
-
body: $body
|
|
296
|
-
labelIds: $labelIds
|
|
297
|
-
}) {
|
|
298
|
-
issue {
|
|
299
|
-
id
|
|
300
|
-
number
|
|
301
|
-
}
|
|
302
|
-
}
|
|
303
|
-
}
|
|
304
|
-
`;
|
|
305
|
-
const repoQuery = `
|
|
306
|
-
query GetRepoId($owner: String!, $repo: String!) {
|
|
307
|
-
repository(owner: $owner, name: $repo) {
|
|
308
|
-
id
|
|
309
|
-
}
|
|
310
|
-
}
|
|
311
|
-
`;
|
|
312
|
-
const repoData = await this.graphql(repoQuery, {
|
|
313
|
-
owner: this.owner,
|
|
314
|
-
repo: this.repo
|
|
315
|
-
});
|
|
316
|
-
const data = await this.graphql(query, {
|
|
317
|
-
repositoryId: repoData.repository.id,
|
|
318
|
-
title,
|
|
319
|
-
body,
|
|
320
|
-
labelIds: labels
|
|
321
|
-
});
|
|
322
|
-
return data.createIssue.issue;
|
|
323
|
-
}
|
|
324
|
-
/**
|
|
325
|
-
* Issue를 Project에 추가
|
|
326
|
-
*/
|
|
327
|
-
async addIssueToProject(projectId, issueId) {
|
|
328
|
-
const query = `
|
|
329
|
-
mutation AddToProject($projectId: ID!, $contentId: ID!) {
|
|
330
|
-
addProjectV2ItemById(input: {
|
|
331
|
-
projectId: $projectId
|
|
332
|
-
contentId: $contentId
|
|
333
|
-
}) {
|
|
334
|
-
item {
|
|
335
|
-
id
|
|
336
|
-
}
|
|
337
|
-
}
|
|
338
|
-
}
|
|
339
|
-
`;
|
|
340
|
-
const data = await this.graphql(query, {
|
|
341
|
-
projectId,
|
|
342
|
-
contentId: issueId
|
|
343
|
-
});
|
|
344
|
-
return data.addProjectV2ItemById.item.id;
|
|
345
|
-
}
|
|
346
|
-
/**
|
|
347
|
-
* Project Item의 Status 업데이트
|
|
348
|
-
*/
|
|
349
|
-
async updateItemStatus(projectId, itemId, fieldId, optionId) {
|
|
350
|
-
const query = `
|
|
351
|
-
mutation UpdateStatus($projectId: ID!, $itemId: ID!, $fieldId: ID!, $optionId: String!) {
|
|
352
|
-
updateProjectV2ItemFieldValue(input: {
|
|
353
|
-
projectId: $projectId
|
|
354
|
-
itemId: $itemId
|
|
355
|
-
fieldId: $fieldId
|
|
356
|
-
value: { singleSelectOptionId: $optionId }
|
|
357
|
-
}) {
|
|
358
|
-
projectV2Item {
|
|
359
|
-
id
|
|
360
|
-
}
|
|
361
|
-
}
|
|
362
|
-
}
|
|
363
|
-
`;
|
|
364
|
-
await this.graphql(query, {
|
|
365
|
-
projectId,
|
|
366
|
-
itemId,
|
|
367
|
-
fieldId,
|
|
368
|
-
optionId
|
|
369
|
-
});
|
|
370
|
-
}
|
|
371
|
-
/**
|
|
372
|
-
* Issue 번호로 Issue 정보 조회
|
|
373
|
-
*/
|
|
374
|
-
async getIssue(issueNumber) {
|
|
375
|
-
const query = `
|
|
376
|
-
query GetIssue($owner: String!, $repo: String!, $number: Int!) {
|
|
377
|
-
repository(owner: $owner, name: $repo) {
|
|
378
|
-
issue(number: $number) {
|
|
379
|
-
id
|
|
380
|
-
title
|
|
381
|
-
state
|
|
382
|
-
}
|
|
383
|
-
}
|
|
384
|
-
}
|
|
385
|
-
`;
|
|
386
|
-
try {
|
|
387
|
-
const data = await this.graphql(query, {
|
|
388
|
-
owner: this.owner,
|
|
389
|
-
repo: this.repo,
|
|
390
|
-
number: issueNumber
|
|
391
|
-
});
|
|
392
|
-
return data.repository.issue;
|
|
393
|
-
} catch {
|
|
394
|
-
return null;
|
|
395
|
-
}
|
|
396
|
-
}
|
|
397
|
-
};
|
|
398
|
-
|
|
399
|
-
// src/sync/mapper.ts
|
|
400
|
-
import fs2 from "fs-extra";
|
|
401
|
-
import path from "path";
|
|
402
|
-
var DEFAULT_MAPPING = {
|
|
403
|
-
version: "1.0.0",
|
|
404
|
-
repository: {
|
|
405
|
-
owner: "thoughtandaction",
|
|
406
|
-
repo: "axis"
|
|
407
|
-
},
|
|
408
|
-
project: {
|
|
409
|
-
number: 4
|
|
410
|
-
},
|
|
411
|
-
mappings: [],
|
|
412
|
-
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
413
|
-
};
|
|
414
|
-
var WiMapper = class {
|
|
415
|
-
mappingPath;
|
|
416
|
-
mapping;
|
|
417
|
-
constructor(mappingPath) {
|
|
418
|
-
this.mappingPath = mappingPath;
|
|
419
|
-
this.mapping = { ...DEFAULT_MAPPING };
|
|
420
|
-
}
|
|
421
|
-
/**
|
|
422
|
-
* 매핑 파일 로드
|
|
423
|
-
*/
|
|
424
|
-
async load() {
|
|
425
|
-
if (await fs2.pathExists(this.mappingPath)) {
|
|
426
|
-
try {
|
|
427
|
-
this.mapping = await fs2.readJSON(this.mappingPath);
|
|
428
|
-
} catch {
|
|
429
|
-
console.warn("\uB9E4\uD551 \uD30C\uC77C \uB85C\uB4DC \uC2E4\uD328, \uAE30\uBCF8\uAC12 \uC0AC\uC6A9");
|
|
430
|
-
this.mapping = { ...DEFAULT_MAPPING };
|
|
431
|
-
}
|
|
432
|
-
}
|
|
433
|
-
}
|
|
434
|
-
/**
|
|
435
|
-
* 매핑 파일 저장
|
|
436
|
-
*/
|
|
437
|
-
async save() {
|
|
438
|
-
this.mapping.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
439
|
-
await fs2.ensureDir(path.dirname(this.mappingPath));
|
|
440
|
-
await fs2.writeJSON(this.mappingPath, this.mapping, { spaces: 2 });
|
|
441
|
-
}
|
|
442
|
-
/**
|
|
443
|
-
* 저장소 정보 반환
|
|
444
|
-
*/
|
|
445
|
-
getRepository() {
|
|
446
|
-
return this.mapping.repository;
|
|
447
|
-
}
|
|
448
|
-
/**
|
|
449
|
-
* 프로젝트 번호 반환
|
|
450
|
-
*/
|
|
451
|
-
getProjectNumber() {
|
|
452
|
-
return this.mapping.project.number;
|
|
453
|
-
}
|
|
454
|
-
/**
|
|
455
|
-
* 프로젝트 ID 설정
|
|
456
|
-
*/
|
|
457
|
-
setProjectId(id) {
|
|
458
|
-
this.mapping.project.id = id;
|
|
459
|
-
}
|
|
460
|
-
/**
|
|
461
|
-
* WI ID로 매핑 찾기
|
|
462
|
-
*/
|
|
463
|
-
findByWiId(wiId) {
|
|
464
|
-
return this.mapping.mappings.find(
|
|
465
|
-
(m) => m.wiId.toUpperCase() === wiId.toUpperCase()
|
|
466
|
-
);
|
|
467
|
-
}
|
|
468
|
-
/**
|
|
469
|
-
* Issue 번호로 매핑 찾기
|
|
470
|
-
*/
|
|
471
|
-
findByIssueNumber(issueNumber) {
|
|
472
|
-
return this.mapping.mappings.find((m) => m.issueNumber === issueNumber);
|
|
473
|
-
}
|
|
474
|
-
/**
|
|
475
|
-
* 매핑 추가 또는 업데이트
|
|
476
|
-
*/
|
|
477
|
-
upsertMapping(wiId, issueNumber, projectItemId, status) {
|
|
478
|
-
const existing = this.findByWiId(wiId);
|
|
479
|
-
if (existing) {
|
|
480
|
-
existing.issueNumber = issueNumber;
|
|
481
|
-
if (projectItemId) {
|
|
482
|
-
existing.projectItemId = projectItemId;
|
|
483
|
-
}
|
|
484
|
-
existing.lastSyncedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
485
|
-
if (status) {
|
|
486
|
-
existing.lastStatus = status;
|
|
487
|
-
}
|
|
488
|
-
} else {
|
|
489
|
-
this.mapping.mappings.push({
|
|
490
|
-
wiId: wiId.toUpperCase(),
|
|
491
|
-
issueNumber,
|
|
492
|
-
projectItemId,
|
|
493
|
-
lastSyncedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
494
|
-
lastStatus: status
|
|
495
|
-
});
|
|
496
|
-
}
|
|
497
|
-
}
|
|
498
|
-
/**
|
|
499
|
-
* 모든 매핑 반환
|
|
500
|
-
*/
|
|
501
|
-
getAllMappings() {
|
|
502
|
-
return [...this.mapping.mappings];
|
|
503
|
-
}
|
|
504
|
-
/**
|
|
505
|
-
* GitHub 프로젝트 아이템에서 WI ID 추출
|
|
506
|
-
*/
|
|
507
|
-
extractWiIdFromItem(item) {
|
|
508
|
-
const title = item.content?.title || "";
|
|
509
|
-
const match = title.match(/\[?(WI-\d{4})\]?/i);
|
|
510
|
-
return match ? match[1].toUpperCase() : null;
|
|
511
|
-
}
|
|
512
|
-
/**
|
|
513
|
-
* 프로젝트 아이템으로 매핑 동기화
|
|
514
|
-
*/
|
|
515
|
-
syncFromProjectItems(items) {
|
|
516
|
-
for (const item of items) {
|
|
517
|
-
if (!item.content) continue;
|
|
518
|
-
const wiId = this.extractWiIdFromItem(item);
|
|
519
|
-
if (wiId) {
|
|
520
|
-
const existing = this.findByWiId(wiId);
|
|
521
|
-
if (existing) {
|
|
522
|
-
existing.projectItemId = item.id;
|
|
523
|
-
} else {
|
|
524
|
-
this.mapping.mappings.push({
|
|
525
|
-
wiId,
|
|
526
|
-
issueNumber: item.content.number,
|
|
527
|
-
projectItemId: item.id,
|
|
528
|
-
lastSyncedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
529
|
-
});
|
|
530
|
-
}
|
|
531
|
-
}
|
|
532
|
-
}
|
|
533
|
-
}
|
|
534
|
-
};
|
|
535
|
-
function generateIssueTitle(workItem) {
|
|
536
|
-
return `[${workItem.wiId}] ${workItem.title}`;
|
|
537
|
-
}
|
|
538
|
-
function generateIssueBody(workItem) {
|
|
539
|
-
const lines = [
|
|
540
|
-
`## Work Item: ${workItem.wiId}`,
|
|
541
|
-
"",
|
|
542
|
-
`**\uC81C\uBAA9**: ${workItem.title}`,
|
|
543
|
-
`**Phase**: ${workItem.phase}`,
|
|
544
|
-
`**\uC6B0\uC120\uC21C\uC704**: ${workItem.priority}`,
|
|
545
|
-
""
|
|
546
|
-
];
|
|
547
|
-
if (workItem.link) {
|
|
548
|
-
lines.push(`**WI \uD3F4\uB354**: [\uBC14\uB85C\uAC00\uAE30](${workItem.link})`);
|
|
549
|
-
lines.push("");
|
|
550
|
-
}
|
|
551
|
-
lines.push("---");
|
|
552
|
-
lines.push("");
|
|
553
|
-
lines.push("> \uC774 Issue\uB294 `project-todo.md`\uC640 \uC790\uB3D9 \uB3D9\uAE30\uD654\uB429\uB2C8\uB2E4.");
|
|
554
|
-
return lines.join("\n");
|
|
555
|
-
}
|
|
556
|
-
|
|
557
|
-
// src/sync/types.ts
|
|
558
|
-
var STATUS_MAP = {
|
|
559
|
-
"\u2705": "Done",
|
|
560
|
-
"\u{1F504}": "In Progress",
|
|
561
|
-
"\u23F3": "Todo"
|
|
562
|
-
};
|
|
563
|
-
|
|
564
|
-
// src/sync/index.ts
|
|
565
|
-
var statusMap = {
|
|
566
|
-
"\u2705": "Done",
|
|
567
|
-
"\u{1F504}": "In Progress",
|
|
568
|
-
"\u23F3": "Todo"
|
|
569
|
-
};
|
|
570
|
-
var DEFAULT_TODO_PATH = "project-todo.md";
|
|
571
|
-
var DEFAULT_MAPPING_PATH = ".github/wi-mapping.json";
|
|
572
|
-
function registerSyncCommand(program) {
|
|
573
|
-
const syncCmd = program.command("sync").description("GitHub Project\uC640 project-todo.md \uB3D9\uAE30\uD654");
|
|
574
|
-
syncCmd.option("--dry-run", "\uBBF8\uB9AC\uBCF4\uAE30 \uBAA8\uB4DC (\uC2E4\uC81C \uBCC0\uACBD \uC5C6\uC74C)").option("--wi <ids...>", "\uD2B9\uC815 WI\uB9CC \uB3D9\uAE30\uD654 (\uC608: WI-0001 WI-0002)").option("-v, --verbose", "\uC0C1\uC138 \uCD9C\uB825").option("--token <token>", "GitHub \uD1A0\uD070 (\uAE30\uBCF8: gh auth token \uC0AC\uC6A9)").action(async (options) => {
|
|
575
|
-
await runSync({
|
|
576
|
-
dryRun: options.dryRun,
|
|
577
|
-
wiIds: options.wi,
|
|
578
|
-
verbose: options.verbose,
|
|
579
|
-
token: options.token
|
|
580
|
-
});
|
|
581
|
-
});
|
|
582
|
-
syncCmd.command("status").description("\uD604\uC7AC \uB3D9\uAE30\uD654 \uC0C1\uD0DC \uD655\uC778").action(async () => {
|
|
583
|
-
await showSyncStatus();
|
|
584
|
-
});
|
|
585
|
-
syncCmd.command("init").description("\uB3D9\uAE30\uD654 \uC124\uC815 \uCD08\uAE30\uD654").option("--owner <owner>", "GitHub \uC800\uC7A5\uC18C \uC18C\uC720\uC790", "thoughtandaction").option("--repo <repo>", "GitHub \uC800\uC7A5\uC18C \uC774\uB984", "axis").option("--project <number>", "GitHub Project \uBC88\uD638", "4").action(async (options) => {
|
|
586
|
-
await initSync(options.owner, options.repo, parseInt(options.project, 10));
|
|
587
|
-
});
|
|
588
|
-
}
|
|
589
|
-
async function runSync(options) {
|
|
590
|
-
const spinner = ora();
|
|
591
|
-
const result = {
|
|
592
|
-
success: true,
|
|
593
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
594
|
-
summary: { total: 0, created: 0, updated: 0, skipped: 0, failed: 0 },
|
|
595
|
-
items: [],
|
|
596
|
-
errors: []
|
|
597
|
-
};
|
|
598
|
-
try {
|
|
599
|
-
spinner.start("project-todo.md \uD30C\uC2F1 \uC911...");
|
|
600
|
-
const todoPath = path2.resolve(DEFAULT_TODO_PATH);
|
|
601
|
-
if (!await fs3.pathExists(todoPath)) {
|
|
602
|
-
throw new Error(`project-todo.md\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${todoPath}`);
|
|
603
|
-
}
|
|
604
|
-
const parseResult = await parseProjectTodo(todoPath);
|
|
605
|
-
let workItems = extractAllWorkItems(parseResult);
|
|
606
|
-
if (options.wiIds && options.wiIds.length > 0) {
|
|
607
|
-
workItems = filterByWiIds(workItems, options.wiIds);
|
|
608
|
-
}
|
|
609
|
-
spinner.succeed(`${workItems.length}\uAC1C Work Item \uD30C\uC2F1 \uC644\uB8CC`);
|
|
610
|
-
if (workItems.length === 0) {
|
|
611
|
-
console.log(chalk.yellow("\n\uB3D9\uAE30\uD654\uD560 Work Item\uC774 \uC5C6\uC2B5\uB2C8\uB2E4."));
|
|
612
|
-
return;
|
|
613
|
-
}
|
|
614
|
-
spinner.start("\uB9E4\uD551 \uD30C\uC77C \uB85C\uB4DC \uC911...");
|
|
615
|
-
const mapper = new WiMapper(path2.resolve(DEFAULT_MAPPING_PATH));
|
|
616
|
-
await mapper.load();
|
|
617
|
-
spinner.succeed("\uB9E4\uD551 \uD30C\uC77C \uB85C\uB4DC \uC644\uB8CC");
|
|
618
|
-
spinner.start("GitHub \uC5F0\uACB0 \uC911...");
|
|
619
|
-
const { owner, repo } = mapper.getRepository();
|
|
620
|
-
const client = new GitHubClient(owner, repo, options.token);
|
|
621
|
-
spinner.succeed("GitHub \uC5F0\uACB0 \uC644\uB8CC");
|
|
622
|
-
spinner.start("GitHub Project \uC815\uBCF4 \uC870\uD68C \uC911...");
|
|
623
|
-
const projectNumber = mapper.getProjectNumber();
|
|
624
|
-
const projectInfo = await client.getProjectInfo(projectNumber);
|
|
625
|
-
mapper.setProjectId(projectInfo.id);
|
|
626
|
-
spinner.succeed(`Project: ${projectInfo.title}`);
|
|
627
|
-
spinner.start("\uD504\uB85C\uC81D\uD2B8 \uC544\uC774\uD15C \uC870\uD68C \uC911...");
|
|
628
|
-
const projectItems = await client.getProjectItems(projectInfo.id);
|
|
629
|
-
mapper.syncFromProjectItems(projectItems);
|
|
630
|
-
spinner.succeed(`${projectItems.length}\uAC1C \uD504\uB85C\uC81D\uD2B8 \uC544\uC774\uD15C \uC870\uD68C \uC644\uB8CC`);
|
|
631
|
-
console.log(chalk.blue("\n\u{1F4E6} \uB3D9\uAE30\uD654 \uC2DC\uC791\n"));
|
|
632
|
-
if (options.dryRun) {
|
|
633
|
-
console.log(chalk.yellow("\u{1F50D} Dry Run \uBAA8\uB4DC - \uC2E4\uC81C \uBCC0\uACBD \uC5C6\uC74C\n"));
|
|
634
|
-
}
|
|
635
|
-
result.summary.total = workItems.length;
|
|
636
|
-
for (const workItem of workItems) {
|
|
637
|
-
const itemResult = await syncWorkItem(
|
|
638
|
-
workItem,
|
|
639
|
-
mapper,
|
|
640
|
-
client,
|
|
641
|
-
projectInfo,
|
|
642
|
-
options
|
|
643
|
-
);
|
|
644
|
-
result.items.push(itemResult);
|
|
645
|
-
switch (itemResult.action) {
|
|
646
|
-
case "create":
|
|
647
|
-
result.summary.created++;
|
|
648
|
-
break;
|
|
649
|
-
case "update":
|
|
650
|
-
result.summary.updated++;
|
|
651
|
-
break;
|
|
652
|
-
case "skip":
|
|
653
|
-
result.summary.skipped++;
|
|
654
|
-
break;
|
|
655
|
-
case "error":
|
|
656
|
-
result.summary.failed++;
|
|
657
|
-
result.errors.push(itemResult.error || "\uC54C \uC218 \uC5C6\uB294 \uC624\uB958");
|
|
658
|
-
break;
|
|
659
|
-
}
|
|
660
|
-
printItemResult(itemResult, options.verbose);
|
|
661
|
-
}
|
|
662
|
-
if (!options.dryRun) {
|
|
663
|
-
spinner.start("\uB9E4\uD551 \uD30C\uC77C \uC800\uC7A5 \uC911...");
|
|
664
|
-
await mapper.save();
|
|
665
|
-
spinner.succeed("\uB9E4\uD551 \uD30C\uC77C \uC800\uC7A5 \uC644\uB8CC");
|
|
666
|
-
}
|
|
667
|
-
printSummary(result);
|
|
668
|
-
} catch (error) {
|
|
669
|
-
spinner.fail(`\uC624\uB958 \uBC1C\uC0DD: ${error.message}`);
|
|
670
|
-
result.success = false;
|
|
671
|
-
result.errors.push(error.message);
|
|
672
|
-
}
|
|
673
|
-
}
|
|
674
|
-
async function syncWorkItem(workItem, mapper, client, projectInfo, options) {
|
|
675
|
-
const mapping = mapper.findByWiId(workItem.wiId);
|
|
676
|
-
const targetStatus = statusMap[workItem.status];
|
|
677
|
-
const statusOptionId = targetStatus === "Done" ? projectInfo.statusField.options.done : targetStatus === "In Progress" ? projectInfo.statusField.options.inProgress : projectInfo.statusField.options.todo;
|
|
678
|
-
try {
|
|
679
|
-
if (!mapping) {
|
|
680
|
-
if (options.dryRun) {
|
|
681
|
-
return {
|
|
682
|
-
wiId: workItem.wiId,
|
|
683
|
-
action: "create",
|
|
684
|
-
newStatus: workItem.status
|
|
685
|
-
};
|
|
686
|
-
}
|
|
687
|
-
const issue = await client.createIssue(
|
|
688
|
-
generateIssueTitle(workItem),
|
|
689
|
-
generateIssueBody(workItem)
|
|
690
|
-
);
|
|
691
|
-
const itemId = await client.addIssueToProject(projectInfo.id, issue.id);
|
|
692
|
-
await client.updateItemStatus(
|
|
693
|
-
projectInfo.id,
|
|
694
|
-
itemId,
|
|
695
|
-
projectInfo.statusField.id,
|
|
696
|
-
statusOptionId
|
|
697
|
-
);
|
|
698
|
-
mapper.upsertMapping(workItem.wiId, issue.number, itemId, workItem.status);
|
|
699
|
-
return {
|
|
700
|
-
wiId: workItem.wiId,
|
|
701
|
-
action: "create",
|
|
702
|
-
newStatus: workItem.status,
|
|
703
|
-
issueNumber: issue.number,
|
|
704
|
-
projectItemId: itemId
|
|
705
|
-
};
|
|
706
|
-
} else {
|
|
707
|
-
const previousStatus = mapping.lastStatus;
|
|
708
|
-
if (previousStatus === workItem.status) {
|
|
709
|
-
return {
|
|
710
|
-
wiId: workItem.wiId,
|
|
711
|
-
action: "skip",
|
|
712
|
-
previousStatus,
|
|
713
|
-
newStatus: workItem.status,
|
|
714
|
-
issueNumber: mapping.issueNumber,
|
|
715
|
-
projectItemId: mapping.projectItemId
|
|
716
|
-
};
|
|
717
|
-
}
|
|
718
|
-
if (options.dryRun) {
|
|
719
|
-
return {
|
|
720
|
-
wiId: workItem.wiId,
|
|
721
|
-
action: "update",
|
|
722
|
-
previousStatus,
|
|
723
|
-
newStatus: workItem.status,
|
|
724
|
-
issueNumber: mapping.issueNumber
|
|
725
|
-
};
|
|
726
|
-
}
|
|
727
|
-
let itemId = mapping.projectItemId;
|
|
728
|
-
if (!itemId) {
|
|
729
|
-
const issue = await client.getIssue(mapping.issueNumber);
|
|
730
|
-
if (issue) {
|
|
731
|
-
itemId = await client.addIssueToProject(projectInfo.id, issue.id);
|
|
732
|
-
}
|
|
733
|
-
}
|
|
734
|
-
if (itemId) {
|
|
735
|
-
await client.updateItemStatus(
|
|
736
|
-
projectInfo.id,
|
|
737
|
-
itemId,
|
|
738
|
-
projectInfo.statusField.id,
|
|
739
|
-
statusOptionId
|
|
740
|
-
);
|
|
741
|
-
mapper.upsertMapping(workItem.wiId, mapping.issueNumber, itemId, workItem.status);
|
|
742
|
-
}
|
|
743
|
-
return {
|
|
744
|
-
wiId: workItem.wiId,
|
|
745
|
-
action: "update",
|
|
746
|
-
previousStatus,
|
|
747
|
-
newStatus: workItem.status,
|
|
748
|
-
issueNumber: mapping.issueNumber,
|
|
749
|
-
projectItemId: itemId
|
|
750
|
-
};
|
|
751
|
-
}
|
|
752
|
-
} catch (error) {
|
|
753
|
-
return {
|
|
754
|
-
wiId: workItem.wiId,
|
|
755
|
-
action: "error",
|
|
756
|
-
newStatus: workItem.status,
|
|
757
|
-
error: error.message
|
|
758
|
-
};
|
|
759
|
-
}
|
|
760
|
-
}
|
|
761
|
-
function printItemResult(result, verbose) {
|
|
762
|
-
const statusIcons = {
|
|
763
|
-
create: chalk.green("+"),
|
|
764
|
-
update: chalk.yellow("~"),
|
|
765
|
-
skip: chalk.gray("-"),
|
|
766
|
-
error: chalk.red("\u2717")
|
|
767
|
-
};
|
|
768
|
-
const icon = statusIcons[result.action];
|
|
769
|
-
const wiId = chalk.cyan(result.wiId);
|
|
770
|
-
const status = result.newStatus;
|
|
771
|
-
let message = `${icon} ${wiId} ${status}`;
|
|
772
|
-
if (result.action === "update" && result.previousStatus) {
|
|
773
|
-
message += chalk.gray(` (${result.previousStatus} \u2192 ${result.newStatus})`);
|
|
774
|
-
}
|
|
775
|
-
if (result.action === "create" && result.issueNumber) {
|
|
776
|
-
message += chalk.gray(` \u2192 Issue #${result.issueNumber}`);
|
|
777
|
-
}
|
|
778
|
-
if (result.action === "error") {
|
|
779
|
-
message += chalk.red(` - ${result.error}`);
|
|
780
|
-
}
|
|
781
|
-
console.log(` ${message}`);
|
|
782
|
-
}
|
|
783
|
-
function printSummary(result) {
|
|
784
|
-
console.log(chalk.blue("\n\u{1F4CA} \uB3D9\uAE30\uD654 \uACB0\uACFC\n"));
|
|
785
|
-
console.log(` \uC804\uCCB4: ${result.summary.total}`);
|
|
786
|
-
console.log(chalk.green(` \uC0DD\uC131: ${result.summary.created}`));
|
|
787
|
-
console.log(chalk.yellow(` \uC5C5\uB370\uC774\uD2B8: ${result.summary.updated}`));
|
|
788
|
-
console.log(chalk.gray(` \uC2A4\uD0B5: ${result.summary.skipped}`));
|
|
789
|
-
if (result.summary.failed > 0) {
|
|
790
|
-
console.log(chalk.red(` \uC2E4\uD328: ${result.summary.failed}`));
|
|
791
|
-
}
|
|
792
|
-
console.log();
|
|
793
|
-
}
|
|
794
|
-
async function showSyncStatus() {
|
|
795
|
-
const spinner = ora();
|
|
796
|
-
try {
|
|
797
|
-
spinner.start("\uC0C1\uD0DC \uD655\uC778 \uC911...");
|
|
798
|
-
const mapper = new WiMapper(path2.resolve(DEFAULT_MAPPING_PATH));
|
|
799
|
-
await mapper.load();
|
|
800
|
-
const todoPath = path2.resolve(DEFAULT_TODO_PATH);
|
|
801
|
-
if (!await fs3.pathExists(todoPath)) {
|
|
802
|
-
throw new Error(`project-todo.md\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${todoPath}`);
|
|
803
|
-
}
|
|
804
|
-
const parseResult = await parseProjectTodo(todoPath);
|
|
805
|
-
const workItems = extractAllWorkItems(parseResult);
|
|
806
|
-
spinner.succeed("\uC0C1\uD0DC \uD655\uC778 \uC644\uB8CC");
|
|
807
|
-
console.log(chalk.blue("\n\u{1F4CB} \uB3D9\uAE30\uD654 \uC0C1\uD0DC\n"));
|
|
808
|
-
const { owner, repo } = mapper.getRepository();
|
|
809
|
-
const projectNumber = mapper.getProjectNumber();
|
|
810
|
-
console.log(chalk.gray(`\uC800\uC7A5\uC18C: ${owner}/${repo}`));
|
|
811
|
-
console.log(chalk.gray(`\uD504\uB85C\uC81D\uD2B8: #${projectNumber}`));
|
|
812
|
-
console.log();
|
|
813
|
-
console.log(chalk.bold("Work Items:"));
|
|
814
|
-
let synced = 0;
|
|
815
|
-
let unsynced = 0;
|
|
816
|
-
for (const wi of workItems) {
|
|
817
|
-
const mapping = mapper.findByWiId(wi.wiId);
|
|
818
|
-
const icon = mapping ? chalk.green("\u2713") : chalk.yellow("\u25CB");
|
|
819
|
-
const issueInfo = mapping ? chalk.gray(` \u2192 #${mapping.issueNumber}`) : chalk.yellow(" (\uBBF8\uB3D9\uAE30\uD654)");
|
|
820
|
-
console.log(` ${icon} ${chalk.cyan(wi.wiId)} ${wi.status} ${wi.title}${issueInfo}`);
|
|
821
|
-
if (mapping) {
|
|
822
|
-
synced++;
|
|
823
|
-
} else {
|
|
824
|
-
unsynced++;
|
|
825
|
-
}
|
|
826
|
-
}
|
|
827
|
-
console.log();
|
|
828
|
-
console.log(chalk.gray(`\uB3D9\uAE30\uD654\uB428: ${synced}, \uBBF8\uB3D9\uAE30\uD654: ${unsynced}`));
|
|
829
|
-
console.log();
|
|
830
|
-
} catch (error) {
|
|
831
|
-
spinner.fail(`\uC624\uB958: ${error.message}`);
|
|
832
|
-
}
|
|
833
|
-
}
|
|
834
|
-
async function initSync(owner, repo, projectNumber) {
|
|
835
|
-
const spinner = ora();
|
|
836
|
-
try {
|
|
837
|
-
spinner.start("\uC124\uC815 \uCD08\uAE30\uD654 \uC911...");
|
|
838
|
-
const mappingPath = path2.resolve(DEFAULT_MAPPING_PATH);
|
|
839
|
-
const mapping = {
|
|
840
|
-
version: "1.0.0",
|
|
841
|
-
repository: { owner, repo },
|
|
842
|
-
project: { number: projectNumber },
|
|
843
|
-
mappings: [],
|
|
844
|
-
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
845
|
-
};
|
|
846
|
-
await fs3.ensureDir(path2.dirname(mappingPath));
|
|
847
|
-
await fs3.writeJSON(mappingPath, mapping, { spaces: 2 });
|
|
848
|
-
spinner.succeed("\uC124\uC815 \uCD08\uAE30\uD654 \uC644\uB8CC");
|
|
849
|
-
console.log(chalk.blue("\n\u2713 \uB3D9\uAE30\uD654 \uC124\uC815 \uC644\uB8CC\n"));
|
|
850
|
-
console.log(chalk.gray(` \uB9E4\uD551 \uD30C\uC77C: ${DEFAULT_MAPPING_PATH}`));
|
|
851
|
-
console.log(chalk.gray(` \uC800\uC7A5\uC18C: ${owner}/${repo}`));
|
|
852
|
-
console.log(chalk.gray(` \uD504\uB85C\uC81D\uD2B8: #${projectNumber}`));
|
|
853
|
-
console.log();
|
|
854
|
-
console.log(chalk.cyan("\uB2E4\uC74C \uBA85\uB839\uC5B4\uB85C \uB3D9\uAE30\uD654\uB97C \uC2DC\uC791\uD558\uC138\uC694:"));
|
|
855
|
-
console.log(chalk.cyan(" npx axis-cli sync"));
|
|
856
|
-
console.log();
|
|
857
|
-
} catch (error) {
|
|
858
|
-
spinner.fail(`\uC624\uB958: ${error.message}`);
|
|
859
|
-
}
|
|
860
|
-
}
|
|
861
|
-
|
|
862
|
-
export {
|
|
863
|
-
parseProjectTodo,
|
|
864
|
-
parseContent,
|
|
865
|
-
extractAllWorkItems,
|
|
866
|
-
filterByWiIds,
|
|
867
|
-
GitHubClient,
|
|
868
|
-
WiMapper,
|
|
869
|
-
generateIssueTitle,
|
|
870
|
-
generateIssueBody,
|
|
871
|
-
STATUS_MAP,
|
|
872
|
-
registerSyncCommand
|
|
873
|
-
};
|