cognova 0.2.0 → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/Claude/CLAUDE.md +9 -4
  2. package/Claude/skills/environment/SKILL.md +6 -0
  3. package/Claude/skills/memory/SKILL.md +6 -0
  4. package/Claude/skills/project/SKILL.md +6 -0
  5. package/Claude/skills/secret/SKILL.md +85 -0
  6. package/Claude/skills/secret/secret.py +146 -0
  7. package/Claude/skills/skill-creator/SKILL.md +30 -0
  8. package/Claude/skills/task/SKILL.md +6 -0
  9. package/app/components/skills/Card.vue +82 -0
  10. package/app/components/skills/CreateModal.vue +156 -0
  11. package/app/components/skills/Editor.vue +135 -0
  12. package/app/components/skills/FileTree.vue +336 -0
  13. package/app/components/skills/LibraryCard.vue +122 -0
  14. package/app/components/skills/RenameModal.vue +84 -0
  15. package/app/layouts/dashboard.vue +7 -0
  16. package/app/pages/skills/[name].vue +198 -0
  17. package/app/pages/skills/index.vue +157 -0
  18. package/app/pages/skills/library.vue +209 -0
  19. package/dist/cli/index.js +23 -23
  20. package/nuxt.config.ts +9 -0
  21. package/package.json +1 -1
  22. package/server/api/skills/[name]/files/create.post.ts +45 -0
  23. package/server/api/skills/[name]/files/delete.post.ts +45 -0
  24. package/server/api/skills/[name]/files/index.get.ts +28 -0
  25. package/server/api/skills/[name]/files/read.post.ts +41 -0
  26. package/server/api/skills/[name]/files/write.post.ts +42 -0
  27. package/server/api/skills/[name]/index.get.ts +54 -0
  28. package/server/api/skills/[name]/rename.post.ts +64 -0
  29. package/server/api/skills/[name]/toggle.post.ts +32 -0
  30. package/server/api/skills/create.post.ts +51 -0
  31. package/server/api/skills/generate.post.ts +126 -0
  32. package/server/api/skills/index.get.ts +57 -0
  33. package/server/api/skills/library/check-updates.get.ts +46 -0
  34. package/server/api/skills/library/index.get.ts +56 -0
  35. package/server/api/skills/library/install.post.ts +73 -0
  36. package/server/db/schema.ts +17 -0
  37. package/server/drizzle/migrations/0012_good_deadpool.sql +12 -0
  38. package/server/drizzle/migrations/0013_swift_snowbird.sql +1 -0
  39. package/server/drizzle/migrations/meta/0012_snapshot.json +1713 -0
  40. package/server/drizzle/migrations/meta/0013_snapshot.json +1720 -0
  41. package/server/drizzle/migrations/meta/_journal.json +14 -0
  42. package/server/middleware/auth.ts +0 -1
  43. package/server/plugins/05.skills-catalog.ts +105 -0
  44. package/server/utils/skills-path.ts +197 -0
  45. package/shared/types/index.ts +63 -0
