@agi_inc/cli 0.5.9 → 0.5.10

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/dist/index.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  // src/index.tsx
2
2
  import React10 from "react";
3
3
  import { render } from "ink";
4
- import { isBinaryAvailable } from "@agi_inc/agi-js";
4
+ import { isBinaryAvailable as isBinaryAvailable2 } from "@agi_inc/agi-js";
5
5
 
6
6
  // src/cli.ts
7
7
  import yargs from "yargs";
@@ -288,7 +288,7 @@ async function parseArgs() {
288
288
  }
289
289
 
290
290
  // src/app/App.tsx
291
- import React9, { useEffect as useEffect5, useState as useState7, useCallback as useCallback3, useMemo as useMemo2 } from "react";
291
+ import React9, { useEffect as useEffect5, useState as useState7, useCallback as useCallback3, useMemo as useMemo2, useRef as useRef3 } from "react";
292
292
  import { Box as Box8, Text as Text9, useApp } from "ink";
293
293
 
294
294
  // src/hooks/useAgent.ts
@@ -674,8 +674,332 @@ import { Text as Text6, Box as Box5, useInput as useInput4 } from "ink";
674
674
  import TextInput2 from "ink-text-input";
675
675
 
676
676
  // src/commands/slash.ts
677
- import { readFileSync as readFileSync2 } from "fs";
678
- import { basename } from "path";
677
+ import { readFileSync as readFileSync3 } from "fs";
678
+ import { basename as basename2 } from "path";
679
+ import os4 from "os";
680
+ import { isBinaryAvailable } from "@agi_inc/agi-js";
681
+
682
+ // src/history.ts
683
+ import fs2 from "fs";
684
+ import path2 from "path";
685
+ import os2 from "os";
686
+ var HISTORY_DIR = path2.join(os2.homedir(), ".agi");
687
+ var HISTORY_FILE = path2.join(HISTORY_DIR, "history.json");
688
+ var MAX_ENTRIES = 200;
689
+ function ensureDir() {
690
+ if (!fs2.existsSync(HISTORY_DIR)) {
691
+ fs2.mkdirSync(HISTORY_DIR, { recursive: true, mode: 448 });
692
+ }
693
+ }
694
+ function loadHistory() {
695
+ try {
696
+ const raw = fs2.readFileSync(HISTORY_FILE, "utf-8");
697
+ const data = JSON.parse(raw);
698
+ if (Array.isArray(data)) return data;
699
+ } catch {
700
+ }
701
+ return [];
702
+ }
703
+ function appendHistory(entry) {
704
+ ensureDir();
705
+ const history = loadHistory();
706
+ history.push(entry);
707
+ const trimmed = history.slice(-MAX_ENTRIES);
708
+ fs2.writeFileSync(HISTORY_FILE, JSON.stringify(trimmed, null, 2) + "\n", {
709
+ mode: 384
710
+ });
711
+ }
712
+ function clearHistory() {
713
+ try {
714
+ fs2.unlinkSync(HISTORY_FILE);
715
+ } catch {
716
+ }
717
+ }
718
+ function formatHistory(entries, limit = 20) {
719
+ if (entries.length === 0) return " No history yet.";
720
+ const recent = entries.slice(-limit).reverse();
721
+ const lines = ["", " Recent sessions:", ""];
722
+ for (const entry of recent) {
723
+ const date = new Date(entry.timestamp);
724
+ const timeStr = date.toLocaleDateString("en-US", {
725
+ month: "short",
726
+ day: "numeric"
727
+ }) + " " + date.toLocaleTimeString("en-US", {
728
+ hour: "2-digit",
729
+ minute: "2-digit",
730
+ hour12: false
731
+ });
732
+ const status = entry.success === true ? "\u25CF" : entry.success === false ? "\u2715" : "\u25CB";
733
+ const duration = entry.durationMs ? ` (${Math.round(entry.durationMs / 1e3)}s)` : "";
734
+ const goal = entry.goal.length > 55 ? entry.goal.slice(0, 55) + "\u2026" : entry.goal;
735
+ lines.push(` ${status} ${timeStr} ${goal}${duration}`);
736
+ }
737
+ lines.push("");
738
+ if (entries.length > limit) {
739
+ lines.push(` Showing ${limit} of ${entries.length} entries`);
740
+ lines.push("");
741
+ }
742
+ return lines.join("\n");
743
+ }
744
+
745
+ // src/project.ts
746
+ import { readFileSync as readFileSync2, existsSync, readdirSync } from "fs";
747
+ import { join as join2, basename } from "path";
748
+
749
+ // src/skills.ts
750
+ import fs3 from "fs";
751
+ import path3 from "path";
752
+ import os3 from "os";
753
+ function parseFrontmatter(content) {
754
+ const match = content.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
755
+ if (!match) return { meta: {}, body: content.trim() };
756
+ const meta = {};
757
+ for (const line of match[1].split("\n")) {
758
+ const colonIdx = line.indexOf(":");
759
+ if (colonIdx > 0) {
760
+ const key = line.slice(0, colonIdx).trim();
761
+ const value = line.slice(colonIdx + 1).trim();
762
+ meta[key] = value;
763
+ }
764
+ }
765
+ return { meta, body: match[2].trim() };
766
+ }
767
+ var NAME_RE = /^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/;
768
+ function isValidSkillName(name) {
769
+ if (name.length > 64) return false;
770
+ if (!NAME_RE.test(name)) return false;
771
+ if (name.includes("--")) return false;
772
+ return true;
773
+ }
774
+ function discoverSkillsInDir(skillsDir, scope) {
775
+ const skills = [];
776
+ if (!fs3.existsSync(skillsDir)) return skills;
777
+ let entries;
778
+ try {
779
+ entries = fs3.readdirSync(skillsDir);
780
+ } catch {
781
+ return skills;
782
+ }
783
+ for (const entry of entries) {
784
+ const skillDir = path3.join(skillsDir, entry);
785
+ const skillFile = path3.join(skillDir, "SKILL.md");
786
+ try {
787
+ if (!fs3.statSync(skillDir).isDirectory()) continue;
788
+ if (!fs3.existsSync(skillFile)) continue;
789
+ } catch {
790
+ continue;
791
+ }
792
+ if (!isValidSkillName(entry)) continue;
793
+ try {
794
+ const content = fs3.readFileSync(skillFile, "utf-8");
795
+ const { meta } = parseFrontmatter(content);
796
+ const name = meta["name"] || entry;
797
+ if (name !== entry) continue;
798
+ const description = meta["description"] || "";
799
+ if (!description) continue;
800
+ skills.push({
801
+ name,
802
+ description,
803
+ path: skillFile,
804
+ root: skillDir,
805
+ scope,
806
+ license: meta["license"],
807
+ compatibility: meta["compatibility"],
808
+ userInvocable: meta["user-invocable"] !== "false",
809
+ argumentHint: meta["argument-hint"],
810
+ metadata: Object.keys(meta).length > 0 ? meta : void 0
811
+ });
812
+ } catch {
813
+ }
814
+ }
815
+ return skills;
816
+ }
817
+ function discoverSkills(cwd = process.cwd()) {
818
+ const personalDir = path3.join(os3.homedir(), ".agi", "skills");
819
+ const projectDir = path3.join(cwd, ".agi", "skills");
820
+ const personal = discoverSkillsInDir(personalDir, "personal");
821
+ const project = discoverSkillsInDir(projectDir, "project");
822
+ const byName = /* @__PURE__ */ new Map();
823
+ for (const skill of personal) byName.set(skill.name, skill);
824
+ for (const skill of project) byName.set(skill.name, skill);
825
+ return Array.from(byName.values());
826
+ }
827
+ function loadSkill(skill) {
828
+ const content = fs3.readFileSync(skill.path, "utf-8");
829
+ const { body } = parseFrontmatter(content);
830
+ return { ...skill, body };
831
+ }
832
+ function generateSkillsPrompt(skills) {
833
+ if (skills.length === 0) return "";
834
+ const lines = ["<available_skills>"];
835
+ for (const skill of skills) {
836
+ lines.push(" <skill>");
837
+ lines.push(` <name>${skill.name}</name>`);
838
+ lines.push(` <description>${skill.description}</description>`);
839
+ lines.push(` <location>${skill.path}</location>`);
840
+ lines.push(" </skill>");
841
+ }
842
+ lines.push("</available_skills>");
843
+ return lines.join("\n");
844
+ }
845
+ function formatSkillsList(skills) {
846
+ if (skills.length === 0) return " No skills found.";
847
+ const lines = ["", " Available skills:", ""];
848
+ for (const skill of skills) {
849
+ const scope = skill.scope === "personal" ? "~" : ".";
850
+ const hint = skill.argumentHint ? ` ${skill.argumentHint}` : "";
851
+ lines.push(` ${scope} /${skill.name}${hint}`);
852
+ lines.push(` ${skill.description}`);
853
+ }
854
+ lines.push("");
855
+ return lines.join("\n");
856
+ }
857
+
858
+ // src/project.ts
859
+ import { mkdirSync, writeFileSync } from "fs";
860
+ var INSTRUCTION_FILES = ["AGI.md", ".agi/AGI.md", ".agi/instructions.md"];
861
+ function loadRules(cwd) {
862
+ const rulesDir = join2(cwd, ".agi", "rules");
863
+ if (!existsSync(rulesDir)) return [];
864
+ const rules = [];
865
+ try {
866
+ const files = readdirSync(rulesDir).filter((f) => f.endsWith(".md")).sort();
867
+ for (const file of files) {
868
+ const filePath = join2(rulesDir, file);
869
+ try {
870
+ const raw = readFileSync2(filePath, "utf-8");
871
+ const name = basename(file, ".md");
872
+ let content = raw.trim();
873
+ let paths;
874
+ const fmMatch = content.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
875
+ if (fmMatch) {
876
+ const meta = fmMatch[1];
877
+ content = fmMatch[2].trim();
878
+ const pathsMatch = meta.match(/^paths:\s*(.+)$/m);
879
+ if (pathsMatch) {
880
+ paths = pathsMatch[1].split(",").map((p) => p.trim()).filter(Boolean);
881
+ }
882
+ }
883
+ if (content) {
884
+ rules.push({ name, path: filePath, content, paths });
885
+ }
886
+ } catch {
887
+ }
888
+ }
889
+ } catch {
890
+ }
891
+ return rules;
892
+ }
893
+ function loadProjectConfig(cwd = process.cwd()) {
894
+ let instructions = null;
895
+ let instructionsPath = null;
896
+ for (const relPath of INSTRUCTION_FILES) {
897
+ const fullPath = join2(cwd, relPath);
898
+ if (existsSync(fullPath)) {
899
+ try {
900
+ instructions = readFileSync2(fullPath, "utf-8").trim();
901
+ instructionsPath = fullPath;
902
+ break;
903
+ } catch {
904
+ }
905
+ }
906
+ }
907
+ const agiDir = join2(cwd, ".agi");
908
+ const agiDirExists = existsSync(agiDir);
909
+ const commandFiles = [];
910
+ const commandsDir = join2(cwd, ".agi", "commands");
911
+ if (existsSync(commandsDir)) {
912
+ try {
913
+ const files = readdirSync(commandsDir);
914
+ for (const file of files) {
915
+ if (file.endsWith(".md")) {
916
+ commandFiles.push(join2(commandsDir, file));
917
+ }
918
+ }
919
+ } catch {
920
+ }
921
+ }
922
+ const rules = loadRules(cwd);
923
+ const skills = discoverSkills(cwd);
924
+ return {
925
+ instructions,
926
+ instructionsPath,
927
+ agiDir: agiDirExists ? agiDir : null,
928
+ commandFiles,
929
+ rules,
930
+ skills
931
+ };
932
+ }
933
+ function withProjectInstructions(goal, config) {
934
+ const sections = [];
935
+ if (config.instructions) {
936
+ sections.push(
937
+ "--- Project Instructions (from " + (config.instructionsPath ?? "AGI.md") + ") ---",
938
+ config.instructions,
939
+ "--- End Project Instructions ---"
940
+ );
941
+ }
942
+ if (config.rules.length > 0) {
943
+ sections.push("--- Project Rules ---");
944
+ for (const rule of config.rules) {
945
+ sections.push(`[${rule.name}]`);
946
+ sections.push(rule.content);
947
+ sections.push("");
948
+ }
949
+ sections.push("--- End Project Rules ---");
950
+ }
951
+ if (config.skills.length > 0) {
952
+ sections.push(generateSkillsPrompt(config.skills));
953
+ }
954
+ if (sections.length === 0) return goal;
955
+ sections.push("", "Task: " + goal);
956
+ return sections.join("\n");
957
+ }
958
+ function initProject(cwd = process.cwd()) {
959
+ const created = [];
960
+ const agiMd = join2(cwd, "AGI.md");
961
+ if (!existsSync(agiMd)) {
962
+ writeFileSync(
963
+ agiMd,
964
+ [
965
+ "# Project Instructions",
966
+ "",
967
+ "<!-- Add project-specific instructions for the AGI agent here. -->",
968
+ "<!-- These instructions are loaded automatically when you run `agi`. -->",
969
+ ""
970
+ ].join("\n")
971
+ );
972
+ created.push("AGI.md");
973
+ }
974
+ const dirs = [
975
+ ".agi",
976
+ ".agi/commands",
977
+ ".agi/rules",
978
+ ".agi/skills"
979
+ ];
980
+ for (const dir of dirs) {
981
+ const fullPath = join2(cwd, dir);
982
+ if (!existsSync(fullPath)) {
983
+ mkdirSync(fullPath, { recursive: true });
984
+ created.push(dir + "/");
985
+ }
986
+ }
987
+ const gitignore = join2(cwd, ".agi", ".gitignore");
988
+ if (!existsSync(gitignore)) {
989
+ writeFileSync(
990
+ gitignore,
991
+ [
992
+ "# Local files that should not be committed",
993
+ "local.md",
994
+ ""
995
+ ].join("\n")
996
+ );
997
+ created.push(".agi/.gitignore");
998
+ }
999
+ return created;
1000
+ }
1001
+
1002
+ // src/commands/slash.ts
679
1003
  var VALID_MODELS = ["claude-sonnet", "claude-opus"];
