diff --git a/.clojure-skills/config.edn b/.clojure-skills/config.edn new file mode 100644 index 0000000..8267461 --- /dev/null +++ b/.clojure-skills/config.edn @@ -0,0 +1,12 @@ +{:database {:path "~/dev/clojure-skills/clojure-skills.db", + :auto-migrate true}, + :project {:root nil, + :skills-dir "skills", + :prompts-dir "prompts", + :build-dir "_build"}, + + :permissions + {:prompt true} + + :search {:max-results 50, :context-lines 3}, + :output {:format :table, :color true}} diff --git a/clojure-skills.db b/clojure-skills.db index 49d3ef6..adce4bb 100644 Binary files a/clojure-skills.db and b/clojure-skills.db differ diff --git a/deps.edn b/deps.edn index d2202eb..668f3af 100644 --- a/deps.edn +++ b/deps.edn @@ -10,7 +10,7 @@ org.clj-commons/pretty {:mvn/version "3.6.7"} dev.weavejester/hashp {:mvn/version "0.5.1"} io.github.paintparty/bling {:mvn/version "0.8.8"} - dev.glossa/metazoa {:mvn/version "0.2.298"} + mvxcvi/puget {:mvn/version "1.3.4"} ;; SQL database dependencies @@ -26,6 +26,7 @@ :aliases {:dev {:extra-deps {vvvvalvalval/scope-capture {:mvn/version "0.3.3"} + dev.glossa/metazoa {:mvn/version "0.2.298"} org.clojure/tools.nrepl {:mvn/version "0.2.11"} nubank/matcher-combinators {:mvn/version "3.9.1"} com.stuartsierra/component.repl {:mvn/version "0.2.0"} diff --git a/prompt_configs/clojure_build.yaml b/prompt_configs/clojure_build.yaml index 6af3ba4..f61961f 100644 --- a/prompt_configs/clojure_build.yaml +++ b/prompt_configs/clojure_build.yaml @@ -15,6 +15,6 @@ fragments: - skills/libraries/metadata/metazoa.md - skills/tooling/hashp.md - skills/libraries/logging/pretty.md - - skills/tooling/clojure_lsp_api + - skills/tooling/clojure_lsp_api.md - skills/tooling/clj_paren_repair.md references: [] diff --git a/prompt_configs/clojure_skill_builder.yaml b/prompt_configs/clojure_skill_builder.yaml index 42cb615..9abd826 100644 --- a/prompt_configs/clojure_skill_builder.yaml +++ b/prompt_configs/clojure_skill_builder.yaml @@ -13,9 +13,7 @@ fragments: - skills/libraries/metadata/metazoa.md - skills/tooling/hashp.md - skills/libraries/logging/pretty.md - - skills/tooling/clojure_lsp_api + - skills/tooling/clojure_lsp_api.md - skills/tooling/clj_paren_repair.md - skills/tooling/clojure_mcp_light.md - - skills/libraries/data_formats/pretty.md - - references: [] +references: [] diff --git a/resources/migrations/001-initial-schema.edn b/resources/migrations/001-initial-schema.edn index a133704..9f5a7ff 100644 --- a/resources/migrations/001-initial-schema.edn +++ b/resources/migrations/001-initial-schema.edn @@ -3,6 +3,7 @@ :up ["PRAGMA foreign_keys = ON" + ;; Skills table "CREATE TABLE IF NOT EXISTS skills ( id INTEGER PRIMARY KEY AUTOINCREMENT, path TEXT NOT NULL UNIQUE, @@ -22,6 +23,7 @@ "CREATE INDEX IF NOT EXISTS idx_skills_name ON skills(name)" "CREATE INDEX IF NOT EXISTS idx_skills_hash ON skills(file_hash)" + ;; Prompts table (includes fragment_refs column from migration 006) "CREATE TABLE IF NOT EXISTS prompts ( id INTEGER PRIMARY KEY AUTOINCREMENT, path TEXT NOT NULL UNIQUE, @@ -33,25 +35,65 @@ file_hash TEXT NOT NULL, size_bytes INTEGER NOT NULL, token_count INTEGER, + fragment_refs TEXT, created_at TEXT NOT NULL DEFAULT (datetime('now')), updated_at TEXT NOT NULL DEFAULT (datetime('now')) )" - "CREATE INDEX IF NOT EXISTS idx_prompts_name ON prompts(name)" "CREATE INDEX IF NOT EXISTS idx_prompts_hash ON prompts(file_hash)" + ;; Unique index on name (from migration 008) + "CREATE UNIQUE INDEX IF NOT EXISTS idx_prompts_name ON prompts(name)" - "CREATE TABLE IF NOT EXISTS prompt_skills ( - prompt_id INTEGER NOT NULL, + ;; Prompt fragments table (from migration 006) + "CREATE TABLE IF NOT EXISTS prompt_fragments ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + name TEXT NOT NULL UNIQUE, + title TEXT, + description TEXT, + created_at TEXT NOT NULL DEFAULT (datetime('now')), + updated_at TEXT NOT NULL DEFAULT (datetime('now')) + )" + + "CREATE INDEX IF NOT EXISTS idx_prompt_fragments_name ON prompt_fragments(name)" + + ;; Prompt fragment skills join table (from migration 006) + "CREATE TABLE IF NOT EXISTS prompt_fragment_skills ( + fragment_id INTEGER NOT NULL, skill_id INTEGER NOT NULL, position INTEGER NOT NULL, - PRIMARY KEY (prompt_id, skill_id), - FOREIGN KEY (prompt_id) REFERENCES prompts(id) ON DELETE CASCADE, + created_at TEXT NOT NULL DEFAULT (datetime('now')), + PRIMARY KEY (fragment_id, skill_id), + FOREIGN KEY (fragment_id) REFERENCES prompt_fragments(id) ON DELETE CASCADE, FOREIGN KEY (skill_id) REFERENCES skills(id) ON DELETE CASCADE )" - "CREATE INDEX IF NOT EXISTS idx_prompt_skills_prompt ON prompt_skills(prompt_id)" - "CREATE INDEX IF NOT EXISTS idx_prompt_skills_skill ON prompt_skills(skill_id)" + "CREATE INDEX IF NOT EXISTS idx_prompt_fragment_skills_fragment ON prompt_fragment_skills(fragment_id)" + "CREATE INDEX IF NOT EXISTS idx_prompt_fragment_skills_skill ON prompt_fragment_skills(skill_id)" + ;; Prompt references table (from migration 006, with FIXED nullable target_prompt_id) + "CREATE TABLE IF NOT EXISTS prompt_references ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + source_prompt_id INTEGER NOT NULL, + target_prompt_id INTEGER, + target_fragment_id INTEGER, + reference_type TEXT NOT NULL CHECK(reference_type IN ('prompt', 'fragment')), + position INTEGER NOT NULL, + created_at TEXT NOT NULL DEFAULT (datetime('now')), + FOREIGN KEY (source_prompt_id) REFERENCES prompts(id) ON DELETE CASCADE, + FOREIGN KEY (target_prompt_id) REFERENCES prompts(id) ON DELETE CASCADE, + FOREIGN KEY (target_fragment_id) REFERENCES prompt_fragments(id) ON DELETE CASCADE, + CONSTRAINT chk_target_reference CHECK ( + (target_prompt_id IS NOT NULL AND target_fragment_id IS NULL) OR + (target_prompt_id IS NULL AND target_fragment_id IS NOT NULL) + ) + )" + + "CREATE INDEX IF NOT EXISTS idx_prompt_references_source ON prompt_references(source_prompt_id)" + "CREATE INDEX IF NOT EXISTS idx_prompt_references_target_prompt ON prompt_references(target_prompt_id)" + "CREATE INDEX IF NOT EXISTS idx_prompt_references_target_fragment ON prompt_references(target_fragment_id)" + "CREATE INDEX IF NOT EXISTS idx_prompt_references_type ON prompt_references(reference_type)" + + ;; Skills FTS table "CREATE VIRTUAL TABLE IF NOT EXISTS skills_fts USING fts5( path, category, @@ -63,6 +105,7 @@ content_rowid='id' )" + ;; Skills FTS triggers "CREATE TRIGGER IF NOT EXISTS skills_ai AFTER INSERT ON skills BEGIN INSERT INTO skills_fts(rowid, path, category, name, title, description, content) VALUES (new.id, new.path, new.category, new.name, new.title, new.description, new.content); @@ -80,6 +123,7 @@ VALUES (new.id, new.path, new.category, new.name, new.title, new.description, new.content); END" + ;; Prompts FTS table "CREATE VIRTUAL TABLE IF NOT EXISTS prompts_fts USING fts5( path, name, @@ -91,6 +135,7 @@ content_rowid='id' )" + ;; Prompts FTS triggers "CREATE TRIGGER IF NOT EXISTS prompts_ai AFTER INSERT ON prompts BEGIN INSERT INTO prompts_fts(rowid, path, name, title, author, description, content) VALUES (new.id, new.path, new.name, new.title, new.author, new.description, new.content); @@ -117,11 +162,18 @@ "DROP TRIGGER IF EXISTS skills_ad" "DROP TRIGGER IF EXISTS skills_ai" "DROP TABLE IF EXISTS skills_fts" - "DROP INDEX IF EXISTS idx_prompt_skills_skill" - "DROP INDEX IF EXISTS idx_prompt_skills_prompt" - "DROP TABLE IF EXISTS prompt_skills" - "DROP INDEX IF EXISTS idx_prompts_hash" + "DROP INDEX IF EXISTS idx_prompt_references_type" + "DROP INDEX IF EXISTS idx_prompt_references_target_fragment" + "DROP INDEX IF EXISTS idx_prompt_references_target_prompt" + "DROP INDEX IF EXISTS idx_prompt_references_source" + "DROP TABLE IF EXISTS prompt_references" + "DROP INDEX IF EXISTS idx_prompt_fragment_skills_skill" + "DROP INDEX IF EXISTS idx_prompt_fragment_skills_fragment" + "DROP TABLE IF EXISTS prompt_fragment_skills" + "DROP INDEX IF EXISTS idx_prompt_fragments_name" + "DROP TABLE IF EXISTS prompt_fragments" "DROP INDEX IF EXISTS idx_prompts_name" + "DROP INDEX IF EXISTS idx_prompts_hash" "DROP TABLE IF EXISTS prompts" "DROP INDEX IF EXISTS idx_skills_hash" "DROP INDEX IF EXISTS idx_skills_name" diff --git a/resources/migrations/006-prompt-fragments.edn b/resources/migrations/006-prompt-fragments.edn deleted file mode 100644 index 16fec05..0000000 --- a/resources/migrations/006-prompt-fragments.edn +++ /dev/null @@ -1,62 +0,0 @@ -{:id "006-prompt-fragments" - :up - ["CREATE TABLE IF NOT EXISTS prompt_fragments ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - name TEXT NOT NULL UNIQUE, - title TEXT, - description TEXT, - created_at TEXT NOT NULL DEFAULT (datetime('now')), - updated_at TEXT NOT NULL DEFAULT (datetime('now')) - )" - - "CREATE INDEX IF NOT EXISTS idx_prompt_fragments_name ON prompt_fragments(name)" - - "CREATE TABLE IF NOT EXISTS prompt_fragment_skills ( - fragment_id INTEGER NOT NULL, - skill_id INTEGER NOT NULL, - position INTEGER NOT NULL, - created_at TEXT NOT NULL DEFAULT (datetime('now')), - PRIMARY KEY (fragment_id, skill_id), - FOREIGN KEY (fragment_id) REFERENCES prompt_fragments(id) ON DELETE CASCADE, - FOREIGN KEY (skill_id) REFERENCES skills(id) ON DELETE CASCADE - )" - - "CREATE INDEX IF NOT EXISTS idx_prompt_fragment_skills_fragment ON prompt_fragment_skills(fragment_id)" - "CREATE INDEX IF NOT EXISTS idx_prompt_fragment_skills_skill ON prompt_fragment_skills(skill_id)" - - "ALTER TABLE prompts ADD COLUMN fragment_refs TEXT" - - "CREATE TABLE IF NOT EXISTS prompt_references ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - source_prompt_id INTEGER NOT NULL, - target_prompt_id INTEGER, - target_fragment_id INTEGER, - reference_type TEXT NOT NULL CHECK(reference_type IN ('prompt', 'fragment')), - position INTEGER NOT NULL, - created_at TEXT NOT NULL DEFAULT (datetime('now')), - FOREIGN KEY (source_prompt_id) REFERENCES prompts(id) ON DELETE CASCADE, - FOREIGN KEY (target_prompt_id) REFERENCES prompts(id) ON DELETE CASCADE, - FOREIGN KEY (target_fragment_id) REFERENCES prompt_fragments(id) ON DELETE CASCADE, - CONSTRAINT chk_target_reference CHECK ( - (target_prompt_id IS NOT NULL AND target_fragment_id IS NULL) OR - (target_prompt_id IS NULL AND target_fragment_id IS NOT NULL) - ) - )" - - "CREATE INDEX IF NOT EXISTS idx_prompt_references_source ON prompt_references(source_prompt_id)" - "CREATE INDEX IF NOT EXISTS idx_prompt_references_target_prompt ON prompt_references(target_prompt_id)" - "CREATE INDEX IF NOT EXISTS idx_prompt_references_target_fragment ON prompt_references(target_fragment_id)" - "CREATE INDEX IF NOT EXISTS idx_prompt_references_type ON prompt_references(reference_type)"] - - :down - ["DROP INDEX IF EXISTS idx_prompt_references_type" - "DROP INDEX IF EXISTS idx_prompt_references_target_fragment" - "DROP INDEX IF EXISTS idx_prompt_references_target_prompt" - "DROP INDEX IF EXISTS idx_prompt_references_source" - "DROP TABLE IF EXISTS prompt_references" - "ALTER TABLE prompts DROP COLUMN fragment_refs" - "DROP INDEX IF EXISTS idx_prompt_fragment_skills_skill" - "DROP INDEX IF EXISTS idx_prompt_fragment_skills_fragment" - "DROP TABLE IF EXISTS prompt_fragment_skills" - "DROP INDEX IF EXISTS idx_prompt_fragments_name" - "DROP TABLE IF EXISTS prompt_fragments"]} \ No newline at end of file diff --git a/resources/migrations/007-drop-prompt-skills.edn b/resources/migrations/007-drop-prompt-skills.edn deleted file mode 100644 index 7a80ef5..0000000 --- a/resources/migrations/007-drop-prompt-skills.edn +++ /dev/null @@ -1,17 +0,0 @@ -{:id "007-drop-prompt-skills" - :up - ["DROP INDEX IF EXISTS idx_prompt_skills_skill" - "DROP INDEX IF EXISTS idx_prompt_skills_prompt" - "DROP TABLE IF EXISTS prompt_skills"] - - :down - ["CREATE TABLE IF NOT EXISTS prompt_skills ( - prompt_id INTEGER NOT NULL, - skill_id INTEGER NOT NULL, - position INTEGER NOT NULL, - PRIMARY KEY (prompt_id, skill_id), - FOREIGN KEY (prompt_id) REFERENCES prompts(id) ON DELETE CASCADE, - FOREIGN KEY (skill_id) REFERENCES skills(id) ON DELETE CASCADE - )" - "CREATE INDEX IF NOT EXISTS idx_prompt_skills_prompt ON prompt_skills(prompt_id)" - "CREATE INDEX IF NOT EXISTS idx_prompt_skills_skill ON prompt_skills(skill_id)"]} diff --git a/resources/migrations/008-unique-prompt-name.edn b/resources/migrations/008-unique-prompt-name.edn deleted file mode 100644 index 9b748c0..0000000 --- a/resources/migrations/008-unique-prompt-name.edn +++ /dev/null @@ -1,9 +0,0 @@ -{:id "008-unique-prompt-name" - :up - ["DELETE FROM prompts WHERE path LIKE '/home/%'" - "DROP INDEX IF EXISTS idx_prompts_name" - "CREATE UNIQUE INDEX idx_prompts_name ON prompts(name)"] - - :down - ["DROP INDEX IF EXISTS idx_prompts_name" - "CREATE INDEX idx_prompts_name ON prompts(name)"]} diff --git a/src/clojure_skills/config.clj b/src/clojure_skills/config.clj index e4bf4b0..7c20c57 100644 --- a/src/clojure_skills/config.clj +++ b/src/clojure_skills/config.clj @@ -106,10 +106,7 @@ :color true} :permissions - {:plan false - :task false - :task-list false - :prompt false}}) + {:prompt false}}) (defn get-config-file-path "Get path to config.edn file." diff --git a/src/clojure_skills/db/prompt_fragments.clj b/src/clojure_skills/db/prompt_fragments.clj index 18bcd87..83edd81 100644 --- a/src/clojure_skills/db/prompt_fragments.clj +++ b/src/clojure_skills/db/prompt_fragments.clj @@ -183,17 +183,25 @@ ;; Note: Using raw SQL for INSERT...RETURNING because HoneySQL has issues ;; with SQLite's RETURNING clause - (jdbc/execute-one! - db - ["INSERT INTO prompt_references - (source_prompt_id, target_prompt_id, target_fragment_id, reference_type, position) - VALUES (?, ?, ?, ?, ?) - RETURNING *" - (:source_prompt_id reference-map) - (:target_prompt_id reference-map) - (:target_fragment_id reference-map) - (:reference_type reference-map) - (:position reference-map)]))) + ;; Build SQL dynamically based on which target is provided + (let [has-target-prompt (some? (:target_prompt_id reference-map)) + columns (if has-target-prompt + "source_prompt_id, target_prompt_id, reference_type, position" + "source_prompt_id, target_fragment_id, reference_type, position") + placeholders (if has-target-prompt "?, ?, ?, ?" "?, ?, ?, ?") + values (if has-target-prompt + [(:source_prompt_id reference-map) + (:target_prompt_id reference-map) + (:reference_type reference-map) + (:position reference-map)] + [(:source_prompt_id reference-map) + (:target_fragment_id reference-map) + (:reference_type reference-map) + (:position reference-map)])] + (jdbc/execute-one! + db + (into [(str "INSERT INTO prompt_references (" columns ") VALUES (" placeholders ") RETURNING *")] + values))))) (defn get-references-for-prompt "Get all references for a specific prompt, ordered by position." diff --git a/tests.edn b/tests.edn new file mode 100644 index 0000000..f89c8e0 --- /dev/null +++ b/tests.edn @@ -0,0 +1,9 @@ +#kaocha/v1 +{:tests [{:id :unit + :test-paths ["test"] + :source-paths ["src"]}] + :plugins [:kaocha.plugin/profiling + :kaocha.plugin/junit-xml] + :reporter [kaocha.report/documentation] + :color? false + :fail-fast? false}