@geraldmaron/construct 1.0.21 → 1.0.23

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 (117) hide show
  1. package/README.md +23 -5
  2. package/bin/construct +191 -13
  3. package/bin/construct-postinstall.mjs +25 -15
  4. package/lib/acp/server.mjs +113 -0
  5. package/lib/agent-instructions/inject.mjs +94 -0
  6. package/lib/auto-docs.mjs +10 -2
  7. package/lib/cli-commands.mjs +43 -15
  8. package/lib/comment-lint.mjs +115 -0
  9. package/lib/completions.mjs +7 -1
  10. package/lib/config/schema.mjs +4 -0
  11. package/lib/decisions/enforced-baseline.json +2 -0
  12. package/lib/docs-verify.mjs +15 -18
  13. package/lib/doctor/cli.mjs +8 -1
  14. package/lib/document-export.mjs +124 -0
  15. package/lib/embed/daemon.mjs +1 -1
  16. package/lib/embed/inbox.mjs +25 -7
  17. package/lib/features.mjs +11 -11
  18. package/lib/git-hooks-path.mjs +61 -0
  19. package/lib/home-namespace.mjs +60 -0
  20. package/lib/hooks/ci-status-check.mjs +62 -40
  21. package/lib/hooks/orchestration-dispatch-guard.mjs +153 -0
  22. package/lib/hooks/pre-push-gate.mjs +15 -6
  23. package/lib/hooks/stop-notify.mjs +32 -17
  24. package/lib/hooks/stop-typecheck.mjs +7 -2
  25. package/lib/host-capabilities.mjs +24 -8
  26. package/lib/host-disposition.mjs +76 -0
  27. package/lib/ingest/provider-extract.mjs +1 -1
  28. package/lib/init-docs.mjs +1 -1
  29. package/lib/init-unified.mjs +320 -219
  30. package/lib/init-update.mjs +4 -84
  31. package/lib/init.mjs +9 -25
  32. package/lib/install/stage-project.mjs +8 -2
  33. package/lib/intent-classifier.mjs +1 -1
  34. package/lib/knowledge/search.mjs +52 -3
  35. package/lib/mcp/server.mjs +57 -14
  36. package/lib/mcp/tools/memory.mjs +2 -2
  37. package/lib/mcp/tools/orchestration-run.mjs +125 -0
  38. package/lib/model-registry.mjs +40 -33
  39. package/lib/observation-store.mjs +6 -2
  40. package/lib/opencode-config.mjs +1 -1
  41. package/lib/orchestration/events.mjs +66 -0
  42. package/lib/orchestration/runtime.mjs +70 -9
  43. package/lib/orchestration/worker.mjs +69 -21
  44. package/lib/orchestration-policy.mjs +27 -3
  45. package/lib/parity.mjs +46 -24
  46. package/lib/policy/unified-gates.mjs +96 -0
  47. package/lib/project-init-shared.mjs +0 -173
  48. package/lib/reconcile/adapter-prune.mjs +105 -0
  49. package/lib/reconcile/agent-instructions-rewrap.mjs +98 -0
  50. package/lib/reconcile/gitignore-coverage.mjs +88 -0
  51. package/lib/reconcile/index.mjs +171 -0
  52. package/lib/reconcile/legacy-doctrine-strip.mjs +139 -0
  53. package/lib/reconcile/legacy-guide-decommit.mjs +67 -0
  54. package/lib/reconcile/legacy-skills-cleanup.mjs +200 -0
  55. package/lib/reconcile/mcp-entry-reconcile.mjs +142 -0
  56. package/lib/reconcile/postgres-namespace.mjs +102 -0
  57. package/lib/runtime/uv-bootstrap.mjs +27 -3
  58. package/lib/schema-infer.mjs +16 -2
  59. package/lib/server/csrf.mjs +14 -2
  60. package/lib/server/index.mjs +95 -0
  61. package/lib/service-manager.mjs +39 -14
  62. package/lib/setup-prompts.mjs +2 -1
  63. package/lib/setup.mjs +165 -141
  64. package/lib/storage/backend.mjs +14 -0
  65. package/lib/storage/unified-storage.mjs +550 -0
  66. package/lib/template-registry.mjs +73 -0
  67. package/lib/term-format.mjs +75 -0
  68. package/lib/uninstall/uninstall.mjs +180 -7
  69. package/package.json +2 -2
  70. package/personas/construct.md +7 -8
  71. package/platforms/claude/settings.template.json +30 -4
  72. package/platforms/opencode/config.template.json +2 -2
  73. package/rules/common/neurodivergent-output.md +66 -0
  74. package/rules/common/tool-invisibility.md +37 -0
  75. package/scripts/sync-specialists.mjs +381 -95
  76. package/skills/operating/orchestration-reference.md +2 -16
  77. package/specialists/policy-inventory.json +14 -0
  78. package/specialists/prompts/cx-accessibility.md +2 -6
  79. package/specialists/prompts/cx-ai-engineer.md +0 -4
  80. package/specialists/prompts/cx-architect.md +3 -5
  81. package/specialists/prompts/cx-business-strategist.md +0 -5
  82. package/specialists/prompts/cx-data-analyst.md +0 -4
  83. package/specialists/prompts/cx-data-engineer.md +0 -4
  84. package/specialists/prompts/cx-debugger.md +2 -6
  85. package/specialists/prompts/cx-designer.md +0 -8
  86. package/specialists/prompts/cx-devil-advocate.md +2 -2
  87. package/specialists/prompts/cx-docs-keeper.md +0 -13
  88. package/specialists/prompts/cx-engineer.md +0 -13
  89. package/specialists/prompts/cx-evaluator.md +2 -2
  90. package/specialists/prompts/cx-explorer.md +4 -5
  91. package/specialists/prompts/cx-legal-compliance.md +4 -5
  92. package/specialists/prompts/cx-operations.md +0 -5
  93. package/specialists/prompts/cx-orchestrator.md +0 -4
  94. package/specialists/prompts/cx-platform-engineer.md +0 -8
  95. package/specialists/prompts/cx-product-manager.md +0 -8
  96. package/specialists/prompts/cx-qa.md +2 -11
  97. package/specialists/prompts/cx-rd-lead.md +0 -5
  98. package/specialists/prompts/cx-release-manager.md +0 -8
  99. package/specialists/prompts/cx-researcher.md +5 -29
  100. package/specialists/prompts/cx-reviewer.md +2 -6
  101. package/specialists/prompts/cx-security.md +2 -11
  102. package/specialists/prompts/cx-sre.md +0 -15
  103. package/specialists/prompts/cx-test-automation.md +0 -4
  104. package/specialists/prompts/cx-trace-reviewer.md +2 -2
  105. package/specialists/prompts/cx-ux-researcher.md +0 -4
  106. package/specialists/registry.json +29 -29
  107. package/templates/distribution/run.mjs +36 -7
  108. package/templates/docs/accessibility-audit.md +56 -0
  109. package/templates/docs/architecture-review.md +59 -0
  110. package/templates/docs/code-review-report.md +46 -0
  111. package/templates/docs/construct_guide.md +14 -14
  112. package/templates/docs/debug-investigation.md +53 -0
  113. package/templates/docs/qa-report.md +48 -0
  114. package/templates/docs/security-audit-report.md +48 -0
  115. package/templates/docs/task-packet.md +49 -0
  116. package/templates/docs/verdict.md +40 -0
  117. package/lib/server/static/index.html +0 -1