680
1004
  var builtinCommands = [
681
1005
  {
@@ -689,8 +1013,9 @@ var builtinCommands = [
689
1013
  " Available commands:",
690
1014
  ""
691
1015
  ];
692
- const builtins = all.filter((c) => !c.isUserDefined);
1016
+ const builtins = all.filter((c) => !c.isUserDefined && !c.isSkill);
693
1017
  const userDefined = all.filter((c) => c.isUserDefined);
1018
+ const skills = all.filter((c) => c.isSkill);
694
1019
  for (const cmd of builtins) {
695
1020
  const aliases = cmd.aliases?.length ? ` (${cmd.aliases.map((a) => "/" + a).join(", ")})` : "";
696
1021
  const usage = cmd.usage ? ` ${cmd.usage}` : "";
@@ -707,6 +1032,65 @@ var builtinCommands = [
707
1032
  lines.push(` ${cmd.description}`);
708
1033
  }
709
1034
  }
1035
+ if (skills.length > 0) {
1036
+ lines.push("");
1037
+ lines.push(" Skills (.agi/skills/):");
1038
+ lines.push("");
1039
+ for (const cmd of skills) {
1040
+ const usage = cmd.usage ? ` ${cmd.usage}` : "";
1041
+ lines.push(` /${cmd.name}${usage}`);
1042
+ lines.push(` ${cmd.description}`);
1043
+ }
1044
+ }
1045
+ lines.push("");
1046
+ return lines.join("\n");
1047
+ }
1048
+ },
1049
+ {
1050
+ name: "history",
1051
+ aliases: ["hist"],
1052
+ description: "Show session history",
1053
+ usage: "[clear]",
1054
+ execute: (args) => {
1055
+ if (args.trim().toLowerCase() === "clear") {
1056
+ clearHistory();
1057
+ return " History cleared.";
1058
+ }
1059
+ const entries = loadHistory();
1060
+ return formatHistory(entries);
1061
+ }
1062
+ },
1063
+ {
1064
+ name: "status",
1065
+ aliases: ["info"],
1066
+ description: "Show environment and session info",
1067
+ execute: (_args, ctx) => {
1068
+ const lines = ["", " Environment:", ""];
1069
+ lines.push(` Version ${ctx.version || "unknown"}`);
1070
+ lines.push(` Model ${ctx.model}`);
1071
+ lines.push(` Working dir ${process.cwd().replace(os4.homedir(), "~")}`);
1072
+ lines.push(` Node.js ${process.version}`);
1073
+ lines.push(` Platform ${process.platform} ${process.arch}`);
1074
+ lines.push(` Driver ${isBinaryAvailable() ? "\u2714 available" : "\u2718 not found"}`);
1075
+ lines.push(` API key ${process.env.AGI_API_KEY ? "\u2714 set" : process.env.ANTHROPIC_API_KEY ? "\u2714 set (ANTHROPIC_API_KEY)" : "\u2718 not set"}`);
1076
+ lines.push("");
1077
+ lines.push(" Session:");
1078
+ lines.push("");
1079
+ lines.push(` Verbose ${ctx.verbose ? "on" : "off"}`);
1080
+ lines.push(` Auto-confirm ${ctx.noConfirm ? "on" : "off"}`);
1081
+ lines.push(` Compact mode ${ctx.compact ? "on" : "off"}`);
1082
+ if (ctx.projectConfig) {
1083
+ lines.push("");
1084
+ lines.push(" Project:");
1085
+ lines.push("");
1086
+ lines.push(` Instructions ${ctx.projectConfig.instructionsPath?.replace(process.cwd(), ".") || "none"}`);
1087
+ lines.push(` Rules ${ctx.projectConfig.rules.length} loaded`);
1088
+ lines.push(` Commands ${ctx.projectConfig.commandFiles.length} custom`);
1089
+ lines.push(` Skills ${ctx.projectConfig.skills.length} discovered`);
1090
+ }
1091
+ const history = loadHistory();
1092
+ lines.push("");
1093
+ lines.push(` History ${history.length} past sessions`);
710
1094
  lines.push("");
711
1095
  return lines.join("\n");
712
1096
  }
@@ -737,6 +1121,14 @@ var builtinCommands = [
737
1121
  return ` Verbose output ${!ctx.verbose ? "on" : "off"}`;
738
1122
  }
739
1123
  },
