@leejungkiin/awkit 1.1.0 → 1.1.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.
- package/README.md +3 -3
- package/VERSION +1 -1
- package/bin/awf.js +1 -1
- package/bin/awk.js +237 -26
- package/core/AGENTS.md +8 -9
- package/core/GEMINI.md +74 -199
- package/package.json +3 -2
- 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 +29 -155
- 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 +362 -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/specs/PROJECT.md +50 -0
- package/templates/specs/ROADMAP.md +79 -0
- package/templates/specs/TECH-SPEC.md +81 -0
- package/templates/specs/task-spec-template.xml +65 -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/init.md +103 -91
- 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,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
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { NextResponse } from 'next/server';
|
|
2
|
+
import { forceReleaseLock } from '../../../lib/core.mjs';
|
|
3
|
+
|
|
4
|
+
// DELETE /api/locks — Force-release a file lock
|
|
5
|
+
export async function DELETE(request) {
|
|
6
|
+
try {
|
|
7
|
+
const body = await request.json();
|
|
8
|
+
|
|
9
|
+
if (!body.file) {
|
|
10
|
+
return NextResponse.json(
|
|
11
|
+
{ error: 'File path is required' },
|
|
12
|
+
{ status: 400 }
|
|
13
|
+
);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const released = forceReleaseLock(body.file);
|
|
17
|
+
|
|
18
|
+
return NextResponse.json({
|
|
19
|
+
released,
|
|
20
|
+
file: body.file,
|
|
21
|
+
message: released ? 'Lock released' : 'No lock found',
|
|
22
|
+
});
|
|
23
|
+
} catch (error) {
|
|
24
|
+
return NextResponse.json(
|
|
25
|
+
{ error: error.message },
|
|
26
|
+
{ status: 500 }
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { NextResponse } from 'next/server';
|
|
2
|
+
import { createNote, listNotes, getNote, updateNote, deleteNote } from '../../../lib/core.mjs';
|
|
3
|
+
|
|
4
|
+
// GET /api/notes — List notes with filters
|
|
5
|
+
export async function GET(request) {
|
|
6
|
+
try {
|
|
7
|
+
const { searchParams } = new URL(request.url);
|
|
8
|
+
const projectId = searchParams.get('projectId') || undefined;
|
|
9
|
+
const type = searchParams.get('type') || undefined;
|
|
10
|
+
const conversationId = searchParams.get('conversationId') || undefined;
|
|
11
|
+
const taskId = searchParams.get('taskId') || undefined;
|
|
12
|
+
const limit = searchParams.get('limit') || '50';
|
|
13
|
+
|
|
14
|
+
const notes = listNotes({
|
|
15
|
+
projectId,
|
|
16
|
+
type,
|
|
17
|
+
conversationId,
|
|
18
|
+
taskId,
|
|
19
|
+
limit: parseInt(limit),
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
return NextResponse.json({ notes });
|
|
23
|
+
} catch (error) {
|
|
24
|
+
return NextResponse.json(
|
|
25
|
+
{ error: error.message },
|
|
26
|
+
{ status: 500 }
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// POST /api/notes — Create a note
|
|
32
|
+
export async function POST(request) {
|
|
33
|
+
try {
|
|
34
|
+
const body = await request.json();
|
|
35
|
+
|
|
36
|
+
if (!body.project_id && !body.projectId) {
|
|
37
|
+
return NextResponse.json(
|
|
38
|
+
{ error: 'project_id is required' },
|
|
39
|
+
{ status: 400 }
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
if (!body.type) {
|
|
43
|
+
return NextResponse.json(
|
|
44
|
+
{ error: 'type is required' },
|
|
45
|
+
{ status: 400 }
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
if (!body.title) {
|
|
49
|
+
return NextResponse.json(
|
|
50
|
+
{ error: 'title is required' },
|
|
51
|
+
{ status: 400 }
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const note = createNote({
|
|
56
|
+
projectId: body.project_id || body.projectId,
|
|
57
|
+
type: body.type,
|
|
58
|
+
title: body.title,
|
|
59
|
+
content: body.content,
|
|
60
|
+
filePath: body.file_path || body.filePath,
|
|
61
|
+
conversationId: body.conversation_id || body.conversationId,
|
|
62
|
+
taskId: body.task_id || body.taskId,
|
|
63
|
+
metadata: body.metadata,
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
return NextResponse.json({ note }, { status: 201 });
|
|
67
|
+
} catch (error) {
|
|
68
|
+
return NextResponse.json(
|
|
69
|
+
{ error: error.message },
|
|
70
|
+
{ status: error.message.includes('required') ? 400 : 500 }
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// PATCH /api/notes — Update a note
|
|
76
|
+
export async function PATCH(request) {
|
|
77
|
+
try {
|
|
78
|
+
const body = await request.json();
|
|
79
|
+
|
|
80
|
+
if (!body.id) {
|
|
81
|
+
return NextResponse.json(
|
|
82
|
+
{ error: 'Note ID is required' },
|
|
83
|
+
{ status: 400 }
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const note = updateNote(body.id, {
|
|
88
|
+
title: body.title,
|
|
89
|
+
content: body.content,
|
|
90
|
+
file_path: body.file_path || body.filePath,
|
|
91
|
+
task_id: body.task_id || body.taskId,
|
|
92
|
+
metadata: body.metadata,
|
|
93
|
+
conversation_id: body.conversation_id || body.conversationId,
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
return NextResponse.json({ note });
|
|
97
|
+
} catch (error) {
|
|
98
|
+
return NextResponse.json(
|
|
99
|
+
{ error: error.message },
|
|
100
|
+
{ status: error.message.includes('not found') ? 404 : 500 }
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// DELETE /api/notes — Delete a note
|
|
106
|
+
export async function DELETE(request) {
|
|
107
|
+
try {
|
|
108
|
+
const body = await request.json();
|
|
109
|
+
|
|
110
|
+
if (!body.id) {
|
|
111
|
+
return NextResponse.json(
|
|
112
|
+
{ error: 'Note ID is required' },
|
|
113
|
+
{ status: 400 }
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
deleteNote(body.id);
|
|
118
|
+
return NextResponse.json({ success: true });
|
|
119
|
+
} catch (error) {
|
|
120
|
+
return NextResponse.json(
|
|
121
|
+
{ error: error.message },
|
|
122
|
+
{ status: error.message.includes('not found') ? 404 : 500 }
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { NextResponse } from 'next/server';
|
|
2
|
+
import { getPreflightStatus } from '../../../lib/core.mjs';
|
|
3
|
+
|
|
4
|
+
// GET /api/preflight — Single-call gate check for AI agents
|
|
5
|
+
// Returns server health, active project, task summary, and gate status
|
|
6
|
+
export async function GET(request) {
|
|
7
|
+
try {
|
|
8
|
+
const { searchParams } = new URL(request.url);
|
|
9
|
+
const project = searchParams.get('project') || undefined;
|
|
10
|
+
|
|
11
|
+
const preflight = getPreflightStatus(project);
|
|
12
|
+
return NextResponse.json(preflight);
|
|
13
|
+
} catch (error) {
|
|
14
|
+
return NextResponse.json(
|
|
15
|
+
{
|
|
16
|
+
gate_status: 'FAIL',
|
|
17
|
+
error: error.message,
|
|
18
|
+
server: { status: 'error' },
|
|
19
|
+
},
|
|
20
|
+
{ status: 500 }
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
}
|