@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.
Files changed (119) hide show
  1. package/README.md +3 -3
  2. package/VERSION +1 -1
  3. package/bin/awf.js +1 -1
  4. package/bin/awk.js +237 -26
  5. package/core/AGENTS.md +8 -9
  6. package/core/GEMINI.md +74 -199
  7. package/package.json +3 -2
  8. package/skill-packs/neural-memory/skills/nm-memory-sync/SKILL.md +2 -2
  9. package/skills/CATALOG.md +3 -2
  10. package/skills/README.md +109 -0
  11. package/skills/android-re-analyzer/SKILL.md +238 -0
  12. package/skills/android-re-analyzer/references/api-extraction-patterns.md +119 -0
  13. package/skills/android-re-analyzer/references/call-flow-analysis.md +176 -0
  14. package/skills/android-re-analyzer/references/fernflower-usage.md +115 -0
  15. package/skills/android-re-analyzer/references/jadx-usage.md +116 -0
  16. package/skills/android-re-analyzer/references/setup-guide.md +221 -0
  17. package/skills/android-re-analyzer/scripts/check-deps.sh +129 -0
  18. package/skills/android-re-analyzer/scripts/decompile.sh +375 -0
  19. package/skills/android-re-analyzer/scripts/find-api-calls.sh +118 -0
  20. package/skills/android-re-analyzer/scripts/install-dep.sh +448 -0
  21. package/skills/awf-session-restore/SKILL.md +108 -184
  22. package/skills/beads-manager/SKILL.md +2 -2
  23. package/skills/brainstorm-agent/SKILL.md +47 -2
  24. package/skills/gemini-conductor/SKILL.md +234 -0
  25. package/skills/memory-sync/SKILL.md +29 -1
  26. package/skills/nm-memory-sync/SKILL.md +2 -2
  27. package/skills/orchestrator/SKILL.md +29 -155
  28. package/skills/skills/nm-memory-sync/SKILL.md +2 -2
  29. package/skills/smali-to-kotlin/SKILL.md +1 -1
  30. package/skills/smali-to-swift/SKILL.md +1 -1
  31. package/skills/swiftui-pro/SKILL.md +108 -0
  32. package/skills/swiftui-pro/agents/openai.yaml +10 -0
  33. package/skills/swiftui-pro/assets/swiftui-pro-icon.png +0 -0
  34. package/skills/swiftui-pro/assets/swiftui-pro-icon.svg +29 -0
  35. package/skills/swiftui-pro/references/accessibility.md +13 -0
  36. package/skills/swiftui-pro/references/api.md +39 -0
  37. package/skills/swiftui-pro/references/data.md +43 -0
  38. package/skills/swiftui-pro/references/design.md +31 -0
  39. package/skills/swiftui-pro/references/hygiene.md +9 -0
  40. package/skills/swiftui-pro/references/navigation.md +14 -0
  41. package/skills/swiftui-pro/references/performance.md +46 -0
  42. package/skills/swiftui-pro/references/swift.md +56 -0
  43. package/skills/swiftui-pro/references/views.md +35 -0
  44. package/skills/symphony-enforcer/SKILL.md +362 -0
  45. package/skills/symphony-orchestrator/SKILL.md +301 -0
  46. package/skills/telegram-notify/SKILL.md +57 -0
  47. package/symphony/LICENSE +21 -0
  48. package/symphony/README.md +178 -0
  49. package/symphony/app/api/agents/route.js +152 -0
  50. package/symphony/app/api/events/route.js +22 -0
  51. package/symphony/app/api/knowledge/route.js +253 -0
  52. package/symphony/app/api/locks/route.js +29 -0
  53. package/symphony/app/api/notes/route.js +125 -0
  54. package/symphony/app/api/preflight/route.js +23 -0
  55. package/symphony/app/api/projects/route.js +116 -0
  56. package/symphony/app/api/roles/route.js +134 -0
  57. package/symphony/app/api/skills/route.js +82 -0
  58. package/symphony/app/api/status/route.js +18 -0
  59. package/symphony/app/api/tasks/route.js +157 -0
  60. package/symphony/app/api/workflows/route.js +61 -0
  61. package/symphony/app/api/workspaces/route.js +15 -0
  62. package/symphony/app/globals.css +2605 -0
  63. package/symphony/app/layout.js +20 -0
  64. package/symphony/app/page.js +2122 -0
  65. package/symphony/cli/index.js +1060 -0
  66. package/symphony/core/agent-manager.js +357 -0
  67. package/symphony/core/context-bus.js +100 -0
  68. package/symphony/core/db.js +223 -0
  69. package/symphony/core/file-lock-manager.js +154 -0
  70. package/symphony/core/merge-pipeline.js +234 -0
  71. package/symphony/core/orchestrator.js +236 -0
  72. package/symphony/core/task-manager.js +335 -0
  73. package/symphony/core/workspace-manager.js +168 -0
  74. package/symphony/jsconfig.json +7 -0
  75. package/symphony/lib/core.mjs +1034 -0
  76. package/symphony/mcp/index.js +29 -0
  77. package/symphony/mcp/server.js +110 -0
  78. package/symphony/mcp/tools/context.js +80 -0
  79. package/symphony/mcp/tools/locks.js +99 -0
  80. package/symphony/mcp/tools/status.js +82 -0
  81. package/symphony/mcp/tools/tasks.js +216 -0
  82. package/symphony/mcp/tools/workspace.js +143 -0
  83. package/symphony/next.config.mjs +7 -0
  84. package/symphony/package.json +53 -0
  85. package/symphony/scripts/postinstall.js +49 -0
  86. package/symphony/symphony.config.js +41 -0
  87. package/templates/conductor-tracks.md +38 -0
  88. package/templates/specs/PROJECT.md +50 -0
  89. package/templates/specs/ROADMAP.md +79 -0
  90. package/templates/specs/TECH-SPEC.md +81 -0
  91. package/templates/specs/task-spec-template.xml +65 -0
  92. package/templates/workflow_dual_mode_template.md +5 -5
  93. package/workflows/_uncategorized/AGENTS.md +38 -0
  94. package/workflows/_uncategorized/decompile.md +67 -0
  95. package/workflows/_uncategorized/skill-health.md +7 -7
  96. package/workflows/ads/ads-audit.md +5 -5
  97. package/workflows/ads/ads-optimize.md +10 -10
  98. package/workflows/ads/adsExpert.md +7 -7
  99. package/workflows/conductor.md +97 -0
  100. package/workflows/context/auto-implement.md +4 -4
  101. package/workflows/context/codebase-sync.md +19 -8
  102. package/workflows/context/next.md +27 -27
  103. package/workflows/context/user-intent-analysis-workflow.md +4 -4
  104. package/workflows/expert/codeExpert.md +28 -31
  105. package/workflows/expert/debugExpert.md +11 -11
  106. package/workflows/expert/planExpert.md +21 -36
  107. package/workflows/git/smart-git-ops.md +49 -6
  108. package/workflows/lifecycle/debug.md +7 -7
  109. package/workflows/lifecycle/deploy.md +10 -10
  110. package/workflows/lifecycle/init.md +103 -91
  111. package/workflows/lifecycle/master-code-workflow.md +3 -3
  112. package/workflows/lifecycle/plan.md +19 -21
  113. package/workflows/quality/audit.md +1 -1
  114. package/workflows/quality/project-audit.md +1 -1
  115. package/workflows/roles/vibe-coding-master-workflow.md +2 -2
  116. package/workflows/smart-git-ops.md +146 -0
  117. package/workflows/ui/app-screen-analyzer.md +4 -4
  118. package/workflows/ui/create-feature.md +8 -8
  119. 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
+ }