agentlas 1.0.44 → 1.0.46
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/CHANGELOG.md +38 -0
- package/engine/agentlas-workforce.cjs +26 -6
- package/engine/commands/graph.cjs +16 -10
- package/engine/commands/help.cjs +34 -153
- package/engine/commands/index.cjs +31 -8
- package/engine/commands/search.cjs +6 -1
- package/engine/ui/commands-catalog.cjs +217 -0
- package/engine/ui/palette.cjs +24 -94
- package/engine/ui/repl.cjs +10 -17
- package/engine/ui/shell.cjs +193 -11
- package/engine/workforce/deps.cjs +7 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,43 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.0.46 — 2026-08-11
|
|
4
|
+
|
|
5
|
+
Pick from a list instead of retyping a slug.
|
|
6
|
+
|
|
7
|
+
- `/search` in the shell now shows its results as a list you move through with the
|
|
8
|
+
arrow keys; Enter installs the one you chose. `/graph` does the same and runs the
|
|
9
|
+
graph you pick. Escape cancels either.
|
|
10
|
+
- Search results say what the listing actually means for you — "callable without
|
|
11
|
+
installing" or "install to use" — rather than repeating the server's own enum.
|
|
12
|
+
- Commands now know which surface you are standing on, so the next step they print is
|
|
13
|
+
one you can actually type there. Inside the shell, `search` ends with `/install <slug>`
|
|
14
|
+
instead of `agentlas install <slug>`, which the shell would have refused.
|
|
15
|
+
|
|
16
|
+
## 1.0.45 — 2026-08-11
|
|
17
|
+
|
|
18
|
+
The command surface was rebuilt from one catalog, and failures stopped printing JSON.
|
|
19
|
+
|
|
20
|
+
- **Commands.** The same list used to be maintained by hand in four places, and they
|
|
21
|
+
disagreed: an English screen advertised a Korean argument hint, one feature was sold
|
|
22
|
+
twice under two names, and a de-duplication pass then hid `/switch`, `/list` and
|
|
23
|
+
`/exit` entirely. There is now a single catalog. Aliases are a field on a command,
|
|
24
|
+
never a row of their own, so a duplicate can no longer appear or be silently dropped.
|
|
25
|
+
- **Help.** `/help` is grouped and short — 25 lines in the terminal instead of 133 —
|
|
26
|
+
ordered by what a new user needs first. `help all` lists everything; `help <command>`
|
|
27
|
+
answers about that one command instead of dumping the whole list. The CLI, the classic
|
|
28
|
+
REPL and the interactive shell now call the same renderer, so they cannot drift apart.
|
|
29
|
+
- **Removed.** `journal` reported success for runs that did not exist and read the wrong
|
|
30
|
+
folder; `career-graph`'s read commands silently created project state; `plugins` was a
|
|
31
|
+
second name for `plugin`. All three now stop with the exact replacement command instead
|
|
32
|
+
of leaking into a paid model turn.
|
|
33
|
+
- **Failures.** A failed staffing run used to print a machine code followed by a raw JSON
|
|
34
|
+
object, with the one useful sentence — run `agentlas login` — buried inside it and cut
|
|
35
|
+
mid-escape by two stacked truncations. The sentence now comes first and the code last;
|
|
36
|
+
no JSON reaches a human. Sign-in expiry is relayed intact instead of being re-wrapped.
|
|
37
|
+
- **Shell layout.** Output now stays above the input box instead of below it,
|
|
38
|
+
the box shows what to type, the whole terminal width is used, blank lines survive,
|
|
39
|
+
and `/permission` `/model` `/runtime` `/effort` work where they were only autocompleted.
|
|
40
|
+
|
|
3
41
|
## 1.0.44 — 2026-08-11
|
|
4
42
|
|
|
5
43
|
Turn the interactive shell on once and keep it.
|
|
@@ -2916,6 +2916,12 @@ const TRANSIENT_MODEL_ERROR_RE = /Connection closed mid-response|"terminal_reaso
|
|
|
2916
2916
|
try {
|
|
2917
2917
|
runtimeContext = await D.loadWorkforceGoalRuntime(cwd, ctx.goalId || null);
|
|
2918
2918
|
} catch (error) {
|
|
2919
|
+
/*
|
|
2920
|
+
* 저자·호스트가 이미 한 문장으로 쓴 안내(로그인 필요, 정직 정지)는 그대로 올린다.
|
|
2921
|
+
* 여기서 다시 감싸면 code/honestStop 이 떨어져 나가고, 유일하게 실행 가능한
|
|
2922
|
+
* 문장이 JSON 의 cause 키 밑으로 들어가 사용자에게 blob 으로 보인다(실사고).
|
|
2923
|
+
*/
|
|
2924
|
+
if (error && (error.honestStop || error.code)) throw error;
|
|
2919
2925
|
fail(
|
|
2920
2926
|
"workforce_goal_runtime_unavailable",
|
|
2921
2927
|
"the active account/project Workforce binding could not be inspected",
|
|
@@ -3291,6 +3297,12 @@ const TRANSIENT_MODEL_ERROR_RE = /Connection closed mid-response|"terminal_reaso
|
|
|
3291
3297
|
hostRuntime: identity.runtimeId,
|
|
3292
3298
|
});
|
|
3293
3299
|
} catch (error) {
|
|
3300
|
+
/*
|
|
3301
|
+
* 저자·호스트가 이미 한 문장으로 쓴 안내(로그인 필요, 정직 정지)는 그대로 올린다.
|
|
3302
|
+
* 여기서 다시 감싸면 code/honestStop 이 떨어져 나가고, 유일하게 실행 가능한
|
|
3303
|
+
* 문장이 JSON 의 cause 키 밑으로 들어가 사용자에게 blob 으로 보인다(실사고).
|
|
3304
|
+
*/
|
|
3305
|
+
if (error && (error.honestStop || error.code)) throw error;
|
|
3294
3306
|
fail(
|
|
3295
3307
|
"workforce_goal_binding_failed",
|
|
3296
3308
|
"prepared execution was blocked because the durable goal binding could not be committed",
|
|
@@ -4552,14 +4564,22 @@ const TRANSIENT_MODEL_ERROR_RE = /Connection closed mid-response|"terminal_reaso
|
|
|
4552
4564
|
const otherDetails = details && typeof details === "object" && !Array.isArray(details)
|
|
4553
4565
|
? Object.fromEntries(Object.entries(details).filter(([key]) => key !== "issues"))
|
|
4554
4566
|
: details;
|
|
4555
|
-
|
|
4556
|
-
|
|
4557
|
-
|
|
4558
|
-
|
|
4567
|
+
// otherDetails 는 사람 줄에 쓰지 않는다 — 영수증에 그대로 남는다.
|
|
4568
|
+
void otherDetails;
|
|
4569
|
+
/*
|
|
4570
|
+
* 사람이 읽는 줄에 JSON 을 찍지 않는다(2026-08-11 오너 지적). 예전에는
|
|
4571
|
+
* `code: message — {"cause":"…"}` 형태로 나가서, 유일하게 실행 가능한 문장
|
|
4572
|
+
* ("agentlas login")이 JSON 안에 묻히고 두 번의 절단(1200자→800자)에 걸려
|
|
4573
|
+
* 이스케이프 중간에서 잘렸다. 지금은 문장이 먼저, 기계 코드는 맨 끝 한 줄.
|
|
4574
|
+
*/
|
|
4575
|
+
const failureError = receipt.failure.error;
|
|
4576
|
+
if (failureError && (failureError.honestStop || failureError.code)) ui.error(failureError);
|
|
4577
|
+
else ui.error(String(receipt.failure.message || ""), { reveal: true });
|
|
4559
4578
|
if (issues) {
|
|
4560
|
-
for (const issue of issues.slice(0, 16)) ui.
|
|
4561
|
-
if (issues.length > 16) ui.
|
|
4579
|
+
for (const issue of issues.slice(0, 16)) ui.line(ui.c.dim(` · ${String(issue).slice(0, 400)}`));
|
|
4580
|
+
if (issues.length > 16) ui.line(ui.c.dim(` · … ${issues.length - 16} more in the persisted receipt`));
|
|
4562
4581
|
}
|
|
4582
|
+
ui.line(ui.c.faint(` ${receipt.failure.code}`));
|
|
4563
4583
|
// 실패한 실행이야말로 토큰이 어디로 갔는지 알아야 하는 순간이다. issues 유무와
|
|
4564
4584
|
// 무관하게 낸다 — 첫 배선이 이 블록 안에 들어가는 바람에 issues 없는 실패에서는
|
|
4565
4585
|
// 장부가 통째로 사라졌다.
|
|
@@ -153,8 +153,8 @@ function listGraphs(ctx) {
|
|
|
153
153
|
}
|
|
154
154
|
ctx.out("");
|
|
155
155
|
ctx.out(ctx.ui.dim(en
|
|
156
|
-
?
|
|
157
|
-
: "
|
|
156
|
+
? `Run one with: ${ctx.surface === "shell" ? "/graph" : "agentlas graph run \"<name>\""}`
|
|
157
|
+
: `실행하려면: ${ctx.surface === "shell" ? "/graph" : "agentlas graph run \"<이름>\""}`));
|
|
158
158
|
return 0;
|
|
159
159
|
}
|
|
160
160
|
|
|
@@ -278,12 +278,11 @@ function graphProblems(graph, en) {
|
|
|
278
278
|
/** 노드 한 줄. 무엇인지, 바깥을 바꾸는지, 어떤 값을 만들고 쓰는지. */
|
|
279
279
|
function nodeLine(ctx, node, en) {
|
|
280
280
|
const effect = node.config?.effect;
|
|
281
|
-
|
|
281
|
+
// ★승인 게이트 폐지(오너 이사회 결정 2026-08-10) — 데스크탑 커널은 실행 중에 멈춰
|
|
282
|
+
// 묻지 않는다. "확인 후 실행" 표시는 거짓이므로 없앴다. 옛 그래프의 approval
|
|
283
|
+
// 선언이 남아 있어도 마찬가지다. 사실 그대로의 고지는 "바깥을 바꿈" 하나다.
|
|
282
284
|
const marks = [
|
|
283
285
|
effect === "mutation" ? (en ? "changes things outside" : "바깥을 바꿈") : null,
|
|
284
|
-
approval === "ask" || (effect === "mutation" && approval !== "auto")
|
|
285
|
-
? (en ? "asks first" : "확인 후 실행")
|
|
286
|
-
: null,
|
|
287
286
|
node.config?.consumes ? `${en ? "uses" : "사용"} {{${node.config.consumes}}}` : null,
|
|
288
287
|
node.config?.produces ? `${en ? "makes" : "생성"} {{${node.config.produces}}}` : null,
|
|
289
288
|
].filter(Boolean);
|
|
@@ -470,7 +469,9 @@ async function runGraph(ctx, needle, flags) {
|
|
|
470
469
|
if (row?.ambiguous) { reportAmbiguous(ctx, needle, row.ambiguous, en); return 1; }
|
|
471
470
|
if (!row) {
|
|
472
471
|
ctx.err(en ? `No graph matches "${needle}".` : `"${needle}"와 맞는 그래프가 없습니다.`);
|
|
473
|
-
ctx.err(en
|
|
472
|
+
ctx.err(en
|
|
473
|
+
? `See what is saved with: ${ctx.surface === "shell" ? "/graph" : "agentlas graph list"}`
|
|
474
|
+
: `저장된 목록: ${ctx.surface === "shell" ? "/graph" : "agentlas graph list"}`);
|
|
474
475
|
return 1;
|
|
475
476
|
}
|
|
476
477
|
const graph = parseGraph(row);
|
|
@@ -746,7 +747,11 @@ async function newGraph(ctx, request, flags) {
|
|
|
746
747
|
const mutations = built.graph.nodes.filter((n) => n.config && n.config.effect === "mutation");
|
|
747
748
|
if (mutations.length) {
|
|
748
749
|
ctx.out("");
|
|
749
|
-
|
|
750
|
+
// ★사실 그대로의 고지 — 이 단계들은 실행 중에 멈춰 묻지 않는다(승인 게이트 폐지,
|
|
751
|
+
// 오너 이사회 결정 2026-08-10). "잠근다"는 거짓말 대신 무엇이 나가는지를 알린다.
|
|
752
|
+
ctx.out(en
|
|
753
|
+
? "Steps that change things outside (they run without stopping to ask):"
|
|
754
|
+
: "바깥을 바꾸는 단계 (실행 중에 멈춰 묻지 않습니다):");
|
|
750
755
|
for (const n of mutations) ctx.out(` · ${n.label}`);
|
|
751
756
|
}
|
|
752
757
|
ctx.out("");
|
|
@@ -1077,9 +1082,10 @@ function installPackage(ctx, filePath, flags = {}) {
|
|
|
1077
1082
|
if (mutations.length) {
|
|
1078
1083
|
ctx.out(en ? "It changes things outside at:" : "바깥을 바꾸는 지점:");
|
|
1079
1084
|
for (const m of mutations) ctx.out(` · ${m.label}`);
|
|
1085
|
+
// ★승인 게이트 폐지(2026-08-10) — "멈추고 묻는다"는 거짓말이었다. 사실만 말한다.
|
|
1080
1086
|
ctx.out(ctx.ui.dim(en
|
|
1081
|
-
? "Those steps stop
|
|
1082
|
-
: "그 단계들은
|
|
1087
|
+
? "Those steps do not stop to ask — once this is switched on, they really change things outside."
|
|
1088
|
+
: "그 단계들은 실행 중에 멈춰 묻지 않습니다 — 켜면 실제로 바깥을 바꿉니다."));
|
|
1083
1089
|
}
|
|
1084
1090
|
ctx.out(ctx.ui.dim(en
|
|
1085
1091
|
? "Nothing runs until you switch it on. The desktop app can simulate it first — the terminal cannot."
|
package/engine/commands/help.cjs
CHANGED
|
@@ -1,164 +1,45 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
/* help / usage — v2 명령 표면. 재구축이 진행되며 이 표가 곧 진실이다. */
|
|
3
|
-
|
|
4
|
-
const HELP = `agentlas — the operating system for agents, in your terminal
|
|
5
|
-
|
|
6
|
-
agentlas open the terminal (REPL)
|
|
7
|
-
agentlas "<task>" run once with this project's controller
|
|
8
|
-
graph new "<what you want run for you>" build an automation by talking it through
|
|
9
|
-
|
|
10
|
-
PROJECT WORK
|
|
11
|
-
run [agent] [prompt] project-first one-shot; exact agent is an explicit advanced override
|
|
12
|
-
firm <firm> [task] delegate to a CEO (--runtime · --model · --effort)
|
|
13
|
-
|
|
14
|
-
AGENTS & HUB
|
|
15
|
-
search "<what you need>" discover agents in the Hub
|
|
16
|
-
install <slug> install an agent from the Hub
|
|
17
|
-
plugin add <slug> · plugin list Hub plugins (MCP servers)
|
|
18
|
-
build "<request>" build an installable agent locally (auto-installs)
|
|
19
|
-
upload <path> save owner-private in Agent Cloud (--visibility marketplace to publish)
|
|
20
|
-
import <path> · cd · native prepare local folder agents
|
|
21
|
-
list installed agents/companies + orchestrator/worker runtimes
|
|
22
|
-
roles [set <role> <rt>] show or set orchestrator/worker model roles
|
|
23
|
-
experience <sub> portable Experience: list|inspect|validate|save|publish|status|export|unpublish
|
|
24
|
-
variant resolve --base-release <id> local variant selection (variant help)
|
|
25
|
-
|
|
26
|
-
EXECUTE
|
|
27
|
-
storm <goal> Goal+UltraCode harness: plan → allocate → execute → verify [--research]
|
|
28
|
-
swarm <goal> emergent agent swarm [--parallel N]
|
|
29
|
-
workforce | network <request> Agent Workforce Ontology route (public Hub menu)
|
|
30
|
-
hep-network "<request>" staff across Local + owner Cloud + public Hub (local Core federation)
|
|
31
|
-
hep-local | hep-cloud | hep-hub "<request>" same, restricted to one source scope
|
|
32
|
-
call "a,b" "<ctx>" · browser <url|status|sites|login> · route "<req>" [--json] · research <sub>
|
|
33
|
-
|
|
34
|
-
KNOWLEDGE
|
|
35
|
-
memory import · evolve memory & prompt-evolution proposals
|
|
36
|
-
ontology · career-graph project knowledge & source routing
|
|
37
|
-
journal <sub> Stormbreaker run journal
|
|
38
|
-
project <sub> connect this folder + set an ordered team, standalone
|
|
39
|
-
(status · init · use <agent> · team <agent>…)
|
|
40
|
-
context <sub> dependency map: refresh|locate|refs|slice|impact|verify
|
|
41
|
-
|
|
42
|
-
ACCOUNT & OPS
|
|
43
|
-
login | logout | whoami Agentlas Cloud sign-in (browser flow)
|
|
44
|
-
cloud <sub> cloud assets: save|publish|package|list|restore|field-test
|
|
45
|
-
automation <sub> list|add|on|off|remove|run <id>|runs|daemon
|
|
46
|
-
graph <sub> new "<what you want>"|list|show|run <name>|export|inspect|install
|
|
47
|
-
creds <sub> · env credentials and shared env keys
|
|
48
|
-
usage · telegram · mcp local usage · telegram bindings · MCP servers (mcp probe <id>)
|
|
49
|
-
multimodal image/video/audio provider settings
|
|
50
|
-
doctor · setup · update health check · first-run wizard · npm update check
|
|
51
|
-
oberon | film <sub> AI film render (scaffold|render|list|open)
|
|
52
|
-
hep <sub…> · netadmin Hephaestus passthrough · local agent network
|
|
53
|
-
version · help
|
|
54
|
-
|
|
55
|
-
IN-REPL (agentlas → interactive, Orca multi-session)
|
|
56
|
-
/sessions · /tree · /s <n> | /switch <n> · /kill <n> · /rm <n>
|
|
57
|
-
/runtime <kind> · /model <id> · /effort <level> · /permission <level> (applies to new sessions)
|
|
58
|
-
every command above also works as a slash command (/graph, /search, /automation, …) — /help lists them all
|
|
59
|
-
typing during a running turn queues steering; ctrl-c interrupts the turn
|
|
60
|
-
|
|
61
|
-
Options: -p|--print · --runtime claude-code|codex|gemini · --model <exact-id> ·
|
|
62
|
-
--effort none|minimal|low|medium|high|xhigh|max ·
|
|
63
|
-
--tier economy|balanced|frontier (requires --model) ·
|
|
64
|
-
--permission read|write|full
|
|
65
|
-
`;
|
|
66
|
-
|
|
67
2
|
/*
|
|
68
|
-
*
|
|
69
|
-
*
|
|
70
|
-
*
|
|
3
|
+
* commands/help — 정본은 ui/commands-catalog.cjs 하나다(2026-08-11 재작성).
|
|
4
|
+
*
|
|
5
|
+
* 이 파일에는 EN 61줄 + KO 133줄의 손유지 블록 두 벌이 있었다. 팔레트와 어긋나
|
|
6
|
+
* 같은 명령이 표면마다 다른 인자·설명을 광고했고(`search "<what you need>"` vs
|
|
7
|
+
* `"<필요한 것>"`), `agentlas help doctor` 는 인자를 무시하고 전체를 쏟아냈다.
|
|
8
|
+
* 이제 CLI·기본 REPL·새 셸이 같은 renderHelp 를 부른다 — 다시 갈라질 수 없다.
|
|
71
9
|
*/
|
|
72
|
-
const
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
firm <firm> [task] 회사 CEO에게 위임 (--runtime · --model · --effort)
|
|
81
|
-
|
|
82
|
-
AGENTS & HUB
|
|
83
|
-
search "<필요한 것>" Hub에서 에이전트 찾기
|
|
84
|
-
install <slug> Hub 에이전트 설치
|
|
85
|
-
plugin add <slug> · plugin list Hub 플러그인 (MCP 서버)
|
|
86
|
-
build "<요청>" 에이전트를 로컬에서 만들고 바로 설치
|
|
87
|
-
upload <path> Agent Cloud에 소유자 비공개 저장 (--visibility marketplace 로 발행)
|
|
88
|
-
import <path> · cd · native prepare 로컬 폴더 에이전트
|
|
89
|
-
list 설치 에이전트/회사 + 오케스트레이터·워커 런타임
|
|
90
|
-
roles [set <role> <rt>] 오케스트레이터·워커 모델 역할 조회/설정
|
|
91
|
-
experience <sub> 이동식 Experience: list|inspect|validate|save|publish|status|export|unpublish
|
|
92
|
-
variant resolve --base-release <id> 로컬 변형 선택 (variant help)
|
|
93
|
-
|
|
94
|
-
EXECUTE
|
|
95
|
-
storm <goal> Goal+UltraCode 하니스: 계획 → 배정 → 실행 → 검증 [--research]
|
|
96
|
-
swarm <goal> 창발형 에이전트 스웜 [--parallel N]
|
|
97
|
-
workforce | network <request> Agent Workforce Ontology 편성 (공개 Hub 메뉴)
|
|
98
|
-
hep-network "<request>" 로컬+오너 클라우드+공개 Hub 연합 편성 (로컬 Core 연합)
|
|
99
|
-
hep-local | hep-cloud | hep-hub "<request>" 같은 편성, 한 소스 스코프로 제한
|
|
100
|
-
call "a,b" "<ctx>" · browser <url|status|sites|login> · route "<req>" [--json] · research <sub>
|
|
101
|
-
|
|
102
|
-
KNOWLEDGE
|
|
103
|
-
memory import · evolve 메모리·프롬프트 진화 제안
|
|
104
|
-
ontology · career-graph 프로젝트 지식·소스 라우팅
|
|
105
|
-
journal <sub> Stormbreaker 런 저널
|
|
106
|
-
project <sub> 이 폴더를 프로젝트로 연결 + 순서 팀 편성 (독립)
|
|
107
|
-
(status · init · use <에이전트> · team <에이전트>…)
|
|
108
|
-
context <sub> 의존성 지도: refresh|locate|refs|slice|impact|verify
|
|
109
|
-
|
|
110
|
-
ACCOUNT & OPS
|
|
111
|
-
login | logout | whoami Agentlas Cloud 로그인 (브라우저 플로)
|
|
112
|
-
cloud <sub> 클라우드 자산: save|publish|package|list|restore|field-test
|
|
113
|
-
automation <sub> list|add|on|off|remove|run <id>|runs|daemon
|
|
114
|
-
graph <sub> new "<시킬 일>"|list|show|run <이름>|export|inspect|install
|
|
115
|
-
creds <sub> · env 자격증명·공유 env 키 (creds list 로 확인)
|
|
116
|
-
usage · telegram · mcp 로컬 사용량 · 텔레그램 연결 · MCP 서버 (mcp probe <id>)
|
|
117
|
-
multimodal 이미지/영상/음성 제공자 설정
|
|
118
|
-
doctor · setup · update 건강 점검 · 첫 실행 마법사 · npm 업데이트 확인
|
|
119
|
-
oberon | film <sub> AI 필름 렌더 (scaffold|render|list|open)
|
|
120
|
-
hep <sub…> · netadmin Hephaestus 패스스루 · 로컬 에이전트 네트워크
|
|
121
|
-
version · help
|
|
122
|
-
|
|
123
|
-
IN-REPL (agentlas → 대화형, Orca 다중 세션)
|
|
124
|
-
/sessions · /tree · /s <n> | /switch <n> · /kill <n> · /rm <n>
|
|
125
|
-
/runtime <kind> · /model <id> · /effort <level> · /permission <level> (새 세션부터 적용)
|
|
126
|
-
위의 모든 명령은 슬래시 명령(/graph, /search, /automation, …)으로도 됩니다 — 전체 목록은 /help
|
|
127
|
-
실행 중 입력하면 조종 큐에 쌓이고, ctrl-c 로 턴을 중단합니다
|
|
128
|
-
|
|
129
|
-
Options: -p|--print · --runtime claude-code|codex|gemini · --model <exact-id> ·
|
|
130
|
-
--effort none|minimal|low|medium|high|xhigh|max ·
|
|
131
|
-
--tier economy|balanced|frontier (--model 필요) ·
|
|
132
|
-
--permission read|write|full
|
|
133
|
-
`;
|
|
134
|
-
|
|
135
|
-
function helpText(ctx) {
|
|
136
|
-
return ctx && ctx.lang === "ko" ? HELP_KO : HELP;
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
function run(ctx) {
|
|
140
|
-
ctx.out(helpText(ctx).trimEnd());
|
|
10
|
+
const catalog = require("../ui/commands-catalog.cjs");
|
|
11
|
+
|
|
12
|
+
function run(ctx, args = []) {
|
|
13
|
+
const first = String((args && args[0]) || "").trim();
|
|
14
|
+
const all = first.toLowerCase() === "all";
|
|
15
|
+
const name = !all && first && !first.startsWith("-") ? first : null;
|
|
16
|
+
if (name) return runForCommand(ctx, name);
|
|
17
|
+
ctx.out(catalog.renderHelp({ lang: ctx.lang, surface: "cli", all }));
|
|
141
18
|
return 0;
|
|
142
19
|
}
|
|
143
20
|
|
|
144
21
|
function runForCommand(ctx, command) {
|
|
145
|
-
const name = String(command || "").trim();
|
|
146
|
-
const
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
ctx.out(`Usage: agentlas ${
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
}
|
|
159
|
-
|
|
22
|
+
const name = String(command || "").trim().replace(/^\//, "");
|
|
23
|
+
const entry = catalog.byName(name);
|
|
24
|
+
const ko = ctx && ctx.lang === "ko";
|
|
25
|
+
if (!entry) {
|
|
26
|
+
// 없는 이름을 지어내지 않는다 — 전체 목록으로 보낸다.
|
|
27
|
+
ctx.out(`Usage: agentlas ${name}`);
|
|
28
|
+
ctx.out(ko ? ' 전체 목록: agentlas help all' : ' Full list: agentlas help all');
|
|
29
|
+
return 0;
|
|
30
|
+
}
|
|
31
|
+
const replOnly = !entry.surfaces.includes("cli");
|
|
32
|
+
ctx.out(`Usage: agentlas ${catalog.usageFor(entry, ctx.lang)}`);
|
|
33
|
+
ctx.out(` ${catalog.descFor(entry, ctx.lang)}`);
|
|
34
|
+
if (entry.aliases && entry.aliases.length) {
|
|
35
|
+
ctx.out(ko ? ` 다른 이름: ${entry.aliases.join(", ")}` : ` Also: ${entry.aliases.join(", ")}`);
|
|
36
|
+
}
|
|
37
|
+
if (replOnly) {
|
|
38
|
+
ctx.out(ko
|
|
39
|
+
? ` 이 명령은 터미널 안에서 씁니다: agentlas 를 실행한 뒤 /${entry.name}`
|
|
40
|
+
: ` This one runs inside the terminal: start agentlas, then /${entry.name}`);
|
|
160
41
|
}
|
|
161
42
|
return 0;
|
|
162
43
|
}
|
|
163
44
|
|
|
164
|
-
module.exports = { run, runForCommand
|
|
45
|
+
module.exports = { run, runForCommand };
|
|
@@ -10,8 +10,9 @@ const path = require("node:path");
|
|
|
10
10
|
|
|
11
11
|
// 각 명령 파일은 { run(ctx, args) } 를 export한다. ctx는 엔진이 만든 얕은 DI 객체.
|
|
12
12
|
const COMMANDS = {
|
|
13
|
+
// 정본 이름은 agents. list 는 별칭으로 영구 호출 가능(스크립트·게이트 사용).
|
|
14
|
+
agents: () => require("./list.cjs"),
|
|
13
15
|
version: () => require("./version.cjs"),
|
|
14
|
-
list: () => require("./list.cjs"),
|
|
15
16
|
graph: () => require("./graph.cjs"),
|
|
16
17
|
doctor: () => require("./doctor.cjs"),
|
|
17
18
|
mcp: () => require("./mcp.cjs"),
|
|
@@ -31,14 +32,11 @@ const COMMANDS = {
|
|
|
31
32
|
cd: () => require("./cd.cjs"),
|
|
32
33
|
install: () => require("./install.cjs"),
|
|
33
34
|
plugin: () => require("./plugin.cjs"),
|
|
34
|
-
plugins: () => require("./plugin.cjs"),
|
|
35
35
|
automation: () => require("./automation.cjs"),
|
|
36
36
|
native: () => require("./native.cjs"),
|
|
37
37
|
multimodal: () => require("./multimodal.cjs"),
|
|
38
38
|
document: () => require("./document.cjs"),
|
|
39
39
|
workforce: () => require("./workforce.cjs"),
|
|
40
|
-
network: () => require("./workforce.cjs"),
|
|
41
|
-
taskforce: () => require("./workforce.cjs"),
|
|
42
40
|
// 소스 스코프 편성 4종 — 2026-08-05 네이티브 배선(경위는 workforce.cjs 참조).
|
|
43
41
|
// 별칭이 아니라 1급인 이유는 여전하다: 별칭은 스코프를 전달하지 못한다.
|
|
44
42
|
"hep-network": () => require("./hep-network.cjs"),
|
|
@@ -46,7 +44,6 @@ const COMMANDS = {
|
|
|
46
44
|
"hep-cloud": () => require("./hep-cloud.cjs"),
|
|
47
45
|
"hep-hub": () => require("./hep-hub.cjs"),
|
|
48
46
|
oberon: () => require("./oberon.cjs"),
|
|
49
|
-
film: () => require("./film.cjs"),
|
|
50
47
|
experience: () => require("./experience.cjs"),
|
|
51
48
|
memory: () => require("./memory.cjs"),
|
|
52
49
|
evolve: () => require("./evolve.cjs"),
|
|
@@ -60,7 +57,6 @@ const COMMANDS = {
|
|
|
60
57
|
route: () => require("./route.cjs"),
|
|
61
58
|
research: () => require("./research.cjs"),
|
|
62
59
|
netadmin: () => require("./netadmin.cjs"),
|
|
63
|
-
journal: () => require("./journal.cjs"),
|
|
64
60
|
cloud: () => require("./cloud.cjs"),
|
|
65
61
|
upload: () => require("./upload.cjs"),
|
|
66
62
|
storm: () => require("./storm.cjs"),
|
|
@@ -68,7 +64,6 @@ const COMMANDS = {
|
|
|
68
64
|
project: () => require("./project.cjs"),
|
|
69
65
|
context: () => require("./context.cjs"),
|
|
70
66
|
ontology: () => require("./ontology.cjs"),
|
|
71
|
-
"career-graph": () => require("./career-graph.cjs"),
|
|
72
67
|
creds: () => require("./creds.cjs"),
|
|
73
68
|
billing: () => require("./billing.cjs"),
|
|
74
69
|
uninstall: () => require("./uninstall.cjs"),
|
|
@@ -139,6 +134,30 @@ const COMMAND_ALIASES = {
|
|
|
139
134
|
"hep-storm": "storm",
|
|
140
135
|
"hep-browser": "browser",
|
|
141
136
|
"hep-connect": "connect",
|
|
137
|
+
// 2026-08-11: 같은 기능을 두 이름으로 광고하던 것을 별칭으로 접었다.
|
|
138
|
+
list: "agents",
|
|
139
|
+
network: "workforce",
|
|
140
|
+
taskforce: "workforce",
|
|
141
|
+
film: "oberon",
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
/*
|
|
145
|
+
* 제거된 명령 — 이름을 그냥 없애면 인자와 함께 프롬프트로 새어 유료 턴이 된다
|
|
146
|
+
* (아래 marketplace 사고 주석과 같은 계열). 착지 안내를 두고 arity 무관하게 잡는다.
|
|
147
|
+
*/
|
|
148
|
+
const REMOVED_COMMANDS = {
|
|
149
|
+
journal: {
|
|
150
|
+
en: "`journal` was removed — it reported \"ok\" for runs that do not exist and read the wrong folder.\nExperts: agentlas hep stormbreaker journal --run-id <id> --journal <path>",
|
|
151
|
+
ko: "`journal` 은 제거됐습니다 — 없는 실행에도 \"ok\" 를 답했고 다른 폴더를 봤습니다.\n전문가용: agentlas hep stormbreaker journal --run-id <id> --journal <path>",
|
|
152
|
+
},
|
|
153
|
+
"career-graph": {
|
|
154
|
+
en: "`career-graph` was removed — its read commands silently created project state.\nSources: agentlas ontology · Index: hephaestus career-graph ingest --project .",
|
|
155
|
+
ko: "`career-graph` 는 제거됐습니다 — 조회 명령이 말없이 프로젝트 상태를 만들었습니다.\n소스: agentlas ontology · 색인: hephaestus career-graph ingest --project .",
|
|
156
|
+
},
|
|
157
|
+
plugins: {
|
|
158
|
+
en: "Use: agentlas plugin list",
|
|
159
|
+
ko: "이렇게 쓰세요: agentlas plugin list",
|
|
160
|
+
},
|
|
142
161
|
};
|
|
143
162
|
|
|
144
163
|
function resolveCommandName(cmd) {
|
|
@@ -189,6 +208,10 @@ function dispatch(ctx, argv) {
|
|
|
189
208
|
// 낙하 가드부터 만들 것: 상위 오타 가드는 한 단어 전용이라 인자가 붙은
|
|
190
209
|
// 삭제 이름은 프롬프트로 흘러 에이전트를 기동한다(실측·토큰 소모).
|
|
191
210
|
|
|
211
|
+
if (REMOVED_COMMANDS[cmd]) {
|
|
212
|
+
ctx.err(REMOVED_COMMANDS[cmd][ctx.lang === "ko" ? "ko" : "en"]);
|
|
213
|
+
return 1;
|
|
214
|
+
}
|
|
192
215
|
if (DESKTOP_ONLY_SURFACES[cmd]) {
|
|
193
216
|
const asTask = [rawCmd, ...rest].join(" ");
|
|
194
217
|
ctx.err(`${DESKTOP_ONLY_SURFACES[cmd]}\nIt was not run as a prompt — rerun with quotes if you meant a task: agentlas "${asTask}"`);
|
|
@@ -207,4 +230,4 @@ function dispatch(ctx, argv) {
|
|
|
207
230
|
return undefined; // 알 수 없는 토큰 — 엔진이 에이전트 이름/프롬프트로 해석 시도
|
|
208
231
|
}
|
|
209
232
|
|
|
210
|
-
module.exports = { dispatch, COMMANDS, COMMAND_ALIASES, resolveCommandName, SELF_HELP_COMMANDS, NOT_YET_PORTED, GUARDED_NO_ARG, DESKTOP_ONLY_SURFACES };
|
|
233
|
+
module.exports = { dispatch, COMMANDS, COMMAND_ALIASES, resolveCommandName, SELF_HELP_COMMANDS, NOT_YET_PORTED, GUARDED_NO_ARG, DESKTOP_ONLY_SURFACES, REMOVED_COMMANDS };
|
|
@@ -48,7 +48,12 @@ async function run(ctx, args) {
|
|
|
48
48
|
ctx.out(`${ctx.ui.accent(String(slug).padEnd(34).slice(0, 34))} ${String(name).slice(0, 26).padEnd(27)} ${ctx.ui.dim(String(kind).padEnd(14))} ${String(tagline).slice(0, 60)}`);
|
|
49
49
|
}
|
|
50
50
|
ctx.out("");
|
|
51
|
-
|
|
51
|
+
/*
|
|
52
|
+
* 안내는 사용자가 서 있는 표면에서 실제로 칠 수 있는 형태여야 한다. 셸 안에서
|
|
53
|
+
* "agentlas install"을 안내하면 그대로 따라 쳤을 때 "여기서는 안 됩니다"가 나온다
|
|
54
|
+
* (오너 실측: /agentlas install → not available here yet).
|
|
55
|
+
*/
|
|
56
|
+
ctx.out(ctx.ui.dim(ctx.surface === "shell" ? "Install: /install <slug>" : "Install: agentlas install <slug>"));
|
|
52
57
|
return 0;
|
|
53
58
|
}
|
|
54
59
|
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*
|
|
3
|
+
* ui/commands-catalog — 명령 정본 한 벌 (2026-08-11 전면 재작성).
|
|
4
|
+
*
|
|
5
|
+
* 배경: 같은 목록이 네 곳에 손으로 유지되고 있었다 — palette.SLASH_COMMANDS(71행),
|
|
6
|
+
* help.HELP(EN 61줄), help.HELP_KO(133줄), shell.toSlashCommands(파생). 넷이 서로
|
|
7
|
+
* 어긋난 것이 결함 대부분의 기계적 원인이었다:
|
|
8
|
+
* · 영문 화면에 한글 인자 힌트(`/graph [run <이름>]`)
|
|
9
|
+
* · 같은 기능이 서로 다른 설명으로 두 줄(`search "<what you need>"` vs `"<필요한 것>"`)
|
|
10
|
+
* · renderPalette 가 설명 텍스트로 중복 제거 → `/switch` `/list` `/exit` 가 조용히 사라짐
|
|
11
|
+
* · /help 가 표면마다 61 / 133 / 67줄
|
|
12
|
+
*
|
|
13
|
+
* 그래서 정본을 하나로 합치고, 별칭은 **행이 아니라 필드**로 둔다. 별칭이 행이 아니면
|
|
14
|
+
* 중복 설명 자체가 생기지 않으므로 위 dedup 이 필요 없어지고, 숨김 사고가 구조적으로 막힌다.
|
|
15
|
+
*
|
|
16
|
+
* 불변식(게이트 test/command-surface-contract.cjs 가 잠근다):
|
|
17
|
+
* - COMMANDS 키 ↔ 카탈로그 행 1:1. 예외는 surfaces:["repl"] (셸 switch 가 직접 처리).
|
|
18
|
+
* - args 는 ASCII 정본. 한국어는 argsKo 로만 — 언어 혼재 금지.
|
|
19
|
+
* - tier "core" 만 기본 /help 에 나온다. 나머지는 /help all. 단 Tab 완성은 전부 된다
|
|
20
|
+
* (완성에서 숨기는 것이 바로 `/switch` 결함이었다).
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
const { visWidth } = require("./width.cjs");
|
|
24
|
+
|
|
25
|
+
const GROUPS = [
|
|
26
|
+
{ key: "start", ko: "시작", en: "Start here" },
|
|
27
|
+
{ key: "work", ko: "일 시키기", en: "Do work" },
|
|
28
|
+
{ key: "agents", ko: "에이전트", en: "Agents" },
|
|
29
|
+
{ key: "automate", ko: "자동화", en: "Automate" },
|
|
30
|
+
{ key: "session", ko: "세션 (셸 안에서만)", en: "Sessions (in-shell only)" },
|
|
31
|
+
{ key: "settings", ko: "설정 (셸 안에서만)", en: "Settings (in-shell only)" },
|
|
32
|
+
{ key: "account", ko: "계정·자산", en: "Account & assets" },
|
|
33
|
+
{ key: "knowledge", ko: "프로젝트 지식", en: "Project knowledge" },
|
|
34
|
+
{ key: "advanced", ko: "고급", en: "Advanced" },
|
|
35
|
+
];
|
|
36
|
+
|
|
37
|
+
const CLI = ["cli"];
|
|
38
|
+
const BOTH = ["cli", "repl"];
|
|
39
|
+
const REPL = ["repl"];
|
|
40
|
+
|
|
41
|
+
/* eslint-disable max-len */
|
|
42
|
+
const CATALOG = [
|
|
43
|
+
// ── 1 start ───────────────────────────────────────────────────────────────
|
|
44
|
+
{ name: "setup", group: "start", tier: "core", surfaces: CLI, args: "", ko: "첫 실행 마법사 — 언어·런타임·권한", en: "First-run wizard — language, runtime, permission" },
|
|
45
|
+
{ name: "doctor", group: "start", tier: "core", surfaces: BOTH, args: "[--json]", ko: "설치 상태 점검", en: "Check this installation" },
|
|
46
|
+
{ name: "login", group: "start", tier: "core", surfaces: BOTH, args: "[--force]", ko: "Agentlas 로그인 (--force 로 계정 전환)", en: "Sign in to Agentlas (--force switches account)" },
|
|
47
|
+
{ name: "help", group: "start", tier: "core", surfaces: BOTH, args: "[all|<command>]", argsKo: "[all|<명령>]", ko: "명령 보기 (all = 전체 목록)", en: "Show commands (all = the full list)" },
|
|
48
|
+
|
|
49
|
+
// ── 2 work ────────────────────────────────────────────────────────────────
|
|
50
|
+
{ name: "run", group: "work", tier: "core", surfaces: CLI, args: '[agent] "<task>"', argsKo: '[에이전트] "<작업>"', ko: "이 프로젝트 컨트롤러로 1회 실행", en: "Run once with this project's controller" },
|
|
51
|
+
{ name: "project", group: "work", tier: "core", surfaces: BOTH, args: "[status|use <agent>]", argsKo: "[status|use <에이전트>]", ko: "이 폴더를 프로젝트로 연결", en: "Connect this folder to a project" },
|
|
52
|
+
{ name: "storm", group: "work", tier: "core", surfaces: BOTH, args: '"<goal>"', argsKo: '"<목표>"', ko: "목표 하나를 계획→실행→검증까지", en: "Drive one goal: plan, execute, verify" },
|
|
53
|
+
{ name: "workforce", group: "work", tier: "core", surfaces: BOTH, aliases: ["network", "taskforce"], args: '"<request>"', argsKo: '"<요청>"', ko: "여러 에이전트를 편성해 실행 (공개 Hub)", en: "Staff several agents and run (public Hub)" },
|
|
54
|
+
{ name: "call", group: "work", tier: "core", surfaces: BOTH, aliases: ["hep-call"], args: '"a,b" "<context>"', argsKo: '"a,b" "<맥락>"', ko: "이름을 아는 에이전트를 직접 호출", en: "Call agents you name" },
|
|
55
|
+
|
|
56
|
+
// ── 3 agents ──────────────────────────────────────────────────────────────
|
|
57
|
+
{ name: "agents", group: "agents", tier: "core", surfaces: BOTH, aliases: ["list"], args: "[--json]", ko: "설치된 에이전트·팀", en: "Installed agents and teams" },
|
|
58
|
+
{ name: "search", group: "agents", tier: "core", surfaces: BOTH, aliases: ["hep-search"], args: '"<what you need>"', argsKo: '"<필요한 것>"', ko: "Hub에서 에이전트 찾기", en: "Find agents in the Hub" },
|
|
59
|
+
{ name: "install", group: "agents", tier: "core", surfaces: BOTH, args: "<slug>", ko: "Hub 에이전트 설치", en: "Install an agent from the Hub" },
|
|
60
|
+
{ name: "build", group: "agents", tier: "core", surfaces: BOTH, aliases: ["hep-build"], args: '"<the agent you want>"', argsKo: '"<원하는 에이전트>"', ko: "에이전트를 여기서 만들어 바로 설치 (쓰기 권한)", en: "Build an agent here and install it (runs with write permission)" },
|
|
61
|
+
{ name: "roles", group: "agents", tier: "core", surfaces: BOTH, args: "[set <role> <runtime>]", argsKo: "[set <역할> <런타임>]", ko: "오케스트레이터·워커 모델 지정", en: "Set the orchestrator and worker models" },
|
|
62
|
+
|
|
63
|
+
// ── 4 automate ────────────────────────────────────────────────────────────
|
|
64
|
+
{ name: "automation", group: "automate", tier: "core", surfaces: BOTH, args: "[list|add|on|off|run]", ko: "예약 실행", en: "Scheduled runs" },
|
|
65
|
+
{ name: "graph", group: "automate", tier: "core", surfaces: BOTH, args: "[list|show|run <name>]", argsKo: "[list|show|run <이름>]", ko: "저장된 자동화 그래프", en: "Saved automation graphs" },
|
|
66
|
+
|
|
67
|
+
// ── 5 session (셸 전용) ───────────────────────────────────────────────────
|
|
68
|
+
{ name: "sessions", group: "session", tier: "core", surfaces: REPL, aliases: ["tree"], args: "", ko: "지금 도는 세션 목록", en: "Sessions running now" },
|
|
69
|
+
{ name: "s", group: "session", tier: "core", surfaces: REPL, aliases: ["switch"], args: "<n>", ko: "그 세션으로 전환", en: "Switch to that session" },
|
|
70
|
+
{ name: "kill", group: "session", tier: "core", surfaces: REPL, args: "<n>", ko: "그 세션의 턴 중단", en: "Interrupt that session's turn" },
|
|
71
|
+
{ name: "rm", group: "session", tier: "core", surfaces: REPL, args: "<n>", ko: "그 세션 닫기", en: "Close that session" },
|
|
72
|
+
{ name: "quit", group: "session", tier: "core", surfaces: REPL, aliases: ["exit"], args: "", ko: "종료", en: "Quit" },
|
|
73
|
+
|
|
74
|
+
// ── 6 settings (셸 전용 · 전부 세션 한정, 영구 경로를 설명에 못박는다) ────
|
|
75
|
+
{ name: "permission", group: "settings", tier: "core", surfaces: REPL, args: "read|write|full", ko: "이 셸의 새 세션 권한 (영구: agentlas setup)", en: "Permission for new sessions here (persist: agentlas setup)" },
|
|
76
|
+
{ name: "model", group: "settings", tier: "core", surfaces: REPL, args: "<id|default>", ko: "이 셸의 새 세션 모델 (영구: agentlas roles set)", en: "Model for new sessions here (persist: agentlas roles set)" },
|
|
77
|
+
{ name: "runtime", group: "settings", tier: "core", surfaces: REPL, args: "<kind>", ko: "이 셸의 새 세션 런타임 (영구: agentlas roles set)", en: "Runtime for new sessions here (persist: agentlas roles set)" },
|
|
78
|
+
{ name: "effort", group: "settings", tier: "core", surfaces: REPL, args: "<level>", ko: "이 셸의 새 세션 추론 강도 (영구: agentlas roles set --effort)", en: "Reasoning effort for new sessions here (persist: agentlas roles set --effort)" },
|
|
79
|
+
{ name: "shell", group: "settings", tier: "core", surfaces: REPL, args: "on|off", ko: "새 대화형 셸 켜기/끄기", en: "Turn the new interactive shell on or off" },
|
|
80
|
+
|
|
81
|
+
// ── 7 account ─────────────────────────────────────────────────────────────
|
|
82
|
+
{ name: "whoami", group: "account", tier: "more", surfaces: BOTH, args: "", ko: "로그인 계정 확인", en: "Show the signed-in account" },
|
|
83
|
+
{ name: "logout", group: "account", tier: "more", surfaces: BOTH, args: "", ko: "로그아웃", en: "Sign out" },
|
|
84
|
+
{ name: "billing", group: "account", tier: "more", surfaces: BOTH, args: "", ko: "크레딧 잔액 (구독·대여 수익)", en: "Credit balances (subscription and rental earnings)" },
|
|
85
|
+
{ name: "usage", group: "account", tier: "more", surfaces: BOTH, args: "", ko: "이 설치의 사용 현황", en: "Local usage on this install" },
|
|
86
|
+
{ name: "cloud", group: "account", tier: "more", surfaces: BOTH, args: "<save|publish|list|...>", ko: "Agent Cloud 자산", en: "Agent Cloud assets" },
|
|
87
|
+
{ name: "upload", group: "account", tier: "more", surfaces: BOTH, aliases: ["hep-upload"], args: "<path> [--visibility ...]", argsKo: "<경로> [--visibility ...]", ko: "기본은 비공개 저장, --visibility marketplace 로 공개 발행", en: "Owner-private by default; --visibility marketplace publishes" },
|
|
88
|
+
{ name: "uninstall", group: "account", tier: "more", surfaces: BOTH, args: "<slug> [--yes]", ko: "에이전트 삭제 (대화 기록도 함께 지워짐)", en: "Delete an agent (its chats are deleted too)" },
|
|
89
|
+
{ name: "update", group: "account", tier: "more", surfaces: BOTH, args: "[--json]", ko: "npm 업데이트 확인", en: "Check for an npm update" },
|
|
90
|
+
{ name: "version", group: "account", tier: "more", surfaces: BOTH, args: "", ko: "버전", en: "Version" },
|
|
91
|
+
|
|
92
|
+
// ── 8 knowledge ───────────────────────────────────────────────────────────
|
|
93
|
+
{ name: "ontology", group: "knowledge", tier: "more", surfaces: BOTH, args: "[status|list|add <path>]", argsKo: "[status|list|add <경로>]", ko: "이 프로젝트가 읽을 지식 소스 등록", en: "Register the knowledge sources this project may read" },
|
|
94
|
+
{ name: "context", group: "knowledge", tier: "more", surfaces: BOTH, args: "<locate|slice|impact|...>", ko: "코드 의존성 맵 (Agentlas OS Core 필요)", en: "Code dependency map (requires Agentlas OS Core)" },
|
|
95
|
+
{ name: "memory", group: "knowledge", tier: "more", surfaces: BOTH, args: "<sub>", ko: "메모리", en: "Memory" },
|
|
96
|
+
{ name: "experience", group: "knowledge", tier: "more", surfaces: BOTH, args: "<list|inspect|save|...>", ko: "이식 가능한 Experience", en: "Portable Experience" },
|
|
97
|
+
{ name: "evolve", group: "knowledge", tier: "more", surfaces: BOTH, args: "", ko: "프롬프트 진화 제안", en: "Prompt-evolution proposals" },
|
|
98
|
+
|
|
99
|
+
// ── 9 advanced ────────────────────────────────────────────────────────────
|
|
100
|
+
{ name: "route", group: "advanced", tier: "more", surfaces: BOTH, args: '"<request>"', argsKo: '"<요청>"', ko: "이 요청에 맞는 에이전트로 라우팅", en: "Route this request to the right agent" },
|
|
101
|
+
{ name: "swarm", group: "advanced", tier: "more", surfaces: BOTH, args: '"<goal>" [--parallel N]', argsKo: '"<목표>" [--parallel N]', ko: "창발형 에이전트 스웜", en: "Emergent agent swarm" },
|
|
102
|
+
{ name: "hep-network", group: "advanced", tier: "more", surfaces: BOTH, aliases: ["legacy-network"], args: '"<request>"', argsKo: '"<요청>"', ko: "로컬+오너 클라우드+공개 Hub 연합 편성 (로컬 Core 필요)", en: "Staff across Local + owner Cloud + public Hub (needs local Core)" },
|
|
103
|
+
{ name: "hep-local", group: "advanced", tier: "more", surfaces: BOTH, args: '"<request>"', argsKo: '"<요청>"', ko: "등록된 로컬 에이전트만으로 편성 (로컬 Core 필요)", en: "Staff from registered Local agents only (needs local Core)" },
|
|
104
|
+
{ name: "hep-cloud", group: "advanced", tier: "more", surfaces: BOTH, args: '"<request>"', argsKo: '"<요청>"', ko: "오너 Agent Cloud만으로 편성 (로그인·로컬 Core 필요)", en: "Staff from owner Agent Cloud only (needs sign-in + local Core)" },
|
|
105
|
+
{ name: "hep-hub", group: "advanced", tier: "more", surfaces: BOTH, args: '"<request>"', argsKo: '"<요청>"', ko: "공개 Hub 에이전트만으로 편성 (로컬 Core 필요)", en: "Staff from public Hub agents only (needs local Core)" },
|
|
106
|
+
{ name: "firm", group: "advanced", tier: "more", surfaces: CLI, args: "<firm> [task]", argsKo: "<회사> [작업]", ko: "회사 CEO에게 위임", en: "Delegate to a company CEO" },
|
|
107
|
+
{ name: "import", group: "advanced", tier: "more", surfaces: BOTH, args: "<path>", argsKo: "<경로>", ko: "로컬 폴더 에이전트 가져오기", en: "Import a local folder agent" },
|
|
108
|
+
{ name: "cd", group: "advanced", tier: "more", surfaces: BOTH, args: "<agent>", argsKo: "<에이전트>", ko: "그 에이전트의 폴더 경로를 출력", en: "Print that agent's folder path" },
|
|
109
|
+
{ name: "native", group: "advanced", tier: "more", surfaces: BOTH, args: "prepare <agent>", argsKo: "prepare <에이전트>", ko: "네이티브 CLI 컨텍스트 생성", en: "Prepare native CLI context" },
|
|
110
|
+
{ name: "mcp", group: "advanced", tier: "more", surfaces: BOTH, args: "[list|probe <id>]", ko: "MCP 서버", en: "MCP servers" },
|
|
111
|
+
{ name: "plugin", group: "advanced", tier: "more", surfaces: BOTH, args: "<add <slug>|list|remove>", ko: "Hub 플러그인 (MCP 서버)", en: "Hub plugins (MCP servers)" },
|
|
112
|
+
{ name: "creds", group: "advanced", tier: "more", surfaces: BOTH, args: "<list|save|file>", ko: "API 키 보관 (값은 절대 표시 안 함)", en: "API keys (values are never printed)" },
|
|
113
|
+
{ name: "env", group: "advanced", tier: "more", surfaces: BOTH, args: "", ko: "공유 환경 변수 이름", en: "Shared env key names" },
|
|
114
|
+
{ name: "multimodal", group: "advanced", tier: "more", surfaces: BOTH, args: "[set <kind> <provider>]", ko: "이미지·영상·음성 제공자", en: "Image, video and audio providers" },
|
|
115
|
+
{ name: "telegram", group: "advanced", tier: "more", surfaces: BOTH, args: "[sub]", ko: "텔레그램 연결", en: "Telegram bindings" },
|
|
116
|
+
{ name: "connect", group: "advanced", tier: "more", surfaces: BOTH, aliases: ["hep-connect"], args: "<target>", argsKo: "<대상>", ko: "에이전트·팀 연결", en: "Connect an agent or team" },
|
|
117
|
+
{ name: "browser", group: "advanced", tier: "more", surfaces: BOTH, aliases: ["hep-browser"], args: "<url|status|sites|login>", ko: "브라우저 하드포인트", en: "Browser hardpoint" },
|
|
118
|
+
{ name: "research", group: "advanced", tier: "more", surfaces: BOTH, args: "<sub>", ko: "리서치", en: "Research" },
|
|
119
|
+
{ name: "document", group: "advanced", tier: "more", surfaces: BOTH, args: "pdf <html|url>", ko: "문서 PDF 내보내기", en: "Export a document to PDF" },
|
|
120
|
+
{ name: "oberon", group: "advanced", tier: "more", surfaces: BOTH, aliases: ["film"], args: "<scaffold|render|list|open>", ko: "AI 필름 렌더", en: "AI film render" },
|
|
121
|
+
{ name: "variant", group: "advanced", tier: "more", surfaces: BOTH, args: "resolve --base-release", ko: "로컬 변형 선택", en: "Local variant selection" },
|
|
122
|
+
{ name: "netadmin", group: "advanced", tier: "more", surfaces: BOTH, args: "<init|status|reindex|...>", ko: "로컬 에이전트 네트워크 관리", en: "Local agent network administration" },
|
|
123
|
+
{ name: "hep", group: "advanced", tier: "more", surfaces: BOTH, aliases: ["hep-storm"], args: "<sub...>", ko: "Hephaestus 패스스루 (전문가용)", en: "Hephaestus passthrough (expert)" },
|
|
124
|
+
];
|
|
125
|
+
/* eslint-enable max-len */
|
|
126
|
+
|
|
127
|
+
const BY_NAME = new Map();
|
|
128
|
+
const ALIAS_TO_NAME = new Map();
|
|
129
|
+
for (const entry of CATALOG) {
|
|
130
|
+
BY_NAME.set(entry.name, entry);
|
|
131
|
+
for (const alias of entry.aliases || []) ALIAS_TO_NAME.set(alias, entry.name);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function byName(name) {
|
|
135
|
+
const key = String(name || "").replace(/^\//, "");
|
|
136
|
+
return BY_NAME.get(key) || BY_NAME.get(ALIAS_TO_NAME.get(key)) || null;
|
|
137
|
+
}
|
|
138
|
+
function aliasMap() { return Object.fromEntries(ALIAS_TO_NAME); }
|
|
139
|
+
function argsFor(entry, lang) { return (lang === "ko" && entry.argsKo) || entry.args || ""; }
|
|
140
|
+
function descFor(entry, lang) { return lang === "ko" ? entry.ko : entry.en; }
|
|
141
|
+
function usageFor(entry, lang) {
|
|
142
|
+
const args = argsFor(entry, lang);
|
|
143
|
+
return entry.name + (args ? " " + args : "");
|
|
144
|
+
}
|
|
145
|
+
function forSurface(surface) { return CATALOG.filter((e) => e.surfaces.includes(surface)); }
|
|
146
|
+
|
|
147
|
+
const padVis = (text, width) => text + " ".repeat(Math.max(0, width - visWidth(text)));
|
|
148
|
+
|
|
149
|
+
/*
|
|
150
|
+
* 정렬은 visWidth 로 잰다 — String.length 로 재면 한글 한 글자를 1칸으로 세어
|
|
151
|
+
* 설명 열이 어긋난다(현행 palette.cjs 의 결함).
|
|
152
|
+
*/
|
|
153
|
+
function renderHelp(options = {}) {
|
|
154
|
+
const lang = options.lang === "ko" ? "ko" : "en";
|
|
155
|
+
const surface = options.surface === "repl" ? "repl" : "cli";
|
|
156
|
+
const all = options.all === true;
|
|
157
|
+
const ko = lang === "ko";
|
|
158
|
+
const rows = forSurface(surface).filter((e) => all || e.tier === "core");
|
|
159
|
+
if (!rows.length) return "";
|
|
160
|
+
/*
|
|
161
|
+
* 머리말은 명령표가 아니라 "이게 무엇이고 어떻게 시작하나"다. 예전 HELP_KO 가
|
|
162
|
+
* 들고 있던 제품 한 줄(터미널 속 에이전트 운영체제)을 여기로 옮겼다 —
|
|
163
|
+
* 그 문장이 사라지면 첫 화면이 명령 나열로만 시작한다.
|
|
164
|
+
*/
|
|
165
|
+
const head = [];
|
|
166
|
+
{
|
|
167
|
+
let version = "";
|
|
168
|
+
try { version = require("../agentlas-banner.cjs").readVersion(); } catch { version = ""; }
|
|
169
|
+
head.push(`agentlas${version ? " " + version : ""} — ${ko ? "터미널 속 에이전트 운영체제" : "the agent operating system in your terminal"}`);
|
|
170
|
+
head.push("");
|
|
171
|
+
if (surface === "cli") {
|
|
172
|
+
head.push(ko ? ' agentlas 셸 열기' : ' agentlas open the shell');
|
|
173
|
+
head.push(ko ? ' agentlas "<하고 싶은 일>" 한 번 실행' : ' agentlas "<what you want>" run once');
|
|
174
|
+
}
|
|
175
|
+
head.push(ko
|
|
176
|
+
? " 셸 안에서는 그냥 문장을 치면 실행됩니다 · / 를 누르면 명령이 뜹니다"
|
|
177
|
+
: " In the shell, plain words run a task · press / for commands");
|
|
178
|
+
}
|
|
179
|
+
// 셸에서 셸 전용 명령은 슬래시를 붙여 보여준다 — 그게 실제로 치는 문자열이다.
|
|
180
|
+
const label = (e) => (surface === "repl" && e.surfaces.length === 1 ? "/" : "") + usageFor(e, lang);
|
|
181
|
+
/*
|
|
182
|
+
* 라벨 열에 상한을 둔다. 상한이 없으면 가장 긴 한 줄(automation 의 서브명령 나열)이
|
|
183
|
+
* 전체 열을 밀어 80칸 터미널에서 설명이 통째로 줄바꿈되고, 이어지는 줄은 들여쓰기를
|
|
184
|
+
* 잃는다. 상한을 넘는 소수 행만 자기 열을 넘어가고 나머지는 정렬을 지킨다.
|
|
185
|
+
*/
|
|
186
|
+
const LABEL_CAP = 34;
|
|
187
|
+
const width = Math.min(LABEL_CAP, Math.max(...rows.map((e) => visWidth(label(e)))));
|
|
188
|
+
const out = head.slice();
|
|
189
|
+
for (const group of GROUPS) {
|
|
190
|
+
const inGroup = rows.filter((e) => e.group === group.key);
|
|
191
|
+
if (!inGroup.length) continue;
|
|
192
|
+
out.push("", ko ? group.ko : group.en);
|
|
193
|
+
for (const e of inGroup) {
|
|
194
|
+
const text = label(e);
|
|
195
|
+
// 상한을 넘는 라벨은 정렬을 포기하되 설명과 붙지는 않게 최소 두 칸을 보장한다.
|
|
196
|
+
const gap = visWidth(text) >= width ? " " : " ".repeat(width + 2 - visWidth(text));
|
|
197
|
+
out.push(` ${text}${gap}${descFor(e, lang)}`);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
if (!all) {
|
|
201
|
+
const hidden = forSurface(surface).length - rows.length;
|
|
202
|
+
if (hidden > 0) {
|
|
203
|
+
out.push("", ko
|
|
204
|
+
? ` 나머지 ${hidden}개 명령: ${surface === "repl" ? "/help all" : "agentlas help all"}`
|
|
205
|
+
: ` ${hidden} more: ${surface === "repl" ? "/help all" : "agentlas help all"}`);
|
|
206
|
+
}
|
|
207
|
+
} else {
|
|
208
|
+
const withAliases = CATALOG.filter((e) => (e.aliases || []).length);
|
|
209
|
+
if (withAliases.length) {
|
|
210
|
+
out.push("", ko ? " 같은 명령의 다른 이름" : " Other names for the same command");
|
|
211
|
+
out.push(" " + withAliases.map((e) => `${e.name} (= ${e.aliases.join(", ")})`).join(" · "));
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
return out.join("\n").replace(/^\n/, "");
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
module.exports = { GROUPS, CATALOG, byName, aliasMap, argsFor, descFor, usageFor, forSurface, renderHelp };
|
package/engine/ui/palette.cjs
CHANGED
|
@@ -11,90 +11,21 @@
|
|
|
11
11
|
const { completePath, isAbsolutePathTask } = require("../agentlas-input.cjs");
|
|
12
12
|
|
|
13
13
|
// command, 인자 힌트, 한 줄 설명 — /help 팔레트와 Tab 완성이 같은 정본을 쓴다.
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
{ command: "/model", args: "<id|default>", ko: "새 세션 모델 지정", en: "Set model for new sessions" },
|
|
30
|
-
{ command: "/effort", args: "<level|none>", ko: "새 세션 추론 강도 지정", en: "Set effort for new sessions" },
|
|
31
|
-
{ command: "/permission", args: "<level>", ko: "새 세션 권한 지정", en: "Set permission for new sessions" },
|
|
32
|
-
{ command: "/login", args: "", ko: "Agentlas Cloud 로그인", en: "Sign in to Agentlas Cloud" },
|
|
33
|
-
{ command: "/whoami", args: "", ko: "로그인 상태", en: "Signed-in account" },
|
|
34
|
-
{ command: "/search", args: "\"<what you need>\"", ko: "Hub 에이전트 검색", en: "Search Hub agents" },
|
|
35
|
-
{ command: "/install", args: "<slug>", ko: "Hub 에이전트 설치", en: "Install a Hub agent" },
|
|
36
|
-
{ command: "/usage", args: "", ko: "로컬 사용 현황", en: "Local usage" },
|
|
37
|
-
{ command: "/billing", args: "", ko: "크레딧 잔액", en: "Credit balance" },
|
|
38
|
-
{ command: "/automation", args: "[sub]", ko: "자동화", en: "Automations" },
|
|
39
|
-
{ command: "/storm", args: "<goal>", ko: "Goal+UltraCode 하니스", en: "Goal+UltraCode harness" },
|
|
40
|
-
{ command: "/swarm", args: "<goal>", ko: "에이전트 스웜", en: "Agent swarm" },
|
|
41
|
-
{ command: "/network", args: "<request>", ko: "Workforce 라우트", en: "Workforce route" },
|
|
42
|
-
{ command: "/workforce", args: "<request>", ko: "Workforce 라우트", en: "Workforce route" },
|
|
43
|
-
{ command: "/taskforce", args: "<request>", ko: "임시 태스크포스 편성", en: "Assemble a task force" },
|
|
44
|
-
// 소스 스코프 편성 4종 — 2026-08-05 같은 날 삭제 후 네이티브 배선으로 복원.
|
|
45
|
-
// 이전에는 외부 CLI 스텁(exit 3)이라 죽은 메뉴였다. 지금은 이 터미널의 편성
|
|
46
|
-
// 루프가 직접 돌고 로컬 Agentlas-OS Core가 선언된 스코프의 메뉴를 연합한다.
|
|
47
|
-
// 팔레트 문구가 곧 사용자에게 하는 약속이다 — 스코프를 문장에 적는다.
|
|
48
|
-
{ command: "/hep-network", args: "\"<request>\"", ko: "로컬+오너 클라우드+공개 Hub 연합 편성", en: "Staff across Local + owner Cloud + public Hub" },
|
|
49
|
-
{ command: "/hep-local", args: "\"<request>\"", ko: "등록된 로컬 에이전트만으로 편성", en: "Staff from registered Local agents only" },
|
|
50
|
-
{ command: "/hep-cloud", args: "\"<request>\"", ko: "오너 Agent Cloud만으로 편성", en: "Staff from owner Agent Cloud only" },
|
|
51
|
-
{ command: "/hep-hub", args: "\"<request>\"", ko: "공개 Hub 에이전트만으로 편성", en: "Staff from public Hub agents only" },
|
|
52
|
-
{ command: "/build", args: "\"<request>\"", ko: "에이전트·팀 제작/수리/패키징", en: "Build, repair or package an agent or team" },
|
|
53
|
-
{ command: "/call", args: "\"a,b\" \"<ctx>\"", ko: "지정 에이전트 호출", en: "Call named agents" },
|
|
54
|
-
{ command: "/route", args: "\"<req>\"", ko: "최적 에이전트 라우팅", en: "Route to the best agent" },
|
|
55
|
-
{ command: "/browser", args: "[sub]", ko: "브라우저 하드포인트", en: "Browser hardpoint" },
|
|
56
|
-
{ command: "/connect", args: "<target>", ko: "에이전트·팀 연결", en: "Connect an agent or team" },
|
|
57
|
-
{ command: "/research", args: "<sub>", ko: "리서치", en: "Research" },
|
|
58
|
-
{ command: "/upload", args: "<path>", ko: "Agent Cloud에 저장·발행", en: "Save to Agent Cloud or publish" },
|
|
59
|
-
{ command: "/cloud", args: "<sub>", ko: "클라우드 자산 관리", en: "Cloud assets" },
|
|
60
|
-
{ command: "/import", args: "<path>", ko: "로컬 폴더 에이전트 가져오기", en: "Import a local folder agent" },
|
|
61
|
-
{ command: "/cd", args: "[path]", ko: "작업 폴더 이동", en: "Change working folder" },
|
|
62
|
-
{ command: "/native", args: "prepare <agent>", ko: "네이티브 CLI 컨텍스트 생성", en: "Prepare native CLI context" },
|
|
63
|
-
{ command: "/plugin", args: "<sub>", ko: "Hub 플러그인(MCP)", en: "Hub plugins (MCP servers)" },
|
|
64
|
-
{ command: "/plugins", args: "", ko: "설치된 플러그인", en: "Installed plugins" },
|
|
65
|
-
{ command: "/experience", args: "<sub>", ko: "이식 가능한 Experience", en: "Portable Experience" },
|
|
66
|
-
{ command: "/variant", args: "resolve", ko: "로컬 변형 선택", en: "Local variant selection" },
|
|
67
|
-
{ command: "/memory", args: "<sub>", ko: "메모리", en: "Memory" },
|
|
68
|
-
{ command: "/evolve", args: "", ko: "프롬프트 진화 제안", en: "Prompt-evolution proposals" },
|
|
69
|
-
// 데스크탑의 `ontology` 는 Core 의 지식·메모리 **런타임**(임베딩 포함)이고, 터미널의
|
|
70
|
-
// 이것은 **이 프로젝트의 지식 소스 등록부**다. 서로 다른 것이 같은 이름을 쓰고 있어
|
|
71
|
-
// (감사 D6) 라벨이라도 정확해야 한다 — 명령 이름은 사용자 습관과 스크립트가 걸려
|
|
72
|
-
// 있어 바꾸지 않는다. Core 의 지식 런타임은 터미널에 아직 미노출이다(결함 아님).
|
|
73
|
-
{ command: "/ontology", args: "", ko: "프로젝트 지식 소스 등록", en: "Project knowledge sources" },
|
|
74
|
-
{ command: "/career-graph", args: "", ko: "소스 라우팅 그래프", en: "Source routing graph" },
|
|
75
|
-
{ command: "/journal", args: "<sub>", ko: "Stormbreaker 실행 일지", en: "Stormbreaker run journal" },
|
|
76
|
-
{ command: "/project", args: "[status|init]", ko: ".agentlas 프로젝트 상태", en: "Private project state" },
|
|
77
|
-
{ command: "/context", args: "<sub>", ko: "의존성 맵", en: "Dependency map" },
|
|
78
|
-
{ command: "/creds", args: "<sub>", ko: "자격증명", en: "Credentials" },
|
|
79
|
-
{ command: "/env", args: "", ko: "공유 환경 키", en: "Shared env keys" },
|
|
80
|
-
{ command: "/multimodal", args: "", ko: "이미지·영상·음성 설정", en: "Image/video/audio providers" },
|
|
81
|
-
{ command: "/document", args: "pdf <html|url>", ko: "문서 PDF 내보내기", en: "Export a document to PDF" },
|
|
82
|
-
{ command: "/roles", args: "[set <role> <runtime>]", ko: "오케스트레이터·워커 모델 역할 조회/설정", en: "Show or set orchestrator/worker model roles" },
|
|
83
|
-
{ command: "/telegram", args: "[sub]", ko: "텔레그램 연결", en: "Telegram bindings" },
|
|
84
|
-
{ command: "/oberon", args: "[sub]", ko: "AI 필름", en: "AI film" },
|
|
85
|
-
{ command: "/film", args: "<sub>", ko: "필름 렌더", en: "Film render" },
|
|
86
|
-
{ command: "/hep", args: "<sub…>", ko: "Hephaestus 패스스루", en: "Hephaestus passthrough" },
|
|
87
|
-
// "네트워크"만 쓰면 WiFi·LAN 관리로 읽힌다. 이 명령이 다루는 것은 로컬
|
|
88
|
-
// 에이전트 네트워크(init|status|reindex|bench|add-source)다.
|
|
89
|
-
{ command: "/netadmin", args: "[sub]", ko: "로컬 에이전트 네트워크 관리", en: "Local agent network" },
|
|
90
|
-
{ command: "/update", args: "", ko: "npm 업데이트 확인", en: "npm update check" },
|
|
91
|
-
{ command: "/version", args: "", ko: "버전", en: "Version" },
|
|
92
|
-
{ command: "/logout", args: "", ko: "로그아웃", en: "Sign out" },
|
|
93
|
-
// 대화가 있으면 --yes 없이는 거절한다(챗/메시지 CASCADE 삭제) — 팔레트에도 노출.
|
|
94
|
-
{ command: "/uninstall", args: "<slug> [--yes]", ko: "에이전트 제거", en: "Uninstall an agent" },
|
|
95
|
-
{ command: "/quit", args: "", ko: "종료", en: "Quit" },
|
|
96
|
-
{ command: "/exit", args: "", ko: "종료", en: "Quit" },
|
|
97
|
-
];
|
|
14
|
+
const catalog = require("./commands-catalog.cjs");
|
|
15
|
+
|
|
16
|
+
/*
|
|
17
|
+
* 정본은 ui/commands-catalog.cjs 하나다(2026-08-11). 여기 있던 71행 리터럴은
|
|
18
|
+
* help.cjs 의 EN/KO 블록과 어긋나 언어 혼재·중복 숨김을 만들었다.
|
|
19
|
+
*/
|
|
20
|
+
const SLASH_COMMANDS = catalog.forSurface("repl").map((entry) => ({
|
|
21
|
+
command: "/" + entry.name,
|
|
22
|
+
args: entry.args || "",
|
|
23
|
+
argsKo: entry.argsKo,
|
|
24
|
+
ko: entry.ko,
|
|
25
|
+
en: entry.en,
|
|
26
|
+
group: entry.group,
|
|
27
|
+
tier: entry.tier,
|
|
28
|
+
}));
|
|
98
29
|
|
|
99
30
|
const SLASH_NAMES = SLASH_COMMANDS.map((c) => c.command);
|
|
100
31
|
const RUNTIME_KINDS = ["claude-code", "codex", "gemini"];
|
|
@@ -157,9 +88,9 @@ function suggestions(line, limit = 12, lang = "en") {
|
|
|
157
88
|
const rows = SLASH_COMMANDS.map((entry) => ({
|
|
158
89
|
command: entry.command,
|
|
159
90
|
description: ko ? entry.ko : entry.en,
|
|
160
|
-
usage:
|
|
91
|
+
usage: "/" + catalog.usageFor(catalog.byName(entry.command) || entry, lang),
|
|
161
92
|
detail: "",
|
|
162
|
-
category: "",
|
|
93
|
+
category: entry.group || "",
|
|
163
94
|
examples: [],
|
|
164
95
|
}));
|
|
165
96
|
const q = value.toLowerCase();
|
|
@@ -173,14 +104,13 @@ function suggestions(line, limit = 12, lang = "en") {
|
|
|
173
104
|
return starts.concat(contains).slice(0, limit);
|
|
174
105
|
}
|
|
175
106
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
.join("\n");
|
|
107
|
+
/*
|
|
108
|
+
* /help 팔레트 — CLI 와 완전히 같은 렌더러를 쓴다. 예전엔 여기서 설명 텍스트로
|
|
109
|
+
* 중복 제거를 해서 `/switch` `/list` `/exit` 가 조용히 사라졌다. 별칭이 필드가 된
|
|
110
|
+
* 지금은 중복 자체가 없으므로 그 필터도 없다.
|
|
111
|
+
*/
|
|
112
|
+
function renderPalette(lang, opts = {}) {
|
|
113
|
+
return catalog.renderHelp({ lang, surface: "repl", all: opts.all === true });
|
|
184
114
|
}
|
|
185
115
|
|
|
186
116
|
module.exports = { SLASH_COMMANDS, SLASH_NAMES, makeCompleter, renderPalette, suggestions };
|
package/engine/ui/repl.cjs
CHANGED
|
@@ -683,22 +683,17 @@ function handleSlash(ctx, cmdline, api) {
|
|
|
683
683
|
switch (cmd) {
|
|
684
684
|
case "quit": case "exit": return "quit";
|
|
685
685
|
case "help": {
|
|
686
|
-
|
|
687
|
-
ctx.out("");
|
|
688
|
-
ctx.out(ui.c.bold(en ? "Project Work runs" : "프로젝트 Work 실행"));
|
|
689
|
-
// 팔레트는 Tab 완성과 같은 정본(ui/palette)에서 렌더한다 — 목록 드리프트 금지.
|
|
690
|
-
ctx.out(require("./palette.cjs").renderPalette(ctx.lang));
|
|
686
|
+
// CLI 와 같은 렌더러 한 번. 예전엔 CLI 61줄 + 팔레트 67줄 + 트레일러 = 133줄이었다.
|
|
687
|
+
ctx.out(require("./palette.cjs").renderPalette(ctx.lang, { all: String(rest[0] || "") === "all" }));
|
|
691
688
|
ctx.out("");
|
|
692
689
|
ctx.out(ctx.uiInstance.c.dim(en
|
|
693
|
-
? "Tab completes commands
|
|
694
|
-
: "Tab:
|
|
695
|
-
// 배너가 광고하는 Shift-Tab 은 여기에도 적힌다 — 문구와 구현은 한 곳에서 움직인다.
|
|
696
|
-
ctx.out(ctx.uiInstance.c.dim(`Shift-Tab: ${i18n.t(ctx.lang, "help.shiftTab")}`));
|
|
690
|
+
? "Tab completes commands · ↑/↓ history · Shift-Tab cycles permission · ctrl-c interrupts a turn"
|
|
691
|
+
: "Tab: 명령 완성 · ↑/↓ 히스토리 · Shift-Tab 권한 순환 · ctrl-c 턴 중단"));
|
|
697
692
|
return;
|
|
698
693
|
}
|
|
699
694
|
case "shell": {
|
|
700
695
|
/*
|
|
701
|
-
* 새 대화형 셸 켜기/끄기. 저장하고
|
|
696
|
+
* 새 대화형 셸 켜기/끄기. 저장하고 재실행을 안내한다 — 프로세스 중간에
|
|
702
697
|
* stdin 소유권을 바꾸면 지금 도는 readline 과 경합한다(실사고 계열).
|
|
703
698
|
*/
|
|
704
699
|
const want = String(rest[0] || "").toLowerCase();
|
|
@@ -711,13 +706,11 @@ function handleSlash(ctx, cmdline, api) {
|
|
|
711
706
|
const { userDataDir } = require("../core/paths.cjs");
|
|
712
707
|
config.updatePrefs(userDataDir(), { shell: want === "on" ? "interactive" : "classic" });
|
|
713
708
|
if (ctx.prefs) ctx.prefs.shell = want === "on" ? "interactive" : "classic";
|
|
714
|
-
ui.line(want === "on"
|
|
715
|
-
?
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
? "Interactive shell disabled — restart agentlas for the classic REPL."
|
|
720
|
-
: "대화형 셸을 껐습니다 — agentlas 를 다시 실행하면 기본 REPL 입니다."));
|
|
709
|
+
ui.line(ui.c.dim(want === "on"
|
|
710
|
+
? (en ? "Interactive shell enabled — restart agentlas to enter it (/shell off to revert)."
|
|
711
|
+
: "대화형 셸을 켰습니다 — agentlas 를 다시 실행하면 그 화면으로 들어갑니다 (/shell off 로 되돌림).")
|
|
712
|
+
: (en ? "Interactive shell disabled — restart agentlas for the classic REPL."
|
|
713
|
+
: "대화형 셸을 껐습니다 — agentlas 를 다시 실행하면 기본 REPL 입니다.")));
|
|
721
714
|
return;
|
|
722
715
|
}
|
|
723
716
|
case "agents": case "list": require("../commands/list.cjs").run(ctx, rest); return;
|
package/engine/ui/shell.cjs
CHANGED
|
@@ -47,9 +47,21 @@ function loadRenderer() {
|
|
|
47
47
|
|
|
48
48
|
/* Ui 를 상속해 write 초크포인트만 렌더러로 돌린다. */
|
|
49
49
|
class ShellUi extends Ui {
|
|
50
|
+
/*
|
|
51
|
+
* 실터미널 대신 dummy 스트림 — 놓친 직접 out.write 가 프레임을 찢는 대신 소멸한다.
|
|
52
|
+
* 단 columns 는 살려야 한다: Ui 의 줄바꿈·표·구분선이 전부 this.out.columns 를 읽는데
|
|
53
|
+
* PassThrough 에는 그 속성이 없어 전부 80/100 에 고정돼 있었다(폭 넓은 터미널에서
|
|
54
|
+
* 화면 절반만 쓰던 원인). 렌더러의 실제 폭을 게터로 물린다.
|
|
55
|
+
*/
|
|
56
|
+
static _sinkFor(tui) {
|
|
57
|
+
const sink = new PassThrough();
|
|
58
|
+
Object.defineProperty(sink, "columns", {
|
|
59
|
+
get() { return (tui.terminal && tui.terminal.columns) || 80; },
|
|
60
|
+
});
|
|
61
|
+
return sink;
|
|
62
|
+
}
|
|
50
63
|
constructor(opts, pi, tui, transcript) {
|
|
51
|
-
|
|
52
|
-
super({ ...opts, stream: new PassThrough(), color: true });
|
|
64
|
+
super({ ...opts, stream: ShellUi._sinkFor(tui), color: true });
|
|
53
65
|
this._pi = pi;
|
|
54
66
|
this._tui = tui;
|
|
55
67
|
this._transcript = transcript;
|
|
@@ -59,7 +71,12 @@ class ShellUi extends Ui {
|
|
|
59
71
|
this._loader = null;
|
|
60
72
|
}
|
|
61
73
|
_appendText(text) {
|
|
62
|
-
|
|
74
|
+
/*
|
|
75
|
+
* 벤더 Text.render 는 공백만 있는 줄에 [] 를 돌려준다 — screens.cjs 의 ui.line("")
|
|
76
|
+
* 22곳이 전부 조용히 사라져 화면이 한 덩어리로 붙어 있었다. 빈 줄은 Spacer 로.
|
|
77
|
+
*/
|
|
78
|
+
const value = String(text);
|
|
79
|
+
this._transcript.addChild(value.trim() === "" ? new this._pi.Spacer(1) : new this._pi.Text(value, 1, 0));
|
|
63
80
|
this._tui.requestRender();
|
|
64
81
|
}
|
|
65
82
|
write(s) {
|
|
@@ -101,7 +118,7 @@ class ShellUi extends Ui {
|
|
|
101
118
|
this.stopSpinner();
|
|
102
119
|
this.ensureNl();
|
|
103
120
|
this._mdText = "";
|
|
104
|
-
this._md = new this._pi.Markdown("",
|
|
121
|
+
this._md = new this._pi.Markdown("", 1, 0, this._mdTheme());
|
|
105
122
|
this._transcript.addChild(this._md);
|
|
106
123
|
this._streaming = true;
|
|
107
124
|
}
|
|
@@ -171,11 +188,22 @@ async function startShell(ctx, opts = {}) {
|
|
|
171
188
|
|
|
172
189
|
const terminal = new pi.ProcessTerminal();
|
|
173
190
|
const tui = new pi.TuiMainScreen(terminal);
|
|
174
|
-
|
|
191
|
+
/*
|
|
192
|
+
* Container.render 는 삽입 순서로 그린다. 예전엔 헤더 3줄 뒤에 Editor 를 넣어서
|
|
193
|
+
* 입력면이 4번째 줄에 영구 고정됐고, 이후 모든 출력이 그 아래로 흘러 입력 상자가
|
|
194
|
+
* 화면 한가운데 박혔다. 위=트랜스크립트 / 아래=입력면 두 칸으로 나눈다.
|
|
195
|
+
*/
|
|
196
|
+
const transcript = new pi.Container();
|
|
197
|
+
const bottom = new pi.Container();
|
|
198
|
+
tui.addChild(transcript);
|
|
199
|
+
tui.addChild(bottom);
|
|
200
|
+
const ui = new ShellUi({ lang: ctx.lang }, pi, tui, transcript);
|
|
175
201
|
|
|
176
202
|
// ctx 초크포인트 재지정 — 55파일의 ctx.out 직출력이 전부 프레임 안으로 들어온다.
|
|
177
203
|
const shellCtx = {
|
|
178
204
|
...ctx,
|
|
205
|
+
// 명령이 "지금 사용자가 어디에 서 있는지"를 알아야 안내를 옳게 쓴다.
|
|
206
|
+
surface: "shell",
|
|
179
207
|
uiInstance: ui,
|
|
180
208
|
out: (s = "") => ui.line(String(s)),
|
|
181
209
|
err: (s = "") => ui.line(ui.c.amber(String(s))),
|
|
@@ -227,7 +255,26 @@ async function startShell(ctx, opts = {}) {
|
|
|
227
255
|
description: ui.c.dim, scrollInfo: ui.c.faint, noMatch: ui.c.dim,
|
|
228
256
|
},
|
|
229
257
|
};
|
|
230
|
-
|
|
258
|
+
/*
|
|
259
|
+
* 빈 입력 상자가 "구분선 사이의 빈 칸"으로 보이던 문제(오너 지적). 렌더러의 Editor 는
|
|
260
|
+
* placeholder 를 지원하지 않으므로, 비어 있을 때만 첫 내용 줄 뒤에 힌트를 덧붙인다.
|
|
261
|
+
* 커서는 그 줄에 이미 그려져 있으므로 교체가 아니라 append 여야 안전하다.
|
|
262
|
+
*/
|
|
263
|
+
class ShellEditor extends pi.Editor {
|
|
264
|
+
render(width) {
|
|
265
|
+
const lines = super.render(width);
|
|
266
|
+
if (this.getText() === "" && lines.length >= 3) {
|
|
267
|
+
const hint = ui.c.faint(en
|
|
268
|
+
? "type a task · / for commands"
|
|
269
|
+
: "할 일을 문장으로 · / 명령");
|
|
270
|
+
// 에디터가 줄을 폭까지 공백으로 채운다 — 그대로 덧붙이면 힌트가 오른쪽 끝으로 밀린다.
|
|
271
|
+
// 꼬리 공백만 걷어내고 커서 바로 뒤에 붙인다(ANSI 리셋은 보존).
|
|
272
|
+
lines[1] = lines[1].replace(/[ \t]+(\u001b\[0m)?$/, "$1") + " " + hint;
|
|
273
|
+
}
|
|
274
|
+
return lines;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
const editor = new ShellEditor(tui, editorTheme, { autocompleteMaxVisible: 8, paddingX: 1 });
|
|
231
278
|
editor.setAutocompleteProvider(new pi.CombinedAutocompleteProvider(toSlashCommands(ctx.lang), process.cwd()));
|
|
232
279
|
|
|
233
280
|
// ── 히스토리 디스크 영속 (증분 2) — cli-history.json v2 계약을 그대로 재사용 ──
|
|
@@ -250,14 +297,51 @@ async function startShell(ctx, opts = {}) {
|
|
|
250
297
|
onMessage: (msg) => { ui.ensureNl(); ui.line(ui.c.dim(msg.text)); },
|
|
251
298
|
});
|
|
252
299
|
|
|
300
|
+
/*
|
|
301
|
+
* 목록 피커 — 슬러그를 손으로 받아치게 하지 않는다(오너 지적).
|
|
302
|
+
* SelectList 는 Focusable 이 아니라 포커스로는 키가 안 온다(Loader 와 같은 함정) —
|
|
303
|
+
* 전역 리스너에서 직접 forward 하고, 뜨는 동안 에디터 입력을 막는다.
|
|
304
|
+
*/
|
|
305
|
+
let activePicker = null;
|
|
306
|
+
function pick(items, opts = {}) {
|
|
307
|
+
return new Promise((resolve) => {
|
|
308
|
+
if (!items.length) { resolve(null); return; }
|
|
309
|
+
ui.ensureNl();
|
|
310
|
+
if (opts.title) ui.line(ui.c.bold(opts.title));
|
|
311
|
+
ui.line(ui.c.dim(en
|
|
312
|
+
? "↑/↓ choose · Enter confirm · Esc cancel"
|
|
313
|
+
: "↑/↓ 이동 · Enter 선택 · Esc 취소"));
|
|
314
|
+
const list = new pi.SelectList(items, Math.min(10, items.length), editorTheme.selectList, {});
|
|
315
|
+
const finish = (value) => {
|
|
316
|
+
if (activePicker !== list) return;
|
|
317
|
+
activePicker = null;
|
|
318
|
+
bottom.removeChild(list);
|
|
319
|
+
tui.setFocus(editor);
|
|
320
|
+
tui.requestRender();
|
|
321
|
+
resolve(value);
|
|
322
|
+
};
|
|
323
|
+
list.onSelect = (item) => finish(item);
|
|
324
|
+
list.onCancel = () => finish(null);
|
|
325
|
+
activePicker = list;
|
|
326
|
+
bottom.addChild(list);
|
|
327
|
+
tui.setFocus(null);
|
|
328
|
+
tui.requestRender();
|
|
329
|
+
});
|
|
330
|
+
}
|
|
331
|
+
|
|
253
332
|
const commands = require("../commands/index.cjs");
|
|
254
333
|
const handleSlash = async (cmdline) => {
|
|
255
334
|
const raw = cmdline.split(/\s+/)[0] || "";
|
|
256
|
-
|
|
335
|
+
// 팔레트가 따옴표 인자를 가르치므로 REPL 과 같은 토크나이저를 쓴다.
|
|
336
|
+
const rest = require("../agentlas-input.cjs").tokenizeCommandLine(cmdline).slice(1);
|
|
257
337
|
const cmd = commands.resolveCommandName(raw);
|
|
258
338
|
if (cmd === "quit" || cmd === "exit") return "quit";
|
|
259
339
|
if (cmd === "help") {
|
|
260
|
-
ui.line(palette.renderPalette(ctx.lang));
|
|
340
|
+
ui.line(palette.renderPalette(ctx.lang, { all: String(rest[0] || "") === "all" }));
|
|
341
|
+
ui.line("");
|
|
342
|
+
ui.line(ui.c.dim(en
|
|
343
|
+
? "Tab completes commands · ↑/↓ history · Shift-Tab cycles permission · Esc interrupts a turn"
|
|
344
|
+
: "Tab: 명령 완성 · ↑/↓ 히스토리 · Shift-Tab 권한 순환 · Esc 턴 중단"));
|
|
261
345
|
return;
|
|
262
346
|
}
|
|
263
347
|
// 그래프 보기 (Phase 4) — 캔버스를 흉내내지 않는다: mermaid → 유니코드 박스 아트.
|
|
@@ -275,7 +359,8 @@ async function startShell(ctx, opts = {}) {
|
|
|
275
359
|
lines.push(n.type === "condition" ? ` ${n.id}{${label}}` : ` ${n.id}[${label}]`);
|
|
276
360
|
}
|
|
277
361
|
for (const e of g.edges || []) {
|
|
278
|
-
const lbl = e.sourceHandle === "true" ?
|
|
362
|
+
const lbl = e.sourceHandle === "true" ? (en ? "|yes|" : "|참|")
|
|
363
|
+
: e.sourceHandle === "false" ? (en ? "|no|" : "|거짓|") : "";
|
|
279
364
|
lines.push(` ${e.source} -->${lbl} ${e.target}`);
|
|
280
365
|
}
|
|
281
366
|
const { render, toAnsi } = require("../vendor/mermaid/index.js");
|
|
@@ -288,10 +373,100 @@ async function startShell(ctx, opts = {}) {
|
|
|
288
373
|
} catch { /* 렌더 실패 → 아래 클래식 폴스루가 텍스트로 보여준다 */ }
|
|
289
374
|
}
|
|
290
375
|
}
|
|
376
|
+
/*
|
|
377
|
+
* /search — 결과를 목록으로 띄우고 방향키로 고른다. 슬러그를 손으로 받아치게
|
|
378
|
+
* 하지 않는다(오너 지적). 고르면 바로 설치까지 간다.
|
|
379
|
+
*
|
|
380
|
+
* kind 는 서버 열거값을 그대로 보여주지 않는다. "cloud-callable" 은 사용자에게
|
|
381
|
+
* "설치 안 해도 바로 부를 수 있음"이라는 뜻이지, 설치가 안 된다는 뜻이 아니다.
|
|
382
|
+
*/
|
|
383
|
+
if (cmd === "search" && rest.length) {
|
|
384
|
+
const query = rest.join(" ");
|
|
385
|
+
const { callHubTool, HubError } = require("../cloud/hub-client.cjs");
|
|
386
|
+
let result;
|
|
387
|
+
ui.updateSpinner(en ? "Searching the Hub…" : "Hub 검색 중…");
|
|
388
|
+
try {
|
|
389
|
+
result = await callHubTool("marketplace.search_agents", { q: query, query, limit: 12 });
|
|
390
|
+
} catch (e) {
|
|
391
|
+
ui.stopSpinner();
|
|
392
|
+
ui.error(Object.assign(new Error(e instanceof HubError ? e.message : String((e && e.message) || e)),
|
|
393
|
+
{ code: "hub_search_failed", honestStop: true }));
|
|
394
|
+
return;
|
|
395
|
+
}
|
|
396
|
+
ui.stopSpinner();
|
|
397
|
+
const raw = (result && (result.results || result.agents || result.items)) || (Array.isArray(result) ? result : []);
|
|
398
|
+
const hidden = (slug) => /^researcher-\d+/.test(String(slug || "").toLowerCase())
|
|
399
|
+
|| String(slug || "").toLowerCase().startsWith("hephaestus-");
|
|
400
|
+
const rows = (Array.isArray(raw) ? raw : []).filter((it) => !hidden(it && (it.slug || it.id)));
|
|
401
|
+
if (!rows.length) { ui.line(ui.c.dim(en ? `No results for "${query}"` : `"${query}" 결과 없음`)); return; }
|
|
402
|
+
const callable = (k) => (String(k || "").includes("cloud")
|
|
403
|
+
? (en ? "callable without installing" : "설치 없이 호출 가능")
|
|
404
|
+
: (en ? "install to use" : "설치해야 사용"));
|
|
405
|
+
const chosen = await pick(rows.map((it) => ({
|
|
406
|
+
value: it.slug || it.id || "?",
|
|
407
|
+
label: `${it.slug || it.id}`,
|
|
408
|
+
description: `${it.name || it.title || ""} — ${callable(it.kind || it.entity_kind)}`,
|
|
409
|
+
})), { title: en ? `Hub results for "${query}"` : `"${query}" Hub 결과` });
|
|
410
|
+
if (!chosen) { ui.line(ui.c.dim(en ? "cancelled" : "취소됨")); return; }
|
|
411
|
+
ui.line(ui.c.dim(en ? `installing ${chosen.value}…` : `${chosen.value} 설치 중…`));
|
|
412
|
+
await commands.COMMANDS.install().run(shellCtx, [chosen.value]);
|
|
413
|
+
return;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
/*
|
|
417
|
+
* /graph — 저장된 그래프를 목록으로 띄우고 고른 것을 실행한다.
|
|
418
|
+
* (저장 테이블 이름은 automations 이지만 이 화면이 다루는 건 그래프다.)
|
|
419
|
+
*/
|
|
420
|
+
if (cmd === "graph" && (!rest.length || rest[0] === "list")) {
|
|
421
|
+
const rowsOf = db.prepare("SELECT name, enabled, schedule, graph_json FROM automations ORDER BY name").all();
|
|
422
|
+
const graphs = rowsOf.filter((r) => r.graph_json);
|
|
423
|
+
if (!graphs.length) { ui.line(ui.c.dim(en ? "No saved graphs yet." : "저장된 그래프가 없습니다.")); return; }
|
|
424
|
+
const chosen = await pick(graphs.map((g) => {
|
|
425
|
+
let steps = 0;
|
|
426
|
+
try { steps = (JSON.parse(g.graph_json).nodes || []).length; } catch { steps = 0; }
|
|
427
|
+
return {
|
|
428
|
+
value: g.name,
|
|
429
|
+
label: g.name,
|
|
430
|
+
description: `${steps} ${en ? "steps" : "단계"} · ${g.enabled ? (en ? "on" : "켜짐") : (en ? "off" : "꺼짐")}`
|
|
431
|
+
+ (g.schedule ? ` · ${g.schedule}` : ""),
|
|
432
|
+
};
|
|
433
|
+
}), { title: en ? "Saved graphs" : "저장된 그래프" });
|
|
434
|
+
if (!chosen) { ui.line(ui.c.dim(en ? "cancelled" : "취소됨")); return; }
|
|
435
|
+
await commands.COMMANDS.graph().run(shellCtx, ["run", chosen.value]);
|
|
436
|
+
return;
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
/*
|
|
440
|
+
* 세션 설정 4종. 자동완성은 되는데 처리 case 가 없어 "여기서는 아직 안 됩니다"만
|
|
441
|
+
* 답하던 죽은 광고였다(신설 게이트가 잡았다). 기본 REPL 과 같은 의미로 배선하고,
|
|
442
|
+
* 영구 저장 경로를 같은 줄에서 알려준다 — 이 값들은 이 셸 한정이다.
|
|
443
|
+
*/
|
|
444
|
+
if (cmd === "permission" || cmd === "model" || cmd === "runtime" || cmd === "effort") {
|
|
445
|
+
const value = String(rest[0] || "").trim();
|
|
446
|
+
const entry = require("./commands-catalog.cjs").byName(cmd);
|
|
447
|
+
if (!value) {
|
|
448
|
+
ui.line(ui.c.dim(`Usage: /${cmd} ${entry ? entry.args : ""}`));
|
|
449
|
+
return;
|
|
450
|
+
}
|
|
451
|
+
if (cmd === "permission") {
|
|
452
|
+
const next = permissions.normalize(value);
|
|
453
|
+
if (!next) { ui.line(ui.c.dim("Usage: /permission read|write|full")); return; }
|
|
454
|
+
permission = next;
|
|
455
|
+
ui.line(ui.c.dim(`permission: ${next} · ${en ? "persist: agentlas setup" : "영구 저장: agentlas setup"}`));
|
|
456
|
+
return;
|
|
457
|
+
}
|
|
458
|
+
if (cmd === "model") opts.model = value === "default" ? null : value;
|
|
459
|
+
else if (cmd === "runtime") opts.runtime = value;
|
|
460
|
+
else opts.effort = value;
|
|
461
|
+
ui.line(ui.c.dim(`${cmd}: ${value} · ${en
|
|
462
|
+
? "applies to new sessions here (persist: agentlas roles set)"
|
|
463
|
+
: "이 셸의 새 세션부터 (영구 저장: agentlas roles set)"}`));
|
|
464
|
+
return;
|
|
465
|
+
}
|
|
291
466
|
// 셸 끄기 — 여기서도 되돌아갈 수 있어야 한다(들어와서 못 나가면 갇힌다)
|
|
292
467
|
if (cmd === "shell") {
|
|
293
468
|
const want = String(rest[0] || "").toLowerCase();
|
|
294
|
-
if (!["on", "off"].includes(want)) { ui.line(ui.c.dim("Usage: /shell on|off
|
|
469
|
+
if (!["on", "off"].includes(want)) { ui.line(ui.c.dim(en ? "Usage: /shell on|off" : "사용법: /shell on|off")); return; }
|
|
295
470
|
const config = require("../agentlas-config.cjs");
|
|
296
471
|
const { userDataDir } = require("../core/paths.cjs");
|
|
297
472
|
config.updatePrefs(userDataDir(), { shell: want === "on" ? "interactive" : "classic" });
|
|
@@ -386,7 +561,7 @@ async function startShell(ctx, opts = {}) {
|
|
|
386
561
|
}
|
|
387
562
|
})();
|
|
388
563
|
};
|
|
389
|
-
|
|
564
|
+
bottom.addChild(editor);
|
|
390
565
|
tui.setFocus(editor);
|
|
391
566
|
|
|
392
567
|
const shutdown = (code) => {
|
|
@@ -396,6 +571,13 @@ async function startShell(ctx, opts = {}) {
|
|
|
396
571
|
};
|
|
397
572
|
|
|
398
573
|
tui.addInputListener((data) => {
|
|
574
|
+
// 피커가 떠 있으면 그 키는 피커 것이다 — 에디터로 새면 목록 위에서 글이 써진다.
|
|
575
|
+
if (activePicker) {
|
|
576
|
+
if (pi.matchesKey(data, "escape")) { activePicker.onCancel && activePicker.onCancel(); return { handled: true }; }
|
|
577
|
+
activePicker.handleInput(data);
|
|
578
|
+
tui.requestRender();
|
|
579
|
+
return { handled: true };
|
|
580
|
+
}
|
|
399
581
|
// Shift-Tab 권한 순환 — 렌더러가 raw mode 를 단독 소유하므로 readline 의
|
|
400
582
|
// swallowCompletion 우회 없이 여기서 직접 소비한다 (D2 위험 2의 해소 형태).
|
|
401
583
|
if (pi.matchesKey(data, "shift+tab")) {
|
|
@@ -296,7 +296,13 @@ function coreHarness() {
|
|
|
296
296
|
|
|
297
297
|
async function workforceAccountContext() {
|
|
298
298
|
const cookie = await hubClient.cloudSessionCookie();
|
|
299
|
-
|
|
299
|
+
// 가장 흔한 로그아웃 경로 — 마커가 없으면 표시 경계가 삼켜 "복구 중" 한 줄이 된다.
|
|
300
|
+
if (!cookie) {
|
|
301
|
+
throw Object.assign(
|
|
302
|
+
new Error("Agentlas sign-in required — run `agentlas login`, then retry."),
|
|
303
|
+
{ code: "auth_required", honestStop: true },
|
|
304
|
+
);
|
|
305
|
+
}
|
|
300
306
|
const webBase = (process.env.AGENTLAS_WEB_BASE_URL || "https://agentlas.cloud").replace(/\/$/, "");
|
|
301
307
|
const mcpBase = (process.env.AGENTLAS_MCP_BASE_URL || `${webBase}/api/mcp/v1`).replace(/\/$/, "");
|
|
302
308
|
const response = await hubClient.fetchHub(mcpBase, {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agentlas",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.46",
|
|
4
4
|
"description": "Agentlas project terminal — run project controllers and task-scoped agent teams from the terminal. Standalone: no desktop app process required.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"agentlas": "bin/agentlas.cjs"
|