@opengsd/gsd-core 1.20.1 → 1.21.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/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/.opencode/plugins/gsd-core.js +33 -2
- package/agents/gsd-executor.md +1 -1
- package/agents/gsd-plan-checker.md +1 -1
- package/agents/gsd-planner.md +3 -3
- package/agents/gsd-verifier.md +2 -2
- package/bin/install.js +234 -126
- package/commands/gsd/autonomous.md +1 -1
- package/gsd-core/bin/gsd-tools.cjs +450 -753
- package/gsd-core/bin/lib/capability-activation.cjs +8 -2
- package/gsd-core/bin/lib/capability-command-router.cjs +6 -2
- package/gsd-core/bin/lib/capability-ledger.cjs +1 -1
- package/gsd-core/bin/lib/capability-lock.cjs +19 -27
- package/gsd-core/bin/lib/capability-registry.cjs +74 -71
- package/gsd-core/bin/lib/capability-validator.cjs +1 -0
- package/gsd-core/bin/lib/capability-writer.cjs +7 -4
- package/gsd-core/bin/lib/claude-orchestration-command-router.cjs +142 -62
- package/gsd-core/bin/lib/claude-orchestration.cjs +214 -122
- package/gsd-core/bin/lib/cli-surface.cjs +545 -0
- package/gsd-core/bin/lib/command-arg-projection.cjs +356 -103
- package/gsd-core/bin/lib/commands.cjs +80 -2
- package/gsd-core/bin/lib/config-file-read.cjs +85 -0
- package/gsd-core/bin/lib/config-loader.cjs +23 -15
- package/gsd-core/bin/lib/config.cjs +139 -101
- package/gsd-core/bin/lib/configuration.cjs +41 -2
- package/gsd-core/bin/lib/criterion-baseline.cjs +346 -0
- package/gsd-core/bin/lib/criterion-replay.cjs +380 -0
- package/gsd-core/bin/lib/estimate-cli.cjs +5 -1
- package/gsd-core/bin/lib/gap-checker.cjs +12 -1
- package/gsd-core/bin/lib/gate-evaluation-scope.cjs +593 -27
- package/gsd-core/bin/lib/gate-phase-context.cjs +16 -4
- package/gsd-core/bin/lib/graphify.cjs +19 -2
- package/gsd-core/bin/lib/health-diagnostic.cjs +5 -1
- package/gsd-core/bin/lib/init.cjs +13 -11
- package/gsd-core/bin/lib/install-effort-resolver.cjs +24 -4
- package/gsd-core/bin/lib/install-engine.cjs +11 -19
- package/gsd-core/bin/lib/install-fs-adapter.cjs +5 -1
- package/gsd-core/bin/lib/install-model-override-resolver.cjs +33 -3
- package/gsd-core/bin/lib/io.cjs +6 -0
- package/gsd-core/bin/lib/markdown-sectionizer.cjs +62 -31
- package/gsd-core/bin/lib/markdown-table.cjs +39 -1
- package/gsd-core/bin/lib/milestone.cjs +8 -6
- package/gsd-core/bin/lib/model-resolver.cjs +8 -4
- package/gsd-core/bin/lib/phase-entry-grammar.cjs +14 -0
- package/gsd-core/bin/lib/phase-locator.cjs +8 -3
- package/gsd-core/bin/lib/phase.cjs +107 -31
- package/gsd-core/bin/lib/plan-dependency-graph.cjs +46 -23
- package/gsd-core/bin/lib/planning-snapshot.cjs +8 -4
- package/gsd-core/bin/lib/planning-workspace.cjs +20 -10
- package/gsd-core/bin/lib/profile-output.cjs +9 -6
- package/gsd-core/bin/lib/project-context.cjs +439 -0
- package/gsd-core/bin/lib/project-root.cjs +9 -230
- package/gsd-core/bin/lib/quick-batch-dispatch.cjs +1 -1
- package/gsd-core/bin/lib/report-parser.cjs +16 -0
- package/gsd-core/bin/lib/roadmap-parser.cjs +328 -50
- package/gsd-core/bin/lib/roadmap-upgrade.cjs +24 -5
- package/gsd-core/bin/lib/roadmap.cjs +2 -2
- package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +7 -4
- package/gsd-core/bin/lib/runtime-artifact-install-plan.cjs +81 -3
- package/gsd-core/bin/lib/runtime-hooks-surface.cjs +279 -8
- package/gsd-core/bin/lib/runtime-slash.cjs +7 -2
- package/gsd-core/bin/lib/shell-command-projection.cjs +1010 -10
- package/gsd-core/bin/lib/stale-bake-guard.cjs +4 -1
- package/gsd-core/bin/lib/state-transition.cjs +91 -41
- package/gsd-core/bin/lib/state.cjs +446 -61
- package/gsd-core/bin/lib/surface.cjs +4 -7
- package/gsd-core/bin/lib/tdd-red-evidence.cjs +10 -3
- package/gsd-core/bin/lib/verify-command-router.cjs +6 -1
- package/gsd-core/bin/lib/verify.cjs +49 -6
- package/gsd-core/bin/lib/worktree-safety.cjs +8 -111
- package/gsd-core/bin/shared/config-schema.manifest.json +1 -0
- package/gsd-core/references/autonomous-smart-discuss.md +99 -18
- package/gsd-core/references/checkpoints.md +1 -1
- package/gsd-core/references/planner-guidance.md +11 -0
- package/gsd-core/references/planner-human-verify-mode.md +1 -1
- package/gsd-core/references/planner-reversibility.md +2 -0
- package/gsd-core/references/planner-verify-command-grounding.md +22 -6
- package/gsd-core/workflows/autonomous.md +4 -4
- package/gsd-core/workflows/code-review-fix.md +5 -7
- package/gsd-core/workflows/execute-phase.md +21 -7
- package/gsd-core/workflows/plan-phase/steps/plan-pre-dispatch.md +101 -0
- package/gsd-core/workflows/plan-phase.md +18 -19
- package/hooks/dist/gsd-agent-isolation-guard.js +3 -2
- package/hooks/dist/gsd-config-reload.js +2 -2
- package/hooks/dist/gsd-context-monitor.js +2 -1
- package/hooks/dist/gsd-cursor-subagent-start.js +8 -5
- package/hooks/dist/gsd-read-injection-scanner.js +2 -2
- package/hooks/dist/gsd-secret-read-guard.js +141 -6
- package/hooks/dist/gsd-statusline.js +3 -2
- package/hooks/dist/gsd-workflow-guard.js +2 -1
- package/hooks/dist/lib/config-read.js +49 -0
- package/hooks/dist/lib/git-cmd.js +3 -1
- package/hooks/dist/lib/git-head-tree.js +474 -0
- package/hooks/dist/lib/isolation-sentinel.js +97 -17
- package/hooks/dist/lib/secret-read-exemptions.js +161 -0
- package/hooks/gsd-agent-isolation-guard.js +3 -2
- package/hooks/gsd-config-reload.js +2 -2
- package/hooks/gsd-context-monitor.js +2 -1
- package/hooks/gsd-cursor-subagent-start.js +8 -5
- package/hooks/gsd-read-injection-scanner.js +2 -2
- package/hooks/gsd-secret-read-guard.js +141 -6
- package/hooks/gsd-statusline.js +3 -2
- package/hooks/gsd-workflow-guard.js +2 -1
- package/hooks/lib/config-read.js +49 -0
- package/hooks/lib/git-cmd.js +3 -1
- package/hooks/lib/git-head-tree.js +474 -0
- package/hooks/lib/isolation-sentinel.js +97 -17
- package/hooks/lib/secret-read-exemptions.js +161 -0
- package/package.json +7 -2
- package/pi/package-agents/gsd-executor.md +1 -1
- package/pi/package-agents/gsd-plan-checker.md +1 -1
- package/pi/package-agents/gsd-planner.md +3 -3
- package/pi/package-agents/gsd-verifier.md +2 -2
- package/pi/package-agents/manifest.json +4 -4
- package/pi/package-skills/gsd-autonomous/SKILL.md +1 -1
- package/scripts/changeset/cli.cjs +2 -2
- package/scripts/changeset/serialize.cjs +12 -2
- package/scripts/ci-compact-v8-coverage.cjs +204 -0
- package/scripts/ci-fix-next-rerun.cjs +149 -0
- package/scripts/ci-next-health.cjs +52 -9
- package/scripts/ci-test-scope.cjs +77 -71
- package/scripts/command-contract-helpers.cjs +56 -7
- package/scripts/docs-guard-registry.cjs +3 -0
- package/scripts/gen-cli-surface-manifest.cjs +287 -0
- package/scripts/gen-platform-conformance-tier.cjs +258 -19
- package/scripts/lib/macos-conformance-tier.generated.cjs +55 -47
- package/scripts/lib/platform-conformance-tier.generated.cjs +19 -64
- package/scripts/lib/shellcheck-fetch.cjs +71 -4
- package/scripts/lint-removed-but-needed.cjs +562 -50
- package/scripts/release-notes/changelog-release-notes.cjs +345 -0
- package/scripts/release-notes/discord-release-summary.cjs +82 -1
- package/scripts/timeout-evidence-from-ci.cjs +964 -0
- package/skills/gsd-autonomous/SKILL.md +1 -1
- package/vscode/package.json +1 -1
|
@@ -25,6 +25,7 @@ const capabilityRegistry = require('./capability-registry.cjs');
|
|
|
25
25
|
const { hostBehaviorsFor } = require('./runtime-name-policy.cjs');
|
|
26
26
|
const { resolveConfigHomeFromDescriptor } = require('./runtime-homes.cjs');
|
|
27
27
|
const { resolveActiveRuntime } = require('./runtime-slash.cjs');
|
|
28
|
+
const { readConfigFileBounded } = require('./config-file-read.cjs');
|
|
28
29
|
|
|
29
30
|
/**
|
|
30
31
|
* Runtimes whose agent config is static frontmatter/TOML baked at install time,
|
|
@@ -226,7 +227,9 @@ function _readRuntimeConfig(cwd, { fsStatSync = fs.statSync } = {}) {
|
|
|
226
227
|
const candidate = path.join(probe, '.planning', 'config.json');
|
|
227
228
|
try {
|
|
228
229
|
fsStatSync(candidate);
|
|
229
|
-
|
|
230
|
+
// Bounded read (#5759): a FIFO/device/oversized config throws and is walked past like a
|
|
231
|
+
// missing one; null (raced away after the stat) makes JSON.parse('') throw into the same catch.
|
|
232
|
+
const raw = readConfigFileBounded(candidate) ?? '';
|
|
230
233
|
const parsed = JSON.parse(raw);
|
|
231
234
|
if (parsed && typeof parsed === 'object') return parsed;
|
|
232
235
|
return {};
|
|
@@ -31,6 +31,8 @@ exports.applyPreserveWhenUnchanged = applyPreserveWhenUnchanged;
|
|
|
31
31
|
exports.applyStatePreservation = applyStatePreservation;
|
|
32
32
|
exports.transitionCore = transitionCore;
|
|
33
33
|
exports.sliceCurrentPositionSection = sliceCurrentPositionSection;
|
|
34
|
+
exports.readPlanPosition = readPlanPosition;
|
|
35
|
+
exports.readRecordedPlanTotal = readRecordedPlanTotal;
|
|
34
36
|
exports.isStatePastPhase = isStatePastPhase;
|
|
35
37
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
36
38
|
const frontmatter = require("./frontmatter.cjs");
|
|
@@ -1261,6 +1263,94 @@ function planNumberFrom(digits) {
|
|
|
1261
1263
|
const n = Number(digits);
|
|
1262
1264
|
return Number.isSafeInteger(n) ? n : null;
|
|
1263
1265
|
}
|
|
1266
|
+
/**
|
|
1267
|
+
* The one plan-position parse: legacy pair first, then the hybrid, then
|
|
1268
|
+
* compound. `advancePlanCore` and `readPlanPosition` both consume it, so the
|
|
1269
|
+
* precedence is decided in exactly one place (#5786).
|
|
1270
|
+
*
|
|
1271
|
+
* These branches decide ONE thing: which numbers the position is read from.
|
|
1272
|
+
* They deliberately do not record which FIELD supplied them, because the
|
|
1273
|
+
* advance write path writes every spelling back from its own raw text (#3791
|
|
1274
|
+
* review round 6, B1/M1).
|
|
1275
|
+
*
|
|
1276
|
+
* Every branch reads its numbers out of an ANCHORED match's capture groups.
|
|
1277
|
+
* Nothing here calls parseInt on a raw field value, so a value the grammar
|
|
1278
|
+
* does not fully describe cannot half-parse into a plausible number.
|
|
1279
|
+
*/
|
|
1280
|
+
function parsePlanFields(content) {
|
|
1281
|
+
const legacyPlan = (0, state_document_cjs_1.stateExtractField)(content, 'Current Plan');
|
|
1282
|
+
const legacyTotal = (0, state_document_cjs_1.stateExtractField)(content, 'Total Plans in Phase');
|
|
1283
|
+
const planField = (0, state_document_cjs_1.stateExtractField)(content, 'Plan');
|
|
1284
|
+
const legacyNMatch = legacyPlan ? PLAN_SHAPE_N.exec(legacyPlan) : null;
|
|
1285
|
+
const legacyNofMMatch = legacyPlan ? PLAN_SHAPE_N_OF_M.exec(legacyPlan) : null;
|
|
1286
|
+
const totalNMatch = legacyTotal ? PLAN_SHAPE_N.exec(legacyTotal) : null;
|
|
1287
|
+
const planNofMMatch = planField ? PLAN_SHAPE_N_OF_M.exec(planField) : null;
|
|
1288
|
+
const planNMatch = planField ? PLAN_SHAPE_N.exec(planField) : null;
|
|
1289
|
+
let current = null;
|
|
1290
|
+
let total = null;
|
|
1291
|
+
if (legacyPlan && legacyTotal && (legacyNMatch || legacyNofMMatch) && totalNMatch) {
|
|
1292
|
+
// Legacy pair wins whenever both fields are present and both are readable,
|
|
1293
|
+
// even if the Current Plan value also carries an "of M" — the explicit
|
|
1294
|
+
// sibling field is the stated intent, so it supplies the total.
|
|
1295
|
+
current = planNumberFrom((legacyNMatch ?? legacyNofMMatch)[1]);
|
|
1296
|
+
total = planNumberFrom(totalNMatch[1]);
|
|
1297
|
+
}
|
|
1298
|
+
else if (legacyNofMMatch) {
|
|
1299
|
+
// Hybrid: legacy field name, compound value, no readable Total Plans
|
|
1300
|
+
// sibling. Written by hand (and by agents) often enough to be worth
|
|
1301
|
+
// reading — #3784.
|
|
1302
|
+
current = planNumberFrom(legacyNofMMatch[1]);
|
|
1303
|
+
total = planNumberFrom(legacyNofMMatch[2]);
|
|
1304
|
+
}
|
|
1305
|
+
else if (planNofMMatch) {
|
|
1306
|
+
current = planNumberFrom(planNofMMatch[1]);
|
|
1307
|
+
total = planNumberFrom(planNofMMatch[2]);
|
|
1308
|
+
}
|
|
1309
|
+
// No branch for a bare `Plan: N` paired with a `Total Plans in Phase: M`
|
|
1310
|
+
// sibling and no `Current Plan` at all (#3791 review round 6, M2). A revision
|
|
1311
|
+
// of this PR accepted it; base did not (its `else if (planField)` arm had no
|
|
1312
|
+
// `of M` match and errored via NaN), and it is out of #3784's scope, which is
|
|
1313
|
+
// the hybrid `Current Plan: N of M`. It cannot be given the schema-row +
|
|
1314
|
+
// forcing-test coupling the other shapes have, either: `Plan` is body-only,
|
|
1315
|
+
// `buildStateFrontmatter` never reads it into frontmatter, so there is no
|
|
1316
|
+
// `current_*` key to hang a row on. An accepted shape with no schema row and
|
|
1317
|
+
// no forcing test is exactly the drift this diff is otherwise built to
|
|
1318
|
+
// prevent, so the shape is refused and named in the error instead.
|
|
1319
|
+
return { legacyPlan, planField, legacyNMatch, legacyNofMMatch, planNofMMatch, planNMatch, current, total };
|
|
1320
|
+
}
|
|
1321
|
+
/**
|
|
1322
|
+
* Read the recorded plan position out of a STATE.md document through the one
|
|
1323
|
+
* plan-position grammar, or `null` when no readable position is recorded
|
|
1324
|
+
* (`Plan: 1 of ?`, `Total Plans in Phase: TBD`, no field at all). #5786.
|
|
1325
|
+
*/
|
|
1326
|
+
function readPlanPosition(content) {
|
|
1327
|
+
const { current, total } = parsePlanFields(content);
|
|
1328
|
+
return current === null || total === null ? null : { current, total };
|
|
1329
|
+
}
|
|
1330
|
+
/**
|
|
1331
|
+
* The plan total a STATE.md document records, for the `init --validate`
|
|
1332
|
+
* plan-count check: a readable `Total Plans in Phase` first, else the
|
|
1333
|
+
* of-M total read straight from the plan fields: `Current Plan`'s of-M is
|
|
1334
|
+
* tried first, then `Plan`'s, and convertibility is checked per field (an
|
|
1335
|
+
* unconvertible `Current Plan` M falls through to `Plan`'s M), unlike
|
|
1336
|
+
* `parsePlanFields`, which commits to the first matching field. The fallback
|
|
1337
|
+
* does not go through the legacy pair, so an unconvertible `Total Plans in
|
|
1338
|
+
* Phase` beside `Current Plan: N of M` still yields M. `null` when neither
|
|
1339
|
+
* yields a convertible M. #5786.
|
|
1340
|
+
*/
|
|
1341
|
+
function readRecordedPlanTotal(content) {
|
|
1342
|
+
// An explicit `Total Plans in Phase` wins over an `of M` in either plan field.
|
|
1343
|
+
const raw = (0, state_document_cjs_1.stateExtractField)(content, 'Total Plans in Phase');
|
|
1344
|
+
const match = raw ? PLAN_SHAPE_N.exec(raw) : null;
|
|
1345
|
+
const t = match ? planNumberFrom(match[1]) : null;
|
|
1346
|
+
if (t !== null)
|
|
1347
|
+
return t;
|
|
1348
|
+
const { legacyNofMMatch, planNofMMatch } = parsePlanFields(content);
|
|
1349
|
+
const fromCurrentPlan = legacyNofMMatch ? planNumberFrom(legacyNofMMatch[2]) : null;
|
|
1350
|
+
if (fromCurrentPlan !== null)
|
|
1351
|
+
return fromCurrentPlan;
|
|
1352
|
+
return planNofMMatch ? planNumberFrom(planNofMMatch[2]) : null;
|
|
1353
|
+
}
|
|
1264
1354
|
/**
|
|
1265
1355
|
* Advance the leading integer of a written plan value, preserving everything
|
|
1266
1356
|
* the author wrote around it: the zero-padding width ("04" -> "05") and any
|
|
@@ -1483,47 +1573,7 @@ function advancePlanCore(content, deps) {
|
|
|
1483
1573
|
// `planSourceField`/`planRawValue` pair here and then wrote the OTHER
|
|
1484
1574
|
// spelling from this one's numbers, which is precisely how a field ended up
|
|
1485
1575
|
// holding a value nothing had derived for it.
|
|
1486
|
-
const legacyPlan
|
|
1487
|
-
const legacyTotal = (0, state_document_cjs_1.stateExtractField)(content, 'Total Plans in Phase');
|
|
1488
|
-
const planField = (0, state_document_cjs_1.stateExtractField)(content, 'Plan');
|
|
1489
|
-
// Every branch below reads its numbers out of an ANCHORED match's capture
|
|
1490
|
-
// groups. Nothing here calls parseInt on a raw field value, so a value the
|
|
1491
|
-
// grammar does not fully describe cannot half-parse into a plausible number.
|
|
1492
|
-
const legacyNMatch = legacyPlan ? PLAN_SHAPE_N.exec(legacyPlan) : null;
|
|
1493
|
-
const legacyNofMMatch = legacyPlan ? PLAN_SHAPE_N_OF_M.exec(legacyPlan) : null;
|
|
1494
|
-
const totalNMatch = legacyTotal ? PLAN_SHAPE_N.exec(legacyTotal) : null;
|
|
1495
|
-
const planNofMMatch = planField ? PLAN_SHAPE_N_OF_M.exec(planField) : null;
|
|
1496
|
-
const planNMatch = planField ? PLAN_SHAPE_N.exec(planField) : null;
|
|
1497
|
-
let parsedCurrent = null;
|
|
1498
|
-
let parsedTotal = null;
|
|
1499
|
-
if (legacyPlan && legacyTotal && (legacyNMatch || legacyNofMMatch) && totalNMatch) {
|
|
1500
|
-
// Legacy pair wins whenever both fields are present and both are readable,
|
|
1501
|
-
// even if the Current Plan value also carries an "of M" — the explicit
|
|
1502
|
-
// sibling field is the stated intent, so it supplies the total.
|
|
1503
|
-
parsedCurrent = planNumberFrom((legacyNMatch ?? legacyNofMMatch)[1]);
|
|
1504
|
-
parsedTotal = planNumberFrom(totalNMatch[1]);
|
|
1505
|
-
}
|
|
1506
|
-
else if (legacyNofMMatch) {
|
|
1507
|
-
// Hybrid: legacy field name, compound value, no readable Total Plans
|
|
1508
|
-
// sibling. Written by hand (and by agents) often enough to be worth
|
|
1509
|
-
// reading — #3784.
|
|
1510
|
-
parsedCurrent = planNumberFrom(legacyNofMMatch[1]);
|
|
1511
|
-
parsedTotal = planNumberFrom(legacyNofMMatch[2]);
|
|
1512
|
-
}
|
|
1513
|
-
else if (planNofMMatch) {
|
|
1514
|
-
parsedCurrent = planNumberFrom(planNofMMatch[1]);
|
|
1515
|
-
parsedTotal = planNumberFrom(planNofMMatch[2]);
|
|
1516
|
-
}
|
|
1517
|
-
// No branch for a bare `Plan: N` paired with a `Total Plans in Phase: M`
|
|
1518
|
-
// sibling and no `Current Plan` at all (#3791 review round 6, M2). A revision
|
|
1519
|
-
// of this PR accepted it; base did not (its `else if (planField)` arm had no
|
|
1520
|
-
// `of M` match and errored via NaN), and it is out of #3784's scope, which is
|
|
1521
|
-
// the hybrid `Current Plan: N of M`. It cannot be given the schema-row +
|
|
1522
|
-
// forcing-test coupling the other shapes have, either: `Plan` is body-only,
|
|
1523
|
-
// `buildStateFrontmatter` never reads it into frontmatter, so there is no
|
|
1524
|
-
// `current_*` key to hang a row on. An accepted shape with no schema row and
|
|
1525
|
-
// no forcing test is exactly the drift this diff is otherwise built to
|
|
1526
|
-
// prevent, so the shape is refused and named in the error instead.
|
|
1576
|
+
const { legacyPlan, planField, legacyNMatch, legacyNofMMatch, planNofMMatch, planNMatch, current: parsedCurrent, total: parsedTotal, } = parsePlanFields(content);
|
|
1527
1577
|
if (parsedCurrent === null || parsedTotal === null) {
|
|
1528
1578
|
return { content: reassemble(body), updated: [], data: { error: true } };
|
|
1529
1579
|
}
|