cdsa-harness 0.19.0 → 0.20.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/README.md CHANGED
@@ -84,7 +84,7 @@ Claude Code·OpenCode 컨벤션을 따릅니다.
84
84
  | `/status` (=`/cost`) | 상태 · 세션 누적 토큰 |
85
85
  | `/models [검색어]` | **실시간 모델 목록에서 번호로 선택**(4개 제공자 모두) |
86
86
  | `/guide` · `/tutorial` | 빠른 시작 안내 · 단계별 인터랙티브 튜토리얼 |
87
- | `/workspace <폴더>` | 작업 폴더 보기/변경 (`.` = 현재 폴더) |
87
+ | `/workspace <폴더>` | 작업 폴더 보기/변경 **기본은 현재 폴더(cwd)**, 터미널답게 |
88
88
  | `/color` | 색상 켜기/끄기(흑백) |
89
89
  | `/setup` | 제공자·API 키·모델 대화형 연결 |
90
90
  | `/provider <이름>` | openai · anthropic · openrouter · mock 전환 |
@@ -231,7 +231,7 @@ cdsa-harness
231
231
  "provider": "openai",
232
232
  "api_key": "",
233
233
  "model": "gpt-4o-mini",
234
- "workspace": "./workspace",
234
+ "workspace": ".",
235
235
  "approval_mode": "manual",
236
236
  "allow_shell": false,
237
237
  "max_steps": 8,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cdsa-harness",
3
- "version": "0.19.0",
3
+ "version": "0.20.1",
4
4
  "description": "AI 에이전트의 내부 동작을 단계별로 드러내는 교육용 터미널 하네스. 실시간 스트리밍 + OpenAI/Claude/OpenRouter + MCP + npm 플러그인·크로스포맷 스킬.",
5
5
  "type": "module",
