@girardmedia/bootspring 3.0.0 → 3.2.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/assets/claude-commands/build.md +2 -0
- package/dist/cli/index.js +691 -144
- package/dist/core/index.d.ts +1 -1
- package/dist/core.js +10 -8
- package/dist/mcp-server.js +409 -65
- package/package.json +1 -1
- package/scripts/postinstall.cjs +7 -1
package/dist/mcp-server.js
CHANGED
|
@@ -33,9 +33,9 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
33
33
|
));
|
|
34
34
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
35
35
|
|
|
36
|
-
// ../../node_modules/.pnpm/tsup@8.5.1_jiti@1.21.7_postcss@8.5.
|
|
36
|
+
// ../../node_modules/.pnpm/tsup@8.5.1_jiti@1.21.7_postcss@8.5.14_tsx@4.21.0_typescript@5.9.3_yaml@2.8.3/node_modules/tsup/assets/cjs_shims.js
|
|
37
37
|
var init_cjs_shims = __esm({
|
|
38
|
-
"../../node_modules/.pnpm/tsup@8.5.1_jiti@1.21.7_postcss@8.5.
|
|
38
|
+
"../../node_modules/.pnpm/tsup@8.5.1_jiti@1.21.7_postcss@8.5.14_tsx@4.21.0_typescript@5.9.3_yaml@2.8.3/node_modules/tsup/assets/cjs_shims.js"() {
|
|
39
39
|
"use strict";
|
|
40
40
|
}
|
|
41
41
|
});
|
|
@@ -26434,13 +26434,16 @@ var require_data = __commonJS({
|
|
|
26434
26434
|
}
|
|
26435
26435
|
});
|
|
26436
26436
|
|
|
26437
|
-
// ../../node_modules/.pnpm/fast-uri@3.1.
|
|
26437
|
+
// ../../node_modules/.pnpm/fast-uri@3.1.2/node_modules/fast-uri/lib/utils.js
|
|
26438
26438
|
var require_utils = __commonJS({
|
|
26439
|
-
"../../node_modules/.pnpm/fast-uri@3.1.
|
|
26439
|
+
"../../node_modules/.pnpm/fast-uri@3.1.2/node_modules/fast-uri/lib/utils.js"(exports2, module2) {
|
|
26440
26440
|
"use strict";
|
|
26441
26441
|
init_cjs_shims();
|
|
26442
26442
|
var isUUID = RegExp.prototype.test.bind(/^[\da-f]{8}-[\da-f]{4}-[\da-f]{4}-[\da-f]{4}-[\da-f]{12}$/iu);
|
|
26443
26443
|
var isIPv4 = RegExp.prototype.test.bind(/^(?:(?:25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|\d)\.){3}(?:25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|\d)$/u);
|
|
26444
|
+
var isHexPair = RegExp.prototype.test.bind(/^[\da-f]{2}$/iu);
|
|
26445
|
+
var isUnreserved = RegExp.prototype.test.bind(/^[\da-z\-._~]$/iu);
|
|
26446
|
+
var isPathCharacter = RegExp.prototype.test.bind(/^[\da-z\-._~!$&'()*+,;=:@/]$/iu);
|
|
26444
26447
|
function stringArrayToHexStripped(input) {
|
|
26445
26448
|
let acc = "";
|
|
26446
26449
|
let code = 0;
|
|
@@ -26633,27 +26636,77 @@ var require_utils = __commonJS({
|
|
|
26633
26636
|
}
|
|
26634
26637
|
return output.join("");
|
|
26635
26638
|
}
|
|
26636
|
-
|
|
26637
|
-
|
|
26638
|
-
|
|
26639
|
-
|
|
26640
|
-
|
|
26641
|
-
|
|
26642
|
-
|
|
26643
|
-
|
|
26644
|
-
|
|
26645
|
-
|
|
26639
|
+
var HOST_DELIMS = { "@": "%40", "/": "%2F", "?": "%3F", "#": "%23", ":": "%3A" };
|
|
26640
|
+
var HOST_DELIM_RE = /[@/?#:]/g;
|
|
26641
|
+
var HOST_DELIM_NO_COLON_RE = /[@/?#]/g;
|
|
26642
|
+
function reescapeHostDelimiters(host, isIP) {
|
|
26643
|
+
const re = isIP ? HOST_DELIM_NO_COLON_RE : HOST_DELIM_RE;
|
|
26644
|
+
re.lastIndex = 0;
|
|
26645
|
+
return host.replace(re, (ch) => HOST_DELIMS[ch]);
|
|
26646
|
+
}
|
|
26647
|
+
function normalizePercentEncoding(input, decodeUnreserved = false) {
|
|
26648
|
+
if (input.indexOf("%") === -1) {
|
|
26649
|
+
return input;
|
|
26646
26650
|
}
|
|
26647
|
-
|
|
26648
|
-
|
|
26651
|
+
let output = "";
|
|
26652
|
+
for (let i = 0; i < input.length; i++) {
|
|
26653
|
+
if (input[i] === "%" && i + 2 < input.length) {
|
|
26654
|
+
const hex = input.slice(i + 1, i + 3);
|
|
26655
|
+
if (isHexPair(hex)) {
|
|
26656
|
+
const normalizedHex = hex.toUpperCase();
|
|
26657
|
+
const decoded = String.fromCharCode(parseInt(normalizedHex, 16));
|
|
26658
|
+
if (decodeUnreserved && isUnreserved(decoded)) {
|
|
26659
|
+
output += decoded;
|
|
26660
|
+
} else {
|
|
26661
|
+
output += "%" + normalizedHex;
|
|
26662
|
+
}
|
|
26663
|
+
i += 2;
|
|
26664
|
+
continue;
|
|
26665
|
+
}
|
|
26666
|
+
}
|
|
26667
|
+
output += input[i];
|
|
26649
26668
|
}
|
|
26650
|
-
|
|
26651
|
-
|
|
26669
|
+
return output;
|
|
26670
|
+
}
|
|
26671
|
+
function normalizePathEncoding(input) {
|
|
26672
|
+
let output = "";
|
|
26673
|
+
for (let i = 0; i < input.length; i++) {
|
|
26674
|
+
if (input[i] === "%" && i + 2 < input.length) {
|
|
26675
|
+
const hex = input.slice(i + 1, i + 3);
|
|
26676
|
+
if (isHexPair(hex)) {
|
|
26677
|
+
const normalizedHex = hex.toUpperCase();
|
|
26678
|
+
const decoded = String.fromCharCode(parseInt(normalizedHex, 16));
|
|
26679
|
+
if (decoded !== "." && isUnreserved(decoded)) {
|
|
26680
|
+
output += decoded;
|
|
26681
|
+
} else {
|
|
26682
|
+
output += "%" + normalizedHex;
|
|
26683
|
+
}
|
|
26684
|
+
i += 2;
|
|
26685
|
+
continue;
|
|
26686
|
+
}
|
|
26687
|
+
}
|
|
26688
|
+
if (isPathCharacter(input[i])) {
|
|
26689
|
+
output += input[i];
|
|
26690
|
+
} else {
|
|
26691
|
+
output += escape(input[i]);
|
|
26692
|
+
}
|
|
26652
26693
|
}
|
|
26653
|
-
|
|
26654
|
-
|
|
26694
|
+
return output;
|
|
26695
|
+
}
|
|
26696
|
+
function escapePreservingEscapes(input) {
|
|
26697
|
+
let output = "";
|
|
26698
|
+
for (let i = 0; i < input.length; i++) {
|
|
26699
|
+
if (input[i] === "%" && i + 2 < input.length) {
|
|
26700
|
+
const hex = input.slice(i + 1, i + 3);
|
|
26701
|
+
if (isHexPair(hex)) {
|
|
26702
|
+
output += "%" + hex.toUpperCase();
|
|
26703
|
+
i += 2;
|
|
26704
|
+
continue;
|
|
26705
|
+
}
|
|
26706
|
+
}
|
|
26707
|
+
output += escape(input[i]);
|
|
26655
26708
|
}
|
|
26656
|
-
return
|
|
26709
|
+
return output;
|
|
26657
26710
|
}
|
|
26658
26711
|
function recomposeAuthority(component) {
|
|
26659
26712
|
const uriTokens = [];
|
|
@@ -26668,7 +26721,7 @@ var require_utils = __commonJS({
|
|
|
26668
26721
|
if (ipV6res.isIPV6 === true) {
|
|
26669
26722
|
host = `[${ipV6res.escapedHost}]`;
|
|
26670
26723
|
} else {
|
|
26671
|
-
host =
|
|
26724
|
+
host = reescapeHostDelimiters(host, false);
|
|
26672
26725
|
}
|
|
26673
26726
|
}
|
|
26674
26727
|
uriTokens.push(host);
|
|
@@ -26682,7 +26735,10 @@ var require_utils = __commonJS({
|
|
|
26682
26735
|
module2.exports = {
|
|
26683
26736
|
nonSimpleDomain,
|
|
26684
26737
|
recomposeAuthority,
|
|
26685
|
-
|
|
26738
|
+
reescapeHostDelimiters,
|
|
26739
|
+
normalizePercentEncoding,
|
|
26740
|
+
normalizePathEncoding,
|
|
26741
|
+
escapePreservingEscapes,
|
|
26686
26742
|
removeDotSegments,
|
|
26687
26743
|
isIPv4,
|
|
26688
26744
|
isUUID,
|
|
@@ -26692,9 +26748,9 @@ var require_utils = __commonJS({
|
|
|
26692
26748
|
}
|
|
26693
26749
|
});
|
|
26694
26750
|
|
|
26695
|
-
// ../../node_modules/.pnpm/fast-uri@3.1.
|
|
26751
|
+
// ../../node_modules/.pnpm/fast-uri@3.1.2/node_modules/fast-uri/lib/schemes.js
|
|
26696
26752
|
var require_schemes = __commonJS({
|
|
26697
|
-
"../../node_modules/.pnpm/fast-uri@3.1.
|
|
26753
|
+
"../../node_modules/.pnpm/fast-uri@3.1.2/node_modules/fast-uri/lib/schemes.js"(exports2, module2) {
|
|
26698
26754
|
"use strict";
|
|
26699
26755
|
init_cjs_shims();
|
|
26700
26756
|
var { isUUID } = require_utils();
|
|
@@ -26903,17 +26959,17 @@ var require_schemes = __commonJS({
|
|
|
26903
26959
|
}
|
|
26904
26960
|
});
|
|
26905
26961
|
|
|
26906
|
-
// ../../node_modules/.pnpm/fast-uri@3.1.
|
|
26962
|
+
// ../../node_modules/.pnpm/fast-uri@3.1.2/node_modules/fast-uri/index.js
|
|
26907
26963
|
var require_fast_uri = __commonJS({
|
|
26908
|
-
"../../node_modules/.pnpm/fast-uri@3.1.
|
|
26964
|
+
"../../node_modules/.pnpm/fast-uri@3.1.2/node_modules/fast-uri/index.js"(exports2, module2) {
|
|
26909
26965
|
"use strict";
|
|
26910
26966
|
init_cjs_shims();
|
|
26911
|
-
var { normalizeIPv6, removeDotSegments, recomposeAuthority,
|
|
26967
|
+
var { normalizeIPv6, removeDotSegments, recomposeAuthority, normalizePercentEncoding, normalizePathEncoding, escapePreservingEscapes, reescapeHostDelimiters, isIPv4, nonSimpleDomain } = require_utils();
|
|
26912
26968
|
var { SCHEMES, getSchemeHandler } = require_schemes();
|
|
26913
26969
|
function normalize(uri, options) {
|
|
26914
26970
|
if (typeof uri === "string") {
|
|
26915
26971
|
uri = /** @type {T} */
|
|
26916
|
-
|
|
26972
|
+
normalizeString(uri, options);
|
|
26917
26973
|
} else if (typeof uri === "object") {
|
|
26918
26974
|
uri = /** @type {T} */
|
|
26919
26975
|
parse(serialize(uri, options), options);
|
|
@@ -26980,19 +27036,9 @@ var require_fast_uri = __commonJS({
|
|
|
26980
27036
|
return target;
|
|
26981
27037
|
}
|
|
26982
27038
|
function equal(uriA, uriB, options) {
|
|
26983
|
-
|
|
26984
|
-
|
|
26985
|
-
|
|
26986
|
-
} else if (typeof uriA === "object") {
|
|
26987
|
-
uriA = serialize(normalizeComponentEncoding(uriA, true), { ...options, skipEscape: true });
|
|
26988
|
-
}
|
|
26989
|
-
if (typeof uriB === "string") {
|
|
26990
|
-
uriB = unescape(uriB);
|
|
26991
|
-
uriB = serialize(normalizeComponentEncoding(parse(uriB, options), true), { ...options, skipEscape: true });
|
|
26992
|
-
} else if (typeof uriB === "object") {
|
|
26993
|
-
uriB = serialize(normalizeComponentEncoding(uriB, true), { ...options, skipEscape: true });
|
|
26994
|
-
}
|
|
26995
|
-
return uriA.toLowerCase() === uriB.toLowerCase();
|
|
27039
|
+
const normalizedA = normalizeComparableURI(uriA, options);
|
|
27040
|
+
const normalizedB = normalizeComparableURI(uriB, options);
|
|
27041
|
+
return normalizedA !== void 0 && normalizedB !== void 0 && normalizedA.toLowerCase() === normalizedB.toLowerCase();
|
|
26996
27042
|
}
|
|
26997
27043
|
function serialize(cmpts, opts) {
|
|
26998
27044
|
const component = {
|
|
@@ -27017,12 +27063,12 @@ var require_fast_uri = __commonJS({
|
|
|
27017
27063
|
if (schemeHandler && schemeHandler.serialize) schemeHandler.serialize(component, options);
|
|
27018
27064
|
if (component.path !== void 0) {
|
|
27019
27065
|
if (!options.skipEscape) {
|
|
27020
|
-
component.path =
|
|
27066
|
+
component.path = escapePreservingEscapes(component.path);
|
|
27021
27067
|
if (component.scheme !== void 0) {
|
|
27022
27068
|
component.path = component.path.split("%3A").join(":");
|
|
27023
27069
|
}
|
|
27024
27070
|
} else {
|
|
27025
|
-
component.path =
|
|
27071
|
+
component.path = normalizePercentEncoding(component.path);
|
|
27026
27072
|
}
|
|
27027
27073
|
}
|
|
27028
27074
|
if (options.reference !== "suffix" && component.scheme) {
|
|
@@ -27057,7 +27103,16 @@ var require_fast_uri = __commonJS({
|
|
|
27057
27103
|
return uriTokens.join("");
|
|
27058
27104
|
}
|
|
27059
27105
|
var URI_PARSE = /^(?:([^#/:?]+):)?(?:\/\/((?:([^#/?@]*)@)?(\[[^#/?\]]+\]|[^#/:?]*)(?::(\d*))?))?([^#?]*)(?:\?([^#]*))?(?:#((?:.|[\n\r])*))?/u;
|
|
27060
|
-
function
|
|
27106
|
+
function getParseError(parsed, matches) {
|
|
27107
|
+
if (matches[2] !== void 0 && parsed.path && parsed.path[0] !== "/") {
|
|
27108
|
+
return 'URI path must start with "/" when authority is present.';
|
|
27109
|
+
}
|
|
27110
|
+
if (typeof parsed.port === "number" && (parsed.port < 0 || parsed.port > 65535)) {
|
|
27111
|
+
return "URI port is malformed.";
|
|
27112
|
+
}
|
|
27113
|
+
return void 0;
|
|
27114
|
+
}
|
|
27115
|
+
function parseWithStatus(uri, opts) {
|
|
27061
27116
|
const options = Object.assign({}, opts);
|
|
27062
27117
|
const parsed = {
|
|
27063
27118
|
scheme: void 0,
|
|
@@ -27068,6 +27123,7 @@ var require_fast_uri = __commonJS({
|
|
|
27068
27123
|
query: void 0,
|
|
27069
27124
|
fragment: void 0
|
|
27070
27125
|
};
|
|
27126
|
+
let malformedAuthorityOrPort = false;
|
|
27071
27127
|
let isIP = false;
|
|
27072
27128
|
if (options.reference === "suffix") {
|
|
27073
27129
|
if (options.scheme) {
|
|
@@ -27088,6 +27144,11 @@ var require_fast_uri = __commonJS({
|
|
|
27088
27144
|
if (isNaN(parsed.port)) {
|
|
27089
27145
|
parsed.port = matches[5];
|
|
27090
27146
|
}
|
|
27147
|
+
const parseError = getParseError(parsed, matches);
|
|
27148
|
+
if (parseError !== void 0) {
|
|
27149
|
+
parsed.error = parsed.error || parseError;
|
|
27150
|
+
malformedAuthorityOrPort = true;
|
|
27151
|
+
}
|
|
27091
27152
|
if (parsed.host) {
|
|
27092
27153
|
const ipv4result = isIPv4(parsed.host);
|
|
27093
27154
|
if (ipv4result === false) {
|
|
@@ -27126,14 +27187,18 @@ var require_fast_uri = __commonJS({
|
|
|
27126
27187
|
parsed.scheme = unescape(parsed.scheme);
|
|
27127
27188
|
}
|
|
27128
27189
|
if (parsed.host !== void 0) {
|
|
27129
|
-
parsed.host = unescape(parsed.host);
|
|
27190
|
+
parsed.host = reescapeHostDelimiters(unescape(parsed.host), isIP);
|
|
27130
27191
|
}
|
|
27131
27192
|
}
|
|
27132
27193
|
if (parsed.path) {
|
|
27133
|
-
parsed.path =
|
|
27194
|
+
parsed.path = normalizePathEncoding(parsed.path);
|
|
27134
27195
|
}
|
|
27135
27196
|
if (parsed.fragment) {
|
|
27136
|
-
|
|
27197
|
+
try {
|
|
27198
|
+
parsed.fragment = encodeURI(decodeURIComponent(parsed.fragment));
|
|
27199
|
+
} catch {
|
|
27200
|
+
parsed.error = parsed.error || "URI malformed";
|
|
27201
|
+
}
|
|
27137
27202
|
}
|
|
27138
27203
|
}
|
|
27139
27204
|
if (schemeHandler && schemeHandler.parse) {
|
|
@@ -27142,7 +27207,29 @@ var require_fast_uri = __commonJS({
|
|
|
27142
27207
|
} else {
|
|
27143
27208
|
parsed.error = parsed.error || "URI can not be parsed.";
|
|
27144
27209
|
}
|
|
27145
|
-
return parsed;
|
|
27210
|
+
return { parsed, malformedAuthorityOrPort };
|
|
27211
|
+
}
|
|
27212
|
+
function parse(uri, opts) {
|
|
27213
|
+
return parseWithStatus(uri, opts).parsed;
|
|
27214
|
+
}
|
|
27215
|
+
function normalizeString(uri, opts) {
|
|
27216
|
+
return normalizeStringWithStatus(uri, opts).normalized;
|
|
27217
|
+
}
|
|
27218
|
+
function normalizeStringWithStatus(uri, opts) {
|
|
27219
|
+
const { parsed, malformedAuthorityOrPort } = parseWithStatus(uri, opts);
|
|
27220
|
+
return {
|
|
27221
|
+
normalized: malformedAuthorityOrPort ? uri : serialize(parsed, opts),
|
|
27222
|
+
malformedAuthorityOrPort
|
|
27223
|
+
};
|
|
27224
|
+
}
|
|
27225
|
+
function normalizeComparableURI(uri, opts) {
|
|
27226
|
+
if (typeof uri === "string") {
|
|
27227
|
+
const { normalized, malformedAuthorityOrPort } = normalizeStringWithStatus(uri, opts);
|
|
27228
|
+
return malformedAuthorityOrPort ? void 0 : normalized;
|
|
27229
|
+
}
|
|
27230
|
+
if (typeof uri === "object") {
|
|
27231
|
+
return serialize(uri, opts);
|
|
27232
|
+
}
|
|
27146
27233
|
}
|
|
27147
27234
|
var fastUri = {
|
|
27148
27235
|
SCHEMES,
|
|
@@ -31377,7 +31464,7 @@ var init_release = __esm({
|
|
|
31377
31464
|
"../../packages/shared/src/release.ts"() {
|
|
31378
31465
|
"use strict";
|
|
31379
31466
|
init_cjs_shims();
|
|
31380
|
-
BOOTSPRING_VERSION = "3.
|
|
31467
|
+
BOOTSPRING_VERSION = "3.2.0";
|
|
31381
31468
|
BOOTSPRING_PACKAGE_NAME = "@girardmedia/bootspring";
|
|
31382
31469
|
}
|
|
31383
31470
|
});
|
|
@@ -47601,7 +47688,7 @@ var require_dist2 = __commonJS({
|
|
|
47601
47688
|
));
|
|
47602
47689
|
var __toCommonJS2 = (mod) => __copyProps2(__defProp2({}, "__esModule", { value: true }), mod);
|
|
47603
47690
|
var init_cjs_shims2 = __esm2({
|
|
47604
|
-
"../../node_modules/.pnpm/tsup@8.5.1_jiti@1.21.7_postcss@8.5.
|
|
47691
|
+
"../../node_modules/.pnpm/tsup@8.5.1_jiti@1.21.7_postcss@8.5.14_tsx@4.21.0_typescript@5.9.3_yaml@2.8.3/node_modules/tsup/assets/cjs_shims.js"() {
|
|
47605
47692
|
"use strict";
|
|
47606
47693
|
}
|
|
47607
47694
|
});
|
|
@@ -52278,7 +52365,7 @@ var require_package = __commonJS({
|
|
|
52278
52365
|
"../../../package.json"(exports2, module2) {
|
|
52279
52366
|
module2.exports = {
|
|
52280
52367
|
name: "bootspring-workspace",
|
|
52281
|
-
version: "3.
|
|
52368
|
+
version: "3.2.0",
|
|
52282
52369
|
private: true,
|
|
52283
52370
|
description: "Workspace tooling for the Bootspring monorepo",
|
|
52284
52371
|
keywords: [
|
|
@@ -52382,8 +52469,10 @@ var require_package = __commonJS({
|
|
|
52382
52469
|
ajv: "^8.12.0"
|
|
52383
52470
|
},
|
|
52384
52471
|
minimatch: "^10.2.1",
|
|
52385
|
-
hono: "4.12.
|
|
52386
|
-
"@hono/node-server": "1.19.
|
|
52472
|
+
hono: "4.12.18",
|
|
52473
|
+
"@hono/node-server": "1.19.13",
|
|
52474
|
+
axios: ">=1.16.0",
|
|
52475
|
+
"simple-git": ">=3.36.0",
|
|
52387
52476
|
"express-rate-limit": "^8.2.2",
|
|
52388
52477
|
"path-to-regexp@<0.1.13": "0.1.13",
|
|
52389
52478
|
"path-to-regexp@>=8.0.0 <8.4.0": "8.4.0",
|
|
@@ -52414,6 +52503,15 @@ var core = require_dist2();
|
|
|
52414
52503
|
var api2 = core.api;
|
|
52415
52504
|
var auth2 = core.auth;
|
|
52416
52505
|
var VERSION = require_package().version;
|
|
52506
|
+
var BUILD_PROGRESS_ARTIFACT = "planning/BUILD_PROGRESS.md";
|
|
52507
|
+
var VISIBLE_WORK_CHECKPOINTS = [
|
|
52508
|
+
"Announce the active task and intended checkpoints in the chat or terminal.",
|
|
52509
|
+
"Inspect the relevant files and summarize what changed before editing.",
|
|
52510
|
+
"Patch the implementation and keep unrelated files untouched.",
|
|
52511
|
+
"Add or update focused tests when the task changes behavior.",
|
|
52512
|
+
"Run the relevant verification commands and report pass/fail output.",
|
|
52513
|
+
"Update the build state with action=done only after verification."
|
|
52514
|
+
];
|
|
52417
52515
|
function getProjectRoot() {
|
|
52418
52516
|
return process.cwd();
|
|
52419
52517
|
}
|
|
@@ -52486,9 +52584,164 @@ function formatBuildTask(task) {
|
|
|
52486
52584
|
phase: task.phase,
|
|
52487
52585
|
source: task.source,
|
|
52488
52586
|
sourceSection: task.sourceSection,
|
|
52489
|
-
acceptanceCriteria: task.acceptanceCriteria || []
|
|
52587
|
+
acceptanceCriteria: task.acceptanceCriteria || [],
|
|
52588
|
+
dependencies: task.dependencies || []
|
|
52589
|
+
};
|
|
52590
|
+
}
|
|
52591
|
+
function renderVisibleProgressBar(percent, width = 30) {
|
|
52592
|
+
const clamped = Math.max(0, Math.min(100, Math.round(percent || 0)));
|
|
52593
|
+
const filled = Math.round(clamped / 100 * width);
|
|
52594
|
+
return `[${"#".repeat(filled)}${".".repeat(width - filled)}] ${clamped}%`;
|
|
52595
|
+
}
|
|
52596
|
+
function writeVisibleProgressArtifact(task, stats, event) {
|
|
52597
|
+
const fs3 = require("fs");
|
|
52598
|
+
const path3 = require("path");
|
|
52599
|
+
const artifactPath = path3.join(getProjectRoot(), BUILD_PROGRESS_ARTIFACT);
|
|
52600
|
+
const progress = {
|
|
52601
|
+
completed: stats?.completed || 0,
|
|
52602
|
+
pending: stats?.pending || 0,
|
|
52603
|
+
inProgress: stats?.inProgress || 0,
|
|
52604
|
+
total: stats?.total || 0,
|
|
52605
|
+
percent: stats?.percent || 0
|
|
52606
|
+
};
|
|
52607
|
+
const taskData = formatBuildTask(task);
|
|
52608
|
+
fs3.mkdirSync(path3.dirname(artifactPath), { recursive: true });
|
|
52609
|
+
fs3.writeFileSync(artifactPath, [
|
|
52610
|
+
"# Build Progress",
|
|
52611
|
+
"",
|
|
52612
|
+
`Updated: ${(/* @__PURE__ */ new Date()).toISOString()}`,
|
|
52613
|
+
`Event: ${event}`,
|
|
52614
|
+
`Overall: ${progress.completed}/${progress.total} ${renderVisibleProgressBar(progress.percent)}`,
|
|
52615
|
+
"",
|
|
52616
|
+
"## Active Task",
|
|
52617
|
+
"",
|
|
52618
|
+
taskData ? [
|
|
52619
|
+
`- ID: ${taskData.id}`,
|
|
52620
|
+
`- Title: ${taskData.title}`,
|
|
52621
|
+
`- Phase: ${taskData.phase || "Unknown"}`,
|
|
52622
|
+
taskData.description ? `- Description: ${taskData.description}` : null,
|
|
52623
|
+
taskData.dependencies?.length ? `- Dependencies: ${taskData.dependencies.join(", ")}` : null
|
|
52624
|
+
].filter(Boolean).join("\n") : "No active or pending task.",
|
|
52625
|
+
"",
|
|
52626
|
+
"## Acceptance Criteria",
|
|
52627
|
+
"",
|
|
52628
|
+
taskData?.acceptanceCriteria?.length ? taskData.acceptanceCriteria.map((item) => `- [ ] ${item}`).join("\n") : "- [ ] No task-specific acceptance criteria recorded.",
|
|
52629
|
+
"",
|
|
52630
|
+
"## Visible Work Contract",
|
|
52631
|
+
"",
|
|
52632
|
+
VISIBLE_WORK_CHECKPOINTS.map((item) => `- [ ] ${item}`).join("\n"),
|
|
52633
|
+
"",
|
|
52634
|
+
"## Host Notes",
|
|
52635
|
+
"",
|
|
52636
|
+
"- Claude Code and Codex CLI can stream terminal output directly.",
|
|
52637
|
+
"- Codex Desktop, Claude Desktop, and other MCP hosts should keep a visible plan/checklist updated while the terminal is hidden.",
|
|
52638
|
+
"- Update this file after inspection, edits, verification, and completion so the workspace has a durable progress surface.",
|
|
52639
|
+
""
|
|
52640
|
+
].join("\n"));
|
|
52641
|
+
return BUILD_PROGRESS_ARTIFACT;
|
|
52642
|
+
}
|
|
52643
|
+
function buildVisibilityContract(task, stats, event) {
|
|
52644
|
+
const artifact = writeVisibleProgressArtifact(task, stats, event);
|
|
52645
|
+
return {
|
|
52646
|
+
artifact,
|
|
52647
|
+
checkpoints: VISIBLE_WORK_CHECKPOINTS,
|
|
52648
|
+
hostInstructions: [
|
|
52649
|
+
"Immediately show the active task and a short visible checklist to the user.",
|
|
52650
|
+
"Post visible updates after inspection, implementation, verification, and completion.",
|
|
52651
|
+
`Keep ${artifact} current when the host hides terminal output.`
|
|
52652
|
+
]
|
|
52490
52653
|
};
|
|
52491
52654
|
}
|
|
52655
|
+
function autoCommitTask(taskId, taskTitle) {
|
|
52656
|
+
try {
|
|
52657
|
+
const { execSync } = require("child_process");
|
|
52658
|
+
const cwd = getProjectRoot();
|
|
52659
|
+
const status = execSync("git status --porcelain", { cwd, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim();
|
|
52660
|
+
if (!status) return { committed: false, reason: "no_changes" };
|
|
52661
|
+
execSync("git add -A", { cwd, stdio: ["ignore", "ignore", "ignore"] });
|
|
52662
|
+
const msg = `feat(${taskId}): ${taskTitle}`;
|
|
52663
|
+
execSync(`git commit -m ${JSON.stringify(msg)}`, { cwd, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] });
|
|
52664
|
+
return { committed: true, message: msg };
|
|
52665
|
+
} catch (e) {
|
|
52666
|
+
return { committed: false, reason: "git_error", error: String(e?.message || e).slice(0, 200) };
|
|
52667
|
+
}
|
|
52668
|
+
}
|
|
52669
|
+
function verifyQuality() {
|
|
52670
|
+
try {
|
|
52671
|
+
const { execSync } = require("child_process");
|
|
52672
|
+
const cwd = getProjectRoot();
|
|
52673
|
+
const fs3 = require("fs");
|
|
52674
|
+
const path3 = require("path");
|
|
52675
|
+
const pkgPath = path3.join(cwd, "package.json");
|
|
52676
|
+
if (!fs3.existsSync(pkgPath)) return { verified: true, skipped: true };
|
|
52677
|
+
const pkg = JSON.parse(fs3.readFileSync(pkgPath, "utf-8"));
|
|
52678
|
+
const hasTest = pkg.scripts?.test;
|
|
52679
|
+
const hasTypecheck = pkg.scripts?.typecheck;
|
|
52680
|
+
const results = { verified: true, checks: [] };
|
|
52681
|
+
if (hasTypecheck) {
|
|
52682
|
+
try {
|
|
52683
|
+
execSync("npm run typecheck 2>&1", { cwd, encoding: "utf8", timeout: 6e4, stdio: ["ignore", "pipe", "pipe"] });
|
|
52684
|
+
results.checks.push({ name: "typecheck", passed: true });
|
|
52685
|
+
} catch (e) {
|
|
52686
|
+
results.checks.push({ name: "typecheck", passed: false, error: String(e?.stdout || e?.message || "").slice(0, 500) });
|
|
52687
|
+
results.verified = false;
|
|
52688
|
+
}
|
|
52689
|
+
}
|
|
52690
|
+
if (hasTest && pkg.devDependencies?.vitest) {
|
|
52691
|
+
try {
|
|
52692
|
+
const changed = execSync("git diff --name-only HEAD 2>/dev/null || true", { cwd, encoding: "utf8" }).trim();
|
|
52693
|
+
const testFiles = changed.split("\n").filter((f) => f.match(/\.(test|spec)\.(ts|js|tsx|jsx)$/));
|
|
52694
|
+
if (testFiles.length > 0) {
|
|
52695
|
+
execSync(`npx vitest run ${testFiles.join(" ")} 2>&1`, { cwd, encoding: "utf8", timeout: 12e4, stdio: ["ignore", "pipe", "pipe"] });
|
|
52696
|
+
results.checks.push({ name: "tests", passed: true, files: testFiles.length });
|
|
52697
|
+
} else {
|
|
52698
|
+
results.checks.push({ name: "tests", passed: true, skipped: true, reason: "no_changed_test_files" });
|
|
52699
|
+
}
|
|
52700
|
+
} catch (e) {
|
|
52701
|
+
results.checks.push({ name: "tests", passed: false, error: String(e?.stdout || e?.message || "").slice(0, 500) });
|
|
52702
|
+
results.verified = false;
|
|
52703
|
+
}
|
|
52704
|
+
}
|
|
52705
|
+
return results;
|
|
52706
|
+
} catch {
|
|
52707
|
+
return { verified: true, skipped: true };
|
|
52708
|
+
}
|
|
52709
|
+
}
|
|
52710
|
+
function loadHandoff() {
|
|
52711
|
+
const fs3 = require("fs");
|
|
52712
|
+
const path3 = require("path");
|
|
52713
|
+
const handoffPath = path3.join(getProjectRoot(), "planning", ".handoff.json");
|
|
52714
|
+
if (!fs3.existsSync(handoffPath)) return null;
|
|
52715
|
+
try {
|
|
52716
|
+
return JSON.parse(fs3.readFileSync(handoffPath, "utf-8"));
|
|
52717
|
+
} catch {
|
|
52718
|
+
return null;
|
|
52719
|
+
}
|
|
52720
|
+
}
|
|
52721
|
+
function saveHandoff(data) {
|
|
52722
|
+
const fs3 = require("fs");
|
|
52723
|
+
const path3 = require("path");
|
|
52724
|
+
const planDir = path3.join(getProjectRoot(), "planning");
|
|
52725
|
+
if (!fs3.existsSync(planDir)) fs3.mkdirSync(planDir, { recursive: true });
|
|
52726
|
+
fs3.writeFileSync(path3.join(planDir, ".handoff.json"), JSON.stringify(data, null, 2));
|
|
52727
|
+
}
|
|
52728
|
+
function getBatchTasks(state, count) {
|
|
52729
|
+
const q = state?.implementationQueue || [];
|
|
52730
|
+
const pending = [];
|
|
52731
|
+
for (const task of q) {
|
|
52732
|
+
if (task.status !== "pending") continue;
|
|
52733
|
+
if (task.dependencies?.length > 0) {
|
|
52734
|
+
const allDone = task.dependencies.every((depId) => {
|
|
52735
|
+
const dep = q.find((t) => t.id === depId);
|
|
52736
|
+
return dep && dep.status === "completed";
|
|
52737
|
+
});
|
|
52738
|
+
if (!allDone) continue;
|
|
52739
|
+
}
|
|
52740
|
+
pending.push(task);
|
|
52741
|
+
if (pending.length >= count) break;
|
|
52742
|
+
}
|
|
52743
|
+
return pending;
|
|
52744
|
+
}
|
|
52492
52745
|
async function executeLocalBuild(args) {
|
|
52493
52746
|
const action = args?.action || "status";
|
|
52494
52747
|
const state = loadBuildState();
|
|
@@ -52505,6 +52758,7 @@ async function executeLocalBuild(args) {
|
|
|
52505
52758
|
status: state.status,
|
|
52506
52759
|
progress: { completed: stats.completed, pending: stats.pending, inProgress: stats.inProgress, total: stats.total, percent: stats.percent },
|
|
52507
52760
|
currentTask: currentTask ? { id: currentTask.id, title: currentTask.title } : null,
|
|
52761
|
+
visibility: buildVisibilityContract(currentTask, stats, "status"),
|
|
52508
52762
|
nextAction: currentTask ? "Complete the current task, then use action=done" : "Use action=next to get the next task"
|
|
52509
52763
|
}, null, 2) }] };
|
|
52510
52764
|
}
|
|
@@ -52518,6 +52772,7 @@ async function executeLocalBuild(args) {
|
|
|
52518
52772
|
message: "Task already in progress",
|
|
52519
52773
|
state: "in_progress",
|
|
52520
52774
|
task: formatBuildTask(inProgress),
|
|
52775
|
+
visibility: buildVisibilityContract(inProgress, buildGetStats(state), "already_in_progress"),
|
|
52521
52776
|
hint: "Complete this task with action=done, or use action=skip to skip it"
|
|
52522
52777
|
}, null, 2) }] };
|
|
52523
52778
|
}
|
|
@@ -52534,6 +52789,7 @@ async function executeLocalBuild(args) {
|
|
|
52534
52789
|
state: "task_ready",
|
|
52535
52790
|
file: "planning/TODO.md",
|
|
52536
52791
|
progress: { completed: stats.completed, total: stats.total, percent: stats.percent },
|
|
52792
|
+
visibility: buildVisibilityContract(nextTask, stats, "task_ready"),
|
|
52537
52793
|
instructions: [
|
|
52538
52794
|
`Find ${nextTask.id} in planning/TODO.md for full details`,
|
|
52539
52795
|
"Implement the task in the codebase",
|
|
@@ -52554,6 +52810,7 @@ async function executeLocalBuild(args) {
|
|
|
52554
52810
|
}
|
|
52555
52811
|
return { content: [{ type: "text", text: JSON.stringify({
|
|
52556
52812
|
task: formatBuildTask(currentTask),
|
|
52813
|
+
visibility: buildVisibilityContract(currentTask, buildGetStats(state), "current"),
|
|
52557
52814
|
instructions: ["Implement this task in the codebase", "Run quality checks (lint, test)", "Commit your changes", "Use action=done to mark complete"]
|
|
52558
52815
|
}, null, 2) }] };
|
|
52559
52816
|
}
|
|
@@ -52565,6 +52822,8 @@ async function executeLocalBuild(args) {
|
|
|
52565
52822
|
if (!inProgress) {
|
|
52566
52823
|
return { content: [{ type: "text", text: JSON.stringify({ error: "No task currently in progress", hint: "Use action=next to get a task first" }, null, 2) }] };
|
|
52567
52824
|
}
|
|
52825
|
+
const quality = args?.verify !== false ? verifyQuality() : { verified: true, skipped: true };
|
|
52826
|
+
const commitResult = args?.autoCommit !== false ? autoCommitTask(inProgress.id, inProgress.title) : { committed: false, reason: "disabled" };
|
|
52568
52827
|
buildUpdateTaskStatus(state, inProgress.id, "completed");
|
|
52569
52828
|
const nextTask = buildGetNextTask(state);
|
|
52570
52829
|
if (nextTask) {
|
|
@@ -52574,10 +52833,14 @@ async function executeLocalBuild(args) {
|
|
|
52574
52833
|
const stats = buildGetStats(state);
|
|
52575
52834
|
return { content: [{ type: "text", text: JSON.stringify({
|
|
52576
52835
|
completed: { id: inProgress.id, title: inProgress.title },
|
|
52836
|
+
commit: commitResult,
|
|
52837
|
+
quality,
|
|
52577
52838
|
progress: { completed: stats.completed, total: stats.total, percent: stats.percent },
|
|
52578
52839
|
nextTask: nextTask ? { ...formatBuildTask(nextTask), file: "planning/TODO.md" } : null,
|
|
52579
52840
|
allComplete: !nextTask,
|
|
52580
|
-
|
|
52841
|
+
state: nextTask ? "advanced" : "all_complete",
|
|
52842
|
+
visibility: buildVisibilityContract(nextTask, stats, nextTask ? "advanced" : "all_complete"),
|
|
52843
|
+
message: nextTask ? `Done! Committed ${inProgress.id}. Next: ${nextTask.id} \u2014 ${nextTask.title}` : "Build complete! All tasks finished."
|
|
52581
52844
|
}, null, 2) }] };
|
|
52582
52845
|
}
|
|
52583
52846
|
case "skip": {
|
|
@@ -52597,6 +52860,7 @@ async function executeLocalBuild(args) {
|
|
|
52597
52860
|
return { content: [{ type: "text", text: JSON.stringify({
|
|
52598
52861
|
skipped: { id: inProgress.id, title: inProgress.title, reason: args?.reason || "Skipped" },
|
|
52599
52862
|
nextTask: nextTask ? formatBuildTask(nextTask) : null,
|
|
52863
|
+
visibility: buildVisibilityContract(nextTask, buildGetStats(state), nextTask ? "skipped_next_ready" : "skipped_all_done"),
|
|
52600
52864
|
message: nextTask ? `Skipped. Next: ${nextTask.title}` : "No more tasks available"
|
|
52601
52865
|
}, null, 2) }] };
|
|
52602
52866
|
}
|
|
@@ -52622,13 +52886,14 @@ async function executeLocalBuild(args) {
|
|
|
52622
52886
|
const nextTask2 = buildGetNextTask(state);
|
|
52623
52887
|
if (nextTask2) buildUpdateTaskStatus(state, nextTask2.id, "in_progress");
|
|
52624
52888
|
saveBuildState(state);
|
|
52625
|
-
const
|
|
52889
|
+
const stats2 = buildGetStats(state);
|
|
52626
52890
|
return { content: [{ type: "text", text: JSON.stringify({
|
|
52627
52891
|
state: nextTask2 ? "advanced" : "all_complete",
|
|
52628
52892
|
autoCompleted: true,
|
|
52629
52893
|
completed: { id: activeTask.id, title: activeTask.title },
|
|
52630
52894
|
nextTask: nextTask2 ? formatBuildTask(nextTask2) : null,
|
|
52631
|
-
progress: { completed:
|
|
52895
|
+
progress: { completed: stats2.completed, total: stats2.total, percent: stats2.percent },
|
|
52896
|
+
visibility: buildVisibilityContract(nextTask2, stats2, nextTask2 ? "advanced" : "all_complete")
|
|
52632
52897
|
}, null, 2) }] };
|
|
52633
52898
|
}
|
|
52634
52899
|
} catch {
|
|
@@ -52637,20 +52902,23 @@ async function executeLocalBuild(args) {
|
|
|
52637
52902
|
return { content: [{ type: "text", text: JSON.stringify({
|
|
52638
52903
|
state: "in_progress",
|
|
52639
52904
|
task: formatBuildTask(activeTask),
|
|
52905
|
+
visibility: buildVisibilityContract(activeTask, buildGetStats(state), "in_progress"),
|
|
52640
52906
|
hint: "Finish implementation and use action=done, or call action=advance with autoDone=true after commit"
|
|
52641
52907
|
}, null, 2) }] };
|
|
52642
52908
|
}
|
|
52643
52909
|
const nextTask = buildGetNextTask(state);
|
|
52644
52910
|
if (!nextTask) {
|
|
52645
|
-
const
|
|
52646
|
-
return { content: [{ type: "text", text: JSON.stringify({ state: "all_complete", message: "All tasks complete!", progress: { completed:
|
|
52911
|
+
const stats2 = buildGetStats(state);
|
|
52912
|
+
return { content: [{ type: "text", text: JSON.stringify({ state: "all_complete", message: "All tasks complete!", progress: { completed: stats2.completed, total: stats2.total, percent: stats2.percent }, visibility: buildVisibilityContract(null, stats2, "all_complete") }, null, 2) }] };
|
|
52647
52913
|
}
|
|
52648
52914
|
buildUpdateTaskStatus(state, nextTask.id, "in_progress");
|
|
52649
52915
|
saveBuildState(state);
|
|
52916
|
+
const stats = buildGetStats(state);
|
|
52650
52917
|
return { content: [{ type: "text", text: JSON.stringify({
|
|
52651
52918
|
state: "task_ready",
|
|
52652
52919
|
task: formatBuildTask(nextTask),
|
|
52653
52920
|
file: "planning/TODO.md",
|
|
52921
|
+
visibility: buildVisibilityContract(nextTask, stats, "task_ready"),
|
|
52654
52922
|
hint: "Implement task, then call action=advance (or action=done) to continue loop"
|
|
52655
52923
|
}, null, 2) }] };
|
|
52656
52924
|
}
|
|
@@ -52799,8 +53067,66 @@ async function executeLocalBuild(args) {
|
|
|
52799
53067
|
message: marked.length > 0 ? `Marked ${marked.length} task${marked.length === 1 ? "" : "s"} as completed from git history` : `Scanned ${lines.length} commits, found ${foundIds.size} bs-IDs, but none matched pending/in-progress tasks`
|
|
52800
53068
|
}, null, 2) }] };
|
|
52801
53069
|
}
|
|
53070
|
+
case "handoff": {
|
|
53071
|
+
if (args?.save) {
|
|
53072
|
+
const handoffData = {
|
|
53073
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
53074
|
+
lastTaskId: args.lastTaskId || null,
|
|
53075
|
+
inProgressTask: state ? (state.implementationQueue || []).find((t) => t.status === "in_progress")?.id || null : null,
|
|
53076
|
+
filesModified: args.filesModified || [],
|
|
53077
|
+
notes: args.notes || "",
|
|
53078
|
+
blockers: args.blockers || [],
|
|
53079
|
+
progress: state ? buildGetStats(state) : null
|
|
53080
|
+
};
|
|
53081
|
+
saveHandoff(handoffData);
|
|
53082
|
+
return { content: [{ type: "text", text: JSON.stringify({
|
|
53083
|
+
saved: true,
|
|
53084
|
+
handoff: handoffData,
|
|
53085
|
+
message: "Session handoff saved. Next session will pick up from here."
|
|
53086
|
+
}, null, 2) }] };
|
|
53087
|
+
}
|
|
53088
|
+
const handoff = loadHandoff();
|
|
53089
|
+
if (!handoff) {
|
|
53090
|
+
return { content: [{ type: "text", text: JSON.stringify({
|
|
53091
|
+
hasHandoff: false,
|
|
53092
|
+
message: "No previous session handoff found. Start fresh with action=next."
|
|
53093
|
+
}, null, 2) }] };
|
|
53094
|
+
}
|
|
53095
|
+
const currentState = state ? buildGetStats(state) : null;
|
|
53096
|
+
return { content: [{ type: "text", text: JSON.stringify({
|
|
53097
|
+
hasHandoff: true,
|
|
53098
|
+
handoff,
|
|
53099
|
+
currentProgress: currentState,
|
|
53100
|
+
message: `Previous session left off at ${handoff.inProgressTask || handoff.lastTaskId || "unknown"}. ${handoff.notes || "No notes."}`,
|
|
53101
|
+
suggestedAction: handoff.inProgressTask ? "action=current to see the in-progress task" : "action=next to continue"
|
|
53102
|
+
}, null, 2) }] };
|
|
53103
|
+
}
|
|
53104
|
+
case "batch": {
|
|
53105
|
+
if (!state) {
|
|
53106
|
+
return { content: [{ type: "text", text: JSON.stringify({ error: "No build state found" }, null, 2) }] };
|
|
53107
|
+
}
|
|
53108
|
+
const count = Math.min(Math.max(args?.count || 5, 1), 10);
|
|
53109
|
+
const batchTasks = getBatchTasks(state, count);
|
|
53110
|
+
if (batchTasks.length === 0) {
|
|
53111
|
+
const stats = buildGetStats(state);
|
|
53112
|
+
return { content: [{ type: "text", text: JSON.stringify({
|
|
53113
|
+
message: "No eligible pending tasks",
|
|
53114
|
+
progress: stats
|
|
53115
|
+
}, null, 2) }] };
|
|
53116
|
+
}
|
|
53117
|
+
return { content: [{ type: "text", text: JSON.stringify({
|
|
53118
|
+
batch: batchTasks.map(formatBuildTask),
|
|
53119
|
+
count: batchTasks.length,
|
|
53120
|
+
instructions: [
|
|
53121
|
+
"Implement all tasks in this batch",
|
|
53122
|
+
"For each completed task, call action=done",
|
|
53123
|
+
"Tasks are ordered by dependency \u2014 complete them in order",
|
|
53124
|
+
"If stuck on one, call action=skip and continue with the next"
|
|
53125
|
+
]
|
|
53126
|
+
}, null, 2) }] };
|
|
53127
|
+
}
|
|
52802
53128
|
default:
|
|
52803
|
-
return { content: [{ type: "text", text: JSON.stringify({ error: `Unknown action: ${action}`, validActions: ["next", "current", "done", "skip", "status", "list", "init", "sync", "advance", "scan"] }, null, 2) }] };
|
|
53129
|
+
return { content: [{ type: "text", text: JSON.stringify({ error: `Unknown action: ${action}`, validActions: ["next", "current", "done", "skip", "status", "list", "init", "sync", "advance", "scan", "handoff", "batch"] }, null, 2) }] };
|
|
52804
53130
|
}
|
|
52805
53131
|
}
|
|
52806
53132
|
async function executeLocalSeed(args) {
|
|
@@ -53058,13 +53384,31 @@ var FALLBACK_TOOLS = [
|
|
|
53058
53384
|
},
|
|
53059
53385
|
{
|
|
53060
53386
|
name: "bootspring_build",
|
|
53061
|
-
description:
|
|
53387
|
+
description: `Autonomous build loop. Manages task queue with auto-commit and quality verification.
|
|
53388
|
+
|
|
53389
|
+
Core loop: next -> implement -> done -> (auto-advances to next task)
|
|
53390
|
+
- action=done auto-commits with conventional message and returns next task
|
|
53391
|
+
- action=batch returns multiple tasks for efficient batch execution
|
|
53392
|
+
- action=handoff saves/loads session state for cross-session continuity
|
|
53393
|
+
- Dependencies are checked automatically \u2014 blocked tasks are skipped`,
|
|
53062
53394
|
inputSchema: {
|
|
53063
53395
|
type: "object",
|
|
53064
53396
|
properties: {
|
|
53065
|
-
action: {
|
|
53066
|
-
|
|
53067
|
-
|
|
53397
|
+
action: {
|
|
53398
|
+
type: "string",
|
|
53399
|
+
enum: ["next", "current", "done", "skip", "status", "list", "init", "sync", "advance", "scan", "handoff", "batch"],
|
|
53400
|
+
description: "next: get next task | done: mark complete + auto-commit + return next | batch: get N tasks | handoff: save/load session state | advance: autonomous state machine"
|
|
53401
|
+
},
|
|
53402
|
+
reason: { type: "string", description: "Reason for skipping (action=skip)" },
|
|
53403
|
+
autoDone: { type: "boolean", description: "Auto-complete if git is clean (action=advance)" },
|
|
53404
|
+
autoCommit: { type: "boolean", description: "Auto-commit on done (default: true)" },
|
|
53405
|
+
verify: { type: "boolean", description: "Run quality checks before done (default: true)" },
|
|
53406
|
+
count: { type: "number", description: "Number of tasks to return (action=batch, max 10)" },
|
|
53407
|
+
save: { type: "boolean", description: "Save handoff state (action=handoff)" },
|
|
53408
|
+
notes: { type: "string", description: "Handoff notes for next session (action=handoff save)" },
|
|
53409
|
+
filesModified: { type: "array", items: { type: "string" }, description: "Files modified this session (action=handoff save)" },
|
|
53410
|
+
blockers: { type: "array", items: { type: "string" }, description: "Unresolved blockers (action=handoff save)" },
|
|
53411
|
+
lastTaskId: { type: "string", description: "Last completed task ID (action=handoff save)" }
|
|
53068
53412
|
},
|
|
53069
53413
|
required: ["action"]
|
|
53070
53414
|
}
|