@opengsd/gsd-core 1.5.0-rc.2 → 1.5.0-rc.3

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.
Files changed (69) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/agents/gsd-mempalace-curator.md +47 -0
  3. package/bin/install.js +25 -7
  4. package/commands/gsd/mempalace-capture.md +71 -0
  5. package/commands/gsd/mempalace-recall.md +102 -0
  6. package/commands/gsd/ns-context.md +4 -2
  7. package/gemini-extension.json +1 -1
  8. package/gsd-core/bin/gsd-tools.cjs +110 -92
  9. package/gsd-core/bin/lib/capability-activation.cjs +86 -0
  10. package/gsd-core/bin/lib/capability-registry.cjs +1468 -11
  11. package/gsd-core/bin/lib/capability-state.cjs +31 -18
  12. package/gsd-core/bin/lib/check-command-router.cjs +328 -1
  13. package/gsd-core/bin/lib/clusters.cjs +2 -0
  14. package/gsd-core/bin/lib/command-roster.cjs +19 -0
  15. package/gsd-core/bin/lib/commands.cjs +33 -10
  16. package/gsd-core/bin/lib/config-loader.cjs +7 -8
  17. package/gsd-core/bin/lib/config-schema.cjs +32 -3
  18. package/gsd-core/bin/lib/config.cjs +1 -2
  19. package/gsd-core/bin/lib/core.cjs +5 -2
  20. package/gsd-core/bin/lib/init.cjs +8 -5
  21. package/gsd-core/bin/lib/install-profiles.cjs +2 -1
  22. package/gsd-core/bin/lib/loop-resolver.cjs +157 -16
  23. package/gsd-core/bin/lib/model-resolver.cjs +47 -5
  24. package/gsd-core/bin/lib/phase.cjs +71 -11
  25. package/gsd-core/bin/lib/profile-output.cjs +45 -4
  26. package/gsd-core/bin/lib/profile-pipeline-command-router.cjs +138 -0
  27. package/gsd-core/bin/lib/roadmap.cjs +97 -7
  28. package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +1700 -0
  29. package/gsd-core/bin/lib/runtime-artifact-layout.cjs +20 -28
  30. package/gsd-core/bin/lib/runtime-config-adapter-registry.cjs +27 -19
  31. package/gsd-core/bin/lib/runtime-homes.cjs +26 -20
  32. package/gsd-core/bin/lib/state-command-router.cjs +15 -3
  33. package/gsd-core/bin/lib/state-document.cjs +46 -1
  34. package/gsd-core/bin/lib/state.cjs +221 -35
  35. package/gsd-core/bin/lib/verify.cjs +19 -4
  36. package/gsd-core/bin/shared/config-defaults.manifest.json +1 -2
  37. package/gsd-core/bin/shared/config-schema.manifest.json +0 -18
  38. package/gsd-core/bin/shared/model-catalog.json +1 -0
  39. package/gsd-core/references/loop-hook-dispatch.md +61 -0
  40. package/gsd-core/templates/config.json +1 -1
  41. package/gsd-core/workflows/audit-milestone.md +5 -3
  42. package/gsd-core/workflows/autonomous.md +10 -5
  43. package/gsd-core/workflows/code-review-fix.md +9 -7
  44. package/gsd-core/workflows/code-review.md +8 -6
  45. package/gsd-core/workflows/discuss-phase.md +14 -0
  46. package/gsd-core/workflows/execute-phase.md +82 -143
  47. package/gsd-core/workflows/help/modes/full.md +3 -1
  48. package/gsd-core/workflows/new-project.md +3 -3
  49. package/gsd-core/workflows/plan-phase.md +89 -221
  50. package/gsd-core/workflows/profile-user.md +3 -1
  51. package/gsd-core/workflows/progress.md +58 -1
  52. package/gsd-core/workflows/quick.md +9 -6
  53. package/gsd-core/workflows/review.md +19 -2
  54. package/gsd-core/workflows/secure-phase.md +4 -2
  55. package/gsd-core/workflows/settings-advanced.md +2 -0
  56. package/gsd-core/workflows/ship.md +57 -0
  57. package/gsd-core/workflows/validate-phase.md +4 -2
  58. package/gsd-core/workflows/verify-work.md +27 -11
  59. package/package.json +3 -2
  60. package/scripts/affected-tests-lib.cjs +16 -4
  61. package/scripts/gen-capability-registry.cjs +359 -48
  62. package/scripts/gen-inventory-manifest.cjs +1 -4
  63. package/scripts/gen-loop-host-contract.cjs +55 -0
  64. package/scripts/issue-version-gate.cjs +140 -0
  65. package/scripts/lint-allow-test-rule-refs.allowlist.json +326 -0
  66. package/scripts/lint-allow-test-rule-refs.cjs +162 -0
  67. package/scripts/lint-test-file-count.allowlist.json +8 -0
  68. package/scripts/release-tarball-smoke.cjs +7 -1
  69. package/scripts/run-tests.cjs +74 -14
@@ -6,9 +6,9 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
6
6
  * Runtime artifact layout module — resolves the artifact directory shapes
7
7
  * (commands, agents, skills) for each supported runtime.
8
8
  *
9
- * grok is intentionally absent: it is in runtime-homes.cjs but not wired
10
- * here. The TypeError on unknown runtime is the loud-fail signal that a
11
- * runtime was added to the homes list without a layout entry.
9
+ * grok is intentionally absent: it is in runtime-homes.cjs but has no runtime
10
+ * capability descriptor. The TypeError on unknown runtime is the loud-fail
11
+ * signal that a runtime was added without an artifact layout descriptor.
12
12
  *
13
13
  * ADR-457 build-at-publish: the hand-written bin/lib/runtime-artifact-layout.cjs
14
14
  * collapsed to a TypeScript source of truth. Behaviour is preserved byte-for-behaviour
@@ -20,6 +20,9 @@ const node_os_1 = __importDefault(require("node:os"));
20
20
  // eslint-disable-next-line @typescript-eslint/no-require-imports
21
21
  const installProfiles = require("./install-profiles.cjs");
22
22
  const { stageSkillsForProfile, stageAgentsForProfile, stageSkillsForRuntimeAsSkills, stageCommandsForRuntimeFlat, } = installProfiles;
23
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
24
+ const runtimeArtifactConversion = require("./runtime-artifact-conversion.cjs");
25
+ const conversionExports = runtimeArtifactConversion;
23
26
  // In .cts (CommonJS output) files, `require` is available as a global.
24
27
  const _require = require;
25
28
  /**
@@ -125,14 +128,6 @@ function findAgentsSourceRoot(runtimeConfigDir) {
125
128
  throw new Error(`findAgentsSourceRoot: could not locate agents/ from ${__dirname}`);
126
129
  }
127
130
  // ---------------------------------------------------------------------------
128
- // Allowlisted runtimes
129
- // ---------------------------------------------------------------------------
130
- const ALLOWED_RUNTIMES = new Set([
131
- 'claude', 'cursor', 'gemini', 'codex', 'copilot', 'antigravity',
132
- 'windsurf', 'augment', 'trae', 'qwen', 'hermes', 'codebuddy',
133
- 'cline', 'kimi', 'opencode', 'kilo',
134
- ]);
135
- // ---------------------------------------------------------------------------
136
131
  // Layout table builders
137
132
  // ---------------------------------------------------------------------------
138
133
  function commandsKind(destSubpath, prefix, configDir) {
@@ -157,8 +152,7 @@ function kimiAgentsKind(destSubpath, prefix, configDir) {
157
152
  destSubpath,
158
153
  prefix,
159
154
  stage: (resolved) => {
160
- const installExports = getInstallExports();
161
- const buildKimiAgentArtifacts = installExports['buildKimiAgentArtifacts'];
155
+ const buildKimiAgentArtifacts = conversionExports['buildKimiAgentArtifacts'];
162
156
  const stagedAgents = stageAgentsForProfile(findAgentsSourceRoot(configDir), resolved);
163
157
  const subagents = [];
164
158
  if (node_fs_1.default.existsSync(stagedAgents)) {
@@ -193,7 +187,7 @@ function kimiAgentsKind(destSubpath, prefix, configDir) {
193
187
  *
194
188
  * @param destSubpath
195
189
  * @param prefix
196
- * @param converterName name of converter function in bin/install.js exports
190
+ * @param converterName name of converter function in Runtime Artifact Conversion exports
197
191
  * @param runtime canonical runtime ID (gates Hermes/Qwen branding in converter)
198
192
  * @param configDir runtime config dir (for .gsd-source marker resolution)
199
193
  * @param nested if true, nest concrete skills under their ns-* routers (#69)
@@ -208,15 +202,16 @@ function skillsKind(destSubpath, prefix, converterName, runtime, configDir, nest
208
202
  destSubpath,
209
203
  prefix,
210
204
  stage: (resolved) => {
211
- const installExports = getInstallExports();
212
- const realConverter = installExports[converterName];
205
+ const realConverter = conversionExports[converterName];
213
206
  // Compute cmdNames once per stage call for performance (#3583).
214
207
  // Extra trailing args are ignored by converters that don't need them. The
215
208
  // isGlobal flag is the 5th positional (NOT the 3rd): the 3rd positional is
216
209
  // `runtime` for the claude/kimi/cline converters, so the scope-aware
217
210
  // converters (antigravity, copilot) read isGlobal from position 5 to avoid
218
211
  // colliding with `runtime` and always taking the global branch.
219
- const cmdNames = installExports.readGsdCommandNames();
212
+ const cmdNames = conversionExports.readGsdCommandNames
213
+ ? conversionExports.readGsdCommandNames()
214
+ : [];
220
215
  const isGlobal = scope === 'global';
221
216
  const wrappedConverter = (content, skillName) => realConverter(content, skillName, runtime, cmdNames, isGlobal);
222
217
  return stageSkillsForRuntimeAsSkills(findInstallSourceRoot(configDir), resolved, wrappedConverter, prefix, nested);
@@ -228,7 +223,7 @@ function skillsKind(destSubpath, prefix, converterName, runtime, configDir, nest
228
223
  * commands directory with per-file conversion (e.g. Cursor 1.6 slash commands).
229
224
  *
230
225
  * Unlike `commandsKind` (which passes raw source files through), this kind
231
- * applies `converterName` from bin/install.js exports to each file during
226
+ * applies `converterName` from Runtime Artifact Conversion exports to each file during
232
227
  * staging, writing flat `${prefix}${stem}.md` files to the staged directory.
233
228
  *
234
229
  * The staged files are then written by `_copyStaged` (commands branch) which
@@ -236,7 +231,7 @@ function skillsKind(destSubpath, prefix, converterName, runtime, configDir, nest
236
231
  *
237
232
  * @param destSubpath destination subpath within configDir (e.g. 'commands')
238
233
  * @param prefix filename prefix, e.g. 'gsd-'
239
- * @param converterName name of converter function in bin/install.js exports
234
+ * @param converterName name of converter function in Runtime Artifact Conversion exports
240
235
  * @param configDir runtime config dir (for .gsd-source marker resolution)
241
236
  */
242
237
  function convertedCommandsKind(destSubpath, prefix, converterName, configDir) {
@@ -245,13 +240,11 @@ function convertedCommandsKind(destSubpath, prefix, converterName, configDir) {
245
240
  destSubpath,
246
241
  prefix,
247
242
  stage: (resolved) => {
248
- const installExports = getInstallExports();
249
- const converter = installExports[converterName];
243
+ const converter = conversionExports[converterName];
250
244
  return stageCommandsForRuntimeFlat(findInstallSourceRoot(configDir), resolved, converter, prefix);
251
245
  },
252
246
  };
253
247
  }
254
- /** Lazy registry accessor — mirrors pattern from 5b/5c (runtime-homes.cts). */
255
248
  function getRegistry() {
256
249
  return _require('./capability-registry.cjs');
257
250
  }
@@ -288,22 +281,21 @@ function dispatchKindEntry(entry, runtime, configDir, scope) {
288
281
  * instead of a hardcoded switch statement.
289
282
  */
290
283
  function resolveRuntimeArtifactLayout(runtime, configDir, scope = 'global') {
284
+ return resolveRuntimeArtifactLayoutFromRegistry(getRegistry(), runtime, configDir, scope);
285
+ }
286
+ function resolveRuntimeArtifactLayoutFromRegistry(registry, runtime, configDir, scope = 'global') {
291
287
  if (typeof configDir !== 'string' || configDir === '') {
292
288
  throw new TypeError('configDir must be a non-empty string');
293
289
  }
294
290
  if (scope !== 'local' && scope !== 'global') {
295
291
  throw new TypeError('scope must be "local" or "global"');
296
292
  }
297
- if (!ALLOWED_RUNTIMES.has(runtime)) {
298
- throw new TypeError(`Unknown runtime: '${runtime}' — add to runtime-artifact-layout.cjs table`);
299
- }
300
- const desc = getRegistry().runtimes[runtime]?.runtime?.artifactLayout;
293
+ const desc = registry.runtimes[runtime]?.runtime?.artifactLayout;
301
294
  if (!desc) {
302
- // Runtime is in ALLOWED_RUNTIMES but has no descriptor — reproduce old default: throw.
303
295
  throw new TypeError(`Unknown runtime: '${runtime}' — add to runtime-artifact-layout.cjs table`);
304
296
  }
305
297
  const entries = desc[scope] ?? [];
306
298
  const kinds = entries.map((entry) => dispatchKindEntry(entry, runtime, configDir, scope));
307
299
  return { runtime, configDir, scope, kinds };
308
300
  }
309
- module.exports = { resolveRuntimeArtifactLayout, findInstallSourceRoot, getInstallExports };
301
+ module.exports = { resolveRuntimeArtifactLayout, resolveRuntimeArtifactLayoutFromRegistry, findInstallSourceRoot, getInstallExports };
@@ -27,9 +27,8 @@
27
27
  */
28
28
  // eslint-disable-next-line @typescript-eslint/no-require-imports
29
29
  const { runtimes } = require('./capability-registry.cjs');
30
- // ---------------------------------------------------------------------------
31
- // Exports
32
- // ---------------------------------------------------------------------------
30
+ /** Valid sandboxTier enum values — mirrors the gen-capability-registry validator vocabulary. */
31
+ const VALID_SANDBOX_TIERS = new Set(['none', 'codex-agent-sandbox']);
33
32
  /** The complete set of 16 supported runtimes for config-adapter dispatch. */
34
33
  const ALLOWED_CONFIG_RUNTIMES = new Set(Object.entries(runtimes)
35
34
  .filter(([, cap]) => cap && cap.runtime && typeof cap.runtime['installSurface'] === 'string')
@@ -63,6 +62,29 @@ function resolveRuntimeConfigIntent(runtime) {
63
62
  finishPermissionWriter: permissionWriter == null ? null : permissionWriter,
64
63
  };
65
64
  }
65
+ function resolveInstallPlanFromRuntimes(runtimeDescriptors, runtime) {
66
+ const desc = runtimeDescriptors[runtime]?.runtime;
67
+ if (!desc)
68
+ throw new TypeError(`Unknown runtime for install plan: ${runtime}`);
69
+ if (desc['hooksSurface'] == null) {
70
+ throw new TypeError(`runtime.hooksSurface is required for install plan: ${runtime}`);
71
+ }
72
+ const sandboxTier = desc['sandboxTier'];
73
+ if (typeof sandboxTier !== 'string' || !VALID_SANDBOX_TIERS.has(sandboxTier)) {
74
+ throw new TypeError(`Runtime '${runtime}' has a missing or invalid sandboxTier descriptor axis: ${JSON.stringify(sandboxTier)}`);
75
+ }
76
+ const permissionWriter = desc['permissionWriter'];
77
+ return {
78
+ runtime,
79
+ installSurface: desc['installSurface'],
80
+ writesSharedSettings: desc['writesSharedSettings'],
81
+ finishPermissionWriter: permissionWriter == null ? null : permissionWriter,
82
+ hookEvents: desc['hookEvents'],
83
+ extendedHookEvents: Array.isArray(desc['extendedHookEvents']) ? [...desc['extendedHookEvents']] : [],
84
+ hooksSurface: desc['hooksSurface'],
85
+ sandboxTier,
86
+ };
87
+ }
66
88
  /**
67
89
  * Resolve the complete install plan for a given runtime.
68
90
  *
@@ -76,20 +98,6 @@ function resolveRuntimeConfigIntent(runtime) {
76
98
  * @throws {TypeError} if runtime is not a known supported runtime.
77
99
  */
78
100
  function resolveInstallPlan(runtime) {
79
- const desc = runtimes[runtime]?.runtime;
80
- if (!desc)
81
- throw new TypeError(`Unknown runtime for install plan: ${runtime}`);
82
- const configIntent = resolveRuntimeConfigIntent(runtime);
83
- return {
84
- runtime,
85
- installSurface: configIntent.installSurface,
86
- writesSharedSettings: configIntent.writesSharedSettings,
87
- finishPermissionWriter: configIntent.finishPermissionWriter,
88
- hookEvents: desc['hookEvents'],
89
- extendedHookEvents: Array.isArray(desc['extendedHookEvents']) ? desc['extendedHookEvents'] : [],
90
- hooksSurface: desc['hooksSurface'] != null
91
- ? desc['hooksSurface']
92
- : ((runtime === 'opencode' || runtime === 'kilo') ? 'none' : 'settings-json'),
93
- };
101
+ return resolveInstallPlanFromRuntimes(runtimes, runtime);
94
102
  }
95
- module.exports = { resolveRuntimeConfigIntent, resolveInstallPlan, ALLOWED_CONFIG_RUNTIMES, INSTALL_SURFACES };
103
+ module.exports = { resolveRuntimeConfigIntent, resolveInstallPlan, resolveInstallPlanFromRuntimes, ALLOWED_CONFIG_RUNTIMES, INSTALL_SURFACES };
@@ -38,6 +38,7 @@ exports.resolveConfigHomeFromDescriptor = resolveConfigHomeFromDescriptor;
38
38
  exports.resolveAntigravityGlobalDir = resolveAntigravityGlobalDir;
39
39
  exports.resolveKimiGlobalDir = resolveKimiGlobalDir;
40
40
  exports.getGlobalConfigDir = getGlobalConfigDir;
41
+ exports.resolveSkillsBaseFromDescriptor = resolveSkillsBaseFromDescriptor;
41
42
  exports.getGlobalSkillsBase = getGlobalSkillsBase;
42
43
  exports.getGlobalSkillDir = getGlobalSkillDir;
43
44
  exports.getGlobalSkillDisplayPath = getGlobalSkillDisplayPath;
@@ -54,6 +55,17 @@ function expandTilde(p) {
54
55
  return node_path_1.default.join(node_os_1.default.homedir(), p.slice(1));
55
56
  return p;
56
57
  }
58
+ function resolveDescriptorWithOptions(configHome) {
59
+ return resolveConfigHomeFromDescriptor(configHome, {
60
+ env: process.env,
61
+ home: node_os_1.default.homedir(),
62
+ existsSync: node_fs_1.default.existsSync,
63
+ });
64
+ }
65
+ function getRegistry() {
66
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
67
+ return require('./capability-registry.cjs');
68
+ }
57
69
  /**
58
70
  * Resolve a configHome descriptor to an absolute directory path.
59
71
  *
@@ -209,15 +221,10 @@ function getGlobalConfigDir(runtime, explicitDir) {
209
221
  return env['GROK_AGENTS_HOME'] ? expandTilde(env['GROK_AGENTS_HOME']) : node_path_1.default.join(node_os_1.default.homedir(), '.agents');
210
222
  }
211
223
  // ── Descriptor-driven: look up in capability-registry ────────────────────
212
- // eslint-disable-next-line @typescript-eslint/no-require-imports
213
- const { runtimes } = require('./capability-registry.cjs');
224
+ const { runtimes } = getRegistry();
214
225
  const runtimeEntry = runtimes[runtime];
215
226
  if (runtimeEntry?.runtime?.configHome) {
216
- return resolveConfigHomeFromDescriptor(runtimeEntry.runtime.configHome, {
217
- env: process.env,
218
- home: node_os_1.default.homedir(),
219
- existsSync: node_fs_1.default.existsSync,
220
- });
227
+ return resolveDescriptorWithOptions(runtimeEntry.runtime.configHome);
221
228
  }
222
229
  // ── Default (unknown runtime → Claude fallback) ───────────────────────────
223
230
  const env = process.env;
@@ -225,22 +232,21 @@ function getGlobalConfigDir(runtime, explicitDir) {
225
232
  }
226
233
  /**
227
234
  * Return the global skills base directory for the given runtime.
228
- * Most runtimes: <configDir>/skills
229
- * Hermes: <configDir>/skills/gsd (nested category layout — #2841)
230
- * Cline ≥ v3.48.0: <configDir>/skills (SKILL.md-based global skills — #782)
235
+ * Descriptor-backed runtimes derive the base home from configHome.skillsHome
236
+ * when present, then append the first global skills artifact destSubpath.
231
237
  */
238
+ function resolveSkillsBaseFromDescriptor(configHome, opts = {}, skillsDestSubpath = 'skills') {
239
+ const baseDescriptor = configHome.skillsHome ?? configHome;
240
+ const base = resolveConfigHomeFromDescriptor(baseDescriptor, opts);
241
+ return node_path_1.default.join(base, skillsDestSubpath);
242
+ }
232
243
  function getGlobalSkillsBase(runtime) {
233
- if (runtime === 'hermes') {
234
- const configDir = getGlobalConfigDir(runtime);
235
- return node_path_1.default.join(configDir, 'skills', 'gsd');
244
+ const runtimeEntry = getRegistry().runtimes[runtime];
245
+ const descriptor = runtimeEntry?.runtime;
246
+ const globalSkillsKind = descriptor?.artifactLayout?.global?.find((entry) => entry.kind === 'skills');
247
+ if (descriptor?.configHome && globalSkillsKind?.destSubpath) {
248
+ return resolveSkillsBaseFromDescriptor(descriptor.configHome, { env: process.env, home: node_os_1.default.homedir(), existsSync: node_fs_1.default.existsSync }, globalSkillsKind.destSubpath);
236
249
  }
237
- // Kilo Code discovers global skills from ~/.kilo/skills/ (HOME-relative),
238
- // independent of the XDG-based config dir (~/.config/kilo) used for commands.
239
- // See: https://kilo.ai/docs/customize/skills
240
- // "Global skills are located in the `.kilo` directory within your Home
241
- // directory: ~/.kilo/skills/"
242
- if (runtime === 'kilo')
243
- return node_path_1.default.join(node_os_1.default.homedir(), '.kilo', 'skills');
244
250
  const configDir = getGlobalConfigDir(runtime);
245
251
  return node_path_1.default.join(configDir, 'skills');
246
252
  }
@@ -42,9 +42,10 @@ function routeStateCommand({ state, args, cwd, raw, error }) {
42
42
  args,
43
43
  subcommands: ['load', 'complete-phase', ...command_aliases_cjs_1.STATE_SUBCOMMANDS.filter((s) => s !== 'load')],
44
44
  defaultSubcommand: 'load',
45
- unsupported: {
46
- 'add-roadmap-evolution': 'state add-roadmap-evolution is SDK-only. Use: gsd-tools query state.add-roadmap-evolution ...',
47
- },
45
+ // No SDK-only state subcommands remain: add-roadmap-evolution was the last
46
+ // holdout after the SDK retirement (ADR-0174) and is now implemented in CJS
47
+ // (handler below). See #1140.
48
+ unsupported: {},
48
49
  error,
49
50
  cwd,
50
51
  raw,
@@ -113,6 +114,17 @@ function routeStateCommand({ state, args, cwd, raw, error }) {
113
114
  const a = (0, command_arg_projection_cjs_1.parseNamedArgs)(args, ['text', 'text-file']);
114
115
  state.cmdStateAddBlocker(cwd, { text: strArg(a, 'text'), text_file: strArg(a, 'text-file') }, raw);
115
116
  },
117
+ 'add-roadmap-evolution': () => {
118
+ const a = (0, command_arg_projection_cjs_1.parseNamedArgs)(args, ['phase', 'action', 'after', 'note', 'note-file'], ['urgent']);
119
+ state.cmdStateAddRoadmapEvolution(cwd, {
120
+ phase: strArg(a, 'phase'),
121
+ action: strArg(a, 'action'),
122
+ after: strArg(a, 'after'),
123
+ note: strArg(a, 'note'),
124
+ note_file: strArg(a, 'note-file'),
125
+ urgent: a['urgent'] === true,
126
+ }, raw);
127
+ },
116
128
  'resolve-blocker': () => state.cmdStateResolveBlocker(cwd, strArg((0, command_arg_projection_cjs_1.parseNamedArgs)(args, ['text']), 'text'), raw),
117
129
  'record-session': () => {
118
130
  const a = (0, command_arg_projection_cjs_1.parseNamedArgs)(args, ['stopped-at', 'resume-file']);
@@ -31,26 +31,71 @@ function existingProgressExceedsDerived(existingProgress, derivedProgress, key)
31
31
  const derived = toFiniteNumber(derivedProgress[key]);
32
32
  return existing !== null && derived !== null && existing > derived;
33
33
  }
34
+ /**
35
+ * Return true if a pipe-table row's first cell is a separator cell (`---`
36
+ * variants) rather than a field name. Prevents the separator row
37
+ * `| --- | --- |` from being treated as a field named "---".
38
+ */
39
+ function isTableSeparatorRow(firstCell) {
40
+ // A separator cell contains only dashes, colons (alignment hints), and whitespace.
41
+ return /^[\s\-:]+$/.test(firstCell.trim());
42
+ }
43
+ /**
44
+ * Build a regex that matches a pipe-table row `| FieldName | value |` for the
45
+ * given (already-escaped) field name. The match is case-insensitive and
46
+ * tolerates variable amounts of whitespace around the cell contents.
47
+ *
48
+ * Capture group 1: leading pipe + whitespace before the field cell
49
+ * Capture group 2: the field name cell text (trimmed)
50
+ * Capture group 3: whitespace between field cell and separator pipe
51
+ * Capture group 4: the value cell text (trimmed)
52
+ * Capture group 5: trailing whitespace + closing pipe(s)
53
+ *
54
+ * We use a single-line match (`m` flag so ^ anchors work on each line) to
55
+ * avoid cross-row replacement.
56
+ */
57
+ function tableRowPattern(escapedFieldName) {
58
+ return new RegExp(`^(\\|[ \\t]*)(${escapedFieldName})([ \\t]*\\|[ \\t]*)([^|\\n]*?)([ \\t]*\\|[ \\t]*)$`, 'im');
59
+ }
34
60
  function stateExtractField(content, fieldName) {
35
61
  const escaped = escapeRegex(fieldName);
62
+ // Bold inline format: **FieldName:** value
36
63
  const boldPattern = new RegExp(`\\*\\*${escaped}:\\*\\*[ \\t]*(.+)`, 'i');
37
64
  const boldMatch = content.match(boldPattern);
38
65
  if (boldMatch)
39
66
  return boldMatch[1].trim();
67
+ // Plain line-start format: FieldName: value
40
68
  const plainPattern = new RegExp(`^${escaped}:[ \\t]*(.+)`, 'im');
41
69
  const plainMatch = content.match(plainPattern);
42
- return plainMatch ? plainMatch[1].trim() : null;
70
+ if (plainMatch)
71
+ return plainMatch[1].trim();
72
+ // Pipe-table format: | FieldName | value |
73
+ // (Separator rows such as `| --- | --- |` are excluded.)
74
+ const tableMatch = content.match(tableRowPattern(escaped));
75
+ if (tableMatch && !isTableSeparatorRow(tableMatch[2]))
76
+ return tableMatch[4].trim();
77
+ return null;
43
78
  }
44
79
  function stateReplaceField(content, fieldName, newValue) {
45
80
  const escaped = escapeRegex(fieldName);
81
+ // Bold inline format: **FieldName:** value
46
82
  const boldPattern = new RegExp(`(\\*\\*${escaped}:\\*\\*\\s*)(.*)`, 'i');
47
83
  if (boldPattern.test(content)) {
48
84
  return content.replace(boldPattern, (_match, prefix) => `${prefix}${newValue}`);
49
85
  }
86
+ // Plain line-start format: FieldName: value
50
87
  const plainPattern = new RegExp(`(^${escaped}:\\s*)(.*)`, 'im');
51
88
  if (plainPattern.test(content)) {
52
89
  return content.replace(plainPattern, (_match, prefix) => `${prefix}${newValue}`);
53
90
  }
91
+ // Pipe-table format: | FieldName | value |
92
+ // Preserve the surrounding pipe/whitespace structure; only swap the value cell.
93
+ const tblPat = tableRowPattern(escaped);
94
+ const tblMatch = content.match(tblPat);
95
+ if (tblMatch && !isTableSeparatorRow(tblMatch[2])) {
96
+ // Reconstruct the row, preserving the original surrounding whitespace/pipes.
97
+ return content.replace(tblPat, (_m, leadPipe, fieldCell, midPipe, _oldVal, trailPipe) => `${leadPipe}${fieldCell}${midPipe}${newValue}${trailPipe}`);
98
+ }
54
99
  return null;
55
100
  }
56
101
  function stateReplaceFieldWithFallback(content, primary, fallback, value) {