get-shit-done-cc 1.42.1 → 1.42.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -0
- package/get-shit-done/bin/lib/core.cjs +33 -13
- package/get-shit-done/bin/lib/phase.cjs +23 -14
- package/get-shit-done/bin/lib/roadmap.cjs +15 -17
- package/get-shit-done/bin/lib/state-document.cjs +6 -116
- package/get-shit-done/bin/lib/state-document.generated.cjs +127 -0
- package/package.json +2 -1
- package/sdk/package-lock.json +506 -2
- package/sdk/package.json +4 -1
- package/sdk/src/query/state-document.test.ts +197 -0
- package/sdk-bundle/gsd-sdk.tgz +0 -0
package/README.md
CHANGED
|
@@ -144,6 +144,8 @@ GSD is built for frictionless automation. Skip-permissions is how it's intended
|
|
|
144
144
|
|
|
145
145
|
Install only the skills you need with `--profile=core` (six core-loop skills), `--profile=standard` (core + phase management), or the default full install. Profiles compose: `--profile=core,audit`. `--minimal` is an alias for `--profile=core`. See **[docs/USER-GUIDE.md](docs/USER-GUIDE.md)** for the full walkthrough, non-interactive install flags for all 15 runtimes, and permissions configuration. See [ADR-0011](docs/adr/0011-skill-surface-budget-module.md) for the profile model and runtime surface control.
|
|
146
146
|
|
|
147
|
+
Current release highlights are in [docs/RELEASE-v1.42.1.md](docs/RELEASE-v1.42.1.md): package legitimacy checks, safer installer migrations, runtime surface control, custom ship PR sections, reviewer defaults, fallow structural review, and quota-aware execution recovery.
|
|
148
|
+
|
|
147
149
|
---
|
|
148
150
|
|
|
149
151
|
## Commands
|
|
@@ -196,6 +198,8 @@ Key dials:
|
|
|
196
198
|
|
|
197
199
|
Optional structural review: set `code_quality.fallow.enabled` to `true` to add a fallow pre-pass to `/gsd-code-review`. GSD writes `.planning/phases/<phase>/FALLOW.json` and surfaces a `Structural Findings (fallow)` section in `REVIEW.md`. Install with `npm install -D fallow@^2.70.0` (or system-wide via `cargo install fallow`; note that the Rust binary's JSON schema must match the documented v2.70+ contract — older versions may produce silent zero-finding output).
|
|
198
200
|
|
|
201
|
+
Package legitimacy checks are built into the research, planning, and execution path: recommended dependencies get audited, unverified packages require a human checkpoint, and failed installs stop instead of trying similarly named alternatives.
|
|
202
|
+
|
|
199
203
|
For the full configuration reference — all settings, git branching strategies, per-runtime model overrides, workstream config inheritance, agent skills injection — see **[docs/CONFIGURATION.md](docs/CONFIGURATION.md)**.
|
|
200
204
|
|
|
201
205
|
---
|
|
@@ -726,6 +726,31 @@ function normalizePhaseName(phase) {
|
|
|
726
726
|
return str;
|
|
727
727
|
}
|
|
728
728
|
|
|
729
|
+
/**
|
|
730
|
+
* Render a regex source fragment matching a phase number against ROADMAP/STATE
|
|
731
|
+
* prose regardless of zero-padding on either side. Skills pass the resolved
|
|
732
|
+
* padded form (`02.7`), but human-authored ROADMAP prose is conventionally
|
|
733
|
+
* un-padded (`### Phase 2.7:`); a naive `escapeRegex(phaseNum)` fragment never
|
|
734
|
+
* matches when the two diverge. Strips leading zeros from the integer part
|
|
735
|
+
* before re-emitting with a `0*` prefix, so the fragment matches both `2.7`
|
|
736
|
+
* and `02.7` (and `002.7`).
|
|
737
|
+
*
|
|
738
|
+
* Falls back to `escapeRegex(phaseNum)` for non-numeric IDs (custom project
|
|
739
|
+
* codes like `PROJ-42`) so callers can substitute it unconditionally.
|
|
740
|
+
*
|
|
741
|
+
* See #3537 — wired into every ROADMAP-prose regex builder.
|
|
742
|
+
*/
|
|
743
|
+
function phaseMarkdownRegexSource(phaseNum) {
|
|
744
|
+
const stripped = String(phaseNum).replace(/^[A-Z]{1,6}-(?=\d)/i, '');
|
|
745
|
+
const match = stripped.match(/^0*(\d+)([A-Z])?((?:\.\d+)*)$/i);
|
|
746
|
+
if (!match) return escapeRegex(phaseNum);
|
|
747
|
+
|
|
748
|
+
const integer = match[1].replace(/^0+/, '') || '0';
|
|
749
|
+
const letter = match[2] ? escapeRegex(match[2]) : '';
|
|
750
|
+
const decimal = match[3] ? escapeRegex(match[3]) : '';
|
|
751
|
+
return `0*${escapeRegex(integer)}${letter}${decimal}`;
|
|
752
|
+
}
|
|
753
|
+
|
|
729
754
|
function comparePhaseNum(a, b) {
|
|
730
755
|
// Strip optional project_code prefix before comparing (e.g., 'CK-01-name' → '01-name')
|
|
731
756
|
const sa = String(a).replace(/^[A-Z]{1,6}-/, '');
|
|
@@ -1071,19 +1096,13 @@ function getRoadmapPhaseInternal(cwd, phaseNum) {
|
|
|
1071
1096
|
const roadmapRaw = platformReadSync(roadmapPath);
|
|
1072
1097
|
if (roadmapRaw === null) throw new Error('missing');
|
|
1073
1098
|
const content = extractCurrentMilestone(roadmapRaw, cwd);
|
|
1074
|
-
//
|
|
1075
|
-
//
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
// For purely numeric phases allow optional leading zeros so both "Phase 1:" and
|
|
1082
|
-
// "Phase 01:" are matched regardless of whether the ROADMAP uses padded numbers.
|
|
1083
|
-
const isNumeric = /^\d+$/.test(String(phaseNum));
|
|
1084
|
-
const phasePattern = isNumeric
|
|
1085
|
-
? new RegExp(`#{2,4}\\s*Phase\\s+0*${escapedPhase}:\\s*([^\\n]+)`, 'i')
|
|
1086
|
-
: new RegExp(`#{2,4}\\s*Phase\\s+${escapedPhase}:\\s*([^\\n]+)`, 'i');
|
|
1099
|
+
// #3537: route through canonical padding-tolerant fragment. The prior
|
|
1100
|
+
// hand-rolled `isNumeric` branch only stripped padding on integer-only
|
|
1101
|
+
// ids and missed decimal padding (`02.7` against `Phase 2.7:` headings).
|
|
1102
|
+
const phasePattern = new RegExp(
|
|
1103
|
+
`#{2,4}\\s*Phase\\s+${phaseMarkdownRegexSource(phaseNum)}:\\s*([^\\n]+)`,
|
|
1104
|
+
'i'
|
|
1105
|
+
);
|
|
1087
1106
|
const headerMatch = content.match(phasePattern);
|
|
1088
1107
|
if (!headerMatch) return null;
|
|
1089
1108
|
|
|
@@ -1880,6 +1899,7 @@ module.exports = {
|
|
|
1880
1899
|
isGitIgnored,
|
|
1881
1900
|
escapeRegex,
|
|
1882
1901
|
normalizePhaseName,
|
|
1902
|
+
phaseMarkdownRegexSource,
|
|
1883
1903
|
comparePhaseNum,
|
|
1884
1904
|
searchPhaseInDir,
|
|
1885
1905
|
extractPhaseToken,
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
const fs = require('fs');
|
|
6
6
|
const path = require('path');
|
|
7
|
-
const { escapeRegex, loadConfig, normalizePhaseName, comparePhaseNum, findPhaseInternal, getArchivedPhaseDirs, generateSlugInternal, getMilestonePhaseFilter, stripShippedMilestones, extractCurrentMilestone, replaceInCurrentMilestone, toPosixPath, output, error, readSubdirectories, phaseTokenMatches } = require('./core.cjs');
|
|
7
|
+
const { escapeRegex, loadConfig, normalizePhaseName, phaseMarkdownRegexSource, comparePhaseNum, findPhaseInternal, getArchivedPhaseDirs, generateSlugInternal, getMilestonePhaseFilter, stripShippedMilestones, extractCurrentMilestone, replaceInCurrentMilestone, toPosixPath, output, error, readSubdirectories, phaseTokenMatches } = require('./core.cjs');
|
|
8
8
|
const { platformWriteSync, platformReadSync, platformEnsureDir } = require('./shell-command-projection.cjs');
|
|
9
9
|
const { planningDir, withPlanningLock } = require('./planning-workspace.cjs');
|
|
10
10
|
const { extractFrontmatter } = require('./frontmatter.cjs');
|
|
@@ -170,8 +170,10 @@ function cmdPhaseNextDecimal(cwd, basePhase, raw) {
|
|
|
170
170
|
if (fs.existsSync(roadmapPath)) {
|
|
171
171
|
try {
|
|
172
172
|
const roadmapContent = fs.readFileSync(roadmapPath, 'utf-8');
|
|
173
|
+
// #3537: padding-tolerant on both sides — `0*${escapeRegex(...)}`
|
|
174
|
+
// tolerated extra padding but not missing.
|
|
173
175
|
const phasePattern = new RegExp(
|
|
174
|
-
`#{2,4}\\s*Phase\\s
|
|
176
|
+
`#{2,4}\\s*Phase\\s+${phaseMarkdownRegexSource(normalized)}\\.(\\d+)\\s*:`, 'gi'
|
|
175
177
|
);
|
|
176
178
|
let pm;
|
|
177
179
|
while ((pm = phasePattern.exec(roadmapContent)) !== null) {
|
|
@@ -691,13 +693,14 @@ function cmdPhaseInsert(cwd, afterPhase, description, raw) {
|
|
|
691
693
|
const rawContent = fs.readFileSync(roadmapPath, 'utf-8');
|
|
692
694
|
const content = extractCurrentMilestone(rawContent, cwd);
|
|
693
695
|
|
|
694
|
-
// Normalize input then
|
|
696
|
+
// Normalize input then route through canonical padding-tolerant fragment
|
|
697
|
+
// (#3537). The prior hand-rolled `0*${unpadded}` worked for the integer
|
|
698
|
+
// base but duplicated logic — funnel it through the shared helper.
|
|
695
699
|
const normalizedAfter = normalizePhaseName(afterPhase);
|
|
696
|
-
const
|
|
697
|
-
const
|
|
698
|
-
const targetPattern = new RegExp(`#{2,4}\\s*Phase\\s+0*${afterPhaseEscaped}:`, 'i');
|
|
700
|
+
const afterPhaseEscaped = phaseMarkdownRegexSource(normalizedAfter);
|
|
701
|
+
const targetPattern = new RegExp(`#{2,4}\\s*Phase\\s+${afterPhaseEscaped}:`, 'i');
|
|
699
702
|
if (!targetPattern.test(content)) {
|
|
700
|
-
const checklistPattern = new RegExp(`-\\s*\\[[ x]\\]\\s*\\*\\*Phase\\s
|
|
703
|
+
const checklistPattern = new RegExp(`-\\s*\\[[ x]\\]\\s*\\*\\*Phase\\s+${afterPhaseEscaped}:`, 'i');
|
|
701
704
|
if (checklistPattern.test(content)) {
|
|
702
705
|
error(`Phase ${afterPhase} exists in roadmap summary but is missing a detail section (### Phase ${afterPhase}: ...).`);
|
|
703
706
|
}
|
|
@@ -719,9 +722,11 @@ function cmdPhaseInsert(cwd, afterPhase, description, raw) {
|
|
|
719
722
|
}
|
|
720
723
|
} catch { /* intentionally empty */ }
|
|
721
724
|
|
|
722
|
-
// Also scan ROADMAP.md content (already loaded) for decimal entries
|
|
725
|
+
// Also scan ROADMAP.md content (already loaded) for decimal entries.
|
|
726
|
+
// #3537: padding-tolerant fragment so un-padded `Phase 2.7:` is found
|
|
727
|
+
// when caller passes the padded base `02`.
|
|
723
728
|
const rmPhasePattern = new RegExp(
|
|
724
|
-
`#{2,4}\\s*Phase\\s
|
|
729
|
+
`#{2,4}\\s*Phase\\s+${phaseMarkdownRegexSource(normalizedBase)}\\.(\\d+)\\s*:`, 'gi'
|
|
725
730
|
);
|
|
726
731
|
let rmMatch;
|
|
727
732
|
while ((rmMatch = rmPhasePattern.exec(rawContent)) !== null) {
|
|
@@ -745,7 +750,7 @@ function cmdPhaseInsert(cwd, afterPhase, description, raw) {
|
|
|
745
750
|
const phaseEntry = `\n### Phase ${_decimalPhase}: ${description} (INSERTED)\n\n**Goal:** [Urgent work - to be planned]\n**Requirements**: TBD\n**Depends on:** Phase ${afterPhase}\n**Plans:** 0 plans\n\nPlans:\n- [ ] TBD (run /gsd:plan-phase ${_decimalPhase} to break down)\n`;
|
|
746
751
|
|
|
747
752
|
// Insert after the target phase section
|
|
748
|
-
const headerPattern = new RegExp(`(#{2,4}\\s*Phase\\s
|
|
753
|
+
const headerPattern = new RegExp(`(#{2,4}\\s*Phase\\s+${afterPhaseEscaped}:[^\\n]*\\n)`, 'i');
|
|
749
754
|
const headerMatch = rawContent.match(headerPattern);
|
|
750
755
|
if (!headerMatch) {
|
|
751
756
|
error(`Could not find Phase ${afterPhase} header`);
|
|
@@ -1030,14 +1035,16 @@ function cmdPhaseComplete(cwd, phaseNum, raw) {
|
|
|
1030
1035
|
let roadmapContent = fs.readFileSync(roadmapPath, 'utf-8');
|
|
1031
1036
|
|
|
1032
1037
|
// Checkbox: - [ ] Phase N: → - [x] Phase N: (...completed DATE)
|
|
1038
|
+
// #3537: padding-tolerant fragment so the caller-resolved padded id
|
|
1039
|
+
// matches un-padded ROADMAP prose.
|
|
1040
|
+
const phaseEscaped = phaseMarkdownRegexSource(phaseNum);
|
|
1033
1041
|
const checkboxPattern = new RegExp(
|
|
1034
|
-
`(-\\s*\\[)[ ](\\]\\s*.*Phase\\s+${
|
|
1042
|
+
`(-\\s*\\[)[ ](\\]\\s*.*Phase\\s+${phaseEscaped}[:\\s][^\\n]*)`,
|
|
1035
1043
|
'i'
|
|
1036
1044
|
);
|
|
1037
1045
|
roadmapContent = roadmapContent.replace(checkboxPattern, `$1x$2 (completed ${today})`);
|
|
1038
1046
|
|
|
1039
1047
|
// Progress table: update Status to Complete, add date (handles 4 or 5 column tables)
|
|
1040
|
-
const phaseEscaped = escapeRegex(phaseNum);
|
|
1041
1048
|
const tableRowPattern = new RegExp(
|
|
1042
1049
|
`^(\\|\\s*${phaseEscaped}\\.?\\s[^|]*(?:\\|[^\\n]*))$`,
|
|
1043
1050
|
'im'
|
|
@@ -1093,8 +1100,10 @@ function cmdPhaseComplete(cwd, phaseNum, raw) {
|
|
|
1093
1100
|
// Update REQUIREMENTS.md traceability for this phase's requirements
|
|
1094
1101
|
const reqPath = path.join(planningDir(cwd), 'REQUIREMENTS.md');
|
|
1095
1102
|
if (fs.existsSync(reqPath)) {
|
|
1096
|
-
// Extract the current phase section from roadmap (scoped to avoid cross-phase matching)
|
|
1097
|
-
|
|
1103
|
+
// Extract the current phase section from roadmap (scoped to avoid cross-phase matching).
|
|
1104
|
+
// #3537: padding-tolerant fragment so an un-padded `Phase 2.7:` heading
|
|
1105
|
+
// is found when caller resolved to padded `02.7`.
|
|
1106
|
+
const phaseEsc = phaseMarkdownRegexSource(phaseNum);
|
|
1098
1107
|
const currentMilestoneRoadmap = extractCurrentMilestone(roadmapContent, cwd);
|
|
1099
1108
|
const phaseSectionMatch = currentMilestoneRoadmap.match(
|
|
1100
1109
|
new RegExp(`(#{2,4}\\s*Phase\\s+${phaseEsc}[:\\s][\\s\\S]*?)(?=#{2,4}\\s*Phase\\s+|$)`, 'i')
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
const fs = require('fs');
|
|
6
6
|
const path = require('path');
|
|
7
|
-
const { escapeRegex, normalizePhaseName, output, error, findPhaseInternal, stripShippedMilestones, extractCurrentMilestone, replaceInCurrentMilestone, phaseTokenMatches } = require('./core.cjs');
|
|
7
|
+
const { escapeRegex, normalizePhaseName, phaseMarkdownRegexSource, output, error, findPhaseInternal, stripShippedMilestones, extractCurrentMilestone, replaceInCurrentMilestone, phaseTokenMatches } = require('./core.cjs');
|
|
8
8
|
const { platformWriteSync } = require('./shell-command-projection.cjs');
|
|
9
9
|
const { planningPaths, withPlanningLock } = require('./planning-workspace.cjs');
|
|
10
10
|
const scanPhasePlans = require('./plan-scan.cjs');
|
|
@@ -52,16 +52,8 @@ function countPhasePlansAndSummaries(phaseDir) {
|
|
|
52
52
|
};
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
const match = stripped.match(/^0*(\d+)([A-Z])?((?:\.\d+)*)$/i);
|
|
58
|
-
if (!match) return escapeRegex(phaseNum);
|
|
59
|
-
|
|
60
|
-
const integer = match[1].replace(/^0+/, '') || '0';
|
|
61
|
-
const letter = match[2] ? escapeRegex(match[2]) : '';
|
|
62
|
-
const decimal = match[3] ? escapeRegex(match[3]) : '';
|
|
63
|
-
return `0*${escapeRegex(integer)}${letter}${decimal}`;
|
|
64
|
-
}
|
|
55
|
+
// `phaseMarkdownRegexSource` moved to core.cjs (#3537) so phase.cjs and
|
|
56
|
+
// core.cjs itself can consume it without circular deps. Imported above.
|
|
65
57
|
|
|
66
58
|
/**
|
|
67
59
|
* Search for a phase header (and its section) within the given content string.
|
|
@@ -147,8 +139,9 @@ function cmdRoadmapGetPhase(cwd, phaseNum, raw) {
|
|
|
147
139
|
const rawContent = fs.readFileSync(roadmapPath, 'utf-8');
|
|
148
140
|
const milestoneContent = extractCurrentMilestone(rawContent, cwd);
|
|
149
141
|
|
|
150
|
-
//
|
|
151
|
-
|
|
142
|
+
// #3537: padding-tolerant fragment so callers passing `02.7` still match
|
|
143
|
+
// un-padded ROADMAP prose (`### Phase 2.7:`).
|
|
144
|
+
const escapedPhase = phaseMarkdownRegexSource(phaseNum);
|
|
152
145
|
|
|
153
146
|
// Search the current milestone slice first, then fall back to full roadmap.
|
|
154
147
|
// A malformed_roadmap result (checklist-only) from the milestone should not
|
|
@@ -248,8 +241,11 @@ function cmdRoadmapAnalyze(cwd, raw) {
|
|
|
248
241
|
}
|
|
249
242
|
} catch { /* intentionally empty */ }
|
|
250
243
|
|
|
251
|
-
// Check ROADMAP checkbox status
|
|
252
|
-
|
|
244
|
+
// Check ROADMAP checkbox status.
|
|
245
|
+
// #3537: padding-tolerant fragment — the heading discovered above may use
|
|
246
|
+
// a different padding than the summary-bullet checkbox below it (mixed
|
|
247
|
+
// padding inside one ROADMAP is legal and seen in real projects).
|
|
248
|
+
const checkboxPattern = new RegExp(`-\\s*\\[(x| )\\]\\s*.*Phase\\s+${phaseMarkdownRegexSource(phaseNum)}[:\\s]`, 'i');
|
|
253
249
|
const checkboxMatch = content.match(checkboxPattern);
|
|
254
250
|
const roadmapComplete = checkboxMatch ? checkboxMatch[1] === 'x' : false;
|
|
255
251
|
|
|
@@ -511,8 +507,10 @@ function cmdRoadmapAnnotateDependencies(cwd, phaseNum, raw) {
|
|
|
511
507
|
withPlanningLock(cwd, () => {
|
|
512
508
|
let content = fs.readFileSync(roadmapPath, 'utf-8');
|
|
513
509
|
|
|
514
|
-
// Find the phase section
|
|
515
|
-
|
|
510
|
+
// Find the phase section.
|
|
511
|
+
// #3537: padding-tolerant fragment so the caller's resolved padded id
|
|
512
|
+
// matches un-padded ROADMAP headings.
|
|
513
|
+
const phaseEscaped = phaseMarkdownRegexSource(phaseNum);
|
|
516
514
|
const phaseHeaderPattern = new RegExp(`(#{2,4}\\s*Phase\\s+${phaseEscaped}:[^\\n]*)`, 'i');
|
|
517
515
|
const phaseMatch = content.match(phaseHeaderPattern);
|
|
518
516
|
if (!phaseMatch) return;
|
|
@@ -1,122 +1,12 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* STATE.md Document Module
|
|
4
|
+
* STATE.md Document Module — CJS adapter.
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
6
|
+
* The implementation is generated from sdk/src/query/state-document.ts and
|
|
7
|
+
* lives in state-document.generated.cjs. This file is a thin re-export so
|
|
8
|
+
* that existing call sites (state.cjs, workstream-inventory.cjs, init.cjs,
|
|
9
|
+
* and tests) can continue to require('./state-document') unchanged.
|
|
8
10
|
*/
|
|
9
11
|
|
|
10
|
-
|
|
11
|
-
return String(str).replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
function stateExtractField(content, fieldName) {
|
|
15
|
-
const escaped = escapeRegex(fieldName);
|
|
16
|
-
const boldPattern = new RegExp(`\\*\\*${escaped}:\\*\\*[ \\t]*(.+)`, 'i');
|
|
17
|
-
const boldMatch = content.match(boldPattern);
|
|
18
|
-
if (boldMatch) return boldMatch[1].trim();
|
|
19
|
-
const plainPattern = new RegExp(`^${escaped}:[ \\t]*(.+)`, 'im');
|
|
20
|
-
const plainMatch = content.match(plainPattern);
|
|
21
|
-
return plainMatch ? plainMatch[1].trim() : null;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
function stateReplaceField(content, fieldName, newValue) {
|
|
25
|
-
const escaped = escapeRegex(fieldName);
|
|
26
|
-
const boldPattern = new RegExp(`(\\*\\*${escaped}:\\*\\*\\s*)(.*)`, 'i');
|
|
27
|
-
if (boldPattern.test(content)) {
|
|
28
|
-
return content.replace(boldPattern, (_match, prefix) => `${prefix}${newValue}`);
|
|
29
|
-
}
|
|
30
|
-
const plainPattern = new RegExp(`(^${escaped}:\\s*)(.*)`, 'im');
|
|
31
|
-
if (plainPattern.test(content)) {
|
|
32
|
-
return content.replace(plainPattern, (_match, prefix) => `${prefix}${newValue}`);
|
|
33
|
-
}
|
|
34
|
-
return null;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
function stateReplaceFieldWithFallback(content, primary, fallback, value) {
|
|
38
|
-
let result = stateReplaceField(content, primary, value);
|
|
39
|
-
if (result) return result;
|
|
40
|
-
if (fallback) {
|
|
41
|
-
result = stateReplaceField(content, fallback, value);
|
|
42
|
-
if (result) return result;
|
|
43
|
-
}
|
|
44
|
-
return content;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
function normalizeStateStatus(status, pausedAt) {
|
|
48
|
-
let normalizedStatus = status || 'unknown';
|
|
49
|
-
const statusLower = (status || '').toLowerCase();
|
|
50
|
-
if (statusLower.includes('paused') || statusLower.includes('stopped') || pausedAt) {
|
|
51
|
-
normalizedStatus = 'paused';
|
|
52
|
-
} else if (statusLower.includes('executing') || statusLower.includes('in progress')) {
|
|
53
|
-
normalizedStatus = 'executing';
|
|
54
|
-
} else if (statusLower.includes('planning') || statusLower.includes('ready to plan')) {
|
|
55
|
-
normalizedStatus = 'planning';
|
|
56
|
-
} else if (statusLower.includes('discussing')) {
|
|
57
|
-
normalizedStatus = 'discussing';
|
|
58
|
-
} else if (statusLower.includes('verif')) {
|
|
59
|
-
normalizedStatus = 'verifying';
|
|
60
|
-
} else if (statusLower.includes('complete') || statusLower.includes('done')) {
|
|
61
|
-
normalizedStatus = 'completed';
|
|
62
|
-
} else if (statusLower.includes('ready to execute')) {
|
|
63
|
-
normalizedStatus = 'executing';
|
|
64
|
-
}
|
|
65
|
-
return normalizedStatus;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
function computeProgressPercent(completedPlans, totalPlans, completedPhases, totalPhases) {
|
|
69
|
-
const hasPlanData = totalPlans !== null && totalPlans > 0 && completedPlans !== null;
|
|
70
|
-
const hasPhaseData = totalPhases !== null && totalPhases > 0 && completedPhases !== null;
|
|
71
|
-
|
|
72
|
-
if (!hasPlanData && !hasPhaseData) return null;
|
|
73
|
-
|
|
74
|
-
const planFraction = hasPlanData ? completedPlans / totalPlans : 1;
|
|
75
|
-
const phaseFraction = hasPhaseData ? completedPhases / totalPhases : 1;
|
|
76
|
-
|
|
77
|
-
return Math.min(100, Math.round(Math.min(planFraction, phaseFraction) * 100));
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
function toFiniteNumber(value) {
|
|
81
|
-
const number = Number(value);
|
|
82
|
-
return Number.isFinite(number) ? number : null;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
function existingProgressExceedsDerived(existingProgress, derivedProgress, key) {
|
|
86
|
-
const existing = toFiniteNumber(existingProgress[key]);
|
|
87
|
-
const derived = toFiniteNumber(derivedProgress[key]);
|
|
88
|
-
return existing !== null && derived !== null && existing > derived;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
function shouldPreserveExistingProgress(existingProgress, derivedProgress) {
|
|
92
|
-
if (!existingProgress || typeof existingProgress !== 'object') return false;
|
|
93
|
-
if (!derivedProgress || typeof derivedProgress !== 'object') return false;
|
|
94
|
-
|
|
95
|
-
return (
|
|
96
|
-
existingProgressExceedsDerived(existingProgress, derivedProgress, 'total_phases') ||
|
|
97
|
-
existingProgressExceedsDerived(existingProgress, derivedProgress, 'completed_phases') ||
|
|
98
|
-
existingProgressExceedsDerived(existingProgress, derivedProgress, 'total_plans') ||
|
|
99
|
-
existingProgressExceedsDerived(existingProgress, derivedProgress, 'completed_plans')
|
|
100
|
-
);
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
function normalizeProgressNumbers(progress) {
|
|
104
|
-
if (!progress || typeof progress !== 'object') return progress;
|
|
105
|
-
|
|
106
|
-
const normalized = { ...progress };
|
|
107
|
-
for (const key of ['total_phases', 'completed_phases', 'total_plans', 'completed_plans', 'percent']) {
|
|
108
|
-
const number = toFiniteNumber(normalized[key]);
|
|
109
|
-
if (number !== null) normalized[key] = number;
|
|
110
|
-
}
|
|
111
|
-
return normalized;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
module.exports = {
|
|
115
|
-
computeProgressPercent,
|
|
116
|
-
normalizeProgressNumbers,
|
|
117
|
-
normalizeStateStatus,
|
|
118
|
-
shouldPreserveExistingProgress,
|
|
119
|
-
stateExtractField,
|
|
120
|
-
stateReplaceField,
|
|
121
|
-
stateReplaceFieldWithFallback,
|
|
122
|
-
};
|
|
12
|
+
module.exports = require('./state-document.generated.cjs');
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* GENERATED FILE — DO NOT EDIT.
|
|
5
|
+
*
|
|
6
|
+
* Source: sdk/src/query/state-document.ts
|
|
7
|
+
* Regenerate: cd sdk && npm run gen:state-document
|
|
8
|
+
*
|
|
9
|
+
* STATE.md Document Module — pure transforms for STATE.md text.
|
|
10
|
+
* This module does not read the filesystem and does not own persistence or locking.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
// Internal helpers
|
|
14
|
+
function escapeRegex(str) {
|
|
15
|
+
return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function toFiniteNumber(value) {
|
|
19
|
+
const number = Number(value);
|
|
20
|
+
return Number.isFinite(number) ? number : null;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function existingProgressExceedsDerived(existingProgress, derivedProgress, key) {
|
|
24
|
+
const existing = toFiniteNumber(existingProgress[key]);
|
|
25
|
+
const derived = toFiniteNumber(derivedProgress[key]);
|
|
26
|
+
return existing !== null && derived !== null && existing > derived;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function stateExtractField(content, fieldName) {
|
|
30
|
+
const escaped = escapeRegex(fieldName);
|
|
31
|
+
const boldPattern = new RegExp(`\\*\\*${escaped}:\\*\\*[ \\t]*(.+)`, 'i');
|
|
32
|
+
const boldMatch = content.match(boldPattern);
|
|
33
|
+
if (boldMatch)
|
|
34
|
+
return boldMatch[1].trim();
|
|
35
|
+
const plainPattern = new RegExp(`^${escaped}:[ \\t]*(.+)`, 'im');
|
|
36
|
+
const plainMatch = content.match(plainPattern);
|
|
37
|
+
return plainMatch ? plainMatch[1].trim() : null;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function stateReplaceField(content, fieldName, newValue) {
|
|
41
|
+
const escaped = escapeRegex(fieldName);
|
|
42
|
+
const boldPattern = new RegExp(`(\\*\\*${escaped}:\\*\\*\\s*)(.*)`, 'i');
|
|
43
|
+
if (boldPattern.test(content)) {
|
|
44
|
+
return content.replace(boldPattern, (_match, prefix) => `${prefix}${newValue}`);
|
|
45
|
+
}
|
|
46
|
+
const plainPattern = new RegExp(`(^${escaped}:\\s*)(.*)`, 'im');
|
|
47
|
+
if (plainPattern.test(content)) {
|
|
48
|
+
return content.replace(plainPattern, (_match, prefix) => `${prefix}${newValue}`);
|
|
49
|
+
}
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function stateReplaceFieldWithFallback(content, primary, fallback, value) {
|
|
54
|
+
let result = stateReplaceField(content, primary, value);
|
|
55
|
+
if (result)
|
|
56
|
+
return result;
|
|
57
|
+
if (fallback) {
|
|
58
|
+
result = stateReplaceField(content, fallback, value);
|
|
59
|
+
if (result)
|
|
60
|
+
return result;
|
|
61
|
+
}
|
|
62
|
+
return content;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function normalizeStateStatus(status, pausedAt) {
|
|
66
|
+
let normalizedStatus = status || 'unknown';
|
|
67
|
+
const statusLower = (status || '').toLowerCase();
|
|
68
|
+
if (statusLower.includes('paused') || statusLower.includes('stopped') || pausedAt) {
|
|
69
|
+
normalizedStatus = 'paused';
|
|
70
|
+
}
|
|
71
|
+
else if (statusLower.includes('executing') || statusLower.includes('in progress')) {
|
|
72
|
+
normalizedStatus = 'executing';
|
|
73
|
+
}
|
|
74
|
+
else if (statusLower.includes('planning') || statusLower.includes('ready to plan')) {
|
|
75
|
+
normalizedStatus = 'planning';
|
|
76
|
+
}
|
|
77
|
+
else if (statusLower.includes('discussing')) {
|
|
78
|
+
normalizedStatus = 'discussing';
|
|
79
|
+
}
|
|
80
|
+
else if (statusLower.includes('verif')) {
|
|
81
|
+
normalizedStatus = 'verifying';
|
|
82
|
+
}
|
|
83
|
+
else if (statusLower.includes('complete') || statusLower.includes('done')) {
|
|
84
|
+
normalizedStatus = 'completed';
|
|
85
|
+
}
|
|
86
|
+
else if (statusLower.includes('ready to execute')) {
|
|
87
|
+
normalizedStatus = 'executing';
|
|
88
|
+
}
|
|
89
|
+
return normalizedStatus;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function computeProgressPercent(completedPlans, totalPlans, completedPhases, totalPhases) {
|
|
93
|
+
const hasPlanData = totalPlans !== null && totalPlans > 0 && completedPlans !== null;
|
|
94
|
+
const hasPhaseData = totalPhases !== null && totalPhases > 0 && completedPhases !== null;
|
|
95
|
+
if (!hasPlanData && !hasPhaseData)
|
|
96
|
+
return null;
|
|
97
|
+
const planFraction = hasPlanData ? completedPlans / totalPlans : 1;
|
|
98
|
+
const phaseFraction = hasPhaseData ? completedPhases / totalPhases : 1;
|
|
99
|
+
return Math.min(100, Math.round(Math.min(planFraction, phaseFraction) * 100));
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function shouldPreserveExistingProgress(existingProgress, derivedProgress) {
|
|
103
|
+
if (!existingProgress || typeof existingProgress !== 'object')
|
|
104
|
+
return false;
|
|
105
|
+
if (!derivedProgress || typeof derivedProgress !== 'object')
|
|
106
|
+
return false;
|
|
107
|
+
const existing = existingProgress;
|
|
108
|
+
const derived = derivedProgress;
|
|
109
|
+
return (existingProgressExceedsDerived(existing, derived, 'total_phases') ||
|
|
110
|
+
existingProgressExceedsDerived(existing, derived, 'completed_phases') ||
|
|
111
|
+
existingProgressExceedsDerived(existing, derived, 'total_plans') ||
|
|
112
|
+
existingProgressExceedsDerived(existing, derived, 'completed_plans'));
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function normalizeProgressNumbers(progress) {
|
|
116
|
+
if (!progress || typeof progress !== 'object')
|
|
117
|
+
return progress;
|
|
118
|
+
const normalized = { ...progress };
|
|
119
|
+
for (const key of ['total_phases', 'completed_phases', 'total_plans', 'completed_plans', 'percent']) {
|
|
120
|
+
const number = toFiniteNumber(normalized[key]);
|
|
121
|
+
if (number !== null)
|
|
122
|
+
normalized[key] = number;
|
|
123
|
+
}
|
|
124
|
+
return normalized;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
module.exports = { stateExtractField, stateReplaceField, stateReplaceFieldWithFallback, normalizeStateStatus, computeProgressPercent, shouldPreserveExistingProgress, normalizeProgressNumbers };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "get-shit-done-cc",
|
|
3
|
-
"version": "1.42.
|
|
3
|
+
"version": "1.42.2",
|
|
4
4
|
"description": "A meta-prompting, context engineering and spec-driven development system for Claude Code, OpenCode, Gemini and Codex by TÂCHES.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"get-shit-done-cc": "bin/install.js",
|
|
@@ -62,6 +62,7 @@
|
|
|
62
62
|
"build:hooks": "node scripts/build-hooks.js",
|
|
63
63
|
"build:sdk": "cd sdk && npm ci && npm run build",
|
|
64
64
|
"check:alias-drift": "cd sdk && npm run check:alias-drift",
|
|
65
|
+
"check:state-document-fresh": "cd sdk && npm run check:state-document-fresh",
|
|
65
66
|
"prepublishOnly": "npm run build:hooks && npm run build:sdk",
|
|
66
67
|
"pretest": "npm run build:sdk && npm run lint:skill-deps",
|
|
67
68
|
"pretest:coverage": "npm run build:sdk",
|
package/sdk/package-lock.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gsd-build/sdk",
|
|
3
|
-
"version": "1.42.
|
|
3
|
+
"version": "1.42.2",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@gsd-build/sdk",
|
|
9
|
-
"version": "1.42.
|
|
9
|
+
"version": "1.42.2",
|
|
10
10
|
"license": "MIT",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"@anthropic-ai/claude-agent-sdk": "^0.2.84",
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
"devDependencies": {
|
|
19
19
|
"@types/node": "^22.0.0",
|
|
20
20
|
"@types/ws": "^8.18.1",
|
|
21
|
+
"tsx": "^4.22.0",
|
|
21
22
|
"typescript": "^5.7.0",
|
|
22
23
|
"vitest": "^3.1.1"
|
|
23
24
|
},
|
|
@@ -1754,6 +1755,509 @@
|
|
|
1754
1755
|
"node": ">=14.0.0"
|
|
1755
1756
|
}
|
|
1756
1757
|
},
|
|
1758
|
+
"node_modules/tsx": {
|
|
1759
|
+
"version": "4.22.0",
|
|
1760
|
+
"resolved": "https://registry.npmjs.org/tsx/-/tsx-4.22.0.tgz",
|
|
1761
|
+
"integrity": "sha512-8ccZMPD69s1AbKXx0C5ddTNZfNjwV04iIKgjZmKfKxMynEtSYcK0Lh7iQFh53fI5Yu4pb9usgAiqyPmEONaALg==",
|
|
1762
|
+
"dev": true,
|
|
1763
|
+
"license": "MIT",
|
|
1764
|
+
"dependencies": {
|
|
1765
|
+
"esbuild": "~0.28.0"
|
|
1766
|
+
},
|
|
1767
|
+
"bin": {
|
|
1768
|
+
"tsx": "dist/cli.mjs"
|
|
1769
|
+
},
|
|
1770
|
+
"engines": {
|
|
1771
|
+
"node": ">=18.0.0"
|
|
1772
|
+
},
|
|
1773
|
+
"optionalDependencies": {
|
|
1774
|
+
"fsevents": "~2.3.3"
|
|
1775
|
+
}
|
|
1776
|
+
},
|
|
1777
|
+
"node_modules/tsx/node_modules/@esbuild/aix-ppc64": {
|
|
1778
|
+
"version": "0.28.0",
|
|
1779
|
+
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.0.tgz",
|
|
1780
|
+
"integrity": "sha512-lhRUCeuOyJQURhTxl4WkpFTjIsbDayJHih5kZC1giwE+MhIzAb7mEsQMqMf18rHLsrb5qI1tafG20mLxEWcWlA==",
|
|
1781
|
+
"cpu": [
|
|
1782
|
+
"ppc64"
|
|
1783
|
+
],
|
|
1784
|
+
"dev": true,
|
|
1785
|
+
"license": "MIT",
|
|
1786
|
+
"optional": true,
|
|
1787
|
+
"os": [
|
|
1788
|
+
"aix"
|
|
1789
|
+
],
|
|
1790
|
+
"engines": {
|
|
1791
|
+
"node": ">=18"
|
|
1792
|
+
}
|
|
1793
|
+
},
|
|
1794
|
+
"node_modules/tsx/node_modules/@esbuild/android-arm": {
|
|
1795
|
+
"version": "0.28.0",
|
|
1796
|
+
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.0.tgz",
|
|
1797
|
+
"integrity": "sha512-wqh0ByljabXLKHeWXYLqoJ5jKC4XBaw6Hk08OfMrCRd2nP2ZQ5eleDZC41XHyCNgktBGYMbqnrJKq/K/lzPMSQ==",
|
|
1798
|
+
"cpu": [
|
|
1799
|
+
"arm"
|
|
1800
|
+
],
|
|
1801
|
+
"dev": true,
|
|
1802
|
+
"license": "MIT",
|
|
1803
|
+
"optional": true,
|
|
1804
|
+
"os": [
|
|
1805
|
+
"android"
|
|
1806
|
+
],
|
|
1807
|
+
"engines": {
|
|
1808
|
+
"node": ">=18"
|
|
1809
|
+
}
|
|
1810
|
+
},
|
|
1811
|
+
"node_modules/tsx/node_modules/@esbuild/android-arm64": {
|
|
1812
|
+
"version": "0.28.0",
|
|
1813
|
+
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.0.tgz",
|
|
1814
|
+
"integrity": "sha512-+WzIXQOSaGs33tLEgYPYe/yQHf0WTU0X42Jca3y8NWMbUVhp7rUnw+vAsRC/QiDrdD31IszMrZy+qwPOPjd+rw==",
|
|
1815
|
+
"cpu": [
|
|
1816
|
+
"arm64"
|
|
1817
|
+
],
|
|
1818
|
+
"dev": true,
|
|
1819
|
+
"license": "MIT",
|
|
1820
|
+
"optional": true,
|
|
1821
|
+
"os": [
|
|
1822
|
+
"android"
|
|
1823
|
+
],
|
|
1824
|
+
"engines": {
|
|
1825
|
+
"node": ">=18"
|
|
1826
|
+
}
|
|
1827
|
+
},
|
|
1828
|
+
"node_modules/tsx/node_modules/@esbuild/android-x64": {
|
|
1829
|
+
"version": "0.28.0",
|
|
1830
|
+
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.0.tgz",
|
|
1831
|
+
"integrity": "sha512-+VJggoaKhk2VNNqVL7f6S189UzShHC/mR9EE8rDdSkdpN0KflSwWY/gWjDrNxxisg8Fp1ZCD9jLMo4m0OUfeUA==",
|
|
1832
|
+
"cpu": [
|
|
1833
|
+
"x64"
|
|
1834
|
+
],
|
|
1835
|
+
"dev": true,
|
|
1836
|
+
"license": "MIT",
|
|
1837
|
+
"optional": true,
|
|
1838
|
+
"os": [
|
|
1839
|
+
"android"
|
|
1840
|
+
],
|
|
1841
|
+
"engines": {
|
|
1842
|
+
"node": ">=18"
|
|
1843
|
+
}
|
|
1844
|
+
},
|
|
1845
|
+
"node_modules/tsx/node_modules/@esbuild/darwin-arm64": {
|
|
1846
|
+
"version": "0.28.0",
|
|
1847
|
+
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.0.tgz",
|
|
1848
|
+
"integrity": "sha512-0T+A9WZm+bZ84nZBtk1ckYsOvyA3x7e2Acj1KdVfV4/2tdG4fzUp91YHx+GArWLtwqp77pBXVCPn2We7Letr0Q==",
|
|
1849
|
+
"cpu": [
|
|
1850
|
+
"arm64"
|
|
1851
|
+
],
|
|
1852
|
+
"dev": true,
|
|
1853
|
+
"license": "MIT",
|
|
1854
|
+
"optional": true,
|
|
1855
|
+
"os": [
|
|
1856
|
+
"darwin"
|
|
1857
|
+
],
|
|
1858
|
+
"engines": {
|
|
1859
|
+
"node": ">=18"
|
|
1860
|
+
}
|
|
1861
|
+
},
|
|
1862
|
+
"node_modules/tsx/node_modules/@esbuild/darwin-x64": {
|
|
1863
|
+
"version": "0.28.0",
|
|
1864
|
+
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.0.tgz",
|
|
1865
|
+
"integrity": "sha512-fyzLm/DLDl/84OCfp2f/XQ4flmORsjU7VKt8HLjvIXChJoFFOIL6pLJPH4Yhd1n1gGFF9mPwtlN5Wf82DZs+LQ==",
|
|
1866
|
+
"cpu": [
|
|
1867
|
+
"x64"
|
|
1868
|
+
],
|
|
1869
|
+
"dev": true,
|
|
1870
|
+
"license": "MIT",
|
|
1871
|
+
"optional": true,
|
|
1872
|
+
"os": [
|
|
1873
|
+
"darwin"
|
|
1874
|
+
],
|
|
1875
|
+
"engines": {
|
|
1876
|
+
"node": ">=18"
|
|
1877
|
+
}
|
|
1878
|
+
},
|
|
1879
|
+
"node_modules/tsx/node_modules/@esbuild/freebsd-arm64": {
|
|
1880
|
+
"version": "0.28.0",
|
|
1881
|
+
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.0.tgz",
|
|
1882
|
+
"integrity": "sha512-l9GeW5UZBT9k9brBYI+0WDffcRxgHQD8ShN2Ur4xWq/NFzUKm3k5lsH4PdaRgb2w7mI9u61nr2gI2mLI27Nh3Q==",
|
|
1883
|
+
"cpu": [
|
|
1884
|
+
"arm64"
|
|
1885
|
+
],
|
|
1886
|
+
"dev": true,
|
|
1887
|
+
"license": "MIT",
|
|
1888
|
+
"optional": true,
|
|
1889
|
+
"os": [
|
|
1890
|
+
"freebsd"
|
|
1891
|
+
],
|
|
1892
|
+
"engines": {
|
|
1893
|
+
"node": ">=18"
|
|
1894
|
+
}
|
|
1895
|
+
},
|
|
1896
|
+
"node_modules/tsx/node_modules/@esbuild/freebsd-x64": {
|
|
1897
|
+
"version": "0.28.0",
|
|
1898
|
+
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.0.tgz",
|
|
1899
|
+
"integrity": "sha512-BXoQai/A0wPO6Es3yFJ7APCiKGc1tdAEOgeTNy3SsB491S3aHn4S4r3e976eUnPdU+NbdtmBuLncYir2tMU9Nw==",
|
|
1900
|
+
"cpu": [
|
|
1901
|
+
"x64"
|
|
1902
|
+
],
|
|
1903
|
+
"dev": true,
|
|
1904
|
+
"license": "MIT",
|
|
1905
|
+
"optional": true,
|
|
1906
|
+
"os": [
|
|
1907
|
+
"freebsd"
|
|
1908
|
+
],
|
|
1909
|
+
"engines": {
|
|
1910
|
+
"node": ">=18"
|
|
1911
|
+
}
|
|
1912
|
+
},
|
|
1913
|
+
"node_modules/tsx/node_modules/@esbuild/linux-arm": {
|
|
1914
|
+
"version": "0.28.0",
|
|
1915
|
+
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.0.tgz",
|
|
1916
|
+
"integrity": "sha512-CjaaREJagqJp7iTaNQjjidaNbCKYcd4IDkzbwwxtSvjI7NZm79qiHc8HqciMddQ6CKvJT6aBd8lO9kN/ZudLlw==",
|
|
1917
|
+
"cpu": [
|
|
1918
|
+
"arm"
|
|
1919
|
+
],
|
|
1920
|
+
"dev": true,
|
|
1921
|
+
"license": "MIT",
|
|
1922
|
+
"optional": true,
|
|
1923
|
+
"os": [
|
|
1924
|
+
"linux"
|
|
1925
|
+
],
|
|
1926
|
+
"engines": {
|
|
1927
|
+
"node": ">=18"
|
|
1928
|
+
}
|
|
1929
|
+
},
|
|
1930
|
+
"node_modules/tsx/node_modules/@esbuild/linux-arm64": {
|
|
1931
|
+
"version": "0.28.0",
|
|
1932
|
+
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.0.tgz",
|
|
1933
|
+
"integrity": "sha512-RVyzfb3FWsGA55n6WY0MEIEPURL1FcbhFE6BffZEMEekfCzCIMtB5yyDcFnVbTnwk+CLAgTujmV/Lgvih56W+A==",
|
|
1934
|
+
"cpu": [
|
|
1935
|
+
"arm64"
|
|
1936
|
+
],
|
|
1937
|
+
"dev": true,
|
|
1938
|
+
"license": "MIT",
|
|
1939
|
+
"optional": true,
|
|
1940
|
+
"os": [
|
|
1941
|
+
"linux"
|
|
1942
|
+
],
|
|
1943
|
+
"engines": {
|
|
1944
|
+
"node": ">=18"
|
|
1945
|
+
}
|
|
1946
|
+
},
|
|
1947
|
+
"node_modules/tsx/node_modules/@esbuild/linux-ia32": {
|
|
1948
|
+
"version": "0.28.0",
|
|
1949
|
+
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.0.tgz",
|
|
1950
|
+
"integrity": "sha512-KBnSTt1kxl9x70q+ydterVdl+Cn0H18ngRMRCEQfrbqdUuntQQ0LoMZv47uB97NljZFzY6HcfqEZ2SAyIUTQBQ==",
|
|
1951
|
+
"cpu": [
|
|
1952
|
+
"ia32"
|
|
1953
|
+
],
|
|
1954
|
+
"dev": true,
|
|
1955
|
+
"license": "MIT",
|
|
1956
|
+
"optional": true,
|
|
1957
|
+
"os": [
|
|
1958
|
+
"linux"
|
|
1959
|
+
],
|
|
1960
|
+
"engines": {
|
|
1961
|
+
"node": ">=18"
|
|
1962
|
+
}
|
|
1963
|
+
},
|
|
1964
|
+
"node_modules/tsx/node_modules/@esbuild/linux-loong64": {
|
|
1965
|
+
"version": "0.28.0",
|
|
1966
|
+
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.0.tgz",
|
|
1967
|
+
"integrity": "sha512-zpSlUce1mnxzgBADvxKXX5sl8aYQHo2ezvMNI8I0lbblJtp8V4odlm3Yzlj7gPyt3T8ReksE6bK+pT3WD+aJRg==",
|
|
1968
|
+
"cpu": [
|
|
1969
|
+
"loong64"
|
|
1970
|
+
],
|
|
1971
|
+
"dev": true,
|
|
1972
|
+
"license": "MIT",
|
|
1973
|
+
"optional": true,
|
|
1974
|
+
"os": [
|
|
1975
|
+
"linux"
|
|
1976
|
+
],
|
|
1977
|
+
"engines": {
|
|
1978
|
+
"node": ">=18"
|
|
1979
|
+
}
|
|
1980
|
+
},
|
|
1981
|
+
"node_modules/tsx/node_modules/@esbuild/linux-mips64el": {
|
|
1982
|
+
"version": "0.28.0",
|
|
1983
|
+
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.0.tgz",
|
|
1984
|
+
"integrity": "sha512-2jIfP6mmjkdmeTlsX/9vmdmhBmKADrWqN7zcdtHIeNSCH1SqIoNI63cYsjQR8J+wGa4Y5izRcSHSm8K3QWmk3w==",
|
|
1985
|
+
"cpu": [
|
|
1986
|
+
"mips64el"
|
|
1987
|
+
],
|
|
1988
|
+
"dev": true,
|
|
1989
|
+
"license": "MIT",
|
|
1990
|
+
"optional": true,
|
|
1991
|
+
"os": [
|
|
1992
|
+
"linux"
|
|
1993
|
+
],
|
|
1994
|
+
"engines": {
|
|
1995
|
+
"node": ">=18"
|
|
1996
|
+
}
|
|
1997
|
+
},
|
|
1998
|
+
"node_modules/tsx/node_modules/@esbuild/linux-ppc64": {
|
|
1999
|
+
"version": "0.28.0",
|
|
2000
|
+
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.0.tgz",
|
|
2001
|
+
"integrity": "sha512-bc0FE9wWeC0WBm49IQMPSPILRocGTQt3j5KPCA8os6VprfuJ7KD+5PzESSrJ6GmPIPJK965ZJHTUlSA6GNYEhg==",
|
|
2002
|
+
"cpu": [
|
|
2003
|
+
"ppc64"
|
|
2004
|
+
],
|
|
2005
|
+
"dev": true,
|
|
2006
|
+
"license": "MIT",
|
|
2007
|
+
"optional": true,
|
|
2008
|
+
"os": [
|
|
2009
|
+
"linux"
|
|
2010
|
+
],
|
|
2011
|
+
"engines": {
|
|
2012
|
+
"node": ">=18"
|
|
2013
|
+
}
|
|
2014
|
+
},
|
|
2015
|
+
"node_modules/tsx/node_modules/@esbuild/linux-riscv64": {
|
|
2016
|
+
"version": "0.28.0",
|
|
2017
|
+
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.0.tgz",
|
|
2018
|
+
"integrity": "sha512-SQPZOwoTTT/HXFXQJG/vBX8sOFagGqvZyXcgLA3NhIqcBv1BJU1d46c0rGcrij2B56Z2rNiSLaZOYW5cUk7yLQ==",
|
|
2019
|
+
"cpu": [
|
|
2020
|
+
"riscv64"
|
|
2021
|
+
],
|
|
2022
|
+
"dev": true,
|
|
2023
|
+
"license": "MIT",
|
|
2024
|
+
"optional": true,
|
|
2025
|
+
"os": [
|
|
2026
|
+
"linux"
|
|
2027
|
+
],
|
|
2028
|
+
"engines": {
|
|
2029
|
+
"node": ">=18"
|
|
2030
|
+
}
|
|
2031
|
+
},
|
|
2032
|
+
"node_modules/tsx/node_modules/@esbuild/linux-s390x": {
|
|
2033
|
+
"version": "0.28.0",
|
|
2034
|
+
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.0.tgz",
|
|
2035
|
+
"integrity": "sha512-SCfR0HN8CEEjnYnySJTd2cw0k9OHB/YFzt5zgJEwa+wL/T/raGWYMBqwDNAC6dqFKmJYZoQBRfHjgwLHGSrn3Q==",
|
|
2036
|
+
"cpu": [
|
|
2037
|
+
"s390x"
|
|
2038
|
+
],
|
|
2039
|
+
"dev": true,
|
|
2040
|
+
"license": "MIT",
|
|
2041
|
+
"optional": true,
|
|
2042
|
+
"os": [
|
|
2043
|
+
"linux"
|
|
2044
|
+
],
|
|
2045
|
+
"engines": {
|
|
2046
|
+
"node": ">=18"
|
|
2047
|
+
}
|
|
2048
|
+
},
|
|
2049
|
+
"node_modules/tsx/node_modules/@esbuild/linux-x64": {
|
|
2050
|
+
"version": "0.28.0",
|
|
2051
|
+
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.0.tgz",
|
|
2052
|
+
"integrity": "sha512-us0dSb9iFxIi8srnpl931Nvs65it/Jd2a2K3qs7fz2WfGPHqzfzZTfec7oxZJRNPXPnNYZtanmRc4AL/JwVzHQ==",
|
|
2053
|
+
"cpu": [
|
|
2054
|
+
"x64"
|
|
2055
|
+
],
|
|
2056
|
+
"dev": true,
|
|
2057
|
+
"license": "MIT",
|
|
2058
|
+
"optional": true,
|
|
2059
|
+
"os": [
|
|
2060
|
+
"linux"
|
|
2061
|
+
],
|
|
2062
|
+
"engines": {
|
|
2063
|
+
"node": ">=18"
|
|
2064
|
+
}
|
|
2065
|
+
},
|
|
2066
|
+
"node_modules/tsx/node_modules/@esbuild/netbsd-arm64": {
|
|
2067
|
+
"version": "0.28.0",
|
|
2068
|
+
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.0.tgz",
|
|
2069
|
+
"integrity": "sha512-CR/RYotgtCKwtftMwJlUU7xCVNg3lMYZ0RzTmAHSfLCXw3NtZtNpswLEj/Kkf6kEL3Gw+BpOekRX0BYCtklhUw==",
|
|
2070
|
+
"cpu": [
|
|
2071
|
+
"arm64"
|
|
2072
|
+
],
|
|
2073
|
+
"dev": true,
|
|
2074
|
+
"license": "MIT",
|
|
2075
|
+
"optional": true,
|
|
2076
|
+
"os": [
|
|
2077
|
+
"netbsd"
|
|
2078
|
+
],
|
|
2079
|
+
"engines": {
|
|
2080
|
+
"node": ">=18"
|
|
2081
|
+
}
|
|
2082
|
+
},
|
|
2083
|
+
"node_modules/tsx/node_modules/@esbuild/netbsd-x64": {
|
|
2084
|
+
"version": "0.28.0",
|
|
2085
|
+
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.0.tgz",
|
|
2086
|
+
"integrity": "sha512-nU1yhmYutL+fQ71Kxnhg8uEOdC0pwEW9entHykTgEbna2pw2dkbFSMeqjjyHZoCmt8SBkOSvV+yNmm94aUrrqw==",
|
|
2087
|
+
"cpu": [
|
|
2088
|
+
"x64"
|
|
2089
|
+
],
|
|
2090
|
+
"dev": true,
|
|
2091
|
+
"license": "MIT",
|
|
2092
|
+
"optional": true,
|
|
2093
|
+
"os": [
|
|
2094
|
+
"netbsd"
|
|
2095
|
+
],
|
|
2096
|
+
"engines": {
|
|
2097
|
+
"node": ">=18"
|
|
2098
|
+
}
|
|
2099
|
+
},
|
|
2100
|
+
"node_modules/tsx/node_modules/@esbuild/openbsd-arm64": {
|
|
2101
|
+
"version": "0.28.0",
|
|
2102
|
+
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.0.tgz",
|
|
2103
|
+
"integrity": "sha512-cXb5vApOsRsxsEl4mcZ1XY3D4DzcoMxR/nnc4IyqYs0rTI8ZKmW6kyyg+11Z8yvgMfAEldKzP7AdP64HnSC/6g==",
|
|
2104
|
+
"cpu": [
|
|
2105
|
+
"arm64"
|
|
2106
|
+
],
|
|
2107
|
+
"dev": true,
|
|
2108
|
+
"license": "MIT",
|
|
2109
|
+
"optional": true,
|
|
2110
|
+
"os": [
|
|
2111
|
+
"openbsd"
|
|
2112
|
+
],
|
|
2113
|
+
"engines": {
|
|
2114
|
+
"node": ">=18"
|
|
2115
|
+
}
|
|
2116
|
+
},
|
|
2117
|
+
"node_modules/tsx/node_modules/@esbuild/openbsd-x64": {
|
|
2118
|
+
"version": "0.28.0",
|
|
2119
|
+
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.0.tgz",
|
|
2120
|
+
"integrity": "sha512-8wZM2qqtv9UP3mzy7HiGYNH/zjTA355mpeuA+859TyR+e+Tc08IHYpLJuMsfpDJwoLo1ikIJI8jC3GFjnRClzA==",
|
|
2121
|
+
"cpu": [
|
|
2122
|
+
"x64"
|
|
2123
|
+
],
|
|
2124
|
+
"dev": true,
|
|
2125
|
+
"license": "MIT",
|
|
2126
|
+
"optional": true,
|
|
2127
|
+
"os": [
|
|
2128
|
+
"openbsd"
|
|
2129
|
+
],
|
|
2130
|
+
"engines": {
|
|
2131
|
+
"node": ">=18"
|
|
2132
|
+
}
|
|
2133
|
+
},
|
|
2134
|
+
"node_modules/tsx/node_modules/@esbuild/openharmony-arm64": {
|
|
2135
|
+
"version": "0.28.0",
|
|
2136
|
+
"resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.0.tgz",
|
|
2137
|
+
"integrity": "sha512-FLGfyizszcef5C3YtoyQDACyg95+dndv79i2EekILBofh5wpCa1KuBqOWKrEHZg3zrL3t5ouE5jgr94vA+Wb2w==",
|
|
2138
|
+
"cpu": [
|
|
2139
|
+
"arm64"
|
|
2140
|
+
],
|
|
2141
|
+
"dev": true,
|
|
2142
|
+
"license": "MIT",
|
|
2143
|
+
"optional": true,
|
|
2144
|
+
"os": [
|
|
2145
|
+
"openharmony"
|
|
2146
|
+
],
|
|
2147
|
+
"engines": {
|
|
2148
|
+
"node": ">=18"
|
|
2149
|
+
}
|
|
2150
|
+
},
|
|
2151
|
+
"node_modules/tsx/node_modules/@esbuild/sunos-x64": {
|
|
2152
|
+
"version": "0.28.0",
|
|
2153
|
+
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.0.tgz",
|
|
2154
|
+
"integrity": "sha512-1ZgjUoEdHZZl/YlV76TSCz9Hqj9h9YmMGAgAPYd+q4SicWNX3G5GCyx9uhQWSLcbvPW8Ni7lj4gDa1T40akdlw==",
|
|
2155
|
+
"cpu": [
|
|
2156
|
+
"x64"
|
|
2157
|
+
],
|
|
2158
|
+
"dev": true,
|
|
2159
|
+
"license": "MIT",
|
|
2160
|
+
"optional": true,
|
|
2161
|
+
"os": [
|
|
2162
|
+
"sunos"
|
|
2163
|
+
],
|
|
2164
|
+
"engines": {
|
|
2165
|
+
"node": ">=18"
|
|
2166
|
+
}
|
|
2167
|
+
},
|
|
2168
|
+
"node_modules/tsx/node_modules/@esbuild/win32-arm64": {
|
|
2169
|
+
"version": "0.28.0",
|
|
2170
|
+
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.0.tgz",
|
|
2171
|
+
"integrity": "sha512-Q9StnDmQ/enxnpxCCLSg0oo4+34B9TdXpuyPeTedN/6+iXBJ4J+zwfQI28u/Jl40nOYAxGoNi7mFP40RUtkmUA==",
|
|
2172
|
+
"cpu": [
|
|
2173
|
+
"arm64"
|
|
2174
|
+
],
|
|
2175
|
+
"dev": true,
|
|
2176
|
+
"license": "MIT",
|
|
2177
|
+
"optional": true,
|
|
2178
|
+
"os": [
|
|
2179
|
+
"win32"
|
|
2180
|
+
],
|
|
2181
|
+
"engines": {
|
|
2182
|
+
"node": ">=18"
|
|
2183
|
+
}
|
|
2184
|
+
},
|
|
2185
|
+
"node_modules/tsx/node_modules/@esbuild/win32-ia32": {
|
|
2186
|
+
"version": "0.28.0",
|
|
2187
|
+
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.0.tgz",
|
|
2188
|
+
"integrity": "sha512-zF3ag/gfiCe6U2iczcRzSYJKH1DCI+ByzSENHlM2FcDbEeo5Zd2C86Aq0tKUYAJJ1obRP84ymxIAksZUcdztHA==",
|
|
2189
|
+
"cpu": [
|
|
2190
|
+
"ia32"
|
|
2191
|
+
],
|
|
2192
|
+
"dev": true,
|
|
2193
|
+
"license": "MIT",
|
|
2194
|
+
"optional": true,
|
|
2195
|
+
"os": [
|
|
2196
|
+
"win32"
|
|
2197
|
+
],
|
|
2198
|
+
"engines": {
|
|
2199
|
+
"node": ">=18"
|
|
2200
|
+
}
|
|
2201
|
+
},
|
|
2202
|
+
"node_modules/tsx/node_modules/@esbuild/win32-x64": {
|
|
2203
|
+
"version": "0.28.0",
|
|
2204
|
+
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.0.tgz",
|
|
2205
|
+
"integrity": "sha512-pEl1bO9mfAmIC+tW5btTmrKaujg3zGtUmWNdCw/xs70FBjwAL3o9OEKNHvNmnyylD6ubxUERiEhdsL0xBQ9efw==",
|
|
2206
|
+
"cpu": [
|
|
2207
|
+
"x64"
|
|
2208
|
+
],
|
|
2209
|
+
"dev": true,
|
|
2210
|
+
"license": "MIT",
|
|
2211
|
+
"optional": true,
|
|
2212
|
+
"os": [
|
|
2213
|
+
"win32"
|
|
2214
|
+
],
|
|
2215
|
+
"engines": {
|
|
2216
|
+
"node": ">=18"
|
|
2217
|
+
}
|
|
2218
|
+
},
|
|
2219
|
+
"node_modules/tsx/node_modules/esbuild": {
|
|
2220
|
+
"version": "0.28.0",
|
|
2221
|
+
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.0.tgz",
|
|
2222
|
+
"integrity": "sha512-sNR9MHpXSUV/XB4zmsFKN+QgVG82Cc7+/aaxJ8Adi8hyOac+EXptIp45QBPaVyX3N70664wRbTcLTOemCAnyqw==",
|
|
2223
|
+
"dev": true,
|
|
2224
|
+
"hasInstallScript": true,
|
|
2225
|
+
"license": "MIT",
|
|
2226
|
+
"bin": {
|
|
2227
|
+
"esbuild": "bin/esbuild"
|
|
2228
|
+
},
|
|
2229
|
+
"engines": {
|
|
2230
|
+
"node": ">=18"
|
|
2231
|
+
},
|
|
2232
|
+
"optionalDependencies": {
|
|
2233
|
+
"@esbuild/aix-ppc64": "0.28.0",
|
|
2234
|
+
"@esbuild/android-arm": "0.28.0",
|
|
2235
|
+
"@esbuild/android-arm64": "0.28.0",
|
|
2236
|
+
"@esbuild/android-x64": "0.28.0",
|
|
2237
|
+
"@esbuild/darwin-arm64": "0.28.0",
|
|
2238
|
+
"@esbuild/darwin-x64": "0.28.0",
|
|
2239
|
+
"@esbuild/freebsd-arm64": "0.28.0",
|
|
2240
|
+
"@esbuild/freebsd-x64": "0.28.0",
|
|
2241
|
+
"@esbuild/linux-arm": "0.28.0",
|
|
2242
|
+
"@esbuild/linux-arm64": "0.28.0",
|
|
2243
|
+
"@esbuild/linux-ia32": "0.28.0",
|
|
2244
|
+
"@esbuild/linux-loong64": "0.28.0",
|
|
2245
|
+
"@esbuild/linux-mips64el": "0.28.0",
|
|
2246
|
+
"@esbuild/linux-ppc64": "0.28.0",
|
|
2247
|
+
"@esbuild/linux-riscv64": "0.28.0",
|
|
2248
|
+
"@esbuild/linux-s390x": "0.28.0",
|
|
2249
|
+
"@esbuild/linux-x64": "0.28.0",
|
|
2250
|
+
"@esbuild/netbsd-arm64": "0.28.0",
|
|
2251
|
+
"@esbuild/netbsd-x64": "0.28.0",
|
|
2252
|
+
"@esbuild/openbsd-arm64": "0.28.0",
|
|
2253
|
+
"@esbuild/openbsd-x64": "0.28.0",
|
|
2254
|
+
"@esbuild/openharmony-arm64": "0.28.0",
|
|
2255
|
+
"@esbuild/sunos-x64": "0.28.0",
|
|
2256
|
+
"@esbuild/win32-arm64": "0.28.0",
|
|
2257
|
+
"@esbuild/win32-ia32": "0.28.0",
|
|
2258
|
+
"@esbuild/win32-x64": "0.28.0"
|
|
2259
|
+
}
|
|
2260
|
+
},
|
|
1757
2261
|
"node_modules/typescript": {
|
|
1758
2262
|
"version": "5.9.3",
|
|
1759
2263
|
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
|
package/sdk/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gsd-build/sdk",
|
|
3
|
-
"version": "1.42.
|
|
3
|
+
"version": "1.42.2",
|
|
4
4
|
"description": "GSD SDK — programmatic interface for running GSD plans via the Agent SDK",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -36,6 +36,8 @@
|
|
|
36
36
|
"scripts": {
|
|
37
37
|
"build": "tsc",
|
|
38
38
|
"check:alias-drift": "npm run build && node scripts/check-command-aliases-fresh.mjs",
|
|
39
|
+
"gen:state-document": "npm run build && npx tsx scripts/gen-state-document.ts",
|
|
40
|
+
"check:state-document-fresh": "npm run build && node scripts/check-state-document-fresh.mjs",
|
|
39
41
|
"prepublishOnly": "rm -rf dist && tsc && chmod +x dist/cli.js",
|
|
40
42
|
"test": "vitest run",
|
|
41
43
|
"test:unit": "vitest run --project unit",
|
|
@@ -48,6 +50,7 @@
|
|
|
48
50
|
"devDependencies": {
|
|
49
51
|
"@types/node": "^22.0.0",
|
|
50
52
|
"@types/ws": "^8.18.1",
|
|
53
|
+
"tsx": "^4.22.0",
|
|
51
54
|
"typescript": "^5.7.0",
|
|
52
55
|
"vitest": "^3.1.1"
|
|
53
56
|
}
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit tests for STATE.md Document Module — stateExtractField.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { describe, it, expect } from 'vitest';
|
|
6
|
+
import {
|
|
7
|
+
stateExtractField,
|
|
8
|
+
stateReplaceField,
|
|
9
|
+
stateReplaceFieldWithFallback,
|
|
10
|
+
normalizeStateStatus,
|
|
11
|
+
computeProgressPercent,
|
|
12
|
+
shouldPreserveExistingProgress,
|
|
13
|
+
normalizeProgressNumbers,
|
|
14
|
+
} from './state-document.js';
|
|
15
|
+
|
|
16
|
+
describe('stateExtractField', () => {
|
|
17
|
+
it('extracts value from bold pattern', () => {
|
|
18
|
+
const content = 'Some content\n**FieldName:** the value\nMore content';
|
|
19
|
+
expect(stateExtractField(content, 'FieldName')).toBe('the value');
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it('extracts value from plain pattern', () => {
|
|
23
|
+
const content = 'Some content\nFieldName: the value\nMore content';
|
|
24
|
+
expect(stateExtractField(content, 'FieldName')).toBe('the value');
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it('returns null when field is missing', () => {
|
|
28
|
+
const content = 'Some content\nOtherField: something\nMore content';
|
|
29
|
+
expect(stateExtractField(content, 'FieldName')).toBeNull();
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
describe('stateReplaceField', () => {
|
|
34
|
+
it('replaces value in bold pattern', () => {
|
|
35
|
+
const content = 'Some content\n**Status:** old value\nMore content';
|
|
36
|
+
const result = stateReplaceField(content, 'Status', 'new value');
|
|
37
|
+
expect(result).toBe('Some content\n**Status:** new value\nMore content');
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it('replaces value in plain pattern', () => {
|
|
41
|
+
const content = 'Some content\nStatus: old value\nMore content';
|
|
42
|
+
const result = stateReplaceField(content, 'Status', 'new value');
|
|
43
|
+
expect(result).toBe('Some content\nStatus: new value\nMore content');
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it('returns null when field is missing', () => {
|
|
47
|
+
const content = 'Some content\nOtherField: something\nMore content';
|
|
48
|
+
const result = stateReplaceField(content, 'Status', 'new value');
|
|
49
|
+
expect(result).toBeNull();
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
describe('stateReplaceFieldWithFallback', () => {
|
|
54
|
+
it('replaces primary field when it exists', () => {
|
|
55
|
+
const content = 'Status: old\nState: backup';
|
|
56
|
+
const result = stateReplaceFieldWithFallback(content, 'Status', 'State', 'new');
|
|
57
|
+
expect(result).toBe('Status: new\nState: backup');
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it('replaces fallback field when primary is missing', () => {
|
|
61
|
+
const content = 'Other: something\nState: backup';
|
|
62
|
+
const result = stateReplaceFieldWithFallback(content, 'Status', 'State', 'new');
|
|
63
|
+
expect(result).toBe('Other: something\nState: new');
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it('returns content unchanged when neither field exists', () => {
|
|
67
|
+
const content = 'Other: something\nAnother: value';
|
|
68
|
+
const result = stateReplaceFieldWithFallback(content, 'Status', 'State', 'new');
|
|
69
|
+
expect(result).toBe(content);
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
describe('normalizeStateStatus', () => {
|
|
74
|
+
it('returns paused for status containing "paused"', () => {
|
|
75
|
+
expect(normalizeStateStatus('paused')).toBe('paused');
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it('returns paused for status containing "stopped"', () => {
|
|
79
|
+
expect(normalizeStateStatus('stopped')).toBe('paused');
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it('returns paused when non-null pausedAt is provided', () => {
|
|
83
|
+
expect(normalizeStateStatus('active', '2024-01-01')).toBe('paused');
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it('returns executing for status containing "executing"', () => {
|
|
87
|
+
expect(normalizeStateStatus('executing')).toBe('executing');
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it('returns executing for status "in progress"', () => {
|
|
91
|
+
expect(normalizeStateStatus('in progress')).toBe('executing');
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it('returns executing for status "ready to execute"', () => {
|
|
95
|
+
expect(normalizeStateStatus('ready to execute')).toBe('executing');
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
it('returns planning for status containing "planning"', () => {
|
|
99
|
+
expect(normalizeStateStatus('planning')).toBe('planning');
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it('returns discussing for status containing "discussing"', () => {
|
|
103
|
+
expect(normalizeStateStatus('discussing')).toBe('discussing');
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
it('returns verifying for status containing "verif"', () => {
|
|
107
|
+
expect(normalizeStateStatus('verifying')).toBe('verifying');
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
it('returns completed for status containing "complete"', () => {
|
|
111
|
+
expect(normalizeStateStatus('completed')).toBe('completed');
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
it('returns completed for status containing "done"', () => {
|
|
115
|
+
expect(normalizeStateStatus('done')).toBe('completed');
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
it('returns unknown for unrecognized status', () => {
|
|
119
|
+
expect(normalizeStateStatus('something-else')).toBe('something-else');
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
it('returns unknown for null status', () => {
|
|
123
|
+
expect(normalizeStateStatus(null)).toBe('unknown');
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
describe('computeProgressPercent', () => {
|
|
128
|
+
it('uses only plans data when phases data is absent', () => {
|
|
129
|
+
expect(computeProgressPercent(3, 10, null, null)).toBe(30);
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
it('uses only phases data when plans data is absent', () => {
|
|
133
|
+
expect(computeProgressPercent(null, null, 2, 4)).toBe(50);
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
it('uses minimum fraction when both plans and phases data are present', () => {
|
|
137
|
+
// plans: 8/10 = 80%, phases: 3/10 = 30% → min = 30%
|
|
138
|
+
expect(computeProgressPercent(8, 10, 3, 10)).toBe(30);
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
it('returns null when neither plans nor phases data is present', () => {
|
|
142
|
+
expect(computeProgressPercent(null, null, null, null)).toBeNull();
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
it('returns null when total is 0 (treated as no data)', () => {
|
|
146
|
+
expect(computeProgressPercent(0, 0, null, null)).toBeNull();
|
|
147
|
+
});
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
describe('shouldPreserveExistingProgress', () => {
|
|
151
|
+
it('returns true when existing total_phases exceeds derived', () => {
|
|
152
|
+
expect(shouldPreserveExistingProgress({ total_phases: 10 }, { total_phases: 5 })).toBe(true);
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
it('returns false when derived exceeds existing', () => {
|
|
156
|
+
expect(shouldPreserveExistingProgress({ total_phases: 5 }, { total_phases: 10 })).toBe(false);
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
it('returns false when existingProgress is not an object', () => {
|
|
160
|
+
expect(shouldPreserveExistingProgress(null, { total_phases: 5 })).toBe(false);
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
it('returns false when both are null', () => {
|
|
164
|
+
expect(shouldPreserveExistingProgress(null, null)).toBe(false);
|
|
165
|
+
});
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
describe('normalizeProgressNumbers', () => {
|
|
169
|
+
it('coerces all five tracked keys to numbers', () => {
|
|
170
|
+
const input = {
|
|
171
|
+
total_phases: '10',
|
|
172
|
+
completed_phases: '3',
|
|
173
|
+
total_plans: '5',
|
|
174
|
+
completed_plans: '2',
|
|
175
|
+
percent: '60',
|
|
176
|
+
};
|
|
177
|
+
expect(normalizeProgressNumbers(input)).toEqual({
|
|
178
|
+
total_phases: 10,
|
|
179
|
+
completed_phases: 3,
|
|
180
|
+
total_plans: 5,
|
|
181
|
+
completed_plans: 2,
|
|
182
|
+
percent: 60,
|
|
183
|
+
});
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
it('returns non-object input unchanged', () => {
|
|
187
|
+
expect(normalizeProgressNumbers(null)).toBeNull();
|
|
188
|
+
expect(normalizeProgressNumbers('string')).toBe('string');
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
it('preserves extra keys untouched', () => {
|
|
192
|
+
const input = { total_phases: '4', extra_key: 'hello' };
|
|
193
|
+
const result = normalizeProgressNumbers(input) as Record<string, unknown>;
|
|
194
|
+
expect(result.total_phases).toBe(4);
|
|
195
|
+
expect(result.extra_key).toBe('hello');
|
|
196
|
+
});
|
|
197
|
+
});
|
package/sdk-bundle/gsd-sdk.tgz
CHANGED
|
Binary file
|