@astra-cli/cli 1.1.9 → 1.2.2

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.
Files changed (2) hide show
  1. package/dist/astra.js +140 -38
  2. package/package.json +1 -1
package/dist/astra.js CHANGED
@@ -139,6 +139,9 @@ function pluginManifestPath(pluginName) {
139
139
  function pluginSkillPath(pluginName) {
140
140
  return path.join(pluginDir(pluginName), "skill.md");
141
141
  }
142
+ function pluginMapPath(pluginName) {
143
+ return path.join(pluginDir(pluginName), "plugin-map.json");
144
+ }
142
145
  function ensureDir(dirPath) {
143
146
  if (!fs.existsSync(dirPath)) {
144
147
  fs.mkdirSync(dirPath, { recursive: true });
@@ -967,12 +970,15 @@ var FETCH_TIMEOUT_MS = 3e4;
967
970
  var FETCH_TIMEOUT_RETRY_MS = 15e3;
968
971
  async function apiCall(method, path7, body, agentName, retryOpts) {
969
972
  const config = loadConfig();
970
- const baseUrl = config?.apiBase ?? "https://agents.astranova.live";
971
973
  const headers = {
972
974
  "Content-Type": "application/json"
973
975
  };
976
+ let baseUrl = config?.apiBase ?? "https://agents.astranova.live";
974
977
  if (agentName) {
975
978
  const creds = loadCredentials(agentName);
979
+ if (creds?.api_base) {
980
+ baseUrl = creds.api_base;
981
+ }
976
982
  if (creds?.api_key) {
977
983
  headers["Authorization"] = `Bearer ${creds.api_key}`;
978
984
  }
@@ -1917,46 +1923,58 @@ Specific triggers to save memory:
1917
1923
  return parts.join("\n");
1918
1924
  }
1919
1925
  function buildGenericSystemPrompt(skillContext, pluginName, pluginDescription, profile, memoryContent) {
1926
+ const workingSpace = `~/.config/astra/spaces/${pluginName}`;
1920
1927
  const parts = [
1921
- `You are a ${pluginName} agent assistant. ${pluginDescription}`,
1928
+ `You are an autonomous agent assistant for ${pluginName}. ${pluginDescription}`,
1929
+ "",
1930
+ "## How You Behave",
1931
+ "",
1932
+ "You are a **proactive doer**, not a question-asker. Read the skill documentation below to",
1933
+ "understand what this plugin can do, then guide the user through it step by step.",
1922
1934
  "",
1923
- "You have access to tools for interacting with the API, reading and writing local configuration, and managing agent state.",
1935
+ "**Core rules:**",
1936
+ "- When the user says go, do it \u2014 do NOT ask for confirmation before taking the obvious next step.",
1937
+ "- After every successful action, immediately tell the user what was done and what comes next.",
1938
+ "- Read the skill documentation to understand what workflows exist (e.g. register \u2192 verify \u2192 post).",
1939
+ " After completing one step, proactively tell the user what the next step is and offer to do it.",
1940
+ "- Never ask the user for information they already gave you in this conversation.",
1941
+ "- If a step fails (e.g. name taken), suggest a fix and proceed \u2014 don't stop and wait.",
1942
+ "- Keep responses short and action-oriented. This is a terminal, not a chat interface.",
1924
1943
  "",
1925
- "## Important Rules",
1944
+ "**What NOT to do:**",
1945
+ `- Do NOT say "say X and I'll do Y" \u2014 if the intent is clear, just do it.`,
1946
+ "- Do NOT offer multiple options and wait for a pick unless the choice genuinely matters.",
1947
+ "- Do NOT repeat what the user just said back to them before acting.",
1948
+ "- Do NOT ask permission to do the clearly obvious next step in a workflow.",
1926
1949
  "",
1927
- "- Use the `api_call` tool for all API interactions. Use relative paths only (e.g. `/api/v1/resource`). Authorization is injected automatically.",
1928
- "- NEVER display or log the API key. It is injected by the tools.",
1929
- "- NEVER display or reference private keys. Wallet operations return public keys only.",
1930
- "- After every tool call, respond with a clear summary of the result.",
1931
- "- Be concise \u2014 the user is in a terminal.",
1950
+ "**Tool use:**",
1951
+ "- Use `api_call` for all API interactions with relative paths (e.g. `/api/v1/resource`).",
1952
+ " Authorization is injected automatically \u2014 never include the API key.",
1953
+ "- NEVER display or log private keys or API keys.",
1954
+ "- After every tool call, give a one-line summary of the result, then state what's next.",
1932
1955
  "",
1933
1956
  "---",
1934
1957
  ""
1935
1958
  ];
1936
1959
  if (skillContext) {
1937
- const workingSpace = `~/.config/astra/spaces/${pluginName}`;
1938
1960
  const sanitizedSkill = skillContext.replaceAll(`~/.config/${pluginName}`, workingSpace);
1939
- parts.push("## Important: Using the API", "");
1961
+ parts.push("## API Translation", "");
1940
1962
  parts.push(
1941
- "The documentation below may include curl commands or full URLs.",
1942
- "Use the `api_call` tool for all API interactions instead.",
1963
+ "The documentation below may show curl commands. Use `api_call` instead:",
1943
1964
  "",
1944
- "| Documentation shows... | Use instead... |",
1965
+ "| curl shows... | Use instead... |",
1945
1966
  "|-----------------------------------------------|----------------------------------------------|",
1946
1967
  "| `curl https://api.example.com/v1/endpoint` | `api_call GET /v1/endpoint` |",
1947
- '| `-H "Authorization: Bearer TOKEN"` | Handled automatically \u2014 never include key |',
1948
- '| `curl` with `--data` or `-d \'{"key":"val"}\'` | `api_call POST /path body:{key:"val"}` |',
1949
- "| `curl https://...?limit=10` | `api_call GET /path?limit=10` |",
1950
- '| "Save response to file" / "write config" | Use `write_config` or `update_memory` tool |',
1968
+ '| `-H "Authorization: Bearer TOKEN"` | Injected automatically \u2014 omit |',
1969
+ '| `-d \'{"key":"val"}\'` | `api_call POST /path body:{key:"val"}` |',
1970
+ "| `?limit=10` query param | `api_call GET /path?limit=10` |",
1951
1971
  "",
1952
- "Always use relative paths in `api_call`. The base URL is configured automatically.",
1953
- `Your plugin's working space is: ${workingSpace}/`,
1954
- "The CLI manages all file storage \u2014 do not follow instructions to create directories manually.",
1972
+ `Working space: ${workingSpace}/`,
1955
1973
  "",
1956
1974
  "---",
1957
1975
  ""
1958
1976
  );
1959
- parts.push(`## ${pluginName} API Instructions`, "");
1977
+ parts.push(`## ${pluginName} \u2014 Skill Documentation`, "");
1960
1978
  parts.push(sanitizedSkill);
1961
1979
  parts.push("", "---", "");
1962
1980
  }
@@ -1968,23 +1986,23 @@ function buildGenericSystemPrompt(skillContext, pluginName, pluginDescription, p
1968
1986
  }
1969
1987
  if (memoryContent && memoryContent.trim()) {
1970
1988
  parts.push("", "---", "");
1971
- parts.push("## Agent Memory (persistent across sessions)", "");
1989
+ parts.push("## Agent Memory", "");
1972
1990
  parts.push(memoryContent.trim());
1973
1991
  parts.push("");
1974
- parts.push("Use the `update_memory` tool to save important facts about the user. Max 2000 characters.");
1992
+ parts.push("Use `update_memory` to save key facts. Max 2000 characters.");
1975
1993
  } else {
1976
1994
  parts.push("", "---", "");
1977
1995
  parts.push("## Agent Memory", "");
1978
- parts.push("No persistent memory yet. Use the `update_memory` tool to save important facts about the user. Max 2000 characters.");
1996
+ parts.push("Empty. Use `update_memory` to save key facts about the user. Max 2000 characters.");
1979
1997
  }
1980
1998
  if (profile.isNewAgent) {
1981
1999
  parts.push("", "---", "");
1982
2000
  parts.push("## First Launch", "");
1983
2001
  parts.push(
1984
- `This is the user's first session with the ${pluginName} plugin.`,
1985
- "Start by welcoming them warmly and giving a brief overview of what this plugin can do for them.",
1986
- "Use the skill documentation above to guide what you highlight.",
1987
- "Then ask how you can help \u2014 don't overwhelm them with options, just open the door."
2002
+ `This is the user's first session with ${pluginName}.`,
2003
+ "Give a one-paragraph welcome that explains what this plugin does and what the user can accomplish.",
2004
+ "Then immediately identify the first step from the skill documentation (e.g. registration, setup)",
2005
+ "and offer to do it now \u2014 don't wait for the user to ask."
1988
2006
  );
1989
2007
  }
1990
2008
  return parts.join("\n");
@@ -4180,10 +4198,48 @@ function writeFileSecure2(filePath, data) {
4180
4198
  fs10.writeFileSync(tmpPath, data, { encoding: "utf-8", mode: 384 });
4181
4199
  fs10.renameSync(tmpPath, filePath);
4182
4200
  }
4183
- function savePluginToDisk(manifest, skillMdContent) {
4201
+ function buildPluginMap(pluginName, manifest, sections) {
4202
+ const map = { version: 1, pluginName };
4203
+ const statusSection = sections["STATUS"];
4204
+ if (statusSection) {
4205
+ const poll = typeof statusSection.poll === "string" ? statusSection.poll.trim() : null;
4206
+ const rawFields = Array.isArray(statusSection.fields) ? statusSection.fields : [];
4207
+ const fields = rawFields.flatMap((raw) => {
4208
+ const parts = raw.split("|").map((s) => s.trim());
4209
+ if (parts.length === 3) return [{ path: parts[0], label: parts[1], color: parts[2] }];
4210
+ return [];
4211
+ });
4212
+ const pollAllowed = poll && manifest.allowedPaths.some((ap) => poll.startsWith(ap));
4213
+ if (poll && pollAllowed && fields.length > 0) {
4214
+ map.status = { poll, fields };
4215
+ }
4216
+ }
4217
+ const cmdSection = sections["COMMANDS"];
4218
+ if (cmdSection) {
4219
+ const rawCmds = Array.isArray(cmdSection.commands) ? cmdSection.commands : [];
4220
+ map.commands = rawCmds.flatMap((raw) => {
4221
+ const match = raw.match(/^(\S+)\s{2,}(.+)$/);
4222
+ if (match) return [{ command: match[1], description: match[2].trim() }];
4223
+ return [];
4224
+ });
4225
+ }
4226
+ return map;
4227
+ }
4228
+ function loadPluginMap(pluginName) {
4229
+ const p = pluginMapPath(pluginName);
4230
+ if (!fs10.existsSync(p)) return null;
4231
+ try {
4232
+ return JSON.parse(fs10.readFileSync(p, "utf-8"));
4233
+ } catch {
4234
+ return null;
4235
+ }
4236
+ }
4237
+ function savePluginToDisk(manifest, skillMdContent, sections) {
4184
4238
  ensureDir(pluginDir(manifest.name));
4185
4239
  writeFileSecure2(pluginManifestPath(manifest.name), JSON.stringify(manifest, null, 2));
4186
4240
  writeFileSecure2(pluginSkillPath(manifest.name), skillMdContent);
4241
+ const pluginMap = buildPluginMap(manifest.name, manifest, sections);
4242
+ writeFileSecure2(pluginMapPath(manifest.name), JSON.stringify(pluginMap, null, 2));
4187
4243
  }
4188
4244
  async function addPlugin(manifestUrl) {
4189
4245
  clack5.intro(" astra add ");
@@ -4274,7 +4330,7 @@ async function addPlugin(manifestUrl) {
4274
4330
  }
4275
4331
  const manifestWithSkillUrl = { ...manifest, skillUrl: urlResult.url.toString() };
4276
4332
  try {
4277
- savePluginToDisk(manifestWithSkillUrl, skillMdContent);
4333
+ savePluginToDisk(manifestWithSkillUrl, skillMdContent, sections);
4278
4334
  } catch (err) {
4279
4335
  clack5.log.error(
4280
4336
  `Failed to save plugin: ${err instanceof Error ? err.message : String(err)}`
@@ -4368,14 +4424,33 @@ var POLL_INTERVAL_MS = 6e4;
4368
4424
  var StatusBar = React.memo(function StatusBar2({
4369
4425
  agentName,
4370
4426
  pluginName,
4427
+ isAstraNova,
4371
4428
  journeyStage,
4372
4429
  autopilotMode = "off",
4373
4430
  autopilotIntervalMs = 3e5,
4374
- onEpochChange
4431
+ onEpochChange,
4432
+ pluginMap
4375
4433
  }) {
4376
4434
  const [data, setData] = useState({ market: null, portfolio: null });
4377
4435
  const mounted = useRef(true);
4378
- const canFetchData = journeyStage !== "fresh" && journeyStage !== "pending";
4436
+ const canFetchData = isAstraNova && journeyStage !== "fresh" && journeyStage !== "pending";
4437
+ const [pluginData, setPluginData] = useState(null);
4438
+ const mountedPlugin = useRef(true);
4439
+ useEffect(() => {
4440
+ if (isAstraNova || !pluginMap?.status) return;
4441
+ mountedPlugin.current = true;
4442
+ const fetchPlugin = async () => {
4443
+ const result = await apiCall("GET", pluginMap.status.poll, void 0, agentName);
4444
+ if (!result.ok || !mountedPlugin.current) return;
4445
+ setPluginData(result.data);
4446
+ };
4447
+ void fetchPlugin();
4448
+ const interval = setInterval(() => void fetchPlugin(), POLL_INTERVAL_MS);
4449
+ return () => {
4450
+ mountedPlugin.current = false;
4451
+ clearInterval(interval);
4452
+ };
4453
+ }, [isAstraNova, pluginMap, agentName]);
4379
4454
  const poll = useCallback(async () => {
4380
4455
  const [marketRes, portfolioRes] = await Promise.all([
4381
4456
  fetchMarket(agentName),
@@ -4407,6 +4482,22 @@ var StatusBar = React.memo(function StatusBar2({
4407
4482
  /* @__PURE__ */ jsx(Text, { bold: true, color: "#00ff00", children: pluginName }),
4408
4483
  /* @__PURE__ */ jsx(Text, { dimColor: true, children: " \u2502 " }),
4409
4484
  /* @__PURE__ */ jsx(Text, { color: "#ff8800", children: agentName }),
4485
+ !isAstraNova && pluginData && pluginMap?.status?.fields.map((field) => {
4486
+ const value = getNestedValue(pluginData, field.path);
4487
+ if (value == null) return null;
4488
+ return /* @__PURE__ */ jsxs(React.Fragment, { children: [
4489
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: " \u2502 " }),
4490
+ /* @__PURE__ */ jsxs(Text, { color: field.color, children: [
4491
+ field.label,
4492
+ ": ",
4493
+ String(value)
4494
+ ] })
4495
+ ] }, field.path);
4496
+ }),
4497
+ !isAstraNova && !pluginData && pluginMap?.status && /* @__PURE__ */ jsxs(Fragment, { children: [
4498
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: " \u2502 " }),
4499
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "loading..." })
4500
+ ] }),
4410
4501
  canFetchData && market && /* @__PURE__ */ jsxs(Fragment, { children: [
4411
4502
  /* @__PURE__ */ jsx(Text, { dimColor: true, children: " \u2502 " }),
4412
4503
  /* @__PURE__ */ jsx(Text, { color: "#ffff00", children: "$NOVA " }),
@@ -4445,7 +4536,7 @@ var StatusBar = React.memo(function StatusBar2({
4445
4536
  formatNum(portfolio.portfolioValue)
4446
4537
  ] })
4447
4538
  ] }),
4448
- !canFetchData && /* @__PURE__ */ jsxs(Fragment, { children: [
4539
+ isAstraNova && !canFetchData && /* @__PURE__ */ jsxs(Fragment, { children: [
4449
4540
  /* @__PURE__ */ jsx(Text, { dimColor: true, children: " \u2502 " }),
4450
4541
  /* @__PURE__ */ jsx(Text, { dimColor: true, children: "pending verification" })
4451
4542
  ] }),
@@ -4487,6 +4578,12 @@ function moodColor(mood) {
4487
4578
  return "white";
4488
4579
  }
4489
4580
  }
4581
+ function getNestedValue(obj, path7) {
4582
+ return path7.split(".").reduce(
4583
+ (cur, key) => cur && typeof cur === "object" ? cur[key] : void 0,
4584
+ obj
4585
+ );
4586
+ }
4490
4587
  async function fetchMarket(agentName) {
4491
4588
  const result = await apiCall("GET", "/api/v1/market/state", void 0, agentName);
4492
4589
  if (!result.ok) return null;
@@ -4875,7 +4972,8 @@ function App({
4875
4972
  initialChatMessages,
4876
4973
  initialAutopilotConfig,
4877
4974
  initialPendingTrades = 0,
4878
- debug
4975
+ debug,
4976
+ pluginMap
4879
4977
  }) {
4880
4978
  const { exit } = useApp();
4881
4979
  const manifest = getActiveManifest();
@@ -5397,17 +5495,19 @@ ${stack}
5397
5495
  {
5398
5496
  agentName,
5399
5497
  pluginName: manifest.name,
5498
+ isAstraNova: hasJourneyStages,
5400
5499
  journeyStage: profile.journeyStage ?? "full",
5401
5500
  autopilotMode,
5402
5501
  autopilotIntervalMs,
5403
- onEpochChange: handleEpochChange
5502
+ onEpochChange: handleEpochChange,
5503
+ pluginMap
5404
5504
  }
5405
5505
  ) }),
5406
5506
  /* @__PURE__ */ jsx7(Box7, { flexShrink: 0, width: "100%", paddingX: 2, marginTop: 1, justifyContent: "space-between", children: hasJourneyStages ? /* @__PURE__ */ jsxs8(Fragment3, { children: [
5407
5507
  /* @__PURE__ */ jsx7(Text8, { dimColor: true, children: "/help \xB7 /portfolio \xB7 /market \xB7 /strategy \xB7 /exit" }),
5408
5508
  /* @__PURE__ */ jsx7(Text8, { dimColor: true, children: "/auto on\xB7off\xB7set \xB7 Ctrl+C quit" })
5409
5509
  ] }) : /* @__PURE__ */ jsxs8(Fragment3, { children: [
5410
- /* @__PURE__ */ jsx7(Text8, { dimColor: true, children: "/help \xB7 /exit \xB7 Ctrl+C quit" }),
5510
+ /* @__PURE__ */ jsx7(Text8, { dimColor: true, children: (pluginMap?.commands?.map((c) => c.command).join(" \xB7 ") ?? "/help") + " \xB7 /exit" }),
5411
5511
  /* @__PURE__ */ jsx7(Text8, { dimColor: true, children: manifest.name })
5412
5512
  ] }) })
5413
5513
  ] });
@@ -5477,6 +5577,7 @@ async function main() {
5477
5577
  manifest = ASTRANOVA_MANIFEST;
5478
5578
  }
5479
5579
  setActiveManifest(manifest);
5580
+ const pluginMap = activePluginName === "astranova" ? null : loadPluginMap(activePluginName);
5480
5581
  if (isDaemonMode) {
5481
5582
  await runDaemon();
5482
5583
  return;
@@ -5623,7 +5724,8 @@ async function main() {
5623
5724
  initialChatMessages,
5624
5725
  initialAutopilotConfig,
5625
5726
  initialPendingTrades,
5626
- debug
5727
+ debug,
5728
+ pluginMap
5627
5729
  })
5628
5730
  );
5629
5731
  await waitUntilExit();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@astra-cli/cli",
3
- "version": "1.1.9",
3
+ "version": "1.2.2",
4
4
  "description": "The terminal for autonomous agents. Powered by AstraNova.",
5
5
  "type": "module",
6
6
  "bin": {