@krmxd/onegpt 0.0.1-beta → 0.1.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.
package/README.md CHANGED
@@ -24,6 +24,7 @@ ogpt
24
24
  git commit) - everything else just works
25
25
  - Token dashboard with live tok/s charts at `http://127.0.0.1:8756` (`/dash`)
26
26
  - Session history shared with the Python OGPT build (`/save`, `/load`, `/sessions`)
27
+ - resume where you left off: `ogpt --continue` (or `--session <id>`)
27
28
  - Anti-sloth guardrails: tiny local models are kept on-task until every
28
29
  requested file actually exists
29
30
 
@@ -37,5 +38,10 @@ ogpt
37
38
  | `/auto` | toggle auto-approve |
38
39
  | `/dash` | token dashboard |
39
40
  | `/new` | fresh chat |
41
+ | `/export [md|json] [file]` | save the chat as Markdown or JSON |
42
+ | `/todos` | show the current todo list |
43
+
44
+ CLI flags: `ogpt -m <model>` · `-c/--continue` · `--session <id>` ·
45
+ `--auto` · `--no-stream`.
40
46
 
41
47
  Config lives at `~/.config/ogpt/config.json`.
package/bin/ogpt.js CHANGED
@@ -8,8 +8,30 @@ if (maj < 18) {
8
8
  process.exit(1);
9
9
  }
10
10
 
11
- const { CLI } = require("../src/cli");
12
- const { getConfig } = require("../src/config");
11
+ let CLI, getConfig;
12
+ try {
13
+ ({ CLI } = require("../src/cli"));
14
+ ({ getConfig } = require("../src/config"));
15
+ } catch (e) {
16
+ console.error(`OGPT failed to start: ${e.message}`);
17
+ if (e.code === "MODULE_NOT_FOUND") {
18
+ console.error(`
19
+ The installation looks broken or out of date. Fix it with:
20
+
21
+ npm cache clean --force
22
+ npm i -g @krmxd/onegpt@latest --force
23
+
24
+ If you installed with 'npm link' and later moved or renamed the
25
+ source folder, re-link it:
26
+
27
+ cd <your-ogpt-repo>/nodejs && npm link
28
+
29
+ Or run straight from the repo without installing:
30
+
31
+ cd <your-ogpt-repo>/nodejs && node bin/ogpt.js`);
32
+ }
33
+ process.exit(1);
34
+ }
13
35
 
