Compare commits
4 Commits
7810be3cc1
...
4f507d6bd1
| Author | SHA1 | Date | |
|---|---|---|---|
| 4f507d6bd1 | |||
| 04d7eda8c4 | |||
| 32fda4a7e9 | |||
| 6c816ae5ab |
17
flake.lock
generated
17
flake.lock
generated
@@ -848,6 +848,22 @@
|
|||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"pi-harness": {
|
||||||
|
"flake": false,
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1774210293,
|
||||||
|
"narHash": "sha256-YzQn5r5KAORu6FI7dIkKWTU5f/HYinYu7i8QwPKCYik=",
|
||||||
|
"owner": "aliou",
|
||||||
|
"repo": "pi-harness",
|
||||||
|
"rev": "720db4f57cafd732953fae14ecb5eb50ff4d808c",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "aliou",
|
||||||
|
"repo": "pi-harness",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
"pi-rose-pine": {
|
"pi-rose-pine": {
|
||||||
"flake": false,
|
"flake": false,
|
||||||
"locked": {
|
"locked": {
|
||||||
@@ -911,6 +927,7 @@
|
|||||||
"nixvim": "nixvim",
|
"nixvim": "nixvim",
|
||||||
"pi-agent-stuff": "pi-agent-stuff",
|
"pi-agent-stuff": "pi-agent-stuff",
|
||||||
"pi-elixir": "pi-elixir",
|
"pi-elixir": "pi-elixir",
|
||||||
|
"pi-harness": "pi-harness",
|
||||||
"pi-rose-pine": "pi-rose-pine",
|
"pi-rose-pine": "pi-rose-pine",
|
||||||
"sops-nix": "sops-nix",
|
"sops-nix": "sops-nix",
|
||||||
"zjstatus": "zjstatus"
|
"zjstatus": "zjstatus"
|
||||||
|
|||||||
@@ -76,6 +76,10 @@
|
|||||||
url = "github:dannote/pi-elixir";
|
url = "github:dannote/pi-elixir";
|
||||||
flake = false;
|
flake = false;
|
||||||
};
|
};
|
||||||
|
pi-harness = {
|
||||||
|
url = "github:aliou/pi-harness";
|
||||||
|
flake = false;
|
||||||
|
};
|
||||||
pi-rose-pine = {
|
pi-rose-pine = {
|
||||||
url = "github:zenobi-us/pi-rose-pine";
|
url = "github:zenobi-us/pi-rose-pine";
|
||||||
flake = false;
|
flake = false;
|
||||||
|
|||||||
190
modules/_ai-tools/no-git.ts
Normal file
190
modules/_ai-tools/no-git.ts
Normal file
@@ -0,0 +1,190 @@
|
|||||||
|
/**
|
||||||
|
* No Git Extension
|
||||||
|
*
|
||||||
|
* Blocks direct git invocations and tells the LLM to use jj (Jujutsu) instead.
|
||||||
|
* Mentions of the word "git" in search patterns, strings, comments, etc. are allowed.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
|
||||||
|
import { isToolCallEventType } from "@mariozechner/pi-coding-agent";
|
||||||
|
|
||||||
|
type ShellToken =
|
||||||
|
| { type: "word"; value: string }
|
||||||
|
| { type: "operator"; value: string };
|
||||||
|
|
||||||
|
const COMMAND_PREFIXES = new Set(["env", "command", "builtin", "time", "sudo", "nohup", "nice"]);
|
||||||
|
const SHELL_KEYWORDS = new Set(["if", "then", "elif", "else", "do", "while", "until", "case", "in"]);
|
||||||
|
const SHELL_INTERPRETERS = new Set(["bash", "sh", "zsh", "fish", "nu"]);
|
||||||
|
|
||||||
|
function isAssignmentWord(value: string): boolean {
|
||||||
|
return /^[A-Za-z_][A-Za-z0-9_]*=.*/.test(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
function tokenizeShell(command: string): ShellToken[] {
|
||||||
|
const tokens: ShellToken[] = [];
|
||||||
|
let current = "";
|
||||||
|
let quote: "'" | '"' | null = null;
|
||||||
|
|
||||||
|
const pushWord = () => {
|
||||||
|
if (!current) return;
|
||||||
|
tokens.push({ type: "word", value: current });
|
||||||
|
current = "";
|
||||||
|
};
|
||||||
|
|
||||||
|
for (let i = 0; i < command.length; i++) {
|
||||||
|
const char = command[i];
|
||||||
|
|
||||||
|
if (quote) {
|
||||||
|
if (quote === "'") {
|
||||||
|
if (char === "'") {
|
||||||
|
quote = null;
|
||||||
|
} else {
|
||||||
|
current += char;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (char === '"') {
|
||||||
|
quote = null;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (char === "\\") {
|
||||||
|
if (i + 1 < command.length) {
|
||||||
|
current += command[i + 1];
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
current += char;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (char === "'" || char === '"') {
|
||||||
|
quote = char;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (char === "\\") {
|
||||||
|
if (i + 1 < command.length) {
|
||||||
|
current += command[i + 1];
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (/\s/.test(char)) {
|
||||||
|
pushWord();
|
||||||
|
if (char === "\n") {
|
||||||
|
tokens.push({ type: "operator", value: "\n" });
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const twoCharOperator = command.slice(i, i + 2);
|
||||||
|
if (twoCharOperator === "&&" || twoCharOperator === "||") {
|
||||||
|
pushWord();
|
||||||
|
tokens.push({ type: "operator", value: twoCharOperator });
|
||||||
|
i += 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (char === ";" || char === "|" || char === "(" || char === ")") {
|
||||||
|
pushWord();
|
||||||
|
tokens.push({ type: "operator", value: char });
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
current += char;
|
||||||
|
}
|
||||||
|
|
||||||
|
pushWord();
|
||||||
|
return tokens;
|
||||||
|
}
|
||||||
|
|
||||||
|
function findCommandWord(words: string[]): { word?: string; index: number } {
|
||||||
|
for (let i = 0; i < words.length; i++) {
|
||||||
|
const word = words[i];
|
||||||
|
if (SHELL_KEYWORDS.has(word)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (isAssignmentWord(word)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (COMMAND_PREFIXES.has(word)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
return { word, index: i };
|
||||||
|
}
|
||||||
|
|
||||||
|
return { index: words.length };
|
||||||
|
}
|
||||||
|
|
||||||
|
function getInlineShellCommand(words: string[], commandIndex: number): string | null {
|
||||||
|
for (let i = commandIndex + 1; i < words.length; i++) {
|
||||||
|
const word = words[i];
|
||||||
|
if (/^(?:-[A-Za-z]*c[A-Za-z]*|--command)$/.test(word)) {
|
||||||
|
return words[i + 1] ?? null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function segmentContainsBlockedGit(words: string[]): boolean {
|
||||||
|
const { word, index } = findCommandWord(words);
|
||||||
|
if (!word) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (word === "git") {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (word === "jj") {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (SHELL_INTERPRETERS.has(word)) {
|
||||||
|
const inlineCommand = getInlineShellCommand(words, index);
|
||||||
|
return inlineCommand ? containsBlockedGitInvocation(inlineCommand) : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function containsBlockedGitInvocation(command: string): boolean {
|
||||||
|
const tokens = tokenizeShell(command);
|
||||||
|
let words: string[] = [];
|
||||||
|
|
||||||
|
for (const token of tokens) {
|
||||||
|
if (token.type === "operator") {
|
||||||
|
if (segmentContainsBlockedGit(words)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
words = [];
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
words.push(token.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return segmentContainsBlockedGit(words);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function (pi: ExtensionAPI) {
|
||||||
|
pi.on("tool_call", async (event, _ctx) => {
|
||||||
|
if (!isToolCallEventType("bash", event)) return;
|
||||||
|
|
||||||
|
const command = event.input.command.trim();
|
||||||
|
|
||||||
|
if (containsBlockedGitInvocation(command)) {
|
||||||
|
return {
|
||||||
|
block: true,
|
||||||
|
reason: "git is not used in this project. Use jj (Jujutsu) instead.",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
28
modules/_ai-tools/no-scripting.ts
Normal file
28
modules/_ai-tools/no-scripting.ts
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
/**
|
||||||
|
* No Scripting Extension
|
||||||
|
*
|
||||||
|
* Blocks python, perl, ruby, php, lua, and inline bash/sh scripts.
|
||||||
|
* Tells the LLM to use `nu -c` instead.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
|
||||||
|
import { isToolCallEventType } from "@mariozechner/pi-coding-agent";
|
||||||
|
|
||||||
|
const SCRIPTING_PATTERN =
|
||||||
|
/(?:^|[;&|]\s*|&&\s*|\|\|\s*|\$\(\s*|`\s*)(?:python[23]?|perl|ruby|php|lua|bash\s+-c|sh\s+-c)\s/;
|
||||||
|
|
||||||
|
export default function (pi: ExtensionAPI) {
|
||||||
|
pi.on("tool_call", async (event, _ctx) => {
|
||||||
|
if (!isToolCallEventType("bash", event)) return;
|
||||||
|
|
||||||
|
const command = event.input.command.trim();
|
||||||
|
|
||||||
|
if (SCRIPTING_PATTERN.test(command)) {
|
||||||
|
return {
|
||||||
|
block: true,
|
||||||
|
reason:
|
||||||
|
"Do not use python, perl, ruby, php, lua, or inline bash/sh for scripting. Use `nu -c` instead.",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
260
modules/_ai-tools/session-name.ts
Normal file
260
modules/_ai-tools/session-name.ts
Normal file
@@ -0,0 +1,260 @@
|
|||||||
|
import type { ExtensionAPI, ExtensionContext } from "@mariozechner/pi-coding-agent";
|
||||||
|
import {
|
||||||
|
createAgentSession,
|
||||||
|
DefaultResourceLoader,
|
||||||
|
getAgentDir,
|
||||||
|
SessionManager,
|
||||||
|
SettingsManager,
|
||||||
|
} from "@mariozechner/pi-coding-agent";
|
||||||
|
|
||||||
|
interface SessionNameState {
|
||||||
|
hasAutoNamed: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const TITLE_MODEL = {
|
||||||
|
provider: "openai-codex",
|
||||||
|
id: "gpt-5.4-mini",
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
const MAX_TITLE_LENGTH = 50;
|
||||||
|
const MAX_RETRIES = 2;
|
||||||
|
const FALLBACK_LENGTH = 50;
|
||||||
|
const TITLE_ENTRY_TYPE = "vendored-session-title";
|
||||||
|
|
||||||
|
const TITLE_SYSTEM_PROMPT = `You are generating a succinct title for a coding session based on the provided conversation.
|
||||||
|
|
||||||
|
Requirements:
|
||||||
|
- Maximum 50 characters
|
||||||
|
- Sentence case (capitalize only first word and proper nouns)
|
||||||
|
- Capture the main intent or task
|
||||||
|
- Reuse the user's exact words and technical terms
|
||||||
|
- Match the user's language
|
||||||
|
- No quotes, colons, or markdown formatting
|
||||||
|
- No generic titles like "Coding session" or "Help with code"
|
||||||
|
- No explanations or commentary
|
||||||
|
|
||||||
|
Output ONLY the title text. Nothing else.`;
|
||||||
|
|
||||||
|
function isTurnCompleted(event: unknown): boolean {
|
||||||
|
if (!event || typeof event !== "object") return false;
|
||||||
|
const message = (event as { message?: unknown }).message;
|
||||||
|
if (!message || typeof message !== "object") return false;
|
||||||
|
const stopReason = (message as { stopReason?: unknown }).stopReason;
|
||||||
|
return typeof stopReason === "string" && stopReason.toLowerCase() === "stop";
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildFallbackTitle(userText: string): string {
|
||||||
|
const text = userText.trim();
|
||||||
|
if (text.length <= FALLBACK_LENGTH) return text;
|
||||||
|
const truncated = text.slice(0, FALLBACK_LENGTH - 3);
|
||||||
|
const lastSpace = truncated.lastIndexOf(" ");
|
||||||
|
return `${lastSpace > 0 ? truncated.slice(0, lastSpace) : truncated}...`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function postProcessTitle(raw: string): string {
|
||||||
|
let title = raw;
|
||||||
|
|
||||||
|
title = title.replace(/<thinking[\s\S]*?<\/thinking>\s*/g, "");
|
||||||
|
title = title.replace(/^["'`]+|["'`]+$/g, "");
|
||||||
|
title = title.replace(/^#+\s*/, "");
|
||||||
|
title = title.replace(/\*{1,2}(.*?)\*{1,2}/g, "$1");
|
||||||
|
title = title.replace(/_{1,2}(.*?)_{1,2}/g, "$1");
|
||||||
|
title = title.replace(/^(Title|Summary|Session)\s*:\s*/i, "");
|
||||||
|
title =
|
||||||
|
title
|
||||||
|
.split("\n")
|
||||||
|
.map((line) => line.trim())
|
||||||
|
.find((line) => line.length > 0) ?? title;
|
||||||
|
title = title.trim();
|
||||||
|
|
||||||
|
if (title.length > MAX_TITLE_LENGTH) {
|
||||||
|
const truncated = title.slice(0, MAX_TITLE_LENGTH - 3);
|
||||||
|
const lastSpace = truncated.lastIndexOf(" ");
|
||||||
|
title = `${lastSpace > 0 ? truncated.slice(0, lastSpace) : truncated}...`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return title;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getLatestUserText(ctx: ExtensionContext): string | null {
|
||||||
|
const entries = ctx.sessionManager.getEntries();
|
||||||
|
for (let i = entries.length - 1; i >= 0; i -= 1) {
|
||||||
|
const entry = entries[i];
|
||||||
|
if (!entry || entry.type !== "message") continue;
|
||||||
|
if (entry.message.role !== "user") continue;
|
||||||
|
|
||||||
|
const { content } = entry.message as { content: unknown };
|
||||||
|
if (typeof content === "string") return content;
|
||||||
|
if (!Array.isArray(content)) return null;
|
||||||
|
|
||||||
|
return content
|
||||||
|
.filter(
|
||||||
|
(part): part is { type: string; text?: string } =>
|
||||||
|
typeof part === "object" && part !== null && "type" in part,
|
||||||
|
)
|
||||||
|
.filter((part) => part.type === "text" && typeof part.text === "string")
|
||||||
|
.map((part) => part.text ?? "")
|
||||||
|
.join(" ");
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getLatestAssistantText(ctx: ExtensionContext): string | null {
|
||||||
|
const entries = ctx.sessionManager.getEntries();
|
||||||
|
for (let i = entries.length - 1; i >= 0; i -= 1) {
|
||||||
|
const entry = entries[i];
|
||||||
|
if (!entry || entry.type !== "message") continue;
|
||||||
|
if (entry.message.role !== "assistant") continue;
|
||||||
|
|
||||||
|
const { content } = entry.message as { content: unknown };
|
||||||
|
if (typeof content === "string") return content;
|
||||||
|
if (!Array.isArray(content)) return null;
|
||||||
|
|
||||||
|
return content
|
||||||
|
.filter(
|
||||||
|
(part): part is { type: string; text?: string } =>
|
||||||
|
typeof part === "object" && part !== null && "type" in part,
|
||||||
|
)
|
||||||
|
.filter((part) => part.type === "text" && typeof part.text === "string")
|
||||||
|
.map((part) => part.text ?? "")
|
||||||
|
.join("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolveModel(ctx: ExtensionContext) {
|
||||||
|
const available = ctx.modelRegistry.getAvailable();
|
||||||
|
const model = available.find(
|
||||||
|
(candidate) => candidate.provider === TITLE_MODEL.provider && candidate.id === TITLE_MODEL.id,
|
||||||
|
);
|
||||||
|
if (model) return model;
|
||||||
|
|
||||||
|
const existsWithoutKey = ctx.modelRegistry
|
||||||
|
.getAll()
|
||||||
|
.some((candidate) => candidate.provider === TITLE_MODEL.provider && candidate.id === TITLE_MODEL.id);
|
||||||
|
if (existsWithoutKey) {
|
||||||
|
throw new Error(
|
||||||
|
`Model ${TITLE_MODEL.provider}/${TITLE_MODEL.id} exists but has no configured API key.`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error(`Model ${TITLE_MODEL.provider}/${TITLE_MODEL.id} is not available.`);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function generateTitle(userText: string, assistantText: string, ctx: ExtensionContext): Promise<string> {
|
||||||
|
const agentDir = getAgentDir();
|
||||||
|
const settingsManager = SettingsManager.create(ctx.cwd, agentDir);
|
||||||
|
const resourceLoader = new DefaultResourceLoader({
|
||||||
|
cwd: ctx.cwd,
|
||||||
|
agentDir,
|
||||||
|
settingsManager,
|
||||||
|
noExtensions: true,
|
||||||
|
noPromptTemplates: true,
|
||||||
|
noThemes: true,
|
||||||
|
noSkills: true,
|
||||||
|
systemPromptOverride: () => TITLE_SYSTEM_PROMPT,
|
||||||
|
appendSystemPromptOverride: () => [],
|
||||||
|
agentsFilesOverride: () => ({ agentsFiles: [] }),
|
||||||
|
});
|
||||||
|
await resourceLoader.reload();
|
||||||
|
|
||||||
|
const { session } = await createAgentSession({
|
||||||
|
model: resolveModel(ctx),
|
||||||
|
thinkingLevel: "off",
|
||||||
|
sessionManager: SessionManager.inMemory(),
|
||||||
|
modelRegistry: ctx.modelRegistry,
|
||||||
|
resourceLoader,
|
||||||
|
});
|
||||||
|
|
||||||
|
let accumulated = "";
|
||||||
|
const unsubscribe = session.subscribe((event) => {
|
||||||
|
if (event.type === "message_update" && event.assistantMessageEvent.type === "text_delta") {
|
||||||
|
accumulated += event.assistantMessageEvent.delta;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const description = assistantText
|
||||||
|
? `<user>${userText}</user>\n<assistant>${assistantText}</assistant>`
|
||||||
|
: `<user>${userText}</user>`;
|
||||||
|
const userMessage = `<conversation>\n${description}\n</conversation>\n\nGenerate a title:`;
|
||||||
|
|
||||||
|
try {
|
||||||
|
await session.prompt(userMessage);
|
||||||
|
} finally {
|
||||||
|
unsubscribe();
|
||||||
|
session.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
return postProcessTitle(accumulated);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function generateAndSetTitle(pi: ExtensionAPI, ctx: ExtensionContext): Promise<void> {
|
||||||
|
const userText = getLatestUserText(ctx);
|
||||||
|
if (!userText?.trim()) return;
|
||||||
|
|
||||||
|
const assistantText = getLatestAssistantText(ctx) ?? "";
|
||||||
|
if (!assistantText.trim()) return;
|
||||||
|
|
||||||
|
let lastError: Error | null = null;
|
||||||
|
for (let attempt = 1; attempt <= MAX_RETRIES; attempt += 1) {
|
||||||
|
try {
|
||||||
|
const title = await generateTitle(userText, assistantText, ctx);
|
||||||
|
if (!title) continue;
|
||||||
|
|
||||||
|
pi.setSessionName(title);
|
||||||
|
pi.appendEntry(TITLE_ENTRY_TYPE, {
|
||||||
|
title,
|
||||||
|
rawUserText: userText,
|
||||||
|
rawAssistantText: assistantText,
|
||||||
|
attempt,
|
||||||
|
model: `${TITLE_MODEL.provider}/${TITLE_MODEL.id}`,
|
||||||
|
});
|
||||||
|
ctx.ui.notify(`Session: ${title}`, "info");
|
||||||
|
return;
|
||||||
|
} catch (error) {
|
||||||
|
lastError = error instanceof Error ? error : new Error(String(error));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const fallback = buildFallbackTitle(userText);
|
||||||
|
pi.setSessionName(fallback);
|
||||||
|
pi.appendEntry(TITLE_ENTRY_TYPE, {
|
||||||
|
title: fallback,
|
||||||
|
fallback: true,
|
||||||
|
error: lastError?.message ?? "Unknown error",
|
||||||
|
rawUserText: userText,
|
||||||
|
rawAssistantText: assistantText,
|
||||||
|
model: `${TITLE_MODEL.provider}/${TITLE_MODEL.id}`,
|
||||||
|
});
|
||||||
|
ctx.ui.notify(`Title generation failed, using fallback: ${fallback}`, "warning");
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function setupSessionNameHook(pi: ExtensionAPI) {
|
||||||
|
const state: SessionNameState = {
|
||||||
|
hasAutoNamed: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
pi.on("session_start", async () => {
|
||||||
|
state.hasAutoNamed = false;
|
||||||
|
});
|
||||||
|
|
||||||
|
pi.on("session_switch", async () => {
|
||||||
|
state.hasAutoNamed = false;
|
||||||
|
});
|
||||||
|
|
||||||
|
pi.on("turn_end", async (event, ctx) => {
|
||||||
|
if (state.hasAutoNamed) return;
|
||||||
|
|
||||||
|
if (pi.getSessionName()) {
|
||||||
|
state.hasAutoNamed = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isTurnCompleted(event)) return;
|
||||||
|
|
||||||
|
await generateAndSetTitle(pi, ctx);
|
||||||
|
state.hasAutoNamed = true;
|
||||||
|
});
|
||||||
|
}
|
||||||
6
modules/_overlays/ast-grep.nix
Normal file
6
modules/_overlays/ast-grep.nix
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
{inputs, ...}: final: prev: {
|
||||||
|
ast-grep =
|
||||||
|
prev.ast-grep.overrideAttrs (old: {
|
||||||
|
doCheck = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
44
modules/_overlays/cog-cli.nix
Normal file
44
modules/_overlays/cog-cli.nix
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
{inputs, ...}: final: prev: let
|
||||||
|
version = "0.22.1";
|
||||||
|
srcs = {
|
||||||
|
x86_64-linux =
|
||||||
|
prev.fetchurl {
|
||||||
|
url = "https://github.com/trycog/cog-cli/releases/download/v${version}/cog-linux-x86_64.tar.gz";
|
||||||
|
hash = "sha256-ET+sNXisUrHShR1gxqdumegXycXcxGzJcQOdTr5005w=";
|
||||||
|
};
|
||||||
|
aarch64-darwin =
|
||||||
|
prev.fetchurl {
|
||||||
|
url = "https://github.com/trycog/cog-cli/releases/download/v${version}/cog-darwin-arm64.tar.gz";
|
||||||
|
hash = "sha256-jcN+DtOqr3or5C71jp7AIAz0wh73FYybCC4FRBykKO4=";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
in {
|
||||||
|
cog-cli =
|
||||||
|
prev.stdenvNoCC.mkDerivation {
|
||||||
|
pname = "cog-cli";
|
||||||
|
inherit version;
|
||||||
|
src =
|
||||||
|
srcs.${prev.stdenv.hostPlatform.system}
|
||||||
|
or (throw "Unsupported system for cog-cli: ${prev.stdenv.hostPlatform.system}");
|
||||||
|
|
||||||
|
dontUnpack = true;
|
||||||
|
dontConfigure = true;
|
||||||
|
dontBuild = true;
|
||||||
|
|
||||||
|
installPhase = ''
|
||||||
|
runHook preInstall
|
||||||
|
tar -xzf "$src"
|
||||||
|
install -Dm755 cog "$out/bin/cog"
|
||||||
|
runHook postInstall
|
||||||
|
'';
|
||||||
|
|
||||||
|
meta = with prev.lib; {
|
||||||
|
description = "Memory, code intelligence, and debugging for AI agents";
|
||||||
|
homepage = "https://github.com/trycog/cog-cli";
|
||||||
|
license = licenses.mit;
|
||||||
|
mainProgram = "cog";
|
||||||
|
platforms = builtins.attrNames srcs;
|
||||||
|
sourceProvenance = [sourceTypes.binaryNativeCode];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
3
modules/_overlays/himalaya.nix
Normal file
3
modules/_overlays/himalaya.nix
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
{inputs, ...}: final: prev: {
|
||||||
|
himalaya = inputs.himalaya.packages.${prev.stdenv.hostPlatform.system}.default;
|
||||||
|
}
|
||||||
15
modules/_overlays/jj-ryu.nix
Normal file
15
modules/_overlays/jj-ryu.nix
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
{inputs, ...}: final: prev: let
|
||||||
|
naersk-lib = prev.callPackage inputs.naersk {};
|
||||||
|
manifest = (prev.lib.importTOML "${inputs.jj-ryu}/Cargo.toml").package;
|
||||||
|
in {
|
||||||
|
jj-ryu =
|
||||||
|
naersk-lib.buildPackage {
|
||||||
|
pname = manifest.name;
|
||||||
|
version = manifest.version;
|
||||||
|
src = inputs.jj-ryu;
|
||||||
|
nativeBuildInputs = [prev.pkg-config];
|
||||||
|
buildInputs = [prev.openssl];
|
||||||
|
OPENSSL_NO_VENDOR = 1;
|
||||||
|
doCheck = false;
|
||||||
|
};
|
||||||
|
}
|
||||||
1
modules/_overlays/jj-starship.nix
Normal file
1
modules/_overlays/jj-starship.nix
Normal file
@@ -0,0 +1 @@
|
|||||||
|
{inputs, ...}: inputs.jj-starship.overlays.default
|
||||||
10
modules/_overlays/pi-agent-stuff.nix
Normal file
10
modules/_overlays/pi-agent-stuff.nix
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
{inputs, ...}: final: prev: {
|
||||||
|
pi-agent-stuff =
|
||||||
|
prev.buildNpmPackage {
|
||||||
|
pname = "pi-agent-stuff";
|
||||||
|
version = "1.5.0";
|
||||||
|
src = inputs.pi-agent-stuff;
|
||||||
|
npmDepsHash = "sha256-pyXMNdlie8vAkhz2f3GUGT3CCYuwt+xkWnsijBajXIo=";
|
||||||
|
dontNpmBuild = true;
|
||||||
|
};
|
||||||
|
}
|
||||||
33
modules/_overlays/pi-harness.nix
Normal file
33
modules/_overlays/pi-harness.nix
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
{inputs, ...}: final: prev: {
|
||||||
|
pi-harness =
|
||||||
|
prev.stdenvNoCC.mkDerivation {
|
||||||
|
pname = "pi-harness";
|
||||||
|
version = "0.0.0";
|
||||||
|
src = inputs.pi-harness;
|
||||||
|
|
||||||
|
pnpmDeps =
|
||||||
|
prev.fetchPnpmDeps {
|
||||||
|
pname = "pi-harness";
|
||||||
|
version = "0.0.0";
|
||||||
|
src = inputs.pi-harness;
|
||||||
|
pnpm = prev.pnpm_10;
|
||||||
|
fetcherVersion = 1;
|
||||||
|
hash = "sha256-FgtJnmJ0/udz2A9N2DQns+a2CspMDEDk0DPUAxmCVY4=";
|
||||||
|
};
|
||||||
|
|
||||||
|
nativeBuildInputs = [
|
||||||
|
prev.pnpmConfigHook
|
||||||
|
prev.pnpm_10
|
||||||
|
prev.nodejs
|
||||||
|
];
|
||||||
|
|
||||||
|
dontBuild = true;
|
||||||
|
|
||||||
|
installPhase = ''
|
||||||
|
runHook preInstall
|
||||||
|
mkdir -p $out/lib/node_modules/@aliou/pi-harness
|
||||||
|
cp -r . $out/lib/node_modules/@aliou/pi-harness
|
||||||
|
runHook postInstall
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
}
|
||||||
16
modules/_overlays/pi-mcp-adapter.nix
Normal file
16
modules/_overlays/pi-mcp-adapter.nix
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
{inputs, ...}: final: prev: {
|
||||||
|
pi-mcp-adapter =
|
||||||
|
prev.buildNpmPackage {
|
||||||
|
pname = "pi-mcp-adapter";
|
||||||
|
version = "2.2.0";
|
||||||
|
src =
|
||||||
|
prev.fetchFromGitHub {
|
||||||
|
owner = "nicobailon";
|
||||||
|
repo = "pi-mcp-adapter";
|
||||||
|
rev = "v2.2.0";
|
||||||
|
hash = "sha256-E6Kf+OyTN/pF8pKADJO0B1+buAPqNcXnZl9ssZwSP8U=";
|
||||||
|
};
|
||||||
|
npmDepsHash = "sha256-myJ9h/zC/KDddt8NOVvJjjqbnkdEN4ZR+okCR5nu7hM=";
|
||||||
|
dontNpmBuild = true;
|
||||||
|
};
|
||||||
|
}
|
||||||
3
modules/_overlays/zjstatus.nix
Normal file
3
modules/_overlays/zjstatus.nix
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
{inputs, ...}: final: prev: {
|
||||||
|
zjstatus = inputs.zjstatus.packages.${prev.stdenv.hostPlatform.system}.default;
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@
|
|||||||
den.aspects.ai-tools.homeManager = {
|
den.aspects.ai-tools.homeManager = {
|
||||||
pkgs,
|
pkgs,
|
||||||
inputs',
|
inputs',
|
||||||
|
lib,
|
||||||
...
|
...
|
||||||
}: {
|
}: {
|
||||||
home.packages = [
|
home.packages = [
|
||||||
@@ -20,77 +21,171 @@
|
|||||||
source = "${pkgs.pi-mcp-adapter}/lib/node_modules/pi-mcp-adapter";
|
source = "${pkgs.pi-mcp-adapter}/lib/node_modules/pi-mcp-adapter";
|
||||||
recursive = true;
|
recursive = true;
|
||||||
};
|
};
|
||||||
|
".pi/agent/extensions/no-git.ts".source = ./_ai-tools/no-git.ts;
|
||||||
|
".pi/agent/extensions/no-scripting.ts".source = ./_ai-tools/no-scripting.ts;
|
||||||
".pi/agent/extensions/review.ts".source = ./_ai-tools/review.ts;
|
".pi/agent/extensions/review.ts".source = ./_ai-tools/review.ts;
|
||||||
|
".pi/agent/extensions/session-name.ts".source = ./_ai-tools/session-name.ts;
|
||||||
".pi/agent/skills/elixir-dev" = {
|
".pi/agent/skills/elixir-dev" = {
|
||||||
source = "${inputs.pi-elixir}/skills/elixir-dev";
|
source = "${inputs.pi-elixir}/skills/elixir-dev";
|
||||||
recursive = true;
|
recursive = true;
|
||||||
};
|
};
|
||||||
|
".pi/agent/skills/jujutsu/SKILL.md".text =
|
||||||
|
lib.removePrefix "\n" (builtins.replaceStrings ["\t"] [""] ''
|
||||||
|
---
|
||||||
|
name: jujutsu
|
||||||
|
description: Manages version control with Jujutsu (jj), including rebasing, conflict resolution, and Git interop. Use when tracking changes, navigating history, squashing/splitting commits, or pushing to Git remotes.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Jujutsu
|
||||||
|
|
||||||
|
Git-compatible VCS focused on concurrent development and ease of use.
|
||||||
|
|
||||||
|
> ⚠️ **Not Git!** Jujutsu syntax differs from Git:
|
||||||
|
>
|
||||||
|
> - Parent: `@-` not `@~1` or `@^`
|
||||||
|
> - Grandparent: `@--` not `@~2`
|
||||||
|
> - Child: `@+` not `@~-1`
|
||||||
|
> - Use `jj log` not `jj changes`
|
||||||
|
|
||||||
|
## Key Commands
|
||||||
|
|
||||||
|
| Command | Description |
|
||||||
|
| -------------------------- | -------------------------------------------- |
|
||||||
|
| `jj st` | Show working copy status |
|
||||||
|
| `jj log` | Show change log |
|
||||||
|
| `jj diff` | Show changes in working copy |
|
||||||
|
| `jj new` | Create new change |
|
||||||
|
| `jj desc` | Edit change description |
|
||||||
|
| `jj squash` | Move changes to parent |
|
||||||
|
| `jj split` | Split current change |
|
||||||
|
| `jj rebase -s src -d dest` | Rebase changes |
|
||||||
|
| `jj absorb` | Move changes into stack of mutable revisions |
|
||||||
|
| `jj bisect` | Find bad revision by bisection |
|
||||||
|
| `jj fix` | Update files with formatting fixes |
|
||||||
|
| `jj sign` | Cryptographically sign a revision |
|
||||||
|
| `jj metaedit` | Modify metadata without changing content |
|
||||||
|
|
||||||
|
## Basic Workflow
|
||||||
|
|
||||||
|
```bash
|
||||||
|
jj new # Create new change
|
||||||
|
jj desc -m "feat: add feature" # Set description
|
||||||
|
jj log # View history
|
||||||
|
jj edit change-id # Switch to change
|
||||||
|
jj new --before @ # Time travel (create before current)
|
||||||
|
jj edit @- # Go to parent
|
||||||
|
```
|
||||||
|
|
||||||
|
## Time Travel
|
||||||
|
|
||||||
|
```bash
|
||||||
|
jj edit change-id # Switch to specific change
|
||||||
|
jj next --edit # Next child change
|
||||||
|
jj edit @- # Parent change
|
||||||
|
jj new --before @ -m msg # Insert before current
|
||||||
|
```
|
||||||
|
|
||||||
|
## Merging & Rebasing
|
||||||
|
|
||||||
|
```bash
|
||||||
|
jj new x yz -m msg # Merge changes
|
||||||
|
jj rebase -s src -d dest # Rebase source onto dest
|
||||||
|
jj abandon # Delete current change
|
||||||
|
```
|
||||||
|
|
||||||
|
## Conflicts
|
||||||
|
|
||||||
|
```bash
|
||||||
|
jj resolve # Interactive conflict resolution
|
||||||
|
# Edit files, then continue
|
||||||
|
```
|
||||||
|
|
||||||
|
## Revset Syntax
|
||||||
|
|
||||||
|
**Parent/child operators:**
|
||||||
|
|
||||||
|
| Syntax | Meaning | Example |
|
||||||
|
| ------ | ---------------- | -------------------- |
|
||||||
|
| `@-` | Parent of @ | `jj diff -r @-` |
|
||||||
|
| `@--` | Grandparent | `jj log -r @--` |
|
||||||
|
| `x-` | Parent of x | `jj diff -r abc123-` |
|
||||||
|
| `@+` | Child of @ | `jj log -r @+` |
|
||||||
|
| `x::y` | x to y inclusive | `jj log -r main::@` |
|
||||||
|
| `x..y` | x to y exclusive | `jj log -r main..@` |
|
||||||
|
| `x\|y` | Union (or) | `jj log -r 'a \| b'` |
|
||||||
|
|
||||||
|
**⚠️ Common mistakes:**
|
||||||
|
|
||||||
|
- ❌ `@~1` → ✅ `@-` (parent)
|
||||||
|
- ❌ `@^` → ✅ `@-` (parent)
|
||||||
|
- ❌ `@~-1` → ✅ `@+` (child)
|
||||||
|
- ❌ `jj changes` → ✅ `jj log` or `jj diff`
|
||||||
|
- ❌ `a,b,c` → ✅ `a | b | c` (union uses pipe, not comma)
|
||||||
|
|
||||||
|
**Functions:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
jj log -r 'heads(all())' # All heads
|
||||||
|
jj log -r 'remote_bookmarks()..' # Not on remote
|
||||||
|
jj log -r 'author(name)' # By author
|
||||||
|
jj log -r 'description(regex)' # By description
|
||||||
|
jj log -r 'mine()' # My commits
|
||||||
|
jj log -r 'committer_date(after:"7 days ago")' # Recent commits
|
||||||
|
jj log -r 'mine() & committer_date(after:"yesterday")' # My recent
|
||||||
|
```
|
||||||
|
|
||||||
|
## Templates
|
||||||
|
|
||||||
|
```bash
|
||||||
|
jj log -T 'commit_id ++ "\n" ++ description'
|
||||||
|
```
|
||||||
|
|
||||||
|
## Git Interop
|
||||||
|
|
||||||
|
```bash
|
||||||
|
jj bookmark create main -r @ # Create bookmark
|
||||||
|
jj git push --bookmark main # Push bookmark
|
||||||
|
jj git fetch # Fetch from remote
|
||||||
|
jj bookmark track main@origin # Track remote
|
||||||
|
```
|
||||||
|
|
||||||
|
## Advanced Commands
|
||||||
|
|
||||||
|
```bash
|
||||||
|
jj absorb # Auto-move changes to relevant commits in stack
|
||||||
|
jj bisect start # Start bisection
|
||||||
|
jj bisect good # Mark current as good
|
||||||
|
jj bisect bad # Mark current as bad
|
||||||
|
jj fix # Run configured formatters on files
|
||||||
|
jj sign -r @ # Sign current revision
|
||||||
|
jj metaedit -r @ -m "new message" # Edit metadata only
|
||||||
|
```
|
||||||
|
|
||||||
|
## Tips
|
||||||
|
|
||||||
|
- No staging: changes are immediate
|
||||||
|
- Use conventional commits: `type(scope): desc`
|
||||||
|
- `jj undo` to revert operations
|
||||||
|
- `jj op log` to see operation history
|
||||||
|
- Bookmarks are like branches
|
||||||
|
- `jj absorb` is powerful for fixing up commits in a stack
|
||||||
|
|
||||||
|
## Related Skills
|
||||||
|
|
||||||
|
- **gh**: GitHub CLI for PRs and issues
|
||||||
|
- **review**: Code review before committing
|
||||||
|
'');
|
||||||
".pi/agent/themes" = {
|
".pi/agent/themes" = {
|
||||||
source = "${inputs.pi-rose-pine}/themes";
|
source = "${inputs.pi-rose-pine}/themes";
|
||||||
recursive = true;
|
recursive = true;
|
||||||
};
|
};
|
||||||
".pi/agent/extensions/no-git.ts".text = ''
|
|
||||||
/**
|
|
||||||
* No Git Extension
|
|
||||||
*
|
|
||||||
* Blocks git commands and tells the LLM to use jj (Jujutsu) instead.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
|
|
||||||
import { isToolCallEventType } from "@mariozechner/pi-coding-agent";
|
|
||||||
|
|
||||||
export default function (pi: ExtensionAPI) {
|
|
||||||
pi.on("tool_call", async (event, _ctx) => {
|
|
||||||
if (!isToolCallEventType("bash", event)) return;
|
|
||||||
|
|
||||||
const command = event.input.command.trim();
|
|
||||||
|
|
||||||
if (/\bgit\b/.test(command) && !/\bjj\s+git\b/.test(command)) {
|
|
||||||
return {
|
|
||||||
block: true,
|
|
||||||
reason: "git is not used in this project. Use jj (Jujutsu) instead.",
|
|
||||||
};
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
'';
|
|
||||||
".pi/agent/extensions/no-scripting.ts".text = ''
|
|
||||||
/**
|
|
||||||
* No Scripting Extension
|
|
||||||
*
|
|
||||||
* Blocks python, perl, ruby, php, lua, and inline bash/sh scripts.
|
|
||||||
* Tells the LLM to use `nu -c` instead.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
|
|
||||||
import { isToolCallEventType } from "@mariozechner/pi-coding-agent";
|
|
||||||
|
|
||||||
const SCRIPTING_PATTERN =
|
|
||||||
/(?:^|[;&|]\s*|&&\s*|\|\|\s*|\$\(\s*|`\s*)(?:python[23]?|perl|ruby|php|lua|bash\s+-c|sh\s+-c)\s/;
|
|
||||||
|
|
||||||
export default function (pi: ExtensionAPI) {
|
|
||||||
pi.on("tool_call", async (event, _ctx) => {
|
|
||||||
if (!isToolCallEventType("bash", event)) return;
|
|
||||||
|
|
||||||
const command = event.input.command.trim();
|
|
||||||
|
|
||||||
if (SCRIPTING_PATTERN.test(command)) {
|
|
||||||
return {
|
|
||||||
block: true,
|
|
||||||
reason:
|
|
||||||
"Do not use python, perl, ruby, php, lua, or inline bash/sh for scripting. Use `nu -c` instead.",
|
|
||||||
};
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
'';
|
|
||||||
".pi/agent/settings.json".text =
|
".pi/agent/settings.json".text =
|
||||||
builtins.toJSON {
|
builtins.toJSON {
|
||||||
lastChangelogVersion = "0.61.1";
|
|
||||||
theme = "rose-pine-dawn";
|
theme = "rose-pine-dawn";
|
||||||
|
quietStartup = true;
|
||||||
hideThinkingBlock = true;
|
hideThinkingBlock = true;
|
||||||
defaultProvider = "anthropic";
|
defaultProvider = "openai-codex";
|
||||||
defaultModel = "claude-opus-4-6";
|
defaultModel = "gpt-5.4";
|
||||||
defaultThinkingLevel = "high";
|
defaultThinkingLevel = "high";
|
||||||
packages = [
|
packages = [
|
||||||
{
|
{
|
||||||
@@ -105,6 +200,13 @@
|
|||||||
prompts = [];
|
prompts = [];
|
||||||
themes = [];
|
themes = [];
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
source = "${pkgs.pi-harness}/lib/node_modules/@aliou/pi-harness";
|
||||||
|
extensions = ["extensions/breadcrumbs/index.ts"];
|
||||||
|
skills = [];
|
||||||
|
prompts = [];
|
||||||
|
themes = [];
|
||||||
|
}
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
".pi/agent/mcp.json".text =
|
".pi/agent/mcp.json".text =
|
||||||
|
|||||||
@@ -66,6 +66,10 @@
|
|||||||
url = "github:zenobi-us/pi-rose-pine";
|
url = "github:zenobi-us/pi-rose-pine";
|
||||||
flake = false;
|
flake = false;
|
||||||
};
|
};
|
||||||
|
pi-harness = {
|
||||||
|
url = "github:aliou/pi-harness";
|
||||||
|
flake = false;
|
||||||
|
};
|
||||||
# Overlay inputs
|
# Overlay inputs
|
||||||
himalaya.url = "github:pimalaya/himalaya";
|
himalaya.url = "github:pimalaya/himalaya";
|
||||||
jj-ryu = {
|
jj-ryu = {
|
||||||
|
|||||||
@@ -1,112 +1,23 @@
|
|||||||
{inputs, ...}: let
|
{inputs, ...}: let
|
||||||
overlays = [
|
overlays = [
|
||||||
# himalaya
|
# himalaya
|
||||||
(final: prev: {
|
(import ./_overlays/himalaya.nix {inherit inputs;})
|
||||||
himalaya = inputs.himalaya.packages.${prev.stdenv.hostPlatform.system}.default;
|
|
||||||
})
|
|
||||||
# ast-grep (test_scan_invalid_rule_id fails on darwin in sandbox)
|
# ast-grep (test_scan_invalid_rule_id fails on darwin in sandbox)
|
||||||
(final: prev: {
|
(import ./_overlays/ast-grep.nix {inherit inputs;})
|
||||||
ast-grep =
|
|
||||||
prev.ast-grep.overrideAttrs (old: {
|
|
||||||
doCheck = false;
|
|
||||||
});
|
|
||||||
})
|
|
||||||
# jj-ryu
|
# jj-ryu
|
||||||
(final: prev: let
|
(import ./_overlays/jj-ryu.nix {inherit inputs;})
|
||||||
naersk-lib = prev.callPackage inputs.naersk {};
|
|
||||||
manifest = (prev.lib.importTOML "${inputs.jj-ryu}/Cargo.toml").package;
|
|
||||||
in {
|
|
||||||
jj-ryu =
|
|
||||||
naersk-lib.buildPackage {
|
|
||||||
pname = manifest.name;
|
|
||||||
version = manifest.version;
|
|
||||||
src = inputs.jj-ryu;
|
|
||||||
nativeBuildInputs = [prev.pkg-config];
|
|
||||||
buildInputs = [prev.openssl];
|
|
||||||
OPENSSL_NO_VENDOR = 1;
|
|
||||||
doCheck = false;
|
|
||||||
};
|
|
||||||
})
|
|
||||||
|
|
||||||
# cog-cli
|
# cog-cli
|
||||||
(final: prev: let
|
(import ./_overlays/cog-cli.nix {inherit inputs;})
|
||||||
version = "0.22.0";
|
|
||||||
srcs = {
|
|
||||||
x86_64-linux =
|
|
||||||
prev.fetchurl {
|
|
||||||
url = "https://github.com/trycog/cog-cli/releases/download/v${version}/cog-linux-x86_64.tar.gz";
|
|
||||||
hash = "sha256-GKCO8kUUPl1OQSVhefRy3CIitdkm//nI7LKifyOx+lk=";
|
|
||||||
};
|
|
||||||
aarch64-darwin =
|
|
||||||
prev.fetchurl {
|
|
||||||
url = "https://github.com/trycog/cog-cli/releases/download/v${version}/cog-darwin-arm64.tar.gz";
|
|
||||||
hash = "sha256-uFkOoy2uFtRvuS7Cqb7ivKdvuDzV9ERNgMq2wiOHI7k=";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
in {
|
|
||||||
cog-cli =
|
|
||||||
prev.stdenvNoCC.mkDerivation {
|
|
||||||
pname = "cog-cli";
|
|
||||||
inherit version;
|
|
||||||
src =
|
|
||||||
srcs.${prev.stdenv.hostPlatform.system}
|
|
||||||
or (throw "Unsupported system for cog-cli: ${prev.stdenv.hostPlatform.system}");
|
|
||||||
|
|
||||||
dontUnpack = true;
|
|
||||||
dontConfigure = true;
|
|
||||||
dontBuild = true;
|
|
||||||
|
|
||||||
installPhase = ''
|
|
||||||
runHook preInstall
|
|
||||||
tar -xzf "$src"
|
|
||||||
install -Dm755 cog "$out/bin/cog"
|
|
||||||
runHook postInstall
|
|
||||||
'';
|
|
||||||
|
|
||||||
meta = with prev.lib; {
|
|
||||||
description = "Memory, code intelligence, and debugging for AI agents";
|
|
||||||
homepage = "https://github.com/trycog/cog-cli";
|
|
||||||
license = licenses.mit;
|
|
||||||
mainProgram = "cog";
|
|
||||||
platforms = builtins.attrNames srcs;
|
|
||||||
sourceProvenance = [sourceTypes.binaryNativeCode];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
})
|
|
||||||
# pi-agent-stuff (mitsuhiko)
|
# pi-agent-stuff (mitsuhiko)
|
||||||
(final: prev: {
|
(import ./_overlays/pi-agent-stuff.nix {inherit inputs;})
|
||||||
pi-agent-stuff =
|
# pi-harness (aliou)
|
||||||
prev.buildNpmPackage {
|
(import ./_overlays/pi-harness.nix {inherit inputs;})
|
||||||
pname = "pi-agent-stuff";
|
|
||||||
version = "1.5.0";
|
|
||||||
src = inputs.pi-agent-stuff;
|
|
||||||
npmDepsHash = "sha256-pyXMNdlie8vAkhz2f3GUGT3CCYuwt+xkWnsijBajXIo=";
|
|
||||||
dontNpmBuild = true;
|
|
||||||
};
|
|
||||||
})
|
|
||||||
# pi-mcp-adapter
|
# pi-mcp-adapter
|
||||||
(final: prev: {
|
(import ./_overlays/pi-mcp-adapter.nix {inherit inputs;})
|
||||||
pi-mcp-adapter =
|
|
||||||
prev.buildNpmPackage {
|
|
||||||
pname = "pi-mcp-adapter";
|
|
||||||
version = "2.2.0";
|
|
||||||
src =
|
|
||||||
prev.fetchFromGitHub {
|
|
||||||
owner = "nicobailon";
|
|
||||||
repo = "pi-mcp-adapter";
|
|
||||||
rev = "v2.2.0";
|
|
||||||
hash = "sha256-E6Kf+OyTN/pF8pKADJO0B1+buAPqNcXnZl9ssZwSP8U=";
|
|
||||||
};
|
|
||||||
npmDepsHash = "sha256-myJ9h/zC/KDddt8NOVvJjjqbnkdEN4ZR+okCR5nu7hM=";
|
|
||||||
dontNpmBuild = true;
|
|
||||||
};
|
|
||||||
})
|
|
||||||
# jj-starship (passes through upstream overlay)
|
# jj-starship (passes through upstream overlay)
|
||||||
inputs.jj-starship.overlays.default
|
(import ./_overlays/jj-starship.nix {inherit inputs;})
|
||||||
# zjstatus
|
# zjstatus
|
||||||
(final: prev: {
|
(import ./_overlays/zjstatus.nix {inherit inputs;})
|
||||||
zjstatus = inputs.zjstatus.packages.${prev.stdenv.hostPlatform.system}.default;
|
|
||||||
})
|
|
||||||
];
|
];
|
||||||
in {
|
in {
|
||||||
den.default.nixos.nixpkgs.overlays = overlays;
|
den.default.nixos.nixpkgs.overlays = overlays;
|
||||||
|
|||||||
Reference in New Issue
Block a user