@leejungkiin/awkit 1.1.6 → 1.1.9

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 (84) hide show
  1. package/README.md +51 -1
  2. package/bin/awk.js +2 -2
  3. package/core/GEMINI.md +45 -7
  4. package/package.json +8 -5
  5. package/skill-packs/neural-memory/skills/nm-memory-sync/SKILL.md +14 -1
  6. package/skills/ab-test-store-listing/SKILL.md +220 -0
  7. package/skills/android-aso/SKILL.md +197 -0
  8. package/skills/app-analytics/SKILL.md +210 -0
  9. package/skills/app-clips/SKILL.md +163 -0
  10. package/skills/app-icon-optimization/SKILL.md +170 -0
  11. package/skills/app-launch/SKILL.md +153 -0
  12. package/skills/app-marketing-context/SKILL.md +129 -0
  13. package/skills/app-store-featured/SKILL.md +213 -0
  14. package/skills/apple-search-ads/SKILL.md +205 -0
  15. package/skills/asc-metrics/SKILL.md +157 -0
  16. package/skills/aso-audit/SKILL.md +179 -0
  17. package/skills/competitor-analysis/SKILL.md +163 -0
  18. package/skills/competitor-tracking/SKILL.md +185 -0
  19. package/skills/crash-analytics/SKILL.md +181 -0
  20. package/skills/gitnexus-intelligence/SKILL.md +224 -0
  21. package/skills/in-app-events/SKILL.md +176 -0
  22. package/skills/keyword-research/SKILL.md +141 -0
  23. package/skills/localization/SKILL.md +165 -0
  24. package/skills/market-movers/SKILL.md +137 -0
  25. package/skills/market-pulse/SKILL.md +170 -0
  26. package/skills/metadata-optimization/SKILL.md +170 -0
  27. package/skills/monetization-strategy/SKILL.md +175 -0
  28. package/skills/onboarding-optimization/SKILL.md +194 -0
  29. package/skills/orchestrator/SKILL.md +306 -25
  30. package/skills/press-and-pr/SKILL.md +204 -0
  31. package/skills/rating-prompt-strategy/SKILL.md +184 -0
  32. package/skills/retention-optimization/SKILL.md +165 -0
  33. package/skills/review-management/SKILL.md +154 -0
  34. package/skills/screenshot-optimization/SKILL.md +167 -0
  35. package/skills/seasonal-aso/SKILL.md +141 -0
  36. package/skills/spec-gate/SKILL.md +312 -0
  37. package/skills/subscription-lifecycle/SKILL.md +206 -0
  38. package/skills/swiftui-pro/references/design.md +44 -0
  39. package/skills/symphony-enforcer/SKILL.md +92 -11
  40. package/skills/symphony-orchestrator/SKILL.md +9 -7
  41. package/skills/systematic-debugging/SKILL.md +32 -7
  42. package/skills/ua-campaign/SKILL.md +207 -0
  43. package/skills/verification-gate/SKILL.md +23 -2
  44. package/workflows/gitnexus.md +123 -0
  45. package/symphony/LICENSE +0 -21
  46. package/symphony/README.md +0 -178
  47. package/symphony/app/api/agents/route.js +0 -152
  48. package/symphony/app/api/events/route.js +0 -22
  49. package/symphony/app/api/knowledge/route.js +0 -253
  50. package/symphony/app/api/locks/route.js +0 -29
  51. package/symphony/app/api/notes/route.js +0 -125
  52. package/symphony/app/api/preflight/route.js +0 -23
  53. package/symphony/app/api/projects/route.js +0 -116
  54. package/symphony/app/api/roles/route.js +0 -134
  55. package/symphony/app/api/skills/route.js +0 -82
  56. package/symphony/app/api/status/route.js +0 -18
  57. package/symphony/app/api/tasks/route.js +0 -157
  58. package/symphony/app/api/workflows/route.js +0 -61
  59. package/symphony/app/api/workspaces/route.js +0 -15
  60. package/symphony/app/globals.css +0 -2605
  61. package/symphony/app/layout.js +0 -20
  62. package/symphony/app/page.js +0 -2122
  63. package/symphony/cli/index.js +0 -1060
  64. package/symphony/core/agent-manager.js +0 -357
  65. package/symphony/core/context-bus.js +0 -100
  66. package/symphony/core/db.js +0 -223
  67. package/symphony/core/file-lock-manager.js +0 -154
  68. package/symphony/core/merge-pipeline.js +0 -234
  69. package/symphony/core/orchestrator.js +0 -236
  70. package/symphony/core/task-manager.js +0 -335
  71. package/symphony/core/workspace-manager.js +0 -168
  72. package/symphony/jsconfig.json +0 -7
  73. package/symphony/lib/core.mjs +0 -1034
  74. package/symphony/mcp/index.js +0 -29
  75. package/symphony/mcp/server.js +0 -110
  76. package/symphony/mcp/tools/context.js +0 -80
  77. package/symphony/mcp/tools/locks.js +0 -99
  78. package/symphony/mcp/tools/status.js +0 -82
  79. package/symphony/mcp/tools/tasks.js +0 -216
  80. package/symphony/mcp/tools/workspace.js +0 -143
  81. package/symphony/next.config.mjs +0 -7
  82. package/symphony/package.json +0 -53
  83. package/symphony/scripts/postinstall.js +0 -49
  84. package/symphony/symphony.config.js +0 -41