@@ -31,8 +31,11 @@ const pending = existsSync(tcPath)
31
31
  ? readFileSync(tcPath, 'utf8').split('\n').filter(Boolean)
32
32
  : [];
33
33
 
34
+ // No files queued means no typecheck ran this turn — record 'unchecked', not a
35
+ // misleading 'pass'. A stale 'pass' here is what made stop-notify fire a bare
36
+ // "TS OK" banner on every no-op turn.
34
37
  if (pending.length === 0) {
35
- try { writeFileSync(tsResultPath, 'pass'); } catch { /* best effort */ }
38
+ try { writeFileSync(tsResultPath, 'unchecked'); } catch { /* best effort */ }
36
39
  process.exit(0);
37
40
  }
38
41
 
@@ -55,8 +58,10 @@ function findTsConfig(startDir) {
55
58
  const startDir = dirname(pending[0]);
56
59
  const tsconfig = findTsConfig(startDir);
57
60
 
61
+ // Files were edited but no tsconfig governs them — the typecheck couldn't run,
62
+ // so the honest state is 'unchecked', not 'pass'.
58
63
  if (!tsconfig) {
59
- try { writeFileSync(tsResultPath, 'pass'); } catch { /* best effort */ }
64
+ try { writeFileSync(tsResultPath, 'unchecked'); } catch { /* best effort */ }
60
65
  process.exit(0);
61
66
  }
62
67
 
@@ -118,6 +118,15 @@ function detectCopilotAvailability(homeDir = os.homedir()) {
118
118
  return { hasFiles };
119
119
  }
120
120
 
121
+ // `capability` is the honest, normalized functional classification per host:
122
+ // full-native — the host runs a multi-specialist chain itself (native
123
+ // dispatch or config-driven agents): Claude Code, OpenCode.
124
+ // mcp-orchestrated — no native subagent primitive, but the host calls the
125
+ // `orchestration_run` MCP tool to reach the same orchestrated
126
+ // outcome via the daemon: Codex, VS Code, Cursor, Copilot.
127
+ // prompt-only — neither native dispatch nor MCP; instruction files only.
128
+ // No supported 2026 host is prompt-only.
129
+
121
130
  export function detectHostCapabilities() {
122
131
  const claudeRaw = commandVersion("claude");
123
132
  const claudeVersion = parseClaudeVersion(claudeRaw);
@@ -135,6 +144,7 @@ export function detectHostCapabilities() {
135
144
  availability: claudeRaw ? "installed" : "missing",
136
145
  version: claudeRaw,
137
146
  orchestration: claudeTeamsSupported ? "full-multi-agent" : "primary-plus-subagents",
147
+ capability: "full-native",
138
148
  promptableWorkers: claudeTeamsSupported,
139
149
  sharedTaskRuntime: claudeTeamsSupported,
140
150
  lifecycleHooks: ["SubagentStop", "TeammateIdle", "TaskCreated", "TaskCompleted", "Stop"],
@@ -151,6 +161,7 @@ export function detectHostCapabilities() {
151
161
  availability: opencodeRaw ? "installed" : "missing",
152
162
  version: opencodeRaw,
153
163
  orchestration: "primary-plus-subagents",
164
+ capability: "full-native",
154
165
  promptableWorkers: false,
155
166
  sharedTaskRuntime: "tracker-plus-plan",
156
167
  lifecycleHooks: ["session.error", "session.idle", "tool.execute.before", "tool.execute.after"],
@@ -165,12 +176,13 @@ export function detectHostCapabilities() {
165
176
  availability: codexRaw ? "installed" : "missing",
166
177
  version: codexRaw,
167
178
  orchestration: "profile-and-mcp",
179
+ capability: "mcp-orchestrated",
168
180
  promptableWorkers: false,
169
181
  sharedTaskRuntime: "tracker-plus-plan",
170
182
  lifecycleHooks: [],
171
183
  notes: [
172
- "Use Construct profiles, MCP project/memory tools, and the active session.",
173
- "Native profile switching is not automatic; continue in-session when dispatch is unavailable.",
184
+ "Run a full multi-specialist chain via the `orchestration_run` MCP tool (start `construct dashboard`).",
185
+ "Use Construct profiles, MCP project/memory tools, and the active session for single-pass work.",
174
186
  ],
175
187
  },
176
188
  {
@@ -178,12 +190,13 @@ export function detectHostCapabilities() {
178
190
  availability: vscode.version || vscode.hasSettings ? "installed" : "missing",
179
191
  version: vscode.version,
180
192
  orchestration: "copilot-mcp",
193
+ capability: "mcp-orchestrated",
181
194
  promptableWorkers: false,
182
195
  sharedTaskRuntime: "editor-session",
183
196
  lifecycleHooks: [],
184
197
  notes: [
185
- "Construct manages GitHub Copilot MCP registrations in user settings when a VS Code settings.json already exists.",
186
- "VS Code is an editor surface, not Construct's primary multi-agent runtime.",
198
+ "Run a full multi-specialist chain via the `orchestration_run` MCP tool in Copilot agent mode (start `construct dashboard`).",
199
+ "Construct's MCP servers load from `.vscode/mcp.json` (project) and the user-profile `mcp.json` (global, only when it already exists).",
187
200
  ],
188
201
  },
189
202
  {
@@ -191,12 +204,13 @@ export function detectHostCapabilities() {
191
204
  availability: cursor.version || cursor.hasConfig ? "installed" : "missing",
192
205
  version: cursor.version,
193
206
  orchestration: "mcp-only",
207
+ capability: "mcp-orchestrated",
194
208
  promptableWorkers: false,
195
209
  sharedTaskRuntime: "editor-session",
196
210
  lifecycleHooks: [],
197
211
  notes: [
198
- "Construct manages Cursor MCP registrations in ~/.cursor/mcp.json when that file exists.",
199
- "Cursor agent profiles are not generated today; use Construct through MCP-backed tools and shared project state.",
212
+ "Run a full multi-specialist chain via the `orchestration_run` MCP tool (start `construct dashboard`).",
213
+ "Construct manages Cursor MCP registrations in `.cursor/mcp.json` (project) and `~/.cursor/mcp.json` (global, when it already exists).",
200
214
  ],
201
215
  },
202
216
  {
@@ -204,12 +218,13 @@ export function detectHostCapabilities() {
204
218
  availability: copilot.hasFiles ? "installed" : "missing",
205
219
  version: null,
206
220
  orchestration: "prompt-profiles",
221
+ capability: "mcp-orchestrated",
207
222
  promptableWorkers: false,
208
223
  sharedTaskRuntime: "editor-session",
209
224
  lifecycleHooks: [],
210
225
  notes: [
211
- "Construct writes reusable prompt profiles under ~/.github/prompts/ plus a managed block in ~/.github/copilot-instructions.md.",
212
- "Copilot does not expose Construct-style subagent dispatch; use the generated profiles for focused passes.",
226
+ "Copilot agent mode runs Construct's MCP tools use `orchestration_run` for a real multi-specialist chain (start `construct dashboard`).",
227
+ "Construct also writes reusable prompt profiles under `.github/prompts/` for single-pass role work.",
213
228
  ],
214
229
  },
215
230
  ];
@@ -224,6 +239,7 @@ export function printHostCapabilities(hosts = detectHostCapabilities()) {
224
239
  for (const host of hosts) {
225
240
  console.log("");
226
241
  console.log(`${host.host}: ${host.availability}${host.version ? ` (${host.version})` : ""}`);
242
+ console.log(` capability: ${host.capability}`);
227
243
  console.log(` orchestration: ${host.orchestration}`);
228
244
  console.log(` promptable workers: ${host.promptableWorkers === true ? "yes" : "no"}`);
229
245
  console.log(` shared task runtime: ${host.sharedTaskRuntime === true ? "native" : host.sharedTaskRuntime || "none"}`);
@@ -0,0 +1,76 @@
1
+ /**
2
+ * lib/host-disposition.mjs — single source of truth for the disposition of
3
+ * every artifact Construct creates in a host project (ADR-0027 §1).
4
+ *
5
+ * `IGNORED_PATTERNS` enumerates machine-specific, sync-regenerated artifacts
6
+ * that must never be committed; the init `.gitignore` writer and the
7
+ * `gitignore-coverage` reconciliation both read from here, and uninstall /
8
+ * adapter-prune consume `ADAPTER_DIRS`. Keep this 1:1 with the host-relevant
9
+ * subset of Construct's own repo .gitignore so forward-fix and backward-repair
10
+ * never diverge.
11
+ */
12
+
13
+ import fs from 'node:fs';
14
+ import path from 'node:path';
15
+
16
+ // Adapter directories `construct sync` regenerates per host platform. Each
17
+ // carries machine-specific absolute paths (MCP server paths, env-resolved
18
+ // tokens) and is recreated on demand, so none is durable repo state.
19
+
20
+ export const ADAPTER_DIRS = ['.claude', '.codex', '.cursor', '.vscode', '.opencode'];
21
+
22
+ // Every artifact whose disposition is `ignored`. A trailing slash marks a
23
+ // directory. `.construct/` is the per-project launcher (host scope only — the
24
+ // Construct repo itself tracks `.construct/version`, hence the divergence from
25
+ // the repo .gitignore for that one entry).
26
+
27
+ export const IGNORED_PATTERNS = [
28
+ '.cx/',
29
+ '.claude/',
30
+ '.codex/',
31
+ '.cursor/',
32
+ '.vscode/',
33
+ '.opencode/',
34
+ '.construct/',
35
+ '.mcp.json',
36
+ '.github/prompts/',
37
+ '.github/copilot-instructions.md',
38
+ 'config.env',
39
+ 'embed.yaml',
40
+ 'plan.md',
41
+ 'inbox/',
42
+ ];
43
+
44
+ // A pattern counts as already-ignored when an existing line matches it exactly,
45
+ // matches its bare/slashed form, or is a broader `*` / `**` catch-all.
46
+
47
+ export function isPatternIgnored(pattern, presentLines) {
48
+ const present = presentLines instanceof Set ? presentLines : new Set(presentLines);
49
+ if (present.has('*') || present.has('**')) return true;
50
+ const bare = pattern.replace(/\/$/, '');
51
+ return present.has(pattern) || present.has(bare) || present.has(`${bare}/`) || present.has(`${bare}/**`);
52
+ }
53
+
54
+ // Patterns from IGNORED_PATTERNS not yet covered by `content` (a .gitignore body).
55
+
56
+ export function missingIgnorePatterns(content) {
57
+ const present = new Set((content || '').split('\n').map((l) => l.trim()));
58
+ return IGNORED_PATTERNS.filter((p) => !isPatternIgnored(p, present));
59
+ }
60
+
61
+ // Detection: a package.json declaring this as the `construct` package. Used
62
+ // to prevent `construct init` and `construct sync` from misapplying host
63
+ // project rules (like ignoring .construct/) to the Construct repository itself.
64
+
65
+ export function isConstructPackageRepo(dir) {
66
+ try {
67
+ const pkgPath = path.join(dir, 'package.json');
68
+ if (!fs.existsSync(pkgPath)) return false;
69
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
70
+ if (!pkg) return false;
71
+ if (pkg.name === 'construct' || pkg.name === '@geraldmaron/construct') return true;
72
+ return pkg.bin && (pkg.bin === 'bin/construct' || (typeof pkg.bin === 'object' && pkg.bin.construct === 'bin/construct'));
73
+ } catch {
74
+ return false;
75
+ }
76
+ }
@@ -114,7 +114,7 @@ async function callAnthropic({ model, apiKey, content, fetchImpl }) {
114
114
  async function callOpenRouter({ model, apiKey, content, fetchImpl }) {
115
115
  const res = await fetchImpl('https://openrouter.ai/api/v1/chat/completions', {
116
116
  method: 'POST',
117
- headers: { Authorization: `Bearer ${apiKey}`, 'content-type': 'application/json', 'HTTP-Referer': 'https://github.com/construct' },
117
+ headers: { Authorization: `Bearer ${apiKey}`, 'content-type': 'application/json', 'HTTP-Referer': 'https://github.com/geraldmaron/construct' },
118
118
  body: JSON.stringify({
119
119
  model: model.replace(/^openrouter\//, ''),
120
120
  max_tokens: MAX_OUTPUT_TOKENS,
package/lib/init-docs.mjs CHANGED
@@ -785,7 +785,7 @@ async function main() {
785
785
  process.stdout.write(`[init:docs] skipping .cx/inbox/ — ${inboxDecision.reason}. Run with --force to scaffold anyway.\n`);
786
786
  skipped.push('.cx/inbox/ (deferred to existing intake)');
787
787
  } else {
788
- writeIfMissing(path.join(target, '.cx', 'inbox', '.gitkeep'), '');
788
+ fs.mkdirSync(path.join(target, '.cx', 'inbox'), { recursive: true });
789
789
  }
790
790
  }
791
791