code-ollama 0.0.0 → 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/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2026 Menglin "Mark" Xu <mark@remarkablemark.org>
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,64 @@
1
+ # code-ollama
2
+
3
+ [![NPM](https://nodei.co/npm/code-ollama.svg)](https://www.npmjs.com/package/code-ollama)
4
+
5
+ [![NPM version](https://img.shields.io/npm/v/code-ollama.svg)](https://www.npmjs.com/package/code-ollama)
6
+ [![build](https://github.com/ai-action/code-ollama/actions/workflows/build.yml/badge.svg)](https://github.com/ai-action/code-ollama/actions/workflows/build.yml)
7
+ [![codecov](https://codecov.io/gh/ai-action/code-ollama/graph/badge.svg?token=gRGUasRn2k)](https://codecov.io/gh/ai-action/code-ollama)
8
+
9
+ 🦙 [Ollama](https://ollama.com/) coding agent that runs in your terminal.
10
+
11
+ ## Quick Start
12
+
13
+ ```sh
14
+ npx code-ollama
15
+ ```
16
+
17
+ ## Install
18
+
19
+ Install the [CLI](https://www.npmjs.com/package/code-ollama) globally:
20
+
21
+ ```sh
22
+ npm install --global code-ollama
23
+ ```
24
+
25
+ ## Usage
26
+
27
+ ### TUI
28
+
29
+ Open the TUI:
30
+
31
+ ```sh
32
+ code-ollama
33
+ ```
34
+
35
+ Or use the alias:
36
+
37
+ ```sh
38
+ collama
39
+ ```
40
+
41
+ ### CLI
42
+
43
+ Show the version:
44
+
45
+ ```sh
46
+ code-ollama --version
47
+ ```
48
+
49
+ Show the help:
50
+
51
+ ```sh
52
+ code-ollama --help
53
+ ```
54
+
55
+ Run a one-off prompt:
56
+
57
+ ```sh
58
+ # code-ollama run <model> <prompt>
59
+ code-ollama run gemma4 "review diff"
60
+ ```
61
+
62
+ ## License
63
+
64
+ [MIT](https://github.com/ai-action/code-ollama/blob/master/LICENSE)
package/dist/cli.js ADDED
@@ -0,0 +1,70 @@
1
+ #!/usr/bin/env node
2
+ import { d as e, i as t, l as n, o as r, r as i, t as a, u as o } from "./utils-DBXrYZEs.js";
3
+ import { realpathSync as s } from "node:fs";
4
+ import c from "cac";
5
+ //#region src/cli.ts
6
+ var l = c("code-ollama");
7
+ l.version(e), l.help(), l.command("run <model> <prompt>", "Run a one-off prompt").action(async (e, t) => {
8
+ try {
9
+ await u(e, t);
10
+ } catch (e) {
11
+ // v8 ignore next
12
+ let t = e instanceof Error ? e.message : "Unknown error";
13
+ process.stderr.write(`Error: ${t}\n`), process.exitCode = 1;
14
+ }
15
+ });
16
+ async function u(e, t) {
17
+ await d([n(), {
18
+ role: o.USER,
19
+ content: t
20
+ }], e), process.stdout.write("\n");
21
+ }
22
+ async function d(e, t) {
23
+ let n = {
24
+ role: o.ASSISTANT,
25
+ content: ""
26
+ };
27
+ for await (let s of r(e, t, a)) {
28
+ if (s.type === "content") {
29
+ n.content += s.content, process.stdout.write(s.content);
30
+ continue;
31
+ }
32
+ for (let r of s.tool_calls) {
33
+ let a = await i(r.function.name, r.function.arguments), s = {
34
+ role: o.SYSTEM,
35
+ content: `Tool ${r.function.name} result:\n${a.content}${a.error ? `\nError: ${a.error}` : ""}`
36
+ };
37
+ await d([
38
+ ...e,
39
+ n,
40
+ s
41
+ ], t);
42
+ return;
43
+ }
44
+ }
45
+ }
46
+ async function f(e = process.argv.slice(2)) {
47
+ if (!e.length) {
48
+ let { renderApp: e } = await import("./tui-Bu6wAbeu.js");
49
+ t(), e();
50
+ return;
51
+ }
52
+ l.parse([
53
+ "node",
54
+ "code-ollama",
55
+ ...e
56
+ ]);
57
+ }
58
+ /* v8 ignore start */
59
+ function p(e = process.argv[1]) {
60
+ if (!e) return !1;
61
+ try {
62
+ return s(e) === import.meta.filename;
63
+ } catch {
64
+ return !1;
65
+ }
66
+ }
67
+ p() && f();
68
+ /* v8 ignore stop */
69
+ //#endregion
70
+ export { f as main };
@@ -0,0 +1,559 @@
1
+ import { a as e, c as t, d as n, l as r, n as i, o as a, r as o, s, t as c, u as l } from "./utils-DBXrYZEs.js";
2
+ import { homedir as u } from "node:os";
3
+ import { Box as d, Text as f, render as p, useInput as m } from "ink";
4
+ import { useCallback as h, useEffect as g, useState as _ } from "react";
5
+ import { Select as v, Spinner as y, TextInput as b } from "@inkjs/ui";
6
+ //#region \0rolldown/runtime.js
7
+ var x = (e, t) => () => (t || (e((t = { exports: {} }).exports, t), e = null), t.exports), S = /* @__PURE__ */ ((e) => typeof require < "u" ? require : typeof Proxy < "u" ? new Proxy(e, { get: (e, t) => (typeof require < "u" ? require : e)[t] }) : e)(function(e) {
8
+ if (typeof require < "u") return require.apply(this, arguments);
9
+ throw Error("Calling `require` for \"" + e + "\" in an environment that doesn't expose the `require` function. See https://rolldown.rs/in-depth/bundling-cjs#require-external-modules for more details.");
10
+ }), C = [{
11
+ name: "/model",
12
+ description: "switch the model"
13
+ }], w = "🦙", T = /* @__PURE__ */ x(((e) => {
14
+ var t = Symbol.for("react.transitional.element"), n = Symbol.for("react.fragment");
15
+ function r(e, n, r) {
16
+ var i = null;
17
+ if (r !== void 0 && (i = "" + r), n.key !== void 0 && (i = "" + n.key), "key" in n) for (var a in r = {}, n) a !== "key" && (r[a] = n[a]);
18
+ else r = n;
19
+ return n = r.ref, {
20
+ $$typeof: t,
21
+ type: e,
22
+ key: i,
23
+ ref: n === void 0 ? null : n,
24
+ props: r
25
+ };
26
+ }
27
+ e.Fragment = n, e.jsx = r, e.jsxs = r;
28
+ })), E = /* @__PURE__ */ x(((e) => {
29
+ process.env.NODE_ENV !== "production" && (function() {
30
+ function t(e) {
31
+ if (e == null) return null;
32
+ if (typeof e == "function") return e.$$typeof === k ? null : e.displayName || e.name || null;
33
+ if (typeof e == "string") return e;
34
+ switch (e) {
35
+ case _: return "Fragment";
36
+ case y: return "Profiler";
37
+ case v: return "StrictMode";
38
+ case w: return "Suspense";
39
+ case T: return "SuspenseList";
40
+ case O: return "Activity";
41
+ }
42
+ if (typeof e == "object") switch (typeof e.tag == "number" && console.error("Received an unexpected object in getComponentNameFromType(). This is likely a bug in React. Please file an issue."), e.$$typeof) {
43
+ case g: return "Portal";
44
+ case x: return e.displayName || "Context";
45
+ case b: return (e._context.displayName || "Context") + ".Consumer";
46
+ case C:
47
+ var n = e.render;
48
+ return e = e.displayName, e ||= (e = n.displayName || n.name || "", e === "" ? "ForwardRef" : "ForwardRef(" + e + ")"), e;
49
+ case E: return n = e.displayName || null, n === null ? t(e.type) || "Memo" : n;
50
+ case D:
51
+ n = e._payload, e = e._init;
52
+ try {
53
+ return t(e(n));
54
+ } catch {}
55
+ }
56
+ return null;
57
+ }
58
+ function n(e) {
59
+ return "" + e;
60
+ }
61
+ function r(e) {
62
+ try {
63
+ n(e);
64
+ var t = !1;
65
+ } catch {
66
+ t = !0;
67
+ }
68
+ if (t) {
69
+ t = console;
70
+ var r = t.error, i = typeof Symbol == "function" && Symbol.toStringTag && e[Symbol.toStringTag] || e.constructor.name || "Object";
71
+ return r.call(t, "The provided key is an unsupported type %s. This value must be coerced to a string before using it here.", i), n(e);
72
+ }
73
+ }
74
+ function i(e) {
75
+ if (e === _) return "<>";
76
+ if (typeof e == "object" && e && e.$$typeof === D) return "<...>";
77
+ try {
78
+ var n = t(e);
79
+ return n ? "<" + n + ">" : "<...>";
80
+ } catch {
81
+ return "<...>";
82
+ }
83
+ }
84
+ function a() {
85
+ var e = A.A;
86
+ return e === null ? null : e.getOwner();
87
+ }
88
+ function o() {
89
+ return Error("react-stack-top-frame");
90
+ }
91
+ function s(e) {
92
+ if (j.call(e, "key")) {
93
+ var t = Object.getOwnPropertyDescriptor(e, "key").get;
94
+ if (t && t.isReactWarning) return !1;
95
+ }
96
+ return e.key !== void 0;
97
+ }
98
+ function c(e, t) {
99
+ function n() {
100
+ P || (P = !0, console.error("%s: `key` is not a prop. Trying to access it will result in `undefined` being returned. If you need to access the same value within the child component, you should pass it as a different prop. (https://react.dev/link/special-props)", t));
101
+ }
102
+ n.isReactWarning = !0, Object.defineProperty(e, "key", {
103
+ get: n,
104
+ configurable: !0
105
+ });
106
+ }
107
+ function l() {
108
+ var e = t(this.type);
109
+ return F[e] || (F[e] = !0, console.error("Accessing element.ref was removed in React 19. ref is now a regular prop. It will be removed from the JSX Element type in a future release.")), e = this.props.ref, e === void 0 ? null : e;
110
+ }
111
+ function u(e, t, n, r, i, a) {
112
+ var o = n.ref;
113
+ return e = {
114
+ $$typeof: h,
115
+ type: e,
116
+ key: t,
117
+ props: n,
118
+ _owner: r
119
+ }, (o === void 0 ? null : o) === null ? Object.defineProperty(e, "ref", {
120
+ enumerable: !1,
121
+ value: null
122
+ }) : Object.defineProperty(e, "ref", {
123
+ enumerable: !1,
124
+ get: l
125
+ }), e._store = {}, Object.defineProperty(e._store, "validated", {
126
+ configurable: !1,
127
+ enumerable: !1,
128
+ writable: !0,
129
+ value: 0
130
+ }), Object.defineProperty(e, "_debugInfo", {
131
+ configurable: !1,
132
+ enumerable: !1,
133
+ writable: !0,
134
+ value: null
135
+ }), Object.defineProperty(e, "_debugStack", {
136
+ configurable: !1,
137
+ enumerable: !1,
138
+ writable: !0,
139
+ value: i
140
+ }), Object.defineProperty(e, "_debugTask", {
141
+ configurable: !1,
142
+ enumerable: !1,
143
+ writable: !0,
144
+ value: a
145
+ }), Object.freeze && (Object.freeze(e.props), Object.freeze(e)), e;
146
+ }
147
+ function d(e, n, i, o, l, d) {
148
+ var p = n.children;
149
+ if (p !== void 0) if (o) if (M(p)) {
150
+ for (o = 0; o < p.length; o++) f(p[o]);
151
+ Object.freeze && Object.freeze(p);
152
+ } else console.error("React.jsx: Static children should always be an array. You are likely explicitly calling React.jsxs or React.jsxDEV. Use the Babel transform instead.");
153
+ else f(p);
154
+ if (j.call(n, "key")) {
155
+ p = t(e);
156
+ var m = Object.keys(n).filter(function(e) {
157
+ return e !== "key";
158
+ });
159
+ o = 0 < m.length ? "{key: someKey, " + m.join(": ..., ") + ": ...}" : "{key: someKey}", R[p + o] || (m = 0 < m.length ? "{" + m.join(": ..., ") + ": ...}" : "{}", console.error("A props object containing a \"key\" prop is being spread into JSX:\n let props = %s;\n <%s {...props} />\nReact keys must be passed directly to JSX without using spread:\n let props = %s;\n <%s key={someKey} {...props} />", o, p, m, p), R[p + o] = !0);
160
+ }
161
+ if (p = null, i !== void 0 && (r(i), p = "" + i), s(n) && (r(n.key), p = "" + n.key), "key" in n) for (var h in i = {}, n) h !== "key" && (i[h] = n[h]);
162
+ else i = n;
163
+ return p && c(i, typeof e == "function" ? e.displayName || e.name || "Unknown" : e), u(e, p, i, a(), l, d);
164
+ }
165
+ function f(e) {
166
+ p(e) ? e._store && (e._store.validated = 1) : typeof e == "object" && e && e.$$typeof === D && (e._payload.status === "fulfilled" ? p(e._payload.value) && e._payload.value._store && (e._payload.value._store.validated = 1) : e._store && (e._store.validated = 1));
167
+ }
168
+ function p(e) {
169
+ return typeof e == "object" && !!e && e.$$typeof === h;
170
+ }
171
+ var m = S("react"), h = Symbol.for("react.transitional.element"), g = Symbol.for("react.portal"), _ = Symbol.for("react.fragment"), v = Symbol.for("react.strict_mode"), y = Symbol.for("react.profiler"), b = Symbol.for("react.consumer"), x = Symbol.for("react.context"), C = Symbol.for("react.forward_ref"), w = Symbol.for("react.suspense"), T = Symbol.for("react.suspense_list"), E = Symbol.for("react.memo"), D = Symbol.for("react.lazy"), O = Symbol.for("react.activity"), k = Symbol.for("react.client.reference"), A = m.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE, j = Object.prototype.hasOwnProperty, M = Array.isArray, N = console.createTask ? console.createTask : function() {
172
+ return null;
173
+ };
174
+ m = { react_stack_bottom_frame: function(e) {
175
+ return e();
176
+ } };
177
+ var P, F = {}, I = m.react_stack_bottom_frame.bind(m, o)(), L = N(i(o)), R = {};
178
+ e.Fragment = _, e.jsx = function(e, t, n) {
179
+ var r = 1e4 > A.recentlyCreatedOwnerStacks++;
180
+ return d(e, t, n, !1, r ? Error("react-stack-top-frame") : I, r ? N(i(e)) : L);
181
+ }, e.jsxs = function(e, t, n) {
182
+ var r = 1e4 > A.recentlyCreatedOwnerStacks++;
183
+ return d(e, t, n, !0, r ? Error("react-stack-top-frame") : I, r ? N(i(e)) : L);
184
+ };
185
+ })();
186
+ })), D = (/* @__PURE__ */ x(((e, t) => {
187
+ process.env.NODE_ENV === "production" ? t.exports = T() : t.exports = E();
188
+ })))();
189
+ function O(e) {
190
+ return e.startsWith("/") ? C.filter((t) => t.name.startsWith(e)) : [];
191
+ }
192
+ function k({ isDisabled: e = !1, onSubmit: t }) {
193
+ let [n, r] = _(""), [i, a] = _(0), [o, s] = _(0), c = O(n), l = n.startsWith("/");
194
+ m((e, t) => {
195
+ if (l) {
196
+ if (t.upArrow) {
197
+ a((e) => Math.max(0, e - 1));
198
+ return;
199
+ }
200
+ if (t.downArrow) {
201
+ a((e) => Math.min(c.length - 1, e + 1));
202
+ return;
203
+ }
204
+ if (t.tab && c.length > 0) {
205
+ r((c[i] ?? c[0]).name), a(0), s((e) => e + 1);
206
+ return;
207
+ }
208
+ }
209
+ }, { isActive: !e && l });
210
+ let u = h((e) => {
211
+ let n = (l && c.length > 0 && c[i] ? c[i].name : e).trim();
212
+ n && (t(n), r(""), a(0), s((e) => e + 1));
213
+ }, [
214
+ l,
215
+ c,
216
+ t,
217
+ i
218
+ ]);
219
+ return /* @__PURE__ */ (0, D.jsxs)(d, {
220
+ flexDirection: "column",
221
+ children: [/* @__PURE__ */ (0, D.jsxs)(d, { children: [/* @__PURE__ */ (0, D.jsx)(f, { children: "> " }), /* @__PURE__ */ (0, D.jsx)(b, {
222
+ isDisabled: e,
223
+ defaultValue: n,
224
+ onChange: r,
225
+ onSubmit: u
226
+ }, o)] }), l && c.length > 0 && /* @__PURE__ */ (0, D.jsx)(d, {
227
+ flexDirection: "column",
228
+ marginLeft: 2,
229
+ children: c.map((e, t) => {
230
+ let n = t === i;
231
+ return /* @__PURE__ */ (0, D.jsxs)(d, {
232
+ gap: 3,
233
+ children: [/* @__PURE__ */ (0, D.jsx)(f, {
234
+ color: n ? "cyan" : void 0,
235
+ bold: n,
236
+ children: e.name
237
+ }), /* @__PURE__ */ (0, D.jsx)(f, {
238
+ dimColor: !0,
239
+ children: e.description
240
+ })]
241
+ }, e.name);
242
+ })
243
+ })]
244
+ });
245
+ }
246
+ //#endregion
247
+ //#region src/components/Messages.tsx
248
+ function A(e) {
249
+ switch (e) {
250
+ case l.USER: return "black";
251
+ case l.ASSISTANT: return "blue";
252
+ case l.SYSTEM: return "gray";
253
+ default: return;
254
+ }
255
+ }
256
+ function j({ messages: e, isLoading: t }) {
257
+ return /* @__PURE__ */ (0, D.jsxs)(d, {
258
+ flexDirection: "column",
259
+ children: [e.map((e, t) => /* @__PURE__ */ (0, D.jsx)(d, {
260
+ marginBottom: 1,
261
+ children: /* @__PURE__ */ (0, D.jsxs)(f, {
262
+ color: A(e.role),
263
+ dimColor: e.role === l.SYSTEM,
264
+ children: [e.role === l.USER ? "> " : "", e.content]
265
+ })
266
+ }, t)), t && e[e.length - 1]?.content === "" && /* @__PURE__ */ (0, D.jsx)(d, {
267
+ marginTop: -1,
268
+ marginBottom: 1,
269
+ children: /* @__PURE__ */ (0, D.jsx)(y, { label: "Thinking..." })
270
+ })]
271
+ });
272
+ }
273
+ //#endregion
274
+ //#region src/components/ToolApproval.tsx
275
+ function M({ toolCall: e, onApprove: t, onReject: n }) {
276
+ let [r, i] = _("yes");
277
+ m((e, a) => {
278
+ a.return ? r === "yes" ? t() : n() : (a.leftArrow || a.rightArrow) && i((e) => e === "yes" ? "no" : "yes");
279
+ // v8 ignore stop
280
+ });
281
+ let a = JSON.stringify(e.function.arguments, null, 2);
282
+ return /* @__PURE__ */ (0, D.jsxs)(d, {
283
+ flexDirection: "column",
284
+ marginY: 1,
285
+ children: [
286
+ /* @__PURE__ */ (0, D.jsx)(f, {
287
+ color: "yellow",
288
+ bold: !0,
289
+ children: "⚠️ Tool requires approval:"
290
+ }),
291
+ /* @__PURE__ */ (0, D.jsxs)(d, {
292
+ marginX: 2,
293
+ flexDirection: "column",
294
+ children: [/* @__PURE__ */ (0, D.jsxs)(f, { children: [
295
+ /* @__PURE__ */ (0, D.jsx)(f, {
296
+ bold: !0,
297
+ children: "Tool:"
298
+ }),
299
+ " ",
300
+ e.function.name
301
+ ] }), /* @__PURE__ */ (0, D.jsxs)(f, { children: [
302
+ /* @__PURE__ */ (0, D.jsx)(f, {
303
+ bold: !0,
304
+ children: "Arguments:"
305
+ }),
306
+ " ",
307
+ a
308
+ ] })]
309
+ }),
310
+ /* @__PURE__ */ (0, D.jsxs)(d, {
311
+ marginTop: 1,
312
+ gap: 2,
313
+ children: [/* @__PURE__ */ (0, D.jsx)(f, { children: /* @__PURE__ */ (0, D.jsxs)(f, {
314
+ color: r === "yes" ? "green" : void 0,
315
+ children: [r === "yes" ? "▶ " : " ", "✓ Yes (Enter)"]
316
+ }) }), /* @__PURE__ */ (0, D.jsx)(f, { children: /* @__PURE__ */ (0, D.jsxs)(f, {
317
+ color: r === "no" ? "red" : void 0,
318
+ children: [r === "no" ? "▶ " : " ", "✗ No (Esc)"]
319
+ }) })]
320
+ })
321
+ ]
322
+ });
323
+ }
324
+ //#endregion
325
+ //#region src/components/Chat.tsx
326
+ function N({ model: e, onCommand: t, autoExecute: n }) {
327
+ let [s, u] = _([r()]), [f, p] = _(0), [m, g] = _(!1), [v, y] = _(null), b = h(async (t) => {
328
+ let r = {
329
+ role: l.ASSISTANT,
330
+ content: ""
331
+ };
332
+ u((e) => [...e, r]);
333
+ try {
334
+ for await (let s of a(t, e, c)) if (s.type === "content") r.content += s.content, u((e) => {
335
+ let t = [...e];
336
+ return t[t.length - 1] = { ...r }, t;
337
+ });
338
+ else if (s.type === "tool_calls") for (let e of s.tool_calls) {
339
+ let a = i.has(e.function.name);
340
+ if (!n && a) {
341
+ y(e), g(!1);
342
+ return;
343
+ }
344
+ let s = await o(e.function.name, e.function.arguments), c = {
345
+ role: l.SYSTEM,
346
+ content: `Tool ${e.function.name} result:\n${s.content}${s.error ? `\nError: ${s.error}` : ""}`
347
+ }, d = [
348
+ ...t,
349
+ r,
350
+ c
351
+ ];
352
+ u((e) => [...e, c]), await b(d);
353
+ return;
354
+ }
355
+ } catch (e) {
356
+ r.content = `Error: ${e instanceof Error ? e.message : String(e)}`, u((e) => {
357
+ let t = [...e];
358
+ return t[t.length - 1] = { ...r }, t;
359
+ });
360
+ } finally {
361
+ g(!1);
362
+ }
363
+ }, [e, n]), x = h(async (e) => {
364
+ // v8 ignore next
365
+ if (!v) return;
366
+ let t = v;
367
+ if (y(null), g(!0), e) {
368
+ let e = await o(t.function.name, t.function.arguments), n = {
369
+ role: l.SYSTEM,
370
+ content: `Tool ${t.function.name} result:\n${e.content}${e.error ? `\nError: ${e.error}` : ""}`
371
+ }, r = [...s, n];
372
+ u((e) => [...e, n]), await b(r);
373
+ } else {
374
+ let e = {
375
+ role: l.SYSTEM,
376
+ content: `User declined to execute tool ${t.function.name}`
377
+ }, n = [...s, e];
378
+ u((t) => [...t, e]), await b(n);
379
+ }
380
+ }, [
381
+ v,
382
+ s,
383
+ b
384
+ ]), S = h(async (e) => {
385
+ let n = e.trim();
386
+ if (!n) return;
387
+ if (p((e) => e + 1), n.startsWith("/")) {
388
+ t(n);
389
+ return;
390
+ }
391
+ g(!0);
392
+ let r = {
393
+ role: l.USER,
394
+ content: n
395
+ };
396
+ u((e) => [...e, r]), await b([...s, r]);
397
+ }, [
398
+ s,
399
+ t,
400
+ b
401
+ ]);
402
+ return /* @__PURE__ */ (0, D.jsxs)(d, {
403
+ flexDirection: "column",
404
+ children: [
405
+ /* @__PURE__ */ (0, D.jsx)(j, {
406
+ messages: s.slice(1),
407
+ isLoading: m
408
+ }),
409
+ v && /* @__PURE__ */ (0, D.jsx)(M, {
410
+ toolCall: v,
411
+ onApprove: () => void x(!0),
412
+ onReject: () => void x(!1)
413
+ }),
414
+ !v && /* @__PURE__ */ (0, D.jsx)(k, {
415
+ isDisabled: m,
416
+ onSubmit: (e) => {
417
+ S(e);
418
+ }
419
+ }, f)
420
+ ]
421
+ });
422
+ }
423
+ //#endregion
424
+ //#region src/components/Footer.tsx
425
+ function P({ autoExecute: e, onToggleMode: t }) {
426
+ return m((e, n) => {
427
+ n.tab && n.shift && t();
428
+ }), /* @__PURE__ */ (0, D.jsx)(d, {
429
+ justifyContent: "space-between",
430
+ marginTop: 1,
431
+ children: /* @__PURE__ */ (0, D.jsxs)(f, {
432
+ dimColor: !0,
433
+ children: [
434
+ "Mode: ",
435
+ e ? "Auto" : "Safe",
436
+ " (Shift+Tab to toggle)"
437
+ ]
438
+ })
439
+ });
440
+ }
441
+ //#endregion
442
+ //#region src/components/Header.tsx
443
+ function F(e) {
444
+ let t = u();
445
+ return e.startsWith(t) ? `~${e.slice(t.length)}` : e;
446
+ }
447
+ function I({ model: e }) {
448
+ let t = F(process.cwd());
449
+ return /* @__PURE__ */ (0, D.jsxs)(d, {
450
+ borderStyle: "round",
451
+ flexDirection: "column",
452
+ paddingX: 1,
453
+ children: [
454
+ /* @__PURE__ */ (0, D.jsxs)(f, { children: [/* @__PURE__ */ (0, D.jsxs)(f, {
455
+ bold: !0,
456
+ children: [w, "Code Ollama"]
457
+ }), /* @__PURE__ */ (0, D.jsxs)(f, {
458
+ dimColor: !0,
459
+ children: [
460
+ " (v",
461
+ n,
462
+ ")"
463
+ ]
464
+ })] }),
465
+ /* @__PURE__ */ (0, D.jsx)(f, { children: " " }),
466
+ /* @__PURE__ */ (0, D.jsxs)(d, { children: [
467
+ /* @__PURE__ */ (0, D.jsx)(f, {
468
+ dimColor: !0,
469
+ children: "model:".padEnd(11)
470
+ }),
471
+ /* @__PURE__ */ (0, D.jsxs)(f, { children: [e, " "] }),
472
+ /* @__PURE__ */ (0, D.jsx)(f, {
473
+ color: "cyan",
474
+ children: "/model"
475
+ }),
476
+ /* @__PURE__ */ (0, D.jsx)(f, {
477
+ dimColor: !0,
478
+ children: " to switch"
479
+ })
480
+ ] }),
481
+ /* @__PURE__ */ (0, D.jsxs)(d, { children: [/* @__PURE__ */ (0, D.jsx)(f, {
482
+ dimColor: !0,
483
+ children: "directory:".padEnd(11)
484
+ }), /* @__PURE__ */ (0, D.jsx)(f, { children: t })] })
485
+ ]
486
+ });
487
+ }
488
+ //#endregion
489
+ //#region src/components/ModelPicker.tsx
490
+ function L({ currentModel: t, onSelect: n, onCancel: r }) {
491
+ let [i, a] = _([]), [o, s] = _(null);
492
+ return g(() => {
493
+ async function t() {
494
+ try {
495
+ a((await e()).map((e) => ({
496
+ label: e,
497
+ value: e
498
+ })));
499
+ } catch (e) {
500
+ s(e instanceof Error ? e.message : String(e));
501
+ }
502
+ }
503
+ t();
504
+ }, []), m((e, t) => {
505
+ t.escape && r();
506
+ }), o ? /* @__PURE__ */ (0, D.jsxs)(f, {
507
+ color: "red",
508
+ children: ["Error loading models: ", o]
509
+ }) : i.length ? /* @__PURE__ */ (0, D.jsxs)(d, {
510
+ flexDirection: "column",
511
+ children: [/* @__PURE__ */ (0, D.jsx)(f, {
512
+ dimColor: !0,
513
+ children: "Select a model (↑↓ + Enter to confirm, Esc to cancel)"
514
+ }), /* @__PURE__ */ (0, D.jsx)(v, {
515
+ options: i,
516
+ defaultValue: t,
517
+ onChange: n
518
+ })]
519
+ }) : /* @__PURE__ */ (0, D.jsx)(y, { label: "Loading models..." });
520
+ }
521
+ //#endregion
522
+ //#region src/components/App.tsx
523
+ function R() {
524
+ let [e, n] = _(() => s().model), [r, i] = _(!1), [a, o] = _(!1), c = h((e) => {
525
+ e === "/model" && i(!0);
526
+ }, []), l = h((e) => {
527
+ n(e), t({ model: e }), i(!1);
528
+ }, []), u = h(() => {
529
+ i(!1);
530
+ }, []);
531
+ return /* @__PURE__ */ (0, D.jsxs)(d, {
532
+ flexDirection: "column",
533
+ children: [
534
+ /* @__PURE__ */ (0, D.jsx)(I, { model: e }),
535
+ r ? /* @__PURE__ */ (0, D.jsx)(L, {
536
+ currentModel: e,
537
+ onSelect: l,
538
+ onCancel: u
539
+ }) : /* @__PURE__ */ (0, D.jsx)(N, {
540
+ model: e,
541
+ onCommand: c,
542
+ autoExecute: a
543
+ }),
544
+ /* @__PURE__ */ (0, D.jsx)(P, {
545
+ autoExecute: a,
546
+ onToggleMode: () => {
547
+ o((e) => !e);
548
+ }
549
+ })
550
+ ]
551
+ });
552
+ }
553
+ //#endregion
554
+ //#region src/tui.tsx
555
+ function z() {
556
+ p(/* @__PURE__ */ (0, D.jsx)(R, {}));
557
+ }
558
+ //#endregion
559
+ export { z as renderApp };
@@ -0,0 +1,283 @@
1
+ import { existsSync as e, mkdirSync as t, readFileSync as n, readdirSync as r, writeFileSync as i } from "node:fs";
2
+ import { join as a } from "node:path";
3
+ import { homedir as o } from "node:os";
4
+ import { Ollama as s } from "ollama";
5
+ import { exec as c } from "node:child_process";
6
+ import { promisify as l } from "node:util";
7
+ //#endregion
8
+ //#region src/constants/package.ts
9
+ var u = "0.1.0", d = "You are a coding assistant that helps users write, edit, and understand code. You have access to tools for reading files, writing files, running shell commands, and searching code\n\nFollow these rules:\n1. Always use available tools rather than guessing file contents or code behavior\n2. Read files before editing them to understand context\n3. When writing files, provide complete, working code\n4. Explain your reasoning when making non-trivial changes\n5. Prefer minimal changes that achieve the goal\n6. Confirm with the user before destructive operations\n\nWhen tools return results, incorporate them into your response naturally", f = "Available tools:\n- read_file: Read file contents at a path\n- write_file: Write content to a file (requires approval)\n- edit_file: Make precise edits to a file\n- list_dir: List files in a directory\n- grep_search: Search code with regex\n- run_shell: Execute shell commands (requires approval)\n\nAlways use tools when you need to:\n- Check file contents before referencing them\n- Make file changes\n- Explore project structure\n- Search the codebase", p = {
10
+ USER: "user",
11
+ ASSISTANT: "assistant",
12
+ SYSTEM: "system"
13
+ }, m = {
14
+ READ_FILE: "read_file",
15
+ WRITE_FILE: "write_file",
16
+ RUN_SHELL: "run_shell",
17
+ LIST_DIR: "list_dir",
18
+ GREP_SEARCH: "grep_search",
19
+ VIEW_RANGE: "view_range"
20
+ }, h = "AGENTS.md";
21
+ function g() {
22
+ let t = a(process.cwd(), h);
23
+ if (!e(t)) return null;
24
+ try {
25
+ return n(t, "utf8");
26
+ } catch {
27
+ return null;
28
+ }
29
+ }
30
+ function _() {
31
+ let e = [d], t = g();
32
+ return t && e.push("\n\nProject context from AGENTS.md:\n", t), e.push("\n\n", f), e.join("");
33
+ }
34
+ function v() {
35
+ return {
36
+ role: p.SYSTEM,
37
+ content: _()
38
+ };
39
+ }
40
+ //#endregion
41
+ //#region src/utils/config.ts
42
+ var y = a(o(), ".code-ollama"), b = a(y, "config.json"), x = {
43
+ host: "http://localhost:11434",
44
+ model: "gemma4"
45
+ };
46
+ function S() {
47
+ if (!e(b)) return {};
48
+ try {
49
+ return JSON.parse(n(b, "utf8"));
50
+ } catch {
51
+ return {};
52
+ }
53
+ }
54
+ function C() {
55
+ let e = S();
56
+ return {
57
+ host: process.env.OLLAMA_HOST ?? e.host ?? x.host,
58
+ model: process.env.OLLAMA_MODEL ?? e.model ?? x.model
59
+ };
60
+ }
61
+ function w(e) {
62
+ let n = {
63
+ ...S(),
64
+ ...e
65
+ };
66
+ t(y, { recursive: !0 }), i(b, JSON.stringify(n, null, 2) + "\n", "utf8");
67
+ }
68
+ //#endregion
69
+ //#region src/utils/ollama.ts
70
+ var { host: T, model: E } = C(), D = new s({ host: T });
71
+ async function* O(e, t = E, n) {
72
+ let r = await D.chat({
73
+ model: t,
74
+ messages: e,
75
+ stream: !0,
76
+ tools: n
77
+ });
78
+ for await (let e of r) e.message.content && (yield {
79
+ type: "content",
80
+ content: e.message.content
81
+ }), e.message.tool_calls && (yield {
82
+ type: "tool_calls",
83
+ tool_calls: e.message.tool_calls
84
+ });
85
+ }
86
+ async function k() {
87
+ let { models: e } = await D.list();
88
+ return e.map(({ name: e }) => e);
89
+ }
90
+ //#endregion
91
+ //#region src/utils/screen.ts
92
+ var A = "\x1Bc";
93
+ function j() {
94
+ process.stdout.write(A);
95
+ }
96
+ //#endregion
97
+ //#region src/utils/tools.ts
98
+ var M = l(c);
99
+ function N(e, t, n, r) {
100
+ return {
101
+ type: "function",
102
+ function: {
103
+ name: e,
104
+ description: t,
105
+ parameters: {
106
+ type: "object",
107
+ properties: n,
108
+ required: r
109
+ }
110
+ }
111
+ };
112
+ }
113
+ var P = [
114
+ N(m.READ_FILE, "Read the contents of a file at the specified path", { path: {
115
+ type: "string",
116
+ description: "The path to the file to read"
117
+ } }, ["path"]),
118
+ N(m.WRITE_FILE, "Write content to a file at the specified path", {
119
+ path: {
120
+ type: "string",
121
+ description: "The path to the file to write"
122
+ },
123
+ content: {
124
+ type: "string",
125
+ description: "The content to write to the file"
126
+ }
127
+ }, ["path", "content"]),
128
+ N(m.RUN_SHELL, "Execute a shell command", { command: {
129
+ type: "string",
130
+ description: "The shell command to execute"
131
+ } }, ["command"]),
132
+ N(m.LIST_DIR, "List the contents of a directory", { path: {
133
+ type: "string",
134
+ description: "The path to the directory to list"
135
+ } }, ["path"]),
136
+ N(m.GREP_SEARCH, "Search for a pattern in files within a directory", {
137
+ pattern: {
138
+ type: "string",
139
+ description: "The regex pattern to search for"
140
+ },
141
+ path: {
142
+ type: "string",
143
+ description: "The directory path to search in"
144
+ }
145
+ }, ["pattern", "path"]),
146
+ N(m.VIEW_RANGE, "View a specific range of lines from a file", {
147
+ path: {
148
+ type: "string",
149
+ description: "The path to the file"
150
+ },
151
+ start: {
152
+ type: "number",
153
+ description: "The starting line number (1-indexed)"
154
+ },
155
+ end: {
156
+ type: "number",
157
+ description: "The ending line number (inclusive)"
158
+ }
159
+ }, [
160
+ "path",
161
+ "start",
162
+ "end"
163
+ ])
164
+ ], F = new Set([m.WRITE_FILE, m.RUN_SHELL]);
165
+ async function I(e, t) {
166
+ switch (e) {
167
+ case m.READ_FILE: return L(t.path);
168
+ case m.WRITE_FILE: return R(t.path, t.content);
169
+ case m.RUN_SHELL: return V(t.command);
170
+ case m.LIST_DIR: return H(t.path);
171
+ case m.GREP_SEARCH: return await U(t.pattern, t.path);
172
+ case m.VIEW_RANGE: return W(t.path, t.start, t.end);
173
+ default: return {
174
+ content: "",
175
+ error: `Unknown tool: ${e}`
176
+ };
177
+ }
178
+ }
179
+ function L(t) {
180
+ try {
181
+ return e(t) ? { content: n(t, "utf8") } : {
182
+ content: "",
183
+ error: `File not found: ${t}`
184
+ };
185
+ } catch (e) {
186
+ return {
187
+ content: "",
188
+ error: `Failed to read file: ${e instanceof Error ? e.message : String(e)}`
189
+ };
190
+ }
191
+ }
192
+ function R(e, t) {
193
+ try {
194
+ return i(e, t, "utf8"), { content: `File written successfully: ${e}` };
195
+ } catch (e) {
196
+ return {
197
+ content: "",
198
+ error: `Failed to write file: ${e instanceof Error ? e.message : String(e)}`
199
+ };
200
+ }
201
+ }
202
+ var z = {
203
+ timeout: 3e4,
204
+ maxBuffer: 1024 * 1024
205
+ };
206
+ function B(e) {
207
+ return M(e, z);
208
+ }
209
+ async function V(e) {
210
+ try {
211
+ let { stdout: t, stderr: n } = await B(e);
212
+ return { content: t || n };
213
+ } catch (e) {
214
+ return {
215
+ content: "",
216
+ error: `Command failed: ${e instanceof Error ? e.message : String(e)}`
217
+ };
218
+ }
219
+ }
220
+ function H(t) {
221
+ try {
222
+ return e(t) ? { content: r(t, { withFileTypes: !0 }).map((e) => `[${e.isDirectory() ? "d" : "f"}] ${e.name}`).join("\n") } : {
223
+ content: "",
224
+ error: `Directory not found: ${t}`
225
+ };
226
+ } catch (e) {
227
+ return {
228
+ content: "",
229
+ error: `Failed to list directory: ${e instanceof Error ? e.message : String(e)}`
230
+ };
231
+ }
232
+ }
233
+ async function U(t, i) {
234
+ try {
235
+ let { stdout: e } = await B(`rg --line-number --no-heading --smart-case "${t.replace(/"/g, "\\\"")}" "${i}"`);
236
+ // v8 ignore next
237
+ return { content: e || "No matches found" };
238
+ } catch {}
239
+ try {
240
+ if (!e(i)) return {
241
+ content: "",
242
+ error: `Directory not found: ${i}`
243
+ };
244
+ let o = new RegExp(t, "g"), s = [];
245
+ function c(e) {
246
+ let t = r(e, { withFileTypes: !0 });
247
+ for (let r of t) {
248
+ let t = a(e, r.name);
249
+ if (r.isDirectory()) !r.name.startsWith(".") && r.name !== "node_modules" && c(t);
250
+ else if (r.isFile()) try {
251
+ let e = n(t, "utf8").split("\n");
252
+ for (let n = 0; n < e.length; n++) o.test(e[n]) && s.push(`${t}:${(n + 1).toString()}: ${e[n].trim()}`), o.lastIndex = 0;
253
+ } catch {}
254
+ }
255
+ }
256
+ return c(i), s.length === 0 ? { content: "No matches found" } : { content: s.join("\n") };
257
+ } catch (e) {
258
+ return {
259
+ content: "",
260
+ error: `Search failed: ${e instanceof Error ? e.message : String(e)}`
261
+ };
262
+ }
263
+ }
264
+ function W(t, r, i) {
265
+ try {
266
+ if (!e(t)) return {
267
+ content: "",
268
+ error: `File not found: ${t}`
269
+ };
270
+ let a = n(t, "utf8").split("\n"), o = Math.max(0, r - 1), s = Math.min(a.length, i);
271
+ return o >= a.length || o > s ? {
272
+ content: "",
273
+ error: "Invalid line range"
274
+ } : { content: a.slice(o, s).join("\n") };
275
+ } catch (e) {
276
+ return {
277
+ content: "",
278
+ error: `Failed to view range: ${e instanceof Error ? e.message : String(e)}`
279
+ };
280
+ }
281
+ }
282
+ //#endregion
283
+ export { k as a, w as c, u as d, j as i, v as l, F as n, O as o, I as r, C as s, P as t, p as u };
package/package.json CHANGED
@@ -1,4 +1,81 @@
1
1
  {
2
2
  "name": "code-ollama",
3
- "version": "0.0.0"
3
+ "version": "0.1.0",
4
+ "description": "Ollama coding agent that runs in your terminal",
5
+ "author": "Mark <mark@remarkablemark.org> (https://remarkablemark.org)",
6
+ "type": "module",
7
+ "bin": {
8
+ "code-ollama": "./dist/cli.js",
9
+ "collama": "./dist/cli.js"
10
+ },
11
+ "scripts": {
12
+ "build": "vite build",
13
+ "start": "tsx --tsconfig tsconfig.test.json src/cli.ts",
14
+ "clean": "rm -rf coverage dist docs",
15
+ "lint": "eslint .",
16
+ "lint:fix": "npm run lint -- --fix",
17
+ "lint:package": "publint",
18
+ "lint:tsc": "tsc --build",
19
+ "prepare": "husky",
20
+ "prepublishOnly": "npm run build && npm run lint && npm run lint:tsc && npm run test:ci",
21
+ "test": "vitest run",
22
+ "test:ci": "CI=true npm test -- --color --coverage",
23
+ "test:watch": "vitest --coverage.enabled=false"
24
+ },
25
+ "repository": {
26
+ "type": "git",
27
+ "url": "git+https://github.com/ai-action/code-ollama.git"
28
+ },
29
+ "bugs": {
30
+ "url": "https://github.com/ai-action/code-ollama/issues"
31
+ },
32
+ "keywords": [
33
+ "code-ollama",
34
+ "collama",
35
+ "code",
36
+ "ollama",
37
+ "tui",
38
+ "cli",
39
+ "ai"
40
+ ],
41
+ "dependencies": {
42
+ "@inkjs/ui": "2.0.0",
43
+ "cac": "7.0.0",
44
+ "ink": "7.0.1",
45
+ "ollama": "0.6.3",
46
+ "react": "19.2.5"
47
+ },
48
+ "devDependencies": {
49
+ "@commitlint/cli": "20.5.3",
50
+ "@commitlint/config-conventional": "20.5.3",
51
+ "@eslint/compat": "2.0.5",
52
+ "@eslint/js": "10.0.1",
53
+ "@types/node": "25.6.0",
54
+ "@types/react": "19.2.14",
55
+ "@vitest/coverage-v8": "4.1.5",
56
+ "eslint": "10.3.0",
57
+ "eslint-plugin-prettier": "5.5.5",
58
+ "eslint-plugin-simple-import-sort": "13.0.0",
59
+ "globals": "17.6.0",
60
+ "husky": "9.1.7",
61
+ "ink-testing-library": "4.0.0",
62
+ "lint-staged": "16.4.0",
63
+ "prettier": "3.8.3",
64
+ "publint": "0.3.18",
65
+ "tsx": "4.21.0",
66
+ "typescript": "6.0.3",
67
+ "typescript-eslint": "8.59.1",
68
+ "vite": "8.0.10",
69
+ "vitest": "4.1.5"
70
+ },
71
+ "files": [
72
+ "dist/"
73
+ ],
74
+ "funding": [
75
+ {
76
+ "type": "github",
77
+ "url": "https://github.com/sponsors/remarkablemark"
78
+ }
79
+ ],
80
+ "license": "MIT"
4
81
  }