ultra-builder-pro-cli 0.22.0 → 0.23.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/CHANGELOG.md +38 -0
- package/README.md +55 -25
- package/adapters/_shared/codex-assets.cjs +8 -3
- package/adapters/_shared/interaction-contract.cjs +13 -6
- package/adapters/_shared/runtime-assets.cjs +1 -1
- package/adapters/claude.js +1 -1
- package/adapters/kimi.js +1 -1
- package/adapters/opencode.js +1 -1
- package/docs/AGENT-CONTEXT.md +38 -40
- package/docs/ARCHITECTURE.md +29 -33
- package/docs/ARTIFACT-AUTHORITY.md +15 -16
- package/docs/ROADMAP.md +2 -2
- package/docs/RUNTIME-COMPAT-MATRIX.md +7 -6
- package/docs/STATE-DB-ACCESS-POLICY.md +14 -14
- package/docs/WORKFLOW-LIFECYCLE.md +39 -33
- package/mcp-server/lib/artifact-registry.cjs +25 -1
- package/mcp-server/lib/change-workflow.cjs +42 -6
- package/mcp-server/lib/ultra-facade.cjs +646 -0
- package/mcp-server/lib/workflow-state.cjs +166 -2
- package/mcp-server/server.cjs +73 -21
- package/package.json +1 -1
- package/skills/ultra-change/SKILL.md +38 -98
- package/skills/ultra-deliver/SKILL.md +42 -65
- package/skills/ultra-dev/SKILL.md +34 -88
- package/skills/ultra-doctor/SKILL.md +35 -57
- package/skills/ultra-init/SKILL.md +37 -78
- package/skills/ultra-plan/SKILL.md +52 -132
- package/skills/ultra-research/SKILL.md +41 -100
- package/skills/ultra-review/SKILL.md +25 -46
- package/skills/ultra-status/SKILL.md +21 -44
- package/skills/ultra-test/SKILL.md +25 -61
- package/skills/ultra-think/SKILL.md +29 -48
- package/skills/ultra-think/references/decision-dialogue.md +6 -4
- package/spec/cli-protocol.md +12 -0
- package/spec/mcp-tools.yaml +159 -0
- package/spec/schemas/mcp-tools.schema.json +1 -1
|
@@ -657,8 +657,9 @@ function inspectRunHealth(run, steps, rootDir) {
|
|
|
657
657
|
const firstOpen = steps.find(
|
|
658
658
|
(item) => item.required && !['completed', 'skipped'].includes(item.status),
|
|
659
659
|
);
|
|
660
|
-
if (!firstOpen)
|
|
661
|
-
|
|
660
|
+
if (!firstOpen) {
|
|
661
|
+
if (runMetadata.draft_dirty !== true) blockers.push('WORKFLOW_ACTIVE_WITHOUT_OPEN_STEP');
|
|
662
|
+
} else if (run.current_step !== firstOpen.step_id) {
|
|
662
663
|
blockers.push(`WORKFLOW_CURRENT_STEP_INVALID:${run.current_step || 'missing'}`);
|
|
663
664
|
}
|
|
664
665
|
}
|
|
@@ -1196,6 +1197,166 @@ function startWorkflow(db, input = {}, { rootDir = process.cwd() } = {}) {
|
|
|
1196
1197
|
return readWorkflow(db, id, { rootDir });
|
|
1197
1198
|
}
|
|
1198
1199
|
|
|
1200
|
+
function abandonWorkflow(db, input = {}, { rootDir = process.cwd() } = {}) {
|
|
1201
|
+
const id = nonEmpty(input.id, 'id');
|
|
1202
|
+
const reason = nonEmpty(input.reason, 'reason');
|
|
1203
|
+
return ops.tx(db, () => {
|
|
1204
|
+
const run = readWorkflow(db, id, { rootDir });
|
|
1205
|
+
if (!run) throw new WorkflowStateError('WORKFLOW_NOT_FOUND', `workflow ${id} not found`);
|
|
1206
|
+
if (run.status === 'cancelled') return run;
|
|
1207
|
+
if (run.status === 'completed') {
|
|
1208
|
+
throw new WorkflowStateError(
|
|
1209
|
+
'WORKFLOW_NOT_MUTABLE',
|
|
1210
|
+
`accepted workflow ${id} is immutable; create a new draft revision instead`,
|
|
1211
|
+
);
|
|
1212
|
+
}
|
|
1213
|
+
const metadata = {
|
|
1214
|
+
...run.metadata,
|
|
1215
|
+
abandoned: {
|
|
1216
|
+
reason,
|
|
1217
|
+
abandoned_at: nowIso(),
|
|
1218
|
+
},
|
|
1219
|
+
};
|
|
1220
|
+
delete metadata.authority_invalidation;
|
|
1221
|
+
const ts = nowIso();
|
|
1222
|
+
db.prepare(
|
|
1223
|
+
`UPDATE workflow_runs
|
|
1224
|
+
SET status = 'cancelled', current_step = NULL, blockers_json = '[]',
|
|
1225
|
+
metadata_json = ?, completed_at = ?, updated_at = ?
|
|
1226
|
+
WHERE id = ?`,
|
|
1227
|
+
).run(JSON.stringify(metadata), ts, ts, id);
|
|
1228
|
+
ops.appendEventInTx(db, {
|
|
1229
|
+
type: 'workflow_abandoned',
|
|
1230
|
+
task_id: run.task_id,
|
|
1231
|
+
change_id: run.change_id,
|
|
1232
|
+
payload: { workflow_id: id, kind: run.kind, reason },
|
|
1233
|
+
});
|
|
1234
|
+
return readWorkflow(db, id, { rootDir });
|
|
1235
|
+
});
|
|
1236
|
+
}
|
|
1237
|
+
|
|
1238
|
+
function prepareWorkflowCheckpoint(db, input = {}, { rootDir = process.cwd() } = {}) {
|
|
1239
|
+
const id = nonEmpty(input.id, 'id');
|
|
1240
|
+
const suppliedSteps = new Map(
|
|
1241
|
+
(Array.isArray(input.steps) ? input.steps : []).map((item) => [
|
|
1242
|
+
nonEmpty(item.step_id, 'steps.step_id'),
|
|
1243
|
+
item,
|
|
1244
|
+
]),
|
|
1245
|
+
);
|
|
1246
|
+
const defaultEvidence = Array.isArray(input.evidence) && input.evidence.length > 0
|
|
1247
|
+
? normalizeEvidence(input.evidence)
|
|
1248
|
+
: [{
|
|
1249
|
+
kind: 'checkpoint',
|
|
1250
|
+
ref: `workflow:${id}`,
|
|
1251
|
+
summary: 'The model recorded this semantic stage in one checkpoint.',
|
|
1252
|
+
}];
|
|
1253
|
+
return ops.tx(db, () => {
|
|
1254
|
+
const run = readWorkflow(db, id, { rootDir });
|
|
1255
|
+
if (!run) throw new WorkflowStateError('WORKFLOW_NOT_FOUND', `workflow ${id} not found`);
|
|
1256
|
+
if (run.status === 'completed') return { workflow: run, blockers: [], ready: true };
|
|
1257
|
+
if (run.status === 'cancelled') {
|
|
1258
|
+
throw new WorkflowStateError('WORKFLOW_NOT_MUTABLE', `workflow ${id} is cancelled`);
|
|
1259
|
+
}
|
|
1260
|
+
const blockers = [];
|
|
1261
|
+
const ts = nowIso();
|
|
1262
|
+
for (const workflowStep of run.steps) {
|
|
1263
|
+
if (!workflowStep.required) continue;
|
|
1264
|
+
const supplied = suppliedSteps.get(workflowStep.step_id) || {};
|
|
1265
|
+
const definition = WORKFLOW_DEFINITIONS[run.kind]
|
|
1266
|
+
.find((item) => item.id === workflowStep.step_id);
|
|
1267
|
+
const outputs = supplied.outputs === undefined
|
|
1268
|
+
? workflowStep.outputs
|
|
1269
|
+
: normalizeOutputs(supplied.outputs, rootDir);
|
|
1270
|
+
const evidence = supplied.evidence === undefined
|
|
1271
|
+
? (workflowStep.evidence.length > 0 ? workflowStep.evidence : defaultEvidence)
|
|
1272
|
+
: normalizeEvidence(supplied.evidence);
|
|
1273
|
+
const semanticRecords = supplied.semantic_records === undefined
|
|
1274
|
+
? workflowStep.semantic_records
|
|
1275
|
+
: (run.kind === 'research'
|
|
1276
|
+
? normalizeSemanticRecords(supplied.semantic_records, workflowStep.step_id, rootDir)
|
|
1277
|
+
: normalizeObjects(supplied.semantic_records, 'semantic_records'));
|
|
1278
|
+
if (definition.output_required && outputs.length === 0) {
|
|
1279
|
+
blockers.push(`WORKFLOW_OUTPUT_REQUIRED:${workflowStep.step_id}`);
|
|
1280
|
+
continue;
|
|
1281
|
+
}
|
|
1282
|
+
if (definition.evidence_required && evidence.length === 0) {
|
|
1283
|
+
blockers.push(`WORKFLOW_EVIDENCE_REQUIRED:${workflowStep.step_id}`);
|
|
1284
|
+
continue;
|
|
1285
|
+
}
|
|
1286
|
+
if (run.kind === 'research' && semanticRecords.length === 0) {
|
|
1287
|
+
blockers.push(`WORKFLOW_SEMANTIC_RECORDS_REQUIRED:${workflowStep.step_id}`);
|
|
1288
|
+
continue;
|
|
1289
|
+
}
|
|
1290
|
+
db.prepare(
|
|
1291
|
+
`UPDATE workflow_steps
|
|
1292
|
+
SET status = 'completed', evidence_json = ?, outputs_json = ?,
|
|
1293
|
+
decisions_json = ?, semantic_records_json = ?, blockers_json = '[]',
|
|
1294
|
+
started_at = COALESCE(started_at, ?), completed_at = ?, updated_at = ?
|
|
1295
|
+
WHERE run_id = ? AND step_id = ?`,
|
|
1296
|
+
).run(
|
|
1297
|
+
JSON.stringify(evidence),
|
|
1298
|
+
JSON.stringify(outputs),
|
|
1299
|
+
JSON.stringify(supplied.decisions === undefined
|
|
1300
|
+
? workflowStep.decisions
|
|
1301
|
+
: normalizeObjects(supplied.decisions, 'decisions')),
|
|
1302
|
+
JSON.stringify(semanticRecords),
|
|
1303
|
+
ts,
|
|
1304
|
+
ts,
|
|
1305
|
+
ts,
|
|
1306
|
+
id,
|
|
1307
|
+
workflowStep.step_id,
|
|
1308
|
+
);
|
|
1309
|
+
}
|
|
1310
|
+
const metadata = { ...run.metadata };
|
|
1311
|
+
delete metadata.authority_invalidation;
|
|
1312
|
+
metadata.draft_dirty = blockers.length > 0;
|
|
1313
|
+
db.prepare(
|
|
1314
|
+
`UPDATE workflow_runs
|
|
1315
|
+
SET status = ?, current_step = NULL, blockers_json = ?, metadata_json = ?, updated_at = ?
|
|
1316
|
+
WHERE id = ?`,
|
|
1317
|
+
).run(
|
|
1318
|
+
blockers.length > 0 ? 'active' : 'ready',
|
|
1319
|
+
JSON.stringify(blockers),
|
|
1320
|
+
JSON.stringify(metadata),
|
|
1321
|
+
ts,
|
|
1322
|
+
id,
|
|
1323
|
+
);
|
|
1324
|
+
ops.appendEventInTx(db, {
|
|
1325
|
+
type: blockers.length > 0 ? 'workflow_checkpoint_rejected' : 'workflow_checkpoint_prepared',
|
|
1326
|
+
task_id: run.task_id,
|
|
1327
|
+
change_id: run.change_id,
|
|
1328
|
+
payload: { workflow_id: id, kind: run.kind, blockers },
|
|
1329
|
+
});
|
|
1330
|
+
return {
|
|
1331
|
+
workflow: readWorkflow(db, id, { rootDir }),
|
|
1332
|
+
blockers,
|
|
1333
|
+
ready: blockers.length === 0,
|
|
1334
|
+
};
|
|
1335
|
+
});
|
|
1336
|
+
}
|
|
1337
|
+
|
|
1338
|
+
function reopenWorkflowDraft(db, input = {}, { rootDir = process.cwd() } = {}) {
|
|
1339
|
+
const id = nonEmpty(input.id, 'id');
|
|
1340
|
+
const blocker = typeof input.blocker === 'string' && input.blocker.trim()
|
|
1341
|
+
? input.blocker.trim()
|
|
1342
|
+
: 'CHECKPOINT_REJECTED';
|
|
1343
|
+
return ops.tx(db, () => {
|
|
1344
|
+
const run = readWorkflow(db, id, { rootDir });
|
|
1345
|
+
if (!run) throw new WorkflowStateError('WORKFLOW_NOT_FOUND', `workflow ${id} not found`);
|
|
1346
|
+
if (run.status === 'completed' || run.status === 'cancelled') return run;
|
|
1347
|
+
const metadata = { ...run.metadata, draft_dirty: true };
|
|
1348
|
+
delete metadata.authority_invalidation;
|
|
1349
|
+
const blockers = [...new Set([...(run.blockers || []), blocker])];
|
|
1350
|
+
db.prepare(
|
|
1351
|
+
`UPDATE workflow_runs
|
|
1352
|
+
SET status = 'active', blockers_json = ?, metadata_json = ?,
|
|
1353
|
+
updated_at = ?
|
|
1354
|
+
WHERE id = ?`,
|
|
1355
|
+
).run(JSON.stringify(blockers), JSON.stringify(metadata), nowIso(), id);
|
|
1356
|
+
return readWorkflow(db, id, { rootDir });
|
|
1357
|
+
});
|
|
1358
|
+
}
|
|
1359
|
+
|
|
1199
1360
|
function reviseWorkflow(db, input = {}, { rootDir = process.cwd() } = {}) {
|
|
1200
1361
|
const id = nonEmpty(input.id, 'id');
|
|
1201
1362
|
const replacementId = nonEmpty(input.replacement_id, 'replacement_id');
|
|
@@ -2948,6 +3109,9 @@ module.exports = {
|
|
|
2948
3109
|
WorkflowStateError,
|
|
2949
3110
|
insertWorkflowInTx,
|
|
2950
3111
|
startWorkflow,
|
|
3112
|
+
abandonWorkflow,
|
|
3113
|
+
prepareWorkflowCheckpoint,
|
|
3114
|
+
reopenWorkflowDraft,
|
|
2951
3115
|
readWorkflow,
|
|
2952
3116
|
listWorkflows,
|
|
2953
3117
|
recordWorkflowStep,
|
package/mcp-server/server.cjs
CHANGED
|
@@ -39,6 +39,7 @@ const workflows = require('./lib/workflow-state.cjs');
|
|
|
39
39
|
const decisions = require('./lib/decision-dialogue.cjs');
|
|
40
40
|
const doctor = require('./lib/doctor.cjs');
|
|
41
41
|
const artifactRegistry = require('./lib/artifact-registry.cjs');
|
|
42
|
+
const ultraFacade = require('./lib/ultra-facade.cjs');
|
|
42
43
|
const {
|
|
43
44
|
readProjectBreadcrumb,
|
|
44
45
|
renderProjectBreadcrumb,
|
|
@@ -137,19 +138,29 @@ const ARTIFACT_TOOLS = Object.freeze([
|
|
|
137
138
|
'artifact.get',
|
|
138
139
|
]);
|
|
139
140
|
|
|
140
|
-
const
|
|
141
|
+
const LEGACY_TOOLS = Object.freeze([
|
|
141
142
|
...TASK_TOOLS, ...SESSION_TOOLS, ...PLAN_TOOLS, ...BASELINE_TOOLS, ...CHANGE_TOOLS,
|
|
142
143
|
...DECISION_TOOLS,
|
|
143
144
|
...WORKFLOW_TOOLS,
|
|
144
145
|
...ARTIFACT_TOOLS,
|
|
145
146
|
...SYSTEM_TOOLS,
|
|
146
147
|
]);
|
|
148
|
+
const PUBLIC_TOOLS = ultraFacade.PUBLIC_TOOLS;
|
|
149
|
+
const REGISTERED_TOOLS = Object.freeze([...PUBLIC_TOOLS, ...LEGACY_TOOLS]);
|
|
147
150
|
|
|
148
151
|
const STATELESS_TOOLS = new Set(['task.init_project']);
|
|
149
152
|
|
|
153
|
+
function isStatelessPublicCall(name, input = {}) {
|
|
154
|
+
return name === 'ultra.record'
|
|
155
|
+
&& Array.isArray(input.entries)
|
|
156
|
+
&& input.entries.length === 1
|
|
157
|
+
&& input.entries[0]?.operation === 'task.init_project';
|
|
158
|
+
}
|
|
159
|
+
|
|
150
160
|
// init_project owns its target project's schema, baseline seed, and first
|
|
151
161
|
// projection internally. Do not enqueue a second projection on the caller DB.
|
|
152
162
|
const MUTATING_TOOLS = new Set([
|
|
163
|
+
'ultra.record', 'ultra.checkpoint', 'ultra.sync', 'ultra.session', 'ultra.archive',
|
|
153
164
|
'task.create', 'task.update', 'task.delete', 'task.append_event', 'task.switch_tag',
|
|
154
165
|
'task.parse_prd', 'task.expand',
|
|
155
166
|
'task.ledger_import',
|
|
@@ -167,6 +178,7 @@ const MUTATING_TOOLS = new Set([
|
|
|
167
178
|
]);
|
|
168
179
|
|
|
169
180
|
const TEAM_LEDGER_SYNC_TOOLS = new Set([
|
|
181
|
+
'ultra.record', 'ultra.checkpoint', 'ultra.archive',
|
|
170
182
|
'task.create', 'task.update', 'task.delete', 'task.parse_prd', 'task.expand',
|
|
171
183
|
'plan.export',
|
|
172
184
|
'baseline.start', 'baseline.record', 'baseline.converge',
|
|
@@ -651,10 +663,6 @@ async function dispatchTool(name, input, db, ctx = {}) {
|
|
|
651
663
|
// caller must see a typed failure and later mutations must stop.
|
|
652
664
|
throw error;
|
|
653
665
|
}
|
|
654
|
-
taskLedger.publishTaskLedger(db, {
|
|
655
|
-
rootDir,
|
|
656
|
-
reason: 'plan_accepted',
|
|
657
|
-
});
|
|
658
666
|
return {
|
|
659
667
|
plan_path: publication.plan_path,
|
|
660
668
|
plan_md_path: publication.plan_md_path,
|
|
@@ -969,6 +977,19 @@ function startServer({
|
|
|
969
977
|
};
|
|
970
978
|
const getDb = (toolName = null) => {
|
|
971
979
|
if (!db) {
|
|
980
|
+
if (toolName === 'ultra.context') {
|
|
981
|
+
if (!fs.existsSync(authorityDbPath)) {
|
|
982
|
+
const error = new Error(
|
|
983
|
+
'Ultra authority is not initialized; invoke ultra-init before reading project context',
|
|
984
|
+
);
|
|
985
|
+
error.code = 'STATE_DB_MISSING';
|
|
986
|
+
throw error;
|
|
987
|
+
}
|
|
988
|
+
db = openStateDb(authorityDbPath);
|
|
989
|
+
ensureSchemaVersion(db);
|
|
990
|
+
assertStateAuthority(db, rootDir, { importTeamLedger: false });
|
|
991
|
+
return db;
|
|
992
|
+
}
|
|
972
993
|
const projectPaths = runtimePaths.pathsFor(rootDir);
|
|
973
994
|
if ([projectPaths.legacyStateDbPath, projectPaths.stateDbPath].includes(authorityDbPath)) {
|
|
974
995
|
// The packaged launcher has no explicit DB override. Its default path
|
|
@@ -1005,7 +1026,7 @@ function startServer({
|
|
|
1005
1026
|
}
|
|
1006
1027
|
planRecovery = planStore.recoverPlanPublications(db, { rootDir });
|
|
1007
1028
|
}
|
|
1008
|
-
|
|
1029
|
+
if (!['system.doctor', 'ultra.doctor'].includes(toolName)
|
|
1009
1030
|
&& (planRecovery?.pending > 0 || planRecovery?.issues?.length > 0)) {
|
|
1010
1031
|
planRecovery = planStore.recoverPlanPublications(db, { rootDir });
|
|
1011
1032
|
if (planRecovery.pending > 0 || planRecovery.issues.length > 0) {
|
|
@@ -1021,6 +1042,7 @@ function startServer({
|
|
|
1021
1042
|
};
|
|
1022
1043
|
|
|
1023
1044
|
const tools = loadRegisteredTools();
|
|
1045
|
+
const publicTools = tools.filter((tool) => PUBLIC_TOOLS.includes(tool.name));
|
|
1024
1046
|
const ajv = buildAjv();
|
|
1025
1047
|
const inputValidators = new Map();
|
|
1026
1048
|
const outputValidators = new Map();
|
|
@@ -1035,7 +1057,7 @@ function startServer({
|
|
|
1035
1057
|
);
|
|
1036
1058
|
|
|
1037
1059
|
server.setRequestHandler('tools/list', async () => ({
|
|
1038
|
-
tools:
|
|
1060
|
+
tools: publicTools.map((t) => ({
|
|
1039
1061
|
name: t.name,
|
|
1040
1062
|
description: t.description,
|
|
1041
1063
|
inputSchema: t.input_schema,
|
|
@@ -1081,23 +1103,51 @@ function startServer({
|
|
|
1081
1103
|
let result;
|
|
1082
1104
|
let toolDb = null;
|
|
1083
1105
|
try {
|
|
1084
|
-
toolDb = STATELESS_TOOLS.has(name)
|
|
1106
|
+
toolDb = (STATELESS_TOOLS.has(name) || isStatelessPublicCall(name, args))
|
|
1107
|
+
? null
|
|
1108
|
+
: getDb(name);
|
|
1085
1109
|
if (toolDb) assertStateAuthority(toolDb, rootDir);
|
|
1086
1110
|
if (toolDb && TEAM_LEDGER_SYNC_TOOLS.has(name)) {
|
|
1087
1111
|
taskLedger.syncTaskLedger(toolDb, { rootDir });
|
|
1088
1112
|
}
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1113
|
+
const callContext = {
|
|
1114
|
+
rootDir,
|
|
1115
|
+
sessionId,
|
|
1116
|
+
projector: project,
|
|
1117
|
+
markPlanRecoveryRequired,
|
|
1118
|
+
};
|
|
1119
|
+
const callLegacy = async (legacyName, legacyArgs = {}) => {
|
|
1120
|
+
if (!LEGACY_TOOLS.includes(legacyName)) {
|
|
1121
|
+
const error = new Error(`legacy operation ${legacyName} is not supported`);
|
|
1122
|
+
error.code = 'ULTRA_RECORD_OPERATION_UNSUPPORTED';
|
|
1123
|
+
throw error;
|
|
1124
|
+
}
|
|
1125
|
+
const validateLegacyInput = inputValidators.get(legacyName);
|
|
1126
|
+
if (!validateLegacyInput(legacyArgs)) {
|
|
1127
|
+
const error = new Error(ajv.errorsText(validateLegacyInput.errors));
|
|
1128
|
+
error.code = 'VALIDATION_ERROR';
|
|
1129
|
+
error.details = validateLegacyInput.errors;
|
|
1130
|
+
throw error;
|
|
1131
|
+
}
|
|
1132
|
+
const legacyResult = await dispatchTool(
|
|
1133
|
+
legacyName,
|
|
1134
|
+
legacyArgs,
|
|
1135
|
+
toolDb,
|
|
1136
|
+
callContext,
|
|
1137
|
+
);
|
|
1138
|
+
const validateLegacyOutput = outputValidators.get(legacyName);
|
|
1139
|
+
if (!validateLegacyOutput(legacyResult)) {
|
|
1140
|
+
const error = new Error(ajv.errorsText(validateLegacyOutput.errors));
|
|
1141
|
+
error.code = 'OUTPUT_SCHEMA_DRIFT';
|
|
1142
|
+
error.details = validateLegacyOutput.errors;
|
|
1143
|
+
throw error;
|
|
1144
|
+
}
|
|
1145
|
+
return legacyResult;
|
|
1146
|
+
};
|
|
1147
|
+
result = PUBLIC_TOOLS.includes(name)
|
|
1148
|
+
? await ultraFacade.dispatch(name, args, toolDb, { ...callContext, callLegacy })
|
|
1149
|
+
: await dispatchTool(name, args, toolDb, callContext);
|
|
1150
|
+
if (['system.doctor', 'ultra.doctor'].includes(name) && args.repair === true) {
|
|
1101
1151
|
planRecovery = result.repair?.plan_publications || null;
|
|
1102
1152
|
}
|
|
1103
1153
|
} catch (err) {
|
|
@@ -1122,7 +1172,7 @@ function startServer({
|
|
|
1122
1172
|
projection_job_id: job.id,
|
|
1123
1173
|
};
|
|
1124
1174
|
if (own && own.incident_id) runtimeMeta.incident_id = own.incident_id;
|
|
1125
|
-
} else if (toolDb &&
|
|
1175
|
+
} else if (toolDb && ['system.doctor', 'ultra.doctor'].includes(name) && args.repair === true) {
|
|
1126
1176
|
runtimeMeta = {
|
|
1127
1177
|
state_commit: 'committed',
|
|
1128
1178
|
projection_status: result.checks.projections.status === 'pass' ? 'completed' : 'failed',
|
|
@@ -1260,6 +1310,8 @@ module.exports = {
|
|
|
1260
1310
|
ARTIFACT_TOOLS,
|
|
1261
1311
|
SYSTEM_TOOLS,
|
|
1262
1312
|
REGISTERED_TOOLS,
|
|
1313
|
+
LEGACY_TOOLS,
|
|
1314
|
+
PUBLIC_TOOLS,
|
|
1263
1315
|
STATELESS_TOOLS,
|
|
1264
1316
|
MUTATING_TOOLS,
|
|
1265
1317
|
appendHookLifecycleEvent,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ultra-builder-pro-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.23.0",
|
|
4
4
|
"description": "Explicitly invoked Ultra Builder Pro workflows for Claude Code, OpenCode, Codex CLI, and Kimi Code, with project-local MCP authority and lifecycle recovery.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"ultra-builder-pro-cli": "bin/install.js",
|
|
@@ -3,105 +3,45 @@ name: ultra-change
|
|
|
3
3
|
description: Capture or revise one bounded feature, fix, redesign, or incident contract against a ready Ultra baseline. Use when post-baseline work needs accepted intent before research, planning, or implementation.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
# Capture accepted
|
|
6
|
+
# Capture accepted Change intent
|
|
7
7
|
|
|
8
|
-
Change is the durable unit
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
Read `../ultra-think/references/decision-dialogue.md` before asking a material question.
|
|
8
|
+
A Change is the durable unit connecting research, plan, implementation, verification,
|
|
9
|
+
review, documentation, and delivery. Its draft stays editable until a semantic
|
|
10
|
+
checkpoint accepts the downstream authority.
|
|
13
11
|
|
|
14
12
|
## Inspect and align
|
|
15
13
|
|
|
16
|
-
1.
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
## Persist the contract
|
|
52
|
-
|
|
53
|
-
When a durable alignment checkpoint is necessary, bind it to a compact artifact and
|
|
54
|
-
pass its id as `alignment_thread_id`. Do not require a checkpoint for a complete,
|
|
55
|
-
unambiguous request that already provides current authority.
|
|
56
|
-
|
|
57
|
-
Call `change.create`. It records and completes exactly:
|
|
58
|
-
|
|
59
|
-
1. `bind-baseline`
|
|
60
|
-
2. `classify-change`
|
|
61
|
-
3. `record-intent`
|
|
62
|
-
|
|
63
|
-
The returned change workflow must be `completed`; research, plan, context, dev, test,
|
|
64
|
-
review, and deliver remain independent capabilities. Keep every Change-owned semantic
|
|
65
|
-
artifact, finding, plan, context, test result, review result, documentation update,
|
|
66
|
-
and progress projection below `.ultra/changes/active/<id>/`. Incidents maintain the
|
|
67
|
-
structured diagnosis artifact there as well.
|
|
68
|
-
|
|
69
|
-
`change.create` and `change.update` publish the portable Change Contract to
|
|
70
|
-
`.ultra/tasks/tasks.json`, even before the Change owns tasks. The tracked Change
|
|
71
|
-
artifacts carry semantic bodies; the checkpoint carries normalized baseline, Change,
|
|
72
|
-
and task records with digest ancestry. Never edit either generated checkpoint or
|
|
73
|
-
checkout-local projection directly.
|
|
74
|
-
|
|
75
|
-
Before planning a standard or major change, write the accepted specification updates
|
|
76
|
-
to the Change overlay and call `change.delta` with exact baseline id, repository
|
|
77
|
-
revision, specification digests, decisions, non-goals, acceptance, documentation
|
|
78
|
-
impact, unknowns, and mutation before/after digests. If accepted behavior truly does
|
|
79
|
-
not change baseline semantics, record an empty typed delta with a specific
|
|
80
|
-
`no_semantic_change_reason`; do not manufacture a mutation. Read back the registered
|
|
81
|
-
delta and deterministic `progress.md`. This records proposed authority only and must
|
|
82
|
-
not edit baseline specifications.
|
|
83
|
-
|
|
84
|
-
Use `change.update` when accepted intent or evidence changes. Update the Change
|
|
85
|
-
Contract, classification, research disposition, and docs impact together when their
|
|
86
|
-
meaning is coupled. Downstream task, context, test, or review evidence affected by that
|
|
87
|
-
change must become stale and be regenerated; never keep old evidence green by prompt
|
|
88
|
-
assertion.
|
|
89
|
-
|
|
90
|
-
If research is required, recommend `ultra-research` with the recorded disposition. If
|
|
91
|
-
the contract is sufficiently evidenced, finish the typed delta and recommend
|
|
92
|
-
`ultra-plan`. Direct Build skips Research only; it never skips Plan, DB-backed task
|
|
93
|
-
contracts, or fresh task context. Return and wait for an explicit user invocation; do
|
|
94
|
-
not start another workflow. `change.context` is compiled only for an actual plan,
|
|
95
|
-
implementation, test, or review consumer.
|
|
96
|
-
|
|
97
|
-
Stable implementation discoveries use the approval-gated specification-learning
|
|
98
|
-
transitions. External memory and graph providers retain their own content; Ultra stores
|
|
99
|
-
metadata references only.
|
|
100
|
-
|
|
101
|
-
Return the normalized contract, change id and kind, research disposition, blockers,
|
|
102
|
-
and `allowed_transitions`. Recommend a semantic next action from those transitions.
|
|
103
|
-
If current intent does not already select it, present the recommendation and credible
|
|
104
|
-
alternatives through the interaction protocol, then wait. MCP does not encode a
|
|
105
|
-
semantic next action.
|
|
106
|
-
|
|
107
|
-
Never invoke the recommended capability here; wait for an explicit user invocation.
|
|
14
|
+
1. Call `ultra.context { stage: project, detail: full }`.
|
|
15
|
+
2. Inspect or import the team checkpoint with `ultra.sync`. Stop only on a real
|
|
16
|
+
baseline, Change, task, ancestry, or active-session conflict.
|
|
17
|
+
3. Reuse an existing matching Change. Do not duplicate authority.
|
|
18
|
+
4. Convert the request into observable outcome, executable acceptance, non-goals,
|
|
19
|
+
public seams, recovery, verification, documentation impact, risk, and research
|
|
20
|
+
disposition.
|
|
21
|
+
5. Recommend `quick`, `standard`, `major`, or `incident` from evidence. Ask only when
|
|
22
|
+
a choice changes accepted product intent, compatibility, security, material cost,
|
|
23
|
+
external effects, or recovery.
|
|
24
|
+
|
|
25
|
+
Read `../ultra-think/references/decision-dialogue.md` before asking. MCP may report
|
|
26
|
+
contradictory risk fields, but the model and owner choose the semantic correction.
|
|
27
|
+
|
|
28
|
+
## Record the contract
|
|
29
|
+
|
|
30
|
+
Use one `ultra.record` batch for `change.create` or `change.update`, relevant
|
|
31
|
+
`decision.*`, and `change.delta` when the delta is already known. Every entry carries
|
|
32
|
+
a stable idempotency key. Read the result through `ultra.context`.
|
|
33
|
+
|
|
34
|
+
Keep every Change-owned semantic artifact below
|
|
35
|
+
`.ultra/changes/active/<change-id>/`. Standard and major Changes require a typed delta
|
|
36
|
+
before planning. A true no-semantic-change case records an explicit reason instead of
|
|
37
|
+
manufacturing a mutation.
|
|
38
|
+
|
|
39
|
+
Changing accepted intent reopens the draft and makes only genuinely dependent evidence
|
|
40
|
+
stale. `ready` is not an absorbing state. A semantic rejection returns diagnostics;
|
|
41
|
+
repair and retry the same draft. Use `workflow.abandon` only to preserve and cancel an
|
|
42
|
+
attempt intentionally.
|
|
43
|
+
|
|
44
|
+
Publish the durable Change contract with `ultra.sync { action: publish }` when it is a
|
|
45
|
+
useful team handoff. Recommend bounded `ultra-research` for a real evidence gap or
|
|
46
|
+
`ultra-plan` when the contract is sufficiently evidenced. Do not invoke the next
|
|
47
|
+
capability automatically.
|
|
@@ -1,71 +1,48 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: ultra-deliver
|
|
3
|
-
description: Converge a verified Ultra
|
|
3
|
+
description: Converge a verified Ultra Change, reconcile baseline specifications, and archive it with recoverable local evidence. Use when implementation, testing, and review are current.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Converge and archive local authority
|
|
7
7
|
|
|
8
|
-
Delivery closes
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
##
|
|
12
|
-
|
|
13
|
-
1.
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
rebinds every registered artifact into the self-contained archive, and rolls back or
|
|
50
|
-
resumes from its transaction journal after interruption. A partial success is not
|
|
51
|
-
delivery. Archive publication refreshes the checkpoint with the archived Change and
|
|
52
|
-
new baseline summary. Verify the refreshed baseline and archived packet, then record
|
|
53
|
-
`archive-change`.
|
|
54
|
-
|
|
55
|
-
Write `<archived-change-root>/delivery/<workflow-id>/report.json` with
|
|
56
|
-
`ultra-delivery-report-v1`: archived change, baseline, HEAD, worktree and context
|
|
57
|
-
digests, local checks, rollback notes, and timestamp. The report must not contain a
|
|
58
|
-
release decision. Record it under `verify-delivery` and complete the workflow.
|
|
59
|
-
|
|
60
|
-
## External effects
|
|
61
|
-
|
|
62
|
-
If the user separately requests commit, push, tag, publish, or deploy, handle that as a
|
|
63
|
-
host-owned effect after Ultra delivery. Confirm only missing material authority, run
|
|
64
|
-
the repository's release checks, and report remote evidence separately. Never infer an
|
|
65
|
-
external effect from `ultra-deliver`, and never encode it as completed Ultra delivery
|
|
66
|
-
state.
|
|
67
|
-
|
|
68
|
-
Return archive and baseline state, evidence digests, recovery notes, residual risks,
|
|
69
|
-
and allowed transitions.
|
|
70
|
-
|
|
71
|
-
Never invoke the recommended capability here; wait for an explicit user invocation.
|
|
8
|
+
Delivery closes local Ultra authority. It never grants commit, push, tag, registry
|
|
9
|
+
publication, deployment, or another external effect.
|
|
10
|
+
|
|
11
|
+
## Prepare the packet
|
|
12
|
+
|
|
13
|
+
1. Call `ultra.context { stage: deliver, scope: { change_id }, detail: full }`.
|
|
14
|
+
2. Import a newer team checkpoint with `ultra.sync`; stop on a real conflict.
|
|
15
|
+
3. Require current task outcomes, test report, both review axes, accepted decisions,
|
|
16
|
+
typed delta, documentation reconciliation, and current checkout evidence.
|
|
17
|
+
4. Resolve every specification-learning candidate. Keep updates inside the Change
|
|
18
|
+
overlay until archive.
|
|
19
|
+
5. Write and register documentation reconciliation and an
|
|
20
|
+
`ultra-delivery-report-v1` report. The report binds Change, baseline, HEAD,
|
|
21
|
+
worktree/context digests, local checks, rollback guidance, and timestamp; it contains
|
|
22
|
+
no release decision.
|
|
23
|
+
|
|
24
|
+
Use one `ultra.record` batch for the reconciliation, report artifact, learning
|
|
25
|
+
resolutions, and other durable facts.
|
|
26
|
+
|
|
27
|
+
## Archive once
|
|
28
|
+
|
|
29
|
+
Call `ultra.archive` once with the Change id, stable idempotency key, archive summary,
|
|
30
|
+
reconciliation fields, delivery evidence steps, and report output. The operation
|
|
31
|
+
internally:
|
|
32
|
+
|
|
33
|
+
- compiles or reuses convergence context;
|
|
34
|
+
- checks the deliver draft;
|
|
35
|
+
- derives convergence from current dev, test, review, delta, docs, and checkout;
|
|
36
|
+
- applies the overlay and archive through the recoverable filesystem/DB transaction;
|
|
37
|
+
- rebinds registered artifacts into a self-contained archive;
|
|
38
|
+
- completes the durable delivery checkpoint;
|
|
39
|
+
- publishes the updated team checkpoint.
|
|
40
|
+
|
|
41
|
+
A semantic rejection returns mutable blockers. Repair the same packet and retry. A
|
|
42
|
+
hard corruption, path, concurrency, or recovery error remains fail-closed and routes to
|
|
43
|
+
`ultra-doctor`.
|
|
44
|
+
|
|
45
|
+
Verify the archived packet, baseline, checkpoint, and rollback state. Handle any
|
|
46
|
+
separately authorized Git or release effect only after local delivery and report it as
|
|
47
|
+
external evidence, not Ultra delivery authority. Do not invoke another capability
|
|
48
|
+
automatically.
|