@mrpatronz/nexusflow 2.11.0 → 2.12.0
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/LICENSE +1 -1
- package/README.md +151 -12
- package/dist/analyzers/detect-deps.js +1 -1
- package/dist/analyzers/detect-deps.js.map +1 -1
- package/dist/commands/commit.d.ts.map +1 -1
- package/dist/commands/commit.js +15 -0
- package/dist/commands/commit.js.map +1 -1
- package/dist/commands/create.d.ts +10 -1
- package/dist/commands/create.d.ts.map +1 -1
- package/dist/commands/create.js +44 -23
- package/dist/commands/create.js.map +1 -1
- package/dist/commands/finish.d.ts.map +1 -1
- package/dist/commands/finish.js +15 -0
- package/dist/commands/finish.js.map +1 -1
- package/dist/commands/flow.d.ts +13 -0
- package/dist/commands/flow.d.ts.map +1 -0
- package/dist/commands/flow.js +91 -0
- package/dist/commands/flow.js.map +1 -0
- package/dist/commands/skill.d.ts +28 -0
- package/dist/commands/skill.d.ts.map +1 -0
- package/dist/commands/skill.js +250 -0
- package/dist/commands/skill.js.map +1 -0
- package/dist/commands/status.d.ts.map +1 -1
- package/dist/commands/status.js +14 -0
- package/dist/commands/status.js.map +1 -1
- package/dist/commands/tag.d.ts +13 -0
- package/dist/commands/tag.d.ts.map +1 -0
- package/dist/commands/tag.js +179 -0
- package/dist/commands/tag.js.map +1 -0
- package/dist/commands/verify.d.ts +16 -0
- package/dist/commands/verify.d.ts.map +1 -0
- package/dist/commands/verify.js +91 -0
- package/dist/commands/verify.js.map +1 -0
- package/dist/core/adapters/local-storage.d.ts.map +1 -1
- package/dist/core/adapters/local-storage.js +2 -2
- package/dist/core/adapters/local-storage.js.map +1 -1
- package/dist/core/commit.d.ts +2 -0
- package/dist/core/commit.d.ts.map +1 -1
- package/dist/core/commit.js +15 -0
- package/dist/core/commit.js.map +1 -1
- package/dist/core/domain-catalog.d.ts +9 -0
- package/dist/core/domain-catalog.d.ts.map +1 -0
- package/dist/core/domain-catalog.js +47 -0
- package/dist/core/domain-catalog.js.map +1 -0
- package/dist/core/domain-packs.d.ts +81 -0
- package/dist/core/domain-packs.d.ts.map +1 -0
- package/dist/core/domain-packs.js +443 -0
- package/dist/core/domain-packs.js.map +1 -0
- package/dist/core/generation-lock.d.ts.map +1 -1
- package/dist/core/generation-lock.js +1 -3
- package/dist/core/generation-lock.js.map +1 -1
- package/dist/core/lifecycle.d.ts +28 -0
- package/dist/core/lifecycle.d.ts.map +1 -0
- package/dist/core/lifecycle.js +411 -0
- package/dist/core/lifecycle.js.map +1 -0
- package/dist/core/verify.d.ts +39 -0
- package/dist/core/verify.d.ts.map +1 -0
- package/dist/core/verify.js +397 -0
- package/dist/core/verify.js.map +1 -0
- package/dist/core/work-guidance.d.ts +201 -0
- package/dist/core/work-guidance.d.ts.map +1 -0
- package/dist/core/work-guidance.js +225 -0
- package/dist/core/work-guidance.js.map +1 -0
- package/dist/core/workspace-state.d.ts +24 -3
- package/dist/core/workspace-state.d.ts.map +1 -1
- package/dist/core/workspace-state.js +93 -31
- package/dist/core/workspace-state.js.map +1 -1
- package/dist/core/workspace.d.ts.map +1 -1
- package/dist/core/workspace.js +7 -0
- package/dist/core/workspace.js.map +1 -1
- package/dist/generators/base.d.ts.map +1 -1
- package/dist/generators/base.js +38 -4
- package/dist/generators/base.js.map +1 -1
- package/dist/generators/index.d.ts.map +1 -1
- package/dist/generators/index.js +4 -0
- package/dist/generators/index.js.map +1 -1
- package/dist/generators/plan-generator.d.ts.map +1 -1
- package/dist/generators/plan-generator.js +6 -1
- package/dist/generators/plan-generator.js.map +1 -1
- package/dist/generators/skills-generator.d.ts.map +1 -1
- package/dist/generators/skills-generator.js +36 -3
- package/dist/generators/skills-generator.js.map +1 -1
- package/dist/gui/assets/AgentsPage-GCFiyNSm.js +3 -0
- package/dist/gui/assets/DashboardPage-51JHl4Hg.js +1 -0
- package/dist/gui/assets/{GettingStartedPage-1MOTng3B.js → GettingStartedPage-DH-zXYgU.js} +1 -1
- package/dist/gui/assets/ProjectsPage-DUv2fhI8.js +1 -0
- package/dist/gui/assets/ScaffoldRepoInline-Asao9IqG.js +1 -0
- package/dist/gui/assets/SettingsPage--pnZKpmm.js +1 -0
- package/dist/gui/assets/SkillsPage-BPq8arO5.js +7 -0
- package/dist/gui/assets/StartWorkPage-CypGEQZ0.js +1 -0
- package/dist/gui/assets/{StrategiesPage-CdCbbIu-.js → StrategiesPage-BHAzXMnc.js} +2 -2
- package/dist/gui/assets/WorkroomsPage-BYC8c-rP.js +4 -0
- package/dist/gui/assets/WorkspacesPage-DRASo1aL.js +13 -0
- package/dist/gui/assets/{alert-dialog-u7qgb9Hq.js → alert-dialog-BD3wT0w3.js} +1 -1
- package/dist/gui/assets/badge-DTBzXOWn.js +1 -0
- package/dist/gui/assets/index-er-mkc2S.css +2 -0
- package/dist/gui/assets/index-jcGHcXQ1.js +57 -0
- package/dist/gui/assets/square-check-big-Bj8Rf_dH.js +1 -0
- package/dist/gui/assets/tabs-DfbdubLX.js +1 -0
- package/dist/gui/assets/{useAriaLabelledBy-C4zV-1Ra.js → useAriaLabelledBy-BmJErI5a.js} +1 -1
- package/dist/gui/assets/vsc-Dn-smJni.js +1 -0
- package/dist/gui/index.html +3 -5
- package/dist/http/work-guidance-routes.d.ts +4 -0
- package/dist/http/work-guidance-routes.d.ts.map +1 -0
- package/dist/http/work-guidance-routes.js +44 -0
- package/dist/http/work-guidance-routes.js.map +1 -0
- package/dist/index.js +169 -1
- package/dist/index.js.map +1 -1
- package/dist/mcp/tools.d.ts.map +1 -1
- package/dist/mcp/tools.js +183 -0
- package/dist/mcp/tools.js.map +1 -1
- package/dist/resources/contracts.d.ts.map +1 -1
- package/dist/resources/contracts.js +2 -1
- package/dist/resources/contracts.js.map +1 -1
- package/dist/resources/fs-safety.d.ts.map +1 -1
- package/dist/resources/fs-safety.js +2 -1
- package/dist/resources/fs-safety.js.map +1 -1
- package/dist/resources/materializer.d.ts +3 -0
- package/dist/resources/materializer.d.ts.map +1 -1
- package/dist/resources/materializer.js +45 -19
- package/dist/resources/materializer.js.map +1 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +327 -4
- package/dist/server.js.map +1 -1
- package/dist/static-resources/skills/contextspace-verify-release/SKILL.md +51 -0
- package/dist/static-resources/skills/contextspace-verify-release/references/evidence.md +21 -0
- package/dist/static-resources/skills/nexusflow-dev/SKILL.md +49 -0
- package/dist/static-resources/skills/nexusflow-dev/references/cli-conventions.md +15 -0
- package/dist/static-resources/skills/nexusflow-dev/references/verification-checklist.md +28 -0
- package/dist/static-resources/skills/nexusflow-lifecycle/SKILL.md +62 -0
- package/dist/static-resources/skills/nexusflow-lifecycle/references/epic-deliverables.md +22 -0
- package/dist/static-resources/skills/nexusflow-lifecycle/references/flow-selection-matrix.md +21 -0
- package/dist/static-resources/skills/nexusflow-lifecycle/references/stage-outcomes.md +23 -0
- package/dist/static-resources/workflows/epic-multi-slice.md +21 -0
- package/dist/static-resources/workflows/plan-implement-review.md +17 -6
- package/dist/static-resources/workflows/research-verify.md +15 -4
- package/dist/static-resources/workflows/solo-developer.md +11 -2
- package/dist/test-setup.d.ts +2 -0
- package/dist/test-setup.d.ts.map +1 -0
- package/dist/test-setup.js +8 -0
- package/dist/test-setup.js.map +1 -0
- package/dist/types.d.ts +175 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/resolve-workspace.d.ts.map +1 -1
- package/dist/utils/resolve-workspace.js +15 -8
- package/dist/utils/resolve-workspace.js.map +1 -1
- package/dist/utils/skills-catalog.d.ts +13 -9
- package/dist/utils/skills-catalog.d.ts.map +1 -1
- package/dist/utils/skills-catalog.js +241 -341
- package/dist/utils/skills-catalog.js.map +1 -1
- package/dist/utils/workflow-advisor.d.ts +1 -1
- package/dist/utils/workflow-advisor.d.ts.map +1 -1
- package/dist/utils/workflow-advisor.js +16 -4
- package/dist/utils/workflow-advisor.js.map +1 -1
- package/dist/utils/workflows.d.ts.map +1 -1
- package/dist/utils/workflows.js +5 -2
- package/dist/utils/workflows.js.map +1 -1
- package/dist/workrooms/contracts.d.ts +3 -3
- package/dist/workrooms/local-resources.d.ts +4 -1
- package/dist/workrooms/local-resources.d.ts.map +1 -1
- package/dist/workrooms/manager.d.ts +5 -2
- package/dist/workrooms/manager.d.ts.map +1 -1
- package/dist/workrooms/store.d.ts +2 -2
- package/package.json +3 -2
- package/resources/skills/contextspace-verify-release/SKILL.md +51 -0
- package/resources/skills/contextspace-verify-release/references/evidence.md +21 -0
- package/resources/skills/nexusflow-dev/SKILL.md +49 -0
- package/resources/skills/nexusflow-dev/references/cli-conventions.md +15 -0
- package/resources/skills/nexusflow-dev/references/verification-checklist.md +28 -0
- package/resources/skills/nexusflow-lifecycle/SKILL.md +62 -0
- package/resources/skills/nexusflow-lifecycle/references/epic-deliverables.md +22 -0
- package/resources/skills/nexusflow-lifecycle/references/flow-selection-matrix.md +21 -0
- package/resources/skills/nexusflow-lifecycle/references/stage-outcomes.md +23 -0
- package/resources/workflows/epic-multi-slice.md +21 -0
- package/resources/workflows/plan-implement-review.md +17 -6
- package/resources/workflows/research-verify.md +15 -4
- package/resources/workflows/solo-developer.md +11 -2
- package/dist/gui/assets/AgentsPage-DbivhmK0.js +0 -3
- package/dist/gui/assets/DashboardPage-D50Qrm3e.js +0 -1
- package/dist/gui/assets/NullStore-tdPen1kp.js +0 -1
- package/dist/gui/assets/ProjectsPage-srKgf7VN.js +0 -1
- package/dist/gui/assets/ScaffoldRepoInline-CP0yn5mw.js +0 -1
- package/dist/gui/assets/SettingsPage-DWmrLV8o.js +0 -1
- package/dist/gui/assets/SkillsPage-CKjTXdrd.js +0 -6
- package/dist/gui/assets/StartWorkPage-BCUnJ9k8.js +0 -1
- package/dist/gui/assets/WorkroomsPage-BcT-mGtT.js +0 -4
- package/dist/gui/assets/WorkspacesPage-CYFCm-7_.js +0 -2
- package/dist/gui/assets/badge-DwXvB44H.js +0 -1
- package/dist/gui/assets/chunk-BV7QT456-DfUBKG-z.js +0 -3
- package/dist/gui/assets/index-3tD_IeyQ.js +0 -55
- package/dist/gui/assets/index-CK5YRfPV.css +0 -2
- package/dist/gui/assets/square-check-big-BSIiYXPI.js +0 -1
- package/dist/gui/assets/tabs-BrHKp5bD.js +0 -1
- package/dist/gui/assets/vsc-D80c4gBT.js +0 -1
package/dist/server.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { registerWorkGuidanceRoutes } from './http/work-guidance-routes.js';
|
|
1
2
|
/**
|
|
2
3
|
* @module server
|
|
3
4
|
* Hono local web server for the NexusFlow GUI.
|
|
@@ -27,10 +28,11 @@ import { loadProjects, createProject, updateProject, removeProject, slugifyProje
|
|
|
27
28
|
import { getSessionCwd, isInPlace, resolveFeatureRepoPath } from './utils/feature.js';
|
|
28
29
|
import { listBranches } from './utils/git.js';
|
|
29
30
|
import { checkRepoFreshness, checkReposFreshness, fastForwardRepos, } from './utils/repo-freshness.js';
|
|
30
|
-
import { createWorkspace, listWorkspaces, loadFeatureConfig, loadWorkspaceManifest, deleteWorkspace, addRepoToWorkspace, isolateWorkspaceRepo } from './core/workspace.js';
|
|
31
|
+
import { createWorkspace, listWorkspaces, loadFeatureConfig, saveFeatureConfig, loadWorkspaceManifest, deleteWorkspace, addRepoToWorkspace, isolateWorkspaceRepo } from './core/workspace.js';
|
|
31
32
|
import { loadWorkspaceState } from './core/workspace-state.js';
|
|
32
33
|
import { analyzeAllRepos } from './analyzers/index.js';
|
|
33
34
|
import { generateContextFiles } from './generators/index.js';
|
|
35
|
+
import { getAvailableOrganizations, getAvailableDomainPacks, getOrganization, getDomainPack, matchDomainPacks, resolveActiveDomainRules, saveDomainPack, deleteDomainPack, saveOrganization, deleteOrganization, } from './core/domain-packs.js';
|
|
34
36
|
import { detectAIAssistants } from './utils/detect-ai.js';
|
|
35
37
|
import { detectEditors } from './utils/detect-editors.js';
|
|
36
38
|
import { buildWorkspaceLaunchPrompt, detectWorkspaceLaunchTargets, launchTargetIdForEditorCommand, launchWorkspaceTarget, } from './utils/workspace-launch.js';
|
|
@@ -1013,6 +1015,8 @@ async function runCreationJob(jobId, body, config) {
|
|
|
1013
1015
|
const feature = {
|
|
1014
1016
|
id: workspaceId,
|
|
1015
1017
|
mode: inPlace ? 'in-place' : 'worktree',
|
|
1018
|
+
flowType: body.flowType,
|
|
1019
|
+
workType: body.workType,
|
|
1016
1020
|
projectId: body.projectId,
|
|
1017
1021
|
// In-place features never create a branch; keeping branchName populated
|
|
1018
1022
|
// (= id) avoids breaking every consumer of the non-optional field.
|
|
@@ -1028,6 +1032,10 @@ async function runCreationJob(jobId, body, config) {
|
|
|
1028
1032
|
createdAt: new Date().toISOString(),
|
|
1029
1033
|
resumption: body.resumption,
|
|
1030
1034
|
teamworkInstructions: body.teamworkInstructions,
|
|
1035
|
+
organizationId: body.organizationId,
|
|
1036
|
+
domainPacks: Array.isArray(body.domainPacks) && body.domainPacks.length > 0
|
|
1037
|
+
? body.domainPacks
|
|
1038
|
+
: matchDomainPacks(body.description, (body.repos || []).map((r) => r.name)).map((p) => p.id),
|
|
1031
1039
|
};
|
|
1032
1040
|
if (job) {
|
|
1033
1041
|
job.feature = feature;
|
|
@@ -1081,6 +1089,12 @@ async function runCreationJob(jobId, body, config) {
|
|
|
1081
1089
|
app.post('/api/workspace', async (c) => {
|
|
1082
1090
|
try {
|
|
1083
1091
|
const body = await c.req.json();
|
|
1092
|
+
if (body.workType !== undefined && !['bug', 'feature', 'performance', 'refactor', 'rewrite'].includes(body.workType)) {
|
|
1093
|
+
return c.json({ error: 'Choose a valid work type.' }, 400);
|
|
1094
|
+
}
|
|
1095
|
+
if (body.flowType !== undefined && !['quick', 'feature', 'epic'].includes(body.flowType)) {
|
|
1096
|
+
return c.json({ error: 'flowType must be quick, feature, or epic.' }, 400);
|
|
1097
|
+
}
|
|
1084
1098
|
const inPlace = body.mode === 'in-place';
|
|
1085
1099
|
if (inPlace && !body.name?.trim()) {
|
|
1086
1100
|
return c.json({ error: 'In-place workspaces need a "name"' }, 400);
|
|
@@ -2115,12 +2129,76 @@ app.get('/api/workspace/:id/plan', async (c) => {
|
|
|
2115
2129
|
throw err;
|
|
2116
2130
|
}
|
|
2117
2131
|
}
|
|
2132
|
+
const state = await loadWorkspaceState(workspacePath);
|
|
2133
|
+
if (state.lifecycle) {
|
|
2134
|
+
const { renderLifecyclePlan } = await import('./core/lifecycle.js');
|
|
2135
|
+
const milestones = renderLifecyclePlan(state.lifecycle);
|
|
2136
|
+
const marker = /<!-- CONTEXTSPACE:MILESTONES:START -->[\s\S]*?<!-- CONTEXTSPACE:MILESTONES:END -->/;
|
|
2137
|
+
content = marker.test(content) ? content.replace(marker, () => milestones) : `${milestones}\n\n${content}`;
|
|
2138
|
+
}
|
|
2118
2139
|
return c.json({ content });
|
|
2119
2140
|
}
|
|
2120
2141
|
catch (error) {
|
|
2121
2142
|
return errorResponse(c, error);
|
|
2122
2143
|
}
|
|
2123
2144
|
});
|
|
2145
|
+
registerWorkGuidanceRoutes(app, async (id) => {
|
|
2146
|
+
const config = await loadConfig();
|
|
2147
|
+
return resolveExactWorkspaceById(config.workspacesDir, id);
|
|
2148
|
+
});
|
|
2149
|
+
// 13c-1. Get active workspace lifecycle and sister branch fleet
|
|
2150
|
+
app.get('/api/workspace/:id/lifecycle', async (c) => {
|
|
2151
|
+
try {
|
|
2152
|
+
const id = c.req.param('id');
|
|
2153
|
+
const config = await loadConfig();
|
|
2154
|
+
const workspacePath = resolveWorkspacePath(config.workspacesDir, id);
|
|
2155
|
+
const { loadWorkspaceLifecycle, renderLifecyclePlan } = await import('./core/lifecycle.js');
|
|
2156
|
+
const lifecycle = await loadWorkspaceLifecycle(workspacePath);
|
|
2157
|
+
const report = (await loadWorkspaceState(workspacePath)).lastVerification ?? null;
|
|
2158
|
+
return c.json({ lifecycle, report, plan: renderLifecyclePlan(lifecycle) });
|
|
2159
|
+
}
|
|
2160
|
+
catch (error) {
|
|
2161
|
+
return errorResponse(c, error);
|
|
2162
|
+
}
|
|
2163
|
+
});
|
|
2164
|
+
// 13c-2. Transition or advance a lifecycle step
|
|
2165
|
+
app.post('/api/workspace/:id/lifecycle/step', async (c) => {
|
|
2166
|
+
try {
|
|
2167
|
+
const id = c.req.param('id');
|
|
2168
|
+
const config = await loadConfig();
|
|
2169
|
+
const workspacePath = resolveWorkspacePath(config.workspacesDir, id);
|
|
2170
|
+
const body = (await c.req.json().catch(() => ({})));
|
|
2171
|
+
if (!body.stepId || !body.action) {
|
|
2172
|
+
return c.json({ error: 'stepId and action (start|verify|complete) are required.' }, 400);
|
|
2173
|
+
}
|
|
2174
|
+
const { advanceLifecycleStep } = await import('./core/lifecycle.js');
|
|
2175
|
+
const lifecycle = await advanceLifecycleStep(workspacePath, body.stepId, body.action);
|
|
2176
|
+
return c.json({ lifecycle });
|
|
2177
|
+
}
|
|
2178
|
+
catch (error) {
|
|
2179
|
+
return errorResponse(c, error);
|
|
2180
|
+
}
|
|
2181
|
+
});
|
|
2182
|
+
// 13c-3. Run mechanical verification gate for workspace
|
|
2183
|
+
app.post('/api/workspace/:id/verify', async (c) => {
|
|
2184
|
+
try {
|
|
2185
|
+
const id = c.req.param('id');
|
|
2186
|
+
const config = await loadConfig();
|
|
2187
|
+
const workspacePath = resolveWorkspacePath(config.workspacesDir, id);
|
|
2188
|
+
const body = (await c.req.json().catch(() => ({})));
|
|
2189
|
+
const { verifyWorkspace } = await import('./core/verify.js');
|
|
2190
|
+
const report = await verifyWorkspace(workspacePath, {
|
|
2191
|
+
repoName: body.repoName,
|
|
2192
|
+
filter: body.filter,
|
|
2193
|
+
command: body.command,
|
|
2194
|
+
allowDirty: body.allowDirty,
|
|
2195
|
+
});
|
|
2196
|
+
return c.json({ report });
|
|
2197
|
+
}
|
|
2198
|
+
catch (error) {
|
|
2199
|
+
return errorResponse(c, error);
|
|
2200
|
+
}
|
|
2201
|
+
});
|
|
2124
2202
|
// 13c-2. Check workspace migration status (legacy NexusFlow files detected)
|
|
2125
2203
|
app.get('/api/workspace/:id/migration-status', async (c) => {
|
|
2126
2204
|
try {
|
|
@@ -2696,7 +2774,13 @@ app.delete('/api/skills/categories/:id', async (c) => {
|
|
|
2696
2774
|
// materializations are deliberately never treated as catalog sources.
|
|
2697
2775
|
app.get('/api/skills', async (c) => {
|
|
2698
2776
|
try {
|
|
2699
|
-
const
|
|
2777
|
+
const config = await loadConfig();
|
|
2778
|
+
const wsParam = c.req.query('workspace');
|
|
2779
|
+
let wsPath;
|
|
2780
|
+
if (wsParam && wsParam !== 'global') {
|
|
2781
|
+
wsPath = (await resolveExactWorkspaceById(config.workspacesDir, wsParam)) || undefined;
|
|
2782
|
+
}
|
|
2783
|
+
const skills = await getAllSkills(wsPath);
|
|
2700
2784
|
return c.json({ skills });
|
|
2701
2785
|
}
|
|
2702
2786
|
catch (error) {
|
|
@@ -2710,7 +2794,23 @@ app.post('/api/skills', async (c) => {
|
|
|
2710
2794
|
if (!body.name || !body.content) {
|
|
2711
2795
|
return c.json({ error: 'Skill name and content are required.' }, 400);
|
|
2712
2796
|
}
|
|
2713
|
-
const
|
|
2797
|
+
const config = await loadConfig();
|
|
2798
|
+
const wsParam = body.workspaceId || body.workspace || c.req.query('workspace');
|
|
2799
|
+
let wsPath;
|
|
2800
|
+
if (wsParam && wsParam !== 'global') {
|
|
2801
|
+
wsPath = (await resolveExactWorkspaceById(config.workspacesDir, wsParam)) || undefined;
|
|
2802
|
+
if (!wsPath) {
|
|
2803
|
+
return c.json({ error: `Workspace "${wsParam}" not found.` }, 404);
|
|
2804
|
+
}
|
|
2805
|
+
}
|
|
2806
|
+
if (body.scope === 'workspace' && !wsPath) {
|
|
2807
|
+
return c.json({ error: 'A valid workspace is required when saving a workspace-scoped skill.' }, 400);
|
|
2808
|
+
}
|
|
2809
|
+
const isWorkspaceScope = body.scope === 'workspace' || (Boolean(wsPath) && body.scope !== 'global');
|
|
2810
|
+
const options = isWorkspaceScope && wsPath
|
|
2811
|
+
? { scope: 'workspace', workspacePath: wsPath }
|
|
2812
|
+
: { scope: 'global' };
|
|
2813
|
+
const skill = await saveSkill(body, options);
|
|
2714
2814
|
return c.json({ success: true, skill });
|
|
2715
2815
|
}
|
|
2716
2816
|
catch (error) {
|
|
@@ -2723,6 +2823,18 @@ app.delete('/api/skills/:id', async (c) => {
|
|
|
2723
2823
|
try {
|
|
2724
2824
|
const id = decodeURIComponent(c.req.param('id'));
|
|
2725
2825
|
const config = await loadConfig();
|
|
2826
|
+
const wsParam = c.req.query('workspace');
|
|
2827
|
+
let wsPath;
|
|
2828
|
+
if (wsParam && wsParam !== 'global') {
|
|
2829
|
+
wsPath = (await resolveExactWorkspaceById(config.workspacesDir, wsParam)) || undefined;
|
|
2830
|
+
if (!wsPath) {
|
|
2831
|
+
return c.json({ error: `Workspace "${wsParam}" not found.` }, 404);
|
|
2832
|
+
}
|
|
2833
|
+
}
|
|
2834
|
+
if (wsPath) {
|
|
2835
|
+
await deleteSkill(id, { scope: 'workspace', workspacePath: wsPath });
|
|
2836
|
+
return c.json({ success: true });
|
|
2837
|
+
}
|
|
2726
2838
|
const assignments = await withResourceAdministrationLock(async () => {
|
|
2727
2839
|
const currentAssignments = await findResourceAssignments(config.workspacesDir, id, 'skill');
|
|
2728
2840
|
if (!currentAssignments.length)
|
|
@@ -2738,7 +2850,8 @@ app.delete('/api/skills/:id', async (c) => {
|
|
|
2738
2850
|
return c.json({ success: true });
|
|
2739
2851
|
}
|
|
2740
2852
|
catch (error) {
|
|
2741
|
-
|
|
2853
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2854
|
+
return c.json({ error: message }, 400);
|
|
2742
2855
|
}
|
|
2743
2856
|
});
|
|
2744
2857
|
// Codex-native custom-agent catalog. Other harness agent formats are not
|
|
@@ -2841,6 +2954,216 @@ app.post('/api/skills/workspace/:id/assign', async (c) => {
|
|
|
2841
2954
|
return errorResponse(c, error);
|
|
2842
2955
|
}
|
|
2843
2956
|
});
|
|
2957
|
+
// ─── Enterprise & Modular Domain Packs ─────────────────────────────────────
|
|
2958
|
+
// List available enterprise organizations
|
|
2959
|
+
app.get('/api/enterprise/organizations', (c) => {
|
|
2960
|
+
return c.json({ organizations: getAvailableOrganizations() });
|
|
2961
|
+
});
|
|
2962
|
+
// Register custom organization conventions
|
|
2963
|
+
app.post('/api/enterprise/organizations', async (c) => {
|
|
2964
|
+
try {
|
|
2965
|
+
const body = await c.req.json();
|
|
2966
|
+
if (!body.id || !body.name || !Array.isArray(body.rules)) {
|
|
2967
|
+
return c.json({ error: 'id, name, and rules array are required for an organization.' }, 400);
|
|
2968
|
+
}
|
|
2969
|
+
await saveOrganization(body);
|
|
2970
|
+
return c.json({ success: true, organization: getOrganization(body.id) });
|
|
2971
|
+
}
|
|
2972
|
+
catch (error) {
|
|
2973
|
+
return errorResponse(c, error);
|
|
2974
|
+
}
|
|
2975
|
+
});
|
|
2976
|
+
// Unregister custom organization
|
|
2977
|
+
app.delete('/api/enterprise/organizations/:id', async (c) => {
|
|
2978
|
+
const id = decodeURIComponent(c.req.param('id'));
|
|
2979
|
+
const deleted = await deleteOrganization(id);
|
|
2980
|
+
return c.json({ success: deleted });
|
|
2981
|
+
});
|
|
2982
|
+
// List available domain packs
|
|
2983
|
+
app.get('/api/enterprise/domain-packs', (c) => {
|
|
2984
|
+
return c.json({ domainPacks: getAvailableDomainPacks() });
|
|
2985
|
+
});
|
|
2986
|
+
// Get a single domain pack by ID
|
|
2987
|
+
app.get('/api/enterprise/domain-packs/:id', (c) => {
|
|
2988
|
+
const id = decodeURIComponent(c.req.param('id')).toLowerCase().trim();
|
|
2989
|
+
const pack = getDomainPack(id);
|
|
2990
|
+
if (!pack) {
|
|
2991
|
+
return c.json({ error: `Domain pack "${id}" not found.` }, 404);
|
|
2992
|
+
}
|
|
2993
|
+
return c.json({ domainPack: pack });
|
|
2994
|
+
});
|
|
2995
|
+
// Register custom domain pack
|
|
2996
|
+
app.post('/api/enterprise/domain-packs', async (c) => {
|
|
2997
|
+
try {
|
|
2998
|
+
const body = await c.req.json();
|
|
2999
|
+
if (!body.id || !body.name) {
|
|
3000
|
+
return c.json({ error: 'id and name are required for a domain pack.' }, 400);
|
|
3001
|
+
}
|
|
3002
|
+
const normalizedId = body.id.toLowerCase().trim();
|
|
3003
|
+
const tags = Array.isArray(body.tags) && body.tags.length > 0
|
|
3004
|
+
? body.tags
|
|
3005
|
+
: [normalizedId, ...body.name.toLowerCase().split(/\s+/).filter(Boolean)];
|
|
3006
|
+
const packToRegister = {
|
|
3007
|
+
...body,
|
|
3008
|
+
id: normalizedId,
|
|
3009
|
+
tags,
|
|
3010
|
+
isTemplate: false,
|
|
3011
|
+
};
|
|
3012
|
+
await saveDomainPack(packToRegister);
|
|
3013
|
+
return c.json({ success: true, domainPack: getDomainPack(normalizedId) });
|
|
3014
|
+
}
|
|
3015
|
+
catch (error) {
|
|
3016
|
+
return errorResponse(c, error);
|
|
3017
|
+
}
|
|
3018
|
+
});
|
|
3019
|
+
// Update or customize a domain pack
|
|
3020
|
+
app.put('/api/enterprise/domain-packs/:id', async (c) => {
|
|
3021
|
+
try {
|
|
3022
|
+
const id = decodeURIComponent(c.req.param('id')).toLowerCase().trim();
|
|
3023
|
+
const existing = getDomainPack(id);
|
|
3024
|
+
if (!existing) {
|
|
3025
|
+
return c.json({ error: `Domain pack "${id}" not found.` }, 404);
|
|
3026
|
+
}
|
|
3027
|
+
const body = await c.req.json();
|
|
3028
|
+
const tags = Array.isArray(body.tags)
|
|
3029
|
+
? body.tags
|
|
3030
|
+
: (existing.tags ?? [id, ...(body.name || existing.name || id).toLowerCase().split(/\s+/).filter(Boolean)]);
|
|
3031
|
+
const updatedPack = {
|
|
3032
|
+
id,
|
|
3033
|
+
name: body.name ?? existing.name ?? id,
|
|
3034
|
+
description: body.description ?? existing.description ?? '',
|
|
3035
|
+
categoryType: body.categoryType ?? existing.categoryType ?? 'vertical',
|
|
3036
|
+
parent: body.parent !== undefined ? (body.parent ? body.parent.trim() : undefined) : existing.parent,
|
|
3037
|
+
organization: body.organization ?? existing.organization,
|
|
3038
|
+
tags,
|
|
3039
|
+
skills: Array.isArray(body.skills) ? body.skills : existing.skills,
|
|
3040
|
+
contextFiles: Array.isArray(body.contextFiles) ? body.contextFiles : existing.contextFiles,
|
|
3041
|
+
verifyCommand: body.verifyCommand !== undefined ? (body.verifyCommand ? body.verifyCommand.trim() : undefined) : existing.verifyCommand,
|
|
3042
|
+
rules: Array.isArray(body.rules) ? body.rules : existing.rules,
|
|
3043
|
+
defaultRepos: Array.isArray(body.defaultRepos) ? body.defaultRepos : existing.defaultRepos,
|
|
3044
|
+
microservices: Array.isArray(body.microservices) ? body.microservices : existing.microservices,
|
|
3045
|
+
isTemplate: false,
|
|
3046
|
+
};
|
|
3047
|
+
await saveDomainPack(updatedPack);
|
|
3048
|
+
return c.json({ success: true, domainPack: getDomainPack(id) });
|
|
3049
|
+
}
|
|
3050
|
+
catch (error) {
|
|
3051
|
+
return errorResponse(c, error);
|
|
3052
|
+
}
|
|
3053
|
+
});
|
|
3054
|
+
// Unregister custom domain pack
|
|
3055
|
+
app.delete('/api/enterprise/domain-packs/:id', async (c) => {
|
|
3056
|
+
const id = decodeURIComponent(c.req.param('id')).toLowerCase().trim();
|
|
3057
|
+
const deleted = await deleteDomainPack(id);
|
|
3058
|
+
return c.json({ success: deleted });
|
|
3059
|
+
});
|
|
3060
|
+
// Auto-match domain packs from specification or description
|
|
3061
|
+
app.post('/api/enterprise/match-domains', async (c) => {
|
|
3062
|
+
try {
|
|
3063
|
+
const body = await c.req.json();
|
|
3064
|
+
const text = body.description || '';
|
|
3065
|
+
const repos = Array.isArray(body.repos) ? body.repos : [];
|
|
3066
|
+
const matched = matchDomainPacks(text, repos);
|
|
3067
|
+
return c.json({ matched });
|
|
3068
|
+
}
|
|
3069
|
+
catch (error) {
|
|
3070
|
+
return errorResponse(c, error);
|
|
3071
|
+
}
|
|
3072
|
+
});
|
|
3073
|
+
// Get domain packs & organization conventions for a workspace
|
|
3074
|
+
app.get('/api/workspace/:id/domain-packs', async (c) => {
|
|
3075
|
+
try {
|
|
3076
|
+
const id = decodeURIComponent(c.req.param('id'));
|
|
3077
|
+
const config = await loadConfig();
|
|
3078
|
+
const wsPath = await resolveExactWorkspaceById(config.workspacesDir, id);
|
|
3079
|
+
if (!wsPath)
|
|
3080
|
+
return c.json({ error: 'Workspace not found.' }, 404);
|
|
3081
|
+
const feature = await loadFeatureConfig(wsPath);
|
|
3082
|
+
if (!feature)
|
|
3083
|
+
return c.json({ error: 'Failed to load workspace feature.' }, 404);
|
|
3084
|
+
const resolved = resolveActiveDomainRules(feature.organizationId, feature.domainPacks ?? []);
|
|
3085
|
+
return c.json({
|
|
3086
|
+
organizationId: feature.organizationId,
|
|
3087
|
+
assignedDomainPackIds: feature.domainPacks ?? [],
|
|
3088
|
+
...resolved,
|
|
3089
|
+
});
|
|
3090
|
+
}
|
|
3091
|
+
catch (error) {
|
|
3092
|
+
return errorResponse(c, error);
|
|
3093
|
+
}
|
|
3094
|
+
});
|
|
3095
|
+
// Assign or update domain packs & organization for a workspace
|
|
3096
|
+
app.post('/api/workspace/:id/domain-packs', async (c) => {
|
|
3097
|
+
try {
|
|
3098
|
+
const id = decodeURIComponent(c.req.param('id'));
|
|
3099
|
+
const body = await c.req.json();
|
|
3100
|
+
const config = await loadConfig();
|
|
3101
|
+
const wsPath = await resolveExactWorkspaceById(config.workspacesDir, id);
|
|
3102
|
+
if (!wsPath)
|
|
3103
|
+
return c.json({ error: 'Workspace not found.' }, 404);
|
|
3104
|
+
const feature = await loadFeatureConfig(wsPath);
|
|
3105
|
+
if (!feature)
|
|
3106
|
+
return c.json({ error: 'Failed to load workspace feature.' }, 404);
|
|
3107
|
+
if (body.organizationId !== undefined) {
|
|
3108
|
+
feature.organizationId = body.organizationId;
|
|
3109
|
+
}
|
|
3110
|
+
if (Array.isArray(body.domainPacks)) {
|
|
3111
|
+
feature.domainPacks = body.domainPacks;
|
|
3112
|
+
}
|
|
3113
|
+
await saveFeatureConfig(wsPath, feature);
|
|
3114
|
+
// Refresh workspace context so AGENTS.md immediately reflects active domain rules
|
|
3115
|
+
await refreshWorkspace(wsPath, { force: true }).catch(() => { });
|
|
3116
|
+
const resolved = resolveActiveDomainRules(feature.organizationId, feature.domainPacks ?? []);
|
|
3117
|
+
return c.json({
|
|
3118
|
+
success: true,
|
|
3119
|
+
feature,
|
|
3120
|
+
...resolved,
|
|
3121
|
+
});
|
|
3122
|
+
}
|
|
3123
|
+
catch (error) {
|
|
3124
|
+
return errorResponse(c, error);
|
|
3125
|
+
}
|
|
3126
|
+
});
|
|
3127
|
+
// Update workspace specification / PO requirements & adaptively match domain packs
|
|
3128
|
+
app.post('/api/workspace/:id/update-spec', async (c) => {
|
|
3129
|
+
try {
|
|
3130
|
+
const id = decodeURIComponent(c.req.param('id'));
|
|
3131
|
+
const body = await c.req.json();
|
|
3132
|
+
if (!body.description || typeof body.description !== 'string') {
|
|
3133
|
+
return c.json({ error: 'Description is required.' }, 400);
|
|
3134
|
+
}
|
|
3135
|
+
const config = await loadConfig();
|
|
3136
|
+
const wsPath = await resolveExactWorkspaceById(config.workspacesDir, id);
|
|
3137
|
+
if (!wsPath)
|
|
3138
|
+
return c.json({ error: 'Workspace not found.' }, 404);
|
|
3139
|
+
const feature = await loadFeatureConfig(wsPath);
|
|
3140
|
+
if (!feature)
|
|
3141
|
+
return c.json({ error: 'Failed to load workspace feature.' }, 404);
|
|
3142
|
+
feature.description = body.description.trim();
|
|
3143
|
+
const newlyMatched = [];
|
|
3144
|
+
if (body.autoMatchDomains !== false) {
|
|
3145
|
+
const matchedPacks = matchDomainPacks(feature.description);
|
|
3146
|
+
const currentPacks = new Set(feature.domainPacks ?? []);
|
|
3147
|
+
for (const pack of matchedPacks) {
|
|
3148
|
+
if (!currentPacks.has(pack.id)) {
|
|
3149
|
+
currentPacks.add(pack.id);
|
|
3150
|
+
newlyMatched.push(pack.id);
|
|
3151
|
+
}
|
|
3152
|
+
}
|
|
3153
|
+
feature.domainPacks = Array.from(currentPacks);
|
|
3154
|
+
}
|
|
3155
|
+
await saveFeatureConfig(wsPath, feature);
|
|
3156
|
+
await refreshWorkspace(wsPath, { force: true }).catch(() => { });
|
|
3157
|
+
return c.json({
|
|
3158
|
+
success: true,
|
|
3159
|
+
feature,
|
|
3160
|
+
newlyMatched,
|
|
3161
|
+
});
|
|
3162
|
+
}
|
|
3163
|
+
catch (error) {
|
|
3164
|
+
return errorResponse(c, error);
|
|
3165
|
+
}
|
|
3166
|
+
});
|
|
2844
3167
|
// ─── Workrooms: opt-in LAN/VPN collaboration ─────────────────────────────
|
|
2845
3168
|
const WORKROOM_HUMAN_SESSION_COOKIES = [
|
|
2846
3169
|
'contextspace_workroom_human',
|