@@ -0,0 +1,198 @@
1
+ <script setup lang="ts">
2
+ import type { SkillDetail } from '~~/shared/types'
3
+
4
+ definePageMeta({
5
+ layout: 'dashboard',
6
+ middleware: 'auth'
7
+ })
8
+
9
+ const route = useRoute()
10
+ const router = useRouter()
11
+ const toast = useToast()
12
+
13
+ const skillName = computed(() => route.params.name as string)
14
+ const skill = ref<SkillDetail | null>(null)
15
+ const loading = ref(true)
16
+ const selectedFile = ref('')
17
+ const renameOpen = ref(false)
18
+
19
+ async function loadSkill() {
20
+ loading.value = true
21
+ try {
22
+ const res = await $fetch<{ data: SkillDetail }>(`/api/skills/${skillName.value}`)
23
+ skill.value = res.data
24
+
25
+ // Auto-select SKILL.md if exists
26
+ if (!selectedFile.value) {
27
+ const skillMd = res.data.files.find(f => f.name === 'SKILL.md')
28
+ if (skillMd) selectedFile.value = skillMd.path
29
+ }
30
+ } catch {
31
+ toast.add({ title: 'Skill not found', color: 'error' })
32
+ router.push('/skills')
33
+ } finally {
34
+ loading.value = false
35
+ }
36
+ }
37
+
38
+ async function handleToggle() {
39
+ if (!skill.value || skill.value.core) return
40
+ try {
41
+ const res = await $fetch<{ data: { active: boolean } }>(`/api/skills/${skillName.value}/toggle`, {
42
+ method: 'POST'
43
+ })
44
+ skill.value.active = res.data.active
45
+ toast.add({
46
+ title: `${skillName.value} ${res.data.active ? 'enabled' : 'disabled'}`,
47
+ color: 'success'
48
+ })
49
+ } catch {
50
+ toast.add({ title: 'Failed to toggle skill', color: 'error' })
51
+ }
52
+ }
53
+
54
+ function handleRenamed(newName: string) {
55
+ router.replace(`/skills/${newName}`)
56
+ }
57
+
58
+ onMounted(() => loadSkill())
59
+ </script>
60
+
61
+ <template>
62
+ <div class="flex flex-1 min-w-0">
63
+ <!-- File tree sidebar -->
64
+ <UDashboardPanel
65
+ id="skill-filetree"
66
+ collapsible
67
+ resizable
68
+ :min-size="12"
69
+ :default-size="16"
70
+ :max-size="24"
71
+ class="hidden lg:flex"
72
+ >
73
+ <template #header>
74
+ <UDashboardNavbar>
75
+ <template #title>
76
+ <span class="text-sm font-medium truncate">Files</span>
77
+ </template>
78
+ </UDashboardNavbar>
79
+ </template>
80
+
81
+ <template #body>
82
+ <div
83
+ v-if="loading"
84
+ class="p-4 space-y-2"
85
+ >
86
+ <USkeleton
87
+ v-for="i in 4"
88
+ :key="i"
89
+ class="h-6 w-full"
90
+ />
91
+ </div>
92
+ <SkillsFileTree
93
+ v-else-if="skill"
94
+ :skill-name="skillName"
95
+ :files="skill.files"
96
+ :selected-path="selectedFile"
97
+ @select="selectedFile = $event"
98
+ @refresh="loadSkill"
99
+ />
100
+ </template>
101
+ </UDashboardPanel>
102
+
103
+ <!-- Editor panel -->
104
+ <UDashboardPanel
105
+ id="skill-editor"
106
+ grow
107
+ :ui="{ body: '!p-0' }"
108
+ >
109
+ <template #header>
110
+ <UDashboardNavbar>
111
+ <template #title>
112
+ <NuxtLink
113
+ to="/skills"
114
+ class="text-muted hover:text-default transition-colors"
115
+ >
116
+ <UIcon
117
+ name="i-lucide-arrow-left"
118
+ class="size-4"
119
+ />
120
+ </NuxtLink>
121
+ <span class="font-medium">{{ skillName }}</span>
122
+ <UBadge
123
+ v-if="skill?.core"
124
+ variant="subtle"
125
+ color="primary"
126
+ size="xs"
127
+ >
128
+ Core
129
+ </UBadge>
130
+ <UBadge
131
+ v-if="skill && !skill.active"
132
+ variant="subtle"
133
+ color="neutral"
134
+ size="xs"
135
+ >
136
+ Disabled
137
+ </UBadge>
138
+ </template>
139
+ <template #right>
140
+ <USwitch
141
+ v-if="skill && !skill.core"
142
+ :model-value="skill.active"
143
+ size="sm"
144
+ @update:model-value="handleToggle"
145
+ />
146
+ <UButton
147
+ v-if="skill && !skill.core"
148
+ icon="i-lucide-pencil"
149
+ variant="ghost"
150
+ color="neutral"
151
+ size="sm"
152
+ @click="renameOpen = true"
153
+ />
154
+ </template>
155
+ </UDashboardNavbar>
156
+ </template>
157
+
158
+ <template #body>
159
+ <div
160
+ v-if="loading"
161
+ class="flex-1 flex items-center justify-center h-full"
162
+ >
163
+ <UIcon
164
+ name="i-lucide-loader-2"
165
+ class="size-6 animate-spin text-dimmed"
166
+ />
167
+ </div>
168
+ <div
169
+ v-else-if="!selectedFile"
170
+ class="flex-1 flex items-center justify-center h-full text-dimmed"
171
+ >
172
+ <div class="text-center">
173
+ <UIcon
174
+ name="i-lucide-file-code"
175
+ class="size-12 mx-auto mb-4 opacity-50"
176
+ />
177
+ <p>Select a file to edit</p>
178
+ </div>
179
+ </div>
180
+ <SkillsEditor
181
+ v-else
182
+ :key="selectedFile"
183
+ :skill-name="skillName"
184
+ :file-path="selectedFile"
185
+ @saved="loadSkill"
186
+ />
187
+ </template>
188
+ </UDashboardPanel>
189
+
190
+ <SkillsRenameModal
191
+ v-if="skill"
192
+ :open="renameOpen"
193
+ :current-name="skillName"
194
+ @update:open="renameOpen = $event"
195
+ @renamed="handleRenamed"
196
+ />
197
+ </div>
198
+ </template>
@@ -0,0 +1,157 @@
1
+ <script setup lang="ts">
2
+ import type { SkillListItem } from '~~/shared/types'
3
+
4
+ definePageMeta({
5
+ layout: 'dashboard',
6
+ middleware: 'auth'
7
+ })
8
+
9
+ const toast = useToast()
10
+ const skills = ref<SkillListItem[]>([])
11
+ const loading = ref(true)
12
+ const search = ref('')
13
+ const createOpen = ref(false)
14
+
15
+ const filteredSkills = computed(() => {
16
+ if (!search.value.trim()) return skills.value
17
+ const q = search.value.toLowerCase()
18
+ return skills.value.filter(s =>
19
+ s.name.toLowerCase().includes(q)
20
+ || s.description.toLowerCase().includes(q)
21
+ )
22
+ })
23
+
24
+ async function loadSkills() {
25
+ loading.value = true
26
+ try {
27
+ const res = await $fetch<{ data: SkillListItem[] }>('/api/skills')
28
+ skills.value = res.data
29
+ } catch {
30
+ toast.add({ title: 'Failed to load skills', color: 'error' })
31
+ } finally {
32
+ loading.value = false
33
+ }
34
+ }
35
+
36
+ async function handleToggle(name: string) {
37
+ try {
38
+ const res = await $fetch<{ data: { name: string, active: boolean } }>(`/api/skills/${name}/toggle`, {
39
+ method: 'POST'
40
+ })
41
+ const skill = skills.value.find(s => s.name === name)
42
+ if (skill) skill.active = res.data.active
43
+ toast.add({
44
+ title: `${name} ${res.data.active ? 'enabled' : 'disabled'}`,
45
+ color: 'success'
46
+ })
47
+ } catch {
48
+ toast.add({ title: `Failed to toggle ${name}`, color: 'error' })
49
+ }
50
+ }
51
+
52
+ onMounted(() => loadSkills())
53
+ </script>
54
+
55
+ <template>
56
+ <div class="flex flex-1 min-w-0">
57
+ <UDashboardPanel
58
+ id="skills-main"
59
+ grow
60
+ >
61
+ <template #header>
62
+ <UDashboardNavbar title="Skills">
63
+ <template #right>
64
+ <UInput
65
+ v-model="search"
66
+ icon="i-lucide-search"
67
+ placeholder="Search skills..."
68
+ size="sm"
69
+ class="w-48"
70
+ />
71
+ <NuxtLink to="/skills/library">
72
+ <UButton
73
+ icon="i-lucide-library"
74
+ variant="ghost"
75
+ color="neutral"
76
+ size="sm"
77
+ >
78
+ Library
79
+ </UButton>
80
+ </NuxtLink>
81
+ <UButton
82
+ icon="i-lucide-plus"
83
+ size="sm"
84
+ @click="createOpen = true"
85
+ >
86
+ Create Skill
87
+ </UButton>
88
+ </template>
89
+ </UDashboardNavbar>
90
+ </template>
91
+
92
+ <template #body>
93
+ <div class="p-4">
94
+ <!-- Loading -->
95
+ <div
96
+ v-if="loading"
97
+ class="grid gap-4 sm:grid-cols-2 lg:grid-cols-3"
98
+ >
99
+ <div
100
+ v-for="i in 6"
101
+ :key="i"
102
+ class="p-4 rounded-lg border border-default bg-elevated/50"
103
+ >
104
+ <USkeleton class="h-4 w-24 mb-2" />
105
+ <USkeleton class="h-3 w-full mb-1" />
106
+ <USkeleton class="h-3 w-2/3" />
107
+ </div>
108
+ </div>
109
+
110
+ <!-- Empty -->
111
+ <div
112
+ v-else-if="skills.length === 0"
113
+ class="flex flex-col items-center justify-center py-16 text-dimmed"
114
+ >
115
+ <UIcon
116
+ name="i-lucide-puzzle"
117
+ class="size-12 mb-4"
118
+ />
119
+ <p class="text-lg font-medium">
120
+ No skills found
121
+ </p>
122
+ <p class="text-sm mt-1">
123
+ Skills are loaded from ~/.claude/skills/
124
+ </p>
125
+ </div>
126
+
127
+ <!-- No results -->
128
+ <div
129
+ v-else-if="filteredSkills.length === 0"
130
+ class="text-center py-12 text-muted text-sm"
131
+ >
132
+ No skills matching "{{ search }}"
133
+ </div>
134
+
135
+ <!-- Grid -->
136
+ <div
137
+ v-else
138
+ class="grid gap-4 sm:grid-cols-2 lg:grid-cols-3"
139
+ >
140
+ <SkillsCard
141
+ v-for="skill in filteredSkills"
142
+ :key="skill.name"
143
+ :skill="skill"
144
+ @toggle="handleToggle"
145
+ />
146
+ </div>
147
+ </div>
148
+ </template>
149
+ </UDashboardPanel>
150
+
151
+ <SkillsCreateModal
152
+ :open="createOpen"
153
+ @update:open="createOpen = $event"
154
+ @created="loadSkills"
155
+ />
156
+ </div>
157
+ </template>
@@ -0,0 +1,209 @@
1
+ <script setup lang="ts">
2
+ import type { SkillCatalogItem } from '~~/shared/types'
3
+
4
+ definePageMeta({
5
+ layout: 'dashboard',
6
+ middleware: 'auth'
7
+ })
8
+
9
+ const toast = useToast()
10
+ const skills = ref<SkillCatalogItem[]>([])
11
+ const loading = ref(true)
12
+ const installing = ref<string | null>(null)
13
+ const search = ref('')
14
+ const activeTag = ref<string | null>(null)
15
+
16
+ // Collect all unique tags across skills
17
+ const allTags = computed(() => {
18
+ const tagSet = new Set<string>()
19
+ for (const s of skills.value) {
20
+ for (const t of s.tags) tagSet.add(t)
21
+ }
22
+ return [...tagSet].sort()
23
+ })
24
+
25
+ const filteredSkills = computed(() => {
26
+ let result = skills.value
27
+
28
+ if (activeTag.value) {
29
+ result = result.filter(s => s.tags.includes(activeTag.value!))
30
+ }
31
+
32
+ if (search.value.trim()) {
33
+ const q = search.value.toLowerCase()
34
+ result = result.filter(s =>
35
+ s.name.toLowerCase().includes(q)
36
+ || s.description.toLowerCase().includes(q)
37
+ || s.author.toLowerCase().includes(q)
38
+ )
39
+ }
40
+
41
+ return result
42
+ })
43
+
44
+ function toggleTag(tag: string) {
45
+ activeTag.value = activeTag.value === tag ? null : tag
46
+ }
47
+
48
+ async function loadLibrary() {
49
+ loading.value = true
50
+ try {
51
+ const res = await $fetch<{ data: SkillCatalogItem[] }>('/api/skills/library')
52
+ skills.value = res.data
53
+ } catch {
54
+ toast.add({ title: 'Failed to load skills library', color: 'error' })
55
+ } finally {
56
+ loading.value = false
57
+ }
58
+ }
59
+
60
+ async function handleInstall(name: string) {
61
+ installing.value = name
62
+ try {
63
+ await $fetch('/api/skills/library/install', {
64
+ method: 'POST',
65
+ body: { name }
66
+ })
67
+ toast.add({ title: `Installed ${name}`, color: 'success' })
68
+ await loadLibrary()
69
+ } catch (e: unknown) {
70
+ const message = e instanceof Error ? e.message : 'Failed to install skill'
71
+ toast.add({ title: message, color: 'error' })
72
+ } finally {
73
+ installing.value = null
74
+ }
75
+ }
76
+
77
+ async function handleUpdate(name: string) {
78
+ installing.value = name
79
+ try {
80
+ // Remove existing first
81
+ await $fetch(`/api/skills/${name}/files/delete`, {
82
+ method: 'POST',
83
+ body: { path: '.' }
84
+ }).catch(() => {})
85
+
86
+ await $fetch('/api/skills/library/install', {
87
+ method: 'POST',
88
+ body: { name }
89
+ })
90
+ toast.add({ title: `Updated ${name}`, color: 'success' })
91
+ await loadLibrary()
92
+ } catch {
93
+ toast.add({ title: `Failed to update ${name}`, color: 'error' })
94
+ } finally {
95
+ installing.value = null
96
+ }
97
+ }
98
+
99
+ onMounted(() => loadLibrary())
100
+ </script>
101
+
102
+ <template>
103
+ <div class="flex flex-1 min-w-0">
104
+ <UDashboardPanel
105
+ id="skills-library"
106
+ grow
107
+ >
108
+ <template #header>
109
+ <UDashboardNavbar title="Skills Library">
110
+ <template #right>
111
+ <UInput
112
+ v-model="search"
113
+ icon="i-lucide-search"
114
+ placeholder="Search library..."
115
+ size="sm"
116
+ class="w-48"
117
+ />
118
+ <NuxtLink to="/skills">
119
+ <UButton
120
+ icon="i-lucide-arrow-left"
121
+ variant="ghost"
122
+ color="neutral"
123
+ size="sm"
124
+ >
125
+ My Skills
126
+ </UButton>
127
+ </NuxtLink>
128
+ </template>
129
+ </UDashboardNavbar>
130
+ </template>
131
+
132
+ <template #body>
133
+ <div class="p-4">
134
+ <!-- Tag filter bar -->
135
+ <div
136
+ v-if="allTags.length > 0 && !loading"
137
+ class="flex flex-wrap gap-1.5 mb-4"
138
+ >
139
+ <UButton
140
+ v-for="tag in allTags"
141
+ :key="tag"
142
+ size="xs"
143
+ :variant="activeTag === tag ? 'solid' : 'outline'"
144
+ :color="tag === 'official' ? 'primary' : 'neutral'"
145
+ @click="toggleTag(tag)"
146
+ >
147
+ {{ tag }}
148
+ </UButton>
149
+ </div>
150
+
151
+ <!-- Loading -->
152
+ <div
153
+ v-if="loading"
154
+ class="grid gap-4 sm:grid-cols-2 lg:grid-cols-3"
155
+ >
156
+ <div
157
+ v-for="i in 6"
158
+ :key="i"
159
+ class="p-4 rounded-lg border border-default bg-elevated/50"
160
+ >
161
+ <USkeleton class="h-4 w-24 mb-2" />
162
+ <USkeleton class="h-3 w-full mb-1" />
163
+ <USkeleton class="h-3 w-2/3" />
164
+ </div>
165
+ </div>
166
+
167
+ <!-- Empty -->
168
+ <div
169
+ v-else-if="skills.length === 0"
170
+ class="flex flex-col items-center justify-center py-16 text-dimmed"
171
+ >
172
+ <UIcon
173
+ name="i-lucide-library"
174
+ class="size-12 mb-4"
175
+ />
176
+ <p class="text-lg font-medium">
177
+ No community skills available
178
+ </p>
179
+ <p class="text-sm mt-1">
180
+ The skills registry is empty or hasn't synced yet.
181
+ </p>
182
+ </div>
183
+
184
+ <!-- No results -->
185
+ <div
186
+ v-else-if="filteredSkills.length === 0"
187
+ class="text-center py-12 text-muted text-sm"
188
+ >
189
+ No skills matching {{ activeTag ? `"${activeTag}"` : '' }} {{ search ? `"${search}"` : '' }}
190
+ </div>
191
+
192
+ <!-- Grid -->
193
+ <div
194
+ v-else
195
+ class="grid gap-4 sm:grid-cols-2 lg:grid-cols-3"
196
+ >
197
+ <SkillsLibraryCard
198
+ v-for="skill in filteredSkills"
199
+ :key="skill.name"
200
+ :skill="skill"
201
+ @install="handleInstall"
202
+ @update="handleUpdate"
203
+ />
204
+ </div>
205
+ </div>
206
+ </template>
207
+ </UDashboardPanel>
208
+ </div>
209
+ </template>
package/dist/cli/index.js CHANGED
@@ -154,11 +154,6 @@ var require_src = __commonJS({
154
154
  }
155
155
  });
