Tools 🛠
Find a tool that makes your life easier.
Claude Sonnet 3.5 Chat 🔧
Open a chat with the Claude Sonnet 3.5 model in an... (Read more)
// Author: Eduard Uffelmann
// Linkedin: https://www.linkedin.com/in/euffelmann/
// Twitter: @schmedu_
// Website: https://schmedu.com
import "@johnlindquist/kit";
import { startChat } from "../lib/common";
await startChat({
model: "claude-3-5-sonnet-20240620",
temperature: 1,
});
// Author: Eduard Uffelmann
// Linkedin: https://www.linkedin.com/in/euffelmann/
// Twitter: @schmedu_
// Website: https://schmedu.com
import "@johnlindquist/kit";
import { startChat } from "../lib/common";
await startChat({
model: "gpt-4-turbo",
temperature: 1,
functions: [],
});
// Author: Eduard Uffelmann
// Linkedin: https://www.linkedin.com/in/euffelmann/
// Twitter: @schmedu_
// Website: https://schmedu.com
import "@johnlindquist/kit";
import { startChat } from "../lib/common";
await startChat({
model: "claude-3-opus-20240229",
temperature: 1,
});
ChatGPT With Functions 🔧
Allow ChatGPT to access your computer and run func... (Read more)
// Author: Eduard Uffelmann
// Linkedin: https://www.linkedin.com/in/euffelmann/
// Twitter: @schmedu_
// Website: https://schmedu.com
import "@johnlindquist/kit";
import { startChat } from "../lib/common";
import os from "os";
// import { CustomFunctions } from "../../../lib/chatGptFunctions"; // For user build functions that are stored in ~/.kenv/lib/chatGptFunctions/index.ts
await startChat({
model: "gpt-3.5-turbo-0613",
temperature: 1,
systemMessage: `You have now access to my system and you are able to perform any direct interactions with my local environment such as accessing my file system, running bash commands, executing python code with the 'runPython' function, and creating/saving files on my own io the platform.
The name of the user is ${os.userInfo().username}.
You can use every function on the users macOS system to accomplish every task, especially everything that needs to be computed. Try to be bold!
IMPORTANT: Before you decide to install any packages, please check if they are already installed.
Don't run commands, that could return a huge output, always limit the output to the necessary information.
Always assume you are in the Home directory when you run a bash command.`,
functions: [
"all",
// CustomFunctions.greetingsFunction, // Just an example of how to use a custom function
],
});
// Author: Eduard Uffelmann
// Linkedin: https://www.linkedin.com/in/euffelmann/
// Twitter: @schmedu_
// Website: https://schmedu.com
import "@johnlindquist/kit";
import { startChat } from "../lib/common";
await startChat({
systemMessage: `You are a therapist. Stay in your role and don't play out of character. Don't write any comments or recommendations apart from that role.`,
});
// Author: Eduard Uffelmann
// Linkedin: https://www.linkedin.com/in/euffelmann/
// Twitter: @schmedu_
// Website: https://schmedu.com
import "@johnlindquist/kit";
import { continueLastChat } from "../lib/common";
await continueLastChat();
Correct Text With ChatGpt 🔧
Correct Text for misspellings and grammatical erro... (Read more)
Create Cal Event From Screenshot with ChatGPT 🔧
Allow ChatGPT to create a calendar Event for you v... (Read more)
// Author: Eduard Uffelmann
// Linkedin: https://www.linkedin.com/in/euffelmann/
// Twitter: @schmedu_
// Website: https://schmedu.com
import "@johnlindquist/kit";
import { startChat } from "../lib/common";
import {
getCalendars,
getCurrentDayOfWeek,
} from "../lib/functions/implementations/calendar";
import { Functions } from "../lib/functions";
import { getSetting } from "../lib/settings";
import { createFullScreenScreenshotMessage, userMessage } from "../lib/helper";
let calendars = await getCalendars();
const defaultCalendar = await getSetting("defaultCalendar");
const CALENDAR_STRING = calendars.map((cal) => `- ${cal}`).join("\n");
let prompt = undefined;
const screenshotMessage = await createFullScreenScreenshotMessage();
await startChat({
prompt,
model: "gpt-4o-mini", // gpt-3.5 is not reliable with times and calculation
temperature: 1,
systemMessage: `You will now receive a screenshot where you will find the event details and then call the 'createEvent' function with the event details. Make a smart guess about the event duration.`,
functions: [Functions.createEventFunction],
messages: [screenshotMessage],
});
Create Cal Event with ChatGPT 🔧
Allow ChatGPT to create a calendar Event for you v... (Read more)
// Author: Eduard Uffelmann
// Linkedin: https://www.linkedin.com/in/euffelmann/
// Twitter: @schmedu_
// Website: https://schmedu.com
import "@johnlindquist/kit";
import { startChat } from "../lib/common";
import { getCalendars, getCurrentDayOfWeek } from "../lib/functions/implementations/calendar";
import { Functions } from "../lib/functions";
import { getSetting } from "../lib/settings";
let calendars = await getCalendars();
const defaultCalendar = await getSetting("defaultCalendar");
const CALENDAR_STRING = calendars.map(cal => `- ${cal}`).join("\n")
let prompt = undefined;
await startChat({
prompt,
model: "gpt-4-0613", // gpt-3.5 is not reliable with times and calculation
temperature: 1,
systemMessage: `You are my personal assistant and you schedule my events in my calendar. I want you to create an event for me.
IMPORTANT: The current datetime is: ${new Date().toLocaleString()} in 'MM/DD/YYYY, HH:MM:SS AM/PM', it's ${await getCurrentDayOfWeek()}.
If there is no end date or duration specified, I want you to create an event for 1 hour.
The available calendars are:
${CALENDAR_STRING}
Use the first '${defaultCalendar}' calendar if no calendar is specified.
IMPORTANT: Use the 'createEvent' function to create the event.`,
functions: [Functions.createEventFunction],
});
Create Cal Event From Screenshot with ChatGPT 🔧
Allow ChatGPT to create a calendar Event for you v... (Read more)
// Author: Eduard Uffelmann
// Linkedin: https://www.linkedin.com/in/euffelmann/
// Twitter: @schmedu_
// Website: https://schmedu.com
import "@johnlindquist/kit";
import { startChat } from "../lib/common";
import { Functions } from "../lib/functions";
import { createFullScreenScreenshotMessage, userMessage } from "../lib/helper";
const screenshotMessage = await createFullScreenScreenshotMessage();
await startChat({
model: "gpt-4o",
temperature: 1,
systemMessage: `You will now receive a screenshot where you will find the contact details and then call the 'createContact' function with the contact details.`,
functions: [Functions.createContactFunction],
messages: [screenshotMessage],
});
// Author: Eduard Uffelmann
// Linkedin: https://www.linkedin.com/in/euffelmann/
// Twitter: @schmedu_
// Website: https://schmedu.com
import "@johnlindquist/kit";
import { startChat } from "../lib/common";
let selectedText = await getSelectedText();
let task = `Please rewrite the following text in a professional manner, so I can use it for work. Keep it as short as possible. Here is the text:
\`\`\`
${selectedText}
\`\`\``;
await startChat({
prompt: task,
model: "gpt-3.5-turbo",
temperature: 1,
});
// Author: Eduard Uffelmann
// Linkedin: https://www.linkedin.com/in/euffelmann/
// Twitter: @schmedu_
// Website: https://schmedu.com
import "@johnlindquist/kit";
import { changeSettings } from "../lib/settings";
await changeSettings();
// Author: Eduard Uffelmann
// Linkedin: https://www.linkedin.com/in/euffelmann/
// Twitter: @schmedu_
// Website: https://schmedu.com
import "@johnlindquist/kit";
import { startChat } from "../lib/common";
await startChat({
model: "gpt-3.5-turbo",
systemMessage: `You are a proficient software developer who is specialized in Typescript. Please help me with the following and always give code examples in typescript. Always prefer to write code with async/await promises instead of callbacks.`,
});
Lorem Ipsum Text Generator 🔧
Generate Lorem Ipsum text. You can choose between ... (Read more)
Text Case Converter 🔧
Transform the selected text to a different case. Y... (Read more)
// Author: Eduard Uffelmann
// Linkedin: https://www.linkedin.com/in/euffelmann/
// Twitter: @schmedu_
// Website: https://schmedu.com
import "@johnlindquist/kit";
let input = await getSelectedText();
let buildPreview = ([search, replacement]) => {
if (!search) return md(input);
if (!replacement)
return md(
`${input
.replaceAll("\n", "<br>")
.replaceAll(search, `<ins>${search}</ins>`)}`
);
return md(
`${input}
${replacement
.split(",")
.map((item) => {
return input
.replaceAll("\n", "<br>")
.replaceAll(search, `<ins>${item}</ins>`);
})
.join("<br>")}`
);
};
let [search, replacement] = await fields({
fields: ["Search", "Replacement"],
preview: buildPreview(["", ""]),
onChange: async (input, state) => {
let preview = buildPreview(state?.value);
setPreview(preview);
},
});
let result = `${input}
${replacement
.split(",")
.map((item) => {
return input.replaceAll(search, `${item}`);
})
.join("\n")}`;
// Output
await setSelectedText(result);
Get Reading Time of Text 🔧
How much time does the text take to read? Just sel... (Read more)
Login Trigger 🔧
This script is triggered when the user logs in. St... (Read more)
// Author: Eduard Uffelmann
// Linkedin: https://www.linkedin.com/in/euffelmann/
// Twitter: @schmedu_
// Website: https://schmedu.com
// System: unlock-screen
import "@johnlindquist/kit";
async function getSystemInfoDb() {
let database = await db(await kenvPath("db", "system-info.json"), {
lastLogin: new Date().toString(),
lastLogout: void 0,
dates: {},
currentTasks: [],
wasShutDown: false,
});
return database;
}
let database = await getSystemInfoDb();
database.lastLogin = new Date().toString();
await database.write();
await menu("0m");
Logout Trigger 🔧
This script is triggered when the user logs out. R... (Read more)
// Author: Eduard Uffelmann
// Linkedin: https://www.linkedin.com/in/euffelmann/
// Twitter: @schmedu_
// Website: https://schmedu.com
// System: lock-screen
import "@johnlindquist/kit";
async function getSystemInfoDb() {
let database = await db(await kenvPath("db", "system-info.json"), {
lastLogin: new Date().toString(),
lastLogout: void 0,
dates: {},
currentTasks: [],
wasShutDown: false,
});
return database;
}
let database = await getSystemInfoDb();
let now = new Date();
// get current date in format YYYY-MM-DD
let currentDate = now.toISOString().slice(0, 10);
let lastLogin = new Date(database.lastLogin);
let timeSinceLastLogin = (now.getTime() - lastLogin.getTime()) / 1000 / 60;
// parse date from loginTimeDate
let loginTimeDate = lastLogin.toISOString().slice(0, 10);
// check if loginTimeDate is today
if (loginTimeDate === currentDate) {
if (database.dates[currentDate] === undefined) {
database.dates[currentDate] = {
totalTime: timeSinceLastLogin,
};
} else {
database.dates[currentDate].totalTime += timeSinceLastLogin;
}
} else {
// create new date from midnight of today
let midnight = new Date(
now.getFullYear(),
now.getMonth(),
now.getDate(),
0,
0,
0,
0
);
// get minutes from lastLogin to midnight
let minutesSinceLastLogin =
(midnight.getTime() - lastLogin.getTime()) / 1000 / 60;
if (database.dates[loginTimeDate] === undefined) {
database.dates[loginTimeDate] = {
totalTime: timeSinceLastLogin,
};
} else {
database.dates[loginTimeDate].totalTime += timeSinceLastLogin;
database.dates[loginTimeDate].totalTime += minutesSinceLastLogin;
}
// get minutes from midnight to now
let minutesSinceMidnight = (now.getTime() - midnight.getTime()) / 1000 / 60;
database.dates[currentDate].totalTime = minutesSinceMidnight;
}
database.lastLogout = now.toString();
await database.write();
await menu("")
Ultradian Cycle Tracker 🔧
This script tracks the time since the last login a... (Read more)
// Author: Eduard Uffelmann
// Linkedin: https://www.linkedin.com/in/euffelmann/
// Twitter: @schmedu_
// Website: https://schmedu.com
// Schedule: */1 * * * *
import "@johnlindquist/kit";
async function getSystemInfoDb() {
let database = await db(await kenvPath("db", "system-info.json"), {
lastLogin: new Date().toString(),
lastLogout: void 0,
dates: {},
currentTasks: [],
wasShutDown: false,
});
return database;
}
const TIME_LIMIT = 90;
const INTERVAL_TIME = 5;
let database = await getSystemInfoDb();
// check if last login is more than 90 minutes ago
let lastLogin = Date.parse(database.lastLogin);
// how much time has passed since last login
let timeSinceLastLogin = new Date().getTime() - lastLogin;
// timeSinceLastLogin in Minutes
let timeSinceLastLoginInMinutes = parseInt(
(timeSinceLastLogin / 1000 / 60).toFixed(0)
);
await menu(`${timeSinceLastLoginInMinutes}m`); // update the time in the menu bar
if (process.env.KIT_TRIGGER === "menu" || process.env.KIT_TRIGGER === "kar") {
let currentDate = new Date().toISOString().slice(0, 10);
let totalTime = database.dates[currentDate]?.totalTime || 0;
totalTime += timeSinceLastLoginInMinutes;
notify({
title: "Working Time",
message: `Currently: ${timeSinceLastLoginInMinutes.toFixed(0)}m - Total: ${(totalTime / 60).toFixed(
1
)}h`,
});
}
if (
timeSinceLastLoginInMinutes >= TIME_LIMIT &&
Number(timeSinceLastLoginInMinutes.toFixed(0)) % INTERVAL_TIME == 0 &&
Date.parse(database.lastLogout) < lastLogin
) {
notify({
title: "Ultradian Cycle Tracker",
message: `${timeSinceLastLoginInMinutes.toFixed(
0
)} mins worked! Take a break!`,
});
}
🧰 randomScripts /
Connect / Disconnect Bluetooth Devices 🔧
Toggles a bluetooth device connection
Show only active App (Heidi) 🔧
Hide all Apps except the one that is currently act... (Read more)
// Author: Eduard Uffelmann
// Linkedin: https://www.linkedin.com/in/euffelmann/
// Twitter: @schmedu_
// Website: https://schmedu.com
import Anthropic from "@anthropic-ai/sdk";
const anthropic = new Anthropic({
apiKey: await env("ANTHROPIC_API_KEY", {
hint: md(
"Get your API key from https://console.anthropic.com/settings/keys"
),
}),
});
let messages = [];
await chat({
preview: "",
ignoreBlur: true,
onSubmit: async (input) => {
if (input === "") {
return;
}
try {
let newMessage = {
role: "user",
content: [
{
type: "text",
text: input,
},
],
};
chat.addMessage("");
let messageContent = "";
// const response = await anthropic.messages
anthropic.messages
.stream({
model: "claude-3-opus-20240229",
max_tokens: 1000,
temperature: 1,
messages: messages.concat([newMessage]),
})
.on("text", (text) => {
messageContent += text;
chat.setMessage(-1, md(messageContent));
})
.on("message_stop", () => {
messages = messages.concat([
newMessage,
{
role: "assistant",
content: messageContent,
},
]);
});
} catch (e) {
console.log(e);
chat.addMessage("");
chat.setMessage(-1, md("Error: " + e.message));
}
},
shortcuts: [
{
name: "Copy",
key: `cmd+shift+c`,
bar: "right",
onPress: async () => {
await copy(messages[messages.length - 1].content);
},
},
],
});
// Author: Eduard Uffelmann
// Linkedin: https://www.linkedin.com/in/euffelmann/
// Twitter: @schmedu_
// Website: https://schmedu.com
import "@johnlindquist/kit";
import axios from "axios";
import * as fs from "fs";
import AdmZip from "adm-zip";
import * as os from "os";
import { createHash } from "crypto";
function getHash(input: string, algorithm: string): string {
const hash = createHash(algorithm);
hash.update(input);
return hash.digest("hex");
}
async function downloadZipFile(url: string, outputPath: string): Promise<void> {
try {
const response = await axios.get(url, { responseType: "arraybuffer" });
fs.writeFileSync(outputPath, response.data);
notify(`Zip file downloaded to ${outputPath}`);
} catch (error) {
console.error(`Error downloading the zip file: ${error.message}`);
}
}
export interface Kenv {
name: string;
title: string;
description: string;
price: string | undefined;
paylink: string;
priceSingle?: string | undefined;
href: string;
installation?: string;
scriptNames?: string[];
}
interface Credentials {
licenseKey: string;
instanceId: string;
}
async function getDownloadLink(kenv: Kenv) {
let downloadLink;
let credentialsDB = await store("LicenseKeys", {});
if (typeof kenv.price !== "string") {
log("is paid tool");
let licenseKey = "";
let instanceId = "";
if (await credentialsDB.has(kenv.name)) {
// license was activated already
log("has credentials");
// @ts-ignore
let credentials: Credentials = await credentialsDB.get(kenv.name);
licenseKey = credentials.licenseKey;
instanceId = credentials.instanceId;
try {
let downloadLinkRequest = await axios.get(
`${BASE_URL}/api/client/${encodeURIComponent(
kenv.name
)}?secret=${encodeURIComponent(
CLIENT_SECRET
)}&licenseKey=${encodeURIComponent(
licenseKey
)}&instanceId=${encodeURIComponent(instanceId)}`
);
downloadLink = downloadLinkRequest.data.url;
// await credentialsDB.set(kenv.name, { licenseKey, instanceId });
} catch (err) {
log("Caught Error", err);
let body = encodeURIComponent(err.message);
await credentialsDB.delete(kenv.name);
let sendReport = await arg(
{
placeholder: "Error downloading the toolkit",
hint: `There was an error while downloading the toolkit. Please send an error report so I can help you install it. Sorry for the inconvenience!`,
ignoreBlur: true,
},
[
{
name: "Send Error Report and get contacted",
preview:
"You will only send the license key and your (hashed) instanceID and the error message. Nothing else. I will contact you via the email that you used for the purchase.",
value: true,
},
{
name: "Ignore",
value: false,
},
]
);
if (sendReport) {
try {
await axios.post(
`${BASE_URL}/api/client/${encodeURIComponent(
kenv.name
)}/error-reporting?secret=${encodeURIComponent(
CLIENT_SECRET
)}&licenseKey=${encodeURIComponent(
licenseKey
)}&instanceId=${encodeURIComponent(
instanceId
)}&error=${encodeURIComponent(
"LicenseActiveErrorDownloading"
)}&errorBody=${body}`
);
} catch (err) {
// continue
}
}
exit();
}
} else {
log("no credentials");
licenseKey = await arg({
placeholder: "Enter the license Key",
hint: `Buy a license key for the <a href="${kenv.paylink}" target="_blank">${kenv.name}</a> Toolkit for \$${kenv.price}`,
ignoreBlur: true,
alwaysOnTop: true,
});
let instanceName = getHash(os.userInfo().username, "md5");
try {
// try activating the license
let downloadLinkRequest = await axios.post(
`${BASE_URL}/api/client/${encodeURIComponent(
kenv.name
)}?secret=${encodeURIComponent(
CLIENT_SECRET
)}&licenseKey=${encodeURIComponent(
licenseKey
)}&instanceName=${encodeURIComponent(instanceName)}`
);
downloadLink = downloadLinkRequest.data.url;
instanceId = downloadLinkRequest.data.instanceId;
await credentialsDB.set(kenv.name, { licenseKey, instanceId });
} catch (err) {
log(err, err.message, err.response.data, instanceName, licenseKey);
let body = encodeURIComponent(err.message);
let sendReport = await arg(
{
placeholder: "Error activating the License Key",
hint: `There was an error while activating your license key. Please send an error report so I can help you install it. Sorry for the inconvenience.`,
},
[
{
name: "Send error report and get contacted",
preview:
"You will only send the license key and your (hashed) instanceName and the error message. Nothing else. I will contact you via the email that you used for the purchase.",
value: true,
},
{
name: "Ignore",
value: false,
},
]
);
if (sendReport) {
try {
await axios.post(
`${BASE_URL}/api/client/${encodeURIComponent(
kenv.name
)}/error-reporting?secret=${encodeURIComponent(
CLIENT_SECRET
)}&licenseKey=${encodeURIComponent(
licenseKey
)}&instanceName=${encodeURIComponent(
instanceName
)}&error=${encodeURIComponent(
"LicenseActivatingError"
)}&errorBody=${body}`
);
} catch (err) {
// continue
}
}
exit();
}
}
} else {
log("free tool");
try {
let downloadLinkRequest = await axios.get(
`${BASE_URL}/api/client/${encodeURIComponent(
kenv.name
)}?secret=${encodeURIComponent(CLIENT_SECRET)}`
);
downloadLink = downloadLinkRequest.data.url;
} catch (err) {
log("Caught Error", err);
let body = encodeURIComponent(err.message);
let sendReport = await arg(
{
placeholder: "Error downloading the toolkit",
hint: `There was an error while downloading the toolkit. Please send an error report so you can get some help to install it. Sorry for the inconvenience!`,
},
[
{
name: "Send error report and get contacted",
preview:
"You will only send the error message and your email address.",
value: true,
},
{
name: "Ignore",
value: false,
},
]
);
if (sendReport) {
let email = await arg("What is your email?");
try {
await axios.post(
`${BASE_URL}/api/client/${encodeURIComponent(
kenv.name
)}/error-reporting?secret=${encodeURIComponent(
CLIENT_SECRET
)}&email=${encodeURIComponent(email)}&error=${encodeURIComponent(
"LicenseActivatingError"
)}&errorBody=${body}`
);
} catch (err) {
// continue
}
}
exit();
}
}
return downloadLink;
}
const CLIENT_SECRET = "L7J7yLVcZdJbAFcVMpVcJaXwdoNsxLdW3ez9LFASStE";
const BASE_URL = "https://schmedu.com";
let toolsReq = await axios.get(
`${BASE_URL}/api/client?secret=${encodeURIComponent(CLIENT_SECRET)}`
);
let tools = toolsReq.data.tools as Kenv[];
let kenv = await arg(
{
placeholder: "Which kenv",
ignoreBlur: true,
},
tools.map((tool) => {
return {
name: tool.name,
description: tool.description,
value: tool,
// preview: `<img src="${BASE_URL}/img/kenvs/${tool.name}.png" class="" />`,
preview: md(
`# ${tool.name}: ${tool.description}
## Scripts
${tool.scriptNames.map((script) => `- ${script}`).join("\n")}`
),
};
})
);
let downloadLink = await getDownloadLink(kenv);
let zipDownloadPath = home("Downloads", `${kenv.name}.zip`);
await downloadZipFile(downloadLink, zipDownloadPath);
let kenvFolder = kenvPath("kenvs", kenv.name);
if (await isDir(kenvFolder)) {
let shouldOverwrite = await arg(
`${kenv.name} already exists. Do you want to overwrite it?`,
[
{ name: "No", value: false },
{ name: "Yes", value: true },
]
);
if (!shouldOverwrite) {
await div({
html: md(`# ${kenv.name} already exists!
Download canceled because it should not be replaced.`),
});
exit();
} else {
await rm(kenvFolder);
}
}
await mkdir("-p", kenvFolder);
const zip = new AdmZip(zipDownloadPath);
zip.extractAllTo(kenvFolder, false);
await rm(zipDownloadPath);
await div({
html: md(`# Installed ${kenv.name}!
Please allow notifications for ScriptKit. Many tools rely on them :-)`),
});
notify(`${kenv.name} successfully installed!`);
Keyboard-First Tools and Tips 🚀
Subscribe if you want to hear from my learnings and get my newest tools. I will never spam you. Pinky promise 🤙