@@ -1,125 +0,0 @@
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
- }
@@ -1,23 +0,0 @@
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
- }
@@ -1,116 +0,0 @@
1
- import { NextResponse } from 'next/server';
2
- import { listProjects, createProject, updateProject, setActiveProject, deleteProject, getProjectStats, migrateFromLegacy } from '../../../lib/core.mjs';
3
-
4
- // GET /api/projects — List all projects (with optional stats)
5
- export async function GET(request) {
6
- try {
7
- const { searchParams } = new URL(request.url);
8
- const withStats = searchParams.get('stats') === 'true';
9
-
10
- if (withStats) {
11
- const stats = getProjectStats();
12
- return NextResponse.json({ projects: stats });
13
- }
14
-
15
- const projects = listProjects();
16
- return NextResponse.json({ projects });
17
- } catch (error) {
18
- return NextResponse.json(
19
- { error: error.message },
20
- { status: 500 }
21
- );
22
- }
23
- }
24
-
25
- // POST /api/projects — Register a project or trigger migration
26
- export async function POST(request) {
27
- try {
28
- const body = await request.json();
29
-
30
- // Special action: migrate from legacy DB
31
- if (body.action === 'migrate') {
32
- const result = migrateFromLegacy();
33
- return NextResponse.json(result);
34
- }
35
-
36
- if (!body.id || !body.name) {
37
- return NextResponse.json(
38
- { error: 'id and name are required' },
39
- { status: 400 }
40
- );
41
- }
42
-
43
- const project = createProject({
44
- id: body.id,
45
- name: body.name,
46
- projectPath: body.path,
47
- icon: body.icon,
48
- color: body.color,
49
- });
50
-
51
- return NextResponse.json({ project }, { status: 201 });
52
- } catch (error) {
53
- return NextResponse.json(
54
- { error: error.message },
55
- { status: 500 }
56
- );
57
- }
58
- }
59
-
60
- // PATCH /api/projects — Update project or set active
61
- export async function PATCH(request) {
62
- try {
63
- const body = await request.json();
64
-
65
- if (!body.id) {
66
- return NextResponse.json(
67
- { error: 'Project ID is required' },
68
- { status: 400 }
69
- );
70
- }
71
-
72
- // Set active project
73
- if (body.action === 'activate') {
74
- const project = setActiveProject(body.id);
75
- return NextResponse.json({ project });
76
- }
77
-
78
- // Update project fields
79
- const project = updateProject(body.id, {
80
- name: body.name,
81
- path: body.path,
82
- icon: body.icon,
83
- color: body.color,
84
- });
85
-
86
- return NextResponse.json({ project });
87
- } catch (error) {
88
- return NextResponse.json(
89
- { error: error.message },
90
- { status: 500 }
91
- );
92
- }
93
- }
94
-
95
- // DELETE /api/projects — Remove a project
96
- export async function DELETE(request) {
97
- try {
98
- const { searchParams } = new URL(request.url);
99
- const id = searchParams.get('id');
100
-
101
- if (!id) {
102
- return NextResponse.json(
103
- { error: 'Project ID is required' },
104
- { status: 400 }
105
- );
106
- }
107
-
108
- deleteProject(id);
109
- return NextResponse.json({ success: true });
110
- } catch (error) {
111
- return NextResponse.json(
112
- { error: error.message },
113
- { status: 500 }
114
- );
115
- }
116
- }
@@ -1,134 +0,0 @@
1
- /**
2
- * Roles API — CRUD for role manifest (agent-skill mapping)
3
- * GET /api/roles — list all roles from role-manifest.json
4
- * POST /api/roles — create a new role
5
- * PATCH /api/roles — update an existing role
6
- * DELETE /api/roles — delete a role
7
- */
8
- import { NextResponse } from 'next/server';
9
- import { readFileSync, writeFileSync, existsSync } from 'fs';
10
- import { join } from 'path';
11
- import { homedir } from 'os';
12
-
13
- const MANIFEST_PATH = join(homedir(), '.gemini', 'antigravity', 'role-manifest.json');
14
-
15
- function readManifest() {
16
- if (!existsSync(MANIFEST_PATH)) {
17
- return { version: '1.0', description: 'Maps task phases to agent roles', roles: {}, shared: { auto_skills: [], common_workflows: [] } };
18
- }
19
- return JSON.parse(readFileSync(MANIFEST_PATH, 'utf-8'));
20
- }
21
-
22
- function writeManifest(manifest) {
23
- writeFileSync(MANIFEST_PATH, JSON.stringify(manifest, null, 4), 'utf-8');
24
- }
25
-
26
- export async function GET() {
27
- try {
28
- const manifest = readManifest();
29
- return NextResponse.json(manifest);
30
- } catch (err) {
31
- return NextResponse.json({ error: err.message }, { status: 500 });
32
- }
33
- }
34
-
35
- // POST /api/roles — Create a new role
36
- export async function POST(request) {
37
- try {
38
- const body = await request.json();
39
- const { key, name, icon, color, skills, workflows, match } = body;
40
-
41
- if (!key || !name) {
42
- return NextResponse.json({ error: 'key and name are required' }, { status: 400 });
43
- }
44
-
45
- const manifest = readManifest();
46
- if (manifest.roles[key]) {
47
- return NextResponse.json({ error: `Role "${key}" already exists` }, { status: 409 });
48
- }
49
-
50
- manifest.roles[key] = {
51
- name,
52
- icon: icon || '🎭',
53
- color: color || '#8888a0',
54
- skills: skills || [],
55
- workflows: workflows || [],
56
- match: {
57
- phases: match?.phases || [],
58
- keywords: match?.keywords || [],
59
- },
60
- };
61
-
62
- writeManifest(manifest);
63
- return NextResponse.json({ role: { key, ...manifest.roles[key] } }, { status: 201 });
64
- } catch (err) {
65
- return NextResponse.json({ error: err.message }, { status: 500 });
66
- }
67
- }
68
-
69
- // PATCH /api/roles — Update an existing role (or shared section)
70
- export async function PATCH(request) {
71
- try {
72
- const body = await request.json();
73
- const { key, ...fields } = body;
74
-
75
- if (!key) {
76
- return NextResponse.json({ error: 'key is required' }, { status: 400 });
77
- }
78
-
79
- const manifest = readManifest();
80
-
81
- // Update shared section
82
- if (key === '__shared__') {
83
- if (fields.auto_skills) manifest.shared.auto_skills = fields.auto_skills;
84
- if (fields.common_workflows) manifest.shared.common_workflows = fields.common_workflows;
85
- writeManifest(manifest);
86
- return NextResponse.json({ shared: manifest.shared });
87
- }
88
-
89
- if (!manifest.roles[key]) {
90
- return NextResponse.json({ error: `Role "${key}" not found` }, { status: 404 });
91
- }
92
-
93
- const role = manifest.roles[key];
94
- if (fields.name !== undefined) role.name = fields.name;
95
- if (fields.icon !== undefined) role.icon = fields.icon;
96
- if (fields.color !== undefined) role.color = fields.color;
97
- if (fields.skills !== undefined) role.skills = fields.skills;
98
- if (fields.workflows !== undefined) role.workflows = fields.workflows;
99
- if (fields.match !== undefined) {
100
- role.match = {
101
- phases: fields.match.phases ?? role.match?.phases ?? [],
102
- keywords: fields.match.keywords ?? role.match?.keywords ?? [],
103
- };
104
- }
105
-
106
- writeManifest(manifest);
107
- return NextResponse.json({ role: { key, ...role } });
108
- } catch (err) {
109
- return NextResponse.json({ error: err.message }, { status: 500 });
110
- }
111
- }
112
-
113
- // DELETE /api/roles — Delete a role
114
- export async function DELETE(request) {
115
- try {
116
- const body = await request.json();
117
- const { key } = body;
118
-
119
- if (!key) {
120
- return NextResponse.json({ error: 'key is required' }, { status: 400 });
121
- }
122
-
123
- const manifest = readManifest();
124
- if (!manifest.roles[key]) {
125
- return NextResponse.json({ error: `Role "${key}" not found` }, { status: 404 });
126
- }
127
-
128
- delete manifest.roles[key];
129
- writeManifest(manifest);
130
- return NextResponse.json({ success: true, deleted: key });
131
- } catch (err) {
132
- return NextResponse.json({ error: err.message }, { status: 500 });
133
- }
134
- }
@@ -1,82 +0,0 @@
1
- /**
2
- * Skills Discovery API — Scans ~/.gemini/antigravity/skills/ directory
3
- * GET /api/skills — list all available skills with metadata from SKILL.md frontmatter
4
- */
5
- import { NextResponse } from 'next/server';
6
- import { readdirSync, readFileSync, existsSync, statSync } from 'fs';
7
- import { join } from 'path';
8
- import { homedir } from 'os';
9
-
10
- const SKILLS_DIR = join(homedir(), '.gemini', 'antigravity', 'skills');
11
-
12
- function parseFrontmatter(content) {
13
- const match = content.match(/^---\n([\s\S]*?)\n---/);
14
- if (!match) return {};
15
- const fm = {};
16
- match[1].split('\n').forEach(line => {
17
- const idx = line.indexOf(':');
18
- if (idx > 0) {
19
- const key = line.substring(0, idx).trim();
20
- let val = line.substring(idx + 1).trim();
21
- // Remove quotes
22
- if ((val.startsWith('"') && val.endsWith('"')) ||
23
- (val.startsWith("'") && val.endsWith("'"))) {
24
- val = val.slice(1, -1);
25
- }
26
- // Skip multi-line YAML values (e.g., description: >-)
27
- if (val !== '>-' && val !== '|') {
28
- fm[key] = val;
29
- }
30
- }
31
- });
32
- return fm;
33
- }
34
-
35
- export async function GET() {
36
- try {
37
- if (!existsSync(SKILLS_DIR)) {
38
- return NextResponse.json({ skills: [], total: 0 });
39
- }
40
-
41
- const entries = readdirSync(SKILLS_DIR, { withFileTypes: true });
42
- const skills = [];
43
-
44
- for (const entry of entries) {
45
- if (!entry.isDirectory()) continue;
46
-
47
- const skillMd = join(SKILLS_DIR, entry.name, 'SKILL.md');
48
- if (!existsSync(skillMd)) continue;
49
-
50
- const content = readFileSync(skillMd, 'utf-8');
51
- const fm = parseFrontmatter(content);
52
-
53
- // Extract first paragraph as description if not in frontmatter
54
- let description = fm.description || '';
55
- if (!description) {
56
- const lines = content.split('\n');
57
- const descLine = lines.find(l => l.trim() && !l.startsWith('#') && !l.startsWith('---'));
58
- description = descLine ? descLine.trim() : '';
59
- }
60
-
61
- skills.push({
62
- id: entry.name,
63
- name: fm.name || entry.name,
64
- description: description.substring(0, 200),
65
- type: fm.trigger === 'conditional' ? 'manual' : (fm.type || 'auto'),
66
- version: fm.version || null,
67
- priority: fm.priority || null,
68
- });
69
- }
70
-
71
- // Sort: auto skills first, then manual
72
- skills.sort((a, b) => {
73
- if (a.type === 'auto' && b.type !== 'auto') return -1;
74
- if (a.type !== 'auto' && b.type === 'auto') return 1;
75
- return a.name.localeCompare(b.name);
76
- });
77
-
78
- return NextResponse.json({ skills, total: skills.length });
79
- } catch (err) {
80
- return NextResponse.json({ error: err.message }, { status: 500 });
81
- }
82
- }
@@ -1,18 +0,0 @@
1
- import { NextResponse } from 'next/server';
2
- import { getSystemStatus } from '../../../lib/core.mjs';
3
-
4
- // GET /api/status — Full system status (scoped by project)
5
- export async function GET(request) {
6
- try {
7
- const { searchParams } = new URL(request.url);
8
- const project = searchParams.get('project') || undefined;
9
-
10
- const status = getSystemStatus(project);
11
- return NextResponse.json(status);
12
- } catch (error) {
13
- return NextResponse.json(
14
- { error: error.message },
15
- { status: 500 }
16
- );
17
- }
18
- }
@@ -1,157 +0,0 @@
1
- import { NextResponse } from 'next/server';
2
- import { listTasks, getTaskStats, createTask, updateTask, deleteTask, approveTask, bulkApprove, reopenTask, reorderTasks, claimTask, completeTask, abandonTask } from '../../../lib/core.mjs';
3
-
4
- // GET /api/tasks — List tasks (scoped by project)
5
- export async function GET(request) {
6
- try {
7
- const { searchParams } = new URL(request.url);
8
- const status = searchParams.get('status') || undefined;
9
- const project = searchParams.get('project') || undefined;
10
- const limit = searchParams.get('limit') || '50';
11
-
12
- const tasks = listTasks({ status, project, limit: parseInt(limit) });
13
- const stats = getTaskStats(project);
14
-
15
- return NextResponse.json({ tasks, stats });
16
- } catch (error) {
17
- return NextResponse.json(
18
- { error: error.message },
19
- { status: 500 }
20
- );
21
- }
22
- }
23
-
24
- // POST /api/tasks — Create task
25
- export async function POST(request) {
26
- try {
27
- const body = await request.json();
28
-
29
- if (!body.title) {
30
- return NextResponse.json(
31
- { error: 'Title is required' },
32
- { status: 400 }
33
- );
34
- }
35
-
36
- const task = createTask(body.title, {
37
- description: body.description,
38
- priority: body.priority || 2,
39
- acceptance: body.acceptance,
40
- phase: body.phase,
41
- estimatedFiles: body.estimatedFiles,
42
- isDraft: body.isDraft,
43
- projectId: body.project_id || body.projectId,
44
- conversationId: body.conversation_id || body.conversationId,
45
- });
46
-
47
- return NextResponse.json({ task }, { status: 201 });
48
- } catch (error) {
49
- return NextResponse.json(
50
- { error: error.message },
51
- { status: 500 }
52
- );
53
- }
54
- }
55
-
56
- // PATCH /api/tasks — Update task (edit fields, approve, reopen, reorder)
57
- export async function PATCH(request) {
58
- try {
59
- const body = await request.json();
60
-
61
- // Batch approve
62
- if (body.action === 'bulk_approve' && Array.isArray(body.ids)) {
63
- const count = bulkApprove(body.ids);
64
- return NextResponse.json({ approved: count });
65
- }
66
-
67
- // Reorder
68
- if (body.action === 'reorder' && Array.isArray(body.orderedIds)) {
69
- reorderTasks(body.orderedIds);
70
- return NextResponse.json({ success: true });
71
- }
72
-
73
- if (!body.id) {
74
- return NextResponse.json(
75
- { error: 'Task ID is required' },
76
- { status: 400 }
77
- );
78
- }
79
-
80
- // Approve draft → ready
81
- if (body.action === 'approve') {
82
- const task = approveTask(body.id);
83
- return NextResponse.json({ task });
84
- }
85
-
86
- // Claim task (ready → claimed)
87
- if (body.action === 'claim') {
88
- const task = claimTask(body.id, body.agent_id || 'api-user');
89
- return NextResponse.json({ task });
90
- }
91
-
92
- // Complete task (→ done)
93
- if (body.action === 'complete') {
94
- const task = completeTask(body.id, body.summary || '');
95
- return NextResponse.json({ task });
96
- }
97
-
98
- // Abandon task (→ ready)
99
- if (body.action === 'abandon') {
100
- const task = abandonTask(body.id, body.reason || '');
101
- return NextResponse.json({ task });
102
- }
103
-
104
- // Shorthand: { id, status: "done" } — used by GEMINI.md Gate 2
105
- if (body.status === 'done') {
106
- const task = completeTask(body.id, body.summary || '');
107
- return NextResponse.json({ task });
108
- }
109
-
110
- // Reopen done → ready
111
- if (body.action === 'reopen') {
112
- const task = reopenTask(body.id);
113
- return NextResponse.json({ task });
114
- }
115
-
116
- // General field update
117
- const task = updateTask(body.id, {
118
- title: body.title,
119
- description: body.description,
120
- priority: body.priority,
121
- acceptance: body.acceptance,
122
- phase: body.phase,
123
- sort_order: body.sort_order,
124
- project_id: body.project_id,
125
- agent_id: body.agent_id,
126
- });
127
-
128
- return NextResponse.json({ task });
129
- } catch (error) {
130
- return NextResponse.json(
131
- { error: error.message },
132
- { status: 500 }
133
- );
134
- }
135
- }
136
-
137
- // DELETE /api/tasks — Delete task
138
- export async function DELETE(request) {
139
- try {
140
- const body = await request.json();
141
-
142
- if (!body.id) {
143
- return NextResponse.json(
144
- { error: 'Task ID is required' },
145
- { status: 400 }
146
- );
147
- }
148
-
149
- deleteTask(body.id);
150
- return NextResponse.json({ success: true });
151
- } catch (error) {
152
- return NextResponse.json(
153
- { error: error.message },
154
- { status: 500 }
155
- );
156
- }
157
- }