156
156
 
157
- // src/index.ts
158
- import { readFileSync as readFileSync4 } from "fs";
159
- import { join as join11, dirname as dirname2 } from "path";
160
- import { fileURLToPath as fileURLToPath2 } from "url";
161
-
162
157
  // src/commands/init.ts
163
158
  import { execSync as execSync5 } from "child_process";
164
159
  import { join as join7 } from "path";
@@ -1357,6 +1352,14 @@ var __dirname = dirname(__filename);
1357
1352
  function getPackageDir() {
1358
1353
  return join(__dirname, "..", "..");
1359
1354
  }
1355
+ function getPackageVersion() {
1356
+ try {
1357
+ const pkg = JSON.parse(readFileSync(join(getPackageDir(), "package.json"), "utf-8"));
1358
+ return pkg.version || "0.0.0";
1359
+ } catch {
1360
+ return "0.0.0";
1361
+ }
1362
+ }
1360
1363
  function getClaudeDir() {
1361
1364
  return join(homedir(), ".claude");
1362
1365
  }
@@ -1694,6 +1697,7 @@ You run as a persistent service managed by PM2. Your conversations are streamed
1694
1697
  | Task Management | \`/task\` | Create, list, update, complete tasks |
1695
1698
  | Project Management | \`/project\` | Organize tasks into projects |
1696
1699
  | Memory | \`/memory\` | Search past decisions, store insights |
1700
+ | Secrets | \`/secret\` | Store, retrieve, list, delete encrypted API keys and credentials |
1697
1701
  | Environment | \`/environment\` | Check system status, troubleshoot issues |
1698
1702
  | Skill Creator | \`/skill-creator\` | Create new Claude Code skills |
1699
1703
 
@@ -1749,11 +1753,15 @@ Memory is your most important tool. You are stateless between sessions \u2014 wi
1749
1753
  **Rule: When in doubt, store it.** A redundant memory is harmless. A forgotten one wastes ${p.userName}'s time.
1750
1754
 
1751
1755
  ### Secrets & Sensitive Data
1752
- - NEVER store passwords, tokens, API keys, or credentials in memory, notes, or conversation
1753
- - NEVER write secrets to files \u2014 use the Cognova settings UI or secrets API instead
1754
- - If ${p.userName} shares a credential in chat, warn them it should be stored as a secret
1755
- - When you need a token for an integration, check the secrets API first before asking ${p.userName}
1756
+
1757
+ **CRITICAL \u2014 Zero tolerance for leaked secrets:**
1758
+ - NEVER store passwords, tokens, API keys, or credentials in memory, notes, conversation, or any file
1759
+ - NEVER write secrets to files \u2014 use \`/secret set KEY\` or the Cognova settings UI instead
1760
+ - NEVER embed API keys, tokens, or credentials in SKILL.md files or Python scripts when creating or modifying skills \u2014 always use \`get_secret()\` from \`_lib/api.py\`
1761
+ - If ${p.userName} shares a credential in chat, warn them and store it via \`/secret set KEY\` immediately
1762
+ - When you need a token for an integration, check with \`/secret list\` and \`/secret get KEY\` before asking ${p.userName}
1756
1763
  - Treat any string that looks like a key, token, or password as sensitive \u2014 do not echo it back
1764
+ - When creating skills that need external API keys, declare them in \`metadata.requires-secrets\` frontmatter and use \`get_secret()\` in the script
1757
1765
 
1758
1766
  ### Troubleshooting
1759
1767
  - Use \`/environment status\` or \`/environment health\` to diagnose issues
@@ -2205,7 +2213,7 @@ async function init() {
2205
2213
  s.start("Installing Claude Code configuration");
2206
2214
  await installClaudeConfig(config);
2207
2215
  s.stop("Claude config installed to ~/.claude/");
2208
- writeMetadata(resolvedInstallDir, vault.path, "0.1.0");
2216
+ writeMetadata(resolvedInstallDir, vault.path, getPackageVersion());
2209
2217
  R2.step(import_picocolors5.default.bold("Setup"));
2210
2218
  s.start("Installing dependencies");
2211
2219
  execSync5("pnpm install", { cwd: resolvedInstallDir, stdio: "pipe" });
@@ -2387,8 +2395,10 @@ async function update() {
2387
2395
  process.exit(1);
2388
2396
  }
2389
2397
  }
2390
- if (!updateFailed)
2398
+ if (!updateFailed) {
2399
+ writeMetadata(installDir, metadata.vaultPath, latestVersion);
2391
2400
  cleanupBackup(backupDir);
2401
+ }
2392
2402
  if (updateFailed) {
2393
2403
  Le("Update failed. Previous version has been restored. Try again later.");
2394
2404
  process.exit(1);
@@ -2612,18 +2622,8 @@ async function reset() {
2612
2622
  }
2613
2623
 
2614
2624
  // src/index.ts
2615
- var __filename2 = fileURLToPath2(import.meta.url);
2616
- var __dirname2 = dirname2(__filename2);
2617
- function getVersion() {
2618
- try {
2619
- const pkg = JSON.parse(readFileSync4(join11(__dirname2, "..", "..", "package.json"), "utf-8"));
2620
- return pkg.version || "0.0.0";
2621
- } catch {
2622
- return "0.0.0";
2623
- }
2624
- }
2625
2625
  var HELP_TEXT = `
2626
- cognova v${getVersion()} \u2014 Personal knowledge management with Claude Code
2626
+ cognova v${getPackageVersion()} \u2014 Personal knowledge management with Claude Code
2627
2627
 
2628
2628
  Usage: cognova <command> [options]
2629
2629
 
@@ -2720,7 +2720,7 @@ var args = process.argv.slice(2);
2720
2720
  var command = args.find((a) => !a.startsWith("-"));
2721
2721
  var flags = new Set(args.filter((a) => a.startsWith("-")));
2722
2722
  if (flags.has("--version") || flags.has("-v")) {
2723
- console.log(getVersion());
2723
+ console.log(getPackageVersion());
2724
2724
  process.exit(0);
2725
2725
  }
2726
2726
  if (flags.has("--help") || flags.has("-h")) {
package/nuxt.config.ts CHANGED
@@ -10,6 +10,15 @@ export default defineNuxtConfig({
10
10
  enabled: true
11
11
  },
12
12
 
13
+ app: {
14
+ head: {
15
+ link: [
16
+ { rel: 'icon', type: 'image/svg+xml', href: '/favicon.svg' },
17
+ { rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' }
18
+ ]
19
+ }
20
+ },
21
+
13
22
  css: ['~/assets/css/main.css'],
14
23
 
15
24
  mdc: {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "cognova",
3
3
  "type": "module",
4
- "version": "0.2.0",
4
+ "version": "0.2.2",
5
5
  "description": "Personal knowledge management system with Claude Code integration",
6
6
  "repository": {
7
7
  "type": "git",