6
6
  "bin": {
package/src/builtins.js CHANGED
@@ -2,7 +2,7 @@
2
2
  // 스킬/플러그인/버전을 바꾼 뒤 `node scripts/gen-builtins.mjs` 로 재생성하세요.
3
3
  import p0 from "../plugins/hwpx_read.mjs";
4
4
 
5
- export const VERSION = "0.19.0";
5
+ export const VERSION = "0.20.1";
6
6
 
7
7
  export const BUILTIN_PLUGINS = [p0];
8
8
 
package/src/cli.js CHANGED
@@ -198,9 +198,17 @@ function makeApproval(ask, cfg) {
198
198
  } else {
199
199
  console.log(panel([JSON.stringify(req.args)], { title: `🔐 ${req.toolLabel}`, color: "yellow" }));
200
200
  }
201
- const raw = await ask(c.yellow(" 작업을 승인하시겠습니까? [y/N] "));
201
+ const raw = await ask(c.yellow("승인할까요? ") + c.green("[y=승인") + c.yellow(" / n=거부 / ") + c.cyan("a=승인+이후 자동수락]") + " ");
202
202
  const ans = (raw || "").trim().toLowerCase();
203
+ if (ans === "a" || ans === "always") {
204
+ cfg.approval_mode = "auto";
205
+ console.log(c.green("✅ 승인 + 자동 수락 ON — 이후엔 묻지 않아요. (해제: /auto)"));
206
+ return { approved: true, reason: "" };
207
+ }
203
208
  const approved = ans === "y" || ans === "yes";
209
+ if (!approved) {
210
+ console.log(c.yellow("→ 거부로 처리했어요.") + c.dim(" (엔터는 거부예요 — 승인은 y, 항상 승인은 a 또는 /auto)"));
211
+ }
204
212
  streak = approved ? streak + 1 : 0;
205
213
  if (approved && streak >= 3 && !hinted && cfg.approval_mode === "manual") {
206
214
  hinted = true;
@@ -376,7 +384,7 @@ function printIntro(cfg) {
376
384
  const lines = rows.map(([k, v]) => `${c.grey(k.padEnd(9))} ${c.bold(v)}`);
377
385
  console.log(panel(lines, { title: "⚙️ CDSA Harness 설정", color: "cyan" }));
378
386
  console.log(
379
- c.bold(c.cyan("👉 처음이세요? /guide ")) + c.dim("입력하면 빠른 시작 안내가 떠요.")
387
+ c.bold(c.cyan("👉 처음이세요? /guide ")) + c.dim("· '/' 치면 명령 팔레트 · ") + c.cyan("Tab") + c.dim(" 키로 /명령·@파일 자동완성")
380
388
  );
381
389
  console.log(
382
390
  c.dim("명령: ") +
@@ -832,9 +840,8 @@ export async function main(argv = []) {
832
840
  "AI 가 파일을 다룰 ‘작업 폴더’를 정하세요.",
833
841
  c.dim("이 폴더 밖은 절대 건드리지 않아요(안전장치)."),
834
842
  "",
835
- ` ${c.bold("엔터")} 기본값 ${c.cyan("./workspace")} (하위 폴더 자동 생성)`,
836
- ` ${c.bold(".")} 지금 폴더를 그대로 사용`,
837
- ` ${c.bold("경로")} 예) ${c.cyan("./문서")} 또는 ${c.cyan("C:\\작업\\프로젝트")}`,
843
+ ` ${c.bold("엔터")} ${c.cyan("지금 이 폴더")} 그대로 사용 (터미널답게, 기본)`,
844
+ ` ${c.bold("경로")} 따로 격리하고 싶으면 입력 — 예) ${c.cyan("./sandbox")} 또는 ${c.cyan("C:\\작업\\프로젝트")}`,
838
845
  ],
839
846
  { title: "📁 작업 폴더 설정 (처음 한 번)", color: "cyan" }
840
847
  ));
@@ -872,6 +879,21 @@ export async function main(argv = []) {
872
879
  const low = user.toLowerCase();
873
880
 
874
881
  if (["/quit", "/exit", "quit", "exit", ":q"].includes(low)) break;
882
+ if (user === "/") {
883
+ const skillNames = Object.keys(skills).sort();
884
+ console.log(panel([
885
+ c.bold("대화")+" "+c.cyan("/new /compact /resume /undo /context"),
886
+ c.bold("프로젝트")+" "+c.cyan("/init /memory /workspace"),
887
+ c.bold("모델·설정")+" "+c.cyan("/setup /models /model /provider /auto /status /teach /stream /update"),
888
+ c.bold("확장")+" "+c.cyan("/skills /plugins /mcp"),
889
+ c.bold("기타")+" "+c.cyan("/guide /tutorial /about /config /quit"),
890
+ "",
891
+ c.bold(`스킬 ${skillNames.length}개`)+" "+c.grey(skillNames.slice(0,10).map(s=>"/"+s).join(" ")+(skillNames.length>10?" …":"")),
892
+ "",
893
+ c.dim("💡 Tab 키로 자동완성 — 예: /mi[Tab], /provider o[Tab], @파일[Tab]"),
894
+ ], { title: "⌨️ 명령 팔레트", color: "cyan" }));
895
+ continue;
896
+ }
875
897
  if (low === "/help") { printHelp(); continue; }
876
898
  if (low === "/guide" || low === "/start") { printGuide(); continue; }
877
899
  if (low === "/tutorial") { await runTutorial(ask); continue; }
@@ -1162,7 +1184,14 @@ export async function main(argv = []) {
1162
1184
  console.log(c.dim(`(스킬 '/${name}' 실행)`));
1163
1185
  await runTurn(renderSkill(skills[name], argStr));
1164
1186
  } else {
1165
- console.log(c.yellow(`알 없는 명령/스킬: /${name} ${c.cyan("/help")}, ${c.cyan("/skills")} 참고`));
1187
+ const { BUILTIN_COMMANDS } = await import("./completion.js");
1188
+ const all = [...BUILTIN_COMMANDS, ...Object.keys(skills).map((s) => "/" + s)];
1189
+ const near = all.filter((n) => n.includes(name.slice(0, 3)) || n.slice(1).startsWith(name[0] || "")).slice(0, 4);
1190
+ console.log(
1191
+ c.yellow(`알 수 없는 명령: /${name}`) +
1192
+ (near.length ? c.dim(" 혹시? ") + c.cyan(near.join(" ")) : "") +
1193
+ c.dim(" · '/' 입력=팔레트 · Tab=자동완성")
1194
+ );
1166
1195
  }
1167
1196
  continue;
1168
1197
  }
package/src/config.js CHANGED
@@ -34,7 +34,7 @@ const DEFAULTS = {
34
34
  api_key: "",
35
35
  base_url: "", // OpenAI 호환 사내/폐쇄망 LLM 의 전체 엔드포인트 직접 지정(있으면 우선)
36
36
  model: "mock-agent",
37
- workspace: "./workspace",
37
+ workspace: ".", // 기본 = 현재 폴더(cwd). Claude Code/OpenCode 와 동일 — 별도 폴더를 만들지 않는다.
38
38
  approval_mode: "manual",
39
39
  allow_shell: false,
40
40
  max_steps: 8,
@@ -70,7 +70,7 @@ export class Config {
70
70
 
71
71
  // CLI 도구이므로 작업 폴더 상대경로는 '현재 폴더' 기준으로 해석한다(직관적).
72
72
  workspacePath() {
73
- let p = this.workspace || "./workspace";
73
+ let p = this.workspace || ".";
74
74
  if (!path.isAbsolute(p)) p = path.resolve(process.cwd(), p);
75
75
  return p;
76
76
  }
package/src/loop.js CHANGED
@@ -135,7 +135,8 @@ export class AgentLoop {
135
135
  const parts = [
136
136
  "# 정체성",
137
137
  "당신은 CDSA Harness(made by CDSA) 안에서 동작하는 코딩·업무 에이전트입니다.",
138
- "파일시스템에 직접 접근할 수 없으며, 제공된 도구 호출로만 작업 폴더를 다룹니다.",
138
+ "작업 폴더의 파일을 도구(list_dir/read_file/search_files/edit_file/write_file)로 직접 읽고·검색하고·수정할 있습니다.",
139
+ "'파일에 접근할 수 없다'고 답하지 마세요 — 필요한 도구를 호출하면 됩니다. 도구가 곧 당신의 손입니다.",
139
140
  "",
140
141
  "# 환경",
141
142
  `- 작업 폴더(루트): ${ws}`,
package/src/tools.js CHANGED
@@ -86,7 +86,7 @@ export class Toolbox {
86
86
  const root = this.workspace + path.sep;
87
87
  if (candidate !== this.workspace && !candidate.startsWith(root)) {
88
88
  throw new ToolError(
89
- `작업 폴더 밖 경로에는 접근할 수 없습니다: ${rel} (허용 루트: ${this.workspace})`
89
+ `작업 폴더 밖 경로입니다: ${rel} 현재 작업 폴더는 ${this.workspace} 입니다. 다른 폴더에서 작업하려면 사용자가 /workspace <경로> 로 변경할 수 있다고 안내하세요.`
90
90
  );
91
91
  }
92
92
  return candidate;