otto-git-cli 4.0.4

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.
@@ -0,0 +1,71 @@
1
+ import { intro, outro, note } from "@clack/prompts";
2
+ import pc from "picocolors";
3
+ import { git } from "../git/index.js";
4
+ import { PM, OPENAI_API_KEY, SHEET_WEBHOOK_URL } from "../config.js";
5
+
6
+ export const ui = {
7
+ die: (msg) => {
8
+ outro(pc.red(msg));
9
+ process.exit(1);
10
+ },
11
+
12
+ banner: () => {
13
+ console.clear();
14
+ intro(pc.bgCyan(pc.black(" Otto - A Project by Sequence3 ")));
15
+
16
+ const user = git.user();
17
+ const br = git.branch();
18
+
19
+ console.log(pc.dim(`👋 Hello, ${user} (on ${pc.cyan(br)})`));
20
+ console.log(pc.dim(`🔧 Using: ${PM}`));
21
+
22
+ // --- Services Check ---
23
+ const services = [];
24
+ if (OPENAI_API_KEY) services.push("🤖 AI");
25
+ if (SHEET_WEBHOOK_URL) services.push("📊 Sheets");
26
+
27
+ if (services.length > 0) {
28
+ console.log(pc.dim(`⚡ Services: ${services.join(" + ")}`));
29
+ }
30
+
31
+ if (!git.isRepo()) {
32
+ note("You're not inside a git repository.", "ℹ Git");
33
+ return;
34
+ }
35
+
36
+ const defaultBr = git.defaultBranch();
37
+
38
+ if (defaultBr) {
39
+ console.log(pc.dim("─".repeat(50)));
40
+
41
+ const defInfo = git.commitInfo(defaultBr);
42
+ if (defInfo) {
43
+ console.log(
44
+ `${pc.green("🌿 " + defaultBr.padEnd(12))} ` +
45
+ `${pc.dim(defInfo.hash)} ${pc.white(
46
+ defInfo.msg.substring(0, 40)
47
+ )} ${pc.dim("(" + defInfo.time + ")")}`
48
+ );
49
+ }
50
+
51
+ const headInfo = git.commitInfo("HEAD");
52
+ if (headInfo) {
53
+ console.log(
54
+ `${pc.blue("📍 HEAD".padEnd(13))} ` +
55
+ `${pc.dim(headInfo.hash)} ${pc.white(
56
+ headInfo.msg.substring(0, 40)
57
+ )} ${pc.dim("(" + headInfo.time + ")")}`
58
+ );
59
+ }
60
+
61
+ const behind = git.commitsBehind(defaultBr);
62
+ if (parseInt(behind) > 0) {
63
+ console.log(
64
+ pc.yellow(`📉 Status: ${behind} commits behind ${defaultBr}`)
65
+ );
66
+ } else {
67
+ console.log(pc.dim(`✓ Up to date with ${defaultBr}`));
68
+ }
69
+ }
70
+ },
71
+ };
@@ -0,0 +1,2 @@
1
+ export { sh } from "./shell.js";
2
+ export { wrap } from "./text.js";
@@ -0,0 +1,59 @@
1
+ import { text, confirm } from "@clack/prompts";
2
+ import { OPENAI_API_KEY, SHEET_WEBHOOK_URL, updateConfig } from "../config.js";
3
+ import pc from "picocolors";
4
+
5
+ export async function ensureConfig() {
6
+ let updated = false;
7
+
8
+ if (!OPENAI_API_KEY) {
9
+ console.log(pc.yellow("ℹ OpenAI API Key is missing."));
10
+ const key = await text({
11
+ message: "Enter your OpenAI API Key:",
12
+ placeholder: "sk-...",
13
+ validate: (value) => {
14
+ if (!value) return "API Key is required.";
15
+ if (!value.startsWith("sk-")) return "Key usually starts with sk-";
16
+ },
17
+ });
18
+
19
+ if (key === undefined || typeof key === "symbol") {
20
+ process.exit(0);
21
+ }
22
+
23
+ updateConfig("OPENAI_API_KEY", key);
24
+ updated = true;
25
+ }
26
+
27
+ if (!SHEET_WEBHOOK_URL) {
28
+ const setupSheets = await confirm({
29
+ message: "Do you want to configure Google Sheets logging now?",
30
+ initialValue: true,
31
+ });
32
+
33
+ if (setupSheets === undefined || typeof setupSheets === "symbol") {
34
+ process.exit(0);
35
+ }
36
+
37
+ if (setupSheets) {
38
+ const url = await text({
39
+ message: "Enter Google Sheet Webhook URL:",
40
+ placeholder: "https://script.google.com/...",
41
+ validate: (val) => {
42
+ if (!val) return "URL is required if you want to enable logging.";
43
+ if (!val.startsWith("http")) return "Invalid URL.";
44
+ },
45
+ });
46
+
47
+ if (url === undefined || typeof url === "symbol") {
48
+ process.exit(0);
49
+ }
50
+
51
+ updateConfig("GOOGLE_SHEET_WEBHOOK_URL", url);
52
+ updated = true;
53
+ }
54
+ }
55
+
56
+ if (updated) {
57
+ console.log(pc.green("✔ Configuration saved successfully."));
58
+ }
59
+ }
@@ -0,0 +1,17 @@
1
+ import { execSync } from "child_process";
2
+
3
+ /**
4
+ * Execute a shell command and return the output
5
+ * @param {string} cmd - Command to execute
6
+ * @param {boolean} ignore - Whether to ignore errors
7
+ * @returns {string} Command output
8
+ */
9
+ export const sh = (cmd, ignore = false) => {
10
+ try {
11
+ return execSync(cmd, { stdio: "pipe" }).toString().trim();
12
+ } catch (e) {
13
+ if (!ignore)
14
+ throw new Error(e?.stderr?.toString() || e?.message || String(e));
15
+ return "";
16
+ }
17
+ };
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Wrap text to a specified width
3
+ * @param {string} s - String to wrap
4
+ * @param {number} w - Width to wrap at
5
+ * @returns {string} Wrapped string
6
+ */
7
+ export const wrap = (s, w = 60) => {
8
+ if (!s) return "";
9
+ return s
10
+ .split(/\s+/)
11
+ .reduce((acc, word) => {
12
+ const last = acc[acc.length - 1];
13
+ if (last && (last + word).length < w) acc[acc.length - 1] += " " + word;
14
+ else acc.push(word);
15
+ return acc;
16
+ }, [])
17
+ .join("\n");
18
+ };