@leejungkiin/awkit 1.0.9 → 1.1.1
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/README.md +3 -3
- package/VERSION +1 -1
- package/bin/awf.js +1 -1
- package/bin/awk.js +287 -34
- package/core/AGENTS.md +8 -9
- package/core/GEMINI.md +77 -199
- package/package.json +2 -1
- package/skill-packs/neural-memory/skills/nm-memory-sync/SKILL.md +2 -2
- package/skills/CATALOG.md +3 -2
- package/skills/README.md +109 -0
- package/skills/android-re-analyzer/SKILL.md +238 -0
- package/skills/android-re-analyzer/references/api-extraction-patterns.md +119 -0
- package/skills/android-re-analyzer/references/call-flow-analysis.md +176 -0
- package/skills/android-re-analyzer/references/fernflower-usage.md +115 -0
- package/skills/android-re-analyzer/references/jadx-usage.md +116 -0
- package/skills/android-re-analyzer/references/setup-guide.md +221 -0
- package/skills/android-re-analyzer/scripts/check-deps.sh +129 -0
- package/skills/android-re-analyzer/scripts/decompile.sh +375 -0
- package/skills/android-re-analyzer/scripts/find-api-calls.sh +118 -0
- package/skills/android-re-analyzer/scripts/install-dep.sh +448 -0
- package/skills/awf-session-restore/SKILL.md +108 -184
- package/skills/beads-manager/SKILL.md +2 -2
- package/skills/brainstorm-agent/SKILL.md +47 -2
- package/skills/gemini-conductor/SKILL.md +234 -0
- package/skills/memory-sync/SKILL.md +29 -1
- package/skills/nm-memory-sync/SKILL.md +2 -2
- package/skills/orchestrator/SKILL.md +32 -154
- package/skills/skills/nm-memory-sync/SKILL.md +2 -2
- package/skills/smali-to-kotlin/SKILL.md +1 -1
- package/skills/smali-to-swift/SKILL.md +1 -1
- package/skills/swiftui-pro/SKILL.md +108 -0
- package/skills/swiftui-pro/agents/openai.yaml +10 -0
- package/skills/swiftui-pro/assets/swiftui-pro-icon.png +0 -0
- package/skills/swiftui-pro/assets/swiftui-pro-icon.svg +29 -0
- package/skills/swiftui-pro/references/accessibility.md +13 -0
- package/skills/swiftui-pro/references/api.md +39 -0
- package/skills/swiftui-pro/references/data.md +43 -0
- package/skills/swiftui-pro/references/design.md +31 -0
- package/skills/swiftui-pro/references/hygiene.md +9 -0
- package/skills/swiftui-pro/references/navigation.md +14 -0
- package/skills/swiftui-pro/references/performance.md +46 -0
- package/skills/swiftui-pro/references/swift.md +56 -0
- package/skills/swiftui-pro/references/views.md +35 -0
- package/skills/symphony-enforcer/SKILL.md +227 -0
- package/skills/symphony-orchestrator/SKILL.md +301 -0
- package/skills/telegram-notify/SKILL.md +57 -0
- package/symphony/LICENSE +21 -0
- package/symphony/README.md +178 -0
- package/symphony/app/api/agents/route.js +152 -0
- package/symphony/app/api/events/route.js +22 -0
- package/symphony/app/api/knowledge/route.js +253 -0
- package/symphony/app/api/locks/route.js +29 -0
- package/symphony/app/api/notes/route.js +125 -0
- package/symphony/app/api/preflight/route.js +23 -0
- package/symphony/app/api/projects/route.js +116 -0
- package/symphony/app/api/roles/route.js +134 -0
- package/symphony/app/api/skills/route.js +82 -0
- package/symphony/app/api/status/route.js +18 -0
- package/symphony/app/api/tasks/route.js +157 -0
- package/symphony/app/api/workflows/route.js +61 -0
- package/symphony/app/api/workspaces/route.js +15 -0
- package/symphony/app/globals.css +2605 -0
- package/symphony/app/layout.js +20 -0
- package/symphony/app/page.js +2122 -0
- package/symphony/cli/index.js +1060 -0
- package/symphony/core/agent-manager.js +357 -0
- package/symphony/core/context-bus.js +100 -0
- package/symphony/core/db.js +223 -0
- package/symphony/core/file-lock-manager.js +154 -0
- package/symphony/core/merge-pipeline.js +234 -0
- package/symphony/core/orchestrator.js +236 -0
- package/symphony/core/task-manager.js +335 -0
- package/symphony/core/workspace-manager.js +168 -0
- package/symphony/jsconfig.json +7 -0
- package/symphony/lib/core.mjs +1034 -0
- package/symphony/mcp/index.js +29 -0
- package/symphony/mcp/server.js +110 -0
- package/symphony/mcp/tools/context.js +80 -0
- package/symphony/mcp/tools/locks.js +99 -0
- package/symphony/mcp/tools/status.js +82 -0
- package/symphony/mcp/tools/tasks.js +216 -0
- package/symphony/mcp/tools/workspace.js +143 -0
- package/symphony/next.config.mjs +7 -0
- package/symphony/package.json +53 -0
- package/symphony/scripts/postinstall.js +49 -0
- package/symphony/symphony.config.js +41 -0
- package/templates/conductor-tracks.md +38 -0
- package/templates/workflow_dual_mode_template.md +5 -5
- package/workflows/_uncategorized/AGENTS.md +38 -0
- package/workflows/_uncategorized/decompile.md +67 -0
- package/workflows/_uncategorized/skill-health.md +7 -7
- package/workflows/ads/ads-audit.md +5 -5
- package/workflows/ads/ads-optimize.md +10 -10
- package/workflows/ads/adsExpert.md +7 -7
- package/workflows/conductor.md +97 -0
- package/workflows/context/auto-implement.md +4 -4
- package/workflows/context/codebase-sync.md +19 -8
- package/workflows/context/next.md +27 -27
- package/workflows/context/user-intent-analysis-workflow.md +4 -4
- package/workflows/expert/codeExpert.md +28 -31
- package/workflows/expert/debugExpert.md +11 -11
- package/workflows/expert/planExpert.md +21 -36
- package/workflows/git/smart-git-ops.md +49 -6
- package/workflows/lifecycle/debug.md +7 -7
- package/workflows/lifecycle/deploy.md +10 -10
- package/workflows/lifecycle/master-code-workflow.md +3 -3
- package/workflows/lifecycle/plan.md +19 -21
- package/workflows/quality/audit.md +1 -1
- package/workflows/quality/project-audit.md +1 -1
- package/workflows/roles/vibe-coding-master-workflow.md +2 -2
- package/workflows/smart-git-ops.md +146 -0
- package/workflows/ui/app-screen-analyzer.md +4 -4
- package/workflows/ui/create-feature.md +8 -8
- package/workflows/ui/create-spec-architect.md +11 -11
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: telegram-notify
|
|
3
|
+
description: Send Telegram notifications via AWKit CLI (Bot API). Use when tasks complete, deploys finish, or user requests Telegram alerts.
|
|
4
|
+
triggers:
|
|
5
|
+
- keywords: ["telegram", "tg", "notify", "gửi telegram", "báo telegram", "thông báo"]
|
|
6
|
+
- events: ["deploy_complete", "task_complete", "user_request"]
|
|
7
|
+
priority: low
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Telegram Notify Skill
|
|
11
|
+
|
|
12
|
+
Send messages to Telegram groups/topics via `awkit tg` CLI command (Bot API, zero MCP needed).
|
|
13
|
+
|
|
14
|
+
## When to Use
|
|
15
|
+
|
|
16
|
+
- User explicitly asks to send a Telegram message
|
|
17
|
+
- After deploy/release completion (if user has configured notifications)
|
|
18
|
+
- Task completion summaries when user requests
|
|
19
|
+
|
|
20
|
+
## Prerequisites
|
|
21
|
+
|
|
22
|
+
User must have run `awkit tg setup` first. If not configured, guide them:
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
awkit tg setup
|
|
26
|
+
# Prompts: Bot Token → Chat ID → Topic ID (optional)
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Commands
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
# Send plain text
|
|
33
|
+
awkit tg send "message here"
|
|
34
|
+
|
|
35
|
+
# Send to specific chat
|
|
36
|
+
awkit tg send --chat -100xxx "message"
|
|
37
|
+
|
|
38
|
+
# Send to specific forum topic
|
|
39
|
+
awkit tg send --topic 123 "message"
|
|
40
|
+
|
|
41
|
+
# Markdown formatting
|
|
42
|
+
awkit tg send --parse-mode md "**bold** _italic_ `code`"
|
|
43
|
+
|
|
44
|
+
# HTML formatting
|
|
45
|
+
awkit tg send --parse-mode html "<b>bold</b> <i>italic</i>"
|
|
46
|
+
|
|
47
|
+
# Combine flags
|
|
48
|
+
awkit tg send --chat -100xxx --topic 456 --parse-mode md "**Done!**"
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## AI Usage Rules
|
|
52
|
+
|
|
53
|
+
1. **NEVER send without user consent** — always confirm before sending
|
|
54
|
+
2. **Keep messages concise** — summarize, don't dump full logs
|
|
55
|
+
3. **Use Markdown** for formatted messages (`--parse-mode md`)
|
|
56
|
+
4. **Config location**: `~/.gemini/antigravity/.tg_config.json`
|
|
57
|
+
5. If `awkit tg send` returns error about config, tell user to run `awkit tg setup`
|
package/symphony/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 AWKit
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
# 🎼 AWKit Symphony
|
|
2
|
+
|
|
3
|
+
> Multi-Agent Orchestration for AI Coding Assistants
|
|
4
|
+
|
|
5
|
+
Symphony coordinates multiple AI agents working on the same codebase — managing tasks, preventing file conflicts, and providing real-time visibility through a dashboard.
|
|
6
|
+
|
|
7
|
+
## ✨ Features
|
|
8
|
+
|
|
9
|
+
| Feature | Description |
|
|
10
|
+
|---------|-------------|
|
|
11
|
+
| **Task Management** | Create, assign, track tasks with priority & acceptance criteria |
|
|
12
|
+
| **Git Isolation** | Auto worktree/clone per task — agents work on separate branches |
|
|
13
|
+
| **File Locking** | Pessimistic locking prevents two agents from editing the same file |
|
|
14
|
+
| **Merge Pipeline** | Auto-rebase + fast-forward merge when tasks complete |
|
|
15
|
+
| **MCP Server** | 14 tools for IDE integration via Model Context Protocol |
|
|
16
|
+
| **Dashboard** | Real-time Kanban board, agent status, events feed |
|
|
17
|
+
| **Context Bus** | Event pub/sub — agents notify each other of changes |
|
|
18
|
+
|
|
19
|
+
## 🚀 Quick Start
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
# Install
|
|
23
|
+
cd symphony
|
|
24
|
+
npm install
|
|
25
|
+
|
|
26
|
+
# Start dashboard (port 3100)
|
|
27
|
+
npm run dev
|
|
28
|
+
|
|
29
|
+
# Or use CLI
|
|
30
|
+
node cli/index.js status
|
|
31
|
+
node cli/index.js task list
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## 📋 CLI Commands
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
# System
|
|
38
|
+
symphony status # Show system status
|
|
39
|
+
symphony start # Start dashboard server
|
|
40
|
+
symphony dashboard # Open dashboard in browser
|
|
41
|
+
|
|
42
|
+
# Tasks
|
|
43
|
+
symphony task list [-s ready] # List tasks (filter by status)
|
|
44
|
+
symphony task create "Feature X" -p 1 # Create task (priority 1-3)
|
|
45
|
+
symphony task show <id> # Show task details
|
|
46
|
+
|
|
47
|
+
# Workspaces
|
|
48
|
+
symphony workspace list # List active workspaces
|
|
49
|
+
symphony workspace create <task-id> # Create workspace for task
|
|
50
|
+
symphony workspace merge <task-id> # Auto-merge completed task
|
|
51
|
+
symphony workspace clean # Remove merged workspaces
|
|
52
|
+
|
|
53
|
+
# File Locks
|
|
54
|
+
symphony lock list # Show active locks
|
|
55
|
+
symphony lock release <file> # Force-release stuck lock
|
|
56
|
+
|
|
57
|
+
# MCP Server
|
|
58
|
+
symphony mcp-serve [-n "Agent Name"] # Start MCP server (stdio)
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## 🔌 MCP Integration
|
|
62
|
+
|
|
63
|
+
Add to your IDE's MCP config:
|
|
64
|
+
|
|
65
|
+
```json
|
|
66
|
+
{
|
|
67
|
+
"mcpServers": {
|
|
68
|
+
"symphony": {
|
|
69
|
+
"command": "node",
|
|
70
|
+
"args": ["/path/to/symphony/mcp/server.js"],
|
|
71
|
+
"env": {
|
|
72
|
+
"SYMPHONY_AGENT_NAME": "my-agent"
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Available MCP Tools (14)
|
|
80
|
+
|
|
81
|
+
| Tool | Description |
|
|
82
|
+
|------|-------------|
|
|
83
|
+
| `symphony_available_tasks` | List available tasks |
|
|
84
|
+
| `symphony_claim_task` | Claim a task (get workspace + branch) |
|
|
85
|
+
| `symphony_report_progress` | Report progress (0-100%) |
|
|
86
|
+
| `symphony_complete_task` | Complete task + trigger merge |
|
|
87
|
+
| `symphony_abandon_task` | Abandon a task |
|
|
88
|
+
| `symphony_check_files` | Check file lock status |
|
|
89
|
+
| `symphony_lock_files` | Lock files for editing |
|
|
90
|
+
| `symphony_unlock_files` | Release file locks |
|
|
91
|
+
| `symphony_broadcast` | Broadcast event to other agents |
|
|
92
|
+
| `symphony_events` | Query context bus events |
|
|
93
|
+
| `symphony_status` | Get system status |
|
|
94
|
+
| `symphony_create_task` | Create a new task |
|
|
95
|
+
| `symphony_workspace_status` | Get workspace info + diff stats |
|
|
96
|
+
| `symphony_merge_task` | Run auto-merge pipeline |
|
|
97
|
+
|
|
98
|
+
## 🏗️ Architecture
|
|
99
|
+
|
|
100
|
+
```
|
|
101
|
+
symphony/
|
|
102
|
+
├── core/ # Engine
|
|
103
|
+
│ ├── db.js # SQLite (WAL mode)
|
|
104
|
+
│ ├── task-manager.js # Task CRUD + state machine
|
|
105
|
+
│ ├── workspace-manager.js # Git worktree lifecycle
|
|
106
|
+
│ ├── merge-pipeline.js # Auto-rebase + merge
|
|
107
|
+
│ ├── file-lock-manager.js # Pessimistic file locking
|
|
108
|
+
│ ├── context-bus.js # Event pub/sub
|
|
109
|
+
│ └── orchestrator.js # Agent dispatch + state
|
|
110
|
+
├── mcp/ # MCP Server
|
|
111
|
+
│ ├── server.js # stdio transport
|
|
112
|
+
│ ├── index.js # Tool registry (14 tools)
|
|
113
|
+
│ └── tools/ # Tool implementations
|
|
114
|
+
├── cli/ # CLI
|
|
115
|
+
│ └── index.js # Commander.js commands
|
|
116
|
+
├── app/ # Dashboard (Next.js)
|
|
117
|
+
│ ├── page.js # Kanban + status + events
|
|
118
|
+
│ └── api/ # REST API routes
|
|
119
|
+
└── lib/
|
|
120
|
+
└── core.mjs # ESM bridge for Turbopack
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
## ⚙️ Configuration
|
|
124
|
+
|
|
125
|
+
Edit `symphony.config.js`:
|
|
126
|
+
|
|
127
|
+
```js
|
|
128
|
+
module.exports = {
|
|
129
|
+
port: 3100,
|
|
130
|
+
maxAgents: 3,
|
|
131
|
+
workspace: {
|
|
132
|
+
type: 'hybrid', // 'worktree' | 'clone' | 'hybrid'
|
|
133
|
+
cloneThreshold: 30, // files > 30 → full clone
|
|
134
|
+
},
|
|
135
|
+
git: {
|
|
136
|
+
autoMerge: true,
|
|
137
|
+
targetBranch: 'main',
|
|
138
|
+
branchPrefix: 'symphony/',
|
|
139
|
+
},
|
|
140
|
+
locks: {
|
|
141
|
+
strategy: 'pessimistic',
|
|
142
|
+
autoRelease: 3600, // seconds
|
|
143
|
+
},
|
|
144
|
+
};
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
## 📊 Dashboard
|
|
148
|
+
|
|
149
|
+
The dashboard runs on `http://localhost:3100` and provides:
|
|
150
|
+
|
|
151
|
+
- **Kanban Board** — Tasks by status (Ready → In Progress → Review → Done)
|
|
152
|
+
- **Agent Panel** — Connected agents with live status
|
|
153
|
+
- **Events Feed** — Real-time context bus events
|
|
154
|
+
- **Lock Panel** — Active file locks with force-release
|
|
155
|
+
- **Stats Bar** — Task counts and system health
|
|
156
|
+
|
|
157
|
+
## 🔄 Task Lifecycle
|
|
158
|
+
|
|
159
|
+
```
|
|
160
|
+
Ready → Claimed → In Progress → Review → Done
|
|
161
|
+
│ │
|
|
162
|
+
└─── Abandoned ◀────────────────────────┘
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
1. **Create** task via CLI or dashboard
|
|
166
|
+
2. **Agent claims** via MCP → gets isolated workspace + branch
|
|
167
|
+
3. **Agent works** → reports progress, locks files, broadcasts events
|
|
168
|
+
4. **Agent completes** → triggers auto-merge pipeline
|
|
169
|
+
5. **Merge** → rebase onto main, fast-forward, cleanup workspace
|
|
170
|
+
|
|
171
|
+
## 📦 Tech Stack
|
|
172
|
+
|
|
173
|
+
- **Runtime:** Node.js
|
|
174
|
+
- **Database:** SQLite (via better-sqlite3, WAL mode)
|
|
175
|
+
- **Dashboard:** Next.js 16 + React 19
|
|
176
|
+
- **MCP:** @modelcontextprotocol/sdk
|
|
177
|
+
- **CLI:** Commander.js
|
|
178
|
+
- **Git:** Native git commands (worktree, rebase, merge)
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import { NextResponse } from 'next/server';
|
|
2
|
+
import {
|
|
3
|
+
listAllAgents, registerAgent, updateAgentProfile, removeAgent, dispatchTask,
|
|
4
|
+
listProjectAgents, getProjectAgent, createProjectAgent, updateProjectAgent,
|
|
5
|
+
removeProjectAgent, attachProjectAgentSession, detachProjectAgentSession,
|
|
6
|
+
} from '../../../lib/core.mjs';
|
|
7
|
+
|
|
8
|
+
// GET /api/agents — List agents (supports ?project=X for project-scoped filtering)
|
|
9
|
+
export async function GET(request) {
|
|
10
|
+
try {
|
|
11
|
+
const { searchParams } = new URL(request.url);
|
|
12
|
+
const project = searchParams.get('project');
|
|
13
|
+
const type = searchParams.get('type') || 'project'; // 'project' (default) or 'legacy'
|
|
14
|
+
|
|
15
|
+
if (type === 'legacy') {
|
|
16
|
+
const agents = listAllAgents();
|
|
17
|
+
return NextResponse.json({ agents, type: 'legacy' });
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const agents = listProjectAgents(project || undefined);
|
|
21
|
+
return NextResponse.json({ agents, type: 'project' });
|
|
22
|
+
} catch (error) {
|
|
23
|
+
return NextResponse.json(
|
|
24
|
+
{ error: error.message },
|
|
25
|
+
{ status: 500 }
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// POST /api/agents — Create agent (project-scoped or legacy)
|
|
31
|
+
export async function POST(request) {
|
|
32
|
+
try {
|
|
33
|
+
const body = await request.json();
|
|
34
|
+
if (!body.id) {
|
|
35
|
+
return NextResponse.json({ error: 'Agent ID is required' }, { status: 400 });
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Project-scoped agent (new)
|
|
39
|
+
if (body.projectId || body.project_id) {
|
|
40
|
+
const agent = createProjectAgent({
|
|
41
|
+
id: body.id,
|
|
42
|
+
projectId: body.projectId || body.project_id,
|
|
43
|
+
name: body.name || body.id,
|
|
44
|
+
skills: body.skills || [],
|
|
45
|
+
icon: body.icon,
|
|
46
|
+
color: body.color,
|
|
47
|
+
});
|
|
48
|
+
return NextResponse.json({ agent, type: 'project' }, { status: 201 });
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Legacy agent
|
|
52
|
+
const agent = registerAgent(body.id, body.name);
|
|
53
|
+
if (body.specialties || body.color || body.max_concurrent) {
|
|
54
|
+
updateAgentProfile(body.id, {
|
|
55
|
+
specialties: body.specialties,
|
|
56
|
+
color: body.color,
|
|
57
|
+
max_concurrent: body.max_concurrent,
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
const updated = listAllAgents().find(a => a.id === body.id);
|
|
61
|
+
return NextResponse.json({ agent: updated, type: 'legacy' }, { status: 201 });
|
|
62
|
+
} catch (error) {
|
|
63
|
+
return NextResponse.json(
|
|
64
|
+
{ error: error.message },
|
|
65
|
+
{ status: 500 }
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// PATCH /api/agents — Update agent, assign task, attach, or detach
|
|
71
|
+
export async function PATCH(request) {
|
|
72
|
+
try {
|
|
73
|
+
const body = await request.json();
|
|
74
|
+
if (!body.id) {
|
|
75
|
+
return NextResponse.json({ error: 'Agent ID is required' }, { status: 400 });
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Check if this is a project agent
|
|
79
|
+
const projectAgent = getProjectAgent(body.id);
|
|
80
|
+
|
|
81
|
+
if (projectAgent) {
|
|
82
|
+
// Project agent actions
|
|
83
|
+
if (body.action === 'attach' && body.sessionId) {
|
|
84
|
+
const agent = attachProjectAgentSession(body.id, body.sessionId);
|
|
85
|
+
return NextResponse.json({ agent, action: 'attached' });
|
|
86
|
+
}
|
|
87
|
+
if (body.action === 'detach') {
|
|
88
|
+
const agent = detachProjectAgentSession(body.id);
|
|
89
|
+
return NextResponse.json({ agent, action: 'detached' });
|
|
90
|
+
}
|
|
91
|
+
if (body.action === 'assign' && body.taskId) {
|
|
92
|
+
const task = dispatchTask(body.id, body.taskId);
|
|
93
|
+
return NextResponse.json({ task, action: 'assigned' });
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Update project agent fields
|
|
97
|
+
const agent = updateProjectAgent(body.id, {
|
|
98
|
+
name: body.name,
|
|
99
|
+
skills: body.skills,
|
|
100
|
+
icon: body.icon,
|
|
101
|
+
color: body.color,
|
|
102
|
+
});
|
|
103
|
+
return NextResponse.json({ agent, type: 'project' });
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Legacy agent fallback
|
|
107
|
+
if (body.action === 'assign' && body.taskId) {
|
|
108
|
+
const task = dispatchTask(body.id, body.taskId);
|
|
109
|
+
return NextResponse.json({ task });
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const agent = updateAgentProfile(body.id, {
|
|
113
|
+
name: body.name,
|
|
114
|
+
specialties: body.specialties,
|
|
115
|
+
color: body.color,
|
|
116
|
+
max_concurrent: body.max_concurrent,
|
|
117
|
+
});
|
|
118
|
+
const updated = listAllAgents().find(a => a.id === body.id);
|
|
119
|
+
return NextResponse.json({ agent: updated, type: 'legacy' });
|
|
120
|
+
} catch (error) {
|
|
121
|
+
return NextResponse.json(
|
|
122
|
+
{ error: error.message },
|
|
123
|
+
{ status: 500 }
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// DELETE /api/agents — Remove agent
|
|
129
|
+
export async function DELETE(request) {
|
|
130
|
+
try {
|
|
131
|
+
const body = await request.json();
|
|
132
|
+
if (!body.id) {
|
|
133
|
+
return NextResponse.json({ error: 'Agent ID is required' }, { status: 400 });
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Try project agent first
|
|
137
|
+
const projectAgent = getProjectAgent(body.id);
|
|
138
|
+
if (projectAgent) {
|
|
139
|
+
removeProjectAgent(body.id);
|
|
140
|
+
return NextResponse.json({ success: true, type: 'project' });
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Legacy fallback
|
|
144
|
+
removeAgent(body.id);
|
|
145
|
+
return NextResponse.json({ success: true, type: 'legacy' });
|
|
146
|
+
} catch (error) {
|
|
147
|
+
return NextResponse.json(
|
|
148
|
+
{ error: error.message },
|
|
149
|
+
{ status: 500 }
|
|
150
|
+
);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { NextResponse } from 'next/server';
|
|
2
|
+
import { queryEvents } from '../../../lib/core.mjs';
|
|
3
|
+
|
|
4
|
+
// GET /api/events — List recent events (scoped by project)
|
|
5
|
+
export async function GET(request) {
|
|
6
|
+
try {
|
|
7
|
+
const { searchParams } = new URL(request.url);
|
|
8
|
+
const since = searchParams.get('since') || undefined;
|
|
9
|
+
const eventType = searchParams.get('type') || undefined;
|
|
10
|
+
const project = searchParams.get('project') || undefined;
|
|
11
|
+
const limit = parseInt(searchParams.get('limit') || '30');
|
|
12
|
+
|
|
13
|
+
const events = queryEvents({ since, eventType, project, limit });
|
|
14
|
+
|
|
15
|
+
return NextResponse.json({ events });
|
|
16
|
+
} catch (error) {
|
|
17
|
+
return NextResponse.json(
|
|
18
|
+
{ error: error.message },
|
|
19
|
+
{ status: 500 }
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
import { NextResponse } from 'next/server';
|
|
2
|
+
import fs from 'fs';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
|
|
5
|
+
const KNOWLEDGE_DIR = path.join(
|
|
6
|
+
process.env.HOME || process.env.USERPROFILE,
|
|
7
|
+
'.gemini', 'antigravity', 'knowledge'
|
|
8
|
+
);
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* GET /api/knowledge
|
|
12
|
+
*
|
|
13
|
+
* Query params:
|
|
14
|
+
* - (none) → List all KI summaries
|
|
15
|
+
* - id=<folder> → Get KI detail + artifact list
|
|
16
|
+
* - id=<folder>&file=<path> → Read artifact file content
|
|
17
|
+
*/
|
|
18
|
+
export async function GET(request) {
|
|
19
|
+
try {
|
|
20
|
+
const { searchParams } = new URL(request.url);
|
|
21
|
+
const id = searchParams.get('id');
|
|
22
|
+
const file = searchParams.get('file');
|
|
23
|
+
|
|
24
|
+
// Read a specific artifact file
|
|
25
|
+
if (id && file) {
|
|
26
|
+
const filePath = path.join(KNOWLEDGE_DIR, id, file);
|
|
27
|
+
if (!filePath.startsWith(KNOWLEDGE_DIR)) {
|
|
28
|
+
return NextResponse.json({ error: 'Invalid path' }, { status: 400 });
|
|
29
|
+
}
|
|
30
|
+
if (!fs.existsSync(filePath)) {
|
|
31
|
+
return NextResponse.json({ error: 'File not found' }, { status: 404 });
|
|
32
|
+
}
|
|
33
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
34
|
+
return NextResponse.json({ content, path: file });
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Get detail of a specific KI
|
|
38
|
+
if (id) {
|
|
39
|
+
const kiDir = path.join(KNOWLEDGE_DIR, id);
|
|
40
|
+
if (!fs.existsSync(kiDir)) {
|
|
41
|
+
return NextResponse.json({ error: 'KI not found' }, { status: 404 });
|
|
42
|
+
}
|
|
43
|
+
const metadata = readMetadata(kiDir);
|
|
44
|
+
const artifacts = listArtifacts(kiDir);
|
|
45
|
+
return NextResponse.json({ item: { id, ...metadata, artifacts } });
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// List all KIs
|
|
49
|
+
if (!fs.existsSync(KNOWLEDGE_DIR)) {
|
|
50
|
+
return NextResponse.json({ items: [] });
|
|
51
|
+
}
|
|
52
|
+
const entries = fs.readdirSync(KNOWLEDGE_DIR, { withFileTypes: true });
|
|
53
|
+
const items = [];
|
|
54
|
+
for (const entry of entries) {
|
|
55
|
+
if (!entry.isDirectory()) continue;
|
|
56
|
+
const kiDir = path.join(KNOWLEDGE_DIR, entry.name);
|
|
57
|
+
const metaPath = path.join(kiDir, 'metadata.json');
|
|
58
|
+
if (!fs.existsSync(metaPath)) continue;
|
|
59
|
+
const metadata = readMetadata(kiDir);
|
|
60
|
+
const artifactCount = countArtifacts(kiDir);
|
|
61
|
+
const stat = fs.statSync(metaPath);
|
|
62
|
+
items.push({
|
|
63
|
+
id: entry.name,
|
|
64
|
+
title: metadata.title || entry.name,
|
|
65
|
+
summary: metadata.summary || '',
|
|
66
|
+
referenceCount: (metadata.references || []).length,
|
|
67
|
+
artifactCount,
|
|
68
|
+
updatedAt: stat.mtime.toISOString(),
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
// Sort by updatedAt desc
|
|
72
|
+
items.sort((a, b) => new Date(b.updatedAt) - new Date(a.updatedAt));
|
|
73
|
+
return NextResponse.json({ items });
|
|
74
|
+
} catch (error) {
|
|
75
|
+
return NextResponse.json({ error: error.message }, { status: 500 });
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* POST /api/knowledge — Create a new KI
|
|
81
|
+
* Body: { id, title, summary, references? }
|
|
82
|
+
*/
|
|
83
|
+
export async function POST(request) {
|
|
84
|
+
try {
|
|
85
|
+
const body = await request.json();
|
|
86
|
+
if (!body.id || !body.title) {
|
|
87
|
+
return NextResponse.json({ error: 'id and title are required' }, { status: 400 });
|
|
88
|
+
}
|
|
89
|
+
// Sanitize id
|
|
90
|
+
const safeId = body.id.replace(/[^a-z0-9_-]/gi, '_').toLowerCase();
|
|
91
|
+
const kiDir = path.join(KNOWLEDGE_DIR, safeId);
|
|
92
|
+
if (fs.existsSync(kiDir)) {
|
|
93
|
+
return NextResponse.json({ error: 'KI already exists' }, { status: 409 });
|
|
94
|
+
}
|
|
95
|
+
const artifactsDir = path.join(kiDir, 'artifacts');
|
|
96
|
+
fs.mkdirSync(artifactsDir, { recursive: true });
|
|
97
|
+
|
|
98
|
+
const metadata = {
|
|
99
|
+
title: body.title,
|
|
100
|
+
summary: body.summary || '',
|
|
101
|
+
references: body.references || [],
|
|
102
|
+
};
|
|
103
|
+
fs.writeFileSync(
|
|
104
|
+
path.join(kiDir, 'metadata.json'),
|
|
105
|
+
JSON.stringify(metadata, null, 2),
|
|
106
|
+
'utf-8'
|
|
107
|
+
);
|
|
108
|
+
// Create overview.md stub
|
|
109
|
+
fs.writeFileSync(
|
|
110
|
+
path.join(artifactsDir, 'overview.md'),
|
|
111
|
+
`# ${body.title}\n\n${body.summary || 'TODO: Add content'}\n`,
|
|
112
|
+
'utf-8'
|
|
113
|
+
);
|
|
114
|
+
return NextResponse.json({
|
|
115
|
+
item: { id: safeId, ...metadata, artifacts: ['artifacts/overview.md'] }
|
|
116
|
+
}, { status: 201 });
|
|
117
|
+
} catch (error) {
|
|
118
|
+
return NextResponse.json({ error: error.message }, { status: 500 });
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* PATCH /api/knowledge — Update metadata or file content
|
|
124
|
+
* Body: { id, file?, content?, metadata? }
|
|
125
|
+
*/
|
|
126
|
+
export async function PATCH(request) {
|
|
127
|
+
try {
|
|
128
|
+
const body = await request.json();
|
|
129
|
+
if (!body.id) {
|
|
130
|
+
return NextResponse.json({ error: 'id is required' }, { status: 400 });
|
|
131
|
+
}
|
|
132
|
+
const kiDir = path.join(KNOWLEDGE_DIR, body.id);
|
|
133
|
+
if (!fs.existsSync(kiDir)) {
|
|
134
|
+
return NextResponse.json({ error: 'KI not found' }, { status: 404 });
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Update file content
|
|
138
|
+
if (body.file && body.content !== undefined) {
|
|
139
|
+
const filePath = path.join(kiDir, body.file);
|
|
140
|
+
if (!filePath.startsWith(KNOWLEDGE_DIR)) {
|
|
141
|
+
return NextResponse.json({ error: 'Invalid path' }, { status: 400 });
|
|
142
|
+
}
|
|
143
|
+
// Ensure parent dir exists
|
|
144
|
+
const parentDir = path.dirname(filePath);
|
|
145
|
+
if (!fs.existsSync(parentDir)) {
|
|
146
|
+
fs.mkdirSync(parentDir, { recursive: true });
|
|
147
|
+
}
|
|
148
|
+
fs.writeFileSync(filePath, body.content, 'utf-8');
|
|
149
|
+
return NextResponse.json({ success: true, file: body.file });
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Update metadata
|
|
153
|
+
if (body.metadata) {
|
|
154
|
+
const metaPath = path.join(kiDir, 'metadata.json');
|
|
155
|
+
const existing = readMetadata(kiDir);
|
|
156
|
+
const updated = {
|
|
157
|
+
...existing,
|
|
158
|
+
...body.metadata,
|
|
159
|
+
};
|
|
160
|
+
fs.writeFileSync(metaPath, JSON.stringify(updated, null, 2), 'utf-8');
|
|
161
|
+
return NextResponse.json({ success: true, metadata: updated });
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
return NextResponse.json({ error: 'No update specified (need file+content or metadata)' }, { status: 400 });
|
|
165
|
+
} catch (error) {
|
|
166
|
+
return NextResponse.json({ error: error.message }, { status: 500 });
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* DELETE /api/knowledge — Delete an artifact file
|
|
172
|
+
* Body: { id, file }
|
|
173
|
+
*/
|
|
174
|
+
export async function DELETE(request) {
|
|
175
|
+
try {
|
|
176
|
+
const body = await request.json();
|
|
177
|
+
if (!body.id || !body.file) {
|
|
178
|
+
return NextResponse.json({ error: 'id and file are required' }, { status: 400 });
|
|
179
|
+
}
|
|
180
|
+
// Prevent deleting metadata.json
|
|
181
|
+
if (body.file === 'metadata.json') {
|
|
182
|
+
return NextResponse.json({ error: 'Cannot delete metadata.json' }, { status: 400 });
|
|
183
|
+
}
|
|
184
|
+
const filePath = path.join(KNOWLEDGE_DIR, body.id, body.file);
|
|
185
|
+
if (!filePath.startsWith(KNOWLEDGE_DIR)) {
|
|
186
|
+
return NextResponse.json({ error: 'Invalid path' }, { status: 400 });
|
|
187
|
+
}
|
|
188
|
+
if (!fs.existsSync(filePath)) {
|
|
189
|
+
return NextResponse.json({ error: 'File not found' }, { status: 404 });
|
|
190
|
+
}
|
|
191
|
+
fs.unlinkSync(filePath);
|
|
192
|
+
return NextResponse.json({ success: true });
|
|
193
|
+
} catch (error) {
|
|
194
|
+
return NextResponse.json({ error: error.message }, { status: 500 });
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// ─── Helpers ────────────────────────────────────────────────────────────────
|
|
199
|
+
|
|
200
|
+
function readMetadata(kiDir) {
|
|
201
|
+
const metaPath = path.join(kiDir, 'metadata.json');
|
|
202
|
+
if (!fs.existsSync(metaPath)) return { title: '', summary: '', references: [] };
|
|
203
|
+
try {
|
|
204
|
+
return JSON.parse(fs.readFileSync(metaPath, 'utf-8'));
|
|
205
|
+
} catch {
|
|
206
|
+
return { title: '', summary: '', references: [] };
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
function listArtifacts(kiDir) {
|
|
211
|
+
const results = [];
|
|
212
|
+
function walk(dir, prefix) {
|
|
213
|
+
if (!fs.existsSync(dir)) return;
|
|
214
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
215
|
+
for (const entry of entries) {
|
|
216
|
+
const relPath = prefix ? `${prefix}/${entry.name}` : entry.name;
|
|
217
|
+
if (entry.name === 'metadata.json') continue;
|
|
218
|
+
if (entry.name === '.DS_Store') continue;
|
|
219
|
+
if (entry.isDirectory()) {
|
|
220
|
+
walk(path.join(dir, entry.name), relPath);
|
|
221
|
+
} else {
|
|
222
|
+
const stat = fs.statSync(path.join(dir, entry.name));
|
|
223
|
+
results.push({
|
|
224
|
+
path: relPath,
|
|
225
|
+
name: entry.name,
|
|
226
|
+
size: stat.size,
|
|
227
|
+
updatedAt: stat.mtime.toISOString(),
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
walk(kiDir, '');
|
|
233
|
+
return results;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
function countArtifacts(kiDir) {
|
|
237
|
+
const artifactsDir = path.join(kiDir, 'artifacts');
|
|
238
|
+
if (!fs.existsSync(artifactsDir)) return 0;
|
|
239
|
+
let count = 0;
|
|
240
|
+
function walk(dir) {
|
|
241
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
242
|
+
for (const entry of entries) {
|
|
243
|
+
if (entry.name === '.DS_Store') continue;
|
|
244
|
+
if (entry.isDirectory()) {
|
|
245
|
+
walk(path.join(dir, entry.name));
|
|
246
|
+
} else {
|
|
247
|
+
count++;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
walk(artifactsDir);
|
|
252
|
+
return count;
|
|
253
|
+
}
|