1124
+ {
1125
+ name: "compact",
1126
+ description: "Toggle compact output mode",
1127
+ execute: (_args, ctx) => {
1128
+ ctx.setCompact(!ctx.compact);
1129
+ return ` Compact mode ${!ctx.compact ? "on" : "off"}`;
1130
+ }
1131
+ },
740
1132
  {
741
1133
  name: "confirm",
742
1134
  description: "Toggle auto-confirm mode",
@@ -745,6 +1137,80 @@ var builtinCommands = [
745
1137
  return ` Auto-confirm ${!ctx.noConfirm ? "on" : "off"}`;
746
1138
  }
747
1139
  },
1140
+ {
1141
+ name: "init",
1142
+ description: "Initialize AGI.md and .agi/ project structure",
1143
+ execute: () => {
1144
+ const created = initProject();
1145
+ if (created.length === 0) {
1146
+ return " Project already initialized \u2014 AGI.md and .agi/ exist.";
1147
+ }
1148
+ const lines = ["", " Initialized project:", ""];
1149
+ for (const item of created) {
1150
+ lines.push(` \u2714 Created ${item}`);
1151
+ }
1152
+ lines.push("");
1153
+ lines.push(" Edit AGI.md to add project instructions for the agent.");
1154
+ lines.push(" Add custom commands in .agi/commands/*.md");
1155
+ lines.push(" Add rules in .agi/rules/*.md");
1156
+ lines.push(" Add skills in .agi/skills/<name>/SKILL.md");
1157
+ lines.push("");
1158
+ return lines.join("\n");
1159
+ }
1160
+ },
1161
+ {
1162
+ name: "doctor",
1163
+ aliases: ["check"],
1164
+ description: "Check system health and configuration",
1165
+ execute: (_args, ctx) => {
1166
+ const checks = [];
1167
+ const hasKey = !!(process.env.AGI_API_KEY || process.env.ANTHROPIC_API_KEY);
1168
+ checks.push({
1169
+ label: "API key",
1170
+ ok: hasKey,
1171
+ detail: hasKey ? "configured" : "missing \u2014 run `agi login` or set AGI_API_KEY"
1172
+ });
1173
+ const hasDriver = isBinaryAvailable();
1174
+ checks.push({
1175
+ label: "Driver binary",
1176
+ ok: hasDriver,
1177
+ detail: hasDriver ? "found" : "not found \u2014 reinstall @agi_inc/cli"
1178
+ });
1179
+ const nodeVer = process.versions.node;
1180
+ const [major] = nodeVer.split(".").map(Number);
1181
+ checks.push({
1182
+ label: "Node.js",
1183
+ ok: major >= 18,
1184
+ detail: `v${nodeVer}${major < 18 ? " \u2014 v18+ required" : ""}`
1185
+ });
1186
+ if (ctx.projectConfig) {
1187
+ checks.push({
1188
+ label: "Project instructions",
1189
+ ok: !!ctx.projectConfig.instructions,
1190
+ detail: ctx.projectConfig.instructionsPath?.replace(process.cwd(), ".") || "none \u2014 run /init"
1191
+ });
1192
+ }
1193
+ const lines = ["", " System health:", ""];
1194
+ for (const check of checks) {
1195
+ const icon = check.ok ? "\u2714" : "\u2718";
1196
+ const color = check.ok ? "" : "";
1197
+ lines.push(` ${icon} ${check.label.padEnd(22)} ${check.detail}`);
1198
+ }
1199
+ const allOk = checks.every((c) => c.ok);
1200
+ lines.push("");
1201
+ lines.push(allOk ? " All checks passed." : " Some checks failed \u2014 see above.");
1202
+ lines.push("");
1203
+ return lines.join("\n");
1204
+ }
1205
+ },
1206
+ {
1207
+ name: "skills",
1208
+ description: "List discovered skills",
1209
+ execute: (_args, ctx) => {
1210
+ if (!ctx.projectConfig) return " No project config loaded.";
1211
+ return formatSkillsList(ctx.projectConfig.skills);
1212
+ }
1213
+ },
748
1214
  {
749
1215
  name: "clear",
750
1216
  aliases: ["c"],
@@ -764,7 +1230,7 @@ var builtinCommands = [
764
1230
  }
765
1231
  }
766
1232
  ];
767
- function parseFrontmatter(content) {
1233
+ function parseFrontmatter2(content) {
768
1234
  const match = content.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
769
1235
  if (!match) return { meta: {}, body: content.trim() };
770
1236
  const meta = {};
@@ -782,9 +1248,9 @@ function loadUserCommands(commandFiles) {
782
1248
  const userCommands = [];
783
1249
  for (const filePath of commandFiles) {
784
1250
  try {
785
- const content = readFileSync2(filePath, "utf-8");
786
- const { meta, body } = parseFrontmatter(content);
787
- const name = basename(filePath, ".md");
1251
+ const content = readFileSync3(filePath, "utf-8");
1252
+ const { meta, body } = parseFrontmatter2(content);
1253
+ const name = basename2(filePath, ".md");
788
1254
  const description = meta["description"] || body.split("\n")[0].slice(0, 60);
789
1255
  const usage = meta["argument-hint"];
790
1256
  userCommands.push({
@@ -810,6 +1276,26 @@ function loadUserCommands(commandFiles) {
810
1276
  }
811
1277
  return userCommands;
812
1278
  }
1279
+ function loadSkillCommands(skills) {
1280
+ return skills.filter((s) => s.userInvocable).map((skill) => ({
1281
+ name: skill.name,
1282
+ description: skill.description,
1283
+ usage: skill.argumentHint,
1284
+ isSkill: true,
1285
+ execute: (args, ctx) => {
1286
+ if (!ctx.runGoal) return ` Error: cannot run skill commands in this context`;
1287
+ const loaded = loadSkill(skill);
1288
+ let prompt = loaded.body;
1289
+ prompt = prompt.replace(/\$ARGUMENTS/g, args);
1290
+ const argParts = args.split(/\s+/).filter(Boolean);
1291
+ for (let i = 0; i < argParts.length; i++) {
1292
+ prompt = prompt.replace(new RegExp(`\\$${i + 1}`, "g"), argParts[i]);
1293
+ }
1294
+ ctx.runGoal(prompt);
1295
+ return null;
1296
+ }
1297
+ }));
1298
+ }
813
1299
  var allCommands = [...builtinCommands];
814
1300
  var commandMap = /* @__PURE__ */ new Map();
815
1301
  function rebuildMap() {
@@ -1034,57 +1520,6 @@ var Logo = ({
1034
1520
  )), info[r] != null && /* @__PURE__ */ React8.createElement(React8.Fragment, null, /* @__PURE__ */ React8.createElement(Text8, null, " "), info[r]))));
1035
1521
  };
1036
1522
 
1037
- // src/project.ts
1038
- import { readFileSync as readFileSync3, existsSync, readdirSync } from "fs";
1039
- import { join as join2 } from "path";
1040
- var INSTRUCTION_FILES = ["AGI.md", ".agi/AGI.md", ".agi/instructions.md"];
1041
- function loadProjectConfig(cwd = process.cwd()) {
1042
- let instructions = null;
1043
- let instructionsPath = null;
1044
- for (const relPath of INSTRUCTION_FILES) {
1045
- const fullPath = join2(cwd, relPath);
1046
- if (existsSync(fullPath)) {
1047
- try {
1048
- instructions = readFileSync3(fullPath, "utf-8").trim();
1049
- instructionsPath = fullPath;
1050
- break;
1051
- } catch {
1052
- }
1053
- }
1054
- }
1055
- const agiDir = join2(cwd, ".agi");
1056
- const agiDirExists = existsSync(agiDir);
1057
- const commandFiles = [];
1058
- const commandsDir = join2(cwd, ".agi", "commands");
1059
- if (existsSync(commandsDir)) {
1060
- try {
1061
- const files = readdirSync(commandsDir);
1062
- for (const file of files) {
1063
- if (file.endsWith(".md")) {
1064
- commandFiles.push(join2(commandsDir, file));
1065
- }
1066
- }
1067
- } catch {
1068
- }
1069
- }
1070
- return {
1071
- instructions,
1072
- instructionsPath,
1073
- agiDir: agiDirExists ? agiDir : null,
1074
- commandFiles
1075
- };
1076
- }
1077
- function withProjectInstructions(goal, config) {
1078
- if (!config.instructions) return goal;
1079
- return [
1080
- "--- Project Instructions (from " + (config.instructionsPath ?? "AGI.md") + ") ---",
1081
- config.instructions,
1082
- "--- End Project Instructions ---",
1083
- "",
1084
- "Task: " + goal
1085
- ].join("\n");
1086
- }
1087
-
1088
1523
  // src/app/App.tsx
1089
1524
  import { createRequire } from "module";
1090
1525
  var require2 = createRequire(import.meta.url);
@@ -1095,16 +1530,24 @@ var App = ({ args }) => {
1095
1530
  const [currentGoal, setCurrentGoal] = useState7(args.goal ?? "");
1096
1531
  const projectConfig = useMemo2(() => {
1097
1532
  const config = loadProjectConfig();
1533
+ const cmds = [];
1098
1534
  if (config.commandFiles.length > 0) {
1099
- const userCmds = loadUserCommands(config.commandFiles);
1100
- registerCommands(userCmds);
1535
+ cmds.push(...loadUserCommands(config.commandFiles));
1536
+ }
1537
+ if (config.skills.length > 0) {
1538
+ cmds.push(...loadSkillCommands(config.skills));
1539
+ }
1540
+ if (cmds.length > 0) {
1541
+ registerCommands(cmds);
1101
1542
  }
1102
1543
  return config;
1103
1544
  }, []);
1104
1545
  const [model, setModel] = useState7(args.model);
1105
1546
  const [verbose, setVerbose] = useState7(args.verbose);
1106
1547
  const [noConfirm, setNoConfirm] = useState7(args.noConfirm);
1548
+ const [compact, setCompact] = useState7(false);
1107
1549
  const [commandMessage, setCommandMessage] = useState7(null);
1550
+ const taskStartTime = useRef3(null);
1108
1551
  const {
1109
1552
  state,
1110
1553
  step,
@@ -1122,12 +1565,24 @@ var App = ({ args }) => {
1122
1565
  model,
1123
1566
  verbose,
1124
1567
  noConfirm,
1125
- onFinished: () => {
1568
+ onFinished: (result) => {
1569
+ if (currentGoal) {
1570
+ const entry = {
1571
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
1572
+ goal: currentGoal,
1573
+ model,
1574
+ durationMs: taskStartTime.current ? Date.now() - taskStartTime.current : void 0,
1575
+ success: result?.success
1576
+ };
1577
+ appendHistory(entry);
1578
+ taskStartTime.current = null;
1579
+ }
1126
1580
  setPhase("input");
1127
1581
  }
1128
1582
  });
1129
1583
  useEffect5(() => {
1130
1584
  if (phase === "executing" && currentGoal) {
1585
+ taskStartTime.current = Date.now();
1131
1586
  start(withProjectInstructions(currentGoal, projectConfig));
1132
1587
  }
1133
1588
  }, [phase, currentGoal, start, projectConfig]);
@@ -1140,12 +1595,16 @@ var App = ({ args }) => {
1140
1595
  model,
1141
1596
  verbose,
1142
1597
  noConfirm,
1598
+ compact,
1143
1599
  setModel,
1144
1600
  setVerbose,
1145
1601
  setNoConfirm,
1602
+ setCompact,
1146
1603
  clearEvents,
1147
1604
  quit: () => exit(),
1148
- runGoal
1605
+ runGoal,
1606
+ projectConfig,
1607
+ version: CLI_VERSION
1149
1608
  };
1150
1609
  const handleSubmitGoal = useCallback3(
1151
1610
  (input) => {
@@ -1184,7 +1643,7 @@ var App = ({ args }) => {
1184
1643
  if (state === "paused") return "thinking";
1185
1644
  return "active";
1186
1645
  })();
1187
- return /* @__PURE__ */ React9.createElement(Box8, { flexDirection: "column" }, phase === "input" && events.length === 0 && /* @__PURE__ */ React9.createElement(Box8, { flexDirection: "column" }, /* @__PURE__ */ React9.createElement(Logo, { version: CLI_VERSION, model, cwd: process.cwd(), mood: logoMood }), projectConfig.instructions && /* @__PURE__ */ React9.createElement(Box8, { marginTop: 1, marginLeft: 1 }, /* @__PURE__ */ React9.createElement(Text9, { dimColor: true }, "\u25B8 loaded "), /* @__PURE__ */ React9.createElement(Text9, { color: "green" }, projectConfig.instructionsPath)), projectConfig.commandFiles.length > 0 && /* @__PURE__ */ React9.createElement(Box8, { marginLeft: 1 }, /* @__PURE__ */ React9.createElement(Text9, { dimColor: true }, "\u25B8 "), /* @__PURE__ */ React9.createElement(Text9, { color: "green" }, projectConfig.commandFiles.length), /* @__PURE__ */ React9.createElement(Text9, { dimColor: true }, " custom command(s) from .agi/commands/"))), phase === "executing" && /* @__PURE__ */ React9.createElement(StatusBar, { state, step, goal: currentGoal }), /* @__PURE__ */ React9.createElement(Box8, { flexDirection: "column", marginTop: 1 }, /* @__PURE__ */ React9.createElement(EventDisplay, { events }), phase === "executing" && state === "running" && /* @__PURE__ */ React9.createElement(Box8, null, /* @__PURE__ */ React9.createElement(Text9, { dimColor: true }, " \u2503 "), /* @__PURE__ */ React9.createElement(Spinner, null)), phase === "executing" && state === "paused" && /* @__PURE__ */ React9.createElement(Box8, null, /* @__PURE__ */ React9.createElement(Text9, { dimColor: true }, " \u2503 "), /* @__PURE__ */ React9.createElement(Text9, { color: "yellow", bold: true }, "\u25AE\u25AE"), /* @__PURE__ */ React9.createElement(Text9, { dimColor: true }, " paused \u2014 space to resume"))), pendingConfirm && /* @__PURE__ */ React9.createElement(Box8, { marginTop: 1 }, /* @__PURE__ */ React9.createElement(ConfirmDialog, { reason: pendingConfirm, onConfirm: respondConfirm })), pendingQuestion && /* @__PURE__ */ React9.createElement(Box8, { marginTop: 1 }, /* @__PURE__ */ React9.createElement(QuestionDialog, { question: pendingQuestion, onAnswer: respondAnswer })), commandMessage && phase === "input" && /* @__PURE__ */ React9.createElement(Box8, null, /* @__PURE__ */ React9.createElement(Text9, { color: "gray" }, commandMessage)), phase === "input" && /* @__PURE__ */ React9.createElement(Box8, { marginTop: 1 }, /* @__PURE__ */ React9.createElement(PromptInput, { onSubmit: handleSubmitGoal })), phase === "executing" && !isTerminal && !pendingConfirm && !pendingQuestion && /* @__PURE__ */ React9.createElement(Box8, { marginTop: 1 }, /* @__PURE__ */ React9.createElement(Text9, { dimColor: true }, " space pause \xB7 q stop \xB7 ^c quit")));
1646
+ return /* @__PURE__ */ React9.createElement(Box8, { flexDirection: "column" }, phase === "input" && events.length === 0 && /* @__PURE__ */ React9.createElement(Box8, { flexDirection: "column" }, /* @__PURE__ */ React9.createElement(Logo, { version: CLI_VERSION, model, cwd: process.cwd(), mood: logoMood }), projectConfig.instructions && /* @__PURE__ */ React9.createElement(Box8, { marginTop: 1, marginLeft: 1 }, /* @__PURE__ */ React9.createElement(Text9, { dimColor: true }, "\u25B8 loaded "), /* @__PURE__ */ React9.createElement(Text9, { color: "green" }, projectConfig.instructionsPath)), projectConfig.rules.length > 0 && /* @__PURE__ */ React9.createElement(Box8, { marginLeft: 1 }, /* @__PURE__ */ React9.createElement(Text9, { dimColor: true }, "\u25B8 "), /* @__PURE__ */ React9.createElement(Text9, { color: "green" }, projectConfig.rules.length), /* @__PURE__ */ React9.createElement(Text9, { dimColor: true }, " rule(s) from .agi/rules/")), projectConfig.commandFiles.length > 0 && /* @__PURE__ */ React9.createElement(Box8, { marginLeft: 1 }, /* @__PURE__ */ React9.createElement(Text9, { dimColor: true }, "\u25B8 "), /* @__PURE__ */ React9.createElement(Text9, { color: "green" }, projectConfig.commandFiles.length), /* @__PURE__ */ React9.createElement(Text9, { dimColor: true }, " custom command(s) from .agi/commands/")), projectConfig.skills.length > 0 && /* @__PURE__ */ React9.createElement(Box8, { marginLeft: 1 }, /* @__PURE__ */ React9.createElement(Text9, { dimColor: true }, "\u25B8 "), /* @__PURE__ */ React9.createElement(Text9, { color: "green" }, projectConfig.skills.length), /* @__PURE__ */ React9.createElement(Text9, { dimColor: true }, " skill(s) from .agi/skills/"))), phase === "executing" && /* @__PURE__ */ React9.createElement(StatusBar, { state, step, goal: currentGoal }), /* @__PURE__ */ React9.createElement(Box8, { flexDirection: "column", marginTop: 1 }, /* @__PURE__ */ React9.createElement(EventDisplay, { events, maxEvents: compact ? 6 : 12 }), phase === "executing" && state === "running" && /* @__PURE__ */ React9.createElement(Box8, null, /* @__PURE__ */ React9.createElement(Text9, { dimColor: true }, " \u2503 "), /* @__PURE__ */ React9.createElement(Spinner, null)), phase === "executing" && state === "paused" && /* @__PURE__ */ React9.createElement(Box8, null, /* @__PURE__ */ React9.createElement(Text9, { dimColor: true }, " \u2503 "), /* @__PURE__ */ React9.createElement(Text9, { color: "yellow", bold: true }, "\u25AE\u25AE"), /* @__PURE__ */ React9.createElement(Text9, { dimColor: true }, " paused \u2014 space to resume"))), pendingConfirm && /* @__PURE__ */ React9.createElement(Box8, { marginTop: 1 }, /* @__PURE__ */ React9.createElement(ConfirmDialog, { reason: pendingConfirm, onConfirm: respondConfirm })), pendingQuestion && /* @__PURE__ */ React9.createElement(Box8, { marginTop: 1 }, /* @__PURE__ */ React9.createElement(QuestionDialog, { question: pendingQuestion, onAnswer: respondAnswer })), commandMessage && phase === "input" && /* @__PURE__ */ React9.createElement(Box8, null, /* @__PURE__ */ React9.createElement(Text9, { color: "gray" }, commandMessage)), phase === "input" && /* @__PURE__ */ React9.createElement(Box8, { marginTop: 1 }, /* @__PURE__ */ React9.createElement(PromptInput, { onSubmit: handleSubmitGoal })), phase === "executing" && !isTerminal && !pendingConfirm && !pendingQuestion && /* @__PURE__ */ React9.createElement(Box8, { marginTop: 1 }, /* @__PURE__ */ React9.createElement(Text9, { dimColor: true }, " space pause \xB7 q stop \xB7 ^c quit")));
1188
1647
  };
1189
1648
 
1190
1649
  // src/index.tsx
@@ -1207,7 +1666,7 @@ async function main() {
1207
1666
  if (!process.env.AGI_API_KEY && !process.env.ANTHROPIC_API_KEY) {
1208
1667
  process.env.AGI_API_KEY = apiKey;
1209
1668
  }
1210
- if (!isBinaryAvailable()) {
1669
+ if (!isBinaryAvailable2()) {
1211
1670
  console.error("Error: AGI driver binary not found");
1212
1671
  console.error("");
1213
1672
  console.error("The driver binary is required for local agent execution.");