cdsa-harness 0.13.0 → 0.14.0
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/package.json +1 -1
- package/src/builtins.js +1 -1
- package/src/cli.js +77 -2
- package/src/config.js +1 -0
package/package.json
CHANGED
package/src/builtins.js
CHANGED
package/src/cli.js
CHANGED
|
@@ -197,6 +197,44 @@ function makeApproval(ask) {
|
|
|
197
197
|
};
|
|
198
198
|
}
|
|
199
199
|
|
|
200
|
+
function isNewer(a, b) {
|
|
201
|
+
const pa = String(a).split(".").map((n) => parseInt(n, 10) || 0);
|
|
202
|
+
const pb = String(b).split(".").map((n) => parseInt(n, 10) || 0);
|
|
203
|
+
for (let i = 0; i < 3; i++) {
|
|
204
|
+
if ((pa[i] || 0) > (pb[i] || 0)) return true;
|
|
205
|
+
if ((pa[i] || 0) < (pb[i] || 0)) return false;
|
|
206
|
+
}
|
|
207
|
+
return false;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// 시작 시 새 버전 확인(하루 1회, 네트워크 실패는 조용히 무시 → 폐쇄망 안전).
|
|
211
|
+
async function maybeCheckUpdate(cfg) {
|
|
212
|
+
if (cfg.update_check === false) return null;
|
|
213
|
+
const stamp = path.join(configDir(), ".update_check");
|
|
214
|
+
try {
|
|
215
|
+
if (Date.now() - Number(fs.readFileSync(stamp, "utf8")) < 24 * 3600 * 1000) return null;
|
|
216
|
+
} catch {
|
|
217
|
+
/* 첫 확인 */
|
|
218
|
+
}
|
|
219
|
+
try {
|
|
220
|
+
fs.mkdirSync(configDir(), { recursive: true });
|
|
221
|
+
fs.writeFileSync(stamp, String(Date.now())); // 결과와 무관하게 하루 1회로 제한
|
|
222
|
+
} catch {
|
|
223
|
+
/* ignore */
|
|
224
|
+
}
|
|
225
|
+
try {
|
|
226
|
+
const ctrl = new AbortController();
|
|
227
|
+
const t = setTimeout(() => ctrl.abort(), 1500);
|
|
228
|
+
const res = await fetch("https://registry.npmjs.org/cdsa-harness/latest", { signal: ctrl.signal });
|
|
229
|
+
clearTimeout(t);
|
|
230
|
+
if (!res.ok) return null;
|
|
231
|
+
const latest = (await res.json()).version;
|
|
232
|
+
return latest && isNewer(latest, VERSION) ? latest : null;
|
|
233
|
+
} catch {
|
|
234
|
+
return null;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
200
238
|
// 작업 폴더 기준으로 플러그인·스킬·도구상자를 구성한다(시작 시 + /workspace 변경 시).
|
|
201
239
|
async function buildExtensions(cfg, mcp) {
|
|
202
240
|
const filePlugins = await loadPlugins(cfg.workspacePath());
|
|
@@ -539,6 +577,16 @@ export async function main(argv = []) {
|
|
|
539
577
|
|
|
540
578
|
printIntro(cfg);
|
|
541
579
|
|
|
580
|
+
// 새 버전 안내(있을 때만, 하루 1회)
|
|
581
|
+
const newer = await maybeCheckUpdate(cfg);
|
|
582
|
+
if (newer) {
|
|
583
|
+
console.log(
|
|
584
|
+
c.yellow(`⬆️ 새 버전 v${newer} 가 나왔어요!`) +
|
|
585
|
+
c.dim(` 업데이트: npm i -g cdsa-harness@latest · exe 는 Releases 에서 새로 받기`) +
|
|
586
|
+
"\n"
|
|
587
|
+
);
|
|
588
|
+
}
|
|
589
|
+
|
|
542
590
|
// ③ MCP 서버(다른 에이전트와 공용 표준) 연결 — 1회
|
|
543
591
|
if (cfg.mcpServers && Object.keys(cfg.mcpServers).length) {
|
|
544
592
|
process.stdout.write(c.dim("MCP 서버 연결 중...\r"));
|
|
@@ -578,10 +626,37 @@ export async function main(argv = []) {
|
|
|
578
626
|
|
|
579
627
|
const rule = () => console.log(c.grey("─".repeat(Math.min(80, stdout.columns || 80))));
|
|
580
628
|
|
|
581
|
-
// 첫
|
|
629
|
+
// 첫 실행 온보딩(한 번만 — ~/.cdsa_harness/.welcomed 표시): 작업 폴더 설정 + 튜토리얼
|
|
582
630
|
const markerPath = path.join(configDir(), ".welcomed");
|
|
583
631
|
if (stdin.isTTY && !fs.existsSync(markerPath)) {
|
|
584
|
-
|
|
632
|
+
console.log(panel(
|
|
633
|
+
[
|
|
634
|
+
"AI 가 파일을 다룰 ‘작업 폴더’를 정하세요.",
|
|
635
|
+
c.dim("이 폴더 밖은 절대 건드리지 않아요(안전장치)."),
|
|
636
|
+
"",
|
|
637
|
+
` ${c.bold("엔터")} 기본값 ${c.cyan("./workspace")} (하위 폴더 자동 생성)`,
|
|
638
|
+
` ${c.bold(".")} 지금 이 폴더를 그대로 사용`,
|
|
639
|
+
` ${c.bold("경로")} 예) ${c.cyan("./문서")} 또는 ${c.cyan("C:\\작업\\프로젝트")}`,
|
|
640
|
+
],
|
|
641
|
+
{ title: "📁 작업 폴더 설정 (처음 한 번)", color: "cyan" }
|
|
642
|
+
));
|
|
643
|
+
const wsAns = await ask(c.cyan("작업 폴더 [엔터=기본]: "));
|
|
644
|
+
if (wsAns !== null && wsAns.trim()) {
|
|
645
|
+
cfg.workspace = wsAns.trim();
|
|
646
|
+
const rebuilt = await buildExtensions(cfg, mcp);
|
|
647
|
+
toolbox = rebuilt.toolbox;
|
|
648
|
+
skills = rebuilt.skills;
|
|
649
|
+
loop.toolbox = toolbox;
|
|
650
|
+
loop.reset();
|
|
651
|
+
}
|
|
652
|
+
try {
|
|
653
|
+
saveConfig(cfg);
|
|
654
|
+
} catch {
|
|
655
|
+
/* 저장 실패 무시 */
|
|
656
|
+
}
|
|
657
|
+
console.log(c.green(`작업 폴더: ${cfg.workspacePath()}`) + c.dim(" (나중에 /workspace 로 변경 가능)\n"));
|
|
658
|
+
|
|
659
|
+
const a = await ask(c.cyan("짧은 튜토리얼을 볼까요? [Y/n] "));
|
|
585
660
|
if (a !== null && ["", "y", "yes"].includes(a.trim().toLowerCase())) await runTutorial(ask);
|
|
586
661
|
try {
|
|
587
662
|
fs.mkdirSync(configDir(), { recursive: true });
|
package/src/config.js
CHANGED
|
@@ -43,6 +43,7 @@ const DEFAULTS = {
|
|
|
43
43
|
import_foreign_skills: true, // .claude/commands 등 외부 포맷 스킬도 읽기(프로젝트+전역)
|
|
44
44
|
skill_dirs: [], // 스킬을 추가로 읽어올 폴더(절대/상대 경로)
|
|
45
45
|
no_color: false, // 색상 끄기(흑백)
|
|
46
|
+
update_check: true, // 시작 시 새 버전 확인(하루 1회, 실패 시 조용히 무시)
|
|
46
47
|
plugins: [], // 추가로 불러올 npm 플러그인 패키지 이름(이름 규칙과 무관하게 강제 로드)
|
|
47
48
|
mcpServers: {}, // MCP 서버 설정 (Claude Code/Cursor 와 동일한 형식)
|
|
48
49
|
};
|