14
36
  async function main() {
15
37
  const args = process.argv.slice(2);
@@ -19,12 +41,18 @@ async function main() {
19
41
  let prompt = [];
20
42
  let noStream = false;
21
43
  let autoApprove = false;
44
+ let continueLast = false;
45
+ let sessionId = "";
22
46
 
23
47
  for (let i = 0; i < args.length; i++) {
24
48
  if (args[i] === "--no-stream" || args[i] === "-s") {
25
49
  noStream = true;
26
50
  } else if (args[i] === "--auto") {
27
51
  autoApprove = true;
52
+ } else if (args[i] === "--continue" || args[i] === "-c") {
53
+ continueLast = true;
54
+ } else if (args[i] === "--session") {
55
+ sessionId = args[++i] || "";
28
56
  } else if (args[i] === "--model" || args[i] === "-m") {
29
57
  const name = args[++i];
30
58
  cfg.set("active_model", cfg.resolveModel(name));
@@ -37,6 +65,8 @@ async function main() {
37
65
  Options:
38
66
  -m, --model <oGPT-name|id> Override model (e.g. -m oGPT-2a)
39
67
  -s, --no-stream Disable streaming
68
+ -c, --continue Resume your most recent chat
69
+ --session <id> Resume a specific session by ID
40
70
  --auto Auto-approve all tools
41
71
  -v, --version Show version
42
72
  -h, --help Show help`);
@@ -53,6 +83,35 @@ Options:
53
83
  if (autoApprove) {
54
84
  cli.tools._autoApprove = true;
55
85
  }
86
+ if (sessionId || continueLast) {
87
+ const fsMod = require("fs"), osMod = require("os"), pathMod = require("path");
88
+ const dir = pathMod.join(osMod.homedir(), ".config", "ogpt", "sessions");
89
+ let sid = sessionId;
90
+ if (!sid) {
91
+ try {
92
+ sid = fsMod.readdirSync(dir).filter((f) => f.endsWith(".meta.json"))
93
+ .map((f) => { try { return JSON.parse(fsMod.readFileSync(pathMod.join(dir, f), "utf-8")); } catch { return null; } })
94
+ .filter((m) => m && (m.msgs || 0) > 0)
95
+ .sort((a, b) => (b.updated || 0) - (a.updated || 0))[0]?.id || "";
96
+ } catch {}
97
+ }
98
+ const dim = (s) => `\x1b[2m${s}\x1b[0m`;
99
+ let data = null;
100
+ if (sid) {
101
+ try { data = JSON.parse(fsMod.readFileSync(pathMod.join(dir, `${sid}.json`), "utf-8")); } catch {}
102
+ }
103
+ if (Array.isArray(data) && data.length) {
104
+ cli.agent.history = data.map((d) => ({
105
+ role: d.role || "user", content: d.content || "", ts: Date.now() / 1000 }));
106
+ cli._sessionId = sid;
107
+ console.log(dim(continueLast && !sessionId
108
+ ? `Continued last chat: ${sid}` : `Resumed session: ${sid}`));
109
+ } else if (sessionId) {
110
+ console.error(`Session not found: ${sessionId}`);
111
+ process.exitCode = 1;
112
+ return;
113
+ }
114
+ }
56
115
 
57
116
  if (prompt.length) {
58
117
  await cli.runSingle(prompt.join(" "));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@krmxd/onegpt",
3
- "version": "0.0.1-beta",
3
+ "version": "0.1.0",
4
4
  "description": "OGPT - AI coding assistant for the terminal with a built-in local engine",
5
5
  "main": "src/index.js",
6
6
  "bin": {
package/src/cli.js CHANGED
@@ -284,6 +284,10 @@ class CLI {
284
284
  "List plugins", "/plugins", "system", true));
285
285
  reg(new Command("/mcp", () => this.info("No MCP servers configured."),
286
286
  "List MCP servers", "/mcp", "system", true));
287
+ reg(new Command("/export", (a) => this.cmdExport(a),
288
+ "Export the chat as Markdown or JSON", "/export [md|json] [file]", "general"));
289
+ reg(new Command("/todos", () => this.cmdTodos(),
290
+ "Show the current todo list", "/todos", "tools", true));
287
291
 
288
292
  reg(new Command("/tools", () => this.cmdTools(), "List available tools", "/tools", "tools", true));
289
293
  reg(new Command("/approve", (a) => { this.print(this.tools.toggleApprove(a[0] || "")); }, "Auto-approve a specific tool", "/approve <tool>", "tools"));
@@ -683,8 +687,46 @@ class CLI {
683
687
  this.print(`## Project Structure\n\`\`\`\n${lines.join("\n")}\n\`\`\``);
684
688
  }
685
689
 
686
- cmdSession(args) {
687
- const sub = (args && args[0]) || "list";
690
+ cmdExport(args) {
691
+ const fmt = args && args[0] === "json" ? "json" : "md";
692
+ const file = (args && args[1]) ||
693
+ `ogpt-chat-${new Date().toISOString().slice(0, 19).replace(/[:T]/g, "-")}.${fmt}`;
694
+ try {
695
+ const msgs = this.agent.history.filter((m) => m.role !== "tool" &&
696
+ !String(m.content || "").startsWith("[Conversation summary]"));
697
+ if (!msgs.length) { this.info("Nothing to export - no conversation yet."); return; }
698
+ let body;
699
+ if (fmt === "json") {
700
+ body = JSON.stringify({ exported: new Date().toISOString(),
701
+ model: this.cfg.displayName(), messages: msgs }, null, 2);
702
+ } else {
703
+ body = `# OGPT chat export\n\n_${new Date().toLocaleString()} · ${this.cfg.displayName()} · ${msgs.length} messages_\n\n`
704
+ + msgs.map((m) => `## ${m.role === "user" ? "You" : this.cfg.displayName()}\n\n${m.content}\n`).join("\n");
705
+ }
706
+ fs.writeFileSync(file, body);
707
+ this.success(`Exported ${msgs.length} messages to ${file}`);
708
+ } catch (e) {
709
+ this.error(`Export failed: ${e.message}`);
710
+ }
711
+ }
712
+
713
+ _todosFile() {
714
+ return path.join(os.homedir(), ".config", "ogpt", "todos.json");
715
+ }
716
+
717
+ cmdTodos() {
718
+ let todos = [];
719
+ try {
720
+ const data = JSON.parse(fs.readFileSync(this._todosFile(), "utf-8"));
721
+ todos = Array.isArray(data) ? data : data.todos || [];
722
+ } catch {}
723
+ if (!todos.length) { this.info("No todos."); return; }
724
+ const rows = todos.map((t, i) =>
725
+ [String(i + 1), t.done ? "[x]" : "[ ]", String(t.text || "")]);
726
+ this._table("Todos", ["#", "Done", "Task"], rows);
727
+ }
728
+
729
+ cmdSession(args) { const sub = (args && args[0]) || "list";
688
730
  if (sub === "list") return this.cmdSessions();
689
731
  if (sub === "save") return this.cmdSave();
690
732
  if (sub === "new" || sub === "fresh") return this.cmdNew(args.slice(1));
@@ -716,12 +758,16 @@ class CLI {
716
758
  const metaPath = path.join(dir, `${this._sessionId}.meta.json`);
717
759
  try { meta = JSON.parse(fs.readFileSync(metaPath, "utf-8")); } catch {}
718
760
  if (!meta.id) {
719
- meta = { id: this._sessionId,
720
- title: `Session ${new Date().toISOString().slice(0, 16).replace("T", " ")}`,
721
- created: Date.now() / 1000, ...meta };
761
+ meta = { id: this._sessionId, title: "", created: Date.now() / 1000, ...meta };
722
762
  }
723
763
  meta.updated = Date.now() / 1000;
724
764
  meta.msgs = data.length;
765
+ // Auto-title from the first real user message (same rule as Python).
766
+ const existing = (meta.title || "").trim();
767
+ if (!existing || existing.startsWith("Session ")) {
768
+ const first = data.find((m) => m.role === "user" && String(m.content || "").trim());
769
+ if (first) meta.title = String(first.content).trim().slice(0, 50);
770
+ }
725
771
  fs.writeFileSync(metaPath, JSON.stringify(meta));
726
772
  return this._sessionId;
727
773
  } catch {