agentlas 1.0.45 → 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 CHANGED
@@ -1,5 +1,18 @@
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
+
3
16
  ## 1.0.45 — 2026-08-11
4
17
 
5
18
  The command surface was rebuilt from one catalog, and failures stopped printing JSON.
@@ -153,8 +153,8 @@ function listGraphs(ctx) {
153
153
  }
154
154
  ctx.out("");
155
155
  ctx.out(ctx.ui.dim(en
156
- ? "Run one with: agentlas graph run \"<name>\""
157
- : "실행하려면: agentlas graph run \"<이름>\""));
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
- const approval = node.config?.approval;
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 ? "See what is saved with: agentlas graph list" : "저장된 목록: agentlas graph list");
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
- ctx.out(en ? "Steps that go outside (locked to ask first):" : "바깥으로 나가는 단계 (실행 전에 확인하도록 잠급니다):");
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 and ask before they run unless you set them to automatic."
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."
@@ -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
- ctx.out(ctx.ui.dim("Install: agentlas install <slug>"));
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
 
@@ -202,6 +202,8 @@ async function startShell(ctx, opts = {}) {
202
202
  // ctx 초크포인트 재지정 — 55파일의 ctx.out 직출력이 전부 프레임 안으로 들어온다.
203
203
  const shellCtx = {
204
204
  ...ctx,
205
+ // 명령이 "지금 사용자가 어디에 서 있는지"를 알아야 안내를 옳게 쓴다.
206
+ surface: "shell",
205
207
  uiInstance: ui,
206
208
  out: (s = "") => ui.line(String(s)),
207
209
  err: (s = "") => ui.line(ui.c.amber(String(s))),
@@ -295,6 +297,38 @@ async function startShell(ctx, opts = {}) {
295
297
  onMessage: (msg) => { ui.ensureNl(); ui.line(ui.c.dim(msg.text)); },
296
298
  });
297
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
+
298
332
  const commands = require("../commands/index.cjs");
299
333
  const handleSlash = async (cmdline) => {
300
334
  const raw = cmdline.split(/\s+/)[0] || "";
@@ -339,6 +373,69 @@ async function startShell(ctx, opts = {}) {
339
373
  } catch { /* 렌더 실패 → 아래 클래식 폴스루가 텍스트로 보여준다 */ }
340
374
  }
341
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
+
342
439
  /*
343
440
  * 세션 설정 4종. 자동완성은 되는데 처리 case 가 없어 "여기서는 아직 안 됩니다"만
344
441
  * 답하던 죽은 광고였다(신설 게이트가 잡았다). 기본 REPL 과 같은 의미로 배선하고,
@@ -474,6 +571,13 @@ async function startShell(ctx, opts = {}) {
474
571
  };
475
572
 
476
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
+ }
477
581
  // Shift-Tab 권한 순환 — 렌더러가 raw mode 를 단독 소유하므로 readline 의
478
582
  // swallowCompletion 우회 없이 여기서 직접 소비한다 (D2 위험 2의 해소 형태).
479
583
  if (pi.matchesKey(data, "shift+tab")) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentlas",
3
- "version": "1.0.45",
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"