@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.
- package/README.md +51 -1
- package/bin/awk.js +2 -2
- package/core/GEMINI.md +45 -7
- package/package.json +8 -5
- package/skill-packs/neural-memory/skills/nm-memory-sync/SKILL.md +14 -1
- package/skills/ab-test-store-listing/SKILL.md +220 -0
- package/skills/android-aso/SKILL.md +197 -0
- package/skills/app-analytics/SKILL.md +210 -0
- package/skills/app-clips/SKILL.md +163 -0
- package/skills/app-icon-optimization/SKILL.md +170 -0
- package/skills/app-launch/SKILL.md +153 -0
- package/skills/app-marketing-context/SKILL.md +129 -0
- package/skills/app-store-featured/SKILL.md +213 -0
- package/skills/apple-search-ads/SKILL.md +205 -0
- package/skills/asc-metrics/SKILL.md +157 -0
- package/skills/aso-audit/SKILL.md +179 -0
- package/skills/competitor-analysis/SKILL.md +163 -0
- package/skills/competitor-tracking/SKILL.md +185 -0
- package/skills/crash-analytics/SKILL.md +181 -0
- package/skills/gitnexus-intelligence/SKILL.md +224 -0
- package/skills/in-app-events/SKILL.md +176 -0
- package/skills/keyword-research/SKILL.md +141 -0
- package/skills/localization/SKILL.md +165 -0
- package/skills/market-movers/SKILL.md +137 -0
- package/skills/market-pulse/SKILL.md +170 -0
- package/skills/metadata-optimization/SKILL.md +170 -0
- package/skills/monetization-strategy/SKILL.md +175 -0
- package/skills/onboarding-optimization/SKILL.md +194 -0
- package/skills/orchestrator/SKILL.md +306 -25
- package/skills/press-and-pr/SKILL.md +204 -0
- package/skills/rating-prompt-strategy/SKILL.md +184 -0
- package/skills/retention-optimization/SKILL.md +165 -0
- package/skills/review-management/SKILL.md +154 -0
- package/skills/screenshot-optimization/SKILL.md +167 -0
- package/skills/seasonal-aso/SKILL.md +141 -0
- package/skills/spec-gate/SKILL.md +312 -0
- package/skills/subscription-lifecycle/SKILL.md +206 -0
- package/skills/swiftui-pro/references/design.md +44 -0
- package/skills/symphony-enforcer/SKILL.md +92 -11
- package/skills/symphony-orchestrator/SKILL.md +9 -7
- package/skills/systematic-debugging/SKILL.md +32 -7
- package/skills/ua-campaign/SKILL.md +207 -0
- package/skills/verification-gate/SKILL.md +23 -2
- package/workflows/gitnexus.md +123 -0
- package/symphony/LICENSE +0 -21
- package/symphony/README.md +0 -178
- package/symphony/app/api/agents/route.js +0 -152
- package/symphony/app/api/events/route.js +0 -22
- package/symphony/app/api/knowledge/route.js +0 -253
- package/symphony/app/api/locks/route.js +0 -29
- package/symphony/app/api/notes/route.js +0 -125
- package/symphony/app/api/preflight/route.js +0 -23
- package/symphony/app/api/projects/route.js +0 -116
- package/symphony/app/api/roles/route.js +0 -134
- package/symphony/app/api/skills/route.js +0 -82
- package/symphony/app/api/status/route.js +0 -18
- package/symphony/app/api/tasks/route.js +0 -157
- package/symphony/app/api/workflows/route.js +0 -61
- package/symphony/app/api/workspaces/route.js +0 -15
- package/symphony/app/globals.css +0 -2605
- package/symphony/app/layout.js +0 -20
- package/symphony/app/page.js +0 -2122
- package/symphony/cli/index.js +0 -1060
- package/symphony/core/agent-manager.js +0 -357
- package/symphony/core/context-bus.js +0 -100
- package/symphony/core/db.js +0 -223
- package/symphony/core/file-lock-manager.js +0 -154
- package/symphony/core/merge-pipeline.js +0 -234
- package/symphony/core/orchestrator.js +0 -236
- package/symphony/core/task-manager.js +0 -335
- package/symphony/core/workspace-manager.js +0 -168
- package/symphony/jsconfig.json +0 -7
- package/symphony/lib/core.mjs +0 -1034
- package/symphony/mcp/index.js +0 -29
- package/symphony/mcp/server.js +0 -110
- package/symphony/mcp/tools/context.js +0 -80
- package/symphony/mcp/tools/locks.js +0 -99
- package/symphony/mcp/tools/status.js +0 -82
- package/symphony/mcp/tools/tasks.js +0 -216
- package/symphony/mcp/tools/workspace.js +0 -143
- package/symphony/next.config.mjs +0 -7
- package/symphony/package.json +0 -53
- package/symphony/scripts/postinstall.js +0 -49
- 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
|
-
}
|