@hegemonart/get-design-done 1.30.6 → 1.31.5
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 +6 -3
- package/.claude-plugin/plugin.json +5 -2
- package/CHANGELOG.md +105 -0
- package/NOTICE +224 -0
- package/README.md +22 -1
- package/SKILL.md +1 -0
- package/agents/design-authority-watcher.md +1 -1
- package/agents/perf-analyzer.md +2 -2
- package/bin/gdd-mcp +78 -0
- package/bin/gdd-sdk +34 -24
- package/bin/gdd-state-mcp +78 -0
- package/{README.de.md → docs/i18n/README.de.md} +1 -1
- package/{README.fr.md → docs/i18n/README.fr.md} +1 -1
- package/{README.it.md → docs/i18n/README.it.md} +1 -1
- package/{README.ja.md → docs/i18n/README.ja.md} +1 -1
- package/{README.ko.md → docs/i18n/README.ko.md} +1 -1
- package/{README.zh-CN.md → docs/i18n/README.zh-CN.md} +1 -1
- package/hooks/_hook-emit.js +1 -1
- package/hooks/budget-enforcer.ts +5 -5
- package/hooks/context-exhaustion.ts +2 -2
- package/hooks/gdd-precompact-snapshot.js +3 -3
- package/hooks/gdd-read-injection-scanner.ts +2 -2
- package/hooks/gdd-sessionstart-recap.js +1 -1
- package/hooks/gdd-turn-closeout.js +1 -1
- package/package.json +24 -10
- package/recipes/.gitkeep +0 -0
- package/reference/schemas/recipe.schema.json +33 -0
- package/scripts/cli/gdd-events.mjs +5 -5
- package/scripts/lib/cache/gdd-cache-manager.cjs +1 -1
- package/scripts/lib/cli/index.ts +22 -160
- package/scripts/lib/connection-probe/index.cjs +1 -1
- package/scripts/lib/discuss-parallel-runner/aggregator.ts +1 -1
- package/scripts/lib/discuss-parallel-runner/index.ts +1 -1
- package/scripts/lib/error-classifier.cjs +24 -227
- package/scripts/lib/event-stream/index.ts +25 -193
- package/scripts/lib/figma-extract/digest.cjs +430 -0
- package/scripts/lib/figma-extract/parse-url.cjs +87 -0
- package/scripts/lib/figma-extract/payload-schema.json +108 -0
- package/scripts/lib/figma-extract/pull.cjs +394 -0
- package/scripts/lib/figma-extract/receiver.cjs +273 -0
- package/scripts/lib/figma-extract/render-md.cjs +143 -0
- package/scripts/lib/figma-extract/styles-resolver.cjs +147 -0
- package/scripts/lib/figma-extract/walk.cjs +100 -0
- package/scripts/lib/gdd-errors/index.ts +24 -213
- package/scripts/lib/gdd-state/index.ts +23 -161
- package/scripts/lib/health-mirror/index.cjs +88 -1
- package/scripts/lib/iteration-budget.cjs +23 -199
- package/scripts/lib/jittered-backoff.cjs +24 -107
- package/scripts/lib/lockfile.cjs +23 -195
- package/scripts/lib/logger/index.ts +1 -1
- package/scripts/lib/parallelism-engine/concurrency-tuner.cjs +1 -1
- package/scripts/lib/perf-analyzer/index.cjs +1 -1
- package/scripts/lib/pipeline-runner/index.ts +4 -4
- package/scripts/lib/pipeline-runner/state-machine.ts +1 -1
- package/scripts/lib/prompt-dedup/index.cjs +1 -1
- package/scripts/lib/rate-guard.cjs +2 -2
- package/scripts/lib/recipe-loader.cjs +142 -0
- package/scripts/lib/session-runner/errors.ts +3 -3
- package/scripts/lib/session-runner/index.ts +3 -3
- package/scripts/lib/session-runner/transcript.ts +1 -1
- package/scripts/lib/tool-scoping/index.ts +1 -1
- package/scripts/mcp-servers/gdd-mcp/server.ts +29 -311
- package/scripts/mcp-servers/gdd-state/server.ts +28 -282
- package/sdk/README.md +45 -0
- package/{scripts/lib → sdk}/cli/commands/audit.ts +3 -3
- package/{scripts/lib → sdk}/cli/commands/init.ts +3 -3
- package/{scripts/lib → sdk}/cli/commands/query.ts +4 -4
- package/{scripts/lib → sdk}/cli/commands/run.ts +5 -5
- package/{scripts/lib → sdk}/cli/commands/stage.ts +5 -5
- package/sdk/cli/index.js +8091 -0
- package/sdk/cli/index.ts +172 -0
- package/{scripts/lib → sdk}/cli/parse-args.ts +2 -2
- package/{scripts/lib/gdd-errors → sdk/errors}/classification.ts +1 -1
- package/sdk/errors/index.ts +218 -0
- package/{scripts/lib → sdk}/event-stream/emitter.ts +1 -1
- package/sdk/event-stream/index.ts +197 -0
- package/{scripts/lib → sdk}/event-stream/reader.ts +1 -1
- package/{scripts/lib → sdk}/event-stream/types.ts +2 -2
- package/{scripts/lib → sdk}/event-stream/writer.ts +1 -1
- package/sdk/index.ts +19 -0
- package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/README.md +3 -3
- package/sdk/mcp/gdd-mcp/server.js +1924 -0
- package/sdk/mcp/gdd-mcp/server.ts +325 -0
- package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/tools/gdd_cycle_recap.ts +3 -3
- package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/tools/gdd_decisions_list.ts +2 -2
- package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/tools/gdd_events_tail.ts +3 -3
- package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/tools/gdd_health.ts +2 -2
- package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/tools/gdd_intel_get.ts +2 -2
- package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/tools/gdd_learnings_digest.ts +2 -2
- package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/tools/gdd_phase_current.ts +2 -2
- package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/tools/gdd_phases_list.ts +2 -2
- package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/tools/gdd_plans_list.ts +2 -2
- package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/tools/gdd_reflections_latest.ts +2 -2
- package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/tools/gdd_status.ts +3 -3
- package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/tools/gdd_telemetry_query.ts +3 -3
- package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/tools/index.ts +2 -2
- package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/tools/shared.ts +3 -3
- package/sdk/mcp/gdd-state/server.js +2790 -0
- package/sdk/mcp/gdd-state/server.ts +294 -0
- package/{scripts/mcp-servers → sdk/mcp}/gdd-state/tools/add_blocker.ts +3 -3
- package/{scripts/mcp-servers → sdk/mcp}/gdd-state/tools/add_decision.ts +3 -3
- package/{scripts/mcp-servers → sdk/mcp}/gdd-state/tools/add_must_have.ts +3 -3
- package/{scripts/mcp-servers → sdk/mcp}/gdd-state/tools/checkpoint.ts +2 -2
- package/{scripts/mcp-servers → sdk/mcp}/gdd-state/tools/frontmatter_update.ts +2 -2
- package/{scripts/mcp-servers → sdk/mcp}/gdd-state/tools/get.ts +3 -3
- package/{scripts/mcp-servers → sdk/mcp}/gdd-state/tools/index.ts +1 -1
- package/{scripts/mcp-servers → sdk/mcp}/gdd-state/tools/probe_connections.ts +3 -3
- package/{scripts/mcp-servers → sdk/mcp}/gdd-state/tools/resolve_blocker.ts +3 -3
- package/{scripts/mcp-servers → sdk/mcp}/gdd-state/tools/set_status.ts +2 -2
- package/{scripts/mcp-servers → sdk/mcp}/gdd-state/tools/shared.ts +8 -8
- package/{scripts/mcp-servers → sdk/mcp}/gdd-state/tools/transition_stage.ts +4 -4
- package/{scripts/mcp-servers → sdk/mcp}/gdd-state/tools/update_progress.ts +2 -2
- package/sdk/primitives/error-classifier.cjs +232 -0
- package/sdk/primitives/iteration-budget.cjs +205 -0
- package/sdk/primitives/jittered-backoff.cjs +112 -0
- package/sdk/primitives/lockfile.cjs +201 -0
- package/{scripts/lib/gdd-state → sdk/state}/gates.ts +1 -1
- package/sdk/state/index.ts +167 -0
- package/{scripts/lib/gdd-state → sdk/state}/lockfile.ts +1 -1
- package/{scripts/lib/gdd-state → sdk/state}/mutator.ts +1 -1
- package/{scripts/lib/gdd-state → sdk/state}/parser.ts +1 -1
- package/{scripts/lib/gdd-state → sdk/state}/types.ts +4 -4
- package/skills/figma-extract/SKILL.md +64 -0
- package/skills/health/SKILL.md +10 -0
- package/skills/quality-gate/SKILL.md +2 -2
- package/scripts/aggregate-agent-metrics.ts +0 -282
- package/scripts/bootstrap-manifest.txt +0 -3
- package/scripts/bootstrap.sh +0 -80
- package/scripts/build-distribution-bundles.cjs +0 -549
- package/scripts/build-intel.cjs +0 -486
- package/scripts/codegen-schema-types.ts +0 -149
- package/scripts/detect-stale-refs.cjs +0 -107
- package/scripts/e2e/run-headless.ts +0 -514
- package/scripts/extract-changelog-section.cjs +0 -58
- package/scripts/gsd-cleanup-incubator.cjs +0 -367
- package/scripts/injection-patterns.cjs +0 -58
- package/scripts/lint-agentskills-spec.cjs +0 -457
- package/scripts/release-smoke-test.cjs +0 -200
- package/scripts/rollback-release.sh +0 -42
- package/scripts/run-injection-scanner-ci.cjs +0 -83
- package/scripts/tests/test-authority-rejected-kinds.sh +0 -58
- package/scripts/tests/test-authority-watcher-diff.sh +0 -113
- package/scripts/tests/test-motion-provenance.sh +0 -64
- package/scripts/validate-frontmatter.ts +0 -409
- package/scripts/validate-incubator-scope.cjs +0 -133
- package/scripts/validate-schemas.ts +0 -401
- package/scripts/validate-skill-length.cjs +0 -283
- package/scripts/verify-version-sync.cjs +0 -30
- /package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/schemas/gdd_cycle_recap.schema.json +0 -0
- /package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/schemas/gdd_decisions_list.schema.json +0 -0
- /package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/schemas/gdd_events_tail.schema.json +0 -0
- /package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/schemas/gdd_health.schema.json +0 -0
- /package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/schemas/gdd_intel_get.schema.json +0 -0
- /package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/schemas/gdd_learnings_digest.schema.json +0 -0
- /package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/schemas/gdd_phase_current.schema.json +0 -0
- /package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/schemas/gdd_phases_list.schema.json +0 -0
- /package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/schemas/gdd_plans_list.schema.json +0 -0
- /package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/schemas/gdd_reflections_latest.schema.json +0 -0
- /package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/schemas/gdd_status.schema.json +0 -0
- /package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/schemas/gdd_telemetry_query.schema.json +0 -0
- /package/{scripts/mcp-servers → sdk/mcp}/gdd-state/schemas/add_blocker.schema.json +0 -0
- /package/{scripts/mcp-servers → sdk/mcp}/gdd-state/schemas/add_decision.schema.json +0 -0
- /package/{scripts/mcp-servers → sdk/mcp}/gdd-state/schemas/add_must_have.schema.json +0 -0
- /package/{scripts/mcp-servers → sdk/mcp}/gdd-state/schemas/checkpoint.schema.json +0 -0
- /package/{scripts/mcp-servers → sdk/mcp}/gdd-state/schemas/frontmatter_update.schema.json +0 -0
- /package/{scripts/mcp-servers → sdk/mcp}/gdd-state/schemas/get.schema.json +0 -0
- /package/{scripts/mcp-servers → sdk/mcp}/gdd-state/schemas/probe_connections.schema.json +0 -0
- /package/{scripts/mcp-servers → sdk/mcp}/gdd-state/schemas/resolve_blocker.schema.json +0 -0
- /package/{scripts/mcp-servers → sdk/mcp}/gdd-state/schemas/set_status.schema.json +0 -0
- /package/{scripts/mcp-servers → sdk/mcp}/gdd-state/schemas/transition_stage.schema.json +0 -0
- /package/{scripts/mcp-servers → sdk/mcp}/gdd-state/schemas/update_progress.schema.json +0 -0
- /package/{scripts/lib → sdk/primitives}/error-classifier.d.cts +0 -0
- /package/{scripts/lib → sdk/primitives}/iteration-budget.d.cts +0 -0
- /package/{scripts/lib → sdk/primitives}/jittered-backoff.d.cts +0 -0
- /package/{scripts/lib → sdk/primitives}/lockfile.d.cts +0 -0
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
// `resetAt` wins. This matches the D-01 rule in the plan.
|
|
21
21
|
//
|
|
22
22
|
// State-file writes are atomic (temp + rename) and coordinated by
|
|
23
|
-
//
|
|
23
|
+
// sdk/primitives/lockfile.cjs so two child processes hitting `ingestHeaders`
|
|
24
24
|
// concurrently can never corrupt the file. The lock is scoped to the
|
|
25
25
|
// state file, not to the provider directory, so different providers can
|
|
26
26
|
// update in parallel.
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
const fs = require('node:fs');
|
|
31
31
|
const path = require('node:path');
|
|
32
32
|
|
|
33
|
-
const { acquire, renameWithRetry } = require('
|
|
33
|
+
const { acquire, renameWithRetry } = require('../../sdk/primitives/lockfile.cjs');
|
|
34
34
|
|
|
35
35
|
const STATE_DIR_REL = path.join('.design', 'rate-limits');
|
|
36
36
|
const LOCK_MAX_WAIT_MS = 3_000;
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* recipe-loader.cjs — recipes/ scaffold loader (Plan 31-5-03, RECIPE-01 / SC#14).
|
|
3
|
+
*
|
|
4
|
+
* The top-level recipes/ directory ships EMPTY of recipes; it is populated
|
|
5
|
+
* downstream by Phase 32 (skill-trigger recipes), Phase 33.6 (per-provider),
|
|
6
|
+
* Phase 26 (per-runtime/per-model), and Phase 23.5 (bandit-arm shape). This
|
|
7
|
+
* module fixes the loading contract those phases build against so each does not
|
|
8
|
+
* reinvent it. Modelled on Storybloq's src/autonomous/recipes/ loader.ts.
|
|
9
|
+
*
|
|
10
|
+
* Contract:
|
|
11
|
+
* loadRecipe(name, opts?) -> Recipe
|
|
12
|
+
* name : recipe stem; resolves <repoRoot>/recipes/<name>.json
|
|
13
|
+
* opts : { recipesDir, schemaPath, fs } — all injectable for tests
|
|
14
|
+
* returns : the validated, parsed Recipe object
|
|
15
|
+
* throws : Error('recipe not found: <name>') if the file is absent
|
|
16
|
+
* Error('recipe <name> failed schema validation: …') if invalid
|
|
17
|
+
*
|
|
18
|
+
* Cache (SC#14 "caches by SHA"):
|
|
19
|
+
* Keyed by name + ':' + sha256(fileBytes). Each call reads the file once to
|
|
20
|
+
* compute the content hash; on a HIT (unchanged bytes) it returns the cached
|
|
21
|
+
* object WITHOUT re-parsing / re-validating. On a MISS (changed bytes) it
|
|
22
|
+
* parses + validates + stores. Keying by content SHA — not just name — means
|
|
23
|
+
* an edited recipe is correctly re-validated.
|
|
24
|
+
*
|
|
25
|
+
* Empty-dir safety: this module requires cleanly when recipes/ is empty, and a
|
|
26
|
+
* directory listing of an empty (just-.gitkeep) dir does not throw. loadRecipe
|
|
27
|
+
* of a missing name throws the clear not-found error — distinct from "the empty
|
|
28
|
+
* dir is broken" (it is a valid scaffold state).
|
|
29
|
+
*
|
|
30
|
+
* Uses the repo's existing `ajv` dependency (package.json "ajv": "^8.18.0") —
|
|
31
|
+
* no new dependency. The schema is compiled once per schemaPath (singleton).
|
|
32
|
+
*/
|
|
33
|
+
|
|
34
|
+
'use strict';
|
|
35
|
+
|
|
36
|
+
const fs = require('node:fs');
|
|
37
|
+
const path = require('node:path');
|
|
38
|
+
const crypto = require('node:crypto');
|
|
39
|
+
const Ajv = require('ajv');
|
|
40
|
+
|
|
41
|
+
// recipe-loader.cjs lives in scripts/lib/ → two levels up is the repo root.
|
|
42
|
+
const REPO_ROOT = path.resolve(__dirname, '..', '..');
|
|
43
|
+
const DEFAULT_RECIPES_DIR = path.join(REPO_ROOT, 'recipes');
|
|
44
|
+
const DEFAULT_SCHEMA_PATH = path.join(REPO_ROOT, 'reference', 'schemas', 'recipe.schema.json');
|
|
45
|
+
|
|
46
|
+
// Ajv 8 CJS: require('ajv') is (or wraps) the constructor.
|
|
47
|
+
const AjvCtor = Ajv.default || Ajv;
|
|
48
|
+
|
|
49
|
+
// SHA-keyed cache: `name + ':' + sha256(bytes)` → parsed Recipe object.
|
|
50
|
+
const cache = new Map();
|
|
51
|
+
|
|
52
|
+
// Compiled-validator singletons, keyed by resolved schemaPath. The schema is a
|
|
53
|
+
// small trusted local file, so allErrors:true (full error list → clear message)
|
|
54
|
+
// is safe here — unlike the untrusted-HTTP receiver which fails fast.
|
|
55
|
+
const validators = new Map();
|
|
56
|
+
|
|
57
|
+
// Test-observability counter: increments once per parse+validate (i.e. per
|
|
58
|
+
// cache MISS). A cache HIT does not bump it. Tests assert on the delta to prove
|
|
59
|
+
// SHA-keyed hit/miss semantics without depending on raw read counts (a hit must
|
|
60
|
+
// still read the file once to hash it).
|
|
61
|
+
let _validations = 0;
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Get (compiling once) the validator for a schema path.
|
|
65
|
+
* @param {string} schemaPath
|
|
66
|
+
* @param {typeof fs} fsImpl
|
|
67
|
+
* @returns {import('ajv').ValidateFunction}
|
|
68
|
+
*/
|
|
69
|
+
function getValidator(schemaPath, fsImpl) {
|
|
70
|
+
const resolved = path.resolve(schemaPath);
|
|
71
|
+
let validate = validators.get(resolved);
|
|
72
|
+
if (validate) return validate;
|
|
73
|
+
|
|
74
|
+
const schema = JSON.parse(fsImpl.readFileSync(resolved, 'utf8'));
|
|
75
|
+
const ajv = new AjvCtor({ allErrors: true, strict: false });
|
|
76
|
+
validate = ajv.compile(schema);
|
|
77
|
+
validators.set(resolved, validate);
|
|
78
|
+
return validate;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Load + validate a recipe by name.
|
|
83
|
+
* @param {string} name - recipe stem; resolves <recipesDir>/<name>.json
|
|
84
|
+
* @param {{ recipesDir?: string, schemaPath?: string, fs?: typeof fs }} [opts]
|
|
85
|
+
* @returns {Record<string, unknown>} the validated, parsed Recipe object
|
|
86
|
+
*/
|
|
87
|
+
function loadRecipe(name, opts = {}) {
|
|
88
|
+
if (typeof name !== 'string' || name.length === 0) {
|
|
89
|
+
throw new Error('recipe name must be a non-empty string');
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const fsImpl = opts.fs || fs;
|
|
93
|
+
const dir = opts.recipesDir || DEFAULT_RECIPES_DIR;
|
|
94
|
+
const schemaPath = opts.schemaPath || DEFAULT_SCHEMA_PATH;
|
|
95
|
+
const file = path.join(dir, name + '.json');
|
|
96
|
+
|
|
97
|
+
if (!fsImpl.existsSync(file)) {
|
|
98
|
+
throw new Error('recipe not found: ' + name);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Read once per call to compute the content hash (the cache key).
|
|
102
|
+
const bytes = fsImpl.readFileSync(file);
|
|
103
|
+
const sha = crypto.createHash('sha256').update(bytes).digest('hex');
|
|
104
|
+
const key = name + ':' + sha;
|
|
105
|
+
|
|
106
|
+
// Cache HIT: unchanged bytes → return cached object, skip parse + validate.
|
|
107
|
+
if (cache.has(key)) {
|
|
108
|
+
return cache.get(key);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Cache MISS: parse + validate + store.
|
|
112
|
+
let parsed;
|
|
113
|
+
try {
|
|
114
|
+
parsed = JSON.parse(bytes.toString('utf8'));
|
|
115
|
+
} catch (err) {
|
|
116
|
+
throw new Error('recipe ' + name + ' is not valid JSON: ' + err.message);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const validate = getValidator(schemaPath, fsImpl);
|
|
120
|
+
_validations += 1;
|
|
121
|
+
const ok = validate(parsed) === true;
|
|
122
|
+
if (!ok) {
|
|
123
|
+
throw new Error(
|
|
124
|
+
'recipe ' + name + ' failed schema validation: ' + JSON.stringify(validate.errors || []),
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
cache.set(key, parsed);
|
|
129
|
+
return parsed;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/** Test hook: clear the SHA cache (does not drop compiled validators). */
|
|
133
|
+
function _clearCache() {
|
|
134
|
+
cache.clear();
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/** Test hook: introspect parse+validate counts (one per cache MISS). */
|
|
138
|
+
function _stats() {
|
|
139
|
+
return { validations: _validations, cacheSize: cache.size };
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
module.exports = { loadRecipe, _clearCache, _stats };
|
|
@@ -38,7 +38,7 @@ import {
|
|
|
38
38
|
OperationFailedError,
|
|
39
39
|
StateConflictError,
|
|
40
40
|
type GDDError,
|
|
41
|
-
} from '
|
|
41
|
+
} from '../../../sdk/errors/index.ts';
|
|
42
42
|
|
|
43
43
|
/**
|
|
44
44
|
* Build an absolute path to a repo-root-relative file. We can't use
|
|
@@ -68,7 +68,7 @@ const REPO_ROOT = findRepoRoot();
|
|
|
68
68
|
*/
|
|
69
69
|
const nodeRequire = createRequire(join(REPO_ROOT, 'package.json'));
|
|
70
70
|
const transportClassifier = nodeRequire(
|
|
71
|
-
resolve(REPO_ROOT, '
|
|
71
|
+
resolve(REPO_ROOT, 'sdk/primitives/error-classifier.cjs'),
|
|
72
72
|
) as {
|
|
73
73
|
classify: (err: unknown) => {
|
|
74
74
|
reason: string;
|
|
@@ -375,7 +375,7 @@ export function mapSdkError(err: unknown): MappedSdkError {
|
|
|
375
375
|
}
|
|
376
376
|
|
|
377
377
|
// 9. Transport-layer classification (ECONNRESET, ETIMEDOUT, etc.).
|
|
378
|
-
// Delegate to
|
|
378
|
+
// Delegate to sdk/primitives/error-classifier.cjs which knows the errno
|
|
379
379
|
// vocabulary. Only trust its `retryable` flag for transient network
|
|
380
380
|
// classes — other classes were already handled above.
|
|
381
381
|
const classified = transportClassifier.classify(err);
|
|
@@ -26,8 +26,8 @@
|
|
|
26
26
|
// (always; payload status mirrors SessionResult.status). Optional
|
|
27
27
|
// `session.budget_exceeded` emitted when the budget trips.
|
|
28
28
|
|
|
29
|
-
import { appendEvent } from '
|
|
30
|
-
import type { BaseEvent } from '
|
|
29
|
+
import { appendEvent } from '../../../sdk/event-stream/index.ts';
|
|
30
|
+
import type { BaseEvent } from '../../../sdk/event-stream/index.ts';
|
|
31
31
|
import { sanitize as defaultSanitize } from '../prompt-sanitizer/index.ts';
|
|
32
32
|
|
|
33
33
|
import { mapSdkError } from './errors.ts';
|
|
@@ -64,7 +64,7 @@ function _findRepoRoot(): string {
|
|
|
64
64
|
const _REPO_ROOT = _findRepoRoot();
|
|
65
65
|
const _nodeRequire = createRequire(_join(_REPO_ROOT, 'package.json'));
|
|
66
66
|
const jitteredBackoff = _nodeRequire(
|
|
67
|
-
_resolve(_REPO_ROOT, '
|
|
67
|
+
_resolve(_REPO_ROOT, 'sdk/primitives/jittered-backoff.cjs'),
|
|
68
68
|
) as {
|
|
69
69
|
delayMs: (attempt: number, opts?: { baseMs?: number; maxMs?: number; factor?: number; jitter?: number }) => number;
|
|
70
70
|
};
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// transcript writer for Phase 21 headless Agent SDK sessions
|
|
3
3
|
// (Plan 21-01 Task 4).
|
|
4
4
|
//
|
|
5
|
-
// Design mirrors
|
|
5
|
+
// Design mirrors sdk/event-stream/writer.ts but is scoped to one
|
|
6
6
|
// session per file rather than the global telemetry stream. Each session
|
|
7
7
|
// owns a dedicated `.design/sessions/<ISO>-<stage>.jsonl` file; the
|
|
8
8
|
// filename is stable for the full run and survives retries (retries
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
// * Plan 21-01 `session-runner` — computes `allowedTools` for each session.
|
|
22
22
|
// * Plan 21-05 `pipeline-runner` — picks the correct scope per stage.
|
|
23
23
|
|
|
24
|
-
import { ValidationError } from '
|
|
24
|
+
import { ValidationError } from '../../../sdk/errors/index.ts';
|
|
25
25
|
import type { Scope, ScopeInput, ScopeViolation, Stage } from './types.ts';
|
|
26
26
|
import {
|
|
27
27
|
NATIVE_TOOLS,
|
|
@@ -1,317 +1,35 @@
|
|
|
1
|
-
|
|
2
|
-
// scripts/mcp-servers/gdd-mcp/server.ts
|
|
1
|
+
// scripts/mcp-servers/gdd-mcp/server.ts — GDD-DEPRECATION-SHIM (Plan 31-5-06, SDK-05, D-02).
|
|
3
2
|
//
|
|
4
|
-
// MCP
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
//
|
|
3
|
+
// Thin deprecation shim. The real MCP `gdd-mcp` server moved to
|
|
4
|
+
// sdk/mcp/gdd-mcp/server.ts in Plan 31-5-05 (SDK consolidation, D-08). This
|
|
5
|
+
// file is re-created at the OLD path so undocumented EXTERNAL importers /
|
|
6
|
+
// invokers (anyone who reached into node_modules/@hegemonart/get-design-done/
|
|
7
|
+
// scripts/mcp-servers/gdd-mcp/server.ts directly) keep working for one minor
|
|
8
|
+
// grace window.
|
|
8
9
|
//
|
|
9
|
-
//
|
|
10
|
-
//
|
|
11
|
-
//
|
|
12
|
-
//
|
|
13
|
-
//
|
|
14
|
-
//
|
|
15
|
-
// tools; Plan 27.7-02 populates `TOOL_MODULES` with 12 entries.
|
|
16
|
-
// 3. Register `tools/call` — dispatches by name to the matching
|
|
17
|
-
// handler. Each handler returns a typed ToolResponse; the server
|
|
18
|
-
// wraps it into the MCP CallToolResult shape. Unknown tool names
|
|
19
|
-
// return `isError: true` with a structured payload.
|
|
20
|
-
// 4. Attach StdioServerTransport; await connect. NO port allocation
|
|
21
|
-
// (D-05 stdio-only).
|
|
22
|
-
// 5. On SIGINT / SIGTERM: close the transport, exit 0. Re-entrant
|
|
23
|
-
// shutdown is guarded with a module-level `SHUTTING_DOWN` flag.
|
|
10
|
+
// REMOVED IN v1.33.0 (D-02). Grace window: 1.31.5 ships with shims →
|
|
11
|
+
// 1.32.0 still has them → 1.33.0 removes them. The canonical invocation is
|
|
12
|
+
// now the `bin/gdd-mcp` trampoline (Plan 31-5-05); internal callers already
|
|
13
|
+
// use the sdk/ path. This shim is external-only; 31-5-10's
|
|
14
|
+
// no-stale-internal-refs guard excludes files carrying the
|
|
15
|
+
// GDD-DEPRECATION-SHIM marker above.
|
|
24
16
|
//
|
|
25
|
-
//
|
|
26
|
-
//
|
|
27
|
-
//
|
|
28
|
-
//
|
|
29
|
-
//
|
|
30
|
-
//
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
import {
|
|
41
|
-
CallToolRequestSchema,
|
|
42
|
-
ListToolsRequestSchema,
|
|
43
|
-
} from '@modelcontextprotocol/sdk/types.js';
|
|
44
|
-
|
|
45
|
-
import { toToolError } from '../../lib/gdd-errors/classification.ts';
|
|
46
|
-
import { TOOL_MODULES, type ToolModule } from './tools/index.ts';
|
|
47
|
-
|
|
48
|
-
/** Server metadata advertised on initialize. */
|
|
49
|
-
export const SERVER_NAME = 'gdd-mcp';
|
|
50
|
-
export const SERVER_VERSION = '1.27.7';
|
|
51
|
-
|
|
52
|
-
/**
|
|
53
|
-
* Resolve this module's directory. We deliberately avoid `import.meta.url`
|
|
54
|
-
* (not permitted by our tsconfig's Node16+CommonJS-compatible module
|
|
55
|
-
* resolution) and `__dirname` (not portable under strip-types ESM).
|
|
56
|
-
*
|
|
57
|
-
* Strategy: when this module is invoked as a script, `process.argv[1]`
|
|
58
|
-
* points at this file; resolve its dirname. When it is imported for
|
|
59
|
-
* tests, we fall back to walking from `process.cwd()` — tests run
|
|
60
|
-
* from the repo root by convention, so `scripts/mcp-servers/gdd-mcp`
|
|
61
|
-
* resolves reliably. Both branches are canonicalized against the
|
|
62
|
-
* on-disk tools directory.
|
|
63
|
-
*/
|
|
64
|
-
function here(): string {
|
|
65
|
-
const expectedRel = join('scripts', 'mcp-servers', 'gdd-mcp');
|
|
66
|
-
// Script invocation: process.argv[1] === .../server.ts (or a shim).
|
|
67
|
-
const entry = process.argv[1];
|
|
68
|
-
if (typeof entry === 'string' && entry.length > 0) {
|
|
69
|
-
const entryDir = dirname(resolve(entry));
|
|
70
|
-
if (existsSync(join(entryDir, 'tools', 'index.ts'))) {
|
|
71
|
-
return entryDir;
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
// Library-import path (tests): walk cwd forward.
|
|
75
|
-
const candidate = resolve(process.cwd(), expectedRel);
|
|
76
|
-
if (existsSync(join(candidate, 'tools', 'index.ts'))) {
|
|
77
|
-
return candidate;
|
|
78
|
-
}
|
|
79
|
-
// Last-resort: return the expected path even if it does not exist —
|
|
80
|
-
// the subsequent readFileSync() call will produce a clear error.
|
|
81
|
-
return candidate;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
/** Eager cache of input schemas keyed by tool name. We load them once at
|
|
85
|
-
* startup so a tool-call handler never hits the filesystem in the hot
|
|
86
|
-
* path; subsequent schema edits (JSON file on disk) require a server
|
|
87
|
-
* restart, which matches every other part of the pipeline.
|
|
88
|
-
*
|
|
89
|
-
* Scaffold ships with 0 tools — loadTools() returns []. Plan 27.7-02
|
|
90
|
-
* adds 12 tool modules, each with its own `schemaPath` pointing into
|
|
91
|
-
* `scripts/mcp-servers/gdd-mcp/schemas/`. */
|
|
92
|
-
interface LoadedTool extends ToolModule {
|
|
93
|
-
inputSchema: Record<string, unknown>;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
function loadTools(): LoadedTool[] {
|
|
97
|
-
const baseDir = here();
|
|
98
|
-
return TOOL_MODULES.map((m) => {
|
|
99
|
-
const absPath = join(baseDir, 'tools', m.schemaPath);
|
|
100
|
-
const raw = readFileSync(absPath, 'utf8');
|
|
101
|
-
const parsed = JSON.parse(raw) as {
|
|
102
|
-
properties?: {
|
|
103
|
-
input?: { type?: string; properties?: Record<string, unknown> };
|
|
104
|
-
};
|
|
105
|
-
};
|
|
106
|
-
// The per-tool schema files are Draft-07 wrappers shaped as:
|
|
107
|
-
// { type: "object", properties: { input: {...}, output: {...} } }
|
|
108
|
-
// MCP's tools/list advertises only the INPUT half. We project
|
|
109
|
-
// `properties.input` here; when the schema is malformed we fall
|
|
110
|
-
// back to an open object so the tool is still listable.
|
|
111
|
-
const rawInput = parsed.properties?.input;
|
|
112
|
-
const inputSchema: Record<string, unknown> =
|
|
113
|
-
rawInput !== undefined && typeof rawInput === 'object'
|
|
114
|
-
? (rawInput as Record<string, unknown>)
|
|
115
|
-
: { type: 'object' };
|
|
116
|
-
if (!('type' in inputSchema)) inputSchema['type'] = 'object';
|
|
117
|
-
return { ...m, inputSchema };
|
|
118
|
-
});
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
/**
|
|
122
|
-
* Tool descriptions — short, scannable, lifted from the plan. Skill
|
|
123
|
-
* prose uses these verbatim when suggesting a tool to the model.
|
|
124
|
-
*
|
|
125
|
-
* Plan 27.7-02 populates 12 entries — one per tool name in the canonical
|
|
126
|
-
* 12-tool list.
|
|
127
|
-
*/
|
|
128
|
-
export const TOOL_DESCRIPTIONS: Record<string, string> = {
|
|
129
|
-
gdd_status:
|
|
130
|
-
'gdd_status: current cycle phase, branch, last-3 decisions, last-3 completed plans, blocker count.',
|
|
131
|
-
gdd_phase_current:
|
|
132
|
-
'gdd_phase_current: STATE.md <position> block (phase, stage, task_progress, status).',
|
|
133
|
-
gdd_phases_list:
|
|
134
|
-
'gdd_phases_list: parsed ROADMAP.md overview (phase number, name, target version, shipped/planned).',
|
|
135
|
-
gdd_plans_list:
|
|
136
|
-
'gdd_plans_list: plans tracked in STATE.md must_haves (optionally filtered by input.phase).',
|
|
137
|
-
gdd_decisions_list:
|
|
138
|
-
'gdd_decisions_list: decisions from STATE.md (optionally filtered by input.status).',
|
|
139
|
-
gdd_intel_get:
|
|
140
|
-
'gdd_intel_get: read a slice from .design/intel/ (input.slice_id required); directory_not_found if absent.',
|
|
141
|
-
gdd_telemetry_query:
|
|
142
|
-
'gdd_telemetry_query: typed reader over .design/telemetry/*.jsonl with type/since/limit filters.',
|
|
143
|
-
gdd_cycle_recap:
|
|
144
|
-
'gdd_cycle_recap: diff current STATE against latest .design/snapshots/ snapshot; directory_not_found if absent.',
|
|
145
|
-
gdd_reflections_latest:
|
|
146
|
-
'gdd_reflections_latest: newest .design/reflections/ file (excerpt <= 4 KB); directory_not_found if absent.',
|
|
147
|
-
gdd_learnings_digest:
|
|
148
|
-
'gdd_learnings_digest: aggregate last N reflections into a compact digest (<= 5 KB).',
|
|
149
|
-
gdd_events_tail:
|
|
150
|
-
'gdd_events_tail: last-N events from .design/telemetry/events.jsonl with optional type filter.',
|
|
151
|
-
gdd_health:
|
|
152
|
-
'gdd_health: read-only mirror of gdd-health SKILL — 4 checks (CLAUDE.md, .planning/, .design/, package.json).',
|
|
153
|
-
};
|
|
154
|
-
|
|
155
|
-
/** Human-readable annotation hints (MCP clients use these to style the
|
|
156
|
-
* tool in UI). `readOnlyHint: true` — tells clients this tool does NOT
|
|
157
|
-
* modify disk. v1 is read-only (D-04) so every entry is `true`. */
|
|
158
|
-
export const TOOL_READONLY: Record<string, boolean> = {
|
|
159
|
-
gdd_status: true,
|
|
160
|
-
gdd_phase_current: true,
|
|
161
|
-
gdd_phases_list: true,
|
|
162
|
-
gdd_plans_list: true,
|
|
163
|
-
gdd_decisions_list: true,
|
|
164
|
-
gdd_intel_get: true,
|
|
165
|
-
gdd_telemetry_query: true,
|
|
166
|
-
gdd_cycle_recap: true,
|
|
167
|
-
gdd_reflections_latest: true,
|
|
168
|
-
gdd_learnings_digest: true,
|
|
169
|
-
gdd_events_tail: true,
|
|
170
|
-
gdd_health: true,
|
|
171
|
-
};
|
|
172
|
-
|
|
173
|
-
/**
|
|
174
|
-
* Build the MCP server. The tools list and call handlers are the only
|
|
175
|
-
* two request handlers we register; everything else (initialize, ping,
|
|
176
|
-
* cancellation, shutdown) is handled internally by the Protocol class.
|
|
177
|
-
*/
|
|
178
|
-
export function buildServer(): Server {
|
|
179
|
-
const tools = loadTools();
|
|
180
|
-
const byName: Map<string, LoadedTool> = new Map();
|
|
181
|
-
for (const t of tools) byName.set(t.name, t);
|
|
182
|
-
|
|
183
|
-
const server = new Server(
|
|
184
|
-
{ name: SERVER_NAME, version: SERVER_VERSION },
|
|
185
|
-
{
|
|
186
|
-
capabilities: { tools: {} },
|
|
187
|
-
},
|
|
17
|
+
// Re-exporting the sdk/ server keeps the library surface (buildServer,
|
|
18
|
+
// runStdio, SERVER_NAME, SERVER_VERSION, TOOL_DESCRIPTIONS, TOOL_READONLY)
|
|
19
|
+
// reachable via the old path. The sdk/ server's own isMain() entry guard
|
|
20
|
+
// keys off process.argv[1] ending with its own sdk/ path, so a re-export
|
|
21
|
+
// does NOT auto-start the server — direct execution should go through the
|
|
22
|
+
// bin trampoline. Runs under --experimental-strip-types.
|
|
23
|
+
|
|
24
|
+
import { emitWarning } from 'node:process';
|
|
25
|
+
|
|
26
|
+
let warned = false;
|
|
27
|
+
if (!warned) {
|
|
28
|
+
warned = true;
|
|
29
|
+
emitWarning(
|
|
30
|
+
'scripts/mcp-servers/gdd-mcp/server.ts is deprecated; use the bin/gdd-mcp trampoline or import sdk/mcp/gdd-mcp instead. Removed in v1.33.0.',
|
|
31
|
+
'DeprecationWarning',
|
|
188
32
|
);
|
|
189
|
-
|
|
190
|
-
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
191
|
-
return {
|
|
192
|
-
tools: tools.map((t) => {
|
|
193
|
-
const description = TOOL_DESCRIPTIONS[t.name] ?? t.name;
|
|
194
|
-
const readOnly = TOOL_READONLY[t.name] ?? true;
|
|
195
|
-
return {
|
|
196
|
-
name: t.name,
|
|
197
|
-
description,
|
|
198
|
-
inputSchema: t.inputSchema as {
|
|
199
|
-
type: 'object';
|
|
200
|
-
properties?: Record<string, unknown>;
|
|
201
|
-
required?: string[];
|
|
202
|
-
},
|
|
203
|
-
annotations: {
|
|
204
|
-
readOnlyHint: readOnly,
|
|
205
|
-
destructiveHint: !readOnly,
|
|
206
|
-
idempotentHint: false,
|
|
207
|
-
},
|
|
208
|
-
};
|
|
209
|
-
}),
|
|
210
|
-
};
|
|
211
|
-
});
|
|
212
|
-
|
|
213
|
-
server.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
214
|
-
const { name: toolName, arguments: args } = req.params;
|
|
215
|
-
const tool = byName.get(toolName);
|
|
216
|
-
if (tool === undefined) {
|
|
217
|
-
// Unknown tool — return as CallToolResult isError=true so the
|
|
218
|
-
// client gets a structured error rather than a JSON-RPC error.
|
|
219
|
-
const payload = toToolError(
|
|
220
|
-
new Error(`unknown tool: ${toolName}`),
|
|
221
|
-
);
|
|
222
|
-
return {
|
|
223
|
-
isError: true,
|
|
224
|
-
content: [
|
|
225
|
-
{
|
|
226
|
-
type: 'text' as const,
|
|
227
|
-
text: JSON.stringify({ success: false, error: payload.error }),
|
|
228
|
-
},
|
|
229
|
-
],
|
|
230
|
-
structuredContent: { success: false, error: payload.error },
|
|
231
|
-
};
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
let response;
|
|
235
|
-
try {
|
|
236
|
-
response = await tool.handle(args ?? {});
|
|
237
|
-
} catch (err) {
|
|
238
|
-
// Defensive catch — handlers shouldn't throw, but if one does
|
|
239
|
-
// we translate rather than crashing the server.
|
|
240
|
-
const payload = toToolError(err);
|
|
241
|
-
response = { success: false as const, error: payload.error };
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
const text = JSON.stringify(response);
|
|
245
|
-
if (response.success === true) {
|
|
246
|
-
return {
|
|
247
|
-
content: [{ type: 'text' as const, text }],
|
|
248
|
-
structuredContent: response as unknown as Record<string, unknown>,
|
|
249
|
-
};
|
|
250
|
-
}
|
|
251
|
-
return {
|
|
252
|
-
isError: true,
|
|
253
|
-
content: [{ type: 'text' as const, text }],
|
|
254
|
-
structuredContent: response as unknown as Record<string, unknown>,
|
|
255
|
-
};
|
|
256
|
-
});
|
|
257
|
-
|
|
258
|
-
return server;
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
/**
|
|
262
|
-
* Run the server against stdio and block until the transport closes.
|
|
263
|
-
* Called from CLI when this file is invoked as a script.
|
|
264
|
-
*/
|
|
265
|
-
export async function runStdio(): Promise<void> {
|
|
266
|
-
const server = buildServer();
|
|
267
|
-
const transport = new StdioServerTransport();
|
|
268
|
-
|
|
269
|
-
const shutdown = async (signal: string): Promise<void> => {
|
|
270
|
-
// Re-entrant: signal handlers can fire more than once on flaky
|
|
271
|
-
// shells. Guard with a module-level flag.
|
|
272
|
-
if (SHUTTING_DOWN) return;
|
|
273
|
-
SHUTTING_DOWN = true;
|
|
274
|
-
try {
|
|
275
|
-
await server.close();
|
|
276
|
-
} catch {
|
|
277
|
-
// best-effort; we're exiting anyway.
|
|
278
|
-
}
|
|
279
|
-
// SIGTERM / SIGINT convention: exit(0) — orderly shutdown.
|
|
280
|
-
process.exit(signal === 'SIGTERM' ? 0 : 0);
|
|
281
|
-
};
|
|
282
|
-
|
|
283
|
-
process.on('SIGINT', () => {
|
|
284
|
-
void shutdown('SIGINT');
|
|
285
|
-
});
|
|
286
|
-
process.on('SIGTERM', () => {
|
|
287
|
-
void shutdown('SIGTERM');
|
|
288
|
-
});
|
|
289
|
-
|
|
290
|
-
await server.connect(transport);
|
|
291
33
|
}
|
|
292
34
|
|
|
293
|
-
|
|
294
|
-
let SHUTTING_DOWN = false;
|
|
295
|
-
|
|
296
|
-
/**
|
|
297
|
-
* Are we being invoked as a script? We compare the argv[1] file path's
|
|
298
|
-
* basename to `server.ts` — test imports never match this because
|
|
299
|
-
* `node --test tests/*.ts` sets argv[1] to the test runner entry, not
|
|
300
|
-
* our file. A direct `node scripts/mcp-servers/gdd-mcp/server.ts`
|
|
301
|
-
* invocation DOES match. The Windows-safe path normalization uses
|
|
302
|
-
* `.replace(/\\/g, '/')` before the endsWith check.
|
|
303
|
-
*/
|
|
304
|
-
function isMain(): boolean {
|
|
305
|
-
const entry = process.argv[1];
|
|
306
|
-
if (typeof entry !== 'string' || entry.length === 0) return false;
|
|
307
|
-
return entry.replace(/\\/g, '/').endsWith('scripts/mcp-servers/gdd-mcp/server.ts');
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
if (isMain()) {
|
|
311
|
-
runStdio().catch((err) => {
|
|
312
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
313
|
-
// eslint-disable-next-line no-console
|
|
314
|
-
console.error(`[gdd-mcp] fatal: ${msg}`);
|
|
315
|
-
process.exit(1);
|
|
316
|
-
});
|
|
317
|
-
}
|
|
35
|
+
export * from '../../../sdk/mcp/gdd-mcp/server.ts';
|