cognova 0.1.0
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.
- package/.env.example +58 -0
- package/Claude/CLAUDE.md +92 -0
- package/Claude/hooks/lib/__init__.py +1 -0
- package/Claude/hooks/lib/hook_client.py +207 -0
- package/Claude/hooks/log-event.py +97 -0
- package/Claude/hooks/pre-compact.py +46 -0
- package/Claude/hooks/session-end.py +26 -0
- package/Claude/hooks/session-start.py +35 -0
- package/Claude/hooks/stop-extract.py +40 -0
- package/Claude/rules/frontmatter.md +54 -0
- package/Claude/rules/markdown.md +43 -0
- package/Claude/rules/note-organization.md +33 -0
- package/Claude/settings.json +54 -0
- package/Claude/skills/README.md +136 -0
- package/Claude/skills/_lib/__init__.py +1 -0
- package/Claude/skills/_lib/api.py +164 -0
- package/Claude/skills/_lib/output.py +95 -0
- package/Claude/skills/environment/SKILL.md +73 -0
- package/Claude/skills/environment/environment.py +239 -0
- package/Claude/skills/memory/SKILL.md +153 -0
- package/Claude/skills/memory/memory.py +270 -0
- package/Claude/skills/project/SKILL.md +105 -0
- package/Claude/skills/project/project.py +203 -0
- package/Claude/skills/skill-creator/SKILL.md +261 -0
- package/Claude/skills/task/SKILL.md +135 -0
- package/Claude/skills/task/task.py +310 -0
- package/LICENSE +21 -0
- package/README.md +176 -0
- package/app/app.config.ts +8 -0
- package/app/app.vue +39 -0
- package/app/assets/css/main.css +10 -0
- package/app/components/AppLogo.vue +40 -0
- package/app/components/AssistantPanel.client.vue +518 -0
- package/app/components/ConfirmModal.vue +84 -0
- package/app/components/TemplateMenu.vue +49 -0
- package/app/components/agents/AgentActivityChart.client.vue +105 -0
- package/app/components/agents/AgentActivityChart.server.vue +25 -0
- package/app/components/agents/AgentForm.vue +304 -0
- package/app/components/agents/AgentRunModal.vue +154 -0
- package/app/components/agents/AgentStatsCards.vue +98 -0
- package/app/components/chat/ChatInput.vue +85 -0
- package/app/components/chat/ConversationList.vue +78 -0
- package/app/components/chat/MessageBubble.vue +81 -0
- package/app/components/chat/StreamingMessage.vue +36 -0
- package/app/components/chat/ToolCallBlock.vue +77 -0
- package/app/components/editor/CodeEditor.client.vue +212 -0
- package/app/components/editor/CodeEditorFallback.vue +12 -0
- package/app/components/editor/DocumentEditor.vue +326 -0
- package/app/components/editor/DocumentMetadata.vue +140 -0
- package/app/components/editor/MarkdownEditor.vue +146 -0
- package/app/components/files/FileTree.vue +436 -0
- package/app/components/hooks/HookActivityChart.client.vue +117 -0
- package/app/components/hooks/HookActivityChart.server.vue +25 -0
- package/app/components/hooks/HookStatsCards.vue +63 -0
- package/app/components/hooks/RecentEventsTable.vue +123 -0
- package/app/components/hooks/ToolBreakdownTable.vue +72 -0
- package/app/components/search/DashboardSearch.vue +122 -0
- package/app/components/tasks/ProjectSelect.vue +35 -0
- package/app/components/tasks/TaskCard.vue +182 -0
- package/app/components/tasks/TaskDetail.vue +160 -0
- package/app/components/tasks/TaskForm.vue +280 -0
- package/app/components/tasks/TaskList.vue +69 -0
- package/app/components/view/ViewToc.vue +85 -0
- package/app/composables/useAgents.ts +153 -0
- package/app/composables/useAuth.ts +73 -0
- package/app/composables/useChat.ts +298 -0
- package/app/composables/useDocument.ts +141 -0
- package/app/composables/useEditor.ts +100 -0
- package/app/composables/useFileTree.ts +220 -0
- package/app/composables/useHookEvents.ts +68 -0
- package/app/composables/useMemories.ts +83 -0
- package/app/composables/useNotificationBus.ts +154 -0
- package/app/composables/usePreferences.ts +131 -0
- package/app/composables/useProjects.ts +97 -0
- package/app/composables/useSearch.ts +52 -0
- package/app/composables/useTasks.ts +201 -0
- package/app/composables/useTerminal.ts +135 -0
- package/app/layouts/auth.vue +20 -0
- package/app/layouts/dashboard.vue +186 -0
- package/app/layouts/view.vue +60 -0
- package/app/middleware/auth.ts +9 -0
- package/app/pages/agents/[id].vue +602 -0
- package/app/pages/agents/index.vue +412 -0
- package/app/pages/chat.vue +146 -0
- package/app/pages/dashboard.vue +80 -0
- package/app/pages/docs.vue +131 -0
- package/app/pages/hooks.vue +163 -0
- package/app/pages/index.vue +249 -0
- package/app/pages/login.vue +60 -0
- package/app/pages/memories.vue +282 -0
- package/app/pages/settings.vue +625 -0
- package/app/pages/tasks.vue +312 -0
- package/app/pages/view/[uuid].vue +376 -0
- package/dist/cli/index.js +2711 -0
- package/drizzle.config.ts +10 -0
- package/nuxt.config.ts +98 -0
- package/package.json +107 -0
- package/server/api/agents/[id]/cancel.post.ts +27 -0
- package/server/api/agents/[id]/run.post.ts +34 -0
- package/server/api/agents/[id]/runs.get.ts +45 -0
- package/server/api/agents/[id]/stats.get.ts +94 -0
- package/server/api/agents/[id].delete.ts +29 -0
- package/server/api/agents/[id].get.ts +25 -0
- package/server/api/agents/[id].patch.ts +55 -0
- package/server/api/agents/index.get.ts +15 -0
- package/server/api/agents/index.post.ts +48 -0
- package/server/api/agents/stats.get.ts +86 -0
- package/server/api/auth/[...all].ts +5 -0
- package/server/api/conversations/[id].delete.ts +16 -0
- package/server/api/conversations/[id].get.ts +34 -0
- package/server/api/conversations/index.get.ts +17 -0
- package/server/api/documents/[id]/index.delete.ts +47 -0
- package/server/api/documents/[id]/index.put.ts +102 -0
- package/server/api/documents/[id]/public.get.ts +60 -0
- package/server/api/documents/[id]/restore.post.ts +65 -0
- package/server/api/documents/by-path.post.ts +168 -0
- package/server/api/documents/index.get.ts +48 -0
- package/server/api/fs/delete.post.ts +41 -0
- package/server/api/fs/list.get.ts +99 -0
- package/server/api/fs/mkdir.post.ts +44 -0
- package/server/api/fs/move.post.ts +68 -0
- package/server/api/fs/read.post.ts +48 -0
- package/server/api/fs/rename.post.ts +55 -0
- package/server/api/fs/write.post.ts +51 -0
- package/server/api/health.get.ts +40 -0
- package/server/api/home.get.ts +26 -0
- package/server/api/hooks/events/index.get.ts +56 -0
- package/server/api/hooks/events/index.post.ts +36 -0
- package/server/api/hooks/stats.get.ts +99 -0
- package/server/api/memory/[id].delete.ts +26 -0
- package/server/api/memory/context.get.ts +83 -0
- package/server/api/memory/extract.post.ts +42 -0
- package/server/api/memory/search.get.ts +70 -0
- package/server/api/memory/store.post.ts +31 -0
- package/server/api/projects/[id]/index.delete.ts +40 -0
- package/server/api/projects/[id]/index.get.ts +25 -0
- package/server/api/projects/[id]/index.put.ts +50 -0
- package/server/api/projects/index.get.ts +20 -0
- package/server/api/projects/index.post.ts +34 -0
- package/server/api/secrets/[key].delete.ts +31 -0
- package/server/api/secrets/[key].get.ts +30 -0
- package/server/api/secrets/[key].put.ts +52 -0
- package/server/api/secrets/index.get.ts +20 -0
- package/server/api/secrets/index.post.ts +58 -0
- package/server/api/tasks/[id]/index.delete.ts +46 -0
- package/server/api/tasks/[id]/index.get.ts +24 -0
- package/server/api/tasks/[id]/index.put.ts +70 -0
- package/server/api/tasks/[id]/restore.post.ts +49 -0
- package/server/api/tasks/index.get.ts +53 -0
- package/server/api/tasks/index.post.ts +47 -0
- package/server/api/tasks/tags.get.ts +21 -0
- package/server/api/user/email.patch.ts +56 -0
- package/server/db/index.ts +76 -0
- package/server/db/migrate.ts +41 -0
- package/server/db/schema.ts +345 -0
- package/server/db/seed.ts +46 -0
- package/server/db/types.ts +28 -0
- package/server/drizzle/migrations/0000_brown_george_stacy.sql +34 -0
- package/server/drizzle/migrations/0001_stormy_pyro.sql +16 -0
- package/server/drizzle/migrations/0002_clean_colossus.sql +50 -0
- package/server/drizzle/migrations/0003_fine_joystick.sql +12 -0
- package/server/drizzle/migrations/0004_tan_groot.sql +26 -0
- package/server/drizzle/migrations/0005_cloudy_lilith.sql +33 -0
- package/server/drizzle/migrations/0006_ordinary_retro_girl.sql +13 -0
- package/server/drizzle/migrations/0007_flowery_venus.sql +15 -0
- package/server/drizzle/migrations/0008_talented_zombie.sql +13 -0
- package/server/drizzle/migrations/0009_gray_shen.sql +15 -0
- package/server/drizzle/migrations/meta/0000_snapshot.json +230 -0
- package/server/drizzle/migrations/meta/0001_snapshot.json +306 -0
- package/server/drizzle/migrations/meta/0002_snapshot.json +615 -0
- package/server/drizzle/migrations/meta/0003_snapshot.json +730 -0
- package/server/drizzle/migrations/meta/0004_snapshot.json +916 -0
- package/server/drizzle/migrations/meta/0005_snapshot.json +1127 -0
- package/server/drizzle/migrations/meta/0006_snapshot.json +1213 -0
- package/server/drizzle/migrations/meta/0007_snapshot.json +1307 -0
- package/server/drizzle/migrations/meta/0008_snapshot.json +1390 -0
- package/server/drizzle/migrations/meta/0009_snapshot.json +1487 -0
- package/server/drizzle/migrations/meta/_journal.json +76 -0
- package/server/middleware/auth.ts +79 -0
- package/server/plugins/00.env-validate.ts +38 -0
- package/server/plugins/01.api-token.ts +31 -0
- package/server/plugins/02.database.ts +54 -0
- package/server/plugins/03.file-watcher.ts +65 -0
- package/server/plugins/04.cron-agents.ts +26 -0
- package/server/routes/_ws/chat.ts +252 -0
- package/server/routes/notifications.ts +47 -0
- package/server/routes/terminal.ts +98 -0
- package/server/services/agent-executor.ts +218 -0
- package/server/services/cron-scheduler.ts +78 -0
- package/server/services/memory-extractor.ts +120 -0
- package/server/utils/agent-cleanup.ts +91 -0
- package/server/utils/agent-registry.ts +95 -0
- package/server/utils/auth.ts +33 -0
- package/server/utils/chat-session-manager.ts +59 -0
- package/server/utils/crypto.ts +40 -0
- package/server/utils/db-guard.ts +12 -0
- package/server/utils/db-state.ts +63 -0
- package/server/utils/document-sync.ts +207 -0
- package/server/utils/frontmatter.ts +84 -0
- package/server/utils/notification-bus.ts +60 -0
- package/server/utils/path-validator.ts +55 -0
- package/server/utils/pty-manager.ts +130 -0
- package/shared/types/index.ts +604 -0
- package/shared/utils/language-detection.ts +87 -0
- package/tsconfig.json +10 -0
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# Markdown Formatting
|
|
2
|
+
|
|
3
|
+
## Headers
|
|
4
|
+
|
|
5
|
+
Use headers hierarchically:
|
|
6
|
+
- `#` for document title (one per file)
|
|
7
|
+
- `##` for major sections
|
|
8
|
+
- `###` for subsections
|
|
9
|
+
|
|
10
|
+
## Lists
|
|
11
|
+
|
|
12
|
+
- Use `-` for unordered lists
|
|
13
|
+
- Use `1.` for ordered/sequential steps
|
|
14
|
+
- Use `- [ ]` for action items within notes
|
|
15
|
+
|
|
16
|
+
## Links
|
|
17
|
+
|
|
18
|
+
Prefer wiki-style links for internal references:
|
|
19
|
+
```markdown
|
|
20
|
+
See [[project-name]] for details
|
|
21
|
+
Related: [[topic/subtopic]]
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Use standard markdown for external links:
|
|
25
|
+
```markdown
|
|
26
|
+
[Display Text](https://example.com)
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Code
|
|
30
|
+
|
|
31
|
+
Use fenced code blocks with language identifiers:
|
|
32
|
+
```markdown
|
|
33
|
+
```python
|
|
34
|
+
def example():
|
|
35
|
+
pass
|
|
36
|
+
```
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Emphasis
|
|
40
|
+
|
|
41
|
+
- `**bold**` for important terms
|
|
42
|
+
- `*italic*` for emphasis or book/article titles
|
|
43
|
+
- `==highlight==` for key takeaways (if supported)
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# Note Organization
|
|
2
|
+
|
|
3
|
+
## File Naming
|
|
4
|
+
|
|
5
|
+
Use lowercase with hyphens:
|
|
6
|
+
- `project-ideas.md` not `Project Ideas.md`
|
|
7
|
+
- `meeting-notes-2024-01.md` for dated content
|
|
8
|
+
|
|
9
|
+
## Folder Structure
|
|
10
|
+
|
|
11
|
+
Organize by topic or area of responsibility:
|
|
12
|
+
```
|
|
13
|
+
vault/
|
|
14
|
+
├── projects/ # Active projects
|
|
15
|
+
├── areas/ # Ongoing responsibilities
|
|
16
|
+
├── resources/ # Reference material
|
|
17
|
+
├── archive/ # Completed/inactive
|
|
18
|
+
└── inbox/ # Uncategorized captures
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## When Creating Notes
|
|
22
|
+
|
|
23
|
+
1. Check if a related note already exists
|
|
24
|
+
2. Place in appropriate folder
|
|
25
|
+
3. Add frontmatter with required fields
|
|
26
|
+
4. Link to related notes
|
|
27
|
+
|
|
28
|
+
## When to Split Notes
|
|
29
|
+
|
|
30
|
+
Split a note when:
|
|
31
|
+
- It exceeds ~500 lines
|
|
32
|
+
- It covers multiple distinct topics
|
|
33
|
+
- Sections could be referenced independently
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
{
|
|
2
|
+
"hooks": {
|
|
3
|
+
"SessionStart": [
|
|
4
|
+
{
|
|
5
|
+
"hooks": [
|
|
6
|
+
{"type": "command", "command": "python3 ~/.claude/hooks/session-start.py"}
|
|
7
|
+
]
|
|
8
|
+
}
|
|
9
|
+
],
|
|
10
|
+
"PreCompact": [
|
|
11
|
+
{
|
|
12
|
+
"hooks": [
|
|
13
|
+
{"type": "command", "command": "python3 ~/.claude/hooks/pre-compact.py"}
|
|
14
|
+
]
|
|
15
|
+
}
|
|
16
|
+
],
|
|
17
|
+
"Stop": [
|
|
18
|
+
{
|
|
19
|
+
"hooks": [
|
|
20
|
+
{"type": "command", "command": "python3 ~/.claude/hooks/stop-extract.py", "timeout": 60000}
|
|
21
|
+
]
|
|
22
|
+
}
|
|
23
|
+
],
|
|
24
|
+
"SessionEnd": [
|
|
25
|
+
{
|
|
26
|
+
"hooks": [
|
|
27
|
+
{"type": "command", "command": "python3 ~/.claude/hooks/session-end.py"}
|
|
28
|
+
]
|
|
29
|
+
}
|
|
30
|
+
],
|
|
31
|
+
"PreToolUse": [
|
|
32
|
+
{
|
|
33
|
+
"matcher": "Edit|Write|NotebookEdit",
|
|
34
|
+
"hooks": [
|
|
35
|
+
{"type": "command", "command": "python3 ~/.claude/hooks/log-event.py PreToolUse"}
|
|
36
|
+
]
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
"matcher": "Bash",
|
|
40
|
+
"hooks": [
|
|
41
|
+
{"type": "command", "command": "python3 ~/.claude/hooks/log-event.py PreToolUse"}
|
|
42
|
+
]
|
|
43
|
+
}
|
|
44
|
+
],
|
|
45
|
+
"PostToolUse": [
|
|
46
|
+
{
|
|
47
|
+
"matcher": "",
|
|
48
|
+
"hooks": [
|
|
49
|
+
{"type": "command", "command": "python3 ~/.claude/hooks/log-event.py PostToolUse"}
|
|
50
|
+
]
|
|
51
|
+
}
|
|
52
|
+
]
|
|
53
|
+
}
|
|
54
|
+
}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
# Cognova Skills
|
|
2
|
+
|
|
3
|
+
Built-in Claude Code skills for Cognova task and project management.
|
|
4
|
+
|
|
5
|
+
## Available Skills
|
|
6
|
+
|
|
7
|
+
| Skill | Command | Description |
|
|
8
|
+
|-------|---------|-------------|
|
|
9
|
+
| Task | `/task` | Create, list, update, complete tasks |
|
|
10
|
+
| Project | `/project` | Manage projects with duplicate prevention |
|
|
11
|
+
| Skill Creator | `/skill-creator` | Assists in creating new Claude Code skills |
|
|
12
|
+
|
|
13
|
+
## Usage
|
|
14
|
+
|
|
15
|
+
### In Container (Production)
|
|
16
|
+
|
|
17
|
+
Skills are automatically available when running Cognova in Docker:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
docker compose up -d
|
|
21
|
+
docker exec -it cognova claude
|
|
22
|
+
|
|
23
|
+
# Inside Claude Code:
|
|
24
|
+
> /task list
|
|
25
|
+
> /project search homelab
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
### Local Development
|
|
29
|
+
|
|
30
|
+
Run skills directly with Python:
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
# Ensure dev server is running
|
|
34
|
+
pnpm dev
|
|
35
|
+
|
|
36
|
+
# Test task skill (from project root)
|
|
37
|
+
python3 Claude/skills/task/task.py list
|
|
38
|
+
python3 Claude/skills/task/task.py create "Test task" --priority 2
|
|
39
|
+
|
|
40
|
+
# Test project skill
|
|
41
|
+
python3 Claude/skills/project/project.py list
|
|
42
|
+
python3 Claude/skills/project/project.py search "home"
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Environment Variables
|
|
46
|
+
|
|
47
|
+
| Variable | Default | Description |
|
|
48
|
+
|----------|---------|-------------|
|
|
49
|
+
| `COGNOVA_API_URL` | `http://localhost:3000` | API base URL |
|
|
50
|
+
|
|
51
|
+
## Directory Structure
|
|
52
|
+
|
|
53
|
+
```
|
|
54
|
+
Claude/
|
|
55
|
+
├── CLAUDE.md # Default project instructions
|
|
56
|
+
├── rules/ # Documentation standards
|
|
57
|
+
│ ├── markdown.md # Formatting conventions
|
|
58
|
+
│ ├── note-organization.md # File/folder structure
|
|
59
|
+
│ └── frontmatter.md # Required metadata
|
|
60
|
+
└── skills/
|
|
61
|
+
├── _lib/ # Shared Python utilities
|
|
62
|
+
│ ├── api.py # HTTP client
|
|
63
|
+
│ └── output.py # Output formatting
|
|
64
|
+
├── task/ # Task management skill
|
|
65
|
+
├── project/ # Project management skill
|
|
66
|
+
└── skill-creator/ # Skill authoring assistant
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Creating New Skills
|
|
70
|
+
|
|
71
|
+
Use the Skill Creator skill or follow these patterns:
|
|
72
|
+
|
|
73
|
+
### Pure Instruction Skill
|
|
74
|
+
|
|
75
|
+
For simple workflows where Claude uses existing tools:
|
|
76
|
+
|
|
77
|
+
```
|
|
78
|
+
skills/my-skill/
|
|
79
|
+
└── SKILL.md
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### Python Script Skill
|
|
83
|
+
|
|
84
|
+
For complex operations requiring API calls:
|
|
85
|
+
|
|
86
|
+
```
|
|
87
|
+
skills/my-skill/
|
|
88
|
+
├── SKILL.md
|
|
89
|
+
└── my_skill.py
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### SKILL.md Template
|
|
93
|
+
|
|
94
|
+
```yaml
|
|
95
|
+
---
|
|
96
|
+
name: my-skill
|
|
97
|
+
description: When Claude should use this skill
|
|
98
|
+
allowed-tools: Bash, Read
|
|
99
|
+
---
|
|
100
|
+
|
|
101
|
+
# My Skill
|
|
102
|
+
|
|
103
|
+
Instructions for Claude to follow...
|
|
104
|
+
|
|
105
|
+
## Commands
|
|
106
|
+
|
|
107
|
+
\`\`\`bash
|
|
108
|
+
python3 .claude/skills/my-skill/my_skill.py <command> [options]
|
|
109
|
+
\`\`\`
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## Shared Library
|
|
113
|
+
|
|
114
|
+
Skills can import from `_lib/`:
|
|
115
|
+
|
|
116
|
+
```python
|
|
117
|
+
import sys
|
|
118
|
+
from pathlib import Path
|
|
119
|
+
sys.path.insert(0, str(Path(__file__).parent.parent / '_lib'))
|
|
120
|
+
|
|
121
|
+
from api import get, post, put, delete
|
|
122
|
+
from output import success, error, format_task
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## Docker Integration
|
|
126
|
+
|
|
127
|
+
The `Claude/` folder is copied to `/home/node/.claude/` during Docker build:
|
|
128
|
+
|
|
129
|
+
```dockerfile
|
|
130
|
+
# From Dockerfile
|
|
131
|
+
RUN mkdir -p /home/node/.claude && \
|
|
132
|
+
cp -r /app/Claude/* /home/node/.claude/ && \
|
|
133
|
+
chown -R node:node /home/node/.claude
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
This makes skills available as `/task`, `/project`, etc. when running Claude Code inside the container.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# Cognova Skills Library
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Cognova API Client
|
|
4
|
+
|
|
5
|
+
Provides consistent API access for all built-in skills.
|
|
6
|
+
Uses curl subprocess to avoid pip dependencies.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import json
|
|
10
|
+
import os
|
|
11
|
+
import subprocess
|
|
12
|
+
import sys
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
from typing import Any
|
|
15
|
+
|
|
16
|
+
API_BASE = os.environ.get('COGNOVA_API_URL', 'http://localhost:3000')
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def _get_api_token() -> str:
|
|
20
|
+
"""Get API token from environment or .api-token file."""
|
|
21
|
+
# Check environment variable first
|
|
22
|
+
token = os.environ.get('COGNOVA_API_TOKEN', '')
|
|
23
|
+
if token:
|
|
24
|
+
return token
|
|
25
|
+
|
|
26
|
+
# Try to read from .api-token file - check multiple locations
|
|
27
|
+
# to handle bare-metal, Docker, and local dev environments
|
|
28
|
+
possible_paths = [
|
|
29
|
+
# Bare-metal: project dir from environment (set by PM2/settings.json)
|
|
30
|
+
Path(os.environ.get('COGNOVA_PROJECT_DIR', '')) / '.api-token',
|
|
31
|
+
# Docker: app is at /home/node/app, skills at /home/node/.claude/skills
|
|
32
|
+
Path('/home/node/app/.api-token'),
|
|
33
|
+
# Local dev: navigate from _lib -> skills -> Claude -> project root
|
|
34
|
+
Path(__file__).parent.parent.parent.parent / '.api-token',
|
|
35
|
+
# Current working directory
|
|
36
|
+
Path.cwd() / '.api-token',
|
|
37
|
+
]
|
|
38
|
+
|
|
39
|
+
for token_file in possible_paths:
|
|
40
|
+
if token_file.exists():
|
|
41
|
+
try:
|
|
42
|
+
return token_file.read_text().strip()
|
|
43
|
+
except Exception:
|
|
44
|
+
pass
|
|
45
|
+
|
|
46
|
+
return ''
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
# Get token at module load time
|
|
50
|
+
API_TOKEN = _get_api_token()
|
|
51
|
+
|
|
52
|
+
# Debug: warn if no token found
|
|
53
|
+
if not API_TOKEN and os.environ.get('DEBUG'):
|
|
54
|
+
print(f"[api.py] Warning: No API token found", file=sys.stderr)
|
|
55
|
+
print(f"[api.py] Env COGNOVA_API_TOKEN: {bool(os.environ.get('COGNOVA_API_TOKEN'))}", file=sys.stderr)
|
|
56
|
+
print(f"[api.py] Checked paths: {[str(p) for p in [Path('/home/node/app/.api-token'), Path(__file__).parent.parent.parent.parent / '.api-token', Path.cwd() / '.api-token']]}", file=sys.stderr)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def api_request(
|
|
60
|
+
method: str,
|
|
61
|
+
endpoint: str,
|
|
62
|
+
data: dict | None = None,
|
|
63
|
+
params: dict | None = None
|
|
64
|
+
) -> tuple[bool, Any]:
|
|
65
|
+
"""
|
|
66
|
+
Make an API request to Cognova.
|
|
67
|
+
|
|
68
|
+
Args:
|
|
69
|
+
method: HTTP method (GET, POST, PUT, DELETE)
|
|
70
|
+
endpoint: API endpoint (e.g., '/tasks')
|
|
71
|
+
data: Request body for POST/PUT
|
|
72
|
+
params: Query parameters
|
|
73
|
+
|
|
74
|
+
Returns:
|
|
75
|
+
Tuple of (success: bool, data | error_message)
|
|
76
|
+
"""
|
|
77
|
+
url = f"{API_BASE}/api{endpoint}"
|
|
78
|
+
|
|
79
|
+
cmd = ["curl", "-sL", "-X", method]
|
|
80
|
+
cmd.extend(["-H", "Content-Type: application/json"])
|
|
81
|
+
|
|
82
|
+
# Add API token for authentication if available
|
|
83
|
+
if API_TOKEN:
|
|
84
|
+
cmd.extend(["-H", f"X-API-Token: {API_TOKEN}"])
|
|
85
|
+
|
|
86
|
+
if params:
|
|
87
|
+
query_parts = []
|
|
88
|
+
for k, v in params.items():
|
|
89
|
+
if v is not None:
|
|
90
|
+
if isinstance(v, list):
|
|
91
|
+
for item in v:
|
|
92
|
+
query_parts.append(f"{k}={item}")
|
|
93
|
+
else:
|
|
94
|
+
query_parts.append(f"{k}={v}")
|
|
95
|
+
if query_parts:
|
|
96
|
+
url = f"{url}?{'&'.join(query_parts)}"
|
|
97
|
+
|
|
98
|
+
if data and method in ("POST", "PUT"):
|
|
99
|
+
cmd.extend(["-d", json.dumps(data)])
|
|
100
|
+
|
|
101
|
+
cmd.append(url)
|
|
102
|
+
|
|
103
|
+
try:
|
|
104
|
+
result = subprocess.run(cmd, capture_output=True, text=True, timeout=10)
|
|
105
|
+
if result.returncode != 0:
|
|
106
|
+
return False, f"Request failed: {result.stderr}"
|
|
107
|
+
|
|
108
|
+
if not result.stdout.strip():
|
|
109
|
+
return False, "Empty response from server"
|
|
110
|
+
|
|
111
|
+
response = json.loads(result.stdout)
|
|
112
|
+
if response.get("error"):
|
|
113
|
+
# API returns {error: true, message: "..."} on error
|
|
114
|
+
return False, response.get("message", response.get("statusMessage", "Unknown error"))
|
|
115
|
+
|
|
116
|
+
return True, response.get("data", response)
|
|
117
|
+
except json.JSONDecodeError:
|
|
118
|
+
return False, f"Invalid JSON response: {result.stdout[:100]}"
|
|
119
|
+
except subprocess.TimeoutExpired:
|
|
120
|
+
return False, "Request timed out"
|
|
121
|
+
except Exception as e:
|
|
122
|
+
return False, str(e)
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def get(endpoint: str, params: dict | None = None) -> tuple[bool, Any]:
|
|
126
|
+
"""GET request helper."""
|
|
127
|
+
return api_request("GET", endpoint, params=params)
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def post(endpoint: str, data: dict) -> tuple[bool, Any]:
|
|
131
|
+
"""POST request helper."""
|
|
132
|
+
return api_request("POST", endpoint, data=data)
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def put(endpoint: str, data: dict) -> tuple[bool, Any]:
|
|
136
|
+
"""PUT request helper."""
|
|
137
|
+
return api_request("PUT", endpoint, data=data)
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def delete(endpoint: str) -> tuple[bool, Any]:
|
|
141
|
+
"""DELETE request helper."""
|
|
142
|
+
return api_request("DELETE", endpoint)
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def get_secret(key: str) -> tuple[bool, str]:
|
|
146
|
+
"""
|
|
147
|
+
Fetch a decrypted secret by key.
|
|
148
|
+
|
|
149
|
+
Args:
|
|
150
|
+
key: The secret key (e.g., "GOOGLE_API_KEY")
|
|
151
|
+
|
|
152
|
+
Returns:
|
|
153
|
+
Tuple of (success: bool, value | error_message)
|
|
154
|
+
|
|
155
|
+
Example:
|
|
156
|
+
success, api_key = get_secret("GOOGLE_API_KEY")
|
|
157
|
+
if not success:
|
|
158
|
+
print(f"Error: {api_key}")
|
|
159
|
+
sys.exit(1)
|
|
160
|
+
"""
|
|
161
|
+
success, data = get(f"/secrets/{key}")
|
|
162
|
+
if success and isinstance(data, dict):
|
|
163
|
+
return True, data.get("value", "")
|
|
164
|
+
return False, data if isinstance(data, str) else "Secret not found"
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Output formatting for Claude Code skills.
|
|
4
|
+
|
|
5
|
+
Provides consistent, Claude-friendly output formatting.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import sys
|
|
9
|
+
from typing import Any
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def success(message: str, data: Any = None) -> None:
|
|
13
|
+
"""Print success message with optional data."""
|
|
14
|
+
print(f"SUCCESS: {message}")
|
|
15
|
+
if data:
|
|
16
|
+
print(f"\n{format_data(data)}")
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def error(message: str) -> None:
|
|
20
|
+
"""Print error message to stderr."""
|
|
21
|
+
print(f"ERROR: {message}", file=sys.stderr)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def info(message: str) -> None:
|
|
25
|
+
"""Print info message to stderr (not captured in output)."""
|
|
26
|
+
print(f"INFO: {message}", file=sys.stderr)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def warn(message: str) -> None:
|
|
30
|
+
"""Print warning message to stderr."""
|
|
31
|
+
print(f"WARNING: {message}", file=sys.stderr)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def format_task(task: dict) -> str:
|
|
35
|
+
"""Format a task for display."""
|
|
36
|
+
status_icons = {
|
|
37
|
+
'todo': '[ ]',
|
|
38
|
+
'in_progress': '[~]',
|
|
39
|
+
'done': '[x]',
|
|
40
|
+
'blocked': '[!]'
|
|
41
|
+
}
|
|
42
|
+
priority_markers = {1: '', 2: '!', 3: '!!'}
|
|
43
|
+
|
|
44
|
+
status = status_icons.get(task.get('status', 'todo'), '[ ]')
|
|
45
|
+
priority = priority_markers.get(task.get('priority', 2), '!')
|
|
46
|
+
|
|
47
|
+
parts = [status]
|
|
48
|
+
if priority:
|
|
49
|
+
parts.append(priority)
|
|
50
|
+
parts.append(task['title'])
|
|
51
|
+
|
|
52
|
+
if task.get('project'):
|
|
53
|
+
parts.append(f"[{task['project']['name']}]")
|
|
54
|
+
|
|
55
|
+
if task.get('dueDate'):
|
|
56
|
+
due_str = task['dueDate']
|
|
57
|
+
if isinstance(due_str, str):
|
|
58
|
+
due_str = due_str[:10]
|
|
59
|
+
parts.append(f"(due: {due_str})")
|
|
60
|
+
|
|
61
|
+
line1 = ' '.join(parts)
|
|
62
|
+
line2 = f" ID: {task['id'][:8]}"
|
|
63
|
+
|
|
64
|
+
if task.get('tags'):
|
|
65
|
+
line2 += f" | Tags: {', '.join(task['tags'])}"
|
|
66
|
+
|
|
67
|
+
return f"{line1}\n{line2}"
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def format_project(project: dict) -> str:
|
|
71
|
+
"""Format a project for display."""
|
|
72
|
+
desc = ""
|
|
73
|
+
if project.get('description'):
|
|
74
|
+
desc = f"\n {project['description'][:60]}..."
|
|
75
|
+
|
|
76
|
+
return f"- {project['name']} ({project['color']})\n ID: {project['id'][:8]}{desc}"
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def format_data(data: Any) -> str:
|
|
80
|
+
"""Format arbitrary data for display."""
|
|
81
|
+
if isinstance(data, list):
|
|
82
|
+
if not data:
|
|
83
|
+
return "(empty)"
|
|
84
|
+
if 'title' in data[0]:
|
|
85
|
+
return "\n\n".join(format_task(t) for t in data)
|
|
86
|
+
elif 'name' in data[0]:
|
|
87
|
+
return "\n\n".join(format_project(p) for p in data)
|
|
88
|
+
return str(data)
|
|
89
|
+
elif isinstance(data, dict):
|
|
90
|
+
if 'title' in data:
|
|
91
|
+
return format_task(data)
|
|
92
|
+
elif 'name' in data:
|
|
93
|
+
return format_project(data)
|
|
94
|
+
return str(data)
|
|
95
|
+
return str(data)
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: environment
|
|
3
|
+
description: Check system status, view configuration, troubleshoot issues with the Cognova installation. Use when diagnosing problems, checking health, or understanding the current setup.
|
|
4
|
+
allowed-tools: Bash, Read
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Environment Skill
|
|
8
|
+
|
|
9
|
+
Check and manage the Cognova installation environment.
|
|
10
|
+
|
|
11
|
+
## Commands
|
|
12
|
+
|
|
13
|
+
### System info
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
python3 ~/.claude/skills/environment/environment.py info
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Shows install directory, vault path, API URL, OS info, disk usage, vault file counts.
|
|
20
|
+
|
|
21
|
+
### Service status
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
python3 ~/.claude/skills/environment/environment.py status
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Shows PM2 process status (memory, CPU, restarts), API health, and database connectivity.
|
|
28
|
+
|
|
29
|
+
### Recent logs
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
python3 ~/.claude/skills/environment/environment.py logs [--lines N]
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Shows recent PM2 logs (default: 30 lines).
|
|
36
|
+
|
|
37
|
+
### API health check
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
python3 ~/.claude/skills/environment/environment.py health
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Checks multiple API endpoints and reports response times.
|
|
44
|
+
|
|
45
|
+
## Natural Language Patterns
|
|
46
|
+
|
|
47
|
+
- "Is everything running?" → Use `status`
|
|
48
|
+
- "Check the system" → Use `status`
|
|
49
|
+
- "Show me the logs" → Use `logs`
|
|
50
|
+
- "What's the setup?" → Use `info`
|
|
51
|
+
- "Is the API working?" → Use `health`
|
|
52
|
+
- "How much disk space?" → Use `info`
|
|
53
|
+
|
|
54
|
+
## Key Paths
|
|
55
|
+
|
|
56
|
+
| Resource | How to Find |
|
|
57
|
+
|----------|-------------|
|
|
58
|
+
| Install dir | `$COGNOVA_PROJECT_DIR` or check `~/.cognova` metadata file |
|
|
59
|
+
| Vault | `$VAULT_PATH` |
|
|
60
|
+
| PM2 config | `$COGNOVA_PROJECT_DIR/ecosystem.config.cjs` |
|
|
61
|
+
| App logs | `$COGNOVA_PROJECT_DIR/logs/` |
|
|
62
|
+
| Environment | `$COGNOVA_PROJECT_DIR/.env` |
|
|
63
|
+
| Database migrations | `$COGNOVA_PROJECT_DIR/server/drizzle/migrations/` |
|
|
64
|
+
|
|
65
|
+
## Common Troubleshooting
|
|
66
|
+
|
|
67
|
+
| Symptom | Check |
|
|
68
|
+
|---------|-------|
|
|
69
|
+
| 503 errors | `status` — DB might not have initialized; check logs |
|
|
70
|
+
| API unreachable | `pm2 status` — process may have crashed; `pm2 restart cognova` |
|
|
71
|
+
| Skills not working | Verify `~/.claude/skills/` has skill directories; re-run `cognova update` |
|
|
72
|
+
| Out of memory | Check `info` for disk/memory; PM2 config has `--max-old-space-size=4096` |
|
|
73
|
+
| Build failures | `NODE_OPTIONS='--max-old-space-size=4096' pnpm build` |
|