vault backup: 2026-01-26 17:57:11
This commit is contained in:
Vendored
+413
@@ -0,0 +1,413 @@
|
||||
/*
|
||||
THIS IS A GENERATED/BUNDLED FILE BY ESBUILD
|
||||
if you want to view the source, please visit the github repository of this plugin
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
var __defProp = Object.defineProperty;
|
||||
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
||||
var __getOwnPropNames = Object.getOwnPropertyNames;
|
||||
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
||||
var __export = (target, all) => {
|
||||
for (var name in all)
|
||||
__defProp(target, name, { get: all[name], enumerable: true });
|
||||
};
|
||||
var __copyProps = (to, from, except, desc) => {
|
||||
if (from && typeof from === "object" || typeof from === "function") {
|
||||
for (let key of __getOwnPropNames(from))
|
||||
if (!__hasOwnProp.call(to, key) && key !== except)
|
||||
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
||||
}
|
||||
return to;
|
||||
};
|
||||
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
||||
|
||||
// src/main.ts
|
||||
var main_exports = {};
|
||||
__export(main_exports, {
|
||||
default: () => NoteBuddyPlugin
|
||||
});
|
||||
module.exports = __toCommonJS(main_exports);
|
||||
var import_obsidian4 = require("obsidian");
|
||||
|
||||
// src/chat-view.ts
|
||||
var import_obsidian2 = require("obsidian");
|
||||
|
||||
// src/service.ts
|
||||
var import_obsidian = require("obsidian");
|
||||
|
||||
// src/models.ts
|
||||
var defaultSettings = {
|
||||
serviceUrl: "http://127.0.0.1:4096"
|
||||
};
|
||||
|
||||
// src/service.ts
|
||||
var OpenCodeClient = class {
|
||||
constructor(serviceUrl) {
|
||||
this.session = null;
|
||||
this.serviceUrl = serviceUrl;
|
||||
}
|
||||
async request(options) {
|
||||
return await Promise.race([
|
||||
(0, import_obsidian.requestUrl)(options),
|
||||
new Promise(
|
||||
(_, reject) => setTimeout(() => reject(new Error("Request timeout after 10 seconds")), 1e4)
|
||||
)
|
||||
]);
|
||||
}
|
||||
async healthCheck() {
|
||||
const url = `${this.serviceUrl}/global/health`;
|
||||
try {
|
||||
const response = await this.request({
|
||||
url,
|
||||
method: "GET"
|
||||
});
|
||||
if (response.status === 200 && response.json.healthy === true) {
|
||||
return {
|
||||
status: "connected" /* Connected */,
|
||||
lastTestTime: Date.now()
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
status: "disconnected" /* Disconnected */,
|
||||
lastTestTime: Date.now(),
|
||||
lastError: `Service unhealthy or unreachable`
|
||||
};
|
||||
}
|
||||
} catch (error) {
|
||||
const err = error;
|
||||
return {
|
||||
status: "disconnected" /* Disconnected */,
|
||||
lastTestTime: Date.now(),
|
||||
lastError: err.message || "Unknown error"
|
||||
};
|
||||
}
|
||||
}
|
||||
async createSession() {
|
||||
const url = `${this.serviceUrl}/session`;
|
||||
try {
|
||||
const response = await this.request({
|
||||
url,
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json"
|
||||
},
|
||||
body: JSON.stringify({})
|
||||
});
|
||||
if (response.status === 200) {
|
||||
const data = response.json;
|
||||
return {
|
||||
sessionID: data.sessionID,
|
||||
createTime: data.createTime,
|
||||
title: data.title
|
||||
};
|
||||
} else {
|
||||
throw new Error(`HTTP ${response.status}: ${response.text}`);
|
||||
}
|
||||
} catch (error) {
|
||||
const err = error;
|
||||
throw new Error(`Failed to create session: ${err.message}`);
|
||||
}
|
||||
}
|
||||
async sendMessageInternal(sessionID, message) {
|
||||
const url = `${this.serviceUrl}/session/${sessionID}/message`;
|
||||
try {
|
||||
const response = await this.request({
|
||||
url,
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json"
|
||||
},
|
||||
body: JSON.stringify(message)
|
||||
});
|
||||
if (response.status === 200) {
|
||||
return response.json;
|
||||
} else if (response.status === 404) {
|
||||
throw new Error("Session not found");
|
||||
} else {
|
||||
throw new Error(`HTTP ${response.status}: ${response.text}`);
|
||||
}
|
||||
} catch (error) {
|
||||
const err = error;
|
||||
throw new Error(`Failed to send message: ${err.message}`);
|
||||
}
|
||||
}
|
||||
async sendMessage(input) {
|
||||
if (!this.session) {
|
||||
this.session = await this.createSession();
|
||||
}
|
||||
const message = {
|
||||
parts: [{ text: input, role: "user" }]
|
||||
};
|
||||
const response = await this.sendMessageInternal(this.session.sessionID, message);
|
||||
const assistantParts = response.parts.filter((part) => part.role === "assistant");
|
||||
const assistantText = assistantParts.map((part) => part.text).join("\n");
|
||||
return assistantText;
|
||||
}
|
||||
async sendMessageToSession(sessionID, message) {
|
||||
return await this.sendMessageInternal(sessionID, message);
|
||||
}
|
||||
};
|
||||
|
||||
// src/chat-view.ts
|
||||
var VIEW_TYPE_CHAT = "note-buddy-chat-view";
|
||||
var ChatView = class extends import_obsidian2.ItemView {
|
||||
constructor(leaf, plugin) {
|
||||
super(leaf);
|
||||
this.plugin = plugin;
|
||||
this.messages = [];
|
||||
this.client = new OpenCodeClient(plugin.settings.serviceUrl);
|
||||
}
|
||||
getViewType() {
|
||||
return VIEW_TYPE_CHAT;
|
||||
}
|
||||
getDisplayText() {
|
||||
return "NoteBuddy Chat";
|
||||
}
|
||||
async onOpen() {
|
||||
const container = this.containerEl.children[1];
|
||||
container.empty();
|
||||
const chatContainer = container.createDiv({ cls: "nb-chat-container" });
|
||||
chatContainer.style.cssText = `
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
gap: 1rem;
|
||||
`;
|
||||
const messagesContainer = chatContainer.createDiv({ cls: "nb-messages-container" });
|
||||
this.messagesContainer = messagesContainer;
|
||||
messagesContainer.style.cssText = `
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
padding: 1rem;
|
||||
`;
|
||||
this.renderMessages();
|
||||
const welcomeMessage = messagesContainer.createDiv({ cls: "nb-message nb-message-system" });
|
||||
welcomeMessage.createSpan({ text: "Welcome to NoteBuddy! Chat with your notes here." });
|
||||
welcomeMessage.style.cssText = `
|
||||
padding: 0.75rem;
|
||||
background-color: var(--background-secondary);
|
||||
border-radius: 0.5rem;
|
||||
font-size: 0.9rem;
|
||||
color: var(--text-muted);
|
||||
`;
|
||||
const inputContainer = chatContainer.createDiv({ cls: "nb-input-container" });
|
||||
inputContainer.style.cssText = `
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
padding: 1rem;
|
||||
border-top: 1px solid var(--background-modifier-border);
|
||||
`;
|
||||
const textarea = inputContainer.createEl("textarea", {
|
||||
cls: "nb-chat-input",
|
||||
attr: { placeholder: "Type your message..." }
|
||||
});
|
||||
textarea.style.cssText = `
|
||||
flex: 1;
|
||||
min-height: 60px;
|
||||
max-height: 150px;
|
||||
padding: 0.5rem;
|
||||
border: 1px solid var(--background-modifier-border);
|
||||
border-radius: 0.375rem;
|
||||
background-color: var(--background-primary);
|
||||
color: var(--text-normal);
|
||||
resize: vertical;
|
||||
font-family: inherit;
|
||||
`;
|
||||
const sendButton = inputContainer.createEl("button", {
|
||||
cls: "nb-send-button",
|
||||
text: "Send"
|
||||
});
|
||||
sendButton.style.cssText = `
|
||||
padding: 0.5rem 1rem;
|
||||
background-color: var(--interactive-accent);
|
||||
color: var(--text-on-accent);
|
||||
border: none;
|
||||
border-radius: 0.375rem;
|
||||
cursor: pointer;
|
||||
font-weight: 500;
|
||||
`;
|
||||
const sendMessage = async () => {
|
||||
const message = textarea.value.trim();
|
||||
if (!message) return;
|
||||
this.messages.push({ text: message, role: "user" });
|
||||
this.renderMessages();
|
||||
textarea.value = "";
|
||||
try {
|
||||
if (!this.plugin.sessionState) {
|
||||
this.plugin.sessionState = await this.client.createSession();
|
||||
}
|
||||
const sendData = {
|
||||
parts: [{ text: message, role: "user" }],
|
||||
model: {
|
||||
providerID: "anthropic",
|
||||
modelID: "claude-3-5-sonnet-20241022"
|
||||
}
|
||||
};
|
||||
let response;
|
||||
try {
|
||||
response = await this.client.sendMessageToSession(this.plugin.sessionState.sessionID, sendData);
|
||||
} catch (sendError) {
|
||||
const err = sendError;
|
||||
if (err.message.includes("Session not found") || err.message.includes("404")) {
|
||||
this.plugin.sessionState = await this.client.createSession();
|
||||
response = await this.client.sendMessageToSession(this.plugin.sessionState.sessionID, sendData);
|
||||
} else {
|
||||
throw sendError;
|
||||
}
|
||||
}
|
||||
this.messages.push(...response.parts);
|
||||
this.renderMessages();
|
||||
this.messagesContainer.scrollTop = this.messagesContainer.scrollHeight;
|
||||
} catch (error) {
|
||||
console.error("[NoteBuddy] Send failed:", error);
|
||||
new import_obsidian2.Notice(`Failed to send message: ${error.message}`);
|
||||
}
|
||||
};
|
||||
sendButton.onclick = sendMessage;
|
||||
textarea.addEventListener("keydown", (e) => {
|
||||
if (e.key === "Enter" && !e.shiftKey) {
|
||||
e.preventDefault();
|
||||
sendMessage();
|
||||
}
|
||||
});
|
||||
}
|
||||
async onClose() {
|
||||
console.log("[NoteBuddy] Chat view closed");
|
||||
}
|
||||
renderMessages() {
|
||||
this.messagesContainer.empty();
|
||||
if (this.messages.length === 0) {
|
||||
const welcomeMessage = this.messagesContainer.createDiv({ cls: "nb-message nb-message-system" });
|
||||
welcomeMessage.createSpan({ text: "Welcome to NoteBuddy! Chat with your notes here." });
|
||||
welcomeMessage.style.cssText = `
|
||||
padding: 0.75rem;
|
||||
background-color: var(--background-secondary);
|
||||
border-radius: 0.5rem;
|
||||
font-size: 0.9rem;
|
||||
color: var(--text-muted);
|
||||
`;
|
||||
return;
|
||||
}
|
||||
for (const message of this.messages) {
|
||||
const messageEl = this.messagesContainer.createDiv({
|
||||
cls: `nb-message nb-message-${message.role}`
|
||||
});
|
||||
messageEl.createSpan({ text: message.text });
|
||||
messageEl.style.cssText = `
|
||||
padding: 0.75rem;
|
||||
background-color: ${message.role === "user" ? "var(--background-modifier-accent)" : "var(--background-secondary)"};
|
||||
border-radius: 0.5rem;
|
||||
font-size: 0.9rem;
|
||||
align-self: ${message.role === "user" ? "flex-end" : "flex-start"};
|
||||
max-width: 70%;
|
||||
`;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// src/settings.ts
|
||||
var import_obsidian3 = require("obsidian");
|
||||
var NoteBuddySettingTab = class extends import_obsidian3.PluginSettingTab {
|
||||
constructor(app, plugin) {
|
||||
super(app, plugin);
|
||||
this.plugin = plugin;
|
||||
}
|
||||
display() {
|
||||
const { containerEl } = this;
|
||||
containerEl.empty();
|
||||
containerEl.createEl("h2", { text: "NoteBuddy Settings" });
|
||||
new import_obsidian3.Setting(containerEl).setName("Service URL").setDesc("The URL of the OpenCode service (must include protocol, hostname, and port)").addText(
|
||||
(text) => text.setPlaceholder("http://127.0.0.1:4096").setValue(this.plugin.settings.serviceUrl).onChange(async (value) => {
|
||||
if (!value.startsWith("http://") && !value.startsWith("https://")) {
|
||||
new import_obsidian3.Notice("Service URL must start with http:// or https://");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const url = new URL(value);
|
||||
if (!url.hostname || url.hostname === "") {
|
||||
new import_obsidian3.Notice("Service URL must include a valid hostname");
|
||||
return;
|
||||
}
|
||||
if (!url.port || url.port === "") {
|
||||
new import_obsidian3.Notice("Service URL must include a port number");
|
||||
return;
|
||||
}
|
||||
} catch (e) {
|
||||
new import_obsidian3.Notice("Service URL must be a valid URL format");
|
||||
return;
|
||||
}
|
||||
this.plugin.settings.serviceUrl = value;
|
||||
await this.plugin.saveSettings();
|
||||
})
|
||||
);
|
||||
new import_obsidian3.Setting(containerEl).setName("Test Connection").setDesc("Test the connection to the service").addButton(
|
||||
(button) => button.setButtonText("Test").setCta().onClick(async () => {
|
||||
const client = new OpenCodeClient(this.plugin.settings.serviceUrl);
|
||||
const result = await client.healthCheck();
|
||||
if (result.status === "connected") {
|
||||
new import_obsidian3.Notice("Connection successful!");
|
||||
} else {
|
||||
new import_obsidian3.Notice(`Connection failed: ${result.lastError}`);
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
// src/main.ts
|
||||
var DEFAULT_SETTINGS = defaultSettings;
|
||||
var NoteBuddyPlugin = class extends import_obsidian4.Plugin {
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
this.ribbonIconEl = null;
|
||||
}
|
||||
async onload() {
|
||||
console.log("Loading NoteBuddy plugin...");
|
||||
await this.loadSettings();
|
||||
this.registerView(
|
||||
VIEW_TYPE_CHAT,
|
||||
(leaf) => new ChatView(leaf, this)
|
||||
);
|
||||
this.addRibbonIcon("bot", "NoteBuddy", () => {
|
||||
this.activateView();
|
||||
});
|
||||
this.addCommand({
|
||||
id: "open-chat-view",
|
||||
name: "Open NoteBuddy Chat",
|
||||
callback: () => {
|
||||
this.activateView();
|
||||
}
|
||||
});
|
||||
this.app.workspace.onLayoutReady(() => {
|
||||
this.activateView();
|
||||
});
|
||||
this.addSettingTab(new NoteBuddySettingTab(this.app, this));
|
||||
}
|
||||
onunload() {
|
||||
console.log("Unloading NoteBuddy plugin...");
|
||||
}
|
||||
async activateView() {
|
||||
const { workspace } = this.app;
|
||||
let leaf = null;
|
||||
const leaves = workspace.getLeavesOfType(VIEW_TYPE_CHAT);
|
||||
if (leaves.length > 0) {
|
||||
leaf = leaves[0];
|
||||
} else {
|
||||
leaf = workspace.getRightLeaf(false);
|
||||
}
|
||||
if (leaf) {
|
||||
await workspace.setActiveLeaf(leaf);
|
||||
await leaf.setViewState({ type: VIEW_TYPE_CHAT, active: true });
|
||||
}
|
||||
}
|
||||
async loadSettings() {
|
||||
this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData());
|
||||
}
|
||||
async saveSettings() {
|
||||
await this.saveData(this.settings);
|
||||
}
|
||||
};
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
.note-buddy-chat {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background-color: var(--background-secondary);
|
||||
}
|
||||
|
||||
.note-buddy-chat h2 {
|
||||
margin: 10px;
|
||||
font-size: var(--font-ui-medium);
|
||||
color: var(--text-normal);
|
||||
font-weight: var(--font-semibold);
|
||||
}
|
||||
|
||||
.message-container {
|
||||
background-color: var(--background-primary-alt);
|
||||
border-radius: var(--radius-md);
|
||||
margin: 0 10px 10px 10px;
|
||||
}
|
||||
|
||||
.input-bar textarea {
|
||||
background-color: var(--background-primary);
|
||||
color: var(--text-normal);
|
||||
border: 1px solid var(--background-modifier-border);
|
||||
border-radius: var(--radius-sm);
|
||||
font-family: var(--font-interface);
|
||||
font-size: var(--font-ui-sm);
|
||||
}
|
||||
|
||||
.input-bar textarea:focus {
|
||||
outline: none;
|
||||
border-color: var(--interactive-accent);
|
||||
}
|
||||
|
||||
.input-bar button {
|
||||
background-color: var(--interactive-accent);
|
||||
color: var(--text-on-accent);
|
||||
border: none;
|
||||
border-radius: var(--radius-sm);
|
||||
font-family: var(--font-interface);
|
||||
font-weight: var(--font-semibold);
|
||||
font-size: var(--font-ui-sm);
|
||||
transition: background-color 0.2s;
|
||||
}
|
||||
|
||||
.input-bar button:hover {
|
||||
background-color: var(--interactive-accent-hover);
|
||||
}
|
||||
Vendored
+48
@@ -0,0 +1,48 @@
|
||||
.note-buddy-chat {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background-color: var(--background-secondary);
|
||||
}
|
||||
|
||||
.note-buddy-chat h2 {
|
||||
margin: 10px;
|
||||
font-size: var(--font-ui-medium);
|
||||
color: var(--text-normal);
|
||||
font-weight: var(--font-semibold);
|
||||
}
|
||||
|
||||
.message-container {
|
||||
background-color: var(--background-primary-alt);
|
||||
border-radius: var(--radius-md);
|
||||
margin: 0 10px 10px 10px;
|
||||
}
|
||||
|
||||
.input-bar textarea {
|
||||
background-color: var(--background-primary);
|
||||
color: var(--text-normal);
|
||||
border: 1px solid var(--background-modifier-border);
|
||||
border-radius: var(--radius-sm);
|
||||
font-family: var(--font-interface);
|
||||
font-size: var(--font-ui-sm);
|
||||
}
|
||||
|
||||
.input-bar textarea:focus {
|
||||
outline: none;
|
||||
border-color: var(--interactive-accent);
|
||||
}
|
||||
|
||||
.input-bar button {
|
||||
background-color: var(--interactive-accent);
|
||||
color: var(--text-on-accent);
|
||||
border: none;
|
||||
border-radius: var(--radius-sm);
|
||||
font-family: var(--font-interface);
|
||||
font-weight: var(--font-semibold);
|
||||
font-size: var(--font-ui-sm);
|
||||
transition: background-color 0.2s;
|
||||
}
|
||||
|
||||
.input-bar button:hover {
|
||||
background-color: var(--interactive-accent-hover);
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
name: CI
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Bun
|
||||
uses: oven-sh/setup-bun@v2
|
||||
with:
|
||||
bun-version: latest
|
||||
|
||||
- name: Cache Bun dependencies
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ~/.bun/install/cache
|
||||
key: ${{ runner.os }}-bun-${{ hashFiles('**/bun.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-bun-
|
||||
|
||||
- name: Cache OpenCode binary
|
||||
id: cache-opencode
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ~/.bun/bin/opencode
|
||||
key: ${{ runner.os }}-opencode-${{ hashFiles('.github/workflows/ci.yml') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-opencode-
|
||||
|
||||
- name: Install dependencies
|
||||
run: bun install
|
||||
|
||||
- name: Install OpenCode CLI
|
||||
if: steps.cache-opencode.outputs.cache-hit != 'true'
|
||||
run: bun install -g opencode-ai
|
||||
|
||||
- name: Verify OpenCode installation
|
||||
run: opencode --version
|
||||
|
||||
- name: Type check
|
||||
run: bun run tsc -noEmit -skipLibCheck
|
||||
|
||||
- name: Build plugin
|
||||
run: bun run build
|
||||
|
||||
- name: Run tests
|
||||
run: bun test
|
||||
@@ -1,3 +0,0 @@
|
||||
node_modules
|
||||
data.json
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
---
|
||||
description: Implement an approved OpenSpec change and keep tasks in sync.
|
||||
---
|
||||
The user has requested to implement the following change proposal. Find the change proposal and follow the instructions below. If you're not sure or if ambiguous, ask for clarification from the user.
|
||||
<UserRequest>
|
||||
$ARGUMENTS
|
||||
</UserRequest>
|
||||
<!-- OPENSPEC:START -->
|
||||
**Guardrails**
|
||||
- Favor straightforward, minimal implementations first and add complexity only when it is requested or clearly required.
|
||||
- Keep changes tightly scoped to the requested outcome.
|
||||
- Refer to `openspec/AGENTS.md` (located inside the `openspec/` directory—run `ls openspec` or `openspec update` if you don't see it) if you need additional OpenSpec conventions or clarifications.
|
||||
|
||||
**Steps**
|
||||
Track these steps as TODOs and complete them one by one.
|
||||
1. Read `changes/<id>/proposal.md`, `design.md` (if present), and `tasks.md` to confirm scope and acceptance criteria.
|
||||
2. Work through tasks sequentially, keeping edits minimal and focused on the requested change.
|
||||
3. Confirm completion before updating statuses—make sure every item in `tasks.md` is finished.
|
||||
4. Update the checklist after all work is done so each task is marked `- [x]` and reflects reality.
|
||||
5. Reference `openspec list` or `openspec show <item>` when additional context is required.
|
||||
|
||||
**Reference**
|
||||
- Use `openspec show <id> --json --deltas-only` if you need additional context from the proposal while implementing.
|
||||
<!-- OPENSPEC:END -->
|
||||
@@ -1,27 +0,0 @@
|
||||
---
|
||||
description: Archive a deployed OpenSpec change and update specs.
|
||||
---
|
||||
<ChangeId>
|
||||
$ARGUMENTS
|
||||
</ChangeId>
|
||||
<!-- OPENSPEC:START -->
|
||||
**Guardrails**
|
||||
- Favor straightforward, minimal implementations first and add complexity only when it is requested or clearly required.
|
||||
- Keep changes tightly scoped to the requested outcome.
|
||||
- Refer to `openspec/AGENTS.md` (located inside the `openspec/` directory—run `ls openspec` or `openspec update` if you don't see it) if you need additional OpenSpec conventions or clarifications.
|
||||
|
||||
**Steps**
|
||||
1. Determine the change ID to archive:
|
||||
- If this prompt already includes a specific change ID (for example inside a `<ChangeId>` block populated by slash-command arguments), use that value after trimming whitespace.
|
||||
- If the conversation references a change loosely (for example by title or summary), run `openspec list` to surface likely IDs, share the relevant candidates, and confirm which one the user intends.
|
||||
- Otherwise, review the conversation, run `openspec list`, and ask the user which change to archive; wait for a confirmed change ID before proceeding.
|
||||
- If you still cannot identify a single change ID, stop and tell the user you cannot archive anything yet.
|
||||
2. Validate the change ID by running `openspec list` (or `openspec show <id>`) and stop if the change is missing, already archived, or otherwise not ready to archive.
|
||||
3. Run `openspec archive <id> --yes` so the CLI moves the change and applies spec updates without prompts (use `--skip-specs` only for tooling-only work).
|
||||
4. Review the command output to confirm the target specs were updated and the change landed in `changes/archive/`.
|
||||
5. Validate with `openspec validate --strict` and inspect with `openspec show <id>` if anything looks off.
|
||||
|
||||
**Reference**
|
||||
- Use `openspec list` to confirm change IDs before archiving.
|
||||
- Inspect refreshed specs with `openspec list --specs` and address any validation issues before handing off.
|
||||
<!-- OPENSPEC:END -->
|
||||
@@ -1,29 +0,0 @@
|
||||
---
|
||||
description: Scaffold a new OpenSpec change and validate strictly.
|
||||
---
|
||||
The user has requested the following change proposal. Use the openspec instructions to create their change proposal.
|
||||
<UserRequest>
|
||||
$ARGUMENTS
|
||||
</UserRequest>
|
||||
<!-- OPENSPEC:START -->
|
||||
**Guardrails**
|
||||
- Favor straightforward, minimal implementations first and add complexity only when it is requested or clearly required.
|
||||
- Keep changes tightly scoped to the requested outcome.
|
||||
- Refer to `openspec/AGENTS.md` (located inside the `openspec/` directory—run `ls openspec` or `openspec update` if you don't see it) if you need additional OpenSpec conventions or clarifications.
|
||||
- Identify any vague or ambiguous details and ask the necessary follow-up questions before editing files.
|
||||
- Do not write any code during the proposal stage. Only create design documents (proposal.md, tasks.md, design.md, and spec deltas). Implementation happens in the apply stage after approval.
|
||||
|
||||
**Steps**
|
||||
1. Review `openspec/project.md`, run `openspec list` and `openspec list --specs`, and inspect related code or docs (e.g., via `rg`/`ls`) to ground the proposal in current behaviour; note any gaps that require clarification.
|
||||
2. Choose a unique verb-led `change-id` and scaffold `proposal.md`, `tasks.md`, and `design.md` (when needed) under `openspec/changes/<id>/`.
|
||||
3. Map the change into concrete capabilities or requirements, breaking multi-scope efforts into distinct spec deltas with clear relationships and sequencing.
|
||||
4. Capture architectural reasoning in `design.md` when the solution spans multiple systems, introduces new patterns, or demands trade-off discussion before committing to specs.
|
||||
5. Draft spec deltas in `changes/<id>/specs/<capability>/spec.md` (one folder per capability) using `## ADDED|MODIFIED|REMOVED Requirements` with at least one `#### Scenario:` per requirement and cross-reference related capabilities when relevant.
|
||||
6. Draft `tasks.md` as an ordered list of small, verifiable work items that deliver user-visible progress, include validation (tests, tooling), and highlight dependencies or parallelizable work.
|
||||
7. Validate with `openspec validate <id> --strict` and resolve every issue before sharing the proposal.
|
||||
|
||||
**Reference**
|
||||
- Use `openspec show <id> --json --deltas-only` or `openspec show <spec> --type spec` to inspect details when validation fails.
|
||||
- Search existing requirements with `rg -n "Requirement:|Scenario:" openspec/specs` before writing new ones.
|
||||
- Explore the codebase with `rg <keyword>`, `ls`, or direct file reads so proposals align with current implementation realities.
|
||||
<!-- OPENSPEC:END -->
|
||||
-5
@@ -1,5 +0,0 @@
|
||||
Copyright (C) 2020-2025 by Dynalist Inc.
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
-100
File diff suppressed because one or more lines are too long
@@ -1,11 +0,0 @@
|
||||
{
|
||||
"id": "opencode-obsidian",
|
||||
"name": "OpenCode Obsidian",
|
||||
"version": "0.13.1",
|
||||
"minAppVersion": "1.0.0",
|
||||
"description": "OpenCode integration for Obsidian - AI-powered chat interface",
|
||||
"author": "OpenCode Obsidian Team",
|
||||
"authorUrl": "",
|
||||
"fundingUrl": "",
|
||||
"isDesktopOnly": false
|
||||
}
|
||||
-475
@@ -1,475 +0,0 @@
|
||||
/* Search highlight */
|
||||
.search-highlight {
|
||||
background-color: var(--text-highlight-bg);
|
||||
color: var(--text-highlight);
|
||||
padding: 0 2px;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
/* Server Status Styles */
|
||||
.opencode-server-status {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
padding: 8px 0;
|
||||
}
|
||||
|
||||
.opencode-server-status-running {
|
||||
color: var(--color-green);
|
||||
}
|
||||
|
||||
.opencode-server-status-stopped {
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
.opencode-server-status-starting {
|
||||
color: var(--color-blue);
|
||||
}
|
||||
|
||||
.opencode-server-status-error {
|
||||
color: var(--color-red);
|
||||
}
|
||||
|
||||
.opencode-server-status-error-message {
|
||||
color: var(--text-muted);
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.opencode-server-control-buttons {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.opencode-server-metrics {
|
||||
padding: 8px 0;
|
||||
}
|
||||
|
||||
.opencode-server-metrics-text {
|
||||
font-size: 12px;
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
/* Server Start Modal Styles */
|
||||
.opencode-server-start-buttons {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
justify-content: flex-end;
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
/* Core view layout */
|
||||
.opencode-obsidian-view {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.opencode-obsidian-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 8px 10px;
|
||||
border-bottom: 1px solid var(--background-modifier-border);
|
||||
background-color: var(--background-secondary);
|
||||
}
|
||||
|
||||
.opencode-obsidian-status {
|
||||
font-size: 12px;
|
||||
color: var(--text-muted);
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.opencode-obsidian-status.connected {
|
||||
color: var(--text-success);
|
||||
}
|
||||
|
||||
.opencode-obsidian-status.reconnecting,
|
||||
.opencode-obsidian-status.connecting {
|
||||
color: var(--text-warning);
|
||||
}
|
||||
|
||||
.opencode-obsidian-status.disconnected {
|
||||
color: var(--text-error);
|
||||
}
|
||||
|
||||
.opencode-obsidian-controls {
|
||||
margin-left: auto;
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.opencode-obsidian-view-toggle {
|
||||
align-self: flex-start;
|
||||
margin: 8px 10px 0;
|
||||
}
|
||||
|
||||
.opencode-obsidian-search-panel {
|
||||
padding: 6px 10px;
|
||||
border-bottom: 1px solid var(--background-modifier-border);
|
||||
}
|
||||
|
||||
/* Conversation selector */
|
||||
.opencode-obsidian-conversation-selector {
|
||||
padding: 6px 10px;
|
||||
border-bottom: 1px solid var(--background-modifier-border);
|
||||
}
|
||||
|
||||
.opencode-obsidian-tabs-container {
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
overflow-x: auto;
|
||||
padding-bottom: 4px;
|
||||
}
|
||||
|
||||
.opencode-obsidian-tab {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
padding: 4px 8px;
|
||||
border-radius: 6px;
|
||||
background-color: var(--background-secondary);
|
||||
border: 1px solid var(--background-modifier-border);
|
||||
cursor: pointer;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.opencode-obsidian-tab.active {
|
||||
background-color: var(--interactive-accent);
|
||||
border-color: var(--interactive-accent);
|
||||
color: var(--text-on-accent);
|
||||
}
|
||||
|
||||
.opencode-obsidian-tab-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2px;
|
||||
}
|
||||
|
||||
.opencode-obsidian-tab-metadata {
|
||||
font-size: 11px;
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
.opencode-obsidian-tab-close {
|
||||
margin-left: 6px;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.opencode-obsidian-tab:hover .opencode-obsidian-tab-close {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.opencode-obsidian-tab-new,
|
||||
.opencode-obsidian-tab-sync {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.opencode-obsidian-tab-disabled {
|
||||
opacity: 0.6;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.opencode-obsidian-no-conversations {
|
||||
color: var(--text-muted);
|
||||
padding: 8px 0;
|
||||
}
|
||||
|
||||
/* Message list */
|
||||
.opencode-obsidian-messages {
|
||||
flex: 1;
|
||||
overflow: auto;
|
||||
padding: 10px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.opencode-obsidian-message {
|
||||
padding: 8px 10px;
|
||||
border-radius: 8px;
|
||||
border: 1px solid var(--background-modifier-border);
|
||||
background-color: var(--background-secondary);
|
||||
}
|
||||
|
||||
.opencode-obsidian-message-user {
|
||||
align-self: flex-end;
|
||||
background-color: var(--background-modifier-hover);
|
||||
}
|
||||
|
||||
.opencode-obsidian-message-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
font-size: 11px;
|
||||
color: var(--text-muted);
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.opencode-obsidian-message-content {
|
||||
font-size: 13px;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.opencode-obsidian-message-images img {
|
||||
border-radius: 6px;
|
||||
margin-top: 6px;
|
||||
}
|
||||
|
||||
.opencode-obsidian-message-actions {
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
margin-top: 6px;
|
||||
}
|
||||
|
||||
.opencode-obsidian-message-action {
|
||||
font-size: 11px;
|
||||
color: var(--text-muted);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.opencode-obsidian-message-action:hover {
|
||||
color: var(--text-normal);
|
||||
}
|
||||
|
||||
.opencode-obsidian-empty-messages {
|
||||
color: var(--text-muted);
|
||||
text-align: center;
|
||||
padding: 20px 0;
|
||||
}
|
||||
|
||||
.opencode-obsidian-reverted-indicator {
|
||||
padding: 8px 10px;
|
||||
border-radius: 6px;
|
||||
background-color: var(--background-secondary);
|
||||
border: 1px dashed var(--background-modifier-border);
|
||||
font-size: 12px;
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
.opencode-obsidian-unrevert-button {
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
/* Input area */
|
||||
.opencode-obsidian-input {
|
||||
border-top: 1px solid var(--background-modifier-border);
|
||||
padding: 8px 10px;
|
||||
background-color: var(--background-secondary);
|
||||
}
|
||||
|
||||
.opencode-obsidian-input-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.opencode-obsidian-input-toolbar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.opencode-obsidian-agent-select {
|
||||
max-width: 200px;
|
||||
}
|
||||
|
||||
.opencode-obsidian-input-textarea {
|
||||
width: 100%;
|
||||
min-height: 60px;
|
||||
resize: none;
|
||||
}
|
||||
|
||||
.opencode-obsidian-input-status {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
font-size: 11px;
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
.opencode-obsidian-streaming {
|
||||
color: var(--text-accent);
|
||||
}
|
||||
|
||||
.opencode-obsidian-input-buttons {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
/* Command suggestions */
|
||||
.opencode-obsidian-command-suggestions {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.opencode-obsidian-command-suggestions-list {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: calc(100% + 6px);
|
||||
max-height: 200px;
|
||||
overflow: auto;
|
||||
background-color: var(--background-primary);
|
||||
border: 1px solid var(--background-modifier-border);
|
||||
border-radius: 6px;
|
||||
box-shadow: 0 6px 16px rgba(0, 0, 0, 0.12);
|
||||
display: none;
|
||||
z-index: 20;
|
||||
}
|
||||
|
||||
.opencode-obsidian-command-suggestions.is-visible .opencode-obsidian-command-suggestions-list {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.opencode-obsidian-command-suggestion {
|
||||
padding: 6px 8px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.opencode-obsidian-command-suggestion.is-selected {
|
||||
background-color: var(--background-modifier-hover);
|
||||
}
|
||||
|
||||
.opencode-obsidian-command-suggestion-description {
|
||||
display: block;
|
||||
font-size: 11px;
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
.opencode-obsidian-command-suggestion-empty {
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
/* Loading and spinners */
|
||||
.opencode-obsidian-loading-overlay {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100%;
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
.opencode-obsidian-loading-message {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.opencode-obsidian-spinner {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
border: 2px solid var(--background-modifier-border);
|
||||
border-top-color: var(--interactive-accent);
|
||||
border-radius: 50%;
|
||||
animation: opencode-obsidian-spin 0.8s linear infinite;
|
||||
}
|
||||
|
||||
.opencode-obsidian-spinner-large {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
border-width: 3px;
|
||||
}
|
||||
|
||||
@keyframes opencode-obsidian-spin {
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
/* Context menu */
|
||||
.opencode-obsidian-context-menu {
|
||||
background-color: var(--background-primary);
|
||||
border: 1px solid var(--background-modifier-border);
|
||||
border-radius: 6px;
|
||||
padding: 4px;
|
||||
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.opencode-obsidian-context-menu-item {
|
||||
padding: 6px 8px;
|
||||
cursor: pointer;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.opencode-obsidian-context-menu-item:hover {
|
||||
background-color: var(--background-modifier-hover);
|
||||
}
|
||||
|
||||
.opencode-obsidian-context-menu-item-danger {
|
||||
color: var(--text-error);
|
||||
}
|
||||
|
||||
/* Todo list */
|
||||
.opencode-obsidian-todo-container {
|
||||
padding: 10px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.opencode-obsidian-todo-header {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.opencode-obsidian-todo-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.opencode-obsidian-todo-item {
|
||||
padding: 8px 10px;
|
||||
border: 1px solid var(--background-modifier-border);
|
||||
border-radius: 8px;
|
||||
background-color: var(--background-secondary);
|
||||
}
|
||||
|
||||
.opencode-obsidian-todo-title {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.opencode-obsidian-todo-meta {
|
||||
font-size: 11px;
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
.opencode-obsidian-todo-actions {
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
margin-top: 6px;
|
||||
}
|
||||
|
||||
.opencode-obsidian-todo-empty {
|
||||
color: var(--text-muted);
|
||||
padding: 8px 0;
|
||||
}
|
||||
|
||||
/* Modals */
|
||||
.opencode-obsidian-drop-zone {
|
||||
border: 1px dashed var(--background-modifier-border);
|
||||
padding: 12px;
|
||||
text-align: center;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.opencode-obsidian-drop-zone-hover {
|
||||
background-color: var(--background-modifier-hover);
|
||||
}
|
||||
|
||||
.opencode-obsidian-permission-buttons {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 8px;
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
.opencode-obsidian-code-preview {
|
||||
font-family: var(--font-monospace);
|
||||
font-size: 12px;
|
||||
background-color: var(--background-secondary);
|
||||
border: 1px solid var(--background-modifier-border);
|
||||
padding: 8px;
|
||||
border-radius: 6px;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
+13
-13
File diff suppressed because one or more lines are too long
+1
-1
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"id": "templater-obsidian",
|
||||
"name": "Templater",
|
||||
"version": "2.16.4",
|
||||
"version": "2.17.0",
|
||||
"description": "Create and use templates",
|
||||
"minAppVersion": "1.5.0",
|
||||
"author": "SilentVoid",
|
||||
|
||||
Reference in New Issue
Block a user