sdd-tool 1.4.0 → 1.4.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/dist/cli/index.js +286 -168
- package/dist/cli/index.js.map +1 -1
- package/dist/index.d.ts +18 -1
- package/dist/index.js +17 -0
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -105,6 +105,21 @@ var init_messages = __esm({
|
|
|
105
105
|
});
|
|
106
106
|
|
|
107
107
|
// src/errors/base.ts
|
|
108
|
+
function getErrorMessage(error2, fallback = "\uC54C \uC218 \uC5C6\uB294 \uC624\uB958\uAC00 \uBC1C\uC0DD\uD588\uC2B5\uB2C8\uB2E4") {
|
|
109
|
+
if (error2 instanceof Error) {
|
|
110
|
+
return error2.message;
|
|
111
|
+
}
|
|
112
|
+
if (typeof error2 === "string") {
|
|
113
|
+
return error2;
|
|
114
|
+
}
|
|
115
|
+
if (error2 && typeof error2 === "object" && "message" in error2) {
|
|
116
|
+
return String(error2.message);
|
|
117
|
+
}
|
|
118
|
+
return fallback;
|
|
119
|
+
}
|
|
120
|
+
function getResultErrorMessage(error2, fallback = "\uC54C \uC218 \uC5C6\uB294 \uC624\uB958\uAC00 \uBC1C\uC0DD\uD588\uC2B5\uB2C8\uB2E4") {
|
|
121
|
+
return error2?.message ?? fallback;
|
|
122
|
+
}
|
|
108
123
|
var SddError, FileSystemError, ValidationError, ChangeError;
|
|
109
124
|
var init_base = __esm({
|
|
110
125
|
"src/errors/base.ts"() {
|
|
@@ -17717,7 +17732,7 @@ async function executeSyncCommand(specId, options, projectRoot) {
|
|
|
17717
17732
|
...options,
|
|
17718
17733
|
specId
|
|
17719
17734
|
});
|
|
17720
|
-
if (!syncResult.success) {
|
|
17735
|
+
if (!syncResult.success || !syncResult.data) {
|
|
17721
17736
|
return failure(syncResult.error || new Error("\uB3D9\uAE30\uD654 \uAC80\uC99D \uC2E4\uD328"));
|
|
17722
17737
|
}
|
|
17723
17738
|
return success({
|
|
@@ -17756,7 +17771,7 @@ function registerSyncCommand(program2) {
|
|
|
17756
17771
|
}
|
|
17757
17772
|
}
|
|
17758
17773
|
if (result.data.result.missing.length > 0 && !options.json) {
|
|
17759
|
-
process.exit(
|
|
17774
|
+
process.exit(ExitCode.VALIDATION_ERROR);
|
|
17760
17775
|
}
|
|
17761
17776
|
process.exit(ExitCode.SUCCESS);
|
|
17762
17777
|
} catch (error2) {
|
|
@@ -18570,6 +18585,7 @@ async function executeDiff(projectRoot, options = {}) {
|
|
|
18570
18585
|
}
|
|
18571
18586
|
|
|
18572
18587
|
// src/cli/commands/diff.ts
|
|
18588
|
+
init_errors();
|
|
18573
18589
|
function registerDiffCommand(program2) {
|
|
18574
18590
|
program2.command("diff [commit1] [commit2]").description("\uC2A4\uD399 \uBCC0\uACBD\uC0AC\uD56D \uC2DC\uAC01\uD654").option("--staged", "\uC2A4\uD14C\uC774\uC9D5\uB41C \uBCC0\uACBD\uC0AC\uD56D\uB9CC \uD45C\uC2DC").option("--stat", "\uBCC0\uACBD \uD1B5\uACC4 \uC694\uC57D \uD45C\uC2DC").option("--name-only", "\uBCC0\uACBD\uB41C \uD30C\uC77C\uBA85\uB9CC \uD45C\uC2DC").option("--json", "JSON \uD615\uC2DD\uC73C\uB85C \uCD9C\uB825").option("--no-color", "\uCEEC\uB7EC \uCD9C\uB825 \uBE44\uD65C\uC131\uD654").option("-s, --spec <id>", "\uD2B9\uC815 \uC2A4\uD399\uB9CC \uBE44\uAD50").action(async (commit1, commit2, options) => {
|
|
18575
18591
|
const projectRoot = process.cwd();
|
|
@@ -18584,10 +18600,12 @@ function registerDiffCommand(program2) {
|
|
|
18584
18600
|
commit2
|
|
18585
18601
|
});
|
|
18586
18602
|
if (!result.success) {
|
|
18587
|
-
|
|
18588
|
-
process.exit(
|
|
18603
|
+
error(getErrorMessage(result.error));
|
|
18604
|
+
process.exit(ExitCode.GENERAL_ERROR);
|
|
18605
|
+
}
|
|
18606
|
+
if (result.data?.output) {
|
|
18607
|
+
console.log(result.data.output);
|
|
18589
18608
|
}
|
|
18590
|
-
console.log(result.data?.output);
|
|
18591
18609
|
});
|
|
18592
18610
|
}
|
|
18593
18611
|
|
|
@@ -19579,6 +19597,7 @@ function formatExportResult(result) {
|
|
|
19579
19597
|
}
|
|
19580
19598
|
|
|
19581
19599
|
// src/cli/commands/export.ts
|
|
19600
|
+
init_errors();
|
|
19582
19601
|
function registerExportCommand(program2) {
|
|
19583
19602
|
program2.command("export [specId...]").description("\uC2A4\uD399\uC744 HTML, JSON \uB4F1 \uB2E4\uC591\uD55C \uD615\uC2DD\uC73C\uB85C \uB0B4\uBCF4\uB0B4\uAE30").option("-f, --format <format>", "\uCD9C\uB825 \uD615\uC2DD (html, json, markdown, pdf)", "html").option("-o, --output <path>", "\uCD9C\uB825 \uD30C\uC77C \uACBD\uB85C").option("--theme <theme>", "\uD14C\uB9C8 (light, dark)", "light").option("--all", "\uC804\uCCB4 \uC2A4\uD399 \uB0B4\uBCF4\uB0B4\uAE30", false).option("--toc", "\uBAA9\uCC28 \uD3EC\uD568", true).option("--no-toc", "\uBAA9\uCC28 \uC81C\uC678").option("--include-constitution", "Constitution \uD3EC\uD568", false).option("--include-changes", "\uBCC0\uACBD \uC81C\uC548 \uD3EC\uD568", false).option("--json", "JSON \uD615\uC2DD \uCD9C\uB825 (\uACB0\uACFC \uBA54\uD0C0\uC815\uBCF4)").action(async (specIds, options) => {
|
|
19584
19603
|
const projectRoot = process.cwd();
|
|
@@ -19598,7 +19617,10 @@ function registerExportCommand(program2) {
|
|
|
19598
19617
|
console.log(formatExportResult(result));
|
|
19599
19618
|
}
|
|
19600
19619
|
if (!result.success) {
|
|
19601
|
-
|
|
19620
|
+
if (result.error) {
|
|
19621
|
+
error("\uB0B4\uBCF4\uB0B4\uAE30 \uC2E4\uD328", result.error);
|
|
19622
|
+
}
|
|
19623
|
+
process.exit(ExitCode.GENERAL_ERROR);
|
|
19602
19624
|
}
|
|
19603
19625
|
});
|
|
19604
19626
|
}
|
|
@@ -19985,6 +20007,7 @@ function registerDomainCommand(program2) {
|
|
|
19985
20007
|
init_types();
|
|
19986
20008
|
init_fs();
|
|
19987
20009
|
import chalk3 from "chalk";
|
|
20010
|
+
init_errors();
|
|
19988
20011
|
async function executeContextSet(domainIds, options, projectPath) {
|
|
19989
20012
|
const root = projectPath || await findSddRoot(process.cwd());
|
|
19990
20013
|
if (!root) {
|
|
@@ -20078,18 +20101,18 @@ function registerContextCommand(program2) {
|
|
|
20078
20101
|
context.command("set").description("\uC791\uC5C5 \uCEE8\uD14D\uC2A4\uD2B8 \uC124\uC815").argument("<domains...>", "\uD65C\uC131\uD654\uD560 \uB3C4\uBA54\uC778 ID \uBAA9\uB85D").option("--no-include-deps", "\uC758\uC874\uC131 \uB3C4\uBA54\uC778 \uC790\uB3D9 \uD3EC\uD568 \uBE44\uD65C\uC131\uD654").action(async (domains, opts) => {
|
|
20079
20102
|
const result = await executeContextSet(domains, { includeDeps: opts.includeDeps });
|
|
20080
20103
|
if (!result.success) {
|
|
20081
|
-
|
|
20082
|
-
process.exit(
|
|
20104
|
+
error(getResultErrorMessage(result.error));
|
|
20105
|
+
process.exit(ExitCode.GENERAL_ERROR);
|
|
20083
20106
|
}
|
|
20084
|
-
|
|
20107
|
+
success2("\uCEE8\uD14D\uC2A4\uD2B8\uAC00 \uC124\uC815\uB418\uC5C8\uC2B5\uB2C8\uB2E4.");
|
|
20085
20108
|
console.log("");
|
|
20086
20109
|
console.log(formatContextInfo(result.data));
|
|
20087
20110
|
});
|
|
20088
20111
|
context.command("show").description("\uD604\uC7AC \uCEE8\uD14D\uC2A4\uD2B8 \uD45C\uC2DC").option("--json", "JSON \uD615\uC2DD\uC73C\uB85C \uCD9C\uB825").action(async (opts) => {
|
|
20089
20112
|
const result = await executeContextShow();
|
|
20090
20113
|
if (!result.success) {
|
|
20091
|
-
|
|
20092
|
-
process.exit(
|
|
20114
|
+
error(getResultErrorMessage(result.error));
|
|
20115
|
+
process.exit(ExitCode.GENERAL_ERROR);
|
|
20093
20116
|
}
|
|
20094
20117
|
if (opts.json) {
|
|
20095
20118
|
console.log(JSON.stringify(result.data, null, 2));
|
|
@@ -20100,43 +20123,43 @@ function registerContextCommand(program2) {
|
|
|
20100
20123
|
context.command("clear").description("\uCEE8\uD14D\uC2A4\uD2B8 \uD574\uC81C").action(async () => {
|
|
20101
20124
|
const result = await executeContextClear();
|
|
20102
20125
|
if (!result.success) {
|
|
20103
|
-
|
|
20104
|
-
process.exit(
|
|
20126
|
+
error(getResultErrorMessage(result.error));
|
|
20127
|
+
process.exit(ExitCode.GENERAL_ERROR);
|
|
20105
20128
|
}
|
|
20106
|
-
|
|
20129
|
+
success2("\uCEE8\uD14D\uC2A4\uD2B8\uAC00 \uD574\uC81C\uB418\uC5C8\uC2B5\uB2C8\uB2E4.");
|
|
20107
20130
|
});
|
|
20108
20131
|
context.command("add").description("\uCEE8\uD14D\uC2A4\uD2B8\uC5D0 \uB3C4\uBA54\uC778 \uCD94\uAC00").argument("<domain>", "\uCD94\uAC00\uD560 \uB3C4\uBA54\uC778 ID").option("--no-include-deps", "\uC758\uC874\uC131 \uB3C4\uBA54\uC778 \uC790\uB3D9 \uD3EC\uD568 \uBE44\uD65C\uC131\uD654").action(async (domain, opts) => {
|
|
20109
20132
|
const result = await executeContextAdd(domain, { includeDeps: opts.includeDeps });
|
|
20110
20133
|
if (!result.success) {
|
|
20111
|
-
|
|
20112
|
-
process.exit(
|
|
20134
|
+
error(getResultErrorMessage(result.error));
|
|
20135
|
+
process.exit(ExitCode.GENERAL_ERROR);
|
|
20113
20136
|
}
|
|
20114
|
-
|
|
20137
|
+
success2(`\uB3C4\uBA54\uC778 "${domain}"\uC774(\uAC00) \uCEE8\uD14D\uC2A4\uD2B8\uC5D0 \uCD94\uAC00\uB418\uC5C8\uC2B5\uB2C8\uB2E4.`);
|
|
20115
20138
|
console.log("");
|
|
20116
20139
|
console.log(formatContextInfo(result.data));
|
|
20117
20140
|
});
|
|
20118
20141
|
context.command("remove").alias("rm").description("\uCEE8\uD14D\uC2A4\uD2B8\uC5D0\uC11C \uB3C4\uBA54\uC778 \uC81C\uAC70").argument("<domain>", "\uC81C\uAC70\uD560 \uB3C4\uBA54\uC778 ID").action(async (domain) => {
|
|
20119
20142
|
const result = await executeContextRemove(domain);
|
|
20120
20143
|
if (!result.success) {
|
|
20121
|
-
|
|
20122
|
-
process.exit(
|
|
20144
|
+
error(getResultErrorMessage(result.error));
|
|
20145
|
+
process.exit(ExitCode.GENERAL_ERROR);
|
|
20123
20146
|
}
|
|
20124
|
-
|
|
20147
|
+
success2(`\uB3C4\uBA54\uC778 "${domain}"\uC774(\uAC00) \uCEE8\uD14D\uC2A4\uD2B8\uC5D0\uC11C \uC81C\uAC70\uB418\uC5C8\uC2B5\uB2C8\uB2E4.`);
|
|
20125
20148
|
console.log("");
|
|
20126
20149
|
console.log(formatContextInfo(result.data));
|
|
20127
20150
|
});
|
|
20128
20151
|
context.command("specs").description("\uCEE8\uD14D\uC2A4\uD2B8\uC5D0 \uD3EC\uD568\uB41C \uC2A4\uD399 \uBAA9\uB85D").option("--json", "JSON \uD615\uC2DD\uC73C\uB85C \uCD9C\uB825").action(async (opts) => {
|
|
20129
20152
|
const result = await executeContextSpecs(opts);
|
|
20130
20153
|
if (!result.success) {
|
|
20131
|
-
|
|
20132
|
-
process.exit(
|
|
20154
|
+
error(getResultErrorMessage(result.error));
|
|
20155
|
+
process.exit(ExitCode.GENERAL_ERROR);
|
|
20133
20156
|
}
|
|
20134
20157
|
if (opts.json) {
|
|
20135
20158
|
console.log(JSON.stringify(result.data, null, 2));
|
|
20136
20159
|
} else {
|
|
20137
20160
|
const { active, readOnly } = result.data;
|
|
20138
20161
|
if (active.length === 0 && readOnly.length === 0) {
|
|
20139
|
-
|
|
20162
|
+
warn("\uCEE8\uD14D\uC2A4\uD2B8\uC5D0 \uC2A4\uD399\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.");
|
|
20140
20163
|
return;
|
|
20141
20164
|
}
|
|
20142
20165
|
console.log(chalk3.bold("\uCEE8\uD14D\uC2A4\uD2B8 \uC2A4\uD399 \uBAA9\uB85D:"));
|
|
@@ -20159,8 +20182,8 @@ function registerContextCommand(program2) {
|
|
|
20159
20182
|
context.action(async () => {
|
|
20160
20183
|
const result = await executeContextShow();
|
|
20161
20184
|
if (!result.success) {
|
|
20162
|
-
|
|
20163
|
-
process.exit(
|
|
20185
|
+
error(getResultErrorMessage(result.error));
|
|
20186
|
+
process.exit(ExitCode.GENERAL_ERROR);
|
|
20164
20187
|
}
|
|
20165
20188
|
console.log(formatContextInfo(result.data));
|
|
20166
20189
|
});
|
|
@@ -20171,6 +20194,7 @@ init_fs();
|
|
|
20171
20194
|
import path57 from "path";
|
|
20172
20195
|
import chalk10 from "chalk";
|
|
20173
20196
|
init_errors();
|
|
20197
|
+
init_types();
|
|
20174
20198
|
|
|
20175
20199
|
// src/integrations/serena/types.ts
|
|
20176
20200
|
var SymbolKindNames = {
|
|
@@ -20801,12 +20825,12 @@ function inferDomainsFromDirectories(directories, files) {
|
|
|
20801
20825
|
}).sort(([, a], [, b]) => b.files.length - a.files.length).slice(0, 10);
|
|
20802
20826
|
const totalFiles = files.length;
|
|
20803
20827
|
return sortedDomains.map(([name, info2]) => {
|
|
20828
|
+
const estimatedSymbolCount = info2.files.length * 5;
|
|
20804
20829
|
const domain = {
|
|
20805
20830
|
name,
|
|
20806
20831
|
path: info2.path,
|
|
20807
20832
|
fileCount: info2.files.length,
|
|
20808
|
-
symbolCount:
|
|
20809
|
-
// TODO: 심볼 분석 시 업데이트
|
|
20833
|
+
symbolCount: estimatedSymbolCount
|
|
20810
20834
|
};
|
|
20811
20835
|
return {
|
|
20812
20836
|
...domain,
|
|
@@ -22896,16 +22920,12 @@ var aiAssistant = new AIAssistant();
|
|
|
22896
22920
|
|
|
22897
22921
|
// src/cli/commands/reverse.ts
|
|
22898
22922
|
import { promises as fs38 } from "fs";
|
|
22899
|
-
async function
|
|
22900
|
-
const sddRoot = await findSddRoot();
|
|
22923
|
+
async function executeScanCommand(targetPath, options, projectRoot) {
|
|
22924
|
+
const sddRoot = projectRoot || await findSddRoot();
|
|
22901
22925
|
if (!sddRoot) {
|
|
22902
|
-
|
|
22903
|
-
process.exit(ExitCode.GENERAL_ERROR);
|
|
22926
|
+
return failure(new Error("SDD \uD504\uB85C\uC81D\uD2B8\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. `sdd init`\uC744 \uBA3C\uC800 \uC2E4\uD589\uD558\uC138\uC694."));
|
|
22904
22927
|
}
|
|
22905
22928
|
const scanPath2 = targetPath ? path57.resolve(targetPath) : sddRoot;
|
|
22906
|
-
if (!options.quiet) {
|
|
22907
|
-
info(`\uC2A4\uCE94 \uC911: ${scanPath2}`);
|
|
22908
|
-
}
|
|
22909
22929
|
const scanResult = await scanProject(scanPath2, {
|
|
22910
22930
|
depth: options.depth,
|
|
22911
22931
|
include: options.include,
|
|
@@ -22913,15 +22933,51 @@ async function handleScan(targetPath, options) {
|
|
|
22913
22933
|
language: options.language
|
|
22914
22934
|
});
|
|
22915
22935
|
if (!scanResult.success) {
|
|
22916
|
-
|
|
22917
|
-
process.exit(ExitCode.GENERAL_ERROR);
|
|
22936
|
+
return failure(scanResult.error);
|
|
22918
22937
|
}
|
|
22919
22938
|
const result = scanResult.data;
|
|
22920
22939
|
const sddPath = path57.join(sddRoot, ".sdd");
|
|
22921
|
-
|
|
22922
|
-
|
|
22923
|
-
|
|
22940
|
+
await addScanToMeta(sddPath, result);
|
|
22941
|
+
let domainsCreated = 0;
|
|
22942
|
+
let domainsSkipped = 0;
|
|
22943
|
+
const shouldCreateDomains = options.createDomains !== false;
|
|
22944
|
+
if (shouldCreateDomains && result.summary.suggestedDomains.length > 0) {
|
|
22945
|
+
const domainService = createDomainService(sddRoot);
|
|
22946
|
+
const existingDomainsResult = await domainService.list();
|
|
22947
|
+
const existingDomainIds = existingDomainsResult.success ? existingDomainsResult.data.map((d) => d.id) : [];
|
|
22948
|
+
for (const suggested of result.summary.suggestedDomains) {
|
|
22949
|
+
if (existingDomainIds.includes(suggested.name)) {
|
|
22950
|
+
domainsSkipped++;
|
|
22951
|
+
continue;
|
|
22952
|
+
}
|
|
22953
|
+
const createResult = await domainService.create(suggested.name, {
|
|
22954
|
+
description: `${suggested.name} \uB3C4\uBA54\uC778 (reverse scan\uC73C\uB85C \uC790\uB3D9 \uC0DD\uC131)`,
|
|
22955
|
+
path: suggested.path
|
|
22956
|
+
});
|
|
22957
|
+
if (createResult.success) {
|
|
22958
|
+
domainsCreated++;
|
|
22959
|
+
}
|
|
22960
|
+
}
|
|
22924
22961
|
}
|
|
22962
|
+
return success({
|
|
22963
|
+
result,
|
|
22964
|
+
sddRoot,
|
|
22965
|
+
sddPath,
|
|
22966
|
+
domainsCreated,
|
|
22967
|
+
domainsSkipped
|
|
22968
|
+
});
|
|
22969
|
+
}
|
|
22970
|
+
async function handleScan(targetPath, options) {
|
|
22971
|
+
if (!options.quiet) {
|
|
22972
|
+
const scanPath2 = targetPath ? path57.resolve(targetPath) : process.cwd();
|
|
22973
|
+
info(`\uC2A4\uCE94 \uC911: ${scanPath2}`);
|
|
22974
|
+
}
|
|
22975
|
+
const commandResult = await executeScanCommand(targetPath, options);
|
|
22976
|
+
if (!commandResult.success) {
|
|
22977
|
+
error(commandResult.error.message);
|
|
22978
|
+
process.exit(ExitCode.GENERAL_ERROR);
|
|
22979
|
+
}
|
|
22980
|
+
const { result, sddRoot, sddPath, domainsCreated, domainsSkipped } = commandResult.data;
|
|
22925
22981
|
if (options.compare) {
|
|
22926
22982
|
const lastScan = await getLastScan(sddPath);
|
|
22927
22983
|
if (lastScan) {
|
|
@@ -22947,37 +23003,16 @@ async function handleScan(targetPath, options) {
|
|
|
22947
23003
|
error(`\uACB0\uACFC \uC800\uC7A5 \uC2E4\uD328: ${error2}`);
|
|
22948
23004
|
}
|
|
22949
23005
|
}
|
|
22950
|
-
|
|
22951
|
-
|
|
22952
|
-
|
|
22953
|
-
|
|
22954
|
-
|
|
22955
|
-
let createdCount = 0;
|
|
22956
|
-
let skippedCount = 0;
|
|
22957
|
-
for (const suggested of result.summary.suggestedDomains) {
|
|
22958
|
-
if (existingDomainIds.includes(suggested.name)) {
|
|
22959
|
-
skippedCount++;
|
|
22960
|
-
continue;
|
|
22961
|
-
}
|
|
22962
|
-
const createResult = await domainService.create(suggested.name, {
|
|
22963
|
-
description: `${suggested.name} \uB3C4\uBA54\uC778 (reverse scan\uC73C\uB85C \uC790\uB3D9 \uC0DD\uC131)`,
|
|
22964
|
-
path: suggested.path
|
|
22965
|
-
});
|
|
22966
|
-
if (createResult.success) {
|
|
22967
|
-
createdCount++;
|
|
22968
|
-
}
|
|
23006
|
+
if (!options.quiet && (domainsCreated > 0 || domainsSkipped > 0)) {
|
|
23007
|
+
console.log("");
|
|
23008
|
+
console.log(chalk10.bold("\u{1F4C1} \uB3C4\uBA54\uC778 \uC790\uB3D9 \uC0DD\uC131:"));
|
|
23009
|
+
if (domainsCreated > 0) {
|
|
23010
|
+
console.log(chalk10.green(` \u2705 ${domainsCreated}\uAC1C \uB3C4\uBA54\uC778 \uC0DD\uC131\uB428`));
|
|
22969
23011
|
}
|
|
22970
|
-
if (
|
|
22971
|
-
console.log(
|
|
22972
|
-
console.log(chalk10.bold("\u{1F4C1} \uB3C4\uBA54\uC778 \uC790\uB3D9 \uC0DD\uC131:"));
|
|
22973
|
-
if (createdCount > 0) {
|
|
22974
|
-
console.log(chalk10.green(` \u2705 ${createdCount}\uAC1C \uB3C4\uBA54\uC778 \uC0DD\uC131\uB428`));
|
|
22975
|
-
}
|
|
22976
|
-
if (skippedCount > 0) {
|
|
22977
|
-
console.log(chalk10.dim(` \u23ED\uFE0F ${skippedCount}\uAC1C \uB3C4\uBA54\uC778 \uC774\uBBF8 \uC874\uC7AC (\uAC74\uB108\uB700)`));
|
|
22978
|
-
}
|
|
22979
|
-
console.log("");
|
|
23012
|
+
if (domainsSkipped > 0) {
|
|
23013
|
+
console.log(chalk10.dim(` \u23ED\uFE0F ${domainsSkipped}\uAC1C \uB3C4\uBA54\uC778 \uC774\uBBF8 \uC874\uC7AC (\uAC74\uB108\uB700)`));
|
|
22980
23014
|
}
|
|
23015
|
+
console.log("");
|
|
22981
23016
|
}
|
|
22982
23017
|
if (!options.skipSerenaCheck) {
|
|
22983
23018
|
const serenaCheck = await ensureSerenaAvailable("scan", { skipSerenaCheck: true, quiet: true });
|
|
@@ -22992,14 +23027,52 @@ async function handleScan(targetPath, options) {
|
|
|
22992
23027
|
console.log("");
|
|
22993
23028
|
}
|
|
22994
23029
|
}
|
|
22995
|
-
async function
|
|
22996
|
-
const sddRoot = await findSddRoot();
|
|
23030
|
+
async function executeExtractCommand(targetPath, options, onProgress, projectRoot) {
|
|
23031
|
+
const sddRoot = projectRoot || await findSddRoot();
|
|
22997
23032
|
if (!sddRoot) {
|
|
22998
|
-
|
|
22999
|
-
process.exit(ExitCode.GENERAL_ERROR);
|
|
23033
|
+
return failure(new Error("SDD \uD504\uB85C\uC81D\uD2B8\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. `sdd init`\uC744 \uBA3C\uC800 \uC2E4\uD589\uD558\uC138\uC694."));
|
|
23000
23034
|
}
|
|
23001
23035
|
const extractPath = targetPath ? path57.resolve(targetPath) : sddRoot;
|
|
23036
|
+
const scanResult = await scanProject(extractPath, { depth: 5 });
|
|
23037
|
+
if (!scanResult.success) {
|
|
23038
|
+
return failure(scanResult.error);
|
|
23039
|
+
}
|
|
23040
|
+
const extractResult = await extractSpecs(scanResult.data, {
|
|
23041
|
+
depth: options.depth || "medium",
|
|
23042
|
+
ai: options.ai,
|
|
23043
|
+
domain: options.domain
|
|
23044
|
+
}, onProgress);
|
|
23045
|
+
if (!extractResult.success) {
|
|
23046
|
+
return failure(extractResult.error);
|
|
23047
|
+
}
|
|
23048
|
+
const result = extractResult.data;
|
|
23049
|
+
if (result.specs.length === 0) {
|
|
23050
|
+
return success({
|
|
23051
|
+
specs: [],
|
|
23052
|
+
symbolCount: result.symbolCount,
|
|
23053
|
+
skippedCount: result.skippedCount,
|
|
23054
|
+
overallConfidence: result.overallConfidence
|
|
23055
|
+
});
|
|
23056
|
+
}
|
|
23057
|
+
const sddPath = path57.join(sddRoot, ".sdd");
|
|
23058
|
+
const saveResult = await saveExtractedSpecs(sddPath, result, "json");
|
|
23059
|
+
if (!saveResult.success) {
|
|
23060
|
+
return failure(saveResult.error);
|
|
23061
|
+
}
|
|
23062
|
+
await updateExtractionStatus(sddPath, {
|
|
23063
|
+
extractedCount: result.specs.length,
|
|
23064
|
+
pendingReviewCount: result.specs.length
|
|
23065
|
+
});
|
|
23066
|
+
return success({
|
|
23067
|
+
specs: result.specs.map((s) => ({ id: s.id, confidence: s.confidence })),
|
|
23068
|
+
symbolCount: result.symbolCount,
|
|
23069
|
+
skippedCount: result.skippedCount,
|
|
23070
|
+
overallConfidence: result.overallConfidence
|
|
23071
|
+
});
|
|
23072
|
+
}
|
|
23073
|
+
async function handleExtract(targetPath, options) {
|
|
23002
23074
|
if (!options.quiet) {
|
|
23075
|
+
const extractPath = targetPath ? path57.resolve(targetPath) : process.cwd();
|
|
23003
23076
|
info(`\uCD94\uCD9C \uC911: ${extractPath}`);
|
|
23004
23077
|
if (options.depth) {
|
|
23005
23078
|
info(`\uAE4A\uC774: ${options.depth}`);
|
|
@@ -23008,45 +23081,24 @@ async function handleExtract(targetPath, options) {
|
|
|
23008
23081
|
info("AI \uCD94\uB860 \uD65C\uC131\uD654\uB428");
|
|
23009
23082
|
}
|
|
23010
23083
|
}
|
|
23011
|
-
const
|
|
23012
|
-
depth: 5
|
|
23013
|
-
});
|
|
23014
|
-
if (!scanResult.success) {
|
|
23015
|
-
error(scanResult.error.message);
|
|
23016
|
-
process.exit(ExitCode.GENERAL_ERROR);
|
|
23017
|
-
}
|
|
23018
|
-
const extractResult = await extractSpecs(scanResult.data, {
|
|
23019
|
-
depth: options.depth || "medium",
|
|
23020
|
-
ai: options.ai,
|
|
23021
|
-
domain: options.domain
|
|
23022
|
-
}, (progress) => {
|
|
23084
|
+
const commandResult = await executeExtractCommand(targetPath, options, (progress) => {
|
|
23023
23085
|
if (!options.quiet) {
|
|
23024
23086
|
process.stdout.write(`\r \uCC98\uB9AC \uC911: ${progress.processedSymbols}/${progress.totalSymbols} \uC2EC\uBCFC, ${progress.specsGenerated} \uC2A4\uD399 \uC0DD\uC131\uB428`);
|
|
23025
23087
|
}
|
|
23026
23088
|
});
|
|
23027
|
-
if (!
|
|
23028
|
-
error(
|
|
23089
|
+
if (!commandResult.success) {
|
|
23090
|
+
error(commandResult.error.message);
|
|
23029
23091
|
process.exit(ExitCode.GENERAL_ERROR);
|
|
23030
23092
|
}
|
|
23031
23093
|
if (!options.quiet) {
|
|
23032
23094
|
console.log("");
|
|
23033
23095
|
}
|
|
23034
|
-
const result =
|
|
23096
|
+
const result = commandResult.data;
|
|
23035
23097
|
if (result.specs.length === 0) {
|
|
23036
23098
|
warn("\uCD94\uCD9C\uB41C \uC2A4\uD399\uC774 \uC5C6\uC2B5\uB2C8\uB2E4. \uD504\uB85C\uC81D\uD2B8\uC5D0 \uBD84\uC11D \uAC00\uB2A5\uD55C \uC2EC\uBCFC\uC774 \uC5C6\uAC70\uB098 Serena MCP\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4.");
|
|
23037
23099
|
console.log(chalk10.dim("\n\u{1F4A1} Serena MCP\uB97C \uC5F0\uACB0\uD558\uBA74 \uC2EC\uBCFC \uC218\uC900 \uBD84\uC11D\uC774 \uAC00\uB2A5\uD569\uB2C8\uB2E4."));
|
|
23038
23100
|
return;
|
|
23039
23101
|
}
|
|
23040
|
-
const sddPath = path57.join(sddRoot, ".sdd");
|
|
23041
|
-
const saveResult = await saveExtractedSpecs(sddPath, result, "json");
|
|
23042
|
-
if (!saveResult.success) {
|
|
23043
|
-
error(saveResult.error.message);
|
|
23044
|
-
process.exit(ExitCode.GENERAL_ERROR);
|
|
23045
|
-
}
|
|
23046
|
-
await updateExtractionStatus(sddPath, {
|
|
23047
|
-
extractedCount: result.specs.length,
|
|
23048
|
-
pendingReviewCount: result.specs.length
|
|
23049
|
-
});
|
|
23050
23102
|
console.log("");
|
|
23051
23103
|
console.log(chalk10.bold("\u{1F4C4} \uC2A4\uD399 \uCD94\uCD9C \uC644\uB8CC"));
|
|
23052
23104
|
console.log("\u2500".repeat(40));
|
|
@@ -23067,64 +23119,96 @@ async function handleExtract(targetPath, options) {
|
|
|
23067
23119
|
console.log(" sdd reverse review # \uCD94\uCD9C\uB41C \uC2A4\uD399 \uB9AC\uBDF0");
|
|
23068
23120
|
console.log("");
|
|
23069
23121
|
}
|
|
23070
|
-
async function
|
|
23071
|
-
const sddRoot = await findSddRoot();
|
|
23122
|
+
async function executeReviewCommand(specId, options, projectRoot) {
|
|
23123
|
+
const sddRoot = projectRoot || await findSddRoot();
|
|
23072
23124
|
if (!sddRoot) {
|
|
23073
|
-
|
|
23074
|
-
process.exit(ExitCode.GENERAL_ERROR);
|
|
23125
|
+
return failure(new Error("SDD \uD504\uB85C\uC81D\uD2B8\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4."));
|
|
23075
23126
|
}
|
|
23076
23127
|
const sddPath = path57.join(sddRoot, ".sdd");
|
|
23077
23128
|
const draftsPath = path57.join(sddPath, ".reverse-drafts");
|
|
23078
23129
|
if (!await fileExists(draftsPath)) {
|
|
23079
|
-
|
|
23080
|
-
return;
|
|
23130
|
+
return success({ action: "no_drafts" });
|
|
23081
23131
|
}
|
|
23082
23132
|
const loadResult = await loadReviewList(sddPath);
|
|
23083
23133
|
if (!loadResult.success) {
|
|
23084
|
-
|
|
23085
|
-
process.exit(ExitCode.GENERAL_ERROR);
|
|
23134
|
+
return failure(loadResult.error);
|
|
23086
23135
|
}
|
|
23087
23136
|
const items = loadResult.data;
|
|
23088
23137
|
if (items.length === 0) {
|
|
23089
|
-
|
|
23090
|
-
return;
|
|
23138
|
+
return success({ action: "empty" });
|
|
23091
23139
|
}
|
|
23092
23140
|
if (specId) {
|
|
23093
23141
|
const item = items.find((i) => i.specId === specId || i.specId.endsWith(`/${specId}`));
|
|
23094
23142
|
if (!item) {
|
|
23095
|
-
|
|
23096
|
-
process.exit(ExitCode.GENERAL_ERROR);
|
|
23143
|
+
return failure(new Error(`\uC2A4\uD399\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${specId}`));
|
|
23097
23144
|
}
|
|
23098
23145
|
if (options.approve) {
|
|
23099
23146
|
const result = await approveSpec(sddPath, item.specId);
|
|
23100
|
-
if (result.success) {
|
|
23101
|
-
|
|
23102
|
-
console.log("");
|
|
23103
|
-
console.log(chalk10.bold("\u{1F4A1} \uB2E4\uC74C \uB2E8\uACC4:"));
|
|
23104
|
-
console.log(" sdd reverse finalize --all # \uC2B9\uC778\uB41C \uC2A4\uD399 \uD655\uC815");
|
|
23105
|
-
} else {
|
|
23106
|
-
error(result.error.message);
|
|
23107
|
-
process.exit(ExitCode.GENERAL_ERROR);
|
|
23147
|
+
if (!result.success) {
|
|
23148
|
+
return failure(result.error);
|
|
23108
23149
|
}
|
|
23109
|
-
return;
|
|
23150
|
+
return success({ action: "approved", specId: item.specId });
|
|
23110
23151
|
}
|
|
23111
23152
|
if (options.reject) {
|
|
23112
23153
|
const result = await rejectSpec(sddPath, item.specId, options.reason || "\uC0AC\uC6A9\uC790\uC5D0 \uC758\uD574 \uAC70\uBD80\uB428");
|
|
23113
|
-
if (result.success) {
|
|
23114
|
-
|
|
23115
|
-
} else {
|
|
23116
|
-
error(result.error.message);
|
|
23117
|
-
process.exit(ExitCode.GENERAL_ERROR);
|
|
23154
|
+
if (!result.success) {
|
|
23155
|
+
return failure(result.error);
|
|
23118
23156
|
}
|
|
23119
|
-
return;
|
|
23157
|
+
return success({ action: "rejected", specId: item.specId });
|
|
23120
23158
|
}
|
|
23121
|
-
|
|
23122
|
-
|
|
23123
|
-
|
|
23124
|
-
|
|
23125
|
-
|
|
23126
|
-
|
|
23159
|
+
return success({ action: "detail", specId: item.specId, sddPath });
|
|
23160
|
+
}
|
|
23161
|
+
return success({ action: "list", sddPath });
|
|
23162
|
+
}
|
|
23163
|
+
async function handleReview(specId, options) {
|
|
23164
|
+
const commandResult = await executeReviewCommand(specId, options);
|
|
23165
|
+
if (!commandResult.success) {
|
|
23166
|
+
error(commandResult.error.message);
|
|
23167
|
+
process.exit(ExitCode.GENERAL_ERROR);
|
|
23127
23168
|
}
|
|
23169
|
+
const result = commandResult.data;
|
|
23170
|
+
switch (result.action) {
|
|
23171
|
+
case "no_drafts":
|
|
23172
|
+
warn("\uCD94\uCD9C\uB41C \uC2A4\uD399\uC774 \uC5C6\uC2B5\uB2C8\uB2E4. `sdd reverse extract`\uB97C \uBA3C\uC800 \uC2E4\uD589\uD558\uC138\uC694.");
|
|
23173
|
+
return;
|
|
23174
|
+
case "empty":
|
|
23175
|
+
warn("\uB9AC\uBDF0\uD560 \uC2A4\uD399\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.");
|
|
23176
|
+
return;
|
|
23177
|
+
case "approved":
|
|
23178
|
+
success2(`\uC2B9\uC778\uB428: ${result.specId}`);
|
|
23179
|
+
console.log("");
|
|
23180
|
+
console.log(chalk10.bold("\u{1F4A1} \uB2E4\uC74C \uB2E8\uACC4:"));
|
|
23181
|
+
console.log(" sdd reverse finalize --all # \uC2B9\uC778\uB41C \uC2A4\uD399 \uD655\uC815");
|
|
23182
|
+
return;
|
|
23183
|
+
case "rejected":
|
|
23184
|
+
success2(`\uAC70\uBD80\uB428: ${result.specId}`);
|
|
23185
|
+
return;
|
|
23186
|
+
case "detail":
|
|
23187
|
+
if (result.sddPath && result.specId) {
|
|
23188
|
+
const detailLoadResult = await loadReviewList(result.sddPath);
|
|
23189
|
+
if (detailLoadResult.success) {
|
|
23190
|
+
const detailItem = detailLoadResult.data.find(
|
|
23191
|
+
(i) => i.specId === result.specId || i.specId.endsWith(`/${result.specId}`)
|
|
23192
|
+
);
|
|
23193
|
+
if (detailItem) {
|
|
23194
|
+
console.log(formatSpecDetail(detailItem));
|
|
23195
|
+
console.log(chalk10.bold("\u{1F4A1} \uC791\uC5C5:"));
|
|
23196
|
+
console.log(` sdd reverse review ${result.specId} --approve # \uC2B9\uC778`);
|
|
23197
|
+
console.log(` sdd reverse review ${result.specId} --reject # \uAC70\uBD80`);
|
|
23198
|
+
console.log("");
|
|
23199
|
+
}
|
|
23200
|
+
}
|
|
23201
|
+
}
|
|
23202
|
+
return;
|
|
23203
|
+
case "list":
|
|
23204
|
+
break;
|
|
23205
|
+
}
|
|
23206
|
+
const sddRoot = await findSddRoot();
|
|
23207
|
+
if (!sddRoot) return;
|
|
23208
|
+
const sddPath = path57.join(sddRoot, ".sdd");
|
|
23209
|
+
const loadResult = await loadReviewList(sddPath);
|
|
23210
|
+
if (!loadResult.success) return;
|
|
23211
|
+
const items = loadResult.data;
|
|
23128
23212
|
console.log(formatReviewList(items));
|
|
23129
23213
|
const pending = items.filter((i) => i.status === "pending");
|
|
23130
23214
|
if (pending.length > 0) {
|
|
@@ -23139,55 +23223,78 @@ async function handleReview(specId, options) {
|
|
|
23139
23223
|
console.log("");
|
|
23140
23224
|
}
|
|
23141
23225
|
}
|
|
23142
|
-
async function
|
|
23143
|
-
const sddRoot = await findSddRoot();
|
|
23226
|
+
async function executeFinalizeCommand(specId, options, projectRoot) {
|
|
23227
|
+
const sddRoot = projectRoot || await findSddRoot();
|
|
23144
23228
|
if (!sddRoot) {
|
|
23145
|
-
|
|
23146
|
-
process.exit(ExitCode.GENERAL_ERROR);
|
|
23147
|
-
}
|
|
23148
|
-
if (!options.quiet) {
|
|
23149
|
-
if (specId) {
|
|
23150
|
-
info(`\uD655\uC815 \uC911: ${specId}`);
|
|
23151
|
-
} else if (options.all) {
|
|
23152
|
-
info("\uC2B9\uC778\uB41C \uBAA8\uB4E0 \uC2A4\uD399 \uD655\uC815 \uC911");
|
|
23153
|
-
} else if (options.domain) {
|
|
23154
|
-
info(`\uB3C4\uBA54\uC778 \uD655\uC815 \uC911: ${options.domain}`);
|
|
23155
|
-
}
|
|
23229
|
+
return failure(new Error("SDD \uD504\uB85C\uC81D\uD2B8\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4."));
|
|
23156
23230
|
}
|
|
23157
|
-
let result;
|
|
23158
23231
|
if (specId) {
|
|
23159
23232
|
const finalizeResult = await finalizeById(sddRoot, specId);
|
|
23160
23233
|
if (!finalizeResult.success) {
|
|
23161
|
-
|
|
23162
|
-
process.exit(ExitCode.GENERAL_ERROR);
|
|
23234
|
+
return failure(finalizeResult.error);
|
|
23163
23235
|
}
|
|
23164
|
-
|
|
23165
|
-
|
|
23166
|
-
|
|
23167
|
-
|
|
23168
|
-
|
|
23169
|
-
|
|
23236
|
+
return success({
|
|
23237
|
+
action: "single",
|
|
23238
|
+
data: {
|
|
23239
|
+
finalized: [finalizeResult.data],
|
|
23240
|
+
skipped: [],
|
|
23241
|
+
errors: []
|
|
23242
|
+
}
|
|
23243
|
+
});
|
|
23244
|
+
}
|
|
23245
|
+
if (options.domain) {
|
|
23170
23246
|
const finalizeResult = await finalizeDomain(sddRoot, options.domain);
|
|
23171
23247
|
if (!finalizeResult.success) {
|
|
23172
|
-
|
|
23173
|
-
process.exit(ExitCode.GENERAL_ERROR);
|
|
23248
|
+
return failure(finalizeResult.error);
|
|
23174
23249
|
}
|
|
23175
|
-
|
|
23176
|
-
|
|
23250
|
+
return success({
|
|
23251
|
+
action: "domain",
|
|
23252
|
+
data: finalizeResult.data
|
|
23253
|
+
});
|
|
23254
|
+
}
|
|
23255
|
+
if (options.all) {
|
|
23177
23256
|
const finalizeResult = await finalizeAllApproved(sddRoot);
|
|
23178
23257
|
if (!finalizeResult.success) {
|
|
23179
|
-
|
|
23180
|
-
process.exit(ExitCode.GENERAL_ERROR);
|
|
23258
|
+
return failure(finalizeResult.error);
|
|
23181
23259
|
}
|
|
23182
|
-
|
|
23183
|
-
|
|
23184
|
-
|
|
23185
|
-
|
|
23186
|
-
|
|
23187
|
-
|
|
23188
|
-
|
|
23260
|
+
return success({
|
|
23261
|
+
action: "all",
|
|
23262
|
+
data: finalizeResult.data
|
|
23263
|
+
});
|
|
23264
|
+
}
|
|
23265
|
+
return success({ action: "no_target" });
|
|
23266
|
+
}
|
|
23267
|
+
async function handleFinalize(specId, options) {
|
|
23268
|
+
if (!options.quiet) {
|
|
23269
|
+
if (specId) {
|
|
23270
|
+
info(`\uD655\uC815 \uC911: ${specId}`);
|
|
23271
|
+
} else if (options.all) {
|
|
23272
|
+
info("\uC2B9\uC778\uB41C \uBAA8\uB4E0 \uC2A4\uD399 \uD655\uC815 \uC911");
|
|
23273
|
+
} else if (options.domain) {
|
|
23274
|
+
info(`\uB3C4\uBA54\uC778 \uD655\uC815 \uC911: ${options.domain}`);
|
|
23275
|
+
}
|
|
23276
|
+
}
|
|
23277
|
+
const commandResult = await executeFinalizeCommand(specId, options);
|
|
23278
|
+
if (!commandResult.success) {
|
|
23279
|
+
error(commandResult.error.message);
|
|
23280
|
+
process.exit(ExitCode.GENERAL_ERROR);
|
|
23281
|
+
}
|
|
23282
|
+
const result = commandResult.data;
|
|
23283
|
+
switch (result.action) {
|
|
23284
|
+
case "single":
|
|
23285
|
+
case "domain":
|
|
23286
|
+
case "all":
|
|
23287
|
+
if (result.data) {
|
|
23288
|
+
console.log(formatFinalizeResult(result.data));
|
|
23289
|
+
}
|
|
23290
|
+
return;
|
|
23291
|
+
case "no_target":
|
|
23292
|
+
warn("\uD655\uC815\uD560 \uB300\uC0C1\uC744 \uC9C0\uC815\uD558\uC138\uC694:");
|
|
23293
|
+
console.log(" sdd reverse finalize <spec-id> # \uD2B9\uC815 \uC2A4\uD399");
|
|
23294
|
+
console.log(" sdd reverse finalize --all # \uBAA8\uB4E0 \uC2B9\uC778 \uC2A4\uD399");
|
|
23295
|
+
console.log(" sdd reverse finalize -d <domain> # \uD2B9\uC815 \uB3C4\uBA54\uC778");
|
|
23296
|
+
return;
|
|
23189
23297
|
}
|
|
23190
|
-
console.log(formatFinalizeResult(result));
|
|
23191
23298
|
}
|
|
23192
23299
|
function showReverseHelp() {
|
|
23193
23300
|
console.log(`
|
|
@@ -23222,9 +23329,20 @@ ${chalk10.bold("\uC6CC\uD06C\uD50C\uB85C\uC6B0:")}
|
|
|
23222
23329
|
4. finalize \u2192 \uC815\uC2DD \uC2A4\uD399\uC73C\uB85C \uBCC0\uD658
|
|
23223
23330
|
`);
|
|
23224
23331
|
}
|
|
23225
|
-
async function
|
|
23226
|
-
const result = await ensureSerenaAvailable("check", { quiet:
|
|
23332
|
+
async function executeCheckSerenaCommand() {
|
|
23333
|
+
const result = await ensureSerenaAvailable("check", { quiet: true });
|
|
23227
23334
|
if (result.success) {
|
|
23335
|
+
return success({ available: true });
|
|
23336
|
+
}
|
|
23337
|
+
return success({ available: false });
|
|
23338
|
+
}
|
|
23339
|
+
async function handleCheckSerena() {
|
|
23340
|
+
const commandResult = await executeCheckSerenaCommand();
|
|
23341
|
+
if (!commandResult.success) {
|
|
23342
|
+
console.log(createInstallGuide());
|
|
23343
|
+
process.exit(ExitCode.GENERAL_ERROR);
|
|
23344
|
+
}
|
|
23345
|
+
if (commandResult.data.available) {
|
|
23228
23346
|
console.log(chalk10.green("\u2705 Serena MCP \uC0AC\uC6A9 \uAC00\uB2A5"));
|
|
23229
23347
|
} else {
|
|
23230
23348
|
console.log(createInstallGuide());
|