@corenel/term 0.2.8 → 0.3.0

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.
@@ -1,7 +1,8 @@
1
1
  // src/state.ts
2
- function emptyState() {
2
+ function emptyState(model = "") {
3
3
  return {
4
4
  seq: 0,
5
+ model,
5
6
  transcriptGen: 0,
6
7
  committed: [],
7
8
  live: null,
@@ -14,7 +15,11 @@ function emptyState() {
14
15
  status: "idle",
15
16
  error: null,
16
17
  todos: [],
17
- pendingSkill: null
18
+ pendingSkill: null,
19
+ sentTokens: null,
20
+ offloaded: [],
21
+ checkpoints: [],
22
+ toolNames: []
18
23
  };
19
24
  }
20
25
  var TODO_STATUSES = ["pending", "in_progress", "completed", "blocked"];
@@ -0,0 +1,138 @@
1
+ // src/commands.ts
2
+ var TERM_COMMANDS = [
3
+ { name: "help", summary: "what you can type here" },
4
+ { name: "tools", summary: "what the agent can do, and what it can change" },
5
+ { name: "policy", summary: "how this session is governed -- levels, caps, and what actually confines it" },
6
+ { name: "context", summary: "what is in the context window right now -- fill, offloads, and compaction seams" },
7
+ { name: "expand", args: "<#n>", summary: "re-print a tool result in full" },
8
+ { name: "resume", args: "[id]", summary: "reopen an earlier conversation" },
9
+ { name: "clear", summary: "forget the conversation" },
10
+ { name: "exit", summary: "leave" },
11
+ // -- config-workspace commands: what these do to the NEXT turn is real, see
12
+ // TermSessionDeps.system in ./session (the re-derivable system prompt) and
13
+ // the `mode`/`persona`/`skill`/`strategy` cases in runCommand.
14
+ { name: "model", args: "<id>", summary: "list available models (read-only) -- the model is fixed for a session, so an id is refused; restart with --model <id> to change it" },
15
+ { name: "mode", args: "<id>", summary: "switch the agent mode (takes effect next turn); no id lists what is configured" },
16
+ { name: "persona", args: "<id>", summary: "switch persona (takes effect next turn); no id lists what is configured" },
17
+ { name: "skill", args: "<name>", summary: "apply a skill to your NEXT message only, then it clears; no name lists what is installed" },
18
+ { name: "strategy", args: "<name>", summary: "run a saved strategy from .prompd/strategies; no name lists what is saved" },
19
+ { name: "mcp", args: "[list|add|remove|login|refresh|forget]", summary: "MCP servers -- what is connected, and sign in or add one; no argument opens the manager" }
20
+ ];
21
+ function formatHelpText() {
22
+ const width = Math.max(...TERM_COMMANDS.map((c) => `${c.name} ${c.args ?? ""}`.trim().length));
23
+ const lines = TERM_COMMANDS.map((c) => {
24
+ const usage = `${c.name} ${c.args ?? ""}`.trim();
25
+ return ` /${usage.padEnd(width)} ${c.summary}`;
26
+ });
27
+ return [
28
+ "Type a request and press enter.",
29
+ "",
30
+ ...lines,
31
+ "",
32
+ /* Deliberately not 'esc stops a turn': this text is shared, and Escape is
33
+ * an Ink-only key -- the readline fallback has no key handler at all, so
34
+ * there Ctrl-C is the only way to stop a turn. Name the one that is true
35
+ * on both faces, and mark the other as what it is. */
36
+ "Ctrl-C stops a turn; twice leaves. Ctrl-D leaves. In the full terminal face, esc also stops a turn."
37
+ ].join("\n");
38
+ }
39
+ var ALIASES = { quit: "exit" };
40
+ function isKnownCommandWord(word) {
41
+ const w = word.toLowerCase();
42
+ return w in ALIASES || TERM_COMMANDS.some((c) => c.name === w);
43
+ }
44
+ function editDistance(a, b) {
45
+ const m = a.length;
46
+ const n = b.length;
47
+ const d = Array.from({ length: m + 1 }, () => new Array(n + 1).fill(0));
48
+ for (let i = 0; i <= m; i++) d[i][0] = i;
49
+ for (let j = 0; j <= n; j++) d[0][j] = j;
50
+ for (let i = 1; i <= m; i++) {
51
+ for (let j = 1; j <= n; j++) {
52
+ const cost = a[i - 1] === b[j - 1] ? 0 : 1;
53
+ d[i][j] = Math.min(d[i - 1][j] + 1, d[i][j - 1] + 1, d[i - 1][j - 1] + cost);
54
+ }
55
+ }
56
+ return d[m][n];
57
+ }
58
+ function suggestCommand(word) {
59
+ const w = word.toLowerCase();
60
+ let best = null;
61
+ let bestDist = Infinity;
62
+ for (const c of TERM_COMMANDS) {
63
+ const dist = editDistance(w, c.name);
64
+ if (dist < bestDist) {
65
+ bestDist = dist;
66
+ best = c.name;
67
+ }
68
+ }
69
+ if (best && bestDist <= 2 && bestDist < Math.max(w.length, best.length)) return best;
70
+ return null;
71
+ }
72
+ function unknownCommandMessage(word) {
73
+ const suggestion = suggestCommand(word);
74
+ return `unknown command /${word}${suggestion ? ` -- did you mean /${suggestion}?` : ""} -- /help lists what's available. Press enter again on the same line to send it as a message instead.`;
75
+ }
76
+ function parseCommand(line) {
77
+ const t = line.trim();
78
+ if (!t.startsWith("/")) return null;
79
+ const [wordRaw, ...rest] = t.slice(1).split(/\s+/);
80
+ const word = ALIASES[wordRaw.toLowerCase()] ?? wordRaw.toLowerCase();
81
+ const arg = rest.join(" ").trim();
82
+ switch (word) {
83
+ case "help":
84
+ return { cmd: "help" };
85
+ case "tools":
86
+ return { cmd: "tools" };
87
+ case "policy":
88
+ return { cmd: "policy" };
89
+ case "context":
90
+ return { cmd: "context" };
91
+ case "clear":
92
+ return { cmd: "clear" };
93
+ case "exit":
94
+ return { cmd: "exit" };
95
+ case "expand": {
96
+ const m = /^#?(\d+)$/.exec(arg);
97
+ return m ? { cmd: "expand", ord: Number(m[1]) } : { cmd: "expand" };
98
+ }
99
+ case "resume":
100
+ return arg ? { cmd: "resume", id: arg } : { cmd: "resume" };
101
+ case "model":
102
+ return arg ? { cmd: "model", id: arg } : { cmd: "model" };
103
+ case "mode":
104
+ return arg ? { cmd: "mode", id: arg } : { cmd: "mode" };
105
+ case "persona":
106
+ return arg ? { cmd: "persona", id: arg } : { cmd: "persona" };
107
+ case "skill":
108
+ return arg ? { cmd: "skill", id: arg } : { cmd: "skill" };
109
+ case "strategy":
110
+ return arg ? { cmd: "strategy", id: arg } : { cmd: "strategy" };
111
+ case "mcp": {
112
+ if (!arg) return { cmd: "mcp" };
113
+ const [sub, ...restArgs] = arg.split(/\s+/);
114
+ const known = ["list", "add", "remove", "login", "refresh", "forget"];
115
+ const match = known.find((k) => k === sub.toLowerCase());
116
+ if (!match) return { cmd: "mcp", unknown: sub };
117
+ return restArgs.length ? { cmd: "mcp", sub: match, args: restArgs } : { cmd: "mcp", sub: match };
118
+ }
119
+ default:
120
+ return null;
121
+ }
122
+ }
123
+ function filterCommands(input) {
124
+ const t = input.trim();
125
+ if (!t.startsWith("/")) return [];
126
+ const prefix = t.slice(1).split(/\s+/)[0].toLowerCase();
127
+ return TERM_COMMANDS.filter((c) => c.name.startsWith(prefix));
128
+ }
129
+
130
+ export {
131
+ TERM_COMMANDS,
132
+ formatHelpText,
133
+ isKnownCommandWord,
134
+ suggestCommand,
135
+ unknownCommandMessage,
136
+ parseCommand,
137
+ filterCommands